diff --git a/apps/remix-ide-e2e/src/tests/debugger.test.ts b/apps/remix-ide-e2e/src/tests/debugger.test.ts
index b33dbd0ced..69bc648801 100644
--- a/apps/remix-ide-e2e/src/tests/debugger.test.ts
+++ b/apps/remix-ide-e2e/src/tests/debugger.test.ts
@@ -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')
},
@@ -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()
},
@@ -157,7 +175,7 @@ const sources = [
constructor() public {
- }
+ }
function createProject(string memory name, uint goal) public {
Project storage project = projects[projects.length];
@@ -166,7 +184,7 @@ const sources = [
project.state = State.Started;
project.goal = goal;
}
- }
+ }
`
}
},
@@ -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;
+ }
+ }
+ }
+ `
+ }
}
]
diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts
index b141204f68..a95f08aa6e 100644
--- a/apps/remix-ide-e2e/src/tests/terminal.test.ts
+++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts
@@ -80,7 +80,7 @@ const asyncAwait = `
resolve("Promise Resolved")
}, 5000)
})
- }
+ }
var run = async () => {
console.log('Waiting Promise')
diff --git a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js
index 4088021ba6..5df574bab0 100644
--- a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js
+++ b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js
@@ -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 = {}
diff --git a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js
index 592b2ef060..513dc962ea 100644
--- a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js
+++ b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js
@@ -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) {
@@ -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`
${this.basicPanel.render()}
`
return this.view
diff --git a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js
index 025f78bb18..2ddeef72ca 100644
--- a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js
+++ b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js
@@ -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]}
diff --git a/apps/remix-ide/src/app/ui/TreeView.js b/apps/remix-ide/src/app/ui/TreeView.js
index 5fc62d9552..59866a43c5 100644
--- a/apps/remix-ide/src/app/ui/TreeView.js
+++ b/apps/remix-ide/src/app/ui/TreeView.js
@@ -34,6 +34,9 @@ var css = csjs`
.label_value {
min-width: 10%;
}
+ .cursor_pointer {
+ cursor: pointer;
+ }
`
var EventManager = require('../../lib/events')
@@ -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 = []
}
@@ -111,6 +115,9 @@ class TreeView {
self.event.trigger('nodeRightClick', [keyPath, data, label, event])
}
li.appendChild(list)
+ if (data.hasNext) {
+ list.appendChild(yo`Load more`)
+ }
} else {
caret.style.visibility = 'hidden'
label.oncontextmenu = function (event) {
diff --git a/libs/remix-debug/src/debugger/VmDebugger.js b/libs/remix-debug/src/debugger/VmDebugger.js
index 374ba948fb..d92166c56d 100644
--- a/libs/remix-debug/src/debugger/VmDebugger.js
+++ b/libs/remix-debug/src/debugger/VmDebugger.js
@@ -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])
})
diff --git a/libs/remix-debug/src/debugger/solidityLocals.js b/libs/remix-debug/src/debugger/solidityLocals.js
index 7f2c0e7905..a93f828803 100644
--- a/libs/remix-debug/src/debugger/solidityLocals.js
+++ b/libs/remix-debug/src/debugger/solidityLocals.js
@@ -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'])
@@ -28,7 +29,7 @@ class DebuggerSolidityLocals {
}, 500)
}
- decode (sourceLocation) {
+ decode (sourceLocation, cursor) {
const self = this
this.event.trigger('solidityLocalsMessage', [''])
this.traceManager.waterfall([
@@ -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) {
@@ -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
diff --git a/libs/remix-debug/src/solidity-decoder/localDecoder.js b/libs/remix-debug/src/solidity-decoder/localDecoder.js
index 1b87c933ff..d88b0f4fe1 100644
--- a/libs/remix-debug/src/solidity-decoder/localDecoder.js
+++ b/libs/remix-debug/src/solidity-decoder/localDecoder.js
@@ -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' }
@@ -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] = ''
diff --git a/libs/remix-debug/src/solidity-decoder/types/ArrayType.js b/libs/remix-debug/src/solidity-decoder/types/ArrayType.js
index e508032b7c..d21ec1b015 100644
--- a/libs/remix-debug/src/solidity-decoder/types/ArrayType.js
+++ b/libs/remix-debug/src/solidity-decoder/types/ArrayType.js
@@ -72,7 +72,7 @@ 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') {
@@ -80,12 +80,28 @@ class ArrayType extends RefType {
length = parseInt(length, 16)
offset = offset + 32
}
- for (var k = 0; k < length; k++) {
+ if (isNaN(length)) {
+ return {
+ value: '',
+ 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)
+ }
}
}
diff --git a/libs/remix-debug/src/solidity-decoder/types/RefType.js b/libs/remix-debug/src/solidity-decoder/types/RefType.js
index c6f02e1ca0..f4ca0a16f2 100644
--- a/libs/remix-debug/src/solidity-decoder/types/RefType.js
+++ b/libs/remix-debug/src/solidity-decoder/types/RefType.js
@@ -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: '', type: this.typeName}
}
@@ -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: '', type: this.typeName}
}
diff --git a/libs/remix-debug/test/debugger.js b/libs/remix-debug/test/debugger.js
index 1e2b208a3c..43d8a9e5f8 100644
--- a/libs/remix-debug/test/debugger.js
+++ b/libs/remix-debug/test/debugger.js
@@ -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)