All files sourceMappings.ts

100% Statements 38/38
100% Branches 16/16
100% Functions 9/9
100% Lines 38/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 1511x   1x                       1x 8x   6x   8x 8x                       1x   105x   1x                     1x 110x 110x                     1x           1x       1x 1x 16x   1x                   2x 2x   2x 52x         3x     52x   2x 2x                   2x 2x     2x 52x         1x     52x     2x 2x                 4x   3x         1x                
import { isAstNode, AstWalker } from './astWalker';
import { AstNode, LineColPosition, LineColRange, Location } from "./types";
import { util } from "@remix-project/remix-lib";
 
export declare interface SourceMappings {
  // eslint-disable-next-line @typescript-eslint/no-misused-new
  new(): SourceMappings;
}
 
/**
 * Turn an character offset into a "LineColPosition".
 *
 * @param offset  The character offset to convert.
 */
export function lineColPositionFromOffset(offset: number, lineBreaks: Array<number>): LineColPosition {
  let line: number = util.findLowerBound(offset, lineBreaks);
  if (lineBreaks[line] !== offset) {
    line += 1;
  }
  const beginColumn = line === 0 ? 0 : (lineBreaks[line - 1] + 1);
  return <LineColPosition>{
    line: line + 1,
    character: (offset - beginColumn) + 1
  }
}
 
/**
 * Turn a solc AST's "src" attribute string (s:l:f)
 * into a Location
 *
 * @param astNode  The object to convert.
 */
export function sourceLocationFromAstNode(astNode: AstNode): Location | null {
  if (isAstNode(astNode) && astNode.src) {
    return sourceLocationFromSrc(astNode.src)
  }
  return null;
}
 
/**
 * Break out fields of solc AST's "src" attribute string (s:l:f)
 * into its "start", "length", and "file index" components
 * and return that as a Location
 *
 * @param src  A solc "src" field.
 * @returns {Location}
 */
export function sourceLocationFromSrc(src: string): Location {
  const split = src.split(':')
  return <Location>{
    start: parseInt(split[0], 10),
    length: parseInt(split[1], 10),
    file: parseInt(split[2], 10)
  }
}
 
/**
 * Routines for retrieving solc AST object(s) using some criteria, usually
 * includng "src' information.
 */
export class SourceMappings {
 
  readonly source: string;
  readonly lineBreaks: Array<number>;
 
  constructor(source: string) {
    this.source = source;
 
    // Create a list of line offsets which will be used to map between
    // character offset and line/column positions.
    const lineBreaks: Array<number> = [];
    for (let pos = source.indexOf('\n'); pos >= 0; pos = source.indexOf('\n', pos + 1)) {
      lineBreaks.push(pos)
    }
    this.lineBreaks = lineBreaks;
  };
 
  /**
   * Get a list of nodes that are at the given "position".
   *
   * @param astNodeType  Type of node to return or null.
   * @param position     Character offset where AST node should be located.
   */
  nodesAtPosition(astNodeType: string | null, position: Location, ast: AstNode): Array<AstNode> {
    const astWalker = new AstWalker()
    const found: Array<AstNode> = [];
 
    const callback = function(node: AstNode): boolean {
      const nodeLocation = sourceLocationFromAstNode(node);
      if (nodeLocation &&
        nodeLocation.start == position.start &&
        nodeLocation.length == position.length) {
        if (!astNodeType || astNodeType === node.nodeType) {
          found.push(node)
        }
      }
      return true;
    }
    astWalker.walkFull(ast, callback);
    return found;
  }
 
  /**
   * Retrieve the first "astNodeType" that includes the source map at arg instIndex, or "null" if none found.
   *
   * @param astNodeType   nodeType that a found ASTNode must be. Use "null" if any ASTNode can match.
   * @param sourceLocation "src" location that the AST node must match.
   */
  findNodeAtSourceLocation(astNodeType: string | undefined, sourceLocation: Location, ast: AstNode | null): AstNode | null {
    const astWalker = new AstWalker()
    let found = null;
    /* FIXME: Looking at AST walker code,
       I don't understand a need to return a boolean. */
    const callback = function(node: AstNode) {
      const nodeLocation = sourceLocationFromAstNode(node);
      if (nodeLocation &&
        nodeLocation.start == sourceLocation.start &&
        nodeLocation.length == sourceLocation.length) {
        if (astNodeType == undefined || astNodeType === node.nodeType) {
          found = node;
        }
      }
      return true;
    }
 
    astWalker.walkFull(ast, callback);
    return found;
  }
 
  /**
   * Retrieve the line/column range position for the given source-mapping string.
   *
   * @param src  Solc "src" object containing attributes {source} and {length}.
   */
  srcToLineColumnRange(src: string): LineColRange {
    const sourceLocation = sourceLocationFromSrc(src);
    if (sourceLocation.start >= 0 && sourceLocation.length >= 0) {
      return <LineColRange>{
        start: lineColPositionFromOffset(sourceLocation.start, this.lineBreaks),
        end: lineColPositionFromOffset(sourceLocation.start + sourceLocation.length, this.lineBreaks)
      }
    } else {
      return <LineColRange>{
        start: null,
        end: null
      }
    }
  }
 
}