1/*
2 * Copyright (c) 2017, 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 */
20package com.sun.org.apache.bcel.internal.classfile;
21
22import com.sun.org.apache.bcel.internal.Const;
23import java.io.DataInput;
24import java.io.DataOutputStream;
25import java.io.IOException;
26import jdk.xml.internal.SecuritySupport;
27
28/**
29 * This class represents a table of line numbers for debugging purposes. This
30 * attribute is used by the <em>Code</em> attribute. It contains pairs of PCs
31 * and line numbers.
32 *
33 * @version $Id: LineNumberTable.java 1749603 2016-06-21 20:50:19Z ggregory $
34 * @see Code
35 * @see LineNumber
36 */
37public 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     *
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}
211