sourcemappingdecoder

pull/7/head
yann300 9 years ago
parent b9a9d032cb
commit 40f6c6379c
  1. 25
      src/helpers/traceHelper.js
  2. 35
      src/helpers/util.js
  3. 6
      src/index.js
  4. 20
      src/trace/traceManager.js
  5. 2
      src/ui/Ethdebugger.js
  6. 134
      src/util/sourceMappingDecoder.js
  7. 22
      test/resources/sourceMapping.js
  8. 1
      test/tests.js
  9. 79
      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,40 @@ module.exports = {
ret.push(row)
}
return ret
},
/*
Binary Search:
Assumes that @arg array is sorted increasingly
returns the smallest i such that target >= changes[i]
returns arary.length - 1 (if all elements in array are smaller than target)
returns 0 (if target is smaller than the first element of array || if array is empty)
*/
findLowerBound: function (target, array) {
if (array.length === 0) {
return 0
}
return findLowerBoundInternal(target, array, 0, array.length - 1)
}
}
function findLowerBoundInternal (target, array, lowerbound, higherbound) {
if (array[higherbound] < target) {
return higherbound
} else if (array[lowerbound] > target) {
return lowerbound
}
var middle
while (lowerbound + 1 !== higherbound) {
middle = Math.floor((higherbound + lowerbound) / 2)
if (array[middle] > target) {
higherbound = middle
} else if (array[middle] < target) {
lowerbound = middle
higherbound = array.length - 1
} else if (array[middle] === target) {
return middle
}
}
return lowerbound
}

@ -4,11 +4,12 @@ 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) {
if (typeof (window) !== 'undefined') {
window.remix = modules()
}
@ -24,6 +25,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,8 @@ TraceManager.prototype.getStorageAt = function (stepIndex, tx, callback, address
return callback(check, null)
}
if (!address) {
var stoChange = traceHelper.findLowerBound(stepIndex, this.traceCache.storageChanges)
var stoChangeIndex = util.findLowerBound(stepIndex, this.traceCache.storageChanges)
var stoChange = this.traceCache.storageChanges[stoChangeIndex]
if (stoChange === undefined) return callback('no storage found', null)
address = this.traceCache.sstore[stoChange].address
}
@ -122,7 +124,8 @@ TraceManager.prototype.getCallDataAt = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
var callDataChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callDataChanges)
var callDataChangeIndex = util.findLowerBound(stepIndex, this.traceCache.callDataChanges)
var callDataChange = this.traceCache.callDataChanges[callDataChangeIndex]
if (callDataChange === undefined) return callback('no calldata found', null)
callback(null, [this.traceCache.callsData[callDataChange]])
}
@ -132,7 +135,8 @@ TraceManager.prototype.getCallStackAt = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
var callStackChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callChanges)
var callStackChangeIndex = util.findLowerBound(stepIndex, this.traceCache.callChanges)
var callStackChange = this.traceCache.callChanges[callStackChangeIndex]
if (callStackChange === undefined) return callback('no callstack found', null)
callback(null, this.traceCache.callStack[callStackChange].callStack)
}
@ -157,7 +161,8 @@ TraceManager.prototype.getLastCallChangeSince = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
var callChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callChanges)
var callChangeIndex = util.findLowerBound(stepIndex, this.traceCache.callChanges)
var callChange = this.traceCache.callChanges[callChangeIndex]
if (callChange === undefined) {
callback(null, 0)
} else {
@ -203,7 +208,8 @@ TraceManager.prototype.getMemoryAt = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
var lastChanges = traceHelper.findLowerBound(stepIndex, this.traceCache.memoryChanges)
var lastChangesIndex = util.findLowerBound(stepIndex, this.traceCache.memoryChanges)
var lastChanges = this.traceCache.memoryChanges[lastChangesIndex]
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,134 @@
'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(';')
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
/*
var lines = source.split('\n')
var currentPos = 0
var ret = []
for (var k in lines) {
ret.push(currentPos + lines[k].length)
currentPos += lines[k].length + 1
}
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}}
*/
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: -1,
end: -1
}
}
}
function convertFromCharPosition (pos, lineColumnLayout) {
var lowerBound = util.findLowerBound(pos, lineColumnLayout)
var line
if (lowerBound === 0) {
line = 0
} else if (lowerBound < pos) {
line = lowerBound + 1
} else {
line = lowerBound
}
var column
if (lowerBound === 0) {
column = pos
} else {
column = pos - 1 - lineColumnLayout[line - 1]
}
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,79 @@
'use strict'
var sourceMapping = require('./resources/sourceMapping')
var index = require('../src/index')
var tape = require('tape')
tape('Util', function (t) {
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.ok(result[0].start === 0)
st.ok(result[0].length === 205)
st.ok(result[0].file === 4)
st.ok(result[0].jump === '-')
st.ok(result[21].start === 0)
st.ok(result[21].length === 205)
st.ok(result[21].file === 4)
st.ok(result[21].jump === '-')
testSourceMapping[21] = result[21]
st.ok(result[22].start === 55)
st.ok(result[22].length === 74)
st.ok(result[22].file === 4)
st.ok(result[22].jump === '-')
var last = result.length - 1
st.ok(result[last].start === 142)
st.ok(result[last].length === 61)
st.ok(result[last].file === 4)
st.ok(result[last].jump === 'o')
testSourceMapping['last'] = result[last]
console.log('test decompress')
result = sourceMappingDecoder.atIndex(22, sourceMapping.mapping)
console.log(result)
st.ok(result.start === 55)
st.ok(result.length === 74)
st.ok(result.file === 4)
st.ok(result.jump === '-')
testSourceMapping[22] = result
result = sourceMappingDecoder.atIndex(82, sourceMapping.mapping)
console.log(result)
st.ok(result.start === 103)
st.ok(result.length === 2)
st.ok(result.file === 4)
st.ok(result.jump === '-')
testSourceMapping[82] = result
result = sourceMappingDecoder.atIndex(85, sourceMapping.mapping)
console.log(result)
st.ok(result.start === 99)
st.ok(result.length === 6)
st.ok(result.file === 4)
st.ok(result.jump === '-')
testSourceMapping[85] = result
})
t.test('sourceMappingLineColumnConverter', function (st) {
st.plan(10)
var sourceMappingDecoder = new index.util.SourceMappingDecoder()
var linesbreak = sourceMappingDecoder.getLinebreakPositions(sourceMapping.source)
st.ok(linesbreak[0] === 16)
st.ok(linesbreak[5] === 84)
var result = sourceMappingDecoder.convertOffsetToLineColumn(testSourceMapping[21], linesbreak)
st.ok(result.start.line === 0)
st.ok(result.start.column === 0)
st.ok(result.end.line === 15)
st.ok(result.end.column === 1)
result = sourceMappingDecoder.convertOffsetToLineColumn(testSourceMapping[82], linesbreak)
st.ok(result.start.line === 7)
st.ok(result.start.column === 12)
st.ok(result.end.line === 7)
st.ok(result.end.column === 14)
})
})
Loading…
Cancel
Save