diff --git a/src/app/debugger/debuggerUI.js b/src/app/debugger/debuggerUI.js index 98f1d118c0..b5713aa225 100644 --- a/src/app/debugger/debuggerUI.js +++ b/src/app/debugger/debuggerUI.js @@ -2,6 +2,7 @@ var OldEthdebuggerUI = require('./remix-debugger/src/ui/EthdebuggerUI') var Debugger = require('../debugger/debugger') var SourceHighlighter = require('../editor/sourceHighlighter') var TxBrowser = require('./debuggerUI/TxBrowser') +var StepManager = require('./debuggerUI/StepManager') var remixLib = require('remix-lib') var executionContext = remixLib.execution.executionContext var traceHelper = remixLib.helpers.trace @@ -17,6 +18,8 @@ class DebuggerUI { }) this.startTxBrowser() + // this.startStepManager() + this.stepManager = null container.appendChild(this.debugger_ui.render()) @@ -30,7 +33,7 @@ class DebuggerUI { }) this.transactionDebugger.event.register('breakpointStep', function (step) { - self.debugger_ui.stepManager.jumpTo(step) + self.stepManager.jumpTo(step) }) this.debugger_ui.event.register('indexChanged', function (index) { @@ -38,6 +41,10 @@ class DebuggerUI { }) } + // startStepManager () { + // this.stepManager = null + // } + startTxBrowser () { const self = this let web3 = executionContext.web3() @@ -52,7 +59,7 @@ class DebuggerUI { if (!tx.to) { tx.to = traceHelper.contractCreationToken('0') } - return self.debugger_ui.startDebugging(blockNumber, txNumber, tx) + return self.startDebugging(blockNumber, txNumber, tx) } try { @@ -60,13 +67,13 @@ class DebuggerUI { return web3.eth.getTransaction(txNumber, function (error, result) { let tx = result txBrowser.update(error, result) - self.debugger_ui.startDebugging(blockNumber, txNumber, tx) + self.startDebugging(blockNumber, txNumber, tx) }) } web3.eth.getTransactionFromBlock(blockNumber, txNumber, function (error, result) { let tx = result txBrowser.update(error, result) - self.debugger_ui.startDebugging(blockNumber, txNumber, tx) + self.startDebugging(blockNumber, txNumber, tx) }) } catch (e) { self.update(e.message) @@ -92,6 +99,22 @@ class DebuggerUI { return this.isActive } + startDebugging (blockNumber, txNumber, tx) { + const self = this + let shouldOpenDebugger = this.debugger_ui.startDebugging(blockNumber, txNumber, tx) + if (!shouldOpenDebugger) return + + this.stepManager = new StepManager(this.debugger_ui, this.transactionDebugger.debugger.traceManager) + this.stepManager.event.register('stepChanged', this, function (stepIndex) { + self.debugger_ui.stepChanged(stepIndex) + }) + + this.debugger_ui.stepManager = this.stepManager + + this.debugger_ui.createAndAddVmDebugger() + this.transactionDebugger.debugger.debug(tx) + } + debug (txHash) { const self = this this.transactionDebugger.debug(txHash, (error, tx) => { diff --git a/src/app/debugger/debuggerUI/ButtonNavigator.js b/src/app/debugger/debuggerUI/ButtonNavigator.js new file mode 100644 index 0000000000..12c9e4fa54 --- /dev/null +++ b/src/app/debugger/debuggerUI/ButtonNavigator.js @@ -0,0 +1,184 @@ +'use strict' +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager +var yo = require('yo-yo') + +var csjs = require('csjs-inject') +var styleGuide = require('../../ui/styles-guide/theme-chooser') +var styles = styleGuide.chooser() + +var css = csjs` + .buttons { + display: flex; + flex-wrap: wrap; + } + .stepButtons { + width: 100%; + display: flex; + justify-content: center; + } + .stepButton { + ${styles.rightPanel.debuggerTab.button_Debugger} + } + .jumpButtons { + width: 100%; + display: flex; + justify-content: center; + } + .jumpButton { + ${styles.rightPanel.debuggerTab.button_Debugger} + } + .navigator { + color: ${styles.rightPanel.debuggerTab.text_Primary}; + } + .navigator:hover { + color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor}; + } +` + +function ButtonNavigator (_parent, _traceManager) { + this.event = new EventManager() + this.intoBackDisabled = true + this.overBackDisabled = true + this.intoForwardDisabled = true + this.overForwardDisabled = true + this.jumpOutDisabled = true + this.jumpNextBreakpointDisabled = true + this.jumpPreviousBreakpointDisabled = true + + this.traceManager = _traceManager + this.currentCall = null + this.revertionPoint = null + + _parent.event.register('indexChanged', this, (index) => { + if (!this.view) return + if (index < 0) return + if (_parent.currentStepIndex !== index) return + + this.traceManager.buildCallPath(index, (error, callsPath) => { + if (error) { + console.log(error) + resetWarning(this) + } else { + this.currentCall = callsPath[callsPath.length - 1] + if (this.currentCall.reverted) { + this.revertionPoint = this.currentCall.return + this.view.querySelector('#reverted').style.display = 'block' + this.view.querySelector('#reverted #outofgas').style.display = this.currentCall.outOfGas ? 'inline' : 'none' + this.view.querySelector('#reverted #parenthasthrown').style.display = 'none' + } else { + var k = callsPath.length - 2 + while (k >= 0) { + var parent = callsPath[k] + if (parent.reverted) { + this.revertionPoint = parent.return + this.view.querySelector('#reverted').style.display = 'block' + this.view.querySelector('#reverted #parenthasthrown').style.display = parent ? 'inline' : 'none' + this.view.querySelector('#reverted #outofgas').style.display = 'none' + return + } + k-- + } + resetWarning(this) + } + } + }) + }) + + this.view +} + +module.exports = ButtonNavigator + +ButtonNavigator.prototype.render = function () { + var self = this + var view = yo`
+
+ + + + +
+ +
+ + + +
+ +
` + if (!this.view) { + this.view = view + } + return view +} + +ButtonNavigator.prototype.reset = function () { + this.intoBackDisabled = true + this.overBackDisabled = true + this.intoForwardDisabled = true + this.overForwardDisabled = true + this.jumpOutDisabled = true + this.jumpNextBreakpointDisabled = true + this.jumpPreviousBreakpointDisabled = true + resetWarning(this) +} + +ButtonNavigator.prototype.stepChanged = function (step) { + this.intoBackDisabled = step <= 0 + this.overBackDisabled = step <= 0 + if (!this.traceManager) { + this.intoForwardDisabled = true + this.overForwardDisabled = true + } else { + var self = this + this.traceManager.getLength(function (error, length) { + if (error) { + self.reset() + console.log(error) + } else { + self.jumpNextBreakpointDisabled = step >= length - 1 + self.jumpPreviousBreakpointDisabled = step <= 0 + self.intoForwardDisabled = step >= length - 1 + self.overForwardDisabled = step >= length - 1 + var stepOut = self.traceManager.findStepOut(step) + self.jumpOutDisabled = stepOut === step + } + self.updateAll() + }) + } + this.updateAll() +} + +ButtonNavigator.prototype.updateAll = function () { + this.updateDisabled('intoback', this.intoBackDisabled) + this.updateDisabled('overback', this.overBackDisabled) + this.updateDisabled('overforward', this.overForwardDisabled) + this.updateDisabled('intoforward', this.intoForwardDisabled) + this.updateDisabled('jumpout', this.jumpOutDisabled) + this.updateDisabled('jumptoexception', this.jumpOutDisabled) + this.updateDisabled('jumpnextbreakpoint', this.jumpNextBreakpointDisabled) + this.updateDisabled('jumppreviousbreakpoint', this.jumpPreviousBreakpointDisabled) +} + +ButtonNavigator.prototype.updateDisabled = function (id, disabled) { + if (disabled) { + document.getElementById(id).setAttribute('disabled', true) + } else { + document.getElementById(id).removeAttribute('disabled') + } +} + +function resetWarning (self) { + self.view.querySelector('#reverted #outofgas').style.display = 'none' + self.view.querySelector('#reverted #parenthasthrown').style.display = 'none' + self.view.querySelector('#reverted').style.display = 'none' +} + +module.exports = ButtonNavigator diff --git a/src/app/debugger/debuggerUI/Slider.js b/src/app/debugger/debuggerUI/Slider.js new file mode 100644 index 0000000000..10ca255d81 --- /dev/null +++ b/src/app/debugger/debuggerUI/Slider.js @@ -0,0 +1,75 @@ +'use strict' +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager +var yo = require('yo-yo') + +class Slider { + constructor (_traceManager, _stepOverride) { + this.event = new EventManager() + this.traceManager = _traceManager + this.max + this.disabled = true + this.view + this.solidityMode = false + this.stepOverride = _stepOverride + + this.previousValue = null + } + + render () { + var self = this + var view = yo`
+ +
` + if (!this.view) { + this.view = view + } + return view + } + + init (length) { + var slider = this.view.querySelector('#slider') + slider.setAttribute('max', length - 1) + this.max = length - 1 + this.updateDisabled(length === 0) + this.disabled = length === 0 + this.setValue(0) + } + + onChange (event) { + var value = parseInt(this.view.querySelector('#slider').value) + if (this.stepOverride) { + var correctedValue = this.stepOverride(value) + if (correctedValue !== value) { + this.setValue(correctedValue) + value = correctedValue + } + } + if (value === this.previousValue) return + this.previousValue = value + this.event.trigger('moved', [value]) + } + + setValue (value) { + this.view.querySelector('#slider').value = value + } + + updateDisabled (disabled) { + if (disabled) { + this.view.querySelector('#slider').setAttribute('disabled', true) + } else { + this.view.querySelector('#slider').removeAttribute('disabled') + } + } +} + +module.exports = Slider diff --git a/src/app/debugger/debuggerUI/StepManager.js b/src/app/debugger/debuggerUI/StepManager.js new file mode 100644 index 0000000000..d464b292cc --- /dev/null +++ b/src/app/debugger/debuggerUI/StepManager.js @@ -0,0 +1,205 @@ +'use strict' +var ButtonNavigator = require('./ButtonNavigator') +var Slider = require('./Slider') +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager +var yo = require('yo-yo') +var util = remixLib.util + +function StepManager (_parent, _traceManager) { + this.event = new EventManager() + this.parent = _parent.debugger + this.traceManager = _traceManager + this.sourceMapByAddress = {} + this.solidityMode = false + + var self = this + this.parent.event.register('newTraceLoaded', this, function () { + if (!this.slider) return + self.traceManager.getLength(function (error, length) { + if (error) { + console.log(error) + } else { + self.slider.init(length) + self.init() + } + }) + }) + + this.slider = new Slider(this.traceManager, (step) => { + return this.solidityMode ? this.resolveToReducedTrace(step, 0) : step + }) + this.slider.event.register('moved', this, function (step) { + self.sliderMoved(step) + }) + + this.parent.callTree.event.register('callTreeReady', () => { + if (!this.slider) return + if (this.parent.callTree.functionCallStack.length) { + this.jumpTo(this.parent.callTree.functionCallStack[0]) + } + }) + + this.buttonNavigator = new ButtonNavigator(_parent, this.traceManager) + this.buttonNavigator.event.register('stepIntoBack', this, function () { + self.stepIntoBack() + }) + this.buttonNavigator.event.register('stepIntoForward', this, function () { + self.stepIntoForward() + }) + this.buttonNavigator.event.register('stepOverBack', this, function () { + self.stepOverBack() + }) + this.buttonNavigator.event.register('stepOverForward', this, function () { + self.stepOverForward() + }) + this.buttonNavigator.event.register('jumpOut', this, function () { + self.jumpOut() + }) + this.buttonNavigator.event.register('jumpToException', this, function (exceptionIndex) { + self.jumpTo(exceptionIndex) + }) + this.buttonNavigator.event.register('jumpNextBreakpoint', (exceptionIndex) => { + self.parent.breakpointManager.jumpNextBreakpoint(_parent.currentStepIndex, true) + }) + this.buttonNavigator.event.register('jumpPreviousBreakpoint', (exceptionIndex) => { + self.parent.breakpointManager.jumpPreviousBreakpoint(_parent.currentStepIndex, true) + }) +} + +StepManager.prototype.remove = function () { + // used to stop listenning on event. bad and should be "refactored" + this.slider.view = null + this.slider = null + this.buttonNavigator.view = null + this.buttonNavigator = null +} + +StepManager.prototype.resolveToReducedTrace = function (value, incr) { + if (this.parent.callTree.reducedTrace.length) { + var nextSource = util.findClosestIndex(value, this.parent.callTree.reducedTrace) + nextSource = nextSource + incr + if (nextSource <= 0) { + nextSource = 0 + } else if (nextSource > this.parent.callTree.reducedTrace.length) { + nextSource = this.parent.callTree.reducedTrace.length - 1 + } + return this.parent.callTree.reducedTrace[nextSource] + } + return value +} + +StepManager.prototype.render = function () { + return yo`
+ ${this.slider.render()} + ${this.buttonNavigator.render()} +
` +} + +StepManager.prototype.reset = function () { + this.slider.setValue(0) + this.currentStepIndex = 0 + this.buttonNavigator.reset() +} + +StepManager.prototype.init = function () { + this.slider.setValue(0) + this.changeState(0) +} + +StepManager.prototype.newTraceAvailable = function () { + this.init() +} + +StepManager.prototype.jumpTo = function (step) { + if (!this.traceManager.inRange(step)) { + return + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.sliderMoved = function (step) { + if (!this.traceManager.inRange(step)) { + return + } + this.changeState(step) +} + +StepManager.prototype.stepIntoForward = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.currentStepIndex + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, 1) + } else { + step += 1 + } + if (!this.traceManager.inRange(step)) { + return + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.stepIntoBack = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.currentStepIndex + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, -1) + } else { + step -= 1 + } + if (!this.traceManager.inRange(step)) { + return + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.stepOverForward = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.traceManager.findStepOverForward(this.currentStepIndex) + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, 1) + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.stepOverBack = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.traceManager.findStepOverBack(this.currentStepIndex) + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, -1) + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.jumpOut = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.traceManager.findStepOut(this.currentStepIndex) + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, 0) + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.changeState = function (step) { + this.currentStepIndex = step + this.buttonNavigator.stepChanged(step) + this.event.trigger('stepChanged', [step]) +} + +module.exports = StepManager diff --git a/src/app/debugger/remix-debugger/src/ui/EthdebuggerUI.js b/src/app/debugger/remix-debugger/src/ui/EthdebuggerUI.js index 26c258edd9..7988addddf 100644 --- a/src/app/debugger/remix-debugger/src/ui/EthdebuggerUI.js +++ b/src/app/debugger/remix-debugger/src/ui/EthdebuggerUI.js @@ -1,6 +1,6 @@ 'use strict' // var TxBrowser = require('./TxBrowser') -var StepManager = require('./StepManager') +// var StepManager = require('./StepManager') var VmDebugger = require('./VmDebugger') var yo = require('yo-yo') @@ -91,16 +91,11 @@ EthdebuggerUI.prototype.stepChanged = function (stepIndex) { EthdebuggerUI.prototype.startDebugging = function (blockNumber, txIndex, tx) { const self = this if (this.debugger.traceManager.isLoading) { - return + return false } this.tx = tx - this.stepManager = new StepManager(this, this.debugger.traceManager) - this.stepManager.event.register('stepChanged', this, function (stepIndex) { - self.stepChanged(stepIndex) - }) - this.debugger.codeManager.event.register('changed', this, (code, address, instIndex) => { self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, this.currentStepIndex, this.debugger.solidityProxy.contracts, (error, sourceLocation) => { if (!error) { @@ -109,12 +104,14 @@ EthdebuggerUI.prototype.startDebugging = function (blockNumber, txIndex, tx) { }) }) + return true +} + +EthdebuggerUI.prototype.createAndAddVmDebugger = function () { this.vmDebugger = new VmDebugger(this, this.debugger.traceManager, this.debugger.codeManager, this.debugger.solidityProxy, this.debugger.callTree) yo.update(this.debuggerHeadPanelsView, this.vmDebugger.renderHead()) yo.update(this.debuggerPanelsView, this.vmDebugger.render()) yo.update(this.stepManagerView, this.stepManager.render()) - - this.debugger.debug(tx) } module.exports = EthdebuggerUI