Browse Source

Merge branch 'ide_m2' of https://github.com/yann300/cpp-ethereum into yann300-ide_m2

Conflicts:
	mix/CodeEditorExtensionManager.cpp
cl-refactor
Gav Wood 10 years ago
parent
commit
121f485190
  1. 16
      alethzero/MainWin.cpp
  2. 8
      alethzero/MainWin.h
  3. 60
      libdevcore/CommonJS.cpp
  4. 7
      libdevcore/CommonJS.h
  5. 4
      libethereum/Executive.h
  6. 18
      libevmcore/Instruction.h
  7. 87
      mix/AppContext.cpp
  8. 29
      mix/AppContext.h
  9. 104
      mix/AssemblyDebuggerCtrl.cpp
  10. 70
      mix/AssemblyDebuggerCtrl.h
  11. 111
      mix/AssemblyDebuggerModel.cpp
  12. 49
      mix/AssemblyDebuggerModel.h
  13. 22
      mix/CodeEditorExtensionManager.cpp
  14. 4
      mix/CodeEditorExtensionManager.h
  15. 17
      mix/ConstantCompilationCtrl.cpp
  16. 7
      mix/ConstantCompilationCtrl.h
  17. 8
      mix/ConstantCompilationModel.cpp
  18. 7
      mix/ConstantCompilationModel.h
  19. 158
      mix/DebuggingStateWrapper.cpp
  20. 139
      mix/DebuggingStateWrapper.h
  21. 50
      mix/Extension.cpp
  22. 23
      mix/Extension.h
  23. 29
      mix/KeyEventManager.cpp
  24. 42
      mix/KeyEventManager.h
  25. 2
      mix/MixApplication.cpp
  26. 1
      mix/MixApplication.h
  27. 40
      mix/TransactionBuilder.cpp
  28. 43
      mix/TransactionBuilder.h
  29. 9
      mix/main.cpp
  30. 3
      mix/qml.qrc
  31. 20
      mix/qml/BasicMessage.qml
  32. 240
      mix/qml/Debugger.qml
  33. 22
      mix/qml/MainContent.qml
  34. 62
      mix/qml/js/Debugger.js
  35. 40
      mix/qml/main.qml

16
alethzero/MainWin.cpp

@ -65,7 +65,7 @@ static void initUnits(QComboBox* _b)
_b->addItem(QString::fromStdString(units()[n].second), n); _b->addItem(QString::fromStdString(units()[n].second), n);
} }
static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr) QString Main::fromRaw(dev::h256 _n, unsigned* _inc)
{ {
if (_n) if (_n)
{ {
@ -149,7 +149,7 @@ Main::Main(QWidget *parent) :
statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->peerCount);
statusBar()->addPermanentWidget(ui->mineStatus); statusBar()->addPermanentWidget(ui->mineStatus);
statusBar()->addPermanentWidget(ui->blockCount); statusBar()->addPermanentWidget(ui->blockCount);
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"})); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"}));
@ -173,17 +173,17 @@ Main::Main(QWidget *parent) :
connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qweb)); connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qweb));
connect(m_qweb, SIGNAL(onNewId(QString)), this, SLOT(addNewId(QString))); connect(m_qweb, SIGNAL(onNewId(QString)), this, SLOT(addNewId(QString)));
}); });
connect(ui->webView, &QWebView::loadFinished, [=]() connect(ui->webView, &QWebView::loadFinished, [=]()
{ {
m_qweb->poll(); m_qweb->poll();
}); });
connect(ui->webView, &QWebView::titleChanged, [=]() connect(ui->webView, &QWebView::titleChanged, [=]()
{ {
ui->tabWidget->setTabText(0, ui->webView->title()); ui->tabWidget->setTabText(0, ui->webView->title());
}); });
readSettings(); readSettings();
installWatches(); installWatches();
startTimer(100); startTimer(100);
@ -1067,7 +1067,7 @@ void Main::timerEvent(QTimerEvent*)
// 7/18, Alex: aggregating timers, prelude to better threading? // 7/18, Alex: aggregating timers, prelude to better threading?
// Runs much faster on slower dual-core processors // Runs much faster on slower dual-core processors
static int interval = 100; static int interval = 100;
// refresh mining every 200ms // refresh mining every 200ms
if (interval / 100 % 2 == 0) if (interval / 100 % 2 == 0)
refreshMining(); refreshMining();
@ -1093,7 +1093,7 @@ void Main::timerEvent(QTimerEvent*)
} }
else else
interval += 100; interval += 100;
if (m_qweb) if (m_qweb)
m_qweb->poll(); m_qweb->poll();
@ -1711,7 +1711,7 @@ void Main::on_net_triggered()
{ {
ui->port->setEnabled(!ui->net->isChecked()); ui->port->setEnabled(!ui->net->isChecked());
ui->clientName->setEnabled(!ui->net->isChecked()); ui->clientName->setEnabled(!ui->net->isChecked());
string n = string("AlethZero/v") + dev::Version; string n = string("AlethZero/v") + dev::Version;
if (ui->clientName->text().size()) if (ui->clientName->text().size())
n += "/" + ui->clientName->text().toStdString(); n += "/" + ui->clientName->text().toStdString();
n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM);

8
alethzero/MainWin.h

@ -69,7 +69,7 @@ struct WorldState
class Main : public QMainWindow class Main : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit Main(QWidget *parent = 0); explicit Main(QWidget *parent = 0);
~Main(); ~Main();
@ -79,7 +79,7 @@ public:
std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); } std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); }
QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; } QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; }
public slots: public slots:
void load(QString _file); void load(QString _file);
void note(QString _entry); void note(QString _entry);
@ -146,7 +146,7 @@ private slots:
void on_debugDumpState_triggered(int _add = 1); void on_debugDumpState_triggered(int _add = 1);
void on_debugDumpStatePre_triggered(); void on_debugDumpStatePre_triggered();
void on_refresh_triggered(); void on_refresh_triggered();
void on_usePrivate_triggered(); void on_usePrivate_triggered();
void on_enableOptimizer_triggered(); void on_enableOptimizer_triggered();
void on_turboMining_triggered(); void on_turboMining_triggered();
void on_go_triggered(); void on_go_triggered();
@ -259,4 +259,6 @@ private:
std::unique_ptr<QWebThreeConnector> m_qwebConnector; std::unique_ptr<QWebThreeConnector> m_qwebConnector;
std::unique_ptr<OurWebThreeStubServer> m_server; std::unique_ptr<OurWebThreeStubServer> m_server;
QWebThree* m_qweb = nullptr; QWebThree* m_qweb = nullptr;
static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr);
}; };

60
libdevcore/CommonJS.cpp

