diff --git a/libs/remix-debug/src/trace/traceManager.ts b/libs/remix-debug/src/trace/traceManager.ts index f71cb27931..2c06e7bb83 100644 --- a/libs/remix-debug/src/trace/traceManager.ts +++ b/libs/remix-debug/src/trace/traceManager.ts @@ -7,269 +7,277 @@ const traceHelper = require('./traceHelper') const remixLib = require('@remix-project/remix-lib') const util = remixLib.util -function TraceManager (options) { - this.web3 = options.web3 - this.isLoading = false - this.trace = null - this.traceCache = new TraceCache() - this.traceAnalyser = new TraceAnalyser(this.traceCache) - this.traceStepManager = new TraceStepManager(this.traceAnalyser) - this.tx -} - -// init section -TraceManager.prototype.resolveTrace = async function (tx) { - this.tx = tx - this.init() - if (!this.web3) throw new Error('web3 not loaded') - this.isLoading = true - try { - const result = await this.getTrace(tx.hash) - - if (result.structLogs.length > 0) { - this.trace = result.structLogs +export class TraceManager { + + web3 + isLoading: boolean + trace + traceCache + traceAnalyser + traceStepManager + tx + + constructor (options) { + this.web3 = options.web3 + this.isLoading = false + this.trace = null + this.traceCache = new TraceCache() + this.traceAnalyser = new TraceAnalyser(this.traceCache) + this.traceStepManager = new TraceStepManager(this.traceAnalyser) + } - this.traceAnalyser.analyse(result.structLogs, tx) + // init section + async resolveTrace (tx) { + this.tx = tx + this.init() + if (!this.web3) throw new Error('web3 not loaded') + this.isLoading = true + try { + const result = await this.getTrace(tx.hash) + + if (result['structLogs'].length > 0) { + this.trace = result['structLogs'] + + this.traceAnalyser.analyse(result['structLogs'], tx) + this.isLoading = false + return true + } + var mes = tx.hash + ' is not a contract invocation or contract creation.' + console.log(mes) + this.isLoading = false + throw new Error(mes) + } catch (error) { + console.log(error) this.isLoading = false - return true + throw new Error(error) } - var mes = tx.hash + ' is not a contract invocation or contract creation.' - console.log(mes) - this.isLoading = false - throw new Error(mes) - } catch (error) { - console.log(error) - this.isLoading = false - throw new Error(error) } -} -TraceManager.prototype.getTrace = function (txHash) { - return new Promise((resolve, reject) => { - const options = { - disableStorage: true, - disableMemory: false, - disableStack: false, - fullStorage: false - } - this.web3.debug.traceTransaction(txHash, options, function (error, result) { - if (error) return reject(error) - resolve(result) + getTrace (txHash) { + return new Promise((resolve, reject) => { + const options = { + disableStorage: true, + disableMemory: false, + disableStack: false, + fullStorage: false + } + this.web3.debug.traceTransaction(txHash, options, function (error, result) { + if (error) return reject(error) + resolve(result) + }) }) - }) -} - -TraceManager.prototype.init = function () { - this.trace = null - this.traceCache.init() -} - -// API section -TraceManager.prototype.inRange = function (step) { - return this.isLoaded() && step >= 0 && step < this.trace.length -} - -TraceManager.prototype.isLoaded = function () { - return !this.isLoading && this.trace !== null -} + } -TraceManager.prototype.getLength = function (callback) { - if (!this.trace) { - callback('no trace available', null) - } else { - callback(null, this.trace.length) + init () { + this.trace = null + this.traceCache.init() } -} -TraceManager.prototype.accumulateStorageChanges = function (index, address, storageOrigin) { - return this.traceCache.accumulateStorageChanges(index, address, storageOrigin) -} + // API section + inRange (step) { + return this.isLoaded() && step >= 0 && step < this.trace.length + } -TraceManager.prototype.getAddresses = function () { - return this.traceCache.addresses -} + isLoaded () { + return !this.isLoading && this.trace !== null + } -TraceManager.prototype.getCallDataAt = function (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) + getLength (callback) { + if (!this.trace) { + callback('no trace available', null) + } else { + callback(null, this.trace.length) + } } - const callDataChange = util.findLowerBoundValue(stepIndex, this.traceCache.callDataChanges) - if (callDataChange === null) { - throw new Error('no calldata found') + + accumulateStorageChanges (index, address, storageOrigin) { + return this.traceCache.accumulateStorageChanges(index, address, storageOrigin) } - return [this.traceCache.callsData[callDataChange]] -} -TraceManager.prototype.buildCallPath = async function (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) + getAddresses () { + return this.traceCache.addresses } - const callsPath = util.buildCallPath(stepIndex, this.traceCache.callsTree.call) - if (callsPath === null) throw new Error('no call path built') - return callsPath -} -TraceManager.prototype.getCallStackAt = function (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) + getCallDataAt (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + const callDataChange = util.findLowerBoundValue(stepIndex, this.traceCache.callDataChanges) + if (callDataChange === null) { + throw new Error('no calldata found') + } + return [this.traceCache.callsData[callDataChange]] } - const call = util.findCall(stepIndex, this.traceCache.callsTree.call) - if (call === null) { - throw new Error('no callstack found') + + async buildCallPath (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + const callsPath = util.buildCallPath(stepIndex, this.traceCache.callsTree.call) + if (callsPath === null) throw new Error('no call path built') + return callsPath } - return call.callStack -} -TraceManager.prototype.getStackAt = function (stepIndex) { - this.checkRequestedStep(stepIndex) - if (this.trace[stepIndex] && this.trace[stepIndex].stack) { // there's always a stack - let stack = this.trace[stepIndex].stack.slice(0) - stack.reverse() - return stack - } else { - throw new Error('no stack found') + getCallStackAt (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + const call = util.findCall(stepIndex, this.traceCache.callsTree.call) + if (call === null) { + throw new Error('no callstack found') + } + return call.callStack } -} -TraceManager.prototype.getLastCallChangeSince = function (stepIndex) { - try { + getStackAt (stepIndex) { this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) + if (this.trace[stepIndex] && this.trace[stepIndex].stack) { // there's always a stack + let stack = this.trace[stepIndex].stack.slice(0) + stack.reverse() + return stack + } else { + throw new Error('no stack found') + } } - const callChange = util.findCall(stepIndex, this.traceCache.callsTree.call) - if (callChange === null) { - return 0 - } - return callChange -} + getLastCallChangeSince (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } -TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - const resp = this.getLastCallChangeSince(stepIndex) - if (!resp) { - throw new Error('unable to get current called address. ' + stepIndex + ' does not match with a CALL') + const callChange = util.findCall(stepIndex, this.traceCache.callsTree.call) + if (callChange === null) { + return 0 } - return resp.address - } catch (error) { - throw new Error(error) + return callChange } -} -TraceManager.prototype.getContractCreationCode = function (token) { - if (!this.traceCache.contractCreation[token]) { - throw new Error('no contract creation named ' + token) + getCurrentCalledAddressAt (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + const resp = this.getLastCallChangeSince(stepIndex) + if (!resp) { + throw new Error('unable to get current called address. ' + stepIndex + ' does not match with a CALL') + } + return resp.address + } catch (error) { + throw new Error(error) + } } - return this.traceCache.contractCreation[token] -} -TraceManager.prototype.getMemoryAt = function (stepIndex) { - this.checkRequestedStep(stepIndex) - const lastChanges = util.findLowerBoundValue(stepIndex, this.traceCache.memoryChanges) - if (lastChanges === null) { - throw new Error('no memory found') + getContractCreationCode (token) { + if (!this.traceCache.contractCreation[token]) { + throw new Error('no contract creation named ' + token) + } + return this.traceCache.contractCreation[token] } - return this.trace[lastChanges].memory -} -TraceManager.prototype.getCurrentPC = function (stepIndex) { - try { + getMemoryAt (stepIndex) { this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) + const lastChanges = util.findLowerBoundValue(stepIndex, this.traceCache.memoryChanges) + if (lastChanges === null) { + throw new Error('no memory found') + } + return this.trace[lastChanges].memory } - return this.trace[stepIndex].pc -} -TraceManager.prototype.getReturnValue = function (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) + getCurrentPC (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + return this.trace[stepIndex].pc } - if (!this.traceCache.returnValues[stepIndex]) { - throw new Error('current step is not a return step') + + getReturnValue (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + if (!this.traceCache.returnValues[stepIndex]) { + throw new Error('current step is not a return step') + } + return this.traceCache.returnValues[stepIndex] } - return this.traceCache.returnValues[stepIndex] -} -TraceManager.prototype.getCurrentStep = function (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) + getCurrentStep (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + return this.traceCache.steps[stepIndex] } - return this.traceCache.steps[stepIndex] -} -TraceManager.prototype.getMemExpand = function (stepIndex) { - return (this.getStepProperty(stepIndex, 'memexpand') || '') -} + getMemExpand (stepIndex) { + return (this.getStepProperty(stepIndex, 'memexpand') || '') + } -TraceManager.prototype.getStepCost = function (stepIndex) { - return this.getStepProperty(stepIndex, 'gasCost') -} + getStepCost (stepIndex) { + return this.getStepProperty(stepIndex, 'gasCost') + } -TraceManager.prototype.getRemainingGas = function (stepIndex) { - return this.getStepProperty(stepIndex, 'gas') -} + getRemainingGas (stepIndex) { + return this.getStepProperty(stepIndex, 'gas') + } -TraceManager.prototype.getStepProperty = function (stepIndex, property) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) + getStepProperty (stepIndex, property) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + return this.trace[stepIndex][property] } - return this.trace[stepIndex][property] -} -TraceManager.prototype.isCreationStep = function (stepIndex) { - return traceHelper.isCreateInstruction(this.trace[stepIndex]) -} + isCreationStep (stepIndex) { + return traceHelper.isCreateInstruction(this.trace[stepIndex]) + } -// step section -TraceManager.prototype.findStepOverBack = function (currentStep) { - return this.traceStepManager.findStepOverBack(currentStep) -} + // step section + findStepOverBack (currentStep) { + return this.traceStepManager.findStepOverBack(currentStep) + } -TraceManager.prototype.findStepOverForward = function (currentStep) { - return this.traceStepManager.findStepOverForward(currentStep) -} + findStepOverForward (currentStep) { + return this.traceStepManager.findStepOverForward(currentStep) + } -TraceManager.prototype.findNextCall = function (currentStep) { - return this.traceStepManager.findNextCall(currentStep) -} + findNextCall (currentStep) { + return this.traceStepManager.findNextCall(currentStep) + } -TraceManager.prototype.findStepOut = function (currentStep) { - return this.traceStepManager.findStepOut(currentStep) -} + findStepOut (currentStep) { + return this.traceStepManager.findStepOut(currentStep) + } -TraceManager.prototype.checkRequestedStep = function (stepIndex) { - if (!this.trace) { - throw new Error('trace not loaded') - } else if (stepIndex >= this.trace.length) { - throw new Error('trace smaller than requested') + checkRequestedStep (stepIndex) { + if (!this.trace) { + throw new Error('trace not loaded') + } else if (stepIndex >= this.trace.length) { + throw new Error('trace smaller than requested') + } } -} -TraceManager.prototype.waterfall = function (calls, stepindex, cb) { - let ret = [] - let retError = null - for (var call in calls) { - calls[call].apply(this, [stepindex, function (error, result) { - retError = error - ret.push({ error: error, value: result }) - }]) + waterfall (calls, stepindex, cb) { + let ret = [] + let retError = null + for (var call in calls) { + calls[call].apply(this, [stepindex, function (error, result) { + retError = error + ret.push({ error: error, value: result }) + }]) + } + cb(retError, ret) } - cb(retError, ret) } - -module.exports = TraceManager diff --git a/libs/remix-debug/src/trace/traceStepManager.ts b/libs/remix-debug/src/trace/traceStepManager.ts index d3ec4a13a2..6dfaabaea4 100644 --- a/libs/remix-debug/src/trace/traceStepManager.ts +++ b/libs/remix-debug/src/trace/traceStepManager.ts @@ -4,52 +4,55 @@ const traceHelper = require('./traceHelper') const remixLib = require('@remix-project/remix-lib') const util = remixLib.util -function TraceStepManager (_traceAnalyser) { - this.traceAnalyser = _traceAnalyser -} +export class TraceStepManager { -TraceStepManager.prototype.isCallInstruction = function (index) { - const state = this.traceAnalyser.trace[index] - return traceHelper.isCallInstruction(state) && !traceHelper.isCallToPrecompiledContract(index, this.traceAnalyser.trace) -} + traceAnalyser -TraceStepManager.prototype.isReturnInstruction = function (index) { - const state = this.traceAnalyser.trace[index] - return traceHelper.isReturnInstruction(state) -} + constructor (_traceAnalyser) { + this.traceAnalyser = _traceAnalyser + } -TraceStepManager.prototype.findStepOverBack = function (currentStep) { - if (this.isReturnInstruction(currentStep)) { - const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) - return call.start > 0 ? call.start - 1 : 0 + isCallInstruction (index) { + const state = this.traceAnalyser.trace[index] + return traceHelper.isCallInstruction(state) && !traceHelper.isCallToPrecompiledContract(index, this.traceAnalyser.trace) } - return currentStep > 0 ? currentStep - 1 : 0 -} -TraceStepManager.prototype.findStepOverForward = function (currentStep) { - if (this.isCallInstruction(currentStep)) { - const call = util.findCall(currentStep + 1, this.traceAnalyser.traceCache.callsTree.call) - return call.return + 1 < this.traceAnalyser.trace.length ? call.return + 1 : this.traceAnalyser.trace.length - 1 + isReturnInstruction (index) { + const state = this.traceAnalyser.trace[index] + return traceHelper.isReturnInstruction(state) + } + + findStepOverBack (currentStep) { + if (this.isReturnInstruction(currentStep)) { + const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) + return call.start > 0 ? call.start - 1 : 0 + } + return currentStep > 0 ? currentStep - 1 : 0 } - return this.traceAnalyser.trace.length >= currentStep + 1 ? currentStep + 1 : currentStep -} -TraceStepManager.prototype.findNextCall = function (currentStep) { - const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) - const subCalls = Object.keys(call.calls) - if (subCalls.length) { - var callStart = util.findLowerBound(currentStep, subCalls) + 1 - if (subCalls.length > callStart) { - return subCalls[callStart] - 1 + findStepOverForward (currentStep) { + if (this.isCallInstruction(currentStep)) { + const call = util.findCall(currentStep + 1, this.traceAnalyser.traceCache.callsTree.call) + return call.return + 1 < this.traceAnalyser.trace.length ? call.return + 1 : this.traceAnalyser.trace.length - 1 + } + return this.traceAnalyser.trace.length >= currentStep + 1 ? currentStep + 1 : currentStep + } + + findNextCall (currentStep) { + const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) + const subCalls = Object.keys(call.calls) + if (subCalls.length) { + var callStart = util.findLowerBound(currentStep, subCalls) + 1 + if (subCalls.length > callStart) { + return parseInt(subCalls[callStart]) - 1 + } + return currentStep } return currentStep } - return currentStep -} -TraceStepManager.prototype.findStepOut = function (currentStep) { - const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) - return call.return + findStepOut (currentStep) { + const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) + return call.return + } } - -module.exports = TraceStepManager