diff --git a/package.json b/package.json index 17ebcc6126..d5d0101243 100644 --- a/package.json +++ b/package.json @@ -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;", diff --git a/remix-debug/src/debugger/VmDebugger.js b/remix-debug/src/debugger/VmDebugger.js index c51f8ee16e..8291e600ae 100644 --- a/remix-debug/src/debugger/VmDebugger.js +++ b/remix-debug/src/debugger/VmDebugger.js @@ -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) diff --git a/remix-debug/src/solidity-decoder/internalCallTree.js b/remix-debug/src/solidity-decoder/internalCallTree.js index 7da943a0be..f01ae418e1 100644 --- a/remix-debug/src/solidity-decoder/internalCallTree.js +++ b/remix-debug/src/solidity-decoder/internalCallTree.js @@ -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 diff --git a/remix-debug/test/decoder/contracts/intLocal.js b/remix-debug/test/decoder/contracts/intLocal.js index 737bac6e49..0b4a112808 100644 --- a/remix-debug/test/decoder/contracts/intLocal.js +++ b/remix-debug/test/decoder/contracts/intLocal.js @@ -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; } } `} diff --git a/remix-debug/test/decoder/localsTests/int.js b/remix-debug/test/decoder/localsTests/int.js index 766d46414d..b051c13d7a 100644 --- a/remix-debug/test/decoder/localsTests/int.js +++ b/remix-debug/test/decoder/localsTests/int.js @@ -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) }