001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.bcel.classfile;
018
019import java.io.DataInput;
020import java.io.DataOutputStream;
021import java.io.IOException;
022import java.util.Arrays;
023import java.util.Iterator;
024import java.util.stream.Stream;
025
026import org.apache.bcel.Const;
027import org.apache.bcel.util.Args;
028
029/**
030 * This class represents colection of local variables in a method. This attribute is contained in the <em>Code</em>
031 * attribute.
032 *
033 * @see Code
034 * @see LocalVariable
035 */
036public class LocalVariableTable extends Attribute implements Iterable<LocalVariable> {
037
038    private static final LocalVariable[] EMPTY_ARRAY = {};
039
040    private LocalVariable[] localVariableTable; // variables
041
042    /**
043     * Constructs object from input stream.
044     *
045     * @param nameIndex Index in constant pool
046     * @param length Content length in bytes
047     * @param input Input stream
048     * @param constantPool Array of constants
049     * @throws IOException if an I/O error occurs.
050     */
051    LocalVariableTable(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
052        this(nameIndex, length, (LocalVariable[]) null, constantPool);
053        final int localVariableTableLength = input.readUnsignedShort();
054        localVariableTable = new LocalVariable[localVariableTableLength];
055        for (int i = 0; i < localVariableTableLength; i++) {
056            localVariableTable[i] = new LocalVariable(input, constantPool);
057        }
058    }
059
060    /**
061     * @param nameIndex Index in constant pool to 'LocalVariableTable'
062     * @param length Content length in bytes
063     * @param localVariableTable Table of local variables
064     * @param constantPool Array of constants
065     */
066    public LocalVariableTable(final int nameIndex, final int length, final LocalVariable[] localVariableTable, final ConstantPool constantPool) {
067        super(Const.ATTR_LOCAL_VARIABLE_TABLE, nameIndex, length, constantPool);
068        this.localVariableTable = localVariableTable != null ? localVariableTable : EMPTY_ARRAY;
069        Args.requireU2(this.localVariableTable.length, "localVariableTable.length");
070    }
071
072    /**
073     * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
074     * physical copy.
075     *
076     * @param c Source to copy.
077     */
078    public LocalVariableTable(final LocalVariableTable c) {
079        this(c.getNameIndex(), c.getLength(), c.getLocalVariableTable(), c.getConstantPool());
080    }
081
082    /**
083     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
084     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
085     *
086     * @param v Visitor object
087     */
088    @Override
089    public void accept(final Visitor v) {
090        v.visitLocalVariableTable(this);
091    }
092
093    /**
094     * @return deep copy of this attribute
095     */
096    @Override
097    public Attribute copy(final ConstantPool constantPool) {
098        final LocalVariableTable c = (LocalVariableTable) clone();
099        c.localVariableTable = new LocalVariable[localVariableTable.length];
100        Arrays.setAll(c.localVariableTable, i -> localVariableTable[i].copy());
101        c.setConstantPool(constantPool);
102        return c;
103    }
104
105    /**
106     * Dump local variable table attribute to file stream in binary format.
107     *
108     * @param file Output file stream
109     * @throws IOException if an I/O error occurs.
110     */
111    @Override
112    public final void dump(final DataOutputStream file) throws IOException {
113        super.dump(file);
114        file.writeShort(localVariableTable.length);
115        for (final LocalVariable variable : localVariableTable) {
116            variable.dump(file);
117        }
118    }
119
120    /**
121     *
122     * @param index the variable slot
123     *
124     * @return the first LocalVariable that matches the slot or null if not found
125     *
126     * @deprecated since 5.2 because multiple variables can share the same slot, use getLocalVariable(int index, int pc)
127     *             instead.
128     */
129    @java.lang.Deprecated
130    public final LocalVariable getLocalVariable(final int index) {
131        for (final LocalVariable variable : localVariableTable) {
132            if (variable.getIndex() == index) {
133                return variable;
134            }
135        }
136        return null;
137    }
138
139    /**
140     *
141     * @param index the variable slot
142     * @param pc the current pc that this variable is alive
143     *
144     * @return the LocalVariable that matches or null if not found
145     */
146    public final LocalVariable getLocalVariable(final int index, final int pc) {
147        for (final LocalVariable variable : localVariableTable) {
148            if (variable.getIndex() == index) {
149                final int startPc = variable.getStartPC();
150                final int endPc = startPc + variable.getLength();
151                if (pc >= startPc && pc <= endPc) {
152                    return variable;
153                }
154            }
155        }
156        return null;
157    }
158
159    /**
160     * @return Array of local variables of method.
161     */
162    public final LocalVariable[] getLocalVariableTable() {
163        return localVariableTable;
164    }
165
166    public final int getTableLength() {
167        return localVariableTable.length;
168    }
169
170    @Override
171    public Iterator<LocalVariable> iterator() {
172        return Stream.of(localVariableTable).iterator();
173    }
174
175    public final void setLocalVariableTable(final LocalVariable[] localVariableTable) {
176        this.localVariableTable = localVariableTable != null ? localVariableTable : EMPTY_ARRAY;
177    }
178
179    /**
180     * @return String representation.
181     */
182    @Override
183    public final String toString() {
184        final StringBuilder buf = new StringBuilder();
185        for (int i = 0; i < localVariableTable.length; i++) {
186            buf.append(localVariableTable[i]);
187            if (i < localVariableTable.length - 1) {
188                buf.append('\n');
189            }
190        }
191        return buf.toString();
192    }
193}