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