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 }