1 /*
   2  * Copyright (c) 2017, 2020, 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  * @see     Code
  34  * @see LineNumber
  35  * @LastModified: Jan 2020
  36  */
  37 public final class LineNumberTable extends Attribute {
  38 
  39     private static final int MAX_LINE_LENGTH = 72;
  40     private LineNumber[] line_number_table; // Table of line/numbers pairs
  41 
  42 
  43     /*
  44      * Initialize from another object. Note that both objects use the same
  45      * references (shallow copy). Use copy() for a physical copy.
  46      */
  47     public LineNumberTable(final LineNumberTable c) {
  48         this(c.getNameIndex(), c.getLength(), c.getLineNumberTable(), c.getConstantPool());
  49     }
  50 
  51 
  52     /*
  53      * @param name_index Index of name
  54      * @param length Content length in bytes
  55      * @param line_number_table Table of line/numbers pairs
  56      * @param constant_pool Array of constants
  57      */
  58     public LineNumberTable(final int name_index, final int length, final LineNumber[] line_number_table,
  59             final ConstantPool constant_pool) {
  60         super(Const.ATTR_LINE_NUMBER_TABLE, name_index, length, constant_pool);
  61         this.line_number_table = line_number_table;
  62     }
  63 
  64     /**
  65      * Construct object from input stream.
  66      * @param name_index Index of name
  67      * @param length Content length in bytes
  68      * @param input Input stream
  69      * @param constant_pool Array of constants
  70      * @throws IOException if an I/O Exception occurs in readUnsignedShort
  71      */
  72     LineNumberTable(final int name_index, final int length, final DataInput input, final ConstantPool constant_pool)
  73             throws IOException {
  74         this(name_index, length, (LineNumber[]) null, constant_pool);
  75         final int line_number_table_length = input.readUnsignedShort();
  76         line_number_table = new LineNumber[line_number_table_length];
  77         for (int i = 0; i < line_number_table_length; i++) {
  78             line_number_table[i] = new LineNumber(input);
  79         }
  80     }
  81 
  82     /**
  83      * Called by objects that are traversing the nodes of the tree implicitely
  84      * defined by the contents of a Java class. I.e., the hierarchy of methods,
  85      * fields, attributes, etc. spawns a tree of objects.
  86      *
  87      * @param v Visitor object
  88      */
  89     @Override
  90     public void accept( final Visitor v ) {
  91         v.visitLineNumberTable(this);
  92     }
  93 
  94     /**
  95      * Dump line number table attribute to file stream in binary format.
  96      *
  97      * @param file Output file stream
  98      * @throws IOException if an I/O Exception occurs in writeShort
  99      */
 100     @Override
 101     public void dump( final DataOutputStream file ) throws IOException {
 102         super.dump(file);
 103         file.writeShort(line_number_table.length);
 104         for (final LineNumber lineNumber : line_number_table) {
 105             lineNumber.dump(file);
 106         }
 107     }
 108 
 109     /**
 110      * @return Array of (pc offset, line number) pairs.
 111      */
 112     public LineNumber[] getLineNumberTable() {
 113         return line_number_table;
 114     }
 115 
 116     /**
 117      * @param line_number_table the line number entries for this table
 118      */
 119     public void setLineNumberTable( final LineNumber[] line_number_table ) {
 120         this.line_number_table = line_number_table;
 121     }
 122 
 123     /**
 124      * @return String representation.
 125      */
 126     @Override
 127     public String toString() {
 128         final StringBuilder buf = new StringBuilder();
 129         final StringBuilder line = new StringBuilder();
 130 
 131         for (int i = 0; i < line_number_table.length; i++) {
 132             line.append(line_number_table[i].toString());
 133             if (i < line_number_table.length - 1) {
 134                 line.append(", ");
 135             }
 136             if ((line.length() > MAX_LINE_LENGTH) && (i < line_number_table.length - 1)) {
 137                 line.append(SecuritySupport.NEWLINE);
 138                 buf.append(line);
 139                 line.setLength(0);
 140             }
 141         }
 142         buf.append(line);
 143         return buf.toString();
 144     }
 145 
 146     /**
 147      * Map byte code positions to source code lines.
 148      *
 149      * @param pos byte code offset
 150      * @return corresponding line in source code
 151      */
 152     public int getSourceLine( final int pos ) {
 153         int l = 0;
 154         int r = line_number_table.length - 1;
 155         if (r < 0) {
 156             return -1;
 157         }
 158         int min_index = -1;
 159         int min = -1;
 160         /* Do a binary search since the array is ordered.
 161          */
 162         do {
 163             final int i = (l + r) >>> 1;
 164             final int j = line_number_table[i].getStartPC();
 165             if (j == pos) {
 166                 return line_number_table[i].getLineNumber();
 167             } else if (pos < j) {
 168                 r = i - 1;
 169             } else {
 170                 l = i + 1;
 171             }
 172             /* If exact match can't be found (which is the most common case)
 173              * return the line number that corresponds to the greatest index less
 174              * than pos.
 175              */
 176             if (j < pos && j > min) {
 177                 min = j;
 178                 min_index = i;
 179             }
 180         } while (l <= r);
 181         /* It's possible that we did not find any valid entry for the bytecode
 182          * offset we were looking for.
 183          */
 184         if (min_index < 0) {
 185             return -1;
 186         }
 187         return line_number_table[min_index].getLineNumber();
 188     }
 189 
 190     /**
 191      * @return deep copy of this attribute
 192      */
 193     @Override
 194     public Attribute copy( final ConstantPool _constant_pool ) {
 195         // TODO could use the lower level constructor and thereby allow
 196         // line_number_table to be made final
 197         final LineNumberTable c = (LineNumberTable) clone();
 198         c.line_number_table = new LineNumber[line_number_table.length];
 199         for (int i = 0; i < line_number_table.length; i++) {
 200             c.line_number_table[i] = line_number_table[i].copy();
 201         }
 202         c.setConstantPool(_constant_pool);
 203         return c;
 204     }
 205 
 206     public int getTableLength() {
 207         return line_number_table == null ? 0 : line_number_table.length;
 208     }
 209 }