truffle-analyze
Version:
Add vulnerability and weakness analysis via the MythX
231 lines (213 loc) • 7.64 kB
JavaScript
/***
This is modified from remix-lib/src/sourceMappingDecoder.js
The essential difference is that remix-lib uses legacyAST and we
use ast instead. legacyAST has field "children" while ast
renames this to "nodes".
***/
;
var util = require('remix-lib/src/util');
var AstWalker = require('./astWalker');
/**
* Decompress the source mapping given by solc-bin.js
*/
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
*
* @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) {
if (value) {
value = value.split(':');
return {
start: parseInt(value[0]),
length: parseInt(value[1]),
file: parseInt(value[2])
};
}
};
/**
* 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
};
}
};
/**
* 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) {
var line = util.findLowerBound(pos, lineBreakPositions);
if (lineBreakPositions[line] !== pos) {
line += 1;
}
var beginColumn = line === 0 ? 0 : (lineBreakPositions[line - 1] + 1);
var column = pos - beginColumn;
return {
line: line,
column: column
};
}
function sourceLocationFromAstNode (astNode) {
if (astNode.src) {
var split = astNode.src.split(':');
return {
start: parseInt(split[0]),
length: parseInt(split[1]),
file: parseInt(split[2])
};
}
return null;
}
function findNodeAtInstructionIndex (astNodeType, instIndex, sourceMap, ast) {
var sourceLocation = atIndex(instIndex, sourceMap);
return findNodeAtSourceLocation(astNodeType, sourceLocation, ast);
}
function findNodeAtSourceLocation (astNodeType, sourceLocation, ast) {
var astWalker = new AstWalker();
var callback = {};
var found = null;
callback['*'] = function (node) {
const nodeLocation = sourceLocationFromAstNode(node);
if (!nodeLocation) {
return true;
}
if (nodeLocation.start <= sourceLocation.start && nodeLocation.start + nodeLocation.length >= sourceLocation.start + sourceLocation.length) {
if (astNodeType === node.nodeType) {
found = node;
return false;
} else {
return true;
}
} else {
return false;
}
};
astWalker.walk(ast, callback);
return found;
}
function nodesAtPosition (astNodeType, position, ast) {
var astWalker = new AstWalker();
var callback = {};
var found = [];
callback['*'] = function (node) {
var nodeLocation = sourceLocationFromAstNode(node);
if (!nodeLocation) {
return;
}
if (nodeLocation.start <= position && nodeLocation.start + nodeLocation.length >= position) {
if (!astNodeType || astNodeType === node.name) {
found.push(node);
if (astNodeType) return false;
}
return true;
} else {
return false;
}
};
astWalker.walk(ast.ast, callback);
return found;
}
function atIndex (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] !== '-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.file === undefined && current[2] && current[2] !== '-1' && 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;
}
module.exports = SourceMappingDecoder;