diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 809d2dfd6..4e099b05b 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -37,7 +37,6 @@ #include "QVariableDefinition.h" #include "ContractCallDataEncoder.h" #include "CodeModel.h" -#include "ClientModel.h" #include "QEther.h" #include "Web3Server.h" #include "ClientModel.h" @@ -318,19 +317,28 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) QDebugData* debugData = new QDebugData(); QQmlEngine::setObjectOwnership(debugData, QQmlEngine::JavaScriptOwnership); QList codes; + QList> codeMaps; + QList codeItems; + QList contracts; for (MachineCode const& code: _t.executionCode) { - codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code)); + QHash codeMap; + codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code, codeMap)); + codeMaps.push_back(std::move(codeMap)); //try to resolve contract for source level debugging auto nameIter = m_contractNames.find(code.address); if (nameIter != m_contractNames.end()) { CompiledContract const& compilerRes = m_context->codeModel()->contract(nameIter->second); eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems(); - QVariantList locations; - for (eth::AssemblyItem const& item: assemblyItems) - locations.push_back(QVariant::fromValue(new QSourceLocation(debugData, item.getLocation().start, item.getLocation().end))); - codes.back()->setLocations(compilerRes.documentId(), std::move(locations)); + codes.back()->setDocument(compilerRes.documentId()); + codeItems.push_back(std::move(assemblyItems)); + contracts.push_back(&compilerRes); + } + else + { + codeItems.push_back(AssemblyItems()); + contracts.push_back(nullptr); } } @@ -339,18 +347,77 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) data.push_back(QMachineState::getDebugCallData(debugData, d)); QVariantList states; + QStringList solCallStack; + std::map solLocals; // + QList returnStack; + + unsigned prevInstructionIndex = 0; for (MachineState const& s: _t.machineStates) - states.append(QVariant::fromValue(new QMachineState(debugData, s, codes[s.codeIndex], data[s.dataIndex]))); + { + int instructionIndex = codeMaps[s.codeIndex][static_cast(s.curPC)]; + QSolState* solState = nullptr; + if (!codeItems[s.codeIndex].empty() && contracts[s.codeIndex]) + { + CompiledContract const* contract = contracts[s.codeIndex]; + AssemblyItem const& instruction = codeItems[s.codeIndex][instructionIndex]; - debugData->setStates(std::move(states)); + if (instruction.type() == dev::eth::Push && !instruction.data()) + { + //register new local variable initialization + auto localIter = contract->locals().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end)); + if (localIter != contract->locals().end()) + solLocals[s.stack.size()] = localIter.value(); + } - //QList returnParameters; - //returnParameters = encoder.decode(f->returnParameters(), debuggingContent.returnValue); + if (instruction.type() == dev::eth::Tag) //TODO: use annotations + { + //track calls into functions + auto functionIter = contract->functions().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end)); + if (functionIter != contract->functions().end()) + { + QString functionName = functionIter.value(); + solCallStack.push_back(functionName); + returnStack.push_back(prevInstructionIndex + 1); + } + else if (!returnStack.empty() && instructionIndex == returnStack.back()) + { + returnStack.pop_back(); + solCallStack.pop_back(); + } + } + + //format solidity context values + QStringList locals; + for(auto l: solLocals) + if (l.first < (int)s.stack.size()) + locals.push_back(l.second.name + "\t" + formatValue(l.second.type, s.stack[l.first])); + + QStringList storage; + for(auto st: s.storage) + { + if (st.first < std::numeric_limits::max()) + { + auto storageIter = contract->storage().find(static_cast(st.first)); + if (storageIter != contract->storage().end()) + storage.push_back(storageIter.value().name + "\t" + formatValue(storageIter.value().type, st.second)); + } + } + prevInstructionIndex = instructionIndex; + solState = new QSolState(debugData, storage, solCallStack, locals, instruction.getLocation().start, instruction.getLocation().end); + } + + states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState))); + } - //collect states for last transaction + debugData->setStates(std::move(states)); debugDataReady(debugData); } +QString ClientModel::formatValue(SolidityType const&, dev::u256 const& _value) +{ + return QString::fromStdString(prettyU256(_value)); +} + void ClientModel::emptyRecord() { debugDataReady(new QDebugData()); diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 8810502bd..a12fe22e6 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -41,6 +41,7 @@ class QEther; class QDebugData; class MixClient; class QVariableDefinition; +struct SolidityType; /// Backend transaction config class struct TransactionSettings @@ -198,6 +199,7 @@ private: void onNewTransaction(); void onStateReset(); void showDebuggerForTransaction(ExecutionResult const& _t); + QString formatValue(SolidityType const& _type, dev::u256 const& _value); AppContext* m_context; std::atomic m_running; diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index cc2a29b42..bb258334c 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -25,7 +25,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -43,6 +46,91 @@ using namespace dev::mix; const std::set c_predefinedContracts = { "Config", "Coin", "CoinReg", "coin", "service", "owned", "mortal", "NameReg", "named", "std", "configUser" }; + +namespace +{ + using namespace dev::solidity; + class CollectDeclarationsVisitor: public ASTConstVisitor + { + public: + CollectDeclarationsVisitor(QHash* _functions, QHash* _locals, QHash* _storage): + m_functions(_functions), m_locals(_locals), m_storage(_storage), m_functionScope(false), m_storageSlot(0) {} + private: + QHash* m_functions; + QHash* m_locals; + QHash* m_storage; + bool m_functionScope; + uint m_storageSlot; + + LocationPair nodeLocation(ASTNode const& _node) + { + return LocationPair(_node.getLocation().start, _node.getLocation().end); + } + + SolidityType nodeType(Type const* _type) + { + if (!_type) + return SolidityType { SolidityType::Type::UnsignedInteger, 32 }; + switch (_type->getCategory()) + { + case Type::Category::Integer: + { + IntegerType const* it = dynamic_cast(_type); + unsigned size = it->getNumBits() / 8; + SolidityType::Type typeCode = it->isAddress() ? SolidityType::Type::Address : it->isHash() ? SolidityType::Type::Hash : it->isSigned() ? SolidityType::Type::SignedInteger : SolidityType::Type::UnsignedInteger; + return SolidityType { typeCode, size }; + } + case Type::Category::Bool: + return SolidityType { SolidityType::Type::Bool, _type->getSizeOnStack() * 32 }; + case Type::Category::String: + { + StaticStringType const* s = dynamic_cast(_type); + return SolidityType { SolidityType::Type::String, static_cast(s->getNumBytes()) }; + } + case Type::Category::Contract: + return SolidityType { SolidityType::Type::Address, _type->getSizeOnStack() * 32 }; + case Type::Category::Array: + case Type::Category::Enum: + case Type::Category::Function: + case Type::Category::IntegerConstant: + case Type::Category::Magic: + case Type::Category::Mapping: + case Type::Category::Modifier: + case Type::Category::Real: + case Type::Category::Struct: + case Type::Category::TypeType: + case Type::Category::Void: + default: + return SolidityType { SolidityType::Type::UnsignedInteger, 32 }; + } + } + + virtual bool visit(FunctionDefinition const& _node) + { + m_functions->insert(nodeLocation(_node), QString::fromStdString(_node.getName())); + m_functionScope = true; + return true; + } + + virtual void endVisit(FunctionDefinition const&) + { + m_functionScope = false; + } + + virtual bool visit(VariableDeclaration const& _node) + { + SolidityDeclaration decl; + decl.type = nodeType(_node.getType().get()); + decl.name = QString::fromStdString(_node.getName()); + if (m_functionScope) + m_locals->insert(nodeLocation(_node), decl); + else + m_storage->insert(m_storageSlot++, decl); + return true; + } + }; +} + void BackgroundWorker::queueCodeChange(int _jobId) { m_model->runCompilationJob(_jobId); @@ -52,18 +140,23 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler QObject(nullptr), m_sourceHash(qHash(_source)) { - auto const& contractDefinition = _compiler.getContractDefinition(_contractName.toStdString()); + std::string name = _contractName.toStdString(); + auto const& contractDefinition = _compiler.getContractDefinition(name); m_contract.reset(new QContractDefinition(&contractDefinition)); QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership); m_bytes = _compiler.getBytecode(_contractName.toStdString()); - m_assemblyItems = _compiler.getRuntimeAssemblyItems(_contractName.toStdString()); - m_constructorAssemblyItems = _compiler.getAssemblyItems(_contractName.toStdString()); + m_assemblyItems = _compiler.getRuntimeAssemblyItems(name); + m_constructorAssemblyItems = _compiler.getAssemblyItems(name); + dev::solidity::InterfaceHandler interfaceHandler; m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition)); if (m_contractInterface.isEmpty()) m_contractInterface = "[]"; if (contractDefinition.getLocation().sourceName.get()) m_documentId = QString::fromStdString(*contractDefinition.getLocation().sourceName); + + CollectDeclarationsVisitor visitor(&m_functions, &m_locals, &m_storage); + contractDefinition.accept(visitor); } QString CompiledContract::codeHex() const @@ -121,12 +214,11 @@ void CodeModel::reset(QVariantMap const& _documents) void CodeModel::registerCodeChange(QString const& _documentId, QString const& _code) { - { - Guard l(x_contractMap); - CompiledContract* contract = m_contractMap.value(_documentId); - if (contract != nullptr && contract->m_sourceHash == qHash(_code)) - return; + CompiledContract* contract = contractByDocumentId(_documentId); + if (contract != nullptr && contract->m_sourceHash == qHash(_code)) + return; + { Guard pl(x_pendingContracts); m_pendingContracts[_documentId] = _code; } @@ -196,7 +288,8 @@ void CodeModel::runCompilationJob(int _jobId) if (c_predefinedContracts.count(n) != 0) continue; QString name = QString::fromStdString(n); - auto sourceIter = m_pendingContracts.find(name); + QString sourceName = QString::fromStdString(*cs.getContractDefinition(n).getLocation().sourceName); + auto sourceIter = m_pendingContracts.find(sourceName); QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString(); CompiledContract* contract = new CompiledContract(cs, name, source); QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership); diff --git a/mix/CodeModel.h b/mix/CodeModel.h index 511d2e01f..de9a07bab 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -64,6 +64,29 @@ private: CodeModel* m_model; }; +using LocationPair = QPair; + +struct SolidityType +{ + enum class Type //TODO: arrays and structs + { + SignedInteger, + UnsignedInteger, + Hash, + Bool, + Address, + String, + }; + Type type; + unsigned size; //bytes +}; + +struct SolidityDeclaration +{ + QString name; + SolidityType type; +}; + ///Compilation result model. Contains all the compiled contract data required by UI class CompiledContract: public QObject { @@ -93,6 +116,10 @@ public: /// @returns contract source Id QString documentId() const { return m_documentId; } + QHash const& functions() const { return m_functions; } + QHash const& locals() const { return m_locals; } + QHash const& storage() const { return m_storage; } + private: uint m_sourceHash; std::shared_ptr m_contract; @@ -102,11 +129,13 @@ private: QString m_documentId; eth::AssemblyItems m_assemblyItems; eth::AssemblyItems m_constructorAssemblyItems; + QHash m_functions; + QHash m_locals; + QHash m_storage; friend class CodeModel; }; - using ContractMap = QHash; /// Code compilation model. Compiles contracts in background an provides compiled contract data diff --git a/mix/DebuggingStateWrapper.cpp b/mix/DebuggingStateWrapper.cpp index 6cb29bbae..b8fbdef30 100644 --- a/mix/DebuggingStateWrapper.cpp +++ b/mix/DebuggingStateWrapper.cpp @@ -69,7 +69,7 @@ namespace } } -QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code) +QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code, QHash& o_codeMap) { QVariantList codeStr; for (unsigned i = 0; i <= _code.size(); ++i) @@ -80,14 +80,15 @@ QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _addr QString s = QString::fromStdString(instructionInfo((Instruction)b).name); std::ostringstream out; out << std::hex << std::setw(4) << std::setfill('0') << i; - int line = i; + int offset = 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; } - codeStr.append(QVariant::fromValue(new QInstruction(_owner, QString::fromStdString(out.str()) + " " + s, line))); + o_codeMap[offset] = codeStr.size(); + codeStr.append(QVariant::fromValue(new QInstruction(_owner, QString::fromStdString(out.str()) + " " + s))); } catch (...) { diff --git a/mix/DebuggingStateWrapper.h b/mix/DebuggingStateWrapper.h index 7a34d6493..7eb8c932c 100644 --- a/mix/DebuggingStateWrapper.h +++ b/mix/DebuggingStateWrapper.h @@ -26,12 +26,12 @@ #include #include -#include +#include #include #include #include +#include "MachineStates.h" #include "QVariableDefinition.h" -#include "MixClient.h" #include "QBigInt.h" namespace dev @@ -46,32 +46,39 @@ class QInstruction: public QObject { Q_OBJECT Q_PROPERTY(QString line MEMBER m_line CONSTANT) - Q_PROPERTY(int processIndex MEMBER m_processIndex CONSTANT) public: - QInstruction(QObject* _owner, QString _line, int _processIndex): QObject(_owner), m_line(_line), m_processIndex(_processIndex) {} + QInstruction(QObject* _owner, QString _line): QObject(_owner), m_line(_line) {} private: QString m_line; - int m_processIndex; }; - -class QSourceLocation: public QObject +/** + * @brief Solidity state + */ +class QSolState: public QObject { Q_OBJECT + Q_PROPERTY(QStringList storage MEMBER m_storage CONSTANT) + Q_PROPERTY(QStringList callStack MEMBER m_callStack CONSTANT) + Q_PROPERTY(QStringList locals MEMBER m_locals CONSTANT) Q_PROPERTY(int start MEMBER m_start CONSTANT) Q_PROPERTY(int end MEMBER m_end CONSTANT) public: - QSourceLocation(QObject* _owner, int _start, int _end): QObject(_owner), m_start(_start), m_end(_end) {} + QSolState(QObject* _parent, QStringList const& _storage, QStringList const& _callStack, QStringList const& _locals, int _start, int _end): + QObject(_parent), m_storage(_storage), m_callStack(_callStack), m_locals(_locals), m_start(_start), m_end(_end) + { } private: + QStringList m_storage; + QStringList m_callStack; + QStringList m_locals; int m_start; int m_end; }; - /** * @brief Shared container for lines */ @@ -79,19 +86,17 @@ class QCode: public QObject { Q_OBJECT Q_PROPERTY(QVariantList instructions MEMBER m_instructions CONSTANT) - Q_PROPERTY(QVariantList locations MEMBER m_locations CONSTANT) Q_PROPERTY(QString address MEMBER m_address CONSTANT) Q_PROPERTY(QString documentId MEMBER m_document CONSTANT) public: QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(_instrunctions), m_address(_address) {} - void setLocations(QString const& _document, QVariantList&& _locations) { m_document = _document; m_locations = _locations; } + void setDocument(QString const& _documentId) { m_document = _documentId; } private: QVariantList m_instructions; QString m_address; QString m_document; - QVariantList m_locations; }; /** @@ -133,6 +138,7 @@ class QMachineState: public QObject Q_OBJECT Q_PROPERTY(int step READ step CONSTANT) Q_PROPERTY(int curPC READ curPC CONSTANT) + Q_PROPERTY(int instructionIndex MEMBER m_instructionIndex CONSTANT) Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT) Q_PROPERTY(QBigInt* gas READ gas CONSTANT) Q_PROPERTY(QString instruction READ instruction CONSTANT) @@ -146,10 +152,11 @@ class QMachineState: public QObject Q_PROPERTY(QVariantList levels READ levels CONSTANT) Q_PROPERTY(unsigned codeIndex READ codeIndex CONSTANT) Q_PROPERTY(unsigned dataIndex READ dataIndex CONSTANT) + Q_PROPERTY(QObject* solidity MEMBER m_solState CONSTANT) public: - QMachineState(QObject* _owner, MachineState const& _state, QCode* _code, QCallData* _callData): - QObject(_owner), m_state(_state), m_code(_code), m_callData(_callData) {} + QMachineState(QObject* _owner, int _instructionIndex, MachineState const& _state, QCode* _code, QCallData* _callData, QSolState* _solState): + QObject(_owner), m_instructionIndex(_instructionIndex), m_state(_state), m_code(_code), m_callData(_callData), m_solState(_solState) { } /// Get the step of this machine states. int step() { return (int)m_state.steps; } /// Get the proccessed code index. @@ -168,7 +175,7 @@ public: QStringList debugStorage(); /// Get memory. QVariantList debugMemory(); - /// get end of debug information. + /// Get end of debug information. QString endOfDebug(); /// Get the new memory size. QBigInt* newMemSize(); @@ -177,18 +184,18 @@ public: /// Get all previous steps. QVariantList levels(); /// Get the current processed machine state. - MachineState state() { return m_state; } - /// Set the current processed machine state. - void setState(MachineState _state) { m_state = _state; } + MachineState const& state() const { return m_state; } /// Convert all machine states in human readable code. - static QCode* getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code); + static QCode* getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code, QHash& o_codeMap); /// Convert call data into human readable form static QCallData* getDebugCallData(QObject* _owner, bytes const& _data); private: + int m_instructionIndex; MachineState m_state; QCode* m_code; QCallData* m_callData; + QSolState* m_solState; }; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 781924db7..061057bb4 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -41,7 +41,7 @@ namespace mix { const Secret c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); -const u256 c_mixGenesisDifficulty = (u256) 1 << 4; +const u256 c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow class MixBlockChain: public dev::eth::BlockChain { diff --git a/mix/qml/CallStack.qml b/mix/qml/CallStack.qml new file mode 100644 index 000000000..c9e22532d --- /dev/null +++ b/mix/qml/CallStack.qml @@ -0,0 +1,74 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 +import "." + +DebugInfoList +{ + id: callStack + collapsible: true + title : qsTr("Call Stack") + enableSelection: true + itemDelegate: + Item { + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "#4A90E2" + visible: styleData.selected; + } + + RowLayout + { + id: row + anchors.fill: parent + Rectangle + { + color: "#f7f7f7" + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 30 + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + font.family: "monospace" + anchors.leftMargin: 5 + color: "#4a4a4a" + text: styleData.row; + font.pointSize: DebuggerPaneStyle.general.basicFontSize + width: parent.width - 5 + elide: Text.ElideRight + } + } + Rectangle + { + color: "transparent" + Layout.fillWidth: true + Layout.minimumWidth: parent.width - 30 + Layout.maximumWidth: parent.width - 30 + Text { + anchors.leftMargin: 5 + width: parent.width - 5 + wrapMode: Text.NoWrap + anchors.left: parent.left + font.family: "monospace" + anchors.verticalCenter: parent.verticalCenter + color: "#4a4a4a" + text: styleData.value; + elide: Text.ElideRight + font.pointSize: DebuggerPaneStyle.general.basicFontSize + } + } + } + + Rectangle { + anchors.top: row.bottom + width: parent.width; + height: 1; + color: "#cccccc" + anchors.bottom: parent.bottom + } + } +} diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index af46bee1c..732d02f75 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -29,6 +29,7 @@ Rectangle { onAssemblyModeChanged: { Debugger.updateMode(); + machineStates.updateHeight(); } function displayCompilationErrorIfAny() @@ -90,6 +91,9 @@ Rectangle { property alias memoryDumpHeightSettings: memoryRect.height property alias callDataHeightSettings: callDataRect.height property alias transactionLogVisible: transactionLog.visible + property alias solCallStackHeightSettings: solStackRect.height + property alias solStorageHeightSettings: solStorageRect.height + property alias solLocalsHeightSettings: solLocalsRect.height } Rectangle @@ -183,8 +187,12 @@ Rectangle { Layout.fillWidth: true Layout.fillHeight: true function updateHeight() { - statesLayout.height = buttonRow.childrenRect.height + assemblyCodeRow.childrenRect.height + - callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height + 120; + var h = buttonRow.childrenRect.height; + if (assemblyMode) + h += assemblyCodeRow.childrenRect.height + callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height; + else + h += solStackRect.childrenRect.height + solLocalsRect.childrenRect.height + solStorageRect.childrenRect.height; + statesLayout.height = h + 120; } Component.onCompleted: updateHeight(); @@ -546,83 +554,66 @@ Rectangle { Rectangle { - id: callStackRect; + id: solStackRect; color: "transparent" Layout.minimumHeight: 25 Layout.maximumHeight: 800 onHeightChanged: machineStates.updateHeight(); - DebugInfoList - { - id: callStack - collapsible: true + visible: !assemblyMode + CallStack { anchors.fill: parent - title : qsTr("Call Stack") - enableSelection: true - onRowActivated: Debugger.displayFrame(index); - itemDelegate: - Item { - anchors.fill: parent + id: solCallStack + } + } - Rectangle { - anchors.fill: parent - color: "#4A90E2" - visible: styleData.selected; - } + Rectangle + { + id: solLocalsRect; + color: "transparent" + Layout.minimumHeight: 25 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); + visible: !assemblyMode + StorageView { + title : qsTr("Locals") + anchors.fill: parent + id: solLocals + } + } - RowLayout - { - id: row - anchors.fill: parent - Rectangle - { - color: "#f7f7f7" - Layout.fillWidth: true - Layout.minimumWidth: 30 - Layout.maximumWidth: 30 - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - font.family: "monospace" - anchors.leftMargin: 5 - color: "#4a4a4a" - text: styleData.row; - font.pointSize: DebuggerPaneStyle.general.basicFontSize - width: parent.width - 5 - elide: Text.ElideRight - } - } - Rectangle - { - color: "transparent" - Layout.fillWidth: true - Layout.minimumWidth: parent.width - 30 - Layout.maximumWidth: parent.width - 30 - Text { - anchors.leftMargin: 5 - width: parent.width - 5 - wrapMode: Text.NoWrap - anchors.left: parent.left - font.family: "monospace" - anchors.verticalCenter: parent.verticalCenter - color: "#4a4a4a" - text: styleData.value; - elide: Text.ElideRight - font.pointSize: DebuggerPaneStyle.general.basicFontSize - } - } - } + Rectangle + { + id: solStorageRect; + color: "transparent" + Layout.minimumHeight: 25 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); + visible: !assemblyMode + StorageView { + title : qsTr("Members") + anchors.fill: parent + id: solStorage + } + } - Rectangle { - anchors.top: row.bottom - width: parent.width; - height: 1; - color: "#cccccc" - anchors.bottom: parent.bottom - } - } + Rectangle + { + id: callStackRect; + color: "transparent" + Layout.minimumHeight: 25 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); + visible: assemblyMode + CallStack { + anchors.fill: parent + id: callStack + onRowActivated: Debugger.displayFrame(index); } } + + + Rectangle { id: storageRect @@ -631,68 +622,10 @@ Rectangle { Layout.minimumHeight: 25 Layout.maximumHeight: 800 onHeightChanged: machineStates.updateHeight(); - DebugInfoList - { - id: storage + visible: assemblyMode + StorageView { anchors.fill: parent - collapsible: true - title : qsTr("Storage") - itemDelegate: - Item { - anchors.fill: parent - RowLayout - { - id: row - anchors.fill: parent - Rectangle - { - color: "#f7f7f7" - Layout.fillWidth: true - Layout.minimumWidth: parent.width / 2 - Layout.maximumWidth: parent.width / 2 - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - font.family: "monospace" - anchors.leftMargin: 5 - color: "#4a4a4a" - text: styleData.value.split('\t')[0]; - font.pointSize: DebuggerPaneStyle.general.basicFontSize - width: parent.width - 5 - elide: Text.ElideRight - } - } - Rectangle - { - color: "transparent" - Layout.fillWidth: true - Layout.minimumWidth: parent.width / 2 - Layout.maximumWidth: parent.width / 2 - Text { - maximumLineCount: 1 - clip: true - anchors.leftMargin: 5 - width: parent.width - 5 - wrapMode: Text.WrapAnywhere - anchors.left: parent.left - font.family: "monospace" - anchors.verticalCenter: parent.verticalCenter - color: "#4a4a4a" - text: styleData.value.split('\t')[1]; - elide: Text.ElideRight - font.pointSize: DebuggerPaneStyle.general.basicFontSize - } - } - } - - Rectangle { - anchors.top: row.bottom - width: parent.width; - height: 1; - color: "#cccccc" - anchors.bottom: parent.bottom - } - } + id: storage } } @@ -704,6 +637,7 @@ Rectangle { Layout.minimumHeight: 25 Layout.maximumHeight: 800 onHeightChanged: machineStates.updateHeight(); + visible: assemblyMode DebugInfoList { id: memoryDump anchors.fill: parent @@ -726,6 +660,7 @@ Rectangle { Layout.minimumHeight: 25 Layout.maximumHeight: 800 onHeightChanged: machineStates.updateHeight(); + visible: assemblyMode DebugInfoList { id: callDataDump anchors.fill: parent diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index c17e17f89..99ab39b6c 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -258,8 +258,8 @@ Rectangle { color: "transparent" width: 100 height: parent.height - anchors.top: statusHeader.top - anchors.right: statusHeader.right + anchors.top: parent.top + anchors.right: parent.right RowLayout { anchors.fill: parent diff --git a/mix/qml/StorageView.qml b/mix/qml/StorageView.qml new file mode 100644 index 000000000..f4831e3c3 --- /dev/null +++ b/mix/qml/StorageView.qml @@ -0,0 +1,69 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 +import "." + +DebugInfoList +{ + id: storage + collapsible: true + title : qsTr("Storage") + itemDelegate: + Item { + anchors.fill: parent + RowLayout + { + id: row + anchors.fill: parent + Rectangle + { + color: "#f7f7f7" + Layout.fillWidth: true + Layout.minimumWidth: parent.width / 2 + Layout.maximumWidth: parent.width / 2 + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + font.family: "monospace" + anchors.leftMargin: 5 + color: "#4a4a4a" + text: styleData.value.split('\t')[0]; + font.pointSize: DebuggerPaneStyle.general.basicFontSize + width: parent.width - 5 + elide: Text.ElideRight + } + } + Rectangle + { + color: "transparent" + Layout.fillWidth: true + Layout.minimumWidth: parent.width / 2 + Layout.maximumWidth: parent.width / 2 + Text { + maximumLineCount: 1 + clip: true + anchors.leftMargin: 5 + width: parent.width - 5 + wrapMode: Text.WrapAnywhere + anchors.left: parent.left + font.family: "monospace" + anchors.verticalCenter: parent.verticalCenter + color: "#4a4a4a" + text: styleData.value.split('\t')[1]; + elide: Text.ElideRight + font.pointSize: DebuggerPaneStyle.general.basicFontSize + } + } + } + + Rectangle { + anchors.top: row.bottom + width: parent.width; + height: 1; + color: "#cccccc" + anchors.bottom: parent.bottom + } + } +} + diff --git a/mix/qml/js/Debugger.js b/mix/qml/js/Debugger.js index 30dd5489b..23aa81844 100644 --- a/mix/qml/js/Debugger.js +++ b/mix/qml/js/Debugger.js @@ -4,7 +4,6 @@ var currentSelectedState = null; var currentDisplayedState = null; var debugData = null; -var codeMap = null; var locations = []; var locationMap = {}; var breakpoints = {}; @@ -56,7 +55,7 @@ function initLocations() for (var i = 0; i < debugData.states.length - 1; i++) { var code = debugData.states[i].code; - var location = code.documentId ? code.locations[codeStr(i)] : nullLocation; + var location = code.documentId ? debugData.states[i].solidity : nullLocation; if (location.start !== prevLocation.start || location.end !== prevLocation.end || code.documentId !== prevLocation.documentId) { prevLocation = { start: location.start, end: location.end, documentId: code.documentId, state: i }; @@ -65,6 +64,7 @@ function initLocations() locationMap[i] = locations.length - 1; } locations.push({ start: -1, end: -1, documentId: code.documentId, state: i }); + locationMap[debugData.states.length - 1] = locations.length - 1; } @@ -93,12 +93,10 @@ function initSlider() function setupInstructions(stateIndex) { var instructions = debugData.states[stateIndex].code.instructions; - codeMap = {}; statesList.model.clear(); - for (var i = 0; i < instructions.length; i++) { + for (var i = 0; i < instructions.length; i++) statesList.model.append(instructions[i]); - codeMap[instructions[i].processIndex] = i; - } + callDataDump.listModel = debugData.states[stateIndex].callData.items; } @@ -129,14 +127,14 @@ function display(stateIndex) setupInstructions(stateIndex); if (debugData.states[stateIndex].dataIndex !== debugData.states[currentDisplayedState].dataIndex) setupCallData(stateIndex); - var codeLine = codeStr(stateIndex); var state = debugData.states[stateIndex]; + var codeLine = state.instructionIndex; highlightSelection(codeLine); completeCtxInformation(state); currentDisplayedState = stateIndex; var docId = debugData.states[stateIndex].code.documentId; if (docId) - debugExecuteLocation(docId, locations[locationMap[stateIndex]]); + debugExecuteLocation(docId, debugData.states[stateIndex].solidity); } function displayFrame(frameIndex) @@ -183,12 +181,6 @@ function selectState(stateIndex) statesSlider.value = stateIndex; } -function codeStr(stateIndex) -{ - var state = debugData.states[stateIndex]; - return codeMap[state.curPC]; -} - function highlightSelection(index) { statesList.positionViewAtRow(index, ListView.Center); @@ -206,6 +198,15 @@ function completeCtxInformation(state) stack.listModel = state.debugStack; storage.listModel = state.debugStorage; memoryDump.listModel = state.debugMemory; + if (state.solidity) { + solLocals.listModel = state.solidity.locals; + solStorage.listModel = state.solidity.storage; + solCallStack.listModel = state.solidity.callStack; + } else { + solLocals.listModel = []; + solStorage.listModel = []; + solCallStack.listModel = []; + } } function isCallInstruction(index) @@ -229,7 +230,7 @@ function breakpointHit(i) { var bpLocations = breakpoints[debugData.states[i].code.documentId]; if (bpLocations) { - var location = locations[locationMap[i]]; + var location = debugData.states[i].solidity; if (location.start >= 0 && location.end >= location.start) for (var b = 0; b < bpLocations.length; b++) if (locationsIntersect(location, bpLocations[b])) diff --git a/mix/res.qrc b/mix/res.qrc index 5c706fb9c..8bda8124c 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -103,6 +103,8 @@ qml/img/available_updates.png qml/DeploymentDialog.qml qml/img/search_filled.png + qml/StorageView.qml + qml/CallStack.qml qml/img/help.png qml/img/openedfolder.png qml/img/b64.png