LocalVariableList.java revision 2ad60cfc28e14ee8f0bb038720836a4696c478ad
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.cf.code; 18 19import com.android.dx.rop.cst.CstUtf8; 20import com.android.dx.rop.type.Type; 21import com.android.dx.rop.code.LocalItem; 22import com.android.dx.util.FixedSizeList; 23 24/** 25 * List of "local variable" entries, which are the contents of 26 * <code>LocalVariableTable</code> and <code>LocalVariableTypeTable</code> 27 * attributes, as well as combinations of the two. 28 */ 29public final class LocalVariableList extends FixedSizeList { 30 /** non-null; zero-size instance */ 31 public static final LocalVariableList EMPTY = new LocalVariableList(0); 32 33 /** 34 * Returns an instance which is the concatenation of the two given 35 * instances. The result is immutable. 36 * 37 * @param list1 non-null; first instance 38 * @param list2 non-null; second instance 39 * @return non-null; combined instance 40 */ 41 public static LocalVariableList concat(LocalVariableList list1, 42 LocalVariableList list2) { 43 if (list1 == EMPTY) { 44 // easy case 45 return list2; 46 } 47 48 int sz1 = list1.size(); 49 int sz2 = list2.size(); 50 LocalVariableList result = new LocalVariableList(sz1 + sz2); 51 52 for (int i = 0; i < sz1; i++) { 53 result.set(i, list1.get(i)); 54 } 55 56 for (int i = 0; i < sz2; i++) { 57 result.set(sz1 + i, list2.get(i)); 58 } 59 60 result.setImmutable(); 61 return result; 62 } 63 64 /** 65 * Returns an instance which is the result of merging the two 66 * given instances, where one instance should have only type 67 * descriptors and the other only type signatures. The merged 68 * result is identical to the one with descriptors, except that 69 * any element whose {name, index, start, length} matches an 70 * element in the signature list gets augmented with the 71 * corresponding signature. The result is immutable. 72 * 73 * @param descriptorList non-null; list with descriptors 74 * @param signatureList non-null; list with signatures 75 * @return non-null; the merged result 76 */ 77 public static LocalVariableList mergeDescriptorsAndSignatures( 78 LocalVariableList descriptorList, 79 LocalVariableList signatureList) { 80 int signatureSize = signatureList.size(); 81 int descriptorSize = descriptorList.size(); 82 LocalVariableList result = new LocalVariableList(descriptorSize); 83 84 for (int i = 0; i < descriptorSize; i++) { 85 Item item = descriptorList.get(i); 86 Item signatureItem = signatureList.itemToLocal(item); 87 if (signatureItem != null) { 88 CstUtf8 signature = signatureItem.getSignature(); 89 item = item.withSignature(signature); 90 } 91 result.set(i, item); 92 } 93 94 result.setImmutable(); 95 return result; 96 } 97 98 /** 99 * Constructs an instance. 100 * 101 * @param count the number of elements to be in the list 102 */ 103 public LocalVariableList(int count) { 104 super(count); 105 } 106 107 /** 108 * Gets the indicated item. 109 * 110 * @param n >= 0; which item 111 * @return null-ok; the indicated item 112 */ 113 public Item get(int n) { 114 return (Item) get0(n); 115 } 116 117 /** 118 * Sets the item at the given index. 119 * 120 * @param n >= 0, < size(); which element 121 * @param item non-null; the item 122 */ 123 public void set(int n, Item item) { 124 if (item == null) { 125 throw new NullPointerException("item == null"); 126 } 127 128 set0(n, item); 129 } 130 131 /** 132 * Sets the item at the given index. 133 * 134 * <p><b>Note:</b> At least one of <code>descriptor</code> or 135 * <code>signature</code> must be passed as non-null.</p> 136 * 137 * @param n >= 0, < size(); which element 138 * @param startPc >= 0; the start pc of this variable's scope 139 * @param length >= 0; the length (in bytecodes) of this variable's 140 * scope 141 * @param name non-null; the variable's name 142 * @param descriptor null-ok; the variable's type descriptor 143 * @param signature null-ok; the variable's type signature 144 * @param index >= 0; the variable's local index 145 */ 146 public void set(int n, int startPc, int length, CstUtf8 name, 147 CstUtf8 descriptor, CstUtf8 signature, int index) { 148 set0(n, new Item(startPc, length, name, descriptor, signature, index)); 149 } 150 151 /** 152 * Gets the local variable information in this instance which matches 153 * the given {@link com.android.dx.cf.code.LocalVariableList.Item} 154 * in all respects but the type descriptor and signature, if any. 155 * 156 * @param item non-null; local variable information to match 157 * @return null-ok; the corresponding local variable information stored 158 * in this instance, or <code>null</code> if there is no matching 159 * information 160 */ 161 public Item itemToLocal(Item item) { 162 int sz = size(); 163 164 for (int i = 0; i < sz; i++) { 165 Item one = (Item) get0(i); 166 167 if ((one != null) && one.matchesAllButType(item)) { 168 return one; 169 } 170 } 171 172 return null; 173 } 174 175 /** 176 * Gets the local variable information associated with a given address 177 * and local index, if any. <b>Note:</b> In standard classfiles, a 178 * variable's start point is listed as the address of the instruction 179 * <i>just past</i> the one that sets the variable. 180 * 181 * @param pc >= 0; the address to look up 182 * @param index >= 0; the local variable index 183 * @return null-ok; the associated local variable information, or 184 * <code>null</code> if none is known 185 */ 186 public Item pcAndIndexToLocal(int pc, int index) { 187 int sz = size(); 188 189 for (int i = 0; i < sz; i++) { 190 Item one = (Item) get0(i); 191 192 if ((one != null) && one.matchesPcAndIndex(pc, index)) { 193 return one; 194 } 195 } 196 197 return null; 198 } 199 200 /** 201 * Item in a local variable table. 202 */ 203 public static class Item { 204 /** >= 0; the start pc of this variable's scope */ 205 private final int startPc; 206 207 /** >= 0; the length (in bytecodes) of this variable's scope */ 208 private final int length; 209 210 /** non-null; the variable's name */ 211 private final CstUtf8 name; 212 213 /** null-ok; the variable's type descriptor */ 214 private final CstUtf8 descriptor; 215 216 /** null-ok; the variable's type signature */ 217 private final CstUtf8 signature; 218 219 /** >= 0; the variable's local index */ 220 private final int index; 221 222 /** 223 * Constructs an instance. 224 * 225 * <p><b>Note:</b> At least one of <code>descriptor</code> or 226 * <code>signature</code> must be passed as non-null.</p> 227 * 228 * @param startPc >= 0; the start pc of this variable's scope 229 * @param length >= 0; the length (in bytecodes) of this variable's 230 * scope 231 * @param name non-null; the variable's name 232 * @param descriptor null-ok; the variable's type descriptor 233 * @param signature null-ok; the variable's type signature 234 * @param index >= 0; the variable's local index 235 */ 236 public Item(int startPc, int length, CstUtf8 name, 237 CstUtf8 descriptor, CstUtf8 signature, int index) { 238 if (startPc < 0) { 239 throw new IllegalArgumentException("startPc < 0"); 240 } 241 242 if (length < 0) { 243 throw new IllegalArgumentException("length < 0"); 244 } 245 246 if (name == null) { 247 throw new NullPointerException("name == null"); 248 } 249 250 if ((descriptor == null) && (signature == null)) { 251 throw new NullPointerException( 252 "(descriptor == null) && (signature == null)"); 253 } 254 255 if (index < 0) { 256 throw new IllegalArgumentException("index < 0"); 257 } 258 259 this.startPc = startPc; 260 this.length = length; 261 this.name = name; 262 this.descriptor = descriptor; 263 this.signature = signature; 264 this.index = index; 265 } 266 267 /** 268 * Gets the start pc of this variable's scope. 269 * 270 * @return >= 0; the start pc of this variable's scope 271 */ 272 public int getStartPc() { 273 return startPc; 274 } 275 276 /** 277 * Gets the length (in bytecodes) of this variable's scope. 278 * 279 * @return >= 0; the length (in bytecodes) of this variable's scope 280 */ 281 public int getLength() { 282 return length; 283 } 284 285 /** 286 * Gets the variable's type descriptor. 287 * 288 * @return null-ok; the variable's type descriptor 289 */ 290 public CstUtf8 getDescriptor() { 291 return descriptor; 292 } 293 294 /** 295 * Gets the variable's LocalItem, a (name, signature) tuple 296 * 297 * @return null-ok; the variable's type descriptor 298 */ 299 public LocalItem getLocalItem() { 300 return LocalItem.make(name, signature); 301 } 302 303 /** 304 * Gets the variable's type signature. Private because if you need this, 305 * you want getLocalItem() instead. 306 * 307 * @return null-ok; the variable's type signature 308 */ 309 private CstUtf8 getSignature() { 310 return signature; 311 } 312 313 /** 314 * Gets the variable's local index. 315 * 316 * @return >= 0; the variable's local index 317 */ 318 public int getIndex() { 319 return index; 320 } 321 322 /** 323 * Gets the variable's type descriptor. This is a convenient shorthand 324 * for <code>Type.intern(getDescriptor().getString())</code>. 325 * 326 * @return non-null; the variable's type 327 */ 328 public Type getType() { 329 return Type.intern(descriptor.getString()); 330 } 331 332 /** 333 * Constructs and returns an instance which is identical to this 334 * one, except that the signature is changed to the given value. 335 * 336 * @param newSignature non-null; the new signature 337 * @return non-null; an appropriately-constructed instance 338 */ 339 public Item withSignature(CstUtf8 newSignature) { 340 return new Item(startPc, length, name, descriptor, newSignature, 341 index); 342 } 343 344 /** 345 * Gets whether this instance matches (describes) the given 346 * address and index. 347 * 348 * @param pc >= 0; the address in question 349 * @param index >= 0; the local variable index in question 350 * @return <code>true</code> iff this instance matches <code>pc</code> 351 * and <code>index</code> 352 */ 353 public boolean matchesPcAndIndex(int pc, int index) { 354 return (index == this.index) && 355 (pc >= startPc) && 356 (pc < (startPc + length)); 357 } 358 359 /** 360 * Gets whether this instance matches (describes) the given 361 * other instance exactly in all fields except type descriptor and 362 * type signature. 363 * 364 * @param other non-null; the instance to compare to 365 * @return <code>true</code> iff this instance matches 366 */ 367 public boolean matchesAllButType(Item other) { 368 return (startPc == other.startPc) 369 && (length == other.length) 370 && (index == other.index) 371 && name.equals(other.name); 372 } 373 } 374} 375