Merge pull request #1467 from ethereum/stackTrace

API for returning the function stack trace by vm trace index
pull/5370/head
yann300 4 years ago committed by GitHub
commit 0fa87a3a76
  1. 2
      package.json
  2. 2
      remix-debug/src/debugger/VmDebugger.js
  3. 54
      remix-debug/src/solidity-decoder/internalCallTree.js
  4. 16
      remix-debug/test/decoder/contracts/intLocal.js
  5. 37
      remix-debug/test/decoder/localsTests/int.js

@ -7,7 +7,7 @@
"scripts": {
"diff": "lerna diff",
"updated": "lerna updated",
"bootstrap": "npm run build-ts-packages; lerna bootstrap",
"bootstrap": "lerna bootstrap; npm run build-ts-packages",
"build-ts-packages": "lerna run --scope 'remix-analyzer' --scope 'remix-astwalker' --scope 'remix-solidity' --scope 'remix-tests' --scope 'remix-url-resolver' build",
"publish": "npm run build-ts-packages; lerna publish",
"release": "lerna bootstrap; lerna publish;",

@ -58,6 +58,8 @@ class VmDebuggerLogic {
this.event.trigger('indexUpdate', [index])
this.event.trigger('functionsStackUpdate', [this._callTree.retrieveFunctionsStack(index)])
this._traceManager.getCallDataAt(index, (error, calldata) => {
if (error) {
// console.log(error)

@ -66,6 +66,7 @@ class InternalCallTree {
*/
this.sourceLocationTracker.clearCache()
this.functionCallStack = []
this.functionDefinitionsByScope = {}
this.scopeStarts = {}
this.variableDeclarationByFile = {}
this.functionDefinitionByFile = {}
@ -79,21 +80,49 @@ class InternalCallTree {
* @param {Int} vmtraceIndex - index on the vm trace
*/
findScope (vmtraceIndex) {
const scopes = Object.keys(this.scopeStarts)
if (!scopes.length) {
return null
}
let scopeId = util.findLowerBoundValue(vmtraceIndex, scopes)
scopeId = this.scopeStarts[scopeId]
let scopeId = this.findScopeId(vmtraceIndex)
if (scopeId !== '' && !scopeId) return null
let scope = this.scopes[scopeId]
while (scope.lastStep && scope.lastStep < vmtraceIndex && scope.firstStep > 0) {
const matched = scopeId.match(/(.\d|\d)$/)
scopeId = scopeId.replace(matched[1], '')
scopeId = this.parentScope(scopeId)
scope = this.scopes[scopeId]
}
return scope
}
parentScope (scopeId) {
const matched = scopeId.match(/(.\d|\d)$/)
return scopeId.replace(matched[1], '')
}
findScopeId (vmtraceIndex) {
const scopes = Object.keys(this.scopeStarts)
if (!scopes.length) return null
const scopeStart = util.findLowerBoundValue(vmtraceIndex, scopes)
return this.scopeStarts[scopeStart]
}
retrieveFunctionsStack (vmtraceIndex) {
let scope = this.findScope(vmtraceIndex)
if (!scope) return []
let scopeId = this.scopeStarts[scope.firstStep]
let functions = []
if (!scopeId) return functions
let i = 0
while (true) {
i += 1
if (i > 1000) throw new Error('retrieFunctionStack: recursion too deep')
let functionDefinition = this.functionDefinitionsByScope[scopeId]
if (functionDefinition !== undefined) {
functions.push(functionDefinition)
}
let parent = this.parentScope(scopeId)
if (!parent) break
else scopeId = parent
}
return functions
}
extractSourceLocation (step) {
return new Promise((resolve, reject) => {
this.traceManager.getCurrentCalledAddressAt(step, (error, address) => {
@ -220,6 +249,7 @@ function includeVariableDeclaration (tree, step, sourceLocation, scopeId, newLoc
const functionDefinition = resolveFunctionDefinition(tree, step, previousSourceLocation)
if (functionDefinition && (newLocation && traceHelper.isJumpDestInstruction(tree.traceManager.trace[step - 1]) || functionDefinition.attributes.isConstructor)) {
tree.functionCallStack.push(step)
const functionDefinitionAndInputs = {functionDefinition, inputs: []}
// means: the previous location was a function definition && JUMPDEST
// => we are at the beginning of the function and input/output are setup
tree.solidityProxy.contractNameAt(step, (error, contractName) => { // cached
@ -240,7 +270,9 @@ function includeVariableDeclaration (tree, step, sourceLocation, scopeId, newLoc
}
}
// input params
if (inputs) addParams(inputs, tree, scopeId, states, contractName, previousSourceLocation, stack.length, inputs.children.length, -1)
if (inputs) {
functionDefinitionAndInputs.inputs = addParams(inputs, tree, scopeId, states, contractName, previousSourceLocation, stack.length, inputs.children.length, -1)
}
// output params
if (outputs) addParams(outputs, tree, scopeId, states, contractName, previousSourceLocation, stack.length, 0, 1)
}
@ -248,6 +280,7 @@ function includeVariableDeclaration (tree, step, sourceLocation, scopeId, newLoc
})
}
})
tree.functionDefinitionsByScope[scopeId] = functionDefinitionAndInputs
}
}
@ -304,6 +337,7 @@ function extractFunctionDefinitions (ast, astWalker) {
}
function addParams (parameterList, tree, scopeId, states, contractName, sourceLocation, stackLength, stackPosition, dir) {
let params = []
for (let inputParam in parameterList.children) {
const param = parameterList.children[inputParam]
const stackDepth = stackLength + (dir * stackPosition)
@ -317,9 +351,11 @@ function addParams (parameterList, tree, scopeId, states, contractName, sourceLo
stackDepth: stackDepth,
sourceLocation: sourceLocation
}
params.push(attributesName)
}
stackPosition += dir
}
return params
}
module.exports = InternalCallTree

@ -26,17 +26,17 @@ contract intLocal {
int256 i256 = 3434343;
int i = -32432423423;
int32 ishrink = 2;
level11();
level12();
level11();
level11(123);
level12(12);
level11(123);
}
function level11() public {
uint8 ui8 = 123;
level12();
function level11(uint8 foo) public {
uint8 ui8 = foo;
level12(12);
}
function level12() public {
uint8 ui81 = 12;
function level12(uint8 asd) public {
uint8 ui81 = asd;
}
}
`}

@ -37,14 +37,35 @@ module.exports = function (st, vm, privateKey, contractBytecode, compilationResu
})
callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
try {
console.log(scopeStarts)
let functions1 = callTree.retrieveFunctionsStack(102)
let functions2 = callTree.retrieveFunctionsStack(115)
let functions3 = callTree.retrieveFunctionsStack(13)
st.equals(functions1.length, 1)
st.equals(functions2.length, 2)
st.equals(functions3.length, 0)
st.equals(Object.keys(functions1[0])[0], 'functionDefinition')
st.equals(Object.keys(functions1[0])[1], 'inputs')
st.equals(functions1[0].inputs[0], 'foo')
st.equals(Object.keys(functions2[0])[0], 'functionDefinition')
st.equals(Object.keys(functions2[0])[1], 'inputs')
st.equals(Object.keys(functions2[1])[0], 'functionDefinition')
st.equals(Object.keys(functions2[1])[1], 'inputs')
st.equals(functions2[0].inputs[0], 'asd')
st.equals(functions2[1].inputs[0], 'foo')
st.equals(functions1[0].functionDefinition.attributes.name, 'level11')
st.equals(functions2[0].functionDefinition.attributes.name, 'level12')
st.equals(functions2[1].functionDefinition.attributes.name, 'level11')
st.equals(scopeStarts[0], '')
st.equals(scopeStarts[13], '1')
st.equals(scopeStarts[101], '2')
st.equals(scopeStarts[113], '2.1')
st.equals(scopeStarts[131], '3')
st.equals(scopeStarts[146], '4')
st.equals(scopeStarts[158], '4.1')
st.equals(scopeStarts[102], '2')
st.equals(scopeStarts[115], '2.1')
st.equals(scopeStarts[136], '3')
st.equals(scopeStarts[153], '4')
st.equals(scopeStarts[166], '4.1')
st.equals(scopes[''].locals['ui8'].type.typeName, 'uint8')
st.equals(scopes[''].locals['ui16'].type.typeName, 'uint16')
st.equals(scopes[''].locals['ui32'].type.typeName, 'uint32')
@ -88,10 +109,10 @@ module.exports = function (st, vm, privateKey, contractBytecode, compilationResu
st.equals(locals['ishrink'].value, '2')
})
helper.decodeLocals(st, 105, traceManager, callTree, function (locals) {
helper.decodeLocals(st, 106, traceManager, callTree, function (locals) {
try {
st.equals(locals['ui8'].value, '123')
st.equals(Object.keys(locals).length, 1)
st.equals(Object.keys(locals).length, 2)
} catch (e) {
st.fail(e.message)
}

Loading…
Cancel
Save