@ -54,4 +54,64 @@ bytes unpadded(bytes _b)
return _b; return _b;
} }
std::string prettyU256(u256 _n)
{
unsigned inc = 0;
std::string raw;
std::ostringstream s;
if (!(_n >> 64))
s << " " << (uint64_t)_n << " (0x" << (uint64_t)_n << ")";
else if (!~(_n >> 64))
s << " " << (int64_t)_n << " (0x" << (int64_t)_n << ")";
else if ((_n >> 160) == 0)
{
Address a = right160(_n);
std::string n = a.abridged();
if (n.empty())
s << "0x" << a;
else
s << n << "(0x" << a.abridged() << ")";
}
else if ((raw = fromRaw((h256)_n, &inc)).size())
return "\"" + raw + "\"" + (inc ? " + " + std::to_string(inc) : "");
else
s << "" << (h256)_n;
return s.str();
}
std::string fromRaw(h256 _n, unsigned* _inc)
{
if (_n)
{
std::string s((char const*)_n.data(), 32);
auto l = s.find_first_of('\0');
if (!l)
return "";
if (l != std::string::npos)
{
auto p = s.find_first_not_of('\0', l);
if (!(p == std::string::npos || (_inc && p == 31)))
return "";
if (_inc)
*_inc = (byte)s[31];
s.resize(l);
}
for (auto i: s)
if (i < 32)
return "";
return s;
}
return "";
}
Address fromString(std::string const& _sn)
{
if (_sn.size() == 40)
return Address(fromHex(_sn));
else
return Address();
}
} }

7
libdevcore/CommonJS.h

@ -35,12 +35,12 @@ template <unsigned S> std::string toJS(FixedHash<S> const& _h)
{ {
return "0x" + toHex(_h.ref()); return "0x" + toHex(_h.ref());
} }
template <unsigned N> std::string toJS(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N, N, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> const& _n) template <unsigned N> std::string toJS(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N, N, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> const& _n)
{ {
return "0x" + toHex(toCompactBigEndian(_n)); return "0x" + toHex(toCompactBigEndian(_n));
} }
inline std::string toJS(dev::bytes const& _n) inline std::string toJS(dev::bytes const& _n)
{ {
return "0x" + dev::toHex(_n); return "0x" + dev::toHex(_n);
@ -49,6 +49,9 @@ inline std::string toJS(dev::bytes const& _n)
bytes jsToBytes(std::string const& _s); bytes jsToBytes(std::string const& _s);
bytes padded(bytes _b, unsigned _l); bytes padded(bytes _b, unsigned _l);
bytes unpadded(bytes _s); bytes unpadded(bytes _s);
std::string prettyU256(u256 _n);
std::string fromRaw(h256 _n, unsigned* _inc = nullptr);
Address fromString(std::string const& _a);
template <unsigned N> FixedHash<N> jsToFixed(std::string const& _s) template <unsigned N> FixedHash<N> jsToFixed(std::string const& _s)
{ {

4
libethereum/Executive.h

@ -113,8 +113,8 @@ private:
bool m_excepted = false; ///< True if the VM execution resulted in an exception. bool m_excepted = false; ///< True if the VM execution resulted in an exception.
u256 m_endGas; ///< The final amount of gas for the transaction. u256 m_endGas; ///< The final amount of gas for the transaction.
Transaction m_t; ///< The original transaction in the case that setup() was called. Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Only valid LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().
}; };
} }

18
libevmcore/Instruction.h

@ -173,6 +173,24 @@ enum class Instruction: uint8_t
SUICIDE = 0xff ///< halt execution and register account for later deletion SUICIDE = 0xff ///< halt execution and register account for later deletion
}; };
/// @returns the number of PUSH Instruction _inst
inline unsigned getPushNumber(Instruction _inst)
{
return (byte)_inst - unsigned(Instruction::PUSH1) + 1;
}
/// @returns the number of DUP Instruction _inst
inline unsigned getDupNumber(Instruction _inst)
{
return (byte)_inst - unsigned(Instruction::DUP1) + 1;
}
/// @returns the number of SWAP Instruction _inst
inline unsigned getSwapNumber(Instruction _inst)
{
return (byte)_inst - unsigned(Instruction::SWAP1) + 1;
}
/// @returns the PUSH<_number> instruction /// @returns the PUSH<_number> instruction
inline Instruction pushInstruction(unsigned _number) inline Instruction pushInstruction(unsigned _number)
{ {

87
mix/AppContext.cpp

@ -0,0 +1,87 @@
/*
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 AppContext.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Provides access to the current QQmlApplicationEngine which is used to add QML file on the fly.
* In the future this class can be extended to add more variable related to the context of the application.
* For now AppContext provides reference to:
* - QQmlApplicationEngine
* - dev::WebThreeDirect (and dev::eth::Client)
* - KeyEventManager
*/
#include <QDebug>
#include <QMessageBox>
#include <QQmlComponent>
#include <QQmlApplicationEngine>
#include "libdevcrypto/FileSystem.h"
#include "KeyEventManager.h"
#include "AppContext.h"
using namespace dev;
using namespace dev::mix;
using namespace dev::eth;
AppContext* AppContext::Instance = nullptr;
AppContext::AppContext(QQmlApplicationEngine* _engine)
{
m_applicationEngine = std::unique_ptr<QQmlApplicationEngine>(_engine);
m_keyEventManager = std::unique_ptr<KeyEventManager>(new KeyEventManager());
m_webThree = std::unique_ptr<dev::WebThreeDirect>(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"}));
}
QQmlApplicationEngine* AppContext::appEngine()
{
return m_applicationEngine.get();
}
dev::eth::Client* AppContext::getEthereumClient()
{
return m_webThree->ethereum();
}
void AppContext::initKeyEventManager()
{
QObject* mainContent = m_applicationEngine->rootObjects().at(0)->findChild<QObject*>("mainContent", Qt::FindChildrenRecursively);
if (mainContent)
QObject::connect(mainContent, SIGNAL(keyPressed(QVariant)), m_keyEventManager.get(), SLOT(keyPressed(QVariant)));
else
qDebug() << "Unable to find QObject of mainContent.qml. KeyEvent will not be handled!";
}
KeyEventManager* AppContext::getKeyEventManager()
{
return m_keyEventManager.get();
}
void AppContext::setApplicationContext(QQmlApplicationEngine* _engine)
{
if (Instance == nullptr)
Instance = new AppContext(_engine);
}
void AppContext::displayMessageDialog(QString _title, QString _message)
{
QQmlComponent component(m_applicationEngine.get(), QUrl("qrc:/qml/BasicMessage.qml"));
QObject* dialog = component.create();
dialog->findChild<QObject*>("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message);
QObject* dialogWin = AppContext::getInstance()->appEngine()->rootObjects().at(0)->findChild<QObject*>("messageDialog", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(dialogWin, "close");
dialogWin->setProperty("contentItem", QVariant::fromValue(dialog));
dialogWin->setProperty("title", _title);
dialogWin->setProperty("width", "250");
dialogWin->setProperty("height", "100");
QMetaObject::invokeMethod(dialogWin, "open");
}

29
mix/ApplicationCtx.h → mix/AppContext.h

@ -14,37 +14,48 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file ApplicationCtx.h /** @file AppContext.h
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com
* @date 2014 * @date 2014
* Provide an access to the current QQmlApplicationEngine which is used to add QML file on the fly. * Provides access to the current QQmlApplicationEngine which is used to add QML file on the fly.
* In the future this class can be extended to add more variable related to the context of the application. * In the future this class can be extended to add more variable related to the context of the application.
* For now AppContext provides reference to:
* - QQmlApplicationEngine
* - dev::WebThreeDirect (and dev::eth::Client)
* - KeyEventManager
*/ */
#pragma once #pragma once
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include "libwebthree/WebThree.h"
#include "KeyEventManager.h"
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
class ApplicationCtx: public QObject class AppContext: public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
ApplicationCtx(QQmlApplicationEngine* _engine) { m_applicationEngine = _engine; } AppContext(QQmlApplicationEngine* _engine);
~ApplicationCtx() { delete m_applicationEngine; } ~AppContext() {}
static ApplicationCtx* getInstance() { return Instance; } static AppContext* getInstance() { return Instance; }
static void setApplicationContext(QQmlApplicationEngine* _engine); static void setApplicationContext(QQmlApplicationEngine* _engine);
QQmlApplicationEngine* appEngine(); QQmlApplicationEngine* appEngine();
dev::eth::Client* getEthereumClient();
void initKeyEventManager();
KeyEventManager* getKeyEventManager();
void displayMessageDialog(QString _title, QString _message);
private: private:
static ApplicationCtx* Instance; static AppContext* Instance;
QQmlApplicationEngine* m_applicationEngine; std::unique_ptr<QQmlApplicationEngine> m_applicationEngine;
std::unique_ptr<dev::WebThreeDirect> m_webThree;
std::unique_ptr<KeyEventManager> m_keyEventManager;
public slots: public slots:
void quitApplication() { delete Instance; } void quitApplication() { delete Instance; }

104
mix/AssemblyDebuggerCtrl.cpp

@ -0,0 +1,104 @@
/*
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 AssemblyDebuggerCtrl.h
* @author Yann yann@ethdev.com
* @date 2014
* display opcode debugging.
*/
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QQmlContext>
#include <QModelIndex>
#include "libethereum/Transaction.h"
#include "AssemblyDebuggerModel.h"
#include "AssemblyDebuggerCtrl.h"
#include "TransactionBuilder.h"
#include "KeyEventManager.h"
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
using namespace dev::mix;
AssemblyDebuggerCtrl::AssemblyDebuggerCtrl(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::ModalDialog)
{
qRegisterMetaType<AssemblyDebuggerData>();
qRegisterMetaType<DebuggingStatusResult>();
connect(this, SIGNAL(dataAvailable(bool, DebuggingStatusResult, QList<QObject*>, AssemblyDebuggerData)),
this, SLOT(updateGUI(bool, DebuggingStatusResult, QList<QObject*>, AssemblyDebuggerData)), Qt::QueuedConnection);
m_modelDebugger = std::unique_ptr<AssemblyDebuggerModel>(new AssemblyDebuggerModel);
m_doc = _doc;
}
QString AssemblyDebuggerCtrl::contentUrl() const
{
return QStringLiteral("qrc:/qml/Debugger.qml");
}
QString AssemblyDebuggerCtrl::title() const
{
return QApplication::tr("debugger");
}
void AssemblyDebuggerCtrl::start() const
{
//start to listen on F5
m_ctx->getKeyEventManager()->registerEvent(this, SLOT(keyPressed(int)));
}
void AssemblyDebuggerCtrl::keyPressed(int _key)
{
if (_key == Qt::Key_F5)
{
QString code = m_doc->toPlainText();
QtConcurrent::run([this, code]()
{
if (!m_modelDebugger->compile(m_doc->toPlainText()))
{
emit dataAvailable(false, DebuggingStatusResult::Compilationfailed);
return;
}
KeyPair ad = KeyPair::create();
u256 gasPrice = 10000000000000;
u256 gas = 1000000;
u256 amount = 100;
DebuggingContent debuggingContent = m_modelDebugger->getContractInitiationDebugStates(amount, gasPrice, gas, m_doc->toPlainText(), ad);
//we need to wrap states in a QObject before sending to QML.
QList<QObject*> wStates;
for(int i = 0; i < debuggingContent.states.size(); i++)
{
DebuggingStateWrapper* s = new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes(), this);
s->setState(debuggingContent.states.at(i));
wStates.append(s);
}
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode, this);
emit dataAvailable(true, DebuggingStatusResult::Ok, wStates, code);
});
}
}
void AssemblyDebuggerCtrl::updateGUI(bool success, DebuggingStatusResult reason, QList<QObject*> _wStates, AssemblyDebuggerData _code)
{
Q_UNUSED(reason);
if (success)
{
m_appEngine->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates));
m_appEngine->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code)));
m_appEngine->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code)));
this->addContentOn(this);
}
else
m_ctx->displayMessageDialog(QApplication::tr("debugger"), QApplication::tr("compilation failed"));
}

