Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions apps/remix-ide-e2e/src/tests/debugger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ module.exports = {
.waitForElementVisible('*[data-id="slider"]')
.click('*[data-id="slider"]')
.setValue('*[data-id="slider"]', '50')
.pause(2000)
.assert.containsText('*[data-id="solidityLocals"]', 'no locals')
.assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n92')
},
Expand Down Expand Up @@ -130,6 +129,25 @@ module.exports = {
.goToVMTraceStep(717)
.pause(5000)
.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()
},

Expand Down Expand Up @@ -157,7 +175,7 @@ const sources = [

constructor() public {

}
}

function createProject(string memory name, uint goal) public {
Project storage project = projects[projects.length];
Expand All @@ -166,7 +184,7 @@ const sources = [
project.state = State.Started;
project.goal = goal;
}
}
}
`
}
},
Expand Down Expand Up @@ -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;
}
}
}
`
}
}
]

Expand Down
2 changes: 1 addition & 1 deletion apps/remix-ide-e2e/src/tests/terminal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const asyncAwait = `
resolve("Promise Resolved")
}, 5000)
})
}
}

var run = async () => {
console.log('Waiting Promise')
Expand Down
4 changes: 4 additions & 0 deletions apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,13 @@ function VmDebugger (vmDebuggerLogic) {
this.vmDebuggerLogic.event.register('solidityStateUpdating', this.solidityState.setUpdating.bind(this.solidityState))

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('solidityLocalsMessage', this.solidityLocals.setMessage.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.data = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,28 @@ var yo = require('yo-yo')

class SolidityLocals {

constructor (_parent, _traceManager, _internalTreeCall) {
constructor () {
this.event = new EventManager()
this.basicPanel = new DropdownPanel('Solidity Locals', {
json: true,
formatSelf: solidityTypeFormatter.formatSelf,
extractData: solidityTypeFormatter.extractData
extractData: solidityTypeFormatter.extractData,
loadMore: (cursor) => {
this.event.trigger('solidityLocalsLoadMore', [cursor])
}
})
this.view
this._data = null
}

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) {
Expand All @@ -28,6 +38,18 @@ class SolidityLocals {
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 () {
this.view = yo`<div id='soliditylocals' data-id="solidityLocals">${this.basicPanel.render()}</div>`
return this.view
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ function extractData (item, parent, key) {
})
ret.isArray = true
ret.self = parent.isArray ? '' : item.type
ret.cursor = item.cursor
ret.hasNext = item.hasNext
} else if (item.type.indexOf('struct') === 0) {
ret.children = Object.keys((item.value || {})).map(function (key) {
return {key: key, value: item.value[key]}
Expand Down
7 changes: 7 additions & 0 deletions apps/remix-ide/src/app/ui/TreeView.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ var css = csjs`
.label_value {
min-width: 10%;
}
.cursor_pointer {
cursor: pointer;
}
`

var EventManager = require('../../lib/events')
Expand All @@ -49,6 +52,7 @@ class TreeView {
this.event = new EventManager()
this.extractData = opts.extractData || this.extractDataDefault
this.formatSelf = opts.formatSelf || this.formatSelfDefault
this.loadMore = opts.loadMore
this.view = null
this.expandPath = []
}
Expand Down Expand Up @@ -111,6 +115,9 @@ class TreeView {
self.event.trigger('nodeRightClick', [keyPath, data, label, event])
}
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 {
caret.style.visibility = 'hidden'
label.oncontextmenu = function (event) {
Expand Down
4 changes: 4 additions & 0 deletions libs/remix-debug/src/debugger/VmDebugger.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ class VmDebuggerLogic {

listenToSolidityLocalsEvents () {
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.event.trigger('solidityLocals', [state])
})
Expand Down
30 changes: 23 additions & 7 deletions libs/remix-debug/src/debugger/solidityLocals.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class DebuggerSolidityLocals {
}

init (sourceLocation) {
this._sourceLocation = sourceLocation
var decodeTimeout = null
if (!this.storageResolver) {
return this.event.trigger('solidityLocalsMessage', ['storage not ready'])
Expand All @@ -28,7 +29,7 @@ class DebuggerSolidityLocals {
}, 500)
}

decode (sourceLocation) {
decode (sourceLocation, cursor) {
const self = this
this.event.trigger('solidityLocalsMessage', [''])
this.traceManager.waterfall([
Expand Down Expand Up @@ -65,12 +66,18 @@ class DebuggerSolidityLocals {
var memory = result[1].value
try {
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) => {
if (!locals.error) {
this.event.trigger('solidityLocals', [locals])
}
if (!Object.keys(locals).length) {
this.event.trigger('solidityLocalsMessage', ['no locals'])
localDecoder.solidityLocals(this.stepManager.currentStepIndex, this.internalTreeCall, stack, memory, storageViewer, sourceLocation, cursor).then((locals) => {
if (!cursor) {
if (!locals.error) {
this.event.trigger('solidityLocals', [locals])
}
if (!Object.keys(locals).length) {
this.event.trigger('solidityLocalsMessage', ['no locals'])
}
} else {
if (!locals.error) {
this.event.trigger('solidityLocalsLoadMoreCompleted', [locals])
}
}
})
} catch (e) {
Expand All @@ -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
4 changes: 2 additions & 2 deletions libs/remix-debug/src/solidity-decoder/localDecoder.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'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)
if (!scope) {
const error = { 'message': 'Can\'t display locals. reason: compilation result might not have been provided' }
Expand All @@ -18,7 +18,7 @@ async function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory, st
anonymousIncr++
}
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) {
console.log(e)
locals[name] = '<decoding failed - ' + e.message + '>'
Expand Down
22 changes: 19 additions & 3 deletions libs/remix-debug/src/solidity-decoder/types/ArrayType.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,36 @@ class ArrayType extends RefType {
return {value: ret, length: '0x' + size.toString(16), type: this.typeName}
}

decodeFromMemoryInternal (offset, memory) {
decodeFromMemoryInternal (offset, memory, skip) {
const ret = []
let length = this.arraySize
if (this.arraySize === 'dynamic') {
length = memory.substr(2 * offset, 64)
length = parseInt(length, 16)
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
ret.push(this.underlyingType.decodeFromMemory(contentOffset, memory))
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)
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions libs/remix-debug/src/solidity-decoder/types/RefType.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class RefType {
* @param {Object} - storageResolver
* @return {Object} decoded value
*/
async decodeFromStack (stackDepth, stack, memory, storageResolver) {
async decodeFromStack (stackDepth, stack, memory, storageResolver, cursor) {
if (stack.length - 1 < stackDepth) {
return {error: '<decoding failed - stack underflow ' + stackDepth + '>', type: this.typeName}
}
Expand All @@ -34,7 +34,7 @@ class RefType {
}
} else if (this.isInMemory()) {
offset = parseInt(offset, 16)
return this.decodeFromMemoryInternal(offset, memory)
return this.decodeFromMemoryInternal(offset, memory, cursor)
} else {
return {error: '<decoding failed - no decoder for ' + this.location + '>', type: this.typeName}
}
Expand Down
2 changes: 1 addition & 1 deletion libs/remix-debug/test/debugger.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ function testDebugging (debugManager) {

tape('traceManager.decodeLocalsAt', async (t) => {
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 {
const address = debugManager.traceManager.getCurrentCalledAddressAt(330)
const location = await debugManager.sourceLocationFromVMTraceIndex(address, 330)
Expand Down