'use strict' var EventManager = require('./eventManager') var helper = require('./helpers/traceHelper') var SourceMappingDecoder = require('./sourceMappingDecoder') var util = require('./util') /** * Process the source code location for the current executing bytecode */ function SourceLocationTracker (_codeManager) { this.codeManager = _codeManager this.event = new EventManager() this.sourceMappingDecoder = new SourceMappingDecoder() this.sourceMapCacheOfInstructionIndex = {} this.sourceMapCacheOfVMTraceIndex = {} } /** * 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.getSourceLocationFromInstructionIndex = function (address, index, contracts, cb) { var self = this if (self.sourceMapCacheOfInstructionIndex[address]) { return cb(null, self.sourceMappingDecoder.atIndex(index, self.sourceMapCacheOfInstructionIndex[address])) } extractSourceMap(this.codeManager, address, contracts, function (error, sourceMap) { if (error) { cb(error) } else { if (!helper.isContractCreation(address)) self.sourceMapCacheOfInstructionIndex[address] = sourceMap cb(null, self.sourceMappingDecoder.atIndex(index, sourceMap)) } }) } /** * Return the source location associated with the given @arg pc * * @param {String} address - contract address from which the source location is retrieved * @param {Int} vmtraceStepIndex - index of the current code in the vmtrace * @param {Object} contractDetails - AST of compiled contracts * @param {Function} cb - callback function */ SourceLocationTracker.prototype.getSourceLocationFromVMTraceIndex = function (address, vmtraceStepIndex, contracts, cb) { var self = this if (self.sourceMapCacheOfVMTraceIndex[address]) { return cb(null, self.sourceMappingDecoder.atIndex(vmtraceStepIndex, self.sourceMapCacheOfVMTraceIndex[address])) } extractSourceMap(this.codeManager, address, contracts, function (error, sourceMap) { if (!error) { self.codeManager.getInstructionIndex(address, vmtraceStepIndex, function (error, index) { if (error) { cb(error) } else { if (!helper.isContractCreation(address)) self.sourceMapCacheOfVMTraceIndex[address] = sourceMap cb(null, self.sourceMappingDecoder.atIndex(index, sourceMap)) } }) } else { cb(error) } }) } function getSourceMap (address, code, contracts) { var isCreation = helper.isContractCreation(address) var bytes for (var file in contracts) { for (var contract in contracts[file]) { bytes = isCreation ? contracts[file][contract].evm.bytecode.object : contracts[file][contract].evm.deployedBytecode.object if (util.compareByteCode(code, '0x' + bytes)) { return isCreation ? contracts[file][contract].evm.bytecode.sourceMap : contracts[file][contract].evm.deployedBytecode.sourceMap } } } return null } function extractSourceMap (codeManager, address, contracts, cb) { codeManager.getCode(address, function (error, result) { if (!error) { var sourceMap = getSourceMap(address, result.bytecode, contracts) if (sourceMap) { cb(null, sourceMap) } else { cb('no sourcemap associated with the code ' + address) } } else { cb(error) } }) } module.exports = SourceLocationTracker