70
mix/AssemblyDebuggerCtrl.h

@ -0,0 +1,70 @@
/*
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 AssemblyDebuggerCtrl.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QKeySequence>
#include "QTextDocument"
#include "Extension.h"
#include "ConstantCompilationModel.h"
#include "AssemblyDebuggerModel.h"
#include "AppContext.h"
using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
enum DebuggingStatusResult
{
Ok,
Compilationfailed
};
Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(DebuggingStatusResult)
namespace dev
{
namespace mix
{
class AssemblyDebuggerCtrl: public Extension
{
Q_OBJECT
public:
AssemblyDebuggerCtrl(QTextDocument*);
~AssemblyDebuggerCtrl() {}
void start() const override;
QString title() const override;
QString contentUrl() const override;
private:
std::unique_ptr<AssemblyDebuggerModel> m_modelDebugger;
QTextDocument* m_doc;
public slots:
void keyPressed(int);
void updateGUI(bool success, DebuggingStatusResult reason, QList<QObject*> _wStates = QList<QObject*>(), AssemblyDebuggerData _code = AssemblyDebuggerData());
signals:
void dataAvailable(bool success, DebuggingStatusResult reason, QList<QObject*> _wStates = QList<QObject*>(), AssemblyDebuggerData _code = AssemblyDebuggerData());
};
}
}

111
mix/AssemblyDebuggerModel.cpp

@ -0,0 +1,111 @@
/*
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 AssemblyDebuggerModel.h
* @author Yann yann@ethdev.com
* @date 2014
* used as a model to debug contract assembly code.
*/
#include <QApplication>
#include "libethereum/Executive.h"
#include "libethereum/Transaction.h"
#include "libethereum/ExtVM.h"
#include "libevm/VM.h"
#include "libdevcore/Common.h"
#include "AppContext.h"
#include "TransactionBuilder.h"
#include "AssemblyDebuggerModel.h"
#include "ConstantCompilationModel.h"
#include "DebuggingStateWrapper.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
AssemblyDebuggerModel::AssemblyDebuggerModel()
{
m_currentExecution = std::unique_ptr<Executive>(new Executive(m_executiveState, 0));
}
DebuggingContent AssemblyDebuggerModel::getContractInitiationDebugStates(dev::bytesConstRef _rawTransaction)
{
QList<DebuggingState> states;
Transaction tr(_rawTransaction);
m_currentExecution->create(tr.sender(), tr.value(), tr.gasPrice(), tr.gas(), &tr.data(), tr.sender());
std::vector<DebuggingState const*> levels;
bytes code;
bytesConstRef data;
bool firstIteration = true;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
{
VM& vm = *(VM*)voidVM;
ExtVM const& ext = *(ExtVM const*)voidExt;
if (firstIteration)
{
code = ext.code;
data = ext.data;
firstIteration = false;
}
if (levels.size() < ext.depth)
levels.push_back(&states.back());
else
levels.resize(ext.depth);
states.append(DebuggingState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
m_currentExecution->go(onOp);
m_currentExecution->finalize(onOp);
DebuggingContent d;
d.states = states;
d.executionCode = code;
d.executionData = data;
d.contentAvailable = true;
d.message = "ok";
return d;
}
DebuggingContent AssemblyDebuggerModel::getContractInitiationDebugStates(dev::u256 _value,
dev::u256 _gasPrice,
dev::u256 _gas,
QString _code,
KeyPair _key)
{
ConstantCompilationModel compiler;
CompilerResult res = compiler.compile(_code);
if (!res.success)
{
DebuggingContent r;
r.contentAvailable = false;
r.message = QApplication::tr("compilation failed");
return r;
}
TransactionBuilder trBuild;
Transaction tr = trBuild.getCreationTransaction(_value, _gasPrice, _gas, res.bytes,
m_executiveState.transactionsFrom(dev::toAddress(_key.secret())), _key.secret());
bytes b = tr.rlp();
dev::bytesConstRef bytesRef = &b;
return getContractInitiationDebugStates(bytesRef);
}
bool AssemblyDebuggerModel::compile(QString _code)
{
ConstantCompilationModel compiler;
CompilerResult res = compiler.compile(_code);
return res.success;
}

49
mix/AssemblyDebuggerModel.h

@ -0,0 +1,49 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AssemblyDebuggerModel.h
* @author Yann yann@ethdev.com
* @date 2014
* serves as a model to debug contract assembly code.
*/
#pragma once
#include <QObject>
#include <QList>
#include "libethereum/State.h"
#include "libethereum/Executive.h"
#include "libdevcore/Common.h"
#include "DebuggingStateWrapper.h"
namespace dev
{
namespace mix
{
class AssemblyDebuggerModel
{
public:
AssemblyDebuggerModel();
DebuggingContent getContractInitiationDebugStates(dev::u256, dev::u256, dev::u256, QString, KeyPair);
DebuggingContent getContractInitiationDebugStates(dev::bytesConstRef);
bool compile(QString);
private:
std::unique_ptr<dev::eth::Executive> m_currentExecution;
dev::eth::State m_executiveState;
};
}
}

22
mix/CodeEditorExtensionManager.cpp

@ -27,7 +27,8 @@
#include <QQuickTextDocument> #include <QQuickTextDocument>
#include <libevm/VM.h> #include <libevm/VM.h>
#include "ConstantCompilationCtrl.h" #include "ConstantCompilationCtrl.h"
#include "ApplicationCtx.h" #include "AssemblyDebuggerCtrl.h"
#include "AppContext.h"
#include "CodeEditorExtensionManager.h" #include "CodeEditorExtensionManager.h"
using namespace dev::mix; using namespace dev::mix;
@ -58,22 +59,27 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
void CodeEditorExtensionManager::initExtensions() void CodeEditorExtensionManager::initExtensions()
{ {
//only one for now initExtension(std::make_shared<ConstantCompilationCtrl>(m_doc));
std::shared_ptr<ConstantCompilationCtrl> constantCompilation = std::make_shared<ConstantCompilationCtrl>(m_doc); initExtension(std::make_shared<AssemblyDebuggerCtrl>(m_doc));
if (constantCompilation.get()->contentUrl() != "") }
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
{
if (!_ext->contentUrl().isEmpty())
{ {
try try
{ {
constantCompilation.get()->addContentOn(m_tabView); if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::Tab)
_ext->addTabOn(m_tabView);
} }
catch (...) catch (...)
{ {
qDebug() << "Exception when adding content into view."; qDebug() << "Exception when adding tab into view.";
return; return;
} }
} }
constantCompilation.get()->start(); _ext->start();
m_features.append(constantCompilation); m_features.append(_ext);
} }
void CodeEditorExtensionManager::setEditor(QQuickItem* _editor) void CodeEditorExtensionManager::setEditor(QQuickItem* _editor)

4
mix/CodeEditorExtensionManager.h

@ -30,7 +30,6 @@
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
@ -45,12 +44,13 @@ public:
CodeEditorExtensionManager() {} CodeEditorExtensionManager() {}
~CodeEditorExtensionManager(); ~CodeEditorExtensionManager();
void initExtensions(); void initExtensions();
void initExtension(std::shared_ptr<Extension>);
void setEditor(QQuickItem*); void setEditor(QQuickItem*);
void setTabView(QQuickItem*); void setTabView(QQuickItem*);
private: private:
QQuickItem* m_editor; QQuickItem* m_editor;
QVector<std::shared_ptr<ConstantCompilationCtrl>> m_features; QVector<std::shared_ptr<Extension>> m_features;
QQuickItem* m_tabView; QQuickItem* m_tabView;
QTextDocument* m_doc; QTextDocument* m_doc;
void loadEditor(QQuickItem*); void loadEditor(QQuickItem*);

17
mix/ConstantCompilationCtrl.cpp

@ -30,15 +30,10 @@
#include "ConstantCompilationModel.h" #include "ConstantCompilationModel.h"
using namespace dev::mix; using namespace dev::mix;
ConstantCompilationCtrl::ConstantCompilationCtrl(QTextDocument* _doc) ConstantCompilationCtrl::ConstantCompilationCtrl(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::Tab)
{ {
m_editor = _doc; m_editor = _doc;
m_compilationModel = new ConstantCompilationModel(); m_compilationModel = std::unique_ptr<ConstantCompilationModel>(new ConstantCompilationModel());
}
ConstantCompilationCtrl::~ConstantCompilationCtrl()
{
delete m_compilationModel;
} }
QString ConstantCompilationCtrl::contentUrl() const QString ConstantCompilationCtrl::contentUrl() const
@ -48,7 +43,7 @@ QString ConstantCompilationCtrl::contentUrl() const
QString ConstantCompilationCtrl::title() const QString ConstantCompilationCtrl::title() const
{ {
return "compiler"; return QApplication::tr("compiler");
} }
void ConstantCompilationCtrl::start() const void ConstantCompilationCtrl::start() const
@ -64,7 +59,7 @@ void ConstantCompilationCtrl::compile()
resetOutPut(); resetOutPut();
return; return;
} }
CompilerResult res = m_compilationModel->compile(m_editor->toPlainText()); CompilerResult res = m_compilationModel->compile(m_editor->toPlainText().replace("\t", " "));
writeOutPut(res); writeOutPut(res);
} }
@ -85,13 +80,13 @@ void ConstantCompilationCtrl::writeOutPut(CompilerResult const& _res)
status->setProperty("text", "succeeded"); status->setProperty("text", "succeeded");
status->setProperty("color", "green"); status->setProperty("color", "green");
content->setProperty("text", _res.hexCode); content->setProperty("text", _res.hexCode);
qDebug() << QString("compile succeeded " + _res.hexCode); qDebug() << QString(QApplication::tr("compile succeeded") + " " + _res.hexCode);
} }
else else
{ {
status->setProperty("text", "failure"); status->setProperty("text", "failure");
status->setProperty("color", "red"); status->setProperty("color", "red");
content->setProperty("text", _res.comment); content->setProperty("text", _res.comment);
qDebug() << QString("compile failed " + _res.comment); qDebug() << QString(QApplication::tr("compile failed") + " " + _res.comment);
} }
} }

