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.code.LocalItem; 20 import com.android.dx.rop.cst.CstString; 21 import com.android.dx.rop.type.Type; 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 */ concat(LocalVariableList list1, LocalVariableList list2)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 */ mergeDescriptorsAndSignatures( LocalVariableList descriptorList, LocalVariableList signatureList)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 CstString 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 */ LocalVariableList(int count)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 */ get(int n)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 */ set(int n, Item item)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 */ set(int n, int startPc, int length, CstString name, CstString descriptor, CstString signature, int index)145 public void set(int n, int startPc, int length, CstString name, 146 CstString descriptor, CstString 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 */ itemToLocal(Item item)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 */ pcAndIndexToLocal(int pc, int index)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 CstString name; 211 212 /** {@code null-ok;} the variable's type descriptor */ 213 private final CstString descriptor; 214 215 /** {@code null-ok;} the variable's type signature */ 216 private final CstString 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 */ Item(int startPc, int length, CstString name, CstString descriptor, CstString signature, int index)235 public Item(int startPc, int length, CstString name, 236 CstString descriptor, CstString 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 */ getStartPc()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 */ getLength()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 */ getDescriptor()289 public CstString 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 */ getLocalItem()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 */ getSignature()308 private CstString 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 */ getIndex()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 */ getType()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 */ withSignature(CstString newSignature)338 public Item withSignature(CstString 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 */ matchesPcAndIndex(int pc, int index)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 */ matchesAllButType(Item other)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