Merge pull request #95 from yann300/sourceMappingDecoder

Source mapping decoder
pull/7/head
yann300 8 years ago committed by GitHub
commit 5f5c4c7ce3
  1. 25
      src/helpers/traceHelper.js
  2. 31
      src/helpers/util.js
  3. 7
      src/index.js
  4. 15
      src/trace/traceManager.js
  5. 2
      src/ui/Ethdebugger.js
  6. 119
      src/util/sourceMappingDecoder.js
  7. 22
      test/resources/sourceMapping.js
  8. 1
      test/tests.js
  9. 118
      test/util.js

@ -1,31 +1,6 @@
'use strict'
var ui = require('../helpers/ui')
module.exports = {
// util section
findLowerBound: function (target, changes) {
if (changes.length === 0) {
return undefined
}
if (changes.length === 1) {
if (changes[0] > target) {
// we only a closest maximum, returning O
return 0
} else {
return changes[0]
}
}
var middle = Math.floor(changes.length / 2)
if (changes[middle] > target) {
return this.findLowerBound(target, changes.slice(0, middle))
} else if (changes[middle] < target) {
return this.findLowerBound(target, changes.slice(middle, changes.length))
} else {
return changes[middle]
}
},
// vmTraceIndex has to point to a CALL, CODECALL, ...
resolveCalledAddress: function (vmTraceIndex, trace) {
var step = trace[vmTraceIndex]

@ -39,5 +39,36 @@ module.exports = {
ret.push(row)
}
return ret
},
/*
Binary Search:
Assumes that @arg array is sorted increasingly
return largest i such that array[i] <= target; return -1 if array[0] > target || array is empty
*/
findLowerBound: function (target, array) {
var start = 0
var length = array.length
while (length > 0) {
var half = length >> 1
var middle = start + half
if (array[middle] <= target) {
length = length - 1 - half
start = middle + 1
} else {
length = half
}
}
return start - 1
},
/*
Binary Search:
Assumes that @arg array is sorted increasingly
return largest array[i] such that array[i] <= target; return null if array[0] > target || array is empty
*/
findLowerBoundValue: function (target, array) {
var index = this.findLowerBound(target, array)
return index >= 0 ? array[index] : null
}
}

@ -4,13 +4,11 @@ var Debugger = require('./ui/Ethdebugger')
var BasicPanel = require('./ui/BasicPanel')
var TraceManager = require('./trace/traceManager')
var CodeManager = require('./code/codeManager')
var SourceMappingDecoder = require('./util/sourceMappingDecoder')
if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') {
module.exports = modules()
}
if (window) {
window.remix = modules()
}
function modules () {
return {
@ -24,6 +22,9 @@ function modules () {
Debugger: Debugger,
VMdebugger: VMDebugger,
BasicPanel: BasicPanel
},
util: {
SourceMappingDecoder: SourceMappingDecoder
}
}
}

@ -4,7 +4,8 @@ var TraceRetriever = require('./traceRetriever')
var TraceCache = require('./traceCache')
var TraceStepManager = require('./traceStepManager')
var traceHelper = require('../helpers/traceHelper')
var util = require('../helpers/global')
var util = require('../helpers/util')
var globalUtil = require('../helpers/global')
function TraceManager () {
this.isLoading = false
@ -20,7 +21,7 @@ function TraceManager () {
TraceManager.prototype.resolveTrace = function (tx, callback) {
this.tx = tx
this.init()
if (!util.web3) callback('web3 not loaded', false)
if (!globalUtil.web3) callback('web3 not loaded', false)
this.isLoading = true
var self = this
this.traceRetriever.getTrace(tx.hash, function (error, result) {
@ -79,7 +80,7 @@ TraceManager.prototype.getStorageAt = function (stepIndex, tx, callback, address
return callback(check, null)
}
if (!address) {
var stoChange = traceHelper.findLowerBound(stepIndex, this.traceCache.storageChanges)
var stoChange = util.findLowerBoundValue(stepIndex, this.traceCache.storageChanges)
if (stoChange === undefined) return callback('no storage found', null)
address = this.traceCache.sstore[stoChange].address
}
@ -122,7 +123,7 @@ TraceManager.prototype.getCallDataAt = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
var callDataChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callDataChanges)
var callDataChange = util.findLowerBoundValue(stepIndex, this.traceCache.callDataChanges)
if (callDataChange === undefined) return callback('no calldata found', null)
callback(null, [this.traceCache.callsData[callDataChange]])
}
@ -132,7 +133,7 @@ TraceManager.prototype.getCallStackAt = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
var callStackChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callChanges)
var callStackChange = util.findLowerBoundValue(stepIndex, this.traceCache.callChanges)
if (callStackChange === undefined) return callback('no callstack found', null)
callback(null, this.traceCache.callStack[callStackChange].callStack)
}
@ -157,7 +158,7 @@ TraceManager.prototype.getLastCallChangeSince = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
var callChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callChanges)
var callChange = util.findLowerBoundValue(stepIndex, this.traceCache.callChanges)
if (callChange === undefined) {
callback(null, 0)
} else {
@ -203,7 +204,7 @@ TraceManager.prototype.getMemoryAt = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
var lastChanges = traceHelper.findLowerBound(stepIndex, this.traceCache.memoryChanges)
var lastChanges = util.findLowerBoundValue(stepIndex, this.traceCache.memoryChanges)
if (lastChanges === undefined) return callback('no memory found', null)
callback(null, this.trace[lastChanges].memory)
}

