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