diff --git a/src/code/codeManager.js b/src/code/codeManager.js index 88371ed4a4..ee9141f4ca 100644 --- a/src/code/codeManager.js +++ b/src/code/codeManager.js @@ -25,39 +25,45 @@ CodeManager.prototype.resolveStep = function (stepIndex, tx) { this.trigger('resolvingStep') var self = this if (stepIndex === 0) { - self.getCode(tx.to, stepIndex, tx) + self.retrieveCodeAndTrigger(tx.to, stepIndex, tx) } else { this.traceManager.getCurrentCalledAddressAt(stepIndex, function (error, address) { if (error) { console.log(error) } else { - self.getCode(address, stepIndex, tx) + self.retrieveCodeAndTrigger(address, stepIndex, tx) } }) } } -CodeManager.prototype.getCode = function (address, currentStep, tx) { +CodeManager.prototype.retrieveCodeAndTrigger = function (address, stepIndex, tx) { var self = this + this.getCode(address, function (error, result) { + if (!error) { + self.retrieveIndexAndTrigger(address, stepIndex, result.instructions) + } else { + console.log(error) + } + }) +} + +CodeManager.prototype.getCode = function (address, cb) { if (traceHelper.isContractCreation(address)) { var codes = codeResolver.getExecutingCodeFromCache(address) if (!codes) { this.traceManager.getContractCreationCode(address, function (error, hexCode) { - // contract creation - if (error) { - console.log(error) - } else { + if (!error) { codes = codeResolver.cacheExecutingCode(address, hexCode) - self.retrieveIndexAndTrigger(address, currentStep, codes.code) + cb(null, codes) } }) } else { - self.retrieveIndexAndTrigger(address, currentStep, codes.code) + cb(null, codes) } } else { codeResolver.resolveCode(address, function (address, code) { - // resoling code from stack - self.retrieveIndexAndTrigger(address, currentStep, code) + cb(null, code) }) } } diff --git a/src/code/codeResolver.js b/src/code/codeResolver.js index 3f80ba5093..7b24007340 100644 --- a/src/code/codeResolver.js +++ b/src/code/codeResolver.js @@ -3,19 +3,20 @@ var codeUtils = require('./codeUtils') var util = require('../helpers/global') module.exports = { - codes: {}, // assembly items instructions list by contract addesses + bytecodeByAddress: {}, // bytes code by contract addesses + instructionsByAddress: {}, // assembly items instructions list by contract addesses instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index. resolveCode: function (address, callBack) { var cache = this.getExecutingCodeFromCache(address) if (cache) { - callBack(address, cache.code) + callBack(address, cache) return } var self = this this.loadCode(address, function (code) { - callBack(address, self.cacheExecutingCode(address, code).code) + callBack(address, self.cacheExecutingCode(address, code)) }) }, @@ -32,9 +33,10 @@ module.exports = { cacheExecutingCode: function (address, hexCode) { var codes = this.formatCode(hexCode) - this.codes[address] = codes.code + this.bytecodeByAddress[address] = hexCode + this.instructionsByAddress[address] = codes.code this.instructionsIndexByBytesOffset[address] = codes.instructionsIndexByBytesOffset - return codes + return this.getExecutingCodeFromCache(address) }, formatCode: function (hexCode) { @@ -46,10 +48,11 @@ module.exports = { }, getExecutingCodeFromCache: function (address) { - if (this.codes[address]) { + if (this.instructionsByAddress[address]) { return { - code: this.codes[address], - instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address] + instructions: this.instructionsByAddress[address], + instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address], + bytecode: this.bytecodeByAddress[address] } } else { return null diff --git a/src/code/sourceLocationTracker.js b/src/code/sourceLocationTracker.js new file mode 100644 index 0000000000..259c48a8da --- /dev/null +++ b/src/code/sourceLocationTracker.js @@ -0,0 +1,58 @@ +'use strict' +var EventManager = require('../lib/eventManager') +var util = require('../helpers/global') +var helper = require('../helpers/traceHelper') +var SourceMappingDecoder = require('../util/sourceMappingDecoder') + +/** + * Process the source code location for the current executing bytecode + */ +function SourceLocationTracker (_codeManager) { + this.codeManager = _codeManager + util.extend(this, new EventManager()) + this.sourceMappingDecoder = new SourceMappingDecoder() +} + +/** + * Return the source location associated with the given @arg index + * + * @param {String} address - contract address from which the source location is retrieved + * @param {Int} index - index in the instruction list from where the source location is retrieved + * @param {Object} contractDetails - AST of compiled contracts + * @param {Function} cb - callback function + */ +SourceLocationTracker.prototype.getSourceLocation = function (address, index, contractsDetails, cb) { + var self = this + this.codeManager.getCode(address, function (error, result) { + if (!error) { + var sourceMap = getSourceMap(address, result.bytecode, contractsDetails) + if (sourceMap) { + cb(null, self.sourceMappingDecoder.atIndex(index, sourceMap)) + } else { + cb('no srcmap associated with the code ' + address) + } + } else { + cb(error) + } + }) +} + +/** + * backwards compatibility - attribute name will certainly be changed + */ +function srcmapRuntime (contract) { + return contract.srcmapRuntime ? contract.srcmapRuntime : contract['srcmap-runtime'] +} + +function getSourceMap (address, code, contractsDetails) { + var isCreation = helper.isContractCreation(address) + var byteProp = isCreation ? 'bytecode' : 'runtimeBytecode' + for (var k in contractsDetails) { + if ('0x' + contractsDetails[k][byteProp] === code) { + return isCreation ? contractsDetails[k].srcmap : srcmapRuntime(contractsDetails[k]) + } + } + return null +} + +module.exports = SourceLocationTracker diff --git a/src/ui/Ethdebugger.js b/src/ui/Ethdebugger.js index 0599ba039a..84e4f70e3f 100644 --- a/src/ui/Ethdebugger.js +++ b/src/ui/Ethdebugger.js @@ -12,6 +12,7 @@ var ui = require('../helpers/ui') var Web3Providers = require('../web3Provider/web3Providers') var DummyProvider = require('../web3Provider/dummyProvider') var CodeManager = require('../code/codeManager') +var SourceLocationTracker = require('../code/sourceLocationTracker') function Ethdebugger () { util.extend(this, new EventManager()) @@ -26,6 +27,7 @@ function Ethdebugger () { this.switchProvider('DUMMYWEB3') this.traceManager = new TraceManager() this.codeManager = new CodeManager(this.traceManager) + this.sourceLocationTracker = new SourceLocationTracker(this.codeManager) var self = this this.register('indexChanged', this, function (index) { @@ -72,7 +74,11 @@ Ethdebugger.prototype.switchProvider = function (type) { } Ethdebugger.prototype.debug = function (tx) { - this.txBrowser.load(tx.hash) + if (tx instanceof Object) { + this.txBrowser.load(tx.hash) + } else if (tx instanceof String) { + this.txBrowser.load(tx) + } } Ethdebugger.prototype.render = function () { diff --git a/src/web3Provider/web3Providers.js b/src/web3Provider/web3Providers.js index 4fbe0b673c..8ce7f7285a 100644 --- a/src/web3Provider/web3Providers.js +++ b/src/web3Provider/web3Providers.js @@ -9,19 +9,16 @@ Web3Providers.prototype.addProvider = function (type, obj) { if (type === 'INTERNAL') { var web3 = init.loadWeb3() this.addWeb3(type, web3) - } else if (type === 'EXTERNAL') { - init.extendWeb3(obj) - this.addWeb3(type, obj) - } else if (type === 'VM') { + } else if (type === 'vm') { this.addVM(obj) } else { + init.extendWeb3(obj) this.addWeb3(type, obj) } } Web3Providers.prototype.get = function (type, cb) { if (this.modes[type]) { - this.currentMode = type cb(null, this.modes[type]) } else { cb('error: this provider has not been setup (' + type + ')', null)