source src updated

pull/697/head
aniket-engg 4 years ago committed by Aniket
parent 45d38adf48
commit 24d41a8761
  1. 50
      libs/remix-debug/src/source/offsetToLineColumnConverter.ts
  2. 183
      libs/remix-debug/src/source/sourceLocationTracker.ts
  3. 83
      libs/remix-debug/src/source/sourceMappingDecoder.ts

@ -1,33 +1,37 @@
'use strict'
const SourceMappingDecoder = require('./sourceMappingDecoder')
function offsetToColumnConverter (compilerEvent) {
this.lineBreakPositionsByContent = {}
this.sourceMappingDecoder = new SourceMappingDecoder()
if (compilerEvent) {
compilerEvent.register('compilationFinished', (success, data, source) => {
this.clear()
})
export class OffsetToColumnConverter {
lineBreakPositionsByContent
sourceMappingDecoder
constructor(compilerEvent) {
this.lineBreakPositionsByContent = {}
this.sourceMappingDecoder = new SourceMappingDecoder()
if (compilerEvent) {
compilerEvent.register('compilationFinished', (success, data, source) => {
this.clear()
})
}
}
}
offsetToColumnConverter.prototype.offsetToLineColumn = function (rawLocation, file, sources, asts) {
if (!this.lineBreakPositionsByContent[file]) {
for (let filename in asts) {
const source = asts[filename]
// source id was string before. in newer versions it has been changed to an integer so we need to check the type here
if (typeof source.id === 'string') source.id = parseInt(source.id, 10)
if (source.id === file) {
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content)
break
offsetToLineColumn (rawLocation, file, sources, asts) {
if (!this.lineBreakPositionsByContent[file]) {
for (let filename in asts) {
const source = asts[filename]
// source id was string before. in newer versions it has been changed to an integer so we need to check the type here
if (typeof source.id === 'string') source.id = parseInt(source.id, 10)
if (source.id === file) {
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content)
break
}
}
}
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
}
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
}
offsetToColumnConverter.prototype.clear = function () {
this.lineBreakPositionsByContent = {}
clear () {
this.lineBreakPositionsByContent = {}
}
}
module.exports = offsetToColumnConverter

@ -9,106 +9,113 @@ const util = remixLib.util
/**
* Process the source code location for the current executing bytecode
*/
function SourceLocationTracker (_codeManager, { debugWithGeneratedSources }) {
this.opts = {
debugWithGeneratedSources: debugWithGeneratedSources || false
export class SourceLocationTracker {
opts
codeManager
event
sourceMappingDecoder
sourceMapByAddress
constructor (_codeManager, { debugWithGeneratedSources }) {
this.opts = {
debugWithGeneratedSources: debugWithGeneratedSources || false
}
this.codeManager = _codeManager
this.event = new EventManager()
this.sourceMappingDecoder = new SourceMappingDecoder()
this.sourceMapByAddress = {}
}
this.codeManager = _codeManager
this.event = new EventManager()
this.sourceMappingDecoder = new SourceMappingDecoder()
this.sourceMapByAddress = {}
}
/**
* Return the source location associated with the given @arg index (instruction 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
*/
SourceLocationTracker.prototype.getSourceLocationFromInstructionIndex = async function (address, index, contracts) {
const sourceMap = await extractSourceMap(this, this.codeManager, address, contracts)
return this.sourceMappingDecoder.atIndex(index, sourceMap.map)
}
/**
* Return the source location associated with the given @arg index (instruction 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
*/
async getSourceLocationFromInstructionIndex (address, index, contracts) {
const sourceMap = await this.extractSourceMap(this, this.codeManager, address, contracts)
return this.sourceMappingDecoder.atIndex(index, sourceMap['map'])
}
/**
* Return the source location associated with the given @arg vmTraceIndex
*
* @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
*/
SourceLocationTracker.prototype.getSourceLocationFromVMTraceIndex = async function (address, vmtraceStepIndex, contracts) {
const sourceMap = await extractSourceMap(this, this.codeManager, address, contracts)
const index = this.codeManager.getInstructionIndex(address, vmtraceStepIndex)
return this.sourceMappingDecoder.atIndex(index, sourceMap.map)
}
/**
* Return the source location associated with the given @arg vmTraceIndex
*
* @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
*/
async getSourceLocationFromVMTraceIndex (address, vmtraceStepIndex, contracts) {
const sourceMap = await this.extractSourceMap(this, this.codeManager, address, contracts)
const index = this.codeManager.getInstructionIndex(address, vmtraceStepIndex)
return this.sourceMappingDecoder.atIndex(index, sourceMap['map'])
}
/**
* Returns the generated sources from a specific @arg address
*
* @param {String} address - contract address from which has generated sources
* @param {Object} generatedSources - Object containing the sourceid, ast and the source code.
*/
SourceLocationTracker.prototype.getGeneratedSourcesFromAddress = function (address) {
if (!this.opts.debugWithGeneratedSources) return null
if (this.sourceMapByAddress[address]) return this.sourceMapByAddress[address].generatedSources
return null
}
/**
* Returns the generated sources from a specific @arg address
*
* @param {String} address - contract address from which has generated sources
* @param {Object} generatedSources - Object containing the sourceid, ast and the source code.
*/
getGeneratedSourcesFromAddress (address) {
if (!this.opts.debugWithGeneratedSources) return null
if (this.sourceMapByAddress[address]) return this.sourceMapByAddress[address].generatedSources
return null
}
/**
* Return a valid source location associated with the given @arg vmTraceIndex
*
* @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
*/
SourceLocationTracker.prototype.getValidSourceLocationFromVMTraceIndex = async function (address, vmtraceStepIndex, contracts) {
let map = { file: -1}
while (vmtraceStepIndex >= 0 && map.file === -1) {
map = await this.getSourceLocationFromVMTraceIndex(address, vmtraceStepIndex, contracts)
vmtraceStepIndex = vmtraceStepIndex - 1
/**
* Return a valid source location associated with the given @arg vmTraceIndex
*
* @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
*/
async getValidSourceLocationFromVMTraceIndex (address, vmtraceStepIndex, contracts) {
let map = { file: -1}
while (vmtraceStepIndex >= 0 && map.file === -1) {
map = await this.getSourceLocationFromVMTraceIndex(address, vmtraceStepIndex, contracts)
vmtraceStepIndex = vmtraceStepIndex - 1
}
return map
}
return map
}
SourceLocationTracker.prototype.clearCache = function () {
this.sourceMapByAddress = {}
}
clearCache () {
this.sourceMapByAddress = {}
}
function getSourceMap (address, code, contracts) {
const isCreation = helper.isContractCreation(address)
let bytes
for (let file in contracts) {
for (let contract in contracts[file]) {
const bytecode = contracts[file][contract].evm.bytecode
const deployedBytecode = contracts[file][contract].evm.deployedBytecode
if (!deployedBytecode) continue
private getSourceMap (address, code, contracts) {
const isCreation = helper.isContractCreation(address)
let bytes
for (let file in contracts) {
for (let contract in contracts[file]) {
const bytecode = contracts[file][contract].evm.bytecode
const deployedBytecode = contracts[file][contract].evm.deployedBytecode
if (!deployedBytecode) continue
bytes = isCreation ? bytecode.object : deployedBytecode.object
if (util.compareByteCode(code, '0x' + bytes)) {
const generatedSources = isCreation ? bytecode.generatedSources : deployedBytecode.generatedSources
const map = isCreation ? bytecode.sourceMap : deployedBytecode.sourceMap
return { generatedSources, map }
bytes = isCreation ? bytecode.object : deployedBytecode.object
if (util.compareByteCode(code, '0x' + bytes)) {
const generatedSources = isCreation ? bytecode.generatedSources : deployedBytecode.generatedSources
const map = isCreation ? bytecode.sourceMap : deployedBytecode.sourceMap
return { generatedSources, map }
}
}
}
return null
}
return null
}
function extractSourceMap (self, codeManager, address, contracts) {
return new Promise((resolve, reject) => {
if (self.sourceMapByAddress[address]) return resolve(self.sourceMapByAddress[address])
private extractSourceMap (self, codeManager, address, contracts) {
return new Promise((resolve, reject) => {
if (self.sourceMapByAddress[address]) return resolve(self.sourceMapByAddress[address])
codeManager.getCode(address).then((result) => {
const sourceMap = getSourceMap(address, result.bytecode, contracts)
if (sourceMap) {
if (!helper.isContractCreation(address)) self.sourceMapByAddress[address] = sourceMap
return resolve(sourceMap)
}
reject('no sourcemap associated with the code ' + address)
}).catch(reject)
})
codeManager.getCode(address).then((result) => {
const sourceMap = this.getSourceMap(address, result.bytecode, contracts)
if (sourceMap) {
if (!helper.isContractCreation(address)) self.sourceMapByAddress[address] = sourceMap
return resolve(sourceMap)
}
reject('no sourcemap associated with the code ' + address)
}).catch(reject)
})
}
}
module.exports = SourceLocationTracker

@ -5,28 +5,8 @@ const util = remixLib.util
/**
* Decompress the source mapping given by solc-bin.js
* s:l:f:j
*/
function SourceMappingDecoder () {
// s:l:f:j
}
/**
* get a list of nodes that are at the given @arg position
*
* @param {String} astNodeType - type of node to return
* @param {Int} position - cursor position
* @return {Object} ast object given by the compiler
*/
SourceMappingDecoder.prototype.nodesAtPosition = nodesAtPosition
/**
* Decode the source mapping for the given @arg index
*
* @param {Integer} index - source mapping index to decode
* @param {String} mapping - compressed source mapping given by solc-bin
* @return {Object} returns the decompressed source mapping for the given index {start, length, file, jump}
*/
SourceMappingDecoder.prototype.atIndex = atIndex
/**
* Decode the given @arg value
@ -34,7 +14,7 @@ SourceMappingDecoder.prototype.atIndex = atIndex
* @param {string} value - source location to decode ( should be start:length:file )
* @return {Object} returns the decompressed source mapping {start, length, file}
*/
SourceMappingDecoder.prototype.decode = function (value) {
export function decode (value) {
if (value) {
value = value.split(':')
return {
@ -51,7 +31,7 @@ SourceMappingDecoder.prototype.decode = function (value) {
* @param {String} mapping - compressed source mapping given by solc-bin
* @return {Array} returns the decompressed source mapping. Array of {start, length, file, jump}
*/
SourceMappingDecoder.prototype.decompressAll = function (mapping) {
export function decompressAll (mapping) {
const map = mapping.split(';')
const ret = []
for (let k in map) {
@ -73,7 +53,7 @@ SourceMappingDecoder.prototype.decompressAll = function (mapping) {
* @param {String} source - contract source code
* @return {Array} returns an array containing offset of line breaks
*/
SourceMappingDecoder.prototype.getLinebreakPositions = function (source) {
export function getLinebreakPositions (source) {
const ret = []
for (let pos = source.indexOf('\n'); pos >= 0; pos = source.indexOf('\n', pos + 1)) {
ret.push(pos)
@ -88,7 +68,7 @@ SourceMappingDecoder.prototype.getLinebreakPositions = function (source) {
* @param {Array} lineBreakPositions - array returned by the function 'getLinebreakPositions'
* @return {Object} returns an object {start: {line, column}, end: {line, column}} (line/column count start at 0)
*/
SourceMappingDecoder.prototype.convertOffsetToLineColumn = function (sourceLocation, lineBreakPositions) {
export function convertOffsetToLineColumn (sourceLocation, lineBreakPositions) {
if (sourceLocation.start >= 0 && sourceLocation.length >= 0) {
return {
start: convertFromCharPosition(sourceLocation.start, lineBreakPositions),
@ -98,15 +78,6 @@ SourceMappingDecoder.prototype.convertOffsetToLineColumn = function (sourceLocat
return {start: null, end: null}
}
/**
* Retrieve the first @arg astNodeType that include the source map at arg instIndex
*
* @param {String} astNodeType - node type that include the source map instIndex
* @param {String} instIndex - instruction index used to retrieve the source map
* @param {String} sourceMap - source map given by the compilation result
* @param {Object} ast - ast given by the compilation result
*/
SourceMappingDecoder.prototype.findNodeAtInstructionIndex = findNodeAtInstructionIndex
function convertFromCharPosition (pos, lineBreakPositions) {
let line = util.findLowerBound(pos, lineBreakPositions)
@ -130,8 +101,16 @@ function sourceLocationFromAstNode (astNode) {
return null
}
function findNodeAtInstructionIndex (astNodeType, instIndex, sourceMap, ast) {
const sourceLocation = atIndex(instIndex, sourceMap)
/**
* Retrieve the first @arg astNodeType that include the source map at arg instIndex
*
* @param {String} astNodeType - node type that include the source map instIndex
* @param {String} instIndex - instruction index used to retrieve the source map
* @param {String} sourceMap - source map given by the compilation result
* @param {Object} ast - ast given by the compilation result
*/
export function findNodeAtInstructionIndex (astNodeType, instIndex, sourceMap, ast) {
const sourceLocation = this.atIndex(instIndex, sourceMap)
return findNodeAtSourceLocation(astNodeType, sourceLocation, ast)
}
@ -153,7 +132,15 @@ function findNodeAtSourceLocation (astNodeType, sourceLocation, ast) {
return found
}
function nodesAtPosition (astNodeType, position, ast) {
/**
* get a list of nodes that are at the given @arg position
*
* @param {String} astNodeType - type of node to return
* @param {Int} position - cursor position
* @return {Object} ast object given by the compiler
*/
export function nodesAtPosition (astNodeType, position, ast) {
const astWalker = new AstWalker()
const found = []
const callback = function (node) {
@ -186,7 +173,7 @@ function nodesAtPosition (astNodeType, position, ast) {
* @param Array mapping - source maps returned by the compiler. e.g 121:3741:0:-:0;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;121:3741:0;;;;;;;
* @return Object { start, length, file, jump }
*/
function atIndex (index, mapping) {
export function atIndex (index, mapping) {
let ret = {}
const map = mapping.split(';')
if (index >= map.length) {
@ -198,23 +185,21 @@ function atIndex (index, mapping) {
continue
}
current = current.split(':')
if (ret.start === undefined && current[0] && current[0] !== '-1' && current[0].length) {
ret.start = parseInt(current[0])
if (ret['start'] === undefined && current[0] && current[0] !== '-1' && current[0].length) {
ret['start'] = parseInt(current[0])
}
if (ret.length === undefined && current[1] && current[1] !== '-1' && current[1].length) {
ret.length = parseInt(current[1])
if (ret['length'] === undefined && current[1] && current[1] !== '-1' && current[1].length) {
ret['length'] = parseInt(current[1])
}
if (ret.file === undefined && current[2] && current[2].length) {
ret.file = parseInt(current[2])
if (ret['file'] === undefined && current[2] && current[2].length) {
ret['file'] = parseInt(current[2])
}
if (ret.jump === undefined && current[3] && current[3].length) {
ret.jump = current[3]
if (ret['jump'] === undefined && current[3] && current[3].length) {
ret['jump'] = current[3]
}
if (ret.start !== undefined && ret.length !== undefined && ret.file !== undefined && ret.jump !== undefined) {
if (ret['start'] !== undefined && ret['length'] !== undefined && ret['file'] !== undefined && ret['jump'] !== undefined) {
break
}
}
return ret
}
module.exports = SourceMappingDecoder

Loading…
Cancel
Save