7
mix/ConstantCompilationCtrl.h

@ -25,7 +25,6 @@
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
@ -35,18 +34,18 @@ class ConstantCompilationCtrl: public Extension
public: public:
ConstantCompilationCtrl(QTextDocument*); ConstantCompilationCtrl(QTextDocument*);
~ConstantCompilationCtrl(); ~ConstantCompilationCtrl() {}
void start() const override; void start() const override;
QString title() const override; QString title() const override;
QString contentUrl() const override; QString contentUrl() const override;
private: private:
QTextDocument* m_editor; QTextDocument* m_editor;
ConstantCompilationModel* m_compilationModel; std::unique_ptr<ConstantCompilationModel> m_compilationModel;
void writeOutPut(CompilerResult const&); void writeOutPut(CompilerResult const&);
void resetOutPut(); void resetOutPut();
public Q_SLOTS: public slots:
void compile(); void compile();
}; };

8
mix/ConstantCompilationModel.cpp

@ -14,12 +14,13 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file ApplicationCtx.h /** @file ConstantCompilationModel.h
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com
* @date 2014 * @date 2014
* Ethereum IDE client. * Ethereum IDE client.
*/ */
#include <QApplication>
#include <QObject> #include <QObject>
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
@ -42,19 +43,20 @@ CompilerResult ConstantCompilationModel::compile(QString _code)
res.success = true; res.success = true;
res.comment = "ok"; res.comment = "ok";
res.hexCode = QString::fromStdString(dev::eth::disassemble(m_data)); res.hexCode = QString::fromStdString(dev::eth::disassemble(m_data));
res.bytes = m_data;
} }
catch (dev::Exception const& _exception) catch (dev::Exception const& _exception)
{ {
ostringstream error; ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler); solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler);
res.success = false; res.success = false;
res.comment = QString::fromStdString(error.str()).toHtmlEscaped(); res.comment = QString::fromStdString(error.str());
res.hexCode = ""; res.hexCode = "";
} }
catch (...) catch (...)
{ {
res.success = false; res.success = false;
res.comment = "Uncaught exception."; res.comment = QApplication::tr("Uncaught exception.");
res.hexCode = ""; res.hexCode = "";
} }
return res; return res;

