diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 122085225..671322332 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -65,7 +65,7 @@ static void initUnits(QComboBox* _b) _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) { @@ -149,7 +149,7 @@ Main::Main(QWidget *parent) : statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->mineStatus); statusBar()->addPermanentWidget(ui->blockCount); - + 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"})); @@ -173,17 +173,17 @@ Main::Main(QWidget *parent) : connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qweb)); connect(m_qweb, SIGNAL(onNewId(QString)), this, SLOT(addNewId(QString))); }); - + connect(ui->webView, &QWebView::loadFinished, [=]() { m_qweb->poll(); }); - + connect(ui->webView, &QWebView::titleChanged, [=]() { ui->tabWidget->setTabText(0, ui->webView->title()); }); - + readSettings(); installWatches(); startTimer(100); @@ -1067,7 +1067,7 @@ void Main::timerEvent(QTimerEvent*) // 7/18, Alex: aggregating timers, prelude to better threading? // Runs much faster on slower dual-core processors static int interval = 100; - + // refresh mining every 200ms if (interval / 100 % 2 == 0) refreshMining(); @@ -1093,7 +1093,7 @@ void Main::timerEvent(QTimerEvent*) } else interval += 100; - + if (m_qweb) m_qweb->poll(); @@ -1711,7 +1711,7 @@ void Main::on_net_triggered() { ui->port->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()) n += "/" + ui->clientName->text().toStdString(); n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index b39b65106..5c196792d 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -69,7 +69,7 @@ struct WorldState class Main : public QMainWindow { Q_OBJECT - + public: explicit Main(QWidget *parent = 0); ~Main(); @@ -79,7 +79,7 @@ public: std::shared_ptr whisper() const { return m_webThree->whisper(); } QList owned() const { return m_myIdentities + m_myKeys; } - + public slots: void load(QString _file); void note(QString _entry); @@ -146,7 +146,7 @@ private slots: void on_debugDumpState_triggered(int _add = 1); void on_debugDumpStatePre_triggered(); void on_refresh_triggered(); - void on_usePrivate_triggered(); + void on_usePrivate_triggered(); void on_enableOptimizer_triggered(); void on_turboMining_triggered(); void on_go_triggered(); @@ -259,4 +259,6 @@ private: std::unique_ptr m_qwebConnector; std::unique_ptr m_server; QWebThree* m_qweb = nullptr; + + static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr); }; diff --git a/libdevcore/CommonJS.cpp b/libdevcore/CommonJS.cpp index c09a5b565..ad3ade71f 100644 --- a/libdevcore/CommonJS.cpp +++ b/libdevcore/CommonJS.cpp @@ -54,4 +54,64 @@ bytes unpadded(bytes _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(); +} + + } diff --git a/libdevcore/CommonJS.h b/libdevcore/CommonJS.h index 80e1a9ca1..8b96b2e68 100644 --- a/libdevcore/CommonJS.h +++ b/libdevcore/CommonJS.h @@ -35,12 +35,12 @@ template std::string toJS(FixedHash const& _h) { return "0x" + toHex(_h.ref()); } - + template std::string toJS(boost::multiprecision::number> const& _n) { return "0x" + toHex(toCompactBigEndian(_n)); } - + inline std::string toJS(dev::bytes const& _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 padded(bytes _b, unsigned _l); bytes unpadded(bytes _s); +std::string prettyU256(u256 _n); +std::string fromRaw(h256 _n, unsigned* _inc = nullptr); +Address fromString(std::string const& _a); template FixedHash jsToFixed(std::string const& _s) { diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 729014bef..8ccccddf8 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -113,8 +113,8 @@ private: bool m_excepted = false; ///< True if the VM execution resulted in an exception. u256 m_endGas; ///< The final amount of gas for the transaction. - Transaction m_t; ///< The original transaction in the case that setup() was called. - LogEntries m_logs; ///< The log entries created by this transaction. Only valid + Transaction m_t; ///< The original transaction. Set by setup(). + LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). }; } diff --git a/libevmcore/Instruction.h b/libevmcore/Instruction.h index f8a0478f1..555dbf0ea 100644 --- a/libevmcore/Instruction.h +++ b/libevmcore/Instruction.h @@ -173,6 +173,24 @@ enum class Instruction: uint8_t 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 inline Instruction pushInstruction(unsigned _number) { diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp new file mode 100644 index 000000000..5b8b76139 --- /dev/null +++ b/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 . +*/ +/** @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 +#include +#include +#include +#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(_engine); + m_keyEventManager = std::unique_ptr(new KeyEventManager()); + m_webThree = std::unique_ptr(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("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("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message); + QObject* dialogWin = AppContext::getInstance()->appEngine()->rootObjects().at(0)->findChild("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"); +} diff --git a/mix/ApplicationCtx.h b/mix/AppContext.h similarity index 56% rename from mix/ApplicationCtx.h rename to mix/AppContext.h index 37166ea05..0016ab3be 100644 --- a/mix/ApplicationCtx.h +++ b/mix/AppContext.h @@ -14,37 +14,48 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file ApplicationCtx.h +/** @file AppContext.h * @author Yann yann@ethdev.com * @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. + * For now AppContext provides reference to: + * - QQmlApplicationEngine + * - dev::WebThreeDirect (and dev::eth::Client) + * - KeyEventManager */ #pragma once #include +#include "libwebthree/WebThree.h" +#include "KeyEventManager.h" namespace dev { - namespace mix { -class ApplicationCtx: public QObject +class AppContext: public QObject { Q_OBJECT public: - ApplicationCtx(QQmlApplicationEngine* _engine) { m_applicationEngine = _engine; } - ~ApplicationCtx() { delete m_applicationEngine; } - static ApplicationCtx* getInstance() { return Instance; } + AppContext(QQmlApplicationEngine* _engine); + ~AppContext() {} + static AppContext* getInstance() { return Instance; } static void setApplicationContext(QQmlApplicationEngine* _engine); QQmlApplicationEngine* appEngine(); + dev::eth::Client* getEthereumClient(); + void initKeyEventManager(); + KeyEventManager* getKeyEventManager(); + void displayMessageDialog(QString _title, QString _message); private: - static ApplicationCtx* Instance; - QQmlApplicationEngine* m_applicationEngine; + static AppContext* Instance; + std::unique_ptr m_applicationEngine; + std::unique_ptr m_webThree; + std::unique_ptr m_keyEventManager; public slots: void quitApplication() { delete Instance; } diff --git a/mix/AssemblyDebuggerCtrl.cpp b/mix/AssemblyDebuggerCtrl.cpp new file mode 100644 index 000000000..678f94d77 --- /dev/null +++ b/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 . +*/ +/** @file AssemblyDebuggerCtrl.h + * @author Yann yann@ethdev.com + * @date 2014 + * display opcode debugging. + */ + +#include +#include +#include +#include +#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(); + qRegisterMetaType(); + connect(this, SIGNAL(dataAvailable(bool, DebuggingStatusResult, QList, AssemblyDebuggerData)), + this, SLOT(updateGUI(bool, DebuggingStatusResult, QList, AssemblyDebuggerData)), Qt::QueuedConnection); + m_modelDebugger = std::unique_ptr(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 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 _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")); +} diff --git a/mix/AssemblyDebuggerCtrl.h b/mix/AssemblyDebuggerCtrl.h new file mode 100644 index 000000000..e94cc4ce9 --- /dev/null +++ b/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 . +*/ +/** @file AssemblyDebuggerCtrl.h + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#pragma once + +#include +#include "QTextDocument" +#include "Extension.h" +#include "ConstantCompilationModel.h" +#include "AssemblyDebuggerModel.h" +#include "AppContext.h" + +using AssemblyDebuggerData = std::tuple, 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 m_modelDebugger; + QTextDocument* m_doc; + +public slots: + void keyPressed(int); + void updateGUI(bool success, DebuggingStatusResult reason, QList _wStates = QList(), AssemblyDebuggerData _code = AssemblyDebuggerData()); + +signals: + void dataAvailable(bool success, DebuggingStatusResult reason, QList _wStates = QList(), AssemblyDebuggerData _code = AssemblyDebuggerData()); + +}; + +} + +} diff --git a/mix/AssemblyDebuggerModel.cpp b/mix/AssemblyDebuggerModel.cpp new file mode 100644 index 000000000..151c14773 --- /dev/null +++ b/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 . +*/ +/** @file AssemblyDebuggerModel.h + * @author Yann yann@ethdev.com + * @date 2014 + * used as a model to debug contract assembly code. + */ + +#include +#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(new Executive(m_executiveState, 0)); +} + +DebuggingContent AssemblyDebuggerModel::getContractInitiationDebugStates(dev::bytesConstRef _rawTransaction) +{ + QList states; + Transaction tr(_rawTransaction); + m_currentExecution->create(tr.sender(), tr.value(), tr.gasPrice(), tr.gas(), &tr.data(), tr.sender()); + std::vector 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; +} diff --git a/mix/AssemblyDebuggerModel.h b/mix/AssemblyDebuggerModel.h new file mode 100644 index 000000000..3ed6818bc --- /dev/null +++ b/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 . +*/ +/** @file AssemblyDebuggerModel.h + * @author Yann yann@ethdev.com + * @date 2014 + * serves as a model to debug contract assembly code. + */ + +#pragma once + +#include +#include +#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 m_currentExecution; + dev::eth::State m_executiveState; +}; + +} + +} diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index c778d466f..1b2a9cc3c 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -27,7 +27,8 @@ #include #include #include "ConstantCompilationCtrl.h" -#include "ApplicationCtx.h" +#include "AssemblyDebuggerCtrl.h" +#include "AppContext.h" #include "CodeEditorExtensionManager.h" using namespace dev::mix; @@ -58,22 +59,27 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) void CodeEditorExtensionManager::initExtensions() { - //only one for now - std::shared_ptr constantCompilation = std::make_shared(m_doc); - if (constantCompilation.get()->contentUrl() != "") + initExtension(std::make_shared(m_doc)); + initExtension(std::make_shared(m_doc)); +} + +void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) +{ + if (!_ext->contentUrl().isEmpty()) { try { - constantCompilation.get()->addContentOn(m_tabView); + if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::Tab) + _ext->addTabOn(m_tabView); } catch (...) { - qDebug() << "Exception when adding content into view."; + qDebug() << "Exception when adding tab into view."; return; } } - constantCompilation.get()->start(); - m_features.append(constantCompilation); + _ext->start(); + m_features.append(_ext); } void CodeEditorExtensionManager::setEditor(QQuickItem* _editor) diff --git a/mix/CodeEditorExtensionManager.h b/mix/CodeEditorExtensionManager.h index 2b8402bf2..84ce9af92 100644 --- a/mix/CodeEditorExtensionManager.h +++ b/mix/CodeEditorExtensionManager.h @@ -30,7 +30,6 @@ namespace dev { - namespace mix { @@ -45,12 +44,13 @@ public: CodeEditorExtensionManager() {} ~CodeEditorExtensionManager(); void initExtensions(); + void initExtension(std::shared_ptr); void setEditor(QQuickItem*); void setTabView(QQuickItem*); private: QQuickItem* m_editor; - QVector> m_features; + QVector> m_features; QQuickItem* m_tabView; QTextDocument* m_doc; void loadEditor(QQuickItem*); diff --git a/mix/ConstantCompilationCtrl.cpp b/mix/ConstantCompilationCtrl.cpp index 06b9c0284..3ccd87f77 100644 --- a/mix/ConstantCompilationCtrl.cpp +++ b/mix/ConstantCompilationCtrl.cpp @@ -30,15 +30,10 @@ #include "ConstantCompilationModel.h" using namespace dev::mix; -ConstantCompilationCtrl::ConstantCompilationCtrl(QTextDocument* _doc) +ConstantCompilationCtrl::ConstantCompilationCtrl(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::Tab) { m_editor = _doc; - m_compilationModel = new ConstantCompilationModel(); -} - -ConstantCompilationCtrl::~ConstantCompilationCtrl() -{ - delete m_compilationModel; + m_compilationModel = std::unique_ptr(new ConstantCompilationModel()); } QString ConstantCompilationCtrl::contentUrl() const @@ -48,7 +43,7 @@ QString ConstantCompilationCtrl::contentUrl() const QString ConstantCompilationCtrl::title() const { - return "compiler"; + return QApplication::tr("compiler"); } void ConstantCompilationCtrl::start() const @@ -64,7 +59,7 @@ void ConstantCompilationCtrl::compile() resetOutPut(); return; } - CompilerResult res = m_compilationModel->compile(m_editor->toPlainText()); + CompilerResult res = m_compilationModel->compile(m_editor->toPlainText().replace("\t", " ")); writeOutPut(res); } @@ -85,13 +80,13 @@ void ConstantCompilationCtrl::writeOutPut(CompilerResult const& _res) status->setProperty("text", "succeeded"); status->setProperty("color", "green"); content->setProperty("text", _res.hexCode); - qDebug() << QString("compile succeeded " + _res.hexCode); + qDebug() << QString(QApplication::tr("compile succeeded") + " " + _res.hexCode); } else { status->setProperty("text", "failure"); status->setProperty("color", "red"); content->setProperty("text", _res.comment); - qDebug() << QString("compile failed " + _res.comment); + qDebug() << QString(QApplication::tr("compile failed") + " " + _res.comment); } } diff --git a/mix/ConstantCompilationCtrl.h b/mix/ConstantCompilationCtrl.h index e4661c800..7cde63a05 100644 --- a/mix/ConstantCompilationCtrl.h +++ b/mix/ConstantCompilationCtrl.h @@ -25,7 +25,6 @@ namespace dev { - namespace mix { @@ -35,18 +34,18 @@ class ConstantCompilationCtrl: public Extension public: ConstantCompilationCtrl(QTextDocument*); - ~ConstantCompilationCtrl(); + ~ConstantCompilationCtrl() {} void start() const override; QString title() const override; QString contentUrl() const override; private: QTextDocument* m_editor; - ConstantCompilationModel* m_compilationModel; + std::unique_ptr m_compilationModel; void writeOutPut(CompilerResult const&); void resetOutPut(); -public Q_SLOTS: +public slots: void compile(); }; diff --git a/mix/ConstantCompilationModel.cpp b/mix/ConstantCompilationModel.cpp index ea12a267c..0fd1309d0 100644 --- a/mix/ConstantCompilationModel.cpp +++ b/mix/ConstantCompilationModel.cpp @@ -14,12 +14,13 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file ApplicationCtx.h +/** @file ConstantCompilationModel.h * @author Yann yann@ethdev.com * @date 2014 * Ethereum IDE client. */ +#include #include #include #include @@ -42,19 +43,20 @@ CompilerResult ConstantCompilationModel::compile(QString _code) res.success = true; res.comment = "ok"; res.hexCode = QString::fromStdString(dev::eth::disassemble(m_data)); + res.bytes = m_data; } catch (dev::Exception const& _exception) { ostringstream error; solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler); res.success = false; - res.comment = QString::fromStdString(error.str()).toHtmlEscaped(); + res.comment = QString::fromStdString(error.str()); res.hexCode = ""; } catch (...) { res.success = false; - res.comment = "Uncaught exception."; + res.comment = QApplication::tr("Uncaught exception."); res.hexCode = ""; } return res; diff --git a/mix/ConstantCompilationModel.h b/mix/ConstantCompilationModel.h index 4a17853f6..4c161ec09 100644 --- a/mix/ConstantCompilationModel.h +++ b/mix/ConstantCompilationModel.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file ApplicationCtx.h +/** @file ConstantCompilationModel.h * @author Yann yann@ethdev.com * @date 2014 * Ethereum IDE client. @@ -22,11 +22,11 @@ #pragma once +#include #include namespace dev { - namespace mix { @@ -34,6 +34,7 @@ struct CompilerResult { QString hexCode; QString comment; + dev::bytes bytes; bool success; }; @@ -43,7 +44,7 @@ class ConstantCompilationModel public: ConstantCompilationModel() {} ~ConstantCompilationModel() {} - CompilerResult compile(QString code); + CompilerResult compile(QString _code); }; } diff --git a/mix/DebuggingStateWrapper.cpp b/mix/DebuggingStateWrapper.cpp new file mode 100644 index 000000000..36b098379 --- /dev/null +++ b/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 . +*/ +/** @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 +#include +#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, QQMLMap*> DebuggingStateWrapper::getHumanReadableCode(const bytes& _code, QObject* _objUsedAsParent) +{ + QList codeStr; + QMap 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) << "    " << 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"); +} diff --git a/mix/DebuggingStateWrapper.h b/mix/DebuggingStateWrapper.h new file mode 100644 index 000000000..d91d3d69c --- /dev/null +++ b/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 . +*/ +/** @file DebuggingStateWrapper.h + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#pragma once + +#include +#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 storage; + std::vector levels; +}; + +struct DebuggingContent +{ + QList 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 _map, QObject* _parent): QObject(_parent), m_map(_map) { } + Q_INVOKABLE int getValue(int _key) { return m_map.value(_key); } + +private: + QMap 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, QQMLMap*> getHumanReadableCode(bytes const& _code, QObject* _objUsedAsParent); + +private: + DebuggingState m_state; + bytes m_code; + bytes m_data; +}; + +} + +} diff --git a/mix/Extension.cpp b/mix/Extension.cpp index 5aeb0cc17..f9a8ad94d 100644 --- a/mix/Extension.cpp +++ b/mix/Extension.cpp @@ -21,24 +21,58 @@ #include #include #include "Extension.h" -#include "ApplicationCtx.h" +#include "AppContext.h" using namespace dev; 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() == "") return; QVariant returnValue; QQmlComponent* component = new QQmlComponent( - ApplicationCtx::getInstance()->appEngine(), - QUrl(this->contentUrl()), _tabView); + AppContext::getInstance()->appEngine(), + QUrl(contentUrl()), _view); - QMetaObject::invokeMethod(_tabView, "addTab", - Q_RETURN_ARG(QVariant, returnValue), - Q_ARG(QVariant, this->title()), - Q_ARG(QVariant, QVariant::fromValue(component))); + QMetaObject::invokeMethod(_view, "addTab", + Q_RETURN_ARG(QVariant, returnValue), + Q_ARG(QVariant, this->title()), + Q_ARG(QVariant, QVariant::fromValue(component))); m_view = qvariant_cast(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("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. +} + diff --git a/mix/Extension.h b/mix/Extension.h index f8fef0aa6..4d6f4f084 100644 --- a/mix/Extension.h +++ b/mix/Extension.h @@ -21,26 +21,43 @@ #include #include +#include "AppContext.h" namespace dev { - namespace mix { +enum ExtensionDisplayBehavior +{ + Tab, + ModalDialog +}; + + class Extension: public QObject { Q_OBJECT public: - Extension() {} + Extension(); + Extension(ExtensionDisplayBehavior _displayBehavior); virtual QString contentUrl() const { return ""; } virtual QString title() const { return ""; } 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: QObject* m_view; + ExtensionDisplayBehavior m_displayBehavior; + AppContext* m_ctx; + QQmlApplicationEngine* m_appEngine; + +private: + void init(); }; } diff --git a/mix/ApplicationCtx.cpp b/mix/KeyEventManager.cpp similarity index 54% rename from mix/ApplicationCtx.cpp rename to mix/KeyEventManager.cpp index f97478f3c..c32caadb8 100644 --- a/mix/ApplicationCtx.cpp +++ b/mix/KeyEventManager.cpp @@ -1,36 +1,41 @@ /* 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 . */ -/** @file ApplicationCtx.cpp +/** @file KeyEventManager.cpp * @author Yann yann@ethdev.com * @date 2014 - * Provide an 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. + * Used as an event handler for all classes which need keyboard interactions. + * Can be improve by adding the possibility to register to a specific key. */ -#include -#include "ApplicationCtx.h" -using namespace dev::mix; +#include +#include +#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) - Instance = new ApplicationCtx(_engine); + emit onKeyPressed(_event.toInt()); } diff --git a/mix/KeyEventManager.h b/mix/KeyEventManager.h new file mode 100644 index 000000000..f3343cc45 --- /dev/null +++ b/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 . +*/ +/** @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 + +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); +}; + diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index e67ca1b12..86507329c 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -23,7 +23,7 @@ #include "MixApplication.h" using namespace dev::mix; -MixApplication::MixApplication(int _argc, char *_argv[]): QApplication(_argc, _argv) +MixApplication::MixApplication(int _argc, char* _argv[]): QApplication(_argc, _argv) { } diff --git a/mix/MixApplication.h b/mix/MixApplication.h index fdc506268..d927f2e07 100644 --- a/mix/MixApplication.h +++ b/mix/MixApplication.h @@ -27,7 +27,6 @@ namespace dev { - namespace mix { diff --git a/mix/TransactionBuilder.cpp b/mix/TransactionBuilder.cpp new file mode 100644 index 000000000..68eecf10b --- /dev/null +++ b/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 . +*/ +/** @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); +} + diff --git a/mix/TransactionBuilder.h b/mix/TransactionBuilder.h new file mode 100644 index 000000000..9ad929d38 --- /dev/null +++ b/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 . +*/ +/** @file TransactionBuilder.h + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#pragma once + +#include +#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; +}; + +} + +} diff --git a/mix/main.cpp b/mix/main.cpp index 537941290..33a139f85 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -24,7 +24,7 @@ #include #include #include "CodeEditorExtensionManager.h" -#include "ApplicationCtx.h" +#include "AppContext.h" #include "MixApplication.h" using namespace dev::mix; @@ -33,8 +33,11 @@ int main(int _argc, char *_argv[]) QApplication app(_argc, _argv); qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); QQmlApplicationEngine* engine = new QQmlApplicationEngine(); - ApplicationCtx::setApplicationContext(engine); - QObject::connect(&app, SIGNAL(lastWindowClosed()), ApplicationCtx::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff + AppContext::setApplicationContext(engine); + QObject::connect(&app, SIGNAL(lastWindowClosed()), AppContext::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff + 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(); } diff --git a/mix/qml.qrc b/mix/qml.qrc index 267427ce5..2fa92d661 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -4,5 +4,8 @@ qml/main.qml qml/MainContent.qml qml/TabStyle.qml + qml/Debugger.qml + qml/js/Debugger.js + qml/BasicMessage.qml diff --git a/mix/qml/BasicMessage.qml b/mix/qml/BasicMessage.qml new file mode 100644 index 000000000..0678c9b54 --- /dev/null +++ b/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: "" + } +} diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml new file mode 100644 index 000000000..e895d1ada --- /dev/null +++ b/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; + } + } + } +} diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index bd4737c3b..1da48a1c8 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -1,10 +1,18 @@ import QtQuick 2.2 -import QtQuick.Controls 1.1 +import QtQuick.Controls 1.2 import QtQuick.Layouts 1.0 -import QtQuick.Controls.Styles 1.1 +import QtQuick.Controls.Styles 1.2 import CodeEditorExtensionManager 1.0 Rectangle { + objectName: "mainContent" + signal keyPressed(variant event) + focus: true + Keys.enabled: true + Keys.onPressed: + { + root.keyPressed(event.key); + } anchors.fill: parent height: parent.height width: parent.width; @@ -26,11 +34,11 @@ Rectangle { anchors.centerIn: parent tabChangesFocus: false Keys.onPressed: { - if (event.key === Qt.Key_Tab) { - codeEditor.insert(codeEditor.cursorPosition, "\t"); - event.accepted = true; + if (event.key === Qt.Key_Tab) { + codeEditor.insert(codeEditor.cursorPosition, "\t"); + event.accepted = true; + } } - } } } Rectangle { @@ -46,7 +54,7 @@ Rectangle { style: TabStyle {} } } - CodeEditorExtensionManager{ + CodeEditorExtensionManager { tabView: contextualTabs editor: codeEditor } diff --git a/mix/qml/js/Debugger.js b/mix/qml/js/Debugger.js new file mode 100644 index 000000000..5cd88d726 --- /dev/null +++ b/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; +} diff --git a/mix/qml/main.qml b/mix/qml/main.qml index 3553f7710..05b29eb62 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -1,15 +1,22 @@ import QtQuick 2.2 import QtQuick.Controls 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 ApplicationWindow { + id: mainApplication visible: true - width: 1000 - height: 480 + x: Screen.width / 2 - width / 2 + y: Screen.height / 2 - height / 2 + width: 1200 + height: 600 minimumWidth: 400 minimumHeight: 300 title: qsTr("mix") + menuBar: MenuBar { Menu { 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" + } } }