Merge pull request #185 from ethereum/sourceLocationStep2

step to next source location change
pull/7/head
chriseth 8 years ago committed by GitHub
commit 63702ccaef
  1. 21
      src/helpers/util.js
  2. 7
      src/ui/ButtonNavigator.js
  3. 8
      src/ui/CodeListView.js
  4. 6
      src/ui/DropdownPanel.js
  5. 47
      src/ui/Slider.js
  6. 64
      src/ui/StepManager.js
  7. 13
      src/util/internalCallTree.js
  8. 7
      test-browser/init.js
  9. 6
      test-browser/vmdebugger.js

@ -85,6 +85,27 @@ module.exports = {
return index >= 0 ? array[index] : null return index >= 0 ? array[index] : null
}, },
/*
Binary Search:
Assumes that @arg array is sorted increasingly
return Return i such that |array[i] - target| is smallest among all i and -1 for an empty array.
Returns the smallest i for multiple candidates.
*/
findClosestIndex: function (target, array) {
if (array.length === 0) {
return -1
}
var index = this.findLowerBound(target, array)
if (index < 0) {
return array[0]
} else if (index >= array.length - 1) {
return array[array.length - 1]
} else {
var middle = (array[index] + array[index + 1]) / 2
return target <= middle ? index : index + 1
}
},
/** /**
* Find the call from @args rootCall which contains @args index (recursive) * Find the call from @args rootCall which contains @args index (recursive)
* *

@ -10,7 +10,6 @@ function ButtonNavigator (_parent, _traceManager) {
this.overBackDisabled = true this.overBackDisabled = true
this.intoForwardDisabled = true this.intoForwardDisabled = true
this.overForwardDisabled = true this.overForwardDisabled = true
this.nextCallDisabled = true
this.jumpOutDisabled = true this.jumpOutDisabled = true
this.traceManager = _traceManager this.traceManager = _traceManager
@ -67,8 +66,6 @@ ButtonNavigator.prototype.render = function () {
</button> </button>
<button id='overforward' title='step over forward' class='fa fa-angle-double-right' style=${ui.formatCss(style.button)} onclick=${function () { self.event.trigger('stepOverForward') }} disabled=${this.overForwardDisabled} > <button id='overforward' title='step over forward' class='fa fa-angle-double-right' style=${ui.formatCss(style.button)} onclick=${function () { self.event.trigger('stepOverForward') }} disabled=${this.overForwardDisabled} >
</button> </button>
<button id='nextcall' title='step next call' class='fa fa-chevron-right' style=${ui.formatCss(style.button)} onclick=${function () { self.event.trigger('jumpNextCall') }} disabled=${this.nextCallDisabled} >
</button>
<button id='jumpout' title='jump out' class='fa fa-share' style=${ui.formatCss(style.button)} onclick=${function () { self.event.trigger('jumpOut') }} disabled=${this.jumpOutDisabled} > <button id='jumpout' title='jump out' class='fa fa-share' style=${ui.formatCss(style.button)} onclick=${function () { self.event.trigger('jumpOut') }} disabled=${this.jumpOutDisabled} >
</button> </button>
<div id='reverted' style="display:none"> <div id='reverted' style="display:none">
@ -90,7 +87,6 @@ ButtonNavigator.prototype.reset = function () {
this.overBackDisabled = true this.overBackDisabled = true
this.intoForwardDisabled = true this.intoForwardDisabled = true
this.overForwardDisabled = true this.overForwardDisabled = true
this.nextCallDisabled = true
this.jumpOutDisabled = true this.jumpOutDisabled = true
resetWarning(this) resetWarning(this)
} }
@ -110,8 +106,6 @@ ButtonNavigator.prototype.stepChanged = function (step) {
} else { } else {
self.intoForwardDisabled = step >= length - 1 self.intoForwardDisabled = step >= length - 1
self.overForwardDisabled = step >= length - 1 self.overForwardDisabled = step >= length - 1
var nextCall = self.traceManager.findNextCall(step)
self.nextCallDisabled = nextCall === step
var stepOut = self.traceManager.findStepOut(step) var stepOut = self.traceManager.findStepOut(step)
self.jumpOutDisabled = stepOut === step self.jumpOutDisabled = stepOut === step
} }
@ -126,7 +120,6 @@ ButtonNavigator.prototype.updateAll = function () {
this.updateDisabled('overback', this.overBackDisabled) this.updateDisabled('overback', this.overBackDisabled)
this.updateDisabled('overforward', this.overForwardDisabled) this.updateDisabled('overforward', this.overForwardDisabled)
this.updateDisabled('intoforward', this.intoForwardDisabled) this.updateDisabled('intoforward', this.intoForwardDisabled)
this.updateDisabled('nextcall', this.nextCallDisabled)
this.updateDisabled('jumpout', this.jumpOutDisabled) this.updateDisabled('jumpout', this.jumpOutDisabled)
this.updateDisabled('jumptoexception', this.jumpOutDisabled) this.updateDisabled('jumptoexception', this.jumpOutDisabled)
} }

@ -3,8 +3,10 @@ var style = require('./styles/basicStyles')
var yo = require('yo-yo') var yo = require('yo-yo')
var ui = require('../helpers/ui') var ui = require('../helpers/ui')
var DropdownPanel = require('./DropdownPanel') var DropdownPanel = require('./DropdownPanel')
var EventManager = require('../lib/eventManager')
function CodeListView (_parent, _codeManager) { function CodeListView (_parent, _codeManager) {
this.event = new EventManager()
this.parent = _parent this.parent = _parent
this.codeManager = _codeManager this.codeManager = _codeManager
this.code this.code
@ -12,6 +14,12 @@ function CodeListView (_parent, _codeManager) {
this.codeView this.codeView
this.itemSelected this.itemSelected
this.basicPanel = new DropdownPanel('Instructions', {json: false}) this.basicPanel = new DropdownPanel('Instructions', {json: false})
this.basicPanel.event.register('hide', () => {
this.event.trigger('hide', [])
})
this.basicPanel.event.register('show', () => {
this.event.trigger('show', [])
})
this.init() this.init()
} }

@ -4,8 +4,10 @@ var ui = require('../helpers/ui')
var styleDropdown = require('./styles/dropdownPanel') var styleDropdown = require('./styles/dropdownPanel')
var basicStyles = require('./styles/basicStyles') var basicStyles = require('./styles/basicStyles')
var TreeView = require('./TreeView') var TreeView = require('./TreeView')
var EventManager = require('../lib/eventManager')
function DropdownPanel (_name, _opts) { function DropdownPanel (_name, _opts) {
this.event = new EventManager()
if (!_opts) { if (!_opts) {
_opts = {} _opts = {}
} }
@ -70,9 +72,11 @@ DropdownPanel.prototype.toggle = function () {
if (el.style.display === '') { if (el.style.display === '') {
el.style.display = 'none' el.style.display = 'none'
caret.className = 'fa fa-caret-right' caret.className = 'fa fa-caret-right'
this.event.trigger('hide', [])
} else { } else {
el.style.display = '' el.style.display = ''
caret.className = 'fa fa-caret-down' caret.className = 'fa fa-caret-down'
this.event.trigger('show', [])
} }
} }
@ -82,6 +86,7 @@ DropdownPanel.prototype.hide = function () {
var el = this.view.querySelector('.dropdownpanel') var el = this.view.querySelector('.dropdownpanel')
el.style.display = 'none' el.style.display = 'none'
caret.className = 'fa fa-caret-right' caret.className = 'fa fa-caret-right'
this.event.trigger('hide', [])
} }
} }
@ -91,6 +96,7 @@ DropdownPanel.prototype.show = function () {
var el = this.view.querySelector('.dropdownpanel') var el = this.view.querySelector('.dropdownpanel')
el.style.display = '' el.style.display = ''
caret.className = 'fa fa-caret-down' caret.className = 'fa fa-caret-down'
this.event.trigger('show', [])
} }
} }

@ -4,17 +4,20 @@ var EventManager = require('../lib/eventManager')
var yo = require('yo-yo') var yo = require('yo-yo')
var ui = require('../helpers/ui') var ui = require('../helpers/ui')
function Slider (_traceManager) { class Slider {
constructor (_traceManager, _stepOverride) {
this.event = new EventManager() this.event = new EventManager()
this.traceManager = _traceManager this.traceManager = _traceManager
this.max this.max
this.disabled = true this.disabled = true
this.view this.view
this.solidityMode = false
this.stepOverride = _stepOverride
this.previousValue = null this.previousValue = null
} }
Slider.prototype.render = function () { render () {
var self = this var self = this
var view = yo`<div> var view = yo`<div>
<input <input
@ -32,39 +35,41 @@ Slider.prototype.render = function () {
this.view = view this.view = view
} }
return view return view
} }
Slider.prototype.init = function (length) { init (length) {
var slider = document.getElementById('slider') var slider = this.view.querySelector('#slider')
slider.setAttribute('max', length - 1) slider.setAttribute('max', length - 1)
this.max = length - 1 this.max = length - 1
this.updateDisabled(length === 0) this.updateDisabled(length === 0)
this.disabled = length === 0 this.disabled = length === 0
this.setValue(0) this.setValue(0)
} }
Slider.prototype.onChange = function (event) { onChange (event) {
var value = parseInt(document.getElementById('slider').value) 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 if (value === this.previousValue) return
this.previousValue = value this.previousValue = value
this.event.trigger('moved', [value]) this.event.trigger('moved', [value])
} }
Slider.prototype.setValue = function (value) { setValue (value) {
var slider = document.getElementById('slider') this.view.querySelector('#slider').value = value
var diff = value - slider.value
if (diff > 0) {
slider.stepUp(diff)
} else {
slider.stepDown(Math.abs(diff))
} }
}
Slider.prototype.updateDisabled = function (disabled) { updateDisabled (disabled) {
if (disabled) { if (disabled) {
document.getElementById('slider').setAttribute('disabled', true) this.view.querySelector('#slider').setAttribute('disabled', true)
} else { } else {
document.getElementById('slider').removeAttribute('disabled') this.view.querySelector('#slider').removeAttribute('disabled')
}
} }
} }

@ -3,11 +3,14 @@ var ButtonNavigator = require('./ButtonNavigator')
var Slider = require('./Slider') var Slider = require('./Slider')
var EventManager = require('../lib/eventManager') var EventManager = require('../lib/eventManager')
var yo = require('yo-yo') var yo = require('yo-yo')
var utils = require('../helpers/util.js')
function StepManager (_parent, _traceManager) { function StepManager (_parent, _traceManager) {
this.event = new EventManager() this.event = new EventManager()
this.parent = _parent this.parent = _parent
this.traceManager = _traceManager this.traceManager = _traceManager
this.sourceMapByAddress = {}
this.solidityMode = false
var self = this var self = this
this.parent.event.register('newTraceLoaded', this, function () { this.parent.event.register('newTraceLoaded', this, function () {
@ -21,11 +24,23 @@ function StepManager (_parent, _traceManager) {
}) })
}) })
this.slider = new Slider(this.traceManager) this.slider = new Slider(this.traceManager, (step) => {
return this.solidityMode ? this.resolveToReducedTrace(step, 0) : step
})
this.slider.event.register('moved', this, function (step) { this.slider.event.register('moved', this, function (step) {
self.sliderMoved(step) self.sliderMoved(step)
}) })
this.parent.callTree.event.register('callTreeReady', () => {
this.solidityMode = true
this.parent.vmDebugger.asmCode.event.register('hide', () => {
this.solidityMode = this.parent.callTree.reducedTrace.length !== 0
})
this.parent.vmDebugger.asmCode.event.register('show', () => {
this.solidityMode = false
})
})
this.buttonNavigator = new ButtonNavigator(_parent, this.traceManager) this.buttonNavigator = new ButtonNavigator(_parent, this.traceManager)
this.buttonNavigator.event.register('stepIntoBack', this, function () { this.buttonNavigator.event.register('stepIntoBack', this, function () {
self.stepIntoBack() self.stepIntoBack()
@ -39,9 +54,6 @@ function StepManager (_parent, _traceManager) {
this.buttonNavigator.event.register('stepOverForward', this, function () { this.buttonNavigator.event.register('stepOverForward', this, function () {
self.stepOverForward() self.stepOverForward()
}) })
this.buttonNavigator.event.register('jumpNextCall', this, function () {
self.jumpNextCall()
})
this.buttonNavigator.event.register('jumpOut', this, function () { this.buttonNavigator.event.register('jumpOut', this, function () {
self.jumpOut() self.jumpOut()
}) })
@ -50,6 +62,20 @@ function StepManager (_parent, _traceManager) {
}) })
} }
StepManager.prototype.resolveToReducedTrace = function (value, incr) {
if (this.parent.callTree.reducedTrace.length) {
var nextSource = utils.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 () { StepManager.prototype.render = function () {
return ( return (
yo`<div> yo`<div>
@ -93,7 +119,12 @@ StepManager.prototype.stepIntoForward = function () {
if (!this.traceManager.isLoaded()) { if (!this.traceManager.isLoaded()) {
return return
} }
var step = this.currentStepIndex + 1 var step = this.currentStepIndex
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, 1)
} else {
step += 1
}
if (!this.traceManager.inRange(step)) { if (!this.traceManager.inRange(step)) {
return return
} }
@ -105,7 +136,12 @@ StepManager.prototype.stepIntoBack = function () {
if (!this.traceManager.isLoaded()) { if (!this.traceManager.isLoaded()) {
return return
} }
var step = this.currentStepIndex - 1 var step = this.currentStepIndex
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, -1)
} else {
step -= 1
}
if (!this.traceManager.inRange(step)) { if (!this.traceManager.inRange(step)) {
return return
} }
@ -118,6 +154,9 @@ StepManager.prototype.stepOverForward = function () {
return return
} }
var step = this.traceManager.findStepOverForward(this.currentStepIndex) var step = this.traceManager.findStepOverForward(this.currentStepIndex)
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, 1)
}
this.slider.setValue(step) this.slider.setValue(step)
this.changeState(step) this.changeState(step)
} }
@ -127,15 +166,9 @@ StepManager.prototype.stepOverBack = function () {
return return
} }
var step = this.traceManager.findStepOverBack(this.currentStepIndex) var step = this.traceManager.findStepOverBack(this.currentStepIndex)
this.slider.setValue(step) if (this.solidityMode) {
this.changeState(step) step = this.resolveToReducedTrace(step, -1)
}
StepManager.prototype.jumpNextCall = function () {
if (!this.traceManager.isLoaded()) {
return
} }
var step = this.traceManager.findNextCall(this.currentStepIndex)
this.slider.setValue(step) this.slider.setValue(step)
this.changeState(step) this.changeState(step)
} }
@ -145,6 +178,9 @@ StepManager.prototype.jumpOut = function () {
return return
} }
var step = this.traceManager.findStepOut(this.currentStepIndex) var step = this.traceManager.findStepOut(this.currentStepIndex)
if (this.solidityMode) {
step = this.resolveToReducedTrace(step, 0)
}
this.slider.setValue(step) this.slider.setValue(step)
this.changeState(step) this.changeState(step)
} }

@ -37,6 +37,7 @@ class InternalCallTree {
this.event.trigger('callTreeBuildFailed', [result.error]) this.event.trigger('callTreeBuildFailed', [result.error])
} else { } else {
console.log('ready') console.log('ready')
createReducedTrace(this, traceManager.trace.length - 1)
this.event.trigger('callTreeReady', [this.scopes, this.scopeStarts]) this.event.trigger('callTreeReady', [this.scopes, this.scopeStarts])
} }
}) })
@ -59,6 +60,7 @@ class InternalCallTree {
this.scopeStarts = {} this.scopeStarts = {}
this.variableDeclarationByFile = {} this.variableDeclarationByFile = {}
this.astWalker = new AstWalker() this.astWalker = new AstWalker()
this.reducedTrace = []
} }
/** /**
@ -87,10 +89,17 @@ async function buildTree (tree, step, scopeId) {
let subScope = 1 let subScope = 1
tree.scopeStarts[step] = scopeId tree.scopeStarts[step] = scopeId
tree.scopes[scopeId] = { firstStep: step, locals: {} } tree.scopes[scopeId] = { firstStep: step, locals: {} }
var currentSourceLocation = {}
while (step < tree.traceManager.trace.length) { while (step < tree.traceManager.trace.length) {
var sourceLocation var sourceLocation
try { try {
sourceLocation = await extractSourceLocation(tree, step) sourceLocation = await extractSourceLocation(tree, step)
if (sourceLocation.start !== currentSourceLocation.start ||
sourceLocation.length !== currentSourceLocation.length ||
sourceLocation.file !== currentSourceLocation.file) {
tree.reducedTrace.push(step)
currentSourceLocation = sourceLocation
}
} catch (e) { } catch (e) {
return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e.message } return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e.message }
} }
@ -122,6 +131,10 @@ async function buildTree (tree, step, scopeId) {
return { outStep: step } return { outStep: step }
} }
function createReducedTrace (tree, index) {
tree.reducedTrace.push(index)
}
function includeVariableDeclaration (tree, step, sourceLocation, scopeId) { function includeVariableDeclaration (tree, step, sourceLocation, scopeId) {
var variableDeclaration = resolveVariableDeclaration(tree, step, sourceLocation) var variableDeclaration = resolveVariableDeclaration(tree, step, sourceLocation)
if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.attributes.name]) { if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.attributes.name]) {

@ -11,6 +11,13 @@ module.exports = function (browser, callback) {
} }
function extendBrowser (browser) { function extendBrowser (browser) {
browser.multipleClick = function (id, time) {
for (var k = 0; k < time; k++) {
browser.click(id)
}
return browser
}
browser.assertCurrentSelectedItem = function (expected) { browser.assertCurrentSelectedItem = function (expected) {
browser.execute(function (id) { browser.execute(function (id) {
var node = document.querySelector('#asmcodes div div[selected="selected"] span') var node = document.querySelector('#asmcodes div div[selected="selected"] span')

@ -62,7 +62,7 @@ function panels (browser) {
.clearValue('#txinput') .clearValue('#txinput')
.setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
.click('#load') .click('#load')
.click('#nextcall') .multipleClick('#intoforward', 63)
.assertStack(['0:0x', '1:0x60', '2:0x65', '3:0x38', '4:0x55', '5:0x60fe47b1']) .assertStack(['0:0x', '1:0x60', '2:0x65', '3:0x38', '4:0x55', '5:0x60fe47b1'])
.assertStorageChanges(['0x00:0x38']) .assertStorageChanges(['0x00:0x38'])
.assertCallData(['0:0x60fe47b10000000000000000000000000000000000000000000000000000000000000038']) .assertCallData(['0:0x60fe47b10000000000000000000000000000000000000000000000000000000000000038'])
@ -121,7 +121,7 @@ function stepping (browser) {
.click('#intoback') .click('#intoback')
.click('#intoback') .click('#intoback')
.assertCurrentSelectedItem('002 PUSH1 40') .assertCurrentSelectedItem('002 PUSH1 40')
.click('#nextcall') .multipleClick('#intoforward', 62)
.assertCurrentSelectedItem('181 CREATE') .assertCurrentSelectedItem('181 CREATE')
.click('#intoforward') .click('#intoforward')
.click('#intoforward') .click('#intoforward')
@ -170,7 +170,7 @@ function stepdetail (browser) {
}) })
*/ */
.assertStepDetail('6', '6', '', '3', '84476', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') .assertStepDetail('6', '6', '', '3', '84476', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
.click('#nextcall') .multipleClick('#intoforward', 57)
.assertStepDetail('63', '63', '', '32000', '79283', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') .assertStepDetail('63', '63', '', '32000', '79283', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
.click('#overforward') .click('#overforward')
.click('#intoback') .click('#intoback')

Loading…
Cancel
Save