7
mix/ConstantCompilationModel.h

@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file ApplicationCtx.h /** @file ConstantCompilationModel.h
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com
* @date 2014 * @date 2014
* Ethereum IDE client. * Ethereum IDE client.
@ -22,11 +22,11 @@
#pragma once #pragma once
#include <libevm/VM.h>
#include <QObject> #include <QObject>
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
@ -34,6 +34,7 @@ struct CompilerResult
{ {
QString hexCode; QString hexCode;
QString comment; QString comment;
dev::bytes bytes;
bool success; bool success;
}; };
@ -43,7 +44,7 @@ class ConstantCompilationModel
public: public:
ConstantCompilationModel() {} ConstantCompilationModel() {}
~ConstantCompilationModel() {} ~ConstantCompilationModel() {}
CompilerResult compile(QString code); CompilerResult compile(QString _code);
}; };
} }

158
mix/DebuggingStateWrapper.cpp

@ -0,0 +1,158 @@
/*
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 DebuggingStateWrapper.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Used to translate c++ type (u256, bytes, ...) into friendly value (to be used by QML).
*/
#include <QApplication>
#include <QDebug>
#include "libevmcore/Instruction.h"
#include "libdevcore/CommonJS.h"
#include "libdevcrypto/Common.h"
#include "libevmcore/Instruction.h"
#include "libdevcore/Common.h"
#include "DebuggingStateWrapper.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCode(const bytes& _code, QObject* _objUsedAsParent)
{
QList<QObject*> codeStr;
QMap<int, int> codeMapping;
for (unsigned i = 0; i <= _code.size(); ++i)
{
byte b = i < _code.size() ? _code[i] : 0;
try
{
QString s = QString::fromStdString(instructionInfo((Instruction)b).name);
std::ostringstream out;
out << std::hex << std::setw(4) << std::setfill('0') << i;
codeMapping[i] = codeStr.size();
int line = i;
if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32)
{
unsigned bc = getPushNumber((Instruction)b);
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&_code[i + 1], bc)));
i += bc;
}
HumanReadableCode* humanCode = new HumanReadableCode(QString::fromStdString(out.str()) + " " + s, line, _objUsedAsParent);
codeStr.append(humanCode);
}
catch (...)
{
qDebug() << QString("Unhandled exception!") << endl <<
QString::fromStdString(boost::current_exception_diagnostic_information());
break; // probably hit data segment
}
}
return std::make_tuple(codeStr, new QQMLMap(codeMapping, _objUsedAsParent));
}
QString DebuggingStateWrapper::gasLeft()
{
std::ostringstream ss;
ss << std::dec << (m_state.gas - m_state.gasCost);
return QString::fromStdString(ss.str());
}
QString DebuggingStateWrapper::gasCost()
{
std::ostringstream ss;
ss << std::dec << m_state.gasCost;
return QString::fromStdString(ss.str());
}
QString DebuggingStateWrapper::gas()
{
std::ostringstream ss;
ss << std::dec << m_state.gas;
return QString::fromStdString(ss.str());
}
QString DebuggingStateWrapper::debugStack()
{
QString stack;
for (auto i: m_state.stack)
stack.prepend(QString::fromStdString(prettyU256(i)) + "\n");
return stack;
}
QString DebuggingStateWrapper::debugStorage()
{
std::stringstream s;
for (auto const& i: m_state.storage)
s << "@" << prettyU256(i.first) << "&nbsp;&nbsp;&nbsp;&nbsp;" << prettyU256(i.second);
return QString::fromStdString(s.str());
}
QString DebuggingStateWrapper::debugMemory()
{
return QString::fromStdString(memDump(m_state.memory, 16, false));
}
QString DebuggingStateWrapper::debugCallData()
{
return QString::fromStdString(memDump(m_data, 16, false));
}
QStringList DebuggingStateWrapper::levels()
{
QStringList levelsStr;
for (unsigned i = 0; i <= m_state.levels.size(); ++i)
{
std::ostringstream out;
out << m_state.cur.abridged();
if (i)
out << " " << instructionInfo(m_state.inst).name << " @0x" << std::hex << m_state.curPC;
levelsStr.append(QString::fromStdString(out.str()));
}
return levelsStr;
}
QString DebuggingStateWrapper::headerInfo()
{
std::ostringstream ss;
ss << std::dec << " " << QApplication::tr("STEP").toStdString() << " : " << m_state.steps << " | PC: 0x" << std::hex << m_state.curPC << " : " << dev::eth::instructionInfo(m_state.inst).name << " | ADDMEM: " << std::dec << m_state.newMemSize << " " << QApplication::tr("words").toStdString() << " | " << QApplication::tr("COST").toStdString() << " : " << std::dec << m_state.gasCost << " | " << QApplication::tr("GAS").toStdString() << " : " << std::dec << m_state.gas;
return QString::fromStdString(ss.str());
}
QString DebuggingStateWrapper::endOfDebug()
{
if (m_state.gasCost > m_state.gas)
return QApplication::tr("OUT-OF-GAS");
else if (m_state.inst == Instruction::RETURN && m_state.stack.size() >= 2)
{
unsigned from = (unsigned)m_state.stack.back();
unsigned size = (unsigned)m_state.stack[m_state.stack.size() - 2];
unsigned o = 0;
bytes out(size, 0);
for (; o < size && from + o < m_state.memory.size(); ++o)
out[o] = m_state.memory[from + o];
return QApplication::tr("RETURN") + " " + QString::fromStdString(dev::memDump(out, 16, false));
}
else if (m_state.inst == Instruction::STOP)
return QApplication::tr("STOP");
else if (m_state.inst == Instruction::SUICIDE && m_state.stack.size() >= 1)
return QApplication::tr("SUICIDE") + " 0x" + QString::fromStdString(toString(right160(m_state.stack.back())));
else
return QApplication::tr("EXCEPTION");
}

