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.generic; 020 021import org.apache.bcel.Const; 022import org.apache.bcel.classfile.LocalVariable; 023 024/** 025 * Represents a local variable within a method. It contains its scope, name and type. The generated LocalVariable object 026 * can be obtained with getLocalVariable which needs the instruction list and the constant pool as parameters. 027 * 028 * @see LocalVariable 029 * @see MethodGen 030 */ 031public class LocalVariableGen implements InstructionTargeter, NamedAndTyped, Cloneable { 032 033 private int index; 034 private String name; 035 private Type type; 036 private InstructionHandle start; 037 private InstructionHandle end; 038 private int origIndex; // never changes; used to match up with LocalVariableTypeTable entries 039 private boolean liveToEnd; 040 041 /** 042 * Generate a local variable that with index 'index'. Note that double and long variables need two indexs. Index indices 043 * have to be provided by the user. 044 * 045 * @param index index of local variable. 046 * @param name its name. 047 * @param type its type. 048 * @param start from where the instruction is valid (null means from the start). 049 * @param end until where the instruction is valid (null means to the end). 050 */ 051 public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end) { 052 if (index < 0 || index > Const.MAX_SHORT) { 053 throw new ClassGenException("Invalid index: " + index); 054 } 055 this.name = name; 056 this.type = type; 057 this.index = index; 058 setStart(start); 059 setEnd(end); 060 this.origIndex = index; 061 this.liveToEnd = end == null; 062 } 063 064 /** 065 * Generates a local variable that with index 'index'. Note that double and long variables need two indexs. Index 066 * indices have to be provided by the user. 067 * 068 * @param index index of local variable. 069 * @param name its name. 070 * @param type its type. 071 * @param start from where the instruction is valid (null means from the start). 072 * @param end until where the instruction is valid (null means to the end). 073 * @param origIndex index of local variable prior to any changes to index. 074 */ 075 public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end, 076 final int origIndex) { 077 this(index, name, type, start, end); 078 this.origIndex = origIndex; 079 } 080 081 @Override 082 public Object clone() { 083 try { 084 return super.clone(); 085 } catch (final CloneNotSupportedException e) { 086 throw new UnsupportedOperationException("Clone Not Supported", e); // never happens 087 } 088 } 089 090 /** 091 * @return true, if ih is target of this variable. 092 */ 093 @Override 094 public boolean containsTarget(final InstructionHandle ih) { 095 return start == ih || end == ih; 096 } 097 098 /** 099 * Clear the references from and to this variable when it's removed. 100 */ 101 void dispose() { 102 setStart(null); 103 setEnd(null); 104 } 105 106 /** 107 * We consider to local variables to be equal, if the use the same index and are valid in the same range. 108 */ 109 @Override 110 public boolean equals(final Object o) { 111 if (!(o instanceof LocalVariableGen)) { 112 return false; 113 } 114 final LocalVariableGen l = (LocalVariableGen) o; 115 return l.index == index && l.start == start && l.end == end; 116 } 117 118 /** 119 * Gets the end instruction handle. 120 * 121 * @return the end instruction handle. 122 */ 123 public InstructionHandle getEnd() { 124 return end; 125 } 126 127 /** 128 * Gets the index. 129 * 130 * @return the index. 131 */ 132 public int getIndex() { 133 return index; 134 } 135 136 /** 137 * Gets whether the variable lives to the end. 138 * 139 * @return true if the variable lives to the end. 140 */ 141 public boolean getLiveToEnd() { 142 return liveToEnd; 143 } 144 145 /** 146 * Gets LocalVariable object. 147 * 148 * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods 149 * has been called for the instruction list. 150 * 151 * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference 152 * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last 153 * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases. 154 * 155 * @param cp constant pool. 156 * @return the local variable. 157 */ 158 public LocalVariable getLocalVariable(final ConstantPoolGen cp) { 159 int startPc = 0; 160 int length = 0; 161 if (start != null && end != null) { 162 startPc = start.getPosition(); 163 length = end.getPosition() - startPc; 164 if (end.getNext() == null && liveToEnd) { 165 length += end.getInstruction().getLength(); 166 } 167 } 168 final int nameIndex = cp.addUtf8(name); 169 final int signatureIndex = cp.addUtf8(type.getSignature()); 170 return new LocalVariable(startPc, length, nameIndex, signatureIndex, index, cp.getConstantPool(), origIndex); 171 } 172 173 @Override 174 public String getName() { 175 return name; 176 } 177 178 /** 179 * Gets the original index. 180 * 181 * @return the original index. 182 */ 183 public int getOrigIndex() { 184 return origIndex; 185 } 186 187 /** 188 * Gets the start instruction handle. 189 * 190 * @return the start instruction handle. 191 */ 192 public InstructionHandle getStart() { 193 return start; 194 } 195 196 @Override 197 public Type getType() { 198 return type; 199 } 200 201 @Override 202 public int hashCode() { 203 // If the user changes the name or type, problems with the targeter hashmap will occur. 204 // Note: Index cannot be part of hash as it may be changed by the user. 205 return name.hashCode() ^ type.hashCode(); 206 } 207 208 /** 209 * Sets the end instruction handle. 210 * 211 * @param end the end instruction handle. 212 */ 213 public void setEnd(final InstructionHandle end) { // TODO could be package-protected? 214 BranchInstruction.notifyTarget(this.end, end, this); 215 this.end = end; 216 } 217 218 /** 219 * Sets the index. 220 * 221 * @param index the index. 222 */ 223 public void setIndex(final int index) { 224 this.index = index; 225 } 226 227 /** 228 * Sets whether the variable lives to the end. 229 * 230 * @param liveToEnd true if the variable lives to the end. 231 */ 232 public void setLiveToEnd(final boolean liveToEnd) { 233 this.liveToEnd = liveToEnd; 234 } 235 236 @Override 237 public void setName(final String name) { 238 this.name = name; 239 } 240 241 /** 242 * Sets the start instruction handle. 243 * 244 * @param start the start instruction handle. 245 */ 246 public void setStart(final InstructionHandle start) { // TODO could be package-protected? 247 BranchInstruction.notifyTarget(this.start, start, this); 248 this.start = start; 249 } 250 251 @Override 252 public void setType(final Type type) { 253 this.type = type; 254 } 255 256 @Override 257 public String toString() { 258 return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")"; 259 } 260 261 /** 262 * @param oldIh old target, either start or end. 263 * @param newIh new target. 264 */ 265 @Override 266 public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { 267 boolean targeted = false; 268 if (start == oldIh) { 269 targeted = true; 270 setStart(newIh); 271 } 272 if (end == oldIh) { 273 targeted = true; 274 setEnd(newIh); 275 } 276 if (!targeted) { 277 throw new ClassGenException("Not targeting " + oldIh + ", but {" + start + ", " + end + "}"); 278 } 279 } 280} 281