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 17 package com.android.dx.cf.code; 18 19 import com.android.dx.rop.cst.CstUtf8; 20 import com.android.dx.rop.type.Type; 21 import com.android.dx.rop.code.LocalItem; 22 import com.android.dx.util.FixedSizeList; 23 24 /** 25 * List of "local variable" entries, which are the contents of 26 * {@code LocalVariableTable} and {@code LocalVariableTypeTable} 27 * attributes, as well as combinations of the two. 28 */ 29 public final class LocalVariableList extends FixedSizeList { 30 /** {@code 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 {@code non-null;} first instance 38 * @param list2 {@code non-null;} second instance 39 * @return {@code 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 {@code non-null;} list with descriptors 74 * @param signatureList {@code non-null;} list with signatures 75 * @return {@code non-null;} the merged result 76 */ 77 public static LocalVariableList mergeDescriptorsAndSignatures( 78 LocalVariableList descriptorList, 79 LocalVariableList signatureList) { 80 int descriptorSize = descriptorList.size(); 81 LocalVariableList result = new LocalVariableList(descriptorSize); 82 83 for (int i = 0; i < descriptorSize; i++) { 84 Item item = descriptorList.get(i); 85 Item signatureItem = signatureList.itemToLocal(item); 86 if (signatureItem != null) { 87 CstUtf8 signature = signatureItem.getSignature(); 88 item = item.withSignature(signature); 89 } 90 result.set(i, item); 91 } 92 93 result.setImmutable(); 94 return result; 95 } 96 97 /** 98 * Constructs an instance. 99 * 100 * @param count the number of elements to be in the list 101 */ 102 public LocalVariableList(int count) { 103 super(count); 104 } 105 106 /** 107 * Gets the indicated item. 108 * 109 * @param n {@code >= 0;} which item 110 * @return {@code null-ok;} the indicated item 111 */ 112 public Item get(int n) { 113 return (Item) get0(n); 114 } 115 116 /** 117 * Sets the item at the given index. 118 * 119 * @param n {@code >= 0, < size();} which element 120 * @param item {@code non-null;} the item 121 */ 122 public void set(int n, Item item) { 123 if (item == null) { 124 throw new NullPointerException("item == null"); 125 } 126 127 set0(n, item); 128 } 129 130 /** 131 * Sets the item at the given index. 132 * 133 * <p><b>Note:</b> At least one of {@code descriptor} or 134 * {@code signature} must be passed as non-null.</p> 135 * 136 * @param n {@code >= 0, < size();} which element 137 * @param startPc {@code >= 0;} the start pc of this variable's scope 138 * @param length {@code >= 0;} the length (in bytecodes) of this variable's 139 * scope 140 * @param name {@code non-null;} the variable's name 141 * @param descriptor {@code null-ok;} the variable's type descriptor 142 * @param signature {@code null-ok;} the variable's type signature 143 * @param index {@code >= 0;} the variable's local index 144 */ 145 public void set(int n, int startPc, int length, CstUtf8 name, 146 CstUtf8 descriptor, CstUtf8 signature, int index) { 147 set0(n, new Item(startPc, length, name, descriptor, signature, index)); 148 } 149 150 /** 151 * Gets the local variable information in this instance which matches 152 * the given {@link com.android.dx.cf.code.LocalVariableList.Item} 153 * in all respects but the type descriptor and signature, if any. 154 * 155 * @param item {@code non-null;} local variable information to match 156 * @return {@code null-ok;} the corresponding local variable information stored 157 * in this instance, or {@code null} if there is no matching 158 * information 159 */ 160 public Item itemToLocal(Item item) { 161 int sz = size(); 162 163 for (int i = 0; i < sz; i++) { 164 Item one = (Item) get0(i); 165 166 if ((one != null) && one.matchesAllButType(item)) { 167 return one; 168 } 169 } 170 171 return null; 172 } 173 174 /** 175 * Gets the local variable information associated with a given address 176 * and local index, if any. <b>Note:</b> In standard classfiles, a 177 * variable's start point is listed as the address of the instruction 178 * <i>just past</i> the one that sets the variable. 179 * 180 * @param pc {@code >= 0;} the address to look up 181 * @param index {@code >= 0;} the local variable index 182 * @return {@code null-ok;} the associated local variable information, or 183 * {@code null} if none is known 184 */ 185 public Item pcAndIndexToLocal(int pc, int index) { 186 int sz = size(); 187 188 for (int i = 0; i < sz; i++) { 189 Item one = (Item) get0(i); 190 191 if ((one != null) && one.matchesPcAndIndex(pc, index)) { 192 return one; 193 } 194 } 195 196 return null; 197 } 198 199 /** 200 * Item in a local variable table. 201 */ 202 public static class Item { 203 /** {@code >= 0;} the start pc of this variable's scope */ 204 private final int startPc; 205 206 /** {@code >= 0;} the length (in bytecodes) of this variable's scope */ 207 private final int length; 208 209 /** {@code non-null;} the variable's name */ 210 private final CstUtf8 name; 211 212 /** {@code null-ok;} the variable's type descriptor */ 213 private final CstUtf8 descriptor; 214 215 /** {@code null-ok;} the variable's type signature */ 216 private final CstUtf8 signature; 217 218 /** {@code >= 0;} the variable's local index */ 219 private final int index; 220 221 /** 222 * Constructs an instance. 223 * 224 * <p><b>Note:</b> At least one of {@code descriptor} or 225 * {@code signature} must be passed as non-null.</p> 226 * 227 * @param startPc {@code >= 0;} the start pc of this variable's scope 228 * @param length {@code >= 0;} the length (in bytecodes) of this variable's 229 * scope 230 * @param name {@code non-null;} the variable's name 231 * @param descriptor {@code null-ok;} the variable's type descriptor 232 * @param signature {@code null-ok;} the variable's type signature 233 * @param index {@code >= 0;} the variable's local index 234 */ 235 public Item(int startPc, int length, CstUtf8 name, 236 CstUtf8 descriptor, CstUtf8 signature, int index) { 237 if (startPc < 0) { 238 throw new IllegalArgumentException("startPc < 0"); 239 } 240 241 if (length < 0) { 242 throw new IllegalArgumentException("length < 0"); 243 } 244 245 if (name == null) { 246 throw new NullPointerException("name == null"); 247 } 248 249 if ((descriptor == null) && (signature == null)) { 250 throw new NullPointerException( 251 "(descriptor == null) && (signature == null)"); 252 } 253 254 if (index < 0) { 255 throw new IllegalArgumentException("index < 0"); 256 } 257 258 this.startPc = startPc; 259 this.length = length; 260 this.name = name; 261 this.descriptor = descriptor; 262 this.signature = signature; 263 this.index = index; 264 } 265 266 /** 267 * Gets the start pc of this variable's scope. 268 * 269 * @return {@code >= 0;} the start pc of this variable's scope 270 */ 271 public int getStartPc() { 272 return startPc; 273 } 274 275 /** 276 * Gets the length (in bytecodes) of this variable's scope. 277 * 278 * @return {@code >= 0;} the length (in bytecodes) of this variable's scope 279 */ 280 public int getLength() { 281 return length; 282 } 283 284 /** 285 * Gets the variable's type descriptor. 286 * 287 * @return {@code null-ok;} the variable's type descriptor 288 */ 289 public CstUtf8 getDescriptor() { 290 return descriptor; 291 } 292 293 /** 294 * Gets the variable's LocalItem, a (name, signature) tuple 295 * 296 * @return {@code null-ok;} the variable's type descriptor 297 */ 298 public LocalItem getLocalItem() { 299 return LocalItem.make(name, signature); 300 } 301 302 /** 303 * Gets the variable's type signature. Private because if you need this, 304 * you want getLocalItem() instead. 305 * 306 * @return {@code null-ok;} the variable's type signature 307 */ 308 private CstUtf8 getSignature() { 309 return signature; 310 } 311 312 /** 313 * Gets the variable's local index. 314 * 315 * @return {@code >= 0;} the variable's local index 316 */ 317 public int getIndex() { 318 return index; 319 } 320 321 /** 322 * Gets the variable's type descriptor. This is a convenient shorthand 323 * for {@code Type.intern(getDescriptor().getString())}. 324 * 325 * @return {@code non-null;} the variable's type 326 */ 327 public Type getType() { 328 return Type.intern(descriptor.getString()); 329 } 330 331 /** 332 * Constructs and returns an instance which is identical to this 333 * one, except that the signature is changed to the given value. 334 * 335 * @param newSignature {@code non-null;} the new signature 336 * @return {@code non-null;} an appropriately-constructed instance 337 */ 338 public Item withSignature(CstUtf8 newSignature) { 339 return new Item(startPc, length, name, descriptor, newSignature, 340 index); 341 } 342 343 /** 344 * Gets whether this instance matches (describes) the given 345 * address and index. 346 * 347 * @param pc {@code >= 0;} the address in question 348 * @param index {@code >= 0;} the local variable index in question 349 * @return {@code true} iff this instance matches {@code pc} 350 * and {@code index} 351 */ 352 public boolean matchesPcAndIndex(int pc, int index) { 353 return (index == this.index) && 354 (pc >= startPc) && 355 (pc < (startPc + length)); 356 } 357 358 /** 359 * Gets whether this instance matches (describes) the given 360 * other instance exactly in all fields except type descriptor and 361 * type signature. 362 * 363 * @param other {@code non-null;} the instance to compare to 364 * @return {@code true} iff this instance matches 365 */ 366 public boolean matchesAllButType(Item other) { 367 return (startPc == other.startPc) 368 && (length == other.length) 369 && (index == other.index) 370 && name.equals(other.name); 371 } 372 } 373 } 374