139
mix/DebuggingStateWrapper.h

@ -0,0 +1,139 @@
/*
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 DebuggingStateWrapper.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QStringList>
#include "libethereum/State.h"
#include "libethereum/Executive.h"
#include "libdevcore/Common.h"
namespace dev
{
namespace mix
{
struct DebuggingState
{
uint64_t steps;
dev::Address cur;
dev::u256 curPC;
dev::eth::Instruction inst;
dev::bigint newMemSize;
dev::u256 gas;
dev::u256s stack;
dev::bytes memory;
dev::bigint gasCost;
std::map<dev::u256, dev::u256> storage;
std::vector<DebuggingState const*> levels;
};
struct DebuggingContent
{
QList<DebuggingState> states;
bytes executionCode;
bytesConstRef executionData;
bool contentAvailable;
QString message;
};
/**
* @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array.
*/
class HumanReadableCode: public QObject
{
Q_OBJECT
Q_PROPERTY(QString line READ line)
Q_PROPERTY(int processIndex READ processIndex)
public:
HumanReadableCode(QString _line, int _processIndex, QObject* _parent): QObject(_parent), m_line(_line), m_processIndex(_processIndex) {}
QString line() { return m_line; }
int processIndex() { return m_processIndex; }
private:
QString m_line;
int m_processIndex;
};
/**
* @brief Publish QMap type to QML.
*/
class QQMLMap: public QObject
{
Q_OBJECT
public:
QQMLMap(QMap<int, int> _map, QObject* _parent): QObject(_parent), m_map(_map) { }
Q_INVOKABLE int getValue(int _key) { return m_map.value(_key); }
private:
QMap<int, int> m_map;
};
/**
* @brief Wrap DebuggingState in QObject
*/
class DebuggingStateWrapper: public QObject
{
Q_OBJECT
Q_PROPERTY(int step READ step)
Q_PROPERTY(int curPC READ curPC)
Q_PROPERTY(QString gasCost READ gasCost)
Q_PROPERTY(QString gas READ gas)
Q_PROPERTY(QString gasLeft READ gasLeft)
Q_PROPERTY(QString debugStack READ debugStack)
Q_PROPERTY(QString debugStorage READ debugStorage)
Q_PROPERTY(QString debugMemory READ debugMemory)
Q_PROPERTY(QString debugCallData READ debugCallData)
Q_PROPERTY(QString headerInfo READ headerInfo)
Q_PROPERTY(QString endOfDebug READ endOfDebug)
Q_PROPERTY(QStringList levels READ levels)
public:
DebuggingStateWrapper(bytes _code, bytes _data, QObject* _parent): QObject(_parent), m_code(_code), m_data(_data) {}
int step() { return (int)m_state.steps; }
int curPC() { return (int)m_state.curPC; }
QString gasLeft();
QString gasCost();
QString gas();
QString debugStack();
QString debugStorage();
QString debugMemory();
QString debugCallData();
QString headerInfo();
QString endOfDebug();
QStringList levels();
DebuggingState state() { return m_state; }
void setState(DebuggingState _state) { m_state = _state; }
static std::tuple<QList<QObject*>, QQMLMap*> getHumanReadableCode(bytes const& _code, QObject* _objUsedAsParent);
private:
DebuggingState m_state;
bytes m_code;
bytes m_data;
};
}
}

50
mix/Extension.cpp

@ -21,24 +21,58 @@
#include <QDebug> #include <QDebug>
#include <libevm/VM.h> #include <libevm/VM.h>
#include "Extension.h" #include "Extension.h"
#include "ApplicationCtx.h" #include "AppContext.h"
using namespace dev; using namespace dev;
using namespace dev::mix; using namespace dev::mix;
void Extension::addContentOn(QObject* _tabView) Extension::Extension()
{
init();
}
Extension::Extension(ExtensionDisplayBehavior _displayBehavior)
{
init();
m_displayBehavior = _displayBehavior;
}
void Extension::init()
{
m_ctx = AppContext::getInstance();
m_appEngine = m_ctx->appEngine();
}
void Extension::addTabOn(QObject* _view)
{ {
if (contentUrl() == "") if (contentUrl() == "")
return; return;
QVariant returnValue; QVariant returnValue;
QQmlComponent* component = new QQmlComponent( QQmlComponent* component = new QQmlComponent(
ApplicationCtx::getInstance()->appEngine(), AppContext::getInstance()->appEngine(),
QUrl(this->contentUrl()), _tabView); QUrl(contentUrl()), _view);
QMetaObject::invokeMethod(_tabView, "addTab", QMetaObject::invokeMethod(_view, "addTab",
Q_RETURN_ARG(QVariant, returnValue), Q_RETURN_ARG(QVariant, returnValue),
Q_ARG(QVariant, this->title()), Q_ARG(QVariant, this->title()),
Q_ARG(QVariant, QVariant::fromValue(component))); Q_ARG(QVariant, QVariant::fromValue(component)));
m_view = qvariant_cast<QObject*>(returnValue); m_view = qvariant_cast<QObject*>(returnValue);
} }
void Extension::addContentOn(QObject* _view)
{
Q_UNUSED(_view);
if (m_displayBehavior == ExtensionDisplayBehavior::ModalDialog)
{
QQmlComponent component(AppContext::getInstance()->appEngine(), QUrl(contentUrl()));
QObject* dialog = component.create();
QObject* dialogWin = AppContext::getInstance()->appEngine()->rootObjects().at(0)->findChild<QObject*>("dialog", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(dialogWin, "close");
dialogWin->setProperty("contentItem", QVariant::fromValue(dialog));
dialogWin->setProperty("title", title());
QMetaObject::invokeMethod(dialogWin, "open");
}
//TODO add more view type.
}

23
mix/Extension.h

@ -21,26 +21,43 @@
#include <QApplication> #include <QApplication>
#include <QQmlComponent> #include <QQmlComponent>
#include "AppContext.h"
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
enum ExtensionDisplayBehavior
{
Tab,
ModalDialog
};
class Extension: public QObject class Extension: public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Extension() {} Extension();
Extension(ExtensionDisplayBehavior _displayBehavior);
virtual QString contentUrl() const { return ""; } virtual QString contentUrl() const { return ""; }
virtual QString title() const { return ""; } virtual QString title() const { return ""; }
virtual void start() const {} virtual void start() const {}
void addContentOn(QObject* tabView); void addContentOn(QObject* _tabView);
void addTabOn(QObject* _view);
void setDisplayBehavior(ExtensionDisplayBehavior _displayBehavior) { m_displayBehavior = _displayBehavior; }
ExtensionDisplayBehavior getDisplayBehavior() { return m_displayBehavior; }
protected: protected:
QObject* m_view; QObject* m_view;
ExtensionDisplayBehavior m_displayBehavior;
AppContext* m_ctx;
QQmlApplicationEngine* m_appEngine;
private:
void init();
}; };
} }

