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 }