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