29
mix/ApplicationCtx.cpp → mix/KeyEventManager.cpp

@ -1,36 +1,41 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file ApplicationCtx.cpp /** @file KeyEventManager.cpp
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com
* @date 2014 * @date 2014
* Provide an access to the current QQmlApplicationEngine which is used to add QML file on the fly. * Used as an event handler for all classes which need keyboard interactions.
* In the future this class can be extended to add more variable related to the context of the application. * Can be improve by adding the possibility to register to a specific key.
*/ */
#include <QQmlApplicationEngine> #include <QDebug>
#include "ApplicationCtx.h" #include <QKeySequence>
using namespace dev::mix; #include "KeyEventManager.h"
ApplicationCtx* ApplicationCtx::Instance = nullptr; void KeyEventManager::registerEvent(const QObject* _receiver, const char* _slot)
{
QObject::connect(this, SIGNAL(onKeyPressed(int)), _receiver, _slot);
}
QQmlApplicationEngine* ApplicationCtx::appEngine() void KeyEventManager::unRegisterEvent(QObject* _receiver)
{ {
return m_applicationEngine; QObject::disconnect(_receiver);
} }
void ApplicationCtx::setApplicationContext(QQmlApplicationEngine* _engine) void KeyEventManager::keyPressed(QVariant _event)
{ {
if (Instance == nullptr) emit onKeyPressed(_event.toInt());
Instance = new ApplicationCtx(_engine);
} }

42
mix/KeyEventManager.h

@ -0,0 +1,42 @@
/*
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 KeyEventManager.h
* @author Yann yann@ethdev.com
* @date 2014
* use as an event handler for all classes which need keyboard interactions
*/
#pragma once
#include <QObject>
class KeyEventManager: public QObject
{
Q_OBJECT
public:
KeyEventManager() {}
void registerEvent(const QObject* _receiver, const char* _slot);
void unRegisterEvent(QObject* _receiver);
signals:
void onKeyPressed(int);
public slots:
void keyPressed(QVariant _event);
};

2
mix/MixApplication.cpp

@ -23,7 +23,7 @@
#include "MixApplication.h" #include "MixApplication.h"
using namespace dev::mix; using namespace dev::mix;
MixApplication::MixApplication(int _argc, char *_argv[]): QApplication(_argc, _argv) MixApplication::MixApplication(int _argc, char* _argv[]): QApplication(_argc, _argv)
{ {
} }

1
mix/MixApplication.h

