Unverified Commit 8f4f9118 authored by yann300's avatar yann300 Committed by GitHub

Merge pull request #479 from ethereum/slider-bug

Fix Debugger Slider Bug
parents 7a1cbfef 8d7be6dd
...@@ -42,7 +42,6 @@ module.exports = { ...@@ -42,7 +42,6 @@ module.exports = {
.waitForElementVisible('*[data-id="slider"]') .waitForElementVisible('*[data-id="slider"]')
.click('*[data-id="slider"]') .click('*[data-id="slider"]')
.setValue('*[data-id="slider"]', '50') .setValue('*[data-id="slider"]', '50')
.pause(2000)
.assert.containsText('*[data-id="solidityLocals"]', 'no locals') .assert.containsText('*[data-id="solidityLocals"]', 'no locals')
.assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n92') .assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n92')
}, },
...@@ -130,6 +129,25 @@ module.exports = { ...@@ -130,6 +129,25 @@ module.exports = {
.goToVMTraceStep(717) .goToVMTraceStep(717)
.pause(5000) .pause(5000)
.checkVariableDebug('soliditylocals', localVariable_step717_ABIEncoder) // all locals should be initiaed .checkVariableDebug('soliditylocals', localVariable_step717_ABIEncoder) // all locals should be initiaed
},
'Should load more solidity locals array': function (browser: NightwatchBrowser) {
browser.addFile('locals.sol', sources[3]['browser/locals.sol'])
.clickLaunchIcon('udapp')
.createContract('')
.clickInstance(3)
.clickFunction('t - transact (not payable)')
.pause(2000)
.debugTransaction(6)
.waitForElementVisible('*[data-id="slider"]')
.click('*[data-id="slider"]')
.setValue('*[data-id="slider"]', '5000')
.waitForElementPresent('*[data-id="treeViewTogglearray"]')
.click('*[data-id="treeViewTogglearray"]')
.waitForElementPresent('*[data-id="treeViewLoadMore"]')
.click('*[data-id="treeViewLoadMore"]')
.assert.containsText('*[data-id="solidityLocals"]', '149: 0 uint256')
.notContainsText('*[data-id="solidityLocals"]', '150: 0 uint256')
.end() .end()
}, },
...@@ -157,7 +175,7 @@ const sources = [ ...@@ -157,7 +175,7 @@ const sources = [
constructor() public { constructor() public {
} }
function createProject(string memory name, uint goal) public { function createProject(string memory name, uint goal) public {
Project storage project = projects[projects.length]; Project storage project = projects[projects.length];
...@@ -166,7 +184,7 @@ const sources = [ ...@@ -166,7 +184,7 @@ const sources = [
project.state = State.Started; project.state = State.Started;
project.goal = goal; project.goal = goal;
} }
} }
` `
} }
}, },
...@@ -194,6 +212,21 @@ const sources = [ ...@@ -194,6 +212,21 @@ const sources = [
} }
} }
`} `}
},
{
'browser/locals.sol': {
content: `
pragma solidity ^0.6.0;
contract test {
function t () public {
uint[] memory array = new uint[](150);
for (uint k = 0; k < 150; k++) {
array[k] = k;
}
}
}
`
}
} }
] ]
......
...@@ -80,7 +80,7 @@ const asyncAwait = ` ...@@ -80,7 +80,7 @@ const asyncAwait = `
resolve("Promise Resolved") resolve("Promise Resolved")
}, 5000) }, 5000)
}) })
} }
var run = async () => { var run = async () => {
console.log('Waiting Promise') console.log('Waiting Promise')
......
...@@ -83,9 +83,13 @@ function VmDebugger (vmDebuggerLogic) { ...@@ -83,9 +83,13 @@ function VmDebugger (vmDebuggerLogic) {
this.vmDebuggerLogic.event.register('solidityStateUpdating', this.solidityState.setUpdating.bind(this.solidityState)) this.vmDebuggerLogic.event.register('solidityStateUpdating', this.solidityState.setUpdating.bind(this.solidityState))
this.solidityLocals = new SolidityLocals() this.solidityLocals = new SolidityLocals()
this.solidityLocals.event.register('solidityLocalsLoadMore', (cursor) => {
this.vmDebuggerLogic.event.trigger('solidityLocalsLoadMore', [cursor])
})
this.vmDebuggerLogic.event.register('solidityLocals', this.solidityLocals.update.bind(this.solidityLocals)) this.vmDebuggerLogic.event.register('solidityLocals', this.solidityLocals.update.bind(this.solidityLocals))
this.vmDebuggerLogic.event.register('solidityLocalsMessage', this.solidityLocals.setMessage.bind(this.solidityLocals)) this.vmDebuggerLogic.event.register('solidityLocalsMessage', this.solidityLocals.setMessage.bind(this.solidityLocals))
this.vmDebuggerLogic.event.register('solidityLocalsUpdating', this.solidityLocals.setUpdating.bind(this.solidityLocals)) this.vmDebuggerLogic.event.register('solidityLocalsUpdating', this.solidityLocals.setUpdating.bind(this.solidityLocals))
this.vmDebuggerLogic.event.register('solidityLocalsLoadMoreCompleted', this.solidityLocals.loadMore.bind(this.solidityLocals))
this.returnValuesPanel = new DropdownPanel('Return Value', {json: true}) this.returnValuesPanel = new DropdownPanel('Return Value', {json: true})
this.returnValuesPanel.data = {} this.returnValuesPanel.data = {}
......
...@@ -6,18 +6,28 @@ var yo = require('yo-yo') ...@@ -6,18 +6,28 @@ var yo = require('yo-yo')
class SolidityLocals { class SolidityLocals {
constructor (_parent, _traceManager, _internalTreeCall) { constructor () {
this.event = new EventManager() this.event = new EventManager()
this.basicPanel = new DropdownPanel('Solidity Locals', { this.basicPanel = new DropdownPanel('Solidity Locals', {
json: true, json: true,
formatSelf: solidityTypeFormatter.formatSelf, formatSelf: solidityTypeFormatter.formatSelf,
extractData: solidityTypeFormatter.extractData extractData: solidityTypeFormatter.extractData,
loadMore: (cursor) => {
this.event.trigger('solidityLocalsLoadMore', [cursor])
}
}) })
this.view this.view
this._data = null
} }
update (data) { update (data) {
this.basicPanel.update(data) this._data = data
this.basicPanel.update(this._data)
}
loadMore (data) {
this._data = this.mergeLocals(data, this._data)
this.basicPanel.update(this._data)
} }
setMessage (message) { setMessage (message) {
...@@ -28,6 +38,18 @@ class SolidityLocals { ...@@ -28,6 +38,18 @@ class SolidityLocals {
this.basicPanel.setUpdating() this.basicPanel.setUpdating()
} }
mergeLocals (locals1, locals2) {
Object.keys(locals2).map(item => {
if (locals2[item].cursor && (parseInt(locals2[item].cursor) < parseInt(locals1[item].cursor))) {
locals2[item] = {
...locals1[item],
value: [...locals2[item].value, ...locals1[item].value]
}
}
})
return locals2
}
render () { render () {
this.view = yo`<div id='soliditylocals' data-id="solidityLocals">${this.basicPanel.render()}</div>` this.view = yo`<div id='soliditylocals' data-id="solidityLocals">${this.basicPanel.render()}</div>`
return this.view return this.view
......
...@@ -34,6 +34,8 @@ function extractData (item, parent, key) { ...@@ -34,6 +34,8 @@ function extractData (item, parent, key) {
}) })
ret.isArray = true ret.isArray = true
ret.self = parent.isArray ? '' : item.type ret.self = parent.isArray ? '' : item.type
ret.cursor = item.cursor
ret.hasNext = item.hasNext
} else if (item.type.indexOf('struct') === 0) { } else if (item.type.indexOf('struct') === 0) {
ret.children = Object.keys((item.value || {})).map(function (key) { ret.children = Object.keys((item.value || {})).map(function (key) {
return {key: key, value: item.value[key]} return {key: key, value: item.value[key]}
......
...@@ -34,6 +34,9 @@ var css = csjs` ...@@ -34,6 +34,9 @@ var css = csjs`
.label_value { .label_value {
min-width: 10%; min-width: 10%;
} }
.cursor_pointer {
cursor: pointer;
}
` `
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
...@@ -49,6 +52,7 @@ class TreeView { ...@@ -49,6 +52,7 @@ class TreeView {
this.event = new EventManager() this.event = new EventManager()
this.extractData = opts.extractData || this.extractDataDefault this.extractData = opts.extractData || this.extractDataDefault
this.formatSelf = opts.formatSelf || this.formatSelfDefault this.formatSelf = opts.formatSelf || this.formatSelfDefault
this.loadMore = opts.loadMore
this.view = null this.view = null
this.expandPath = [] this.expandPath = []
} }
...@@ -111,6 +115,9 @@ class TreeView { ...@@ -111,6 +115,9 @@ class TreeView {
self.event.trigger('nodeRightClick', [keyPath, data, label, event]) self.event.trigger('nodeRightClick', [keyPath, data, label, event])
} }
li.appendChild(list) li.appendChild(list)
if (data.hasNext) {
list.appendChild(yo`<li><span class="w-100 text-primary ${css.cursor_pointer}" data-id="treeViewLoadMore" onclick="${() => self.loadMore(data.cursor)}">Load more</span></li>`)
}
} else { } else {
caret.style.visibility = 'hidden' caret.style.visibility = 'hidden'
label.oncontextmenu = function (event) { label.oncontextmenu = function (event) {
......
...@@ -228,6 +228,10 @@ class VmDebuggerLogic { ...@@ -228,6 +228,10 @@ class VmDebuggerLogic {
listenToSolidityLocalsEvents () { listenToSolidityLocalsEvents () {
this.event.register('sourceLocationChanged', this.debuggerSolidityLocals.init.bind(this.debuggerSolidityLocals)) this.event.register('sourceLocationChanged', this.debuggerSolidityLocals.init.bind(this.debuggerSolidityLocals))
this.event.register('solidityLocalsLoadMore', this.debuggerSolidityLocals.decodeMore.bind(this.debuggerSolidityLocals))
this.debuggerSolidityLocals.event.register('solidityLocalsLoadMoreCompleted', (locals) => {
this.event.trigger('solidityLocalsLoadMoreCompleted', [locals])
})
this.debuggerSolidityLocals.event.register('solidityLocals', (state) => { this.debuggerSolidityLocals.event.register('solidityLocals', (state) => {
this.event.trigger('solidityLocals', [state]) this.event.trigger('solidityLocals', [state])
}) })
......
...@@ -15,6 +15,7 @@ class DebuggerSolidityLocals { ...@@ -15,6 +15,7 @@ class DebuggerSolidityLocals {
} }
init (sourceLocation) { init (sourceLocation) {
this._sourceLocation = sourceLocation
var decodeTimeout = null var decodeTimeout = null
if (!this.storageResolver) { if (!this.storageResolver) {
return this.event.trigger('solidityLocalsMessage', ['storage not ready']) return this.event.trigger('solidityLocalsMessage', ['storage not ready'])
...@@ -28,7 +29,7 @@ class DebuggerSolidityLocals { ...@@ -28,7 +29,7 @@ class DebuggerSolidityLocals {
}, 500) }, 500)
} }
decode (sourceLocation) { decode (sourceLocation, cursor) {
const self = this const self = this
this.event.trigger('solidityLocalsMessage', ['']) this.event.trigger('solidityLocalsMessage', [''])
this.traceManager.waterfall([ this.traceManager.waterfall([
...@@ -65,12 +66,18 @@ class DebuggerSolidityLocals { ...@@ -65,12 +66,18 @@ class DebuggerSolidityLocals {
var memory = result[1].value var memory = result[1].value
try { try {
var storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: result[2].value }, this.storageResolver, this.traceManager) var storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: result[2].value }, this.storageResolver, this.traceManager)
localDecoder.solidityLocals(this.stepManager.currentStepIndex, this.internalTreeCall, stack, memory, storageViewer, sourceLocation).then((locals) => { localDecoder.solidityLocals(this.stepManager.currentStepIndex, this.internalTreeCall, stack, memory, storageViewer, sourceLocation, cursor).then((locals) => {
if (!locals.error) { if (!cursor) {
this.event.trigger('solidityLocals', [locals]) if (!locals.error) {
} this.event.trigger('solidityLocals', [locals])
if (!Object.keys(locals).length) { }
this.event.trigger('solidityLocalsMessage', ['no locals']) if (!Object.keys(locals).length) {
this.event.trigger('solidityLocalsMessage', ['no locals'])
}
} else {
if (!locals.error) {
this.event.trigger('solidityLocalsLoadMoreCompleted', [locals])
}
} }
}) })
} catch (e) { } catch (e) {
...@@ -79,6 +86,15 @@ class DebuggerSolidityLocals { ...@@ -79,6 +86,15 @@ class DebuggerSolidityLocals {
}) })
} }
decodeMore (cursor) {
let decodeTimeout = null
if (!this.storageResolver) return this.event.trigger('solidityLocalsMessage', ['storage not ready'])
if (decodeTimeout) window.clearTimeout(decodeTimeout)
decodeTimeout = setTimeout(() => {
this.decode(this._sourceLocation, cursor)
}, 500)
}
} }
module.exports = DebuggerSolidityLocals module.exports = DebuggerSolidityLocals
'use strict' 'use strict'
async function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory, storageResolver, currentSourceLocation) { async function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory, storageResolver, currentSourceLocation, cursor) {
const scope = internalTreeCall.findScope(vmtraceIndex) const scope = internalTreeCall.findScope(vmtraceIndex)
if (!scope) { if (!scope) {
const error = { 'message': 'Can\'t display locals. reason: compilation result might not have been provided' } const error = { 'message': 'Can\'t display locals. reason: compilation result might not have been provided' }
...@@ -18,7 +18,7 @@ async function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory, st ...@@ -18,7 +18,7 @@ async function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory, st
anonymousIncr++ anonymousIncr++
} }
try { try {
locals[name] = await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageResolver) locals[name] = await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageResolver, cursor)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
locals[name] = '<decoding failed - ' + e.message + '>' locals[name] = '<decoding failed - ' + e.message + '>'
......
...@@ -72,7 +72,7 @@ class ArrayType extends RefType { ...@@ -72,7 +72,7 @@ class ArrayType extends RefType {
return {value: ret, length: '0x' + size.toString(16), type: this.typeName} return {value: ret, length: '0x' + size.toString(16), type: this.typeName}
} }
decodeFromMemoryInternal (offset, memory) { decodeFromMemoryInternal (offset, memory, skip) {
const ret = [] const ret = []
let length = this.arraySize let length = this.arraySize
if (this.arraySize === 'dynamic') { if (this.arraySize === 'dynamic') {
...@@ -80,12 +80,28 @@ class ArrayType extends RefType { ...@@ -80,12 +80,28 @@ class ArrayType extends RefType {
length = parseInt(length, 16) length = parseInt(length, 16)
offset = offset + 32 offset = offset + 32
} }
for (var k = 0; k < length; k++) { if (isNaN(length)) {
return {
value: '<decoding failed - length is NaN>',
type: this.typeName
}
}
if (!skip) skip = 0
if (skip) offset = offset + (32 * skip)
let limit = length - skip
if (limit > 100) limit = 100
for (var k = 0; k < limit; k++) {
var contentOffset = offset var contentOffset = offset
ret.push(this.underlyingType.decodeFromMemory(contentOffset, memory)) ret.push(this.underlyingType.decodeFromMemory(contentOffset, memory))
offset += 32 offset += 32
} }
return {value: ret, length: '0x' + length.toString(16), type: this.typeName} return {
value: ret,
length: '0x' + length.toString(16),
type: this.typeName,
cursor: skip + limit,
hasNext: length > (skip + limit)
}
} }
} }
......
...@@ -19,7 +19,7 @@ class RefType { ...@@ -19,7 +19,7 @@ class RefType {
* @param {Object} - storageResolver * @param {Object} - storageResolver
* @return {Object} decoded value * @return {Object} decoded value
*/ */
async decodeFromStack (stackDepth, stack, memory, storageResolver) { async decodeFromStack (stackDepth, stack, memory, storageResolver, cursor) {
if (stack.length - 1 < stackDepth) { if (stack.length - 1 < stackDepth) {
return {error: '<decoding failed - stack underflow ' + stackDepth + '>', type: this.typeName} return {error: '<decoding failed - stack underflow ' + stackDepth + '>', type: this.typeName}
} }
...@@ -34,7 +34,7 @@ class RefType { ...@@ -34,7 +34,7 @@ class RefType {
} }
} else if (this.isInMemory()) { } else if (this.isInMemory()) {
offset = parseInt(offset, 16) offset = parseInt(offset, 16)
return this.decodeFromMemoryInternal(offset, memory) return this.decodeFromMemoryInternal(offset, memory, cursor)
} else { } else {
return {error: '<decoding failed - no decoder for ' + this.location + '>', type: this.typeName} return {error: '<decoding failed - no decoder for ' + this.location + '>', type: this.typeName}
} }
......
...@@ -257,7 +257,7 @@ function testDebugging (debugManager) { ...@@ -257,7 +257,7 @@ function testDebugging (debugManager) {
tape('traceManager.decodeLocalsAt', async (t) => { tape('traceManager.decodeLocalsAt', async (t) => {
t.plan(1) t.plan(1)
const tested = JSON.parse('{"proposalNames":{"value":[{"value":"0x48656C6C6F20576F726C64210000000000000000000000000000000000000000","type":"bytes32"}],"length":"0x1","type":"bytes32[]"},"p":{"value":"45","type":"uint256"},"addressLocal":{"value":"0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB","type":"address"},"i":{"value":"2","type":"uint256"},"proposalsLocals":{"value":[{"value":{"name":{"value":"0x48656C6C6F20576F726C64210000000000000000000000000000000000000000","type":"bytes32"},"voteCount":{"value":"0","type":"uint256"}},"type":"struct Ballot.Proposal"}],"length":"0x1","type":"struct Ballot.Proposal[]"}}') const tested = JSON.parse('{"proposalNames":{"value":[{"value":"0x48656C6C6F20576F726C64210000000000000000000000000000000000000000","type":"bytes32"}],"length":"0x1","type":"bytes32[]","cursor":1,"hasNext":false},"p":{"value":"45","type":"uint256"},"addressLocal":{"value":"0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB","type":"address"},"i":{"value":"2","type":"uint256"},"proposalsLocals":{"value":[{"value":{"name":{"value":"0x48656C6C6F20576F726C64210000000000000000000000000000000000000000","type":"bytes32"},"voteCount":{"value":"0","type":"uint256"}},"type":"struct Ballot.Proposal"}],"length":"0x1","type":"struct Ballot.Proposal[]"}}')
try { try {
const address = debugManager.traceManager.getCurrentCalledAddressAt(330) const address = debugManager.traceManager.getCurrentCalledAddressAt(330)
const location = await debugManager.sourceLocationFromVMTraceIndex(address, 330) const location = await debugManager.sourceLocationFromVMTraceIndex(address, 330)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment