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