1 /*
   2  * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 package com.sun.org.apache.bcel.internal.classfile;
  21 
  22 import com.sun.org.apache.bcel.internal.Const;
  23 import java.io.DataInput;
  24 import java.io.DataOutputStream;
  25 import java.io.IOException;
  26 import jdk.xml.internal.SecuritySupport;
  27 
  28 /**
  29  * This class represents a table of line numbers for debugging
  30  * purposes. This attribute is used by the <em>Code</em> attribute. It
  31  * contains pairs of PCs and line numbers.
  32  *
  33  * @version $Id$
  34  * @see     Code
  35  * @see LineNumber
  36  * @LastModified: Jun 2019
  37  */
  38 public final class LineNumberTable extends Attribute {
  39 
  40     private static final int MAX_LINE_LENGTH = 72;
  41     private LineNumber[] line_number_table; // Table of line/numbers pairs
  42 
  43 
  44     /*
  45      * Initialize from another object. Note that both objects use the same
  46      * references (shallow copy). Use copy() for a physical copy.
  47      */
  48     public LineNumberTable(final LineNumberTable c) {
  49         this(c.getNameIndex(), c.getLength(), c.getLineNumberTable(), c.getConstantPool());
  50     }
  51 
  52 
  53     /*
  54      * @param name_index Index of name
  55      * @param length Content length in bytes
  56      * @param line_number_table Table of line/numbers pairs
  57      * @param constant_pool Array of constants
  58      */
  59     public LineNumberTable(final int name_index, final int length, final LineNumber[] line_number_table,
  60             final ConstantPool constant_pool) {
  61         super(Const.ATTR_LINE_NUMBER_TABLE, name_index, length, constant_pool);
  62         this.line_number_table = line_number_table;
  63     }
  64 
  65     /**
  66      * Construct object from input stream.
  67      * @param name_index Index of name
  68      * @param length Content length in bytes
  69      * @param input Input stream
  70      * @param constant_pool Array of constants
  71      * @throws IOEXception if an I/O Exception occurs in readUnsignedShort
  72      */
  73     LineNumberTable(final int name_index, final int length, final DataInput input, final ConstantPool constant_pool)
  74             throws IOException {
  75         this(name_index, length, (LineNumber[]) null, constant_pool);
  76         final int line_number_table_length = input.readUnsignedShort();
  77         line_number_table = new LineNumber[line_number_table_length];
  78         for (int i = 0; i < line_number_table_length; i++) {
  79             line_number_table[i] = new LineNumber(input);
  80         }
  81     }
  82 
  83     /**
  84      * Called by objects that are traversing the nodes of the tree implicitely
  85      * defined by the contents of a Java class. I.e., the hierarchy of methods,
  86      * fields, attributes, etc. spawns a tree of objects.
  87      *
  88      * @param v Visitor object
  89      */
  90     @Override
  91     public void accept( final Visitor v ) {
  92         v.visitLineNumberTable(this);
  93     }
  94 
  95     /**
  96      * Dump line number table attribute to file stream in binary format.
  97      *
  98      * @param file Output file stream
  99      * @throws IOEXception if an I/O Exception occurs in writeShort
 100      */
 101     @Override
 102     public final void dump( final DataOutputStream file ) throws IOException {
 103         super.dump(file);
 104         file.writeShort(line_number_table.length);
 105         for (final LineNumber lineNumber : line_number_table) {
 106             lineNumber.dump(file);
 107         }
 108     }
 109 
 110     /**
 111      * @return Array of (pc offset, line number) pairs.
 112      */
 113     public final LineNumber[] getLineNumberTable() {
 114         return line_number_table;
 115     }
 116 
 117     /**
 118      * @param line_number_table the line number entries for this table
 119      */
 120     public final void setLineNumberTable( final LineNumber[] line_number_table ) {
 121         this.line_number_table = line_number_table;
 122     }
 123 
 124     /**
 125      * @return String representation.
 126      */
 127     @Override
 128     public final String toString() {
 129         final StringBuilder buf = new StringBuilder();
 130         final StringBuilder line = new StringBuilder();
 131 
 132         for (int i = 0; i < line_number_table.length; i++) {
 133             line.append(line_number_table[i].toString());
 134             if (i < line_number_table.length - 1) {
 135                 line.append(", ");
 136             }
 137             if ((line.length() > MAX_LINE_LENGTH) && (i < line_number_table.length - 1)) {
 138                 line.append(SecuritySupport.NEWLINE);
 139                 buf.append(line);
 140                 line.setLength(0);
 141             }
 142         }
 143         buf.append(line);
 144         return buf.toString();
 145     }
 146 
 147     /**
 148      * Map byte code positions to source code lines.
 149      *
 150      * @param pos byte code offset
 151      * @return corresponding line in source code
 152      */
 153     public int getSourceLine( final int pos ) {
 154         int l = 0;
 155         int r = line_number_table.length - 1;
 156         if (r < 0) {
 157             return -1;
 158         }
 159         int min_index = -1;
 160         int min = -1;
 161         /* Do a binary search since the array is ordered.
 162          */
 163         do {
 164             final int i = (l + r) / 2;
 165             final int j = line_number_table[i].getStartPC();
 166             if (j == pos) {
 167                 return line_number_table[i].getLineNumber();
 168             } else if (pos < j) {
 169                 r = i - 1;
 170             } else {
 171                 l = i + 1;
 172             }
 173             /* If exact match can't be found (which is the most common case)
 174              * return the line number that corresponds to the greatest index less
 175              * than pos.
 176              */
 177             if (j < pos && j > min) {
 178                 min = j;
 179                 min_index = i;
 180             }
 181         } while (l <= r);
 182         /* It's possible that we did not find any valid entry for the bytecode
 183          * offset we were looking for.
 184          */
 185         if (min_index < 0) {
 186             return -1;
 187         }
 188         return line_number_table[min_index].getLineNumber();
 189     }
 190 
 191     /**
 192      * @return deep copy of this attribute
 193      */
 194     @Override
 195     public Attribute copy( final ConstantPool _constant_pool ) {
 196         // TODO could use the lower level constructor and thereby allow
 197         // line_number_table to be made final
 198         final LineNumberTable c = (LineNumberTable) clone();
 199         c.line_number_table = new LineNumber[line_number_table.length];
 200         for (int i = 0; i < line_number_table.length; i++) {
 201             c.line_number_table[i] = line_number_table[i].copy();
 202         }
 203         c.setConstantPool(_constant_pool);
 204         return c;
 205     }
 206 
 207     public final int getTableLength() {
 208         return line_number_table == null ? 0 : line_number_table.length;
 209     }
 210 }