@ -27,7 +27,6 @@
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {

40
mix/TransactionBuilder.cpp

@ -0,0 +1,40 @@
/*
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 TransactionBuilder.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include "libethereum/Executive.h"
#include "libdevcore/CommonJS.h"
#include "libdevcore/Common.h"
#include "AppContext.h"
#include "TransactionBuilder.h"
using namespace dev::mix;
using namespace dev::eth;
using namespace dev;
Transaction TransactionBuilder::getCreationTransaction(u256 _value, u256 _gasPrice, u256 _gas,
bytes _data, u256 _nonce, Secret _secret) const
{
return Transaction(_value, _gasPrice, _gas, _data, _nonce, _secret);
}
Transaction TransactionBuilder::getBasicTransaction(u256 _value, u256 _gasPrice, u256 _gas,
QString _address, bytes _data, u256 _nonce, Secret _secret) const
{
return Transaction(_value, _gasPrice, _gas, dev::fromString(_address.toStdString()), _data, _nonce, _secret);
}

43
mix/TransactionBuilder.h

@ -0,0 +1,43 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionBuilder.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QString>
#include "libdevcore/Common.h"
#include "libethereum/Transaction.h"
namespace dev
{
namespace mix
{
class TransactionBuilder
{
public:
TransactionBuilder() {}
dev::eth::Transaction getBasicTransaction(dev::u256 _value, dev::u256 _gasPrice, dev::u256 _gas,
QString address, bytes _data, dev::u256 _nonce, Secret _secret) const;
dev::eth::Transaction getCreationTransaction(dev::u256 _value, dev::u256 _gasPrice, dev::u256 _gas,
dev::bytes _data, dev::u256 _nonce, Secret _secret) const;
};
}
}

9
mix/main.cpp

@ -24,7 +24,7 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQuickItem> #include <QQuickItem>
#include "CodeEditorExtensionManager.h" #include "CodeEditorExtensionManager.h"
#include "ApplicationCtx.h" #include "AppContext.h"
#include "MixApplication.h" #include "MixApplication.h"
using namespace dev::mix; using namespace dev::mix;
@ -33,8 +33,11 @@ int main(int _argc, char *_argv[])
QApplication app(_argc, _argv); QApplication app(_argc, _argv);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
QQmlApplicationEngine* engine = new QQmlApplicationEngine(); QQmlApplicationEngine* engine = new QQmlApplicationEngine();
ApplicationCtx::setApplicationContext(engine); AppContext::setApplicationContext(engine);
QObject::connect(&app, SIGNAL(lastWindowClosed()), ApplicationCtx::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff QObject::connect(&app, SIGNAL(lastWindowClosed()), AppContext::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
engine->load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); engine->load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
AppContext::getInstance()->initKeyEventManager(); //has to be called after the loading of the main view.
return app.exec(); return app.exec();
} }

3
mix/qml.qrc

@ -4,5 +4,8 @@
<file>qml/main.qml</file> <file>qml/main.qml</file>
<file>qml/MainContent.qml</file> <file>qml/MainContent.qml</file>
<file>qml/TabStyle.qml</file> <file>qml/TabStyle.qml</file>
<file>qml/Debugger.qml</file>
<file>qml/js/Debugger.js</file>
<file>qml/BasicMessage.qml</file>
</qresource> </qresource>
</RCC> </RCC>

20
mix/qml/BasicMessage.qml

@ -0,0 +1,20 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
Rectangle {
anchors.fill: parent
color: "lightgrey"
Label
{
width: parent.width
height: parent.height
horizontalAlignment: "AlignHCenter"
verticalAlignment: "AlignVCenter"
objectName: "messageContent"
id: messageTxt
text: ""
}
}

240
mix/qml/Debugger.qml

@ -0,0 +1,240 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import "js/Debugger.js" as Debugger
Rectangle {
anchors.fill: parent;
color: "lightgrey"
Rectangle {
color: "transparent"
id: headerInfo
width: parent.width
height: 30
anchors.top: parent.top
Label {
anchors.centerIn: parent
font.family: "Verdana"
font.pointSize: 9
font.italic: true
id: headerInfoLabel
}
}
Keys.onPressed: {
if (event.key === Qt.Key_F10)
Debugger.moveSelection(1);
else if (event.key === Qt.Key_F9)
Debugger.moveSelection(-1);
}
Rectangle {
color: "transparent"
id: stateListContainer
focus: true
anchors.topMargin: 10
anchors.top: headerInfo.bottom
anchors.left: parent.left
height: parent.height - 30
width: parent.width * 0.5
ListView {
anchors.top: parent.top
height: parent.height * 0.60
width: 200
anchors.horizontalCenter: parent.horizontalCenter
id: statesList
Component.onCompleted: Debugger.init();
model: humanReadableExecutionCode
delegate: renderDelegate
highlight: highlightBar
highlightFollowsCurrentItem: true
}
Component {
id: highlightBar
Rectangle {
height: statesList.currentItem.height
width: statesList.currentItem.width
border.color: "orange"
border.width: 1
Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } }
}
}
Component {
id: renderDelegate
Item {
id: wrapperItem
height: 20
width: parent.width
Text {
anchors.centerIn: parent
text: line
font.pointSize: 9
}
}
}
Rectangle {
id: callStackPanel
anchors.top: statesList.bottom
height: parent.height * 0.35
width: parent.width
anchors.topMargin: 15
color: "transparent"
Label {
id: callStackLabel
anchors.bottomMargin: 10
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
width: parent.width
height: 15
text: "callstack"
}
ListView {
height: parent.height - 15
width: 200
anchors.top: callStackLabel.bottom
anchors.horizontalCenter: parent.horizontalCenter
id: levelList
delegate: Component {
Item {
Text {
font.family: "Verdana"
font.pointSize: 8
text: modelData
}
}
}
}
}
}
Rectangle {
color: "transparent"
anchors.topMargin: 5
anchors.bottomMargin: 10
anchors.rightMargin: 10
height: parent.height - 30
width: parent.width * 0.5
anchors.right: parent.right
anchors.top: headerInfo.bottom
anchors.bottom: parent.bottom
Rectangle {
id: debugStack
anchors.top: parent.top
width: parent.width
height: parent.height * 0.25
color: "transparent"
Label {
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
width: parent.width
height: 15
anchors.top : parent.top
text: "debug stack"
}
TextArea {
anchors.bottom: parent.bottom
width: parent.width
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
height: parent.height - 15
id:debugStackTxt
readOnly: true;
}
}
Rectangle {
id: debugMemory
anchors.top: debugStack.bottom
width: parent.width
height: parent.height * 0.25
color: "transparent"
Label {
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
width: parent.width
height: 15
anchors.top : parent.top
text: "debug memory"
}
TextArea {
anchors.bottom: parent.bottom
width: parent.width
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
height: parent.height - 15
id: debugMemoryTxt
readOnly: true;
}
}
Rectangle {
id: debugStorage
anchors.top: debugMemory.bottom
width: parent.width
height: parent.height * 0.25
color: "transparent"
Label {
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
width: parent.width
height: 15
anchors.top : parent.top
text: "debug storage"
}
TextArea {
anchors.bottom: parent.bottom
width: parent.width
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
height: parent.height - 15
id:debugStorageTxt
readOnly: true;
}
}
Rectangle {
id: debugCallData
anchors.top: debugStorage.bottom
width: parent.width
height: parent.height * 0.25
color: "transparent"
Label {
horizontalAlignment: "AlignHCenter"
font.family: "Verdana"
font.pointSize: 8
font.letterSpacing: 2
width: parent.width
height: 15
anchors.top : parent.top
text: "debug calldata"
}
TextArea {
anchors.bottom: parent.bottom
width: parent.width
height: parent.height - 15
id: debugCallDataTxt
readOnly: true;
}
}
}
}

22
mix/qml/MainContent.qml

@ -1,10 +1,18 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.2
import QtQuick.Layouts 1.0 import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.2
import CodeEditorExtensionManager 1.0 import CodeEditorExtensionManager 1.0
Rectangle { Rectangle {
objectName: "mainContent"
signal keyPressed(variant event)
focus: true
Keys.enabled: true
Keys.onPressed:
{
root.keyPressed(event.key);
}
anchors.fill: parent anchors.fill: parent
height: parent.height height: parent.height
width: parent.width; width: parent.width;
@ -26,11 +34,11 @@ Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
tabChangesFocus: false tabChangesFocus: false
Keys.onPressed: { Keys.onPressed: {
if (event.key === Qt.Key_Tab) { if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t"); codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true; event.accepted = true;
}
} }
}
} }
} }
Rectangle { Rectangle {
@ -46,7 +54,7 @@ Rectangle {
style: TabStyle {} style: TabStyle {}
} }
} }
CodeEditorExtensionManager{ CodeEditorExtensionManager {
tabView: contextualTabs tabView: contextualTabs
editor: codeEditor editor: codeEditor
} }

62
mix/qml/js/Debugger.js

@ -0,0 +1,62 @@
//humanReadableExecutionCode => contain human readable code.
//debugStates => contain all debug states.
//bytesCodeMapping => mapping between humanReadableExecutionCode and bytesCode.
//statesList => ListView
var currentSelectedState = null;
function init()
{
currentSelectedState = 0;
select(currentSelectedState);
}
function moveSelection(incr)
{
if (currentSelectedState + incr >= 0)
{
if (currentSelectedState + incr < debugStates.length)
{
select(currentSelectedState + incr);
}
else
{
endOfDebug();
}
}
}
function select(stateIndex)
{
var state = debugStates[stateIndex];
var codeStr = bytesCodeMapping.getValue(state.curPC);
highlightSelection(codeStr);
currentSelectedState = codeStr;
completeCtxInformation(state);
levelList.model = state.levels;
levelList.update();
}
function highlightSelection(index)
{
console.log(index);
statesList.currentIndex = index;
}
function completeCtxInformation(state)
{
debugStackTxt.text = state.debugStack;
debugStorageTxt.text = state.debugStorage;
debugMemoryTxt.text = state.debugMemory;
debugCallDataTxt.text = state.debugCallData;
headerInfoLabel.text = state.headerInfo
}
function endOfDebug()
{
var state = debugStates[debugStates.length - 1];
debugStorageTxt.text = "";
debugCallDataTxt.text = "";
debugStackTxt.text = "";
debugMemoryTxt.text = state.endOfDebug;
headerInfoLabel.text = "EXIT | GAS: " + state.gasLeft;
}

40
mix/qml/main.qml

@ -1,15 +1,22 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import CodeEditorExtensionManager 1.0 import CodeEditorExtensionManager 1.0
ApplicationWindow { ApplicationWindow {
id: mainApplication
visible: true visible: true
width: 1000 x: Screen.width / 2 - width / 2
height: 480 y: Screen.height / 2 - height / 2
width: 1200
height: 600
minimumWidth: 400 minimumWidth: 400
minimumHeight: 300 minimumHeight: 300
title: qsTr("mix") title: qsTr("mix")
menuBar: MenuBar { menuBar: MenuBar {
Menu { Menu {
title: qsTr("File") title: qsTr("File")
@ -19,6 +26,33 @@ ApplicationWindow {
} }
} }
} }
MainContent{
MainContent {
}
Dialog {
x: mainApplication.x + (mainApplication.width - width) / 2
y: mainApplication.y + (mainApplication.height - height) / 2
objectName: "dialog"
id: dialog
height: 400
width: 700
modality: Qt.WindowModal
contentItem: Rectangle {
objectName: "dialogContent"
}
}
Dialog {
x: mainApplication.x + (mainApplication.width - width) / 2
y: mainApplication.y + (mainApplication.height - height) / 2
objectName: "messageDialog"
id: messageDialog
height: 150
width: 200
modality: Qt.WindowModal
contentItem: Rectangle {
objectName: "messageContent"
}
} }
} }

Loading…
Cancel
Save