From f83b451f7e999813243ee41bc98239d0c96286b7 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 11 Sep 2019 11:27:39 +0200 Subject: [PATCH] add comment in Internal call tree --- .../src/solidity-decoder/internalCallTree.js | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/remix-debug/src/solidity-decoder/internalCallTree.js b/remix-debug/src/solidity-decoder/internalCallTree.js index 16e1c2bb3d..07a6ebe359 100644 --- a/remix-debug/src/solidity-decoder/internalCallTree.js +++ b/remix-debug/src/solidity-decoder/internalCallTree.js @@ -34,6 +34,7 @@ class InternalCallTree { if (!this.solidityProxy.loaded()) { this.event.trigger('callTreeBuildFailed', ['compilation result not loaded. Cannot build internal call tree']) } else { + // each recursive call to buildTree represent a new context (either call, delegatecall, internal function) buildTree(this, 0, '', true).then((result) => { if (result.error) { this.event.trigger('callTreeBuildFailed', [result.error]) @@ -55,7 +56,9 @@ class InternalCallTree { */ reset () { /* - scopes: map of scopes defined by range in the vmtrace {firstStep, lastStep, locals}. Keys represent the level of deepness (scopeId) + scopes: map of scopes defined by range in the vmtrace {firstStep, lastStep, locals}. + Keys represent the level of deepness (scopeId) + scopeId : .. */ this.scopes = {} /* @@ -151,6 +154,7 @@ async function buildTree (tree, step, scopeId, isExternalCall) { return { outStep: step, error: 'InternalCallTree - No source Location. ' + step } } var isCallInstruction = traceHelper.isCallInstruction(tree.traceManager.trace[step]) + // we are checking if we jumping in a new CALL or in an internal function if (isCallInstruction || sourceLocation.jump === 'i') { try { var externalCallResult = await buildTree(tree, step + 1, scopeId === '' ? subScope.toString() : scopeId + '.' + subScope, isCallInstruction) @@ -164,9 +168,12 @@ async function buildTree (tree, step, scopeId, isExternalCall) { return { outStep: step, error: 'InternalCallTree - ' + e.message } } } else if ((isExternalCall && callDepthChange(step, tree.traceManager.trace)) || (!isExternalCall && sourceLocation.jump === 'o')) { + // if not, we might be returning from a CALL or internal function. This is what is checked here. tree.scopes[scopeId].lastStep = step return { outStep: step + 1 } } else { + // if not, we are in the current scope. + // We check in `includeVariableDeclaration` if there is a new local variable in scope for this specific `step` if (tree.includeLocalVariables) { includeVariableDeclaration(tree, step, sourceLocation, scopeId, newLocation, previousSourceLocation) } @@ -177,20 +184,27 @@ async function buildTree (tree, step, scopeId, isExternalCall) { return { outStep: step } } +// the reduced trace contain an entry only if that correspond to a new source location function createReducedTrace (tree, index) { tree.reducedTrace.push(index) } function includeVariableDeclaration (tree, step, sourceLocation, scopeId, newLocation, previousSourceLocation) { var variableDeclaration = resolveVariableDeclaration(tree, step, sourceLocation) + // using the vm trace step, the current source location and the ast, + // we check if the current vm trace step target a new ast node of type VariableDeclaration + // that way we know that there is a new local variable from here. if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.attributes.name]) { tree.traceManager.getStackAt(step, (error, stack) => { + // the stack length at this point is where the value of the new local will be stored. + // so, either this is the direct value, or the offset in memory. That depends on the type. if (!error) { tree.solidityProxy.contractNameAt(step, (error, contractName) => { // cached if (!error) { var states = tree.solidityProxy.extractStatesDefinitions() var location = typesUtil.extractLocationFromAstVariable(variableDeclaration) location = location === 'default' ? 'storage' : location + // we push the new local variable in our tree tree.scopes[scopeId].locals[variableDeclaration.attributes.name] = { name: variableDeclaration.attributes.name, type: decodeInfo.parseType(variableDeclaration.attributes.type, states, contractName, location), @@ -202,6 +216,8 @@ function includeVariableDeclaration (tree, step, sourceLocation, scopeId, newLoc } }) } + // we check here if we are at the beginning inside a new function. + // if that is the case, we have to add to locals tree the inputs and output params var functionDefinition = resolveFunctionDefinition(tree, step, previousSourceLocation) if (functionDefinition && (newLocation && traceHelper.isJumpDestInstruction(tree.traceManager.trace[step - 1]) || functionDefinition.attributes.isConstructor)) { tree.functionCallStack.push(step) @@ -223,6 +239,8 @@ function includeVariableDeclaration (tree, step, sourceLocation, scopeId, newLoc } } +// this extract all the variable declaration for a given ast and file +// and keep this in a cache function resolveVariableDeclaration (tree, step, sourceLocation) { if (!tree.variableDeclarationByFile[sourceLocation.file]) { var ast = tree.solidityProxy.ast(sourceLocation) @@ -236,6 +254,8 @@ function resolveVariableDeclaration (tree, step, sourceLocation) { return tree.variableDeclarationByFile[sourceLocation.file][sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file] } +// this extract all the function definition for a given ast and file +// and keep this in a cache function resolveFunctionDefinition (tree, step, sourceLocation) { if (!tree.functionDefinitionByFile[sourceLocation.file]) { var ast = tree.solidityProxy.ast(sourceLocation)