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; 027 028/** 029 * This class represents colection of local variables in a method. This attribute is contained in the <em>Code</em> 030 * attribute. 031 * 032 * @see Code 033 * @see LocalVariable 034 */ 035public class LocalVariableTable extends Attribute implements Iterable<LocalVariable> { 036 037 private LocalVariable[] localVariableTable; // variables 038 039 /** 040 * Construct object from input stream. 041 * 042 * @param nameIndex Index in constant pool 043 * @param length Content length in bytes 044 * @param input Input stream 045 * @param constantPool Array of constants 046 * @throws IOException if an I/O error occurs. 047 */ 048 LocalVariableTable(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { 049 this(nameIndex, length, (LocalVariable[]) null, constantPool); 050 final int localVariableTableLength = input.readUnsignedShort(); 051 localVariableTable = new LocalVariable[localVariableTableLength]; 052 for (int i = 0; i < localVariableTableLength; i++) { 053 localVariableTable[i] = new LocalVariable(input, constantPool); 054 } 055 } 056 057 /** 058 * @param nameIndex Index in constant pool to `LocalVariableTable' 059 * @param length Content length in bytes 060 * @param localVariableTable Table of local variables 061 * @param constantPool Array of constants 062 */ 063 public LocalVariableTable(final int nameIndex, final int length, final LocalVariable[] localVariableTable, final ConstantPool constantPool) { 064 super(Const.ATTR_LOCAL_VARIABLE_TABLE, nameIndex, length, constantPool); 065 this.localVariableTable = localVariableTable; 066 } 067 068 /** 069 * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a 070 * physical copy. 071 */ 072 public LocalVariableTable(final LocalVariableTable c) { 073 this(c.getNameIndex(), c.getLength(), c.getLocalVariableTable(), c.getConstantPool()); 074 } 075 076 /** 077 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. 078 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. 079 * 080 * @param v Visitor object 081 */ 082 @Override 083 public void accept(final Visitor v) { 084 v.visitLocalVariableTable(this); 085 } 086 087 /** 088 * @return deep copy of this attribute 089 */ 090 @Override 091 public Attribute copy(final ConstantPool constantPool) { 092 final LocalVariableTable c = (LocalVariableTable) clone(); 093 c.localVariableTable = new LocalVariable[localVariableTable.length]; 094 Arrays.setAll(c.localVariableTable, i -> localVariableTable[i].copy()); 095 c.setConstantPool(constantPool); 096 return c; 097 } 098 099 /** 100 * Dump local variable table attribute to file stream in binary format. 101 * 102 * @param file Output file stream 103 * @throws IOException if an I/O error occurs. 104 */ 105 @Override 106 public final void dump(final DataOutputStream file) throws IOException { 107 super.dump(file); 108 file.writeShort(localVariableTable.length); 109 for (final LocalVariable variable : localVariableTable) { 110 variable.dump(file); 111 } 112 } 113 114 /** 115 * 116 * @param index the variable slot 117 * 118 * @return the first LocalVariable that matches the slot or null if not found 119 * 120 * @deprecated since 5.2 because multiple variables can share the same slot, use getLocalVariable(int index, int pc) 121 * instead. 122 */ 123 @java.lang.Deprecated 124 public final LocalVariable getLocalVariable(final int index) { 125 for (final LocalVariable variable : localVariableTable) { 126 if (variable.getIndex() == index) { 127 return variable; 128 } 129 } 130 return null; 131 } 132 133 /** 134 * 135 * @param index the variable slot 136 * @param pc the current pc that this variable is alive 137 * 138 * @return the LocalVariable that matches or null if not found 139 */ 140 public final LocalVariable getLocalVariable(final int index, final int pc) { 141 for (final LocalVariable variable : localVariableTable) { 142 if (variable.getIndex() == index) { 143 final int startPc = variable.getStartPC(); 144 final int endPc = startPc + variable.getLength(); 145 if (pc >= startPc && pc <= endPc) { 146 return variable; 147 } 148 } 149 } 150 return null; 151 } 152 153 /** 154 * @return Array of local variables of method. 155 */ 156 public final LocalVariable[] getLocalVariableTable() { 157 return localVariableTable; 158 } 159 160 public final int getTableLength() { 161 return localVariableTable == null ? 0 : localVariableTable.length; 162 } 163 164 @Override 165 public Iterator<LocalVariable> iterator() { 166 return Stream.of(localVariableTable).iterator(); 167 } 168 169 public final void setLocalVariableTable(final LocalVariable[] localVariableTable) { 170 this.localVariableTable = localVariableTable; 171 } 172 173 /** 174 * @return String representation. 175 */ 176 @Override 177 public final String toString() { 178 final StringBuilder buf = new StringBuilder(); 179 for (int i = 0; i < localVariableTable.length; i++) { 180 buf.append(localVariableTable[i]); 181 if (i < localVariableTable.length - 1) { 182 buf.append('\n'); 183 } 184 } 185 return buf.toString(); 186 } 187}