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 public InstructionHandle getEnd() { 119 return end; 120 } 121 122 public int getIndex() { 123 return index; 124 } 125 126 public boolean getLiveToEnd() { 127 return liveToEnd; 128 } 129 130 /** 131 * Gets LocalVariable object. 132 * 133 * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods 134 * has been called for the instruction list. 135 * 136 * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference 137 * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last 138 * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases. 139 * 140 * @param cp constant pool 141 */ 142 public LocalVariable getLocalVariable(final ConstantPoolGen cp) { 143 int startPc = 0; 144 int length = 0; 145 if (start != null && end != null) { 146 startPc = start.getPosition(); 147 length = end.getPosition() - startPc; 148 if (end.getNext() == null && liveToEnd) { 149 length += end.getInstruction().getLength(); 150 } 151 } 152 final int nameIndex = cp.addUtf8(name); 153 final int signatureIndex = cp.addUtf8(type.getSignature()); 154 return new LocalVariable(startPc, length, nameIndex, signatureIndex, index, cp.getConstantPool(), origIndex); 155 } 156 157 @Override 158 public String getName() { 159 return name; 160 } 161 162 public int getOrigIndex() { 163 return origIndex; 164 } 165 166 public InstructionHandle getStart() { 167 return start; 168 } 169 170 @Override 171 public Type getType() { 172 return type; 173 } 174 175 @Override 176 public int hashCode() { 177 // If the user changes the name or type, problems with the targeter hashmap will occur. 178 // Note: Index cannot be part of hash as it may be changed by the user. 179 return name.hashCode() ^ type.hashCode(); 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 public void setIndex(final int index) { 188 this.index = index; 189 } 190 191 public void setLiveToEnd(final boolean liveToEnd) { 192 this.liveToEnd = liveToEnd; 193 } 194 195 @Override 196 public void setName(final String name) { 197 this.name = name; 198 } 199 200 public void setStart(final InstructionHandle start) { // TODO could be package-protected? 201 BranchInstruction.notifyTarget(this.start, start, this); 202 this.start = start; 203 } 204 205 @Override 206 public void setType(final Type type) { 207 this.type = type; 208 } 209 210 @Override 211 public String toString() { 212 return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")"; 213 } 214 215 /** 216 * @param oldIh old target, either start or end 217 * @param newIh new target 218 */ 219 @Override 220 public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { 221 boolean targeted = false; 222 if (start == oldIh) { 223 targeted = true; 224 setStart(newIh); 225 } 226 if (end == oldIh) { 227 targeted = true; 228 setEnd(newIh); 229 } 230 if (!targeted) { 231 throw new ClassGenException("Not targeting " + oldIh + ", but {" + start + ", " + end + "}"); 232 } 233 } 234}