From f990a1ac632d6ff0067e8e14cdd234f49f0a54db Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 5 Dec 2014 10:17:42 +0100 Subject: [PATCH] mix IDE M1 first commit --- mix/ApplicationCtx.cpp | 62 +++++++- mix/ApplicationCtx.h | 19 ++- mix/AssemblyDebuggerCtrl.cpp | 84 +++++++++++ mix/AssemblyDebuggerCtrl.h | 55 +++++++ mix/AssemblyDebuggerModel.cpp | 111 ++++++++++++++ mix/AssemblyDebuggerModel.h | 49 ++++++ mix/CodeEditorExtensionManager.cpp | 22 ++- mix/CodeEditorExtensionManager.h | 3 +- mix/ConstantCompilationCtrl.cpp | 4 +- mix/ConstantCompilationModel.cpp | 1 + mix/ConstantCompilationModel.h | 2 + mix/DebuggingStateWrapper.cpp | 189 +++++++++++++++++++++++ mix/DebuggingStateWrapper.h | 133 +++++++++++++++++ mix/Extension.cpp | 29 +++- mix/Extension.h | 14 +- mix/KeyEventManager.cpp | 43 ++++++ mix/KeyEventManager.h | 42 ++++++ mix/TransactionBuilder.cpp | 92 ++++++++++++ mix/TransactionBuilder.h | 48 ++++++ mix/main.cpp | 3 + mix/qml.qrc | 3 + mix/qml/BasicMessage.qml | 20 +++ mix/qml/Debugger.qml | 231 +++++++++++++++++++++++++++++ mix/qml/MainContent.qml | 24 ++- mix/qml/js/Debugger.js | 63 ++++++++ mix/qml/main.qml | 40 ++++- 26 files changed, 1353 insertions(+), 33 deletions(-) create mode 100644 mix/AssemblyDebuggerCtrl.cpp create mode 100644 mix/AssemblyDebuggerCtrl.h create mode 100644 mix/AssemblyDebuggerModel.cpp create mode 100644 mix/AssemblyDebuggerModel.h create mode 100644 mix/DebuggingStateWrapper.cpp create mode 100644 mix/DebuggingStateWrapper.h create mode 100644 mix/KeyEventManager.cpp create mode 100644 mix/KeyEventManager.h create mode 100644 mix/TransactionBuilder.cpp create mode 100644 mix/TransactionBuilder.h create mode 100644 mix/qml/BasicMessage.qml create mode 100644 mix/qml/Debugger.qml create mode 100644 mix/qml/js/Debugger.js diff --git a/mix/ApplicationCtx.cpp b/mix/ApplicationCtx.cpp index f97478f3c..52e66dab7 100644 --- a/mix/ApplicationCtx.cpp +++ b/mix/ApplicationCtx.cpp @@ -14,23 +14,83 @@ /** @file ApplicationCtx.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. + * Provides 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. + * For now ApplicationCtx provides reference to: + * - QQmlApplicationEngine + * - dev::WebThreeDirect (and dev::eth::Client) + * - KeyEventManager */ +#include +#include +#include #include +#include "libdevcrypto/FileSystem.h" +#include "KeyEventManager.h" #include "ApplicationCtx.h" +using namespace dev; using namespace dev::mix; +using namespace dev::eth; ApplicationCtx* ApplicationCtx::Instance = nullptr; +ApplicationCtx::ApplicationCtx(QQmlApplicationEngine* _engine) +{ + m_applicationEngine = _engine; + m_keyEventManager = std::unique_ptr(); + m_webThree = std::unique_ptr(); + m_webThree.reset(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"})); + m_keyEventManager.reset(new KeyEventManager()); +} + +ApplicationCtx::~ApplicationCtx() +{ + delete m_applicationEngine; +} + QQmlApplicationEngine* ApplicationCtx::appEngine() { return m_applicationEngine; } +dev::eth::Client* ApplicationCtx::getEthereumClient() +{ + return m_webThree.get()->ethereum(); +} + +void ApplicationCtx::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* ApplicationCtx::getKeyEventManager() +{ + return m_keyEventManager.get(); +} + void ApplicationCtx::setApplicationContext(QQmlApplicationEngine* _engine) { if (Instance == nullptr) Instance = new ApplicationCtx(_engine); } + +void ApplicationCtx::displayMessageDialog(QString _title, QString _message) +{ + QQmlComponent component(m_applicationEngine, QUrl("qrc:/qml/BasicMessage.qml")); + QObject* dialog = component.create(); + dialog->findChild("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message); + QObject* dialogWin = ApplicationCtx::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/ApplicationCtx.h index 37166ea05..05e4f3bb3 100644 --- a/mix/ApplicationCtx.h +++ b/mix/ApplicationCtx.h @@ -16,14 +16,19 @@ */ /** @file ApplicationCtx.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 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. + * For now ApplicationCtx provides reference to: + * - QQmlApplicationEngine + * - dev::WebThreeDirect (and dev::eth::Client) + * - KeyEventManager */ #pragma once #include +#include "libwebthree/WebThree.h" +#include "KeyEventManager.h" namespace dev { @@ -36,15 +41,21 @@ class ApplicationCtx: public QObject Q_OBJECT public: - ApplicationCtx(QQmlApplicationEngine* _engine) { m_applicationEngine = _engine; } - ~ApplicationCtx() { delete m_applicationEngine; } + ApplicationCtx(QQmlApplicationEngine* _engine); + ~ApplicationCtx(); static ApplicationCtx* 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; + 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..e8b0391d8 --- /dev/null +++ b/mix/AssemblyDebuggerCtrl.cpp @@ -0,0 +1,84 @@ +/* + 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 "libethereum/Transaction.h" +#include "AssemblyDebuggerModel.h" +#include "AssemblyDebuggerCtrl.h" +#include "TransactionBuilder.h" +#include "KeyEventManager.h" +#include "ApplicationCtx.h" +#include "DebuggingStateWrapper.h" +using namespace dev::mix; + +AssemblyDebuggerCtrl::AssemblyDebuggerCtrl(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::ModalDialog) +{ + 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 "debugger"; +} + +void AssemblyDebuggerCtrl::start() const +{ + //start to listen on F5 + ApplicationCtx::getInstance()->getKeyEventManager()->registerEvent(this, SLOT(keyPressed(int))); +} + +void AssemblyDebuggerCtrl::keyPressed(int _key) +{ + if (_key == Qt::Key_F5) + { + if (!m_modelDebugger->compile(m_doc->toPlainText())) + { + ApplicationCtx::getInstance()->displayMessageDialog("debugger","compilation failed"); + 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()); + s->setState(debuggingContent.states.at(i)); + wStates.append(s); + } + std::tuple, QQMLMap*> code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode); + ApplicationCtx::getInstance()->appEngine()->rootContext()->setContextProperty("debugStates", QVariant::fromValue(wStates)); + ApplicationCtx::getInstance()->appEngine()->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(code))); + ApplicationCtx::getInstance()->appEngine()->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(code))); + this->addContentOn(this); + }; +} diff --git a/mix/AssemblyDebuggerCtrl.h b/mix/AssemblyDebuggerCtrl.h new file mode 100644 index 000000000..a1642cc14 --- /dev/null +++ b/mix/AssemblyDebuggerCtrl.h @@ -0,0 +1,55 @@ +/* + 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" + +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 Q_SLOTS: + void keyPressed(int); +}; + +} + +} diff --git a/mix/AssemblyDebuggerModel.cpp b/mix/AssemblyDebuggerModel.cpp new file mode 100644 index 000000000..c65beac7d --- /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 "libethereum/Executive.h" +#include "libethereum/Transaction.h" +#include "libethereum/ExtVM.h" +#include "libevm/VM.h" +#include "libdevcore/Common.h" +#include "ApplicationCtx.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)); +} + +DebuggingContent AssemblyDebuggerModel::getContractInitiationDebugStates(dev::bytesConstRef _rawTransaction) +{ + QList states; + Transaction tr(_rawTransaction); + m_currentExecution.get()->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.get()->go(onOp); + m_currentExecution.get()->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 = "compile 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..3cac98db0 --- /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 code); + +private: + std::unique_ptr m_currentExecution; + dev::eth::State m_executiveState; +}; + +} + +} diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 596aea165..c85292f2b 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -27,6 +27,7 @@ #include #include #include "ConstantCompilationCtrl.h" +#include "AssemblyDebuggerCtrl.h" #include "features.h" #include "ApplicationCtx.h" #include "CodeEditorExtensionManager.h" @@ -59,22 +60,29 @@ 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.get()->contentUrl().isEmpty()) { try { - constantCompilation.get()->addContentOn(m_tabView); + if (ext.get()->getDisplayBehavior() == ExtensionDisplayBehavior::Tab) + { + ext.get()->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.get()->start(); + m_features.append(ext); } void CodeEditorExtensionManager::setEditor(QQuickItem* _editor) diff --git a/mix/CodeEditorExtensionManager.h b/mix/CodeEditorExtensionManager.h index 2b8402bf2..8e8501bc9 100644 --- a/mix/CodeEditorExtensionManager.h +++ b/mix/CodeEditorExtensionManager.h @@ -45,12 +45,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..a703e6686 100644 --- a/mix/ConstantCompilationCtrl.cpp +++ b/mix/ConstantCompilationCtrl.cpp @@ -30,7 +30,7 @@ #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(); @@ -64,7 +64,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); } diff --git a/mix/ConstantCompilationModel.cpp b/mix/ConstantCompilationModel.cpp index e06734f59..a6b2f2741 100644 --- a/mix/ConstantCompilationModel.cpp +++ b/mix/ConstantCompilationModel.cpp @@ -42,6 +42,7 @@ 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) { diff --git a/mix/ConstantCompilationModel.h b/mix/ConstantCompilationModel.h index 4a17853f6..01ad32224 100644 --- a/mix/ConstantCompilationModel.h +++ b/mix/ConstantCompilationModel.h @@ -22,6 +22,7 @@ #pragma once +#include #include namespace dev @@ -34,6 +35,7 @@ struct CompilerResult { QString hexCode; QString comment; + dev::bytes bytes; bool success; }; diff --git a/mix/DebuggingStateWrapper.cpp b/mix/DebuggingStateWrapper.cpp new file mode 100644 index 000000000..877598ca5 --- /dev/null +++ b/mix/DebuggingStateWrapper.cpp @@ -0,0 +1,189 @@ +/* + 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 DebuggingState.h + * @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 +#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) +{ + 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 << 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 = b - (byte)Instruction::PUSH1 + 1; + s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&code[i + 1], bc))); + i += bc; + } + HumanReadableCode* humanCode = new HumanReadableCode(QString::fromStdString(out.str()) + " " + s, line); + 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)); +} + +QString DebuggingStateWrapper::debugStack() +{ + QString stack; + for (auto i: m_state.stack) + stack.prepend(prettyU256(i) + "\n"); + + return stack; +} + +QString DebuggingStateWrapper::debugStorage() +{ + std::stringstream s; + for (auto const& i: m_state.storage) + s << "@" << prettyU256(i.first).toStdString() << "    " << prettyU256(i.second).toStdString(); + + 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) + { + DebuggingState const& s = i ? *m_state.levels[m_state.levels.size() - i] : m_state; + std::ostringstream out; + out << m_state.cur.abridged(); + if (i) + out << " " << instructionInfo(m_state.inst).name << " @0x" << hex << m_state.curPC; + levelsStr.append(QString::fromStdString(out.str())); + } + return levelsStr; +} + +QString DebuggingStateWrapper::headerInfo() +{ + std::ostringstream ss; + ss << dec << " STEP: " << m_state.steps << " | PC: 0x" << hex << m_state.curPC << " : " << dev::eth::instructionInfo(m_state.inst).name << " | ADDMEM: " << dec << m_state.newMemSize << " words | COST: " << dec << m_state.gasCost << " | GAS: " << dec << m_state.gas; + return QString::fromStdString(ss.str()); +} + +QString DebuggingStateWrapper::endOfDebug() +{ + if (m_state.gasCost > m_state.gas) + return "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 "RETURN " + QString::fromStdString(dev::memDump(out, 16, false)); + } + else if (m_state.inst == Instruction::STOP) + return "STOP"; + else if (m_state.inst == Instruction::SUICIDE && m_state.stack.size() >= 1) + return "SUICIDE 0x" + QString::fromStdString(toString(right160(m_state.stack.back()))); + else + return "EXCEPTION"; +} + +QString DebuggingStateWrapper::prettyU256(u256 _n) +{ + unsigned inc = 0; + QString raw; + std::ostringstream s; + if (!(_n >> 64)) + s << " " << (uint64_t)_n << " (0x" << hex << (uint64_t)_n << ")"; + else if (!~(_n >> 64)) + s << " " << (int64_t)_n << " (0x" << hex << (int64_t)_n << ")"; + else if ((_n >> 160) == 0) + { + Address a = right160(_n); + + QString n = QString::fromStdString(a.abridged());//pretty(a); + if (n.isNull()) + s << "0x" << a; + else + s << n.toHtmlEscaped().toStdString() << "(0x" << a.abridged() << ")"; + } + else if ((raw = fromRaw((h256)_n, &inc)).size()) + return "\"" + raw.toHtmlEscaped() + "\"" + (inc ? " + " + QString::number(inc) : ""); + else + s << "" << (h256)_n; + return QString::fromStdString(s.str()); +} + +QString DebuggingStateWrapper::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 QString(); + if (l != std::string::npos) + { + auto p = s.find_first_not_of('\0', l); + if (!(p == std::string::npos || (_inc && p == 31))) + return QString(); + if (_inc) + *_inc = (byte)s[31]; + s.resize(l); + } + for (auto i: s) + if (i < 32) + return QString(); + return QString::fromStdString(s); + } + return QString(); +} diff --git a/mix/DebuggingStateWrapper.h b/mix/DebuggingStateWrapper.h new file mode 100644 index 000000000..07716e9ad --- /dev/null +++ b/mix/DebuggingStateWrapper.h @@ -0,0 +1,133 @@ +/* + 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 DebuggingState.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; +}; + +/* 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) : m_line(_line), m_processIndex(_processIndex) {} + QString line() { return m_line; } + int processIndex() { return m_processIndex; } + +private: + QString m_line; + int m_processIndex; +}; + +/* used to publish QMap type to QML */ +class QQMLMap : public QObject +{ + Q_OBJECT + +public: + QQMLMap(QMap _map) : m_map(_map) { } + Q_INVOKABLE int getValue(int _key) { return m_map.value(_key); } + +private: + QMap m_map; +}; + +/* used to publish DebuggingState struct to QML */ +class DebuggingStateWrapper : public QObject +{ + Q_OBJECT + Q_PROPERTY(int step READ step) + Q_PROPERTY(int curPC READ curPC) + Q_PROPERTY(int gasCost READ gasCost) + Q_PROPERTY(int gas READ gas) + 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) : m_code(_code), m_data(_data) {} + int step() { return (int)m_state.steps; } + int curPC() { return (int)m_state.curPC; } + int gasCost() { return (int)m_state.gasCost; } + int gas() { return (int)m_state.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); + +private: + DebuggingState m_state; + bytes m_code; + bytes m_data; + QString prettyU256(u256 _n); + QString fromRaw(h256 _n, unsigned* _inc = nullptr); +}; + +} + +} diff --git a/mix/Extension.cpp b/mix/Extension.cpp index 5aeb0cc17..24cc19aba 100644 --- a/mix/Extension.cpp +++ b/mix/Extension.cpp @@ -25,7 +25,7 @@ using namespace dev; using namespace dev::mix; -void Extension::addContentOn(QObject* _tabView) +void Extension::addTabOn(QObject* _view) { if (contentUrl() == "") return; @@ -33,12 +33,29 @@ void Extension::addContentOn(QObject* _tabView) QVariant returnValue; QQmlComponent* component = new QQmlComponent( ApplicationCtx::getInstance()->appEngine(), - QUrl(this->contentUrl()), _tabView); + 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(ApplicationCtx::getInstance()->appEngine(), QUrl(contentUrl())); + QObject* dialog = component.create(); + QObject* dialogWin = ApplicationCtx::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..b697af58b 100644 --- a/mix/Extension.h +++ b/mix/Extension.h @@ -28,19 +28,31 @@ namespace dev namespace mix { +enum ExtensionDisplayBehavior +{ + Tab, + ModalDialog +}; + + class Extension: public QObject { Q_OBJECT public: Extension() {} + Extension(ExtensionDisplayBehavior _displayBehavior) { m_displayBehavior = _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; }; } diff --git a/mix/KeyEventManager.cpp b/mix/KeyEventManager.cpp new file mode 100644 index 000000000..f5d638869 --- /dev/null +++ b/mix/KeyEventManager.cpp @@ -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 KeyEventManager.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * 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 +#include "KeyEventManager.h" + +void KeyEventManager::registerEvent(const QObject* _receiver, const char* _slot) +{ + QObject::connect(this, SIGNAL(onKeyPressed(int)), _receiver, _slot); +} + +void KeyEventManager::unRegisterEvent(QObject* _receiver) +{ + QObject::disconnect(_receiver); +} + +void KeyEventManager::keyPressed(QVariant _event) +{ + emit onKeyPressed(_event.toInt()); +} + + diff --git a/mix/KeyEventManager.h b/mix/KeyEventManager.h new file mode 100644 index 000000000..ecd768c4c --- /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 Q_SLOTS: + void keyPressed(QVariant event); +}; + diff --git a/mix/TransactionBuilder.cpp b/mix/TransactionBuilder.cpp new file mode 100644 index 000000000..ce274fa00 --- /dev/null +++ b/mix/TransactionBuilder.cpp @@ -0,0 +1,92 @@ +/* + 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/Common.h" +#include "ApplicationCtx.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, fromString(address), _data, _nonce, _secret); +} + +int TransactionBuilder::fromHex(char _i) const +{ + if (_i >= '0' && _i <= '9') + return _i - '0'; + if (_i >= 'a' && _i <= 'f') + return _i - 'a' + 10; + if (_i >= 'A' && _i <= 'F') + return _i - 'A' + 10; + BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); +} + +bytes TransactionBuilder::fromHex(std::string const& _s) const +{ + unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; + std::vector ret; + ret.reserve((_s.size() - s + 1) / 2); + + if (_s.size() % 2) + try + { + ret.push_back(fromHex(_s[s++])); + } + catch (...){ ret.push_back(0); cwarn << boost::current_exception_diagnostic_information(); } + for (unsigned i = s; i < _s.size(); i += 2) + try + { + ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); + } + catch (...){ ret.push_back(0); cwarn << boost::current_exception_diagnostic_information(); } + return ret; +} + +Address TransactionBuilder::fromString(QString const& _a) const +{ + Client* ethClient = ApplicationCtx::getInstance()->getEthereumClient(); + std::string sn = _a.toStdString(); + if (sn.size() > 32) + sn.resize(32); + h256 n; + memcpy(n.data(), sn.data(), sn.size()); + memset(n.data() + sn.size(), 0, 32 - sn.size()); + if (_a.size() == 40) + return Address(fromHex(_a.toStdString())); + else + { + //we try to resolve the recipient adress using nameReg contract state + const Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f"); //NameReg contract + if (h160 nameReg = (u160)ethClient->stateAt(c_config, 0)) + if (h256 a = ethClient->stateAt(nameReg, n)) + return right160(a); + } + return Address(); // should maybe throws exception instead of returning blank address. +} diff --git a/mix/TransactionBuilder.h b/mix/TransactionBuilder.h new file mode 100644 index 000000000..261d3060f --- /dev/null +++ b/mix/TransactionBuilder.h @@ -0,0 +1,48 @@ +/* + 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; +private: + bytes fromHex(std::string const& _s) const; + int fromHex(char _i) const; + Address fromString(QString const& _a) const; +}; + +} + +} diff --git a/mix/main.cpp b/mix/main.cpp index 537941290..4f707f47a 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -35,6 +35,9 @@ int main(int _argc, char *_argv[]) QQmlApplicationEngine* engine = new QQmlApplicationEngine(); ApplicationCtx::setApplicationContext(engine); QObject::connect(&app, SIGNAL(lastWindowClosed()), ApplicationCtx::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff + engine->load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); + + ApplicationCtx::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..14ddbcf0d --- /dev/null +++ b/mix/qml/BasicMessage.qml @@ -0,0 +1,20 @@ +import QtQuick 2.3 +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..1c79e43ce --- /dev/null +++ b/mix/qml/Debugger.qml @@ -0,0 +1,231 @@ +import QtQuick 2.3 +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; + Rectangle { + 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 { + id: stateListContainer + focus: true + 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.55 + 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 { + anchors.top: statesList.bottom + height: parent.height * 0.30 + width: parent.width + + Label { + id: callStackLabel + anchors.top: statesList.bottom + 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 { + 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 + + 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 + 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 + 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 + 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..821b48913 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 2.3 +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..ddaa362a6 --- /dev/null +++ b/mix/qml/js/Debugger.js @@ -0,0 +1,63 @@ +//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 + var gascost = state.gas - state.gasCost; + headerInfoLabel.text = "EXIT | GAS: " + gascost; +} 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" + } } }