@ -28,7 +28,7 @@ function Ethdebugger () {
this.codeManager = new CodeManager(this.traceManager)
var self = this
this.traceManager.register('indexChanged', this, function (index) {
this.register('indexChanged', this, function (index) {
self.codeManager.resolveStep(index, self.tx)
})

@ -0,0 +1,119 @@
'use strict'
var util = require('../helpers/util')
/**
* Decompress the source mapping given by solc-bin.js
*/
function SourceMappingDecoder () {
// s:l:f:j
}
/**
* 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 = function (index, mapping) {
var ret = {}
var map = mapping.split(';')
if (index >= map.length) {
index = map.length - 1
}
for (var k = index; k >= 0; k--) {
var current = map[k]
if (!current.length) {
continue
}
current = current.split(':')
if (ret.start === undefined && current[0] && current[0].length) {
ret.start = parseInt(current[0])
}
if (ret.length === undefined && current[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.jump === undefined && current[3] && current[3].length) {
ret.jump = current[3]
}
if (ret.start !== undefined && ret.length !== undefined && ret.file !== undefined && ret.jump !== undefined) {
break
}
}
return ret
}
/**
* Decode the source mapping for the given compressed mapping
*
* @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) {
var map = mapping.split(';')
var ret = []
for (var k in map) {
var compressed = map[k].split(':')
var sourceMap = {
start: compressed[0] ? parseInt(compressed[0]) : ret[ret.length - 1].start,
length: compressed[1] ? parseInt(compressed[1]) : ret[ret.length - 1].length,
file: compressed[2] ? parseInt(compressed[2]) : ret[ret.length - 1].file,
jump: compressed[3] ? compressed[3] : ret[ret.length - 1].jump
}
ret.push(sourceMap)
}
return ret
}
/**
* Retrieve line/column position of each source char
*
* @param {String} source - contract source code
* @return {Arrray} returns an array containing offset of line breaks
*/
SourceMappingDecoder.prototype.getLinebreakPositions = function (source) {
var ret = []
for (var pos = source.indexOf('\n'); pos >= 0; pos = source.indexOf('\n', pos + 1)) {
ret.push(pos)
}
return ret
}
/**
* Retrieve the line/colum position for the given source mapping
*
* @param {Object} sourceLocation - object containing attributes {source} and {length}
* @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) {
if (sourceLocation.start >= 0 && sourceLocation.length >= 0) {
return {
start: convertFromCharPosition(sourceLocation.start, lineBreakPositions),
end: convertFromCharPosition(sourceLocation.start + sourceLocation.length, lineBreakPositions)
}
} else {
return {
start: null,
end: null
}
}
}
function convertFromCharPosition (pos, lineBreakPositions) {
var line = util.findLowerBound(pos, lineBreakPositions)
if (lineBreakPositions[line] !== pos) {
line = line + 1
}
var beginColumn = line === 0 ? 0 : (lineBreakPositions[line - 1] + 1)
var column = pos - beginColumn
return {
line: line,
column: column
}
}
module.exports = SourceMappingDecoder

@ -0,0 +1,22 @@
var sourceRuntimeMapping = {}
sourceRuntimeMapping.mapping = '0:205:4:-;;;;;;;;;;;;;;;;;;;;;;55:74;;;;;;;;;;;;;;;;;;;;;;;;;;142:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;55:74;103:2;99:1;;:6;;;;;120:2;116:1;;:6;;;;;55:74;;;:::o;142:61::-;166:6;174;142:61;;;:::o'
sourceRuntimeMapping.source = `contract test {
int x;
int y;
function set(int _x, int _y)
{
x = _x;
y = _y;
}
function get() returns (uint x, uint y)
{
}
}`
if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') {
module.exports = sourceRuntimeMapping
}

@ -2,3 +2,4 @@
require('./index.js')
require('./traceManager.js')
require('./codeManager.js')
require('./util.js')

@ -0,0 +1,118 @@
'use strict'
var sourceMapping = require('./resources/sourceMapping')
var index = require('../src/index')
var tape = require('tape')
var util = require('../src/helpers/util')
tape('Util', function (t) {
t.test('lowerbound', function (st) {
st.plan(7)
var array = [2, 5, 8, 9, 45, 56, 78]
var lowerBound = util.findLowerBound(10, array)
st.equal(lowerBound, 3)
lowerBound = util.findLowerBound(3, array)
st.equal(lowerBound, 0)
lowerBound = util.findLowerBound(100, array)
st.equal(lowerBound, 6)
lowerBound = util.findLowerBound(1, array)
st.equal(lowerBound, -1)
lowerBound = util.findLowerBound(45, array)
st.equal(lowerBound, 4)
array = [2, 5, 8, 9, 9, 45, 56, 78]
lowerBound = util.findLowerBound(9, array)
st.equal(lowerBound, 4)
lowerBound = util.findLowerBound(9, [])
st.equal(lowerBound, -1)
})
var testSourceMapping = {}
t.test('sourceMappingDecoder', function (st) {
st.plan(28)
var sourceMappingDecoder = new index.util.SourceMappingDecoder()
console.log('test decompressAll')
var result = sourceMappingDecoder.decompressAll(sourceMapping.mapping)
st.equal(result[0].start, 0)
st.equal(result[0].length, 205)
st.equal(result[0].file, 4)
st.equal(result[0].jump, '-')
st.equal(result[21].start, 0)
st.equal(result[21].length, 205)
st.equal(result[21].file, 4)
st.equal(result[21].jump, '-')
testSourceMapping[21] = result[21]
st.equal(result[22].start, 55)
st.equal(result[22].length, 74)
st.equal(result[22].file, 4)
st.equal(result[22].jump, '-')
var last = result.length - 1
st.equal(result[last].start, 142)
st.equal(result[last].length, 61)
st.equal(result[last].file, 4)
st.equal(result[last].jump, 'o')
testSourceMapping['last'] = result[last]
console.log('test decompress')
result = sourceMappingDecoder.atIndex(22, sourceMapping.mapping)
console.log(result)
st.equal(result.start, 55)
st.equal(result.length, 74)
st.equal(result.file, 4)
st.equal(result.jump, '-')
testSourceMapping[22] = result
result = sourceMappingDecoder.atIndex(82, sourceMapping.mapping)
console.log(result)
st.equal(result.start, 103)
st.equal(result.length, 2)
st.equal(result.file, 4)
st.equal(result.jump, '-')
testSourceMapping[82] = result
result = sourceMappingDecoder.atIndex(85, sourceMapping.mapping)
console.log(result)
st.equal(result.start, 99)
st.equal(result.length, 6)
st.equal(result.file, 4)
st.equal(result.jump, '-')
testSourceMapping[85] = result
})
t.test('sourceMappingLineColumnConverter', function (st) {
st.plan(14)
var sourceMappingDecoder = new index.util.SourceMappingDecoder()
var linesbreak = sourceMappingDecoder.getLinebreakPositions(sourceMapping.source)
st.equal(linesbreak[0], 16)
st.equal(linesbreak[5], 84)
var result = sourceMappingDecoder.convertOffsetToLineColumn(testSourceMapping[21], linesbreak)
st.equal(result.start.line, 0)
st.equal(result.start.column, 0)
st.equal(result.end.line, 15)
st.equal(result.end.column, 1)
result = sourceMappingDecoder.convertOffsetToLineColumn(testSourceMapping[82], linesbreak)
st.equal(result.start.line, 7)
st.equal(result.start.column, 12)
st.equal(result.end.line, 7)
st.equal(result.end.column, 14)
var res = { // point to \n
start: 103,
length: 4,
file: 4,
jump: '-'
}
result = sourceMappingDecoder.convertOffsetToLineColumn(res, linesbreak)
st.equal(result.start.line, 7)
st.equal(result.start.column, 12)
st.equal(result.end.line, 7)
st.equal(result.end.column, 16)
})
})
Loading…
Cancel
Save