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.dexgen.rop; 18 19 import com.android.dexgen.rop.cst.CstType; 20 import com.android.dexgen.rop.type.StdTypeList; 21 import com.android.dexgen.rop.type.TypeList; 22 import com.android.dexgen.util.FixedSizeList; 23 import com.android.dexgen.util.IntList; 24 25 /** 26 * List of catch entries, that is, the elements of an "exception table," 27 * which is part of a standard {@code Code} attribute. 28 */ 29 public final class ByteCatchList extends FixedSizeList { 30 /** {@code non-null;} convenient zero-entry instance */ 31 public static final ByteCatchList EMPTY = new ByteCatchList(0); 32 33 /** 34 * Constructs an instance. 35 * 36 * @param count the number of elements to be in the table 37 */ ByteCatchList(int count)38 public ByteCatchList(int count) { 39 super(count); 40 } 41 42 /** 43 * Gets the total length of this structure in bytes, when included in 44 * a {@code Code} attribute. The returned value includes the 45 * two bytes for {@code exception_table_length}. 46 * 47 * @return {@code >= 2;} the total length, in bytes 48 */ byteLength()49 public int byteLength() { 50 return 2 + size() * 8; 51 } 52 53 /** 54 * Gets the indicated item. 55 * 56 * @param n {@code >= 0;} which item 57 * @return {@code null-ok;} the indicated item 58 */ get(int n)59 public Item get(int n) { 60 return (Item) get0(n); 61 } 62 63 /** 64 * Sets the item at the given index. 65 * 66 * @param n {@code >= 0, < size();} which entry to set 67 * @param item {@code non-null;} the item 68 */ set(int n, Item item)69 public void set(int n, Item item) { 70 if (item == null) { 71 throw new NullPointerException("item == null"); 72 } 73 74 set0(n, item); 75 } 76 77 /** 78 * Sets the item at the given index. 79 * 80 * @param n {@code >= 0, < size();} which entry to set 81 * @param startPc {@code >= 0;} the start pc (inclusive) of the handler's range 82 * @param endPc {@code >= startPc;} the end pc (exclusive) of the 83 * handler's range 84 * @param handlerPc {@code >= 0;} the pc of the exception handler 85 * @param exceptionClass {@code null-ok;} the exception class or 86 * {@code null} to catch all exceptions with this handler 87 */ set(int n, int startPc, int endPc, int handlerPc, CstType exceptionClass)88 public void set(int n, int startPc, int endPc, int handlerPc, 89 CstType exceptionClass) { 90 set0(n, new Item(startPc, endPc, handlerPc, exceptionClass)); 91 } 92 93 /** 94 * Gets the list of items active at the given address. The result is 95 * automatically made immutable. 96 * 97 * @param pc which address 98 * @return {@code non-null;} list of exception handlers active at 99 * {@code pc} 100 */ listFor(int pc)101 public ByteCatchList listFor(int pc) { 102 int sz = size(); 103 Item[] resultArr = new Item[sz]; 104 int resultSz = 0; 105 106 for (int i = 0; i < sz; i++) { 107 Item one = get(i); 108 if (one.covers(pc) && typeNotFound(one, resultArr, resultSz)) { 109 resultArr[resultSz] = one; 110 resultSz++; 111 } 112 } 113 114 if (resultSz == 0) { 115 return EMPTY; 116 } 117 118 ByteCatchList result = new ByteCatchList(resultSz); 119 for (int i = 0; i < resultSz; i++) { 120 result.set(i, resultArr[i]); 121 } 122 123 result.setImmutable(); 124 return result; 125 } 126 127 /** 128 * Helper method for {@link #listFor}, which tells whether a match 129 * is <i>not</i> found for the exception type of the given item in 130 * the given array. A match is considered to be either an exact type 131 * match or the class {@code Object} which represents a catch-all. 132 * 133 * @param item {@code non-null;} item with the exception type to look for 134 * @param arr {@code non-null;} array to search in 135 * @param count {@code non-null;} maximum number of elements in the array to check 136 * @return {@code true} iff the exception type is <i>not</i> found 137 */ typeNotFound(Item item, Item[] arr, int count)138 private static boolean typeNotFound(Item item, Item[] arr, int count) { 139 CstType type = item.getExceptionClass(); 140 141 for (int i = 0; i < count; i++) { 142 CstType one = arr[i].getExceptionClass(); 143 if ((one == type) || (one == CstType.OBJECT)) { 144 return false; 145 } 146 } 147 148 return true; 149 } 150 151 /** 152 * Returns a target list corresponding to this instance. The result 153 * is a list of all the exception handler addresses, with the given 154 * {@code noException} address appended if appropriate. The 155 * result is automatically made immutable. 156 * 157 * @param noException {@code >= -1;} the no-exception address to append, or 158 * {@code -1} not to append anything 159 * @return {@code non-null;} list of exception targets, with 160 * {@code noException} appended if necessary 161 */ toTargetList(int noException)162 public IntList toTargetList(int noException) { 163 if (noException < -1) { 164 throw new IllegalArgumentException("noException < -1"); 165 } 166 167 boolean hasDefault = (noException >= 0); 168 int sz = size(); 169 170 if (sz == 0) { 171 if (hasDefault) { 172 /* 173 * The list is empty, but there is a no-exception 174 * address; so, the result is just that address. 175 */ 176 return IntList.makeImmutable(noException); 177 } 178 /* 179 * The list is empty and there isn't even a no-exception 180 * address. 181 */ 182 return IntList.EMPTY; 183 } 184 185 IntList result = new IntList(sz + (hasDefault ? 1 : 0)); 186 187 for (int i = 0; i < sz; i++) { 188 result.add(get(i).getHandlerPc()); 189 } 190 191 if (hasDefault) { 192 result.add(noException); 193 } 194 195 result.setImmutable(); 196 return result; 197 } 198 199 /** 200 * Returns a rop-style catches list equivalent to this one. 201 * 202 * @return {@code non-null;} the converted instance 203 */ toRopCatchList()204 public TypeList toRopCatchList() { 205 int sz = size(); 206 if (sz == 0) { 207 return StdTypeList.EMPTY; 208 } 209 210 StdTypeList result = new StdTypeList(sz); 211 212 for (int i = 0; i < sz; i++) { 213 result.set(i, get(i).getExceptionClass().getClassType()); 214 } 215 216 result.setImmutable(); 217 return result; 218 } 219 220 /** 221 * Item in an exception handler list. 222 */ 223 public static class Item { 224 /** {@code >= 0;} the start pc (inclusive) of the handler's range */ 225 private final int startPc; 226 227 /** {@code >= startPc;} the end pc (exclusive) of the handler's range */ 228 private final int endPc; 229 230 /** {@code >= 0;} the pc of the exception handler */ 231 private final int handlerPc; 232 233 /** {@code null-ok;} the exception class or {@code null} to catch all 234 * exceptions with this handler */ 235 private final CstType exceptionClass; 236 237 /** 238 * Constructs an instance. 239 * 240 * @param startPc {@code >= 0;} the start pc (inclusive) of the 241 * handler's range 242 * @param endPc {@code >= startPc;} the end pc (exclusive) of the 243 * handler's range 244 * @param handlerPc {@code >= 0;} the pc of the exception handler 245 * @param exceptionClass {@code null-ok;} the exception class or 246 * {@code null} to catch all exceptions with this handler 247 */ Item(int startPc, int endPc, int handlerPc, CstType exceptionClass)248 public Item(int startPc, int endPc, int handlerPc, 249 CstType exceptionClass) { 250 if (startPc < 0) { 251 throw new IllegalArgumentException("startPc < 0"); 252 } 253 254 if (endPc < startPc) { 255 throw new IllegalArgumentException("endPc < startPc"); 256 } 257 258 if (handlerPc < 0) { 259 throw new IllegalArgumentException("handlerPc < 0"); 260 } 261 262 this.startPc = startPc; 263 this.endPc = endPc; 264 this.handlerPc = handlerPc; 265 this.exceptionClass = exceptionClass; 266 } 267 268 /** 269 * Gets the start pc (inclusive) of the handler's range. 270 * 271 * @return {@code >= 0;} the start pc (inclusive) of the handler's range. 272 */ getStartPc()273 public int getStartPc() { 274 return startPc; 275 } 276 277 /** 278 * Gets the end pc (exclusive) of the handler's range. 279 * 280 * @return {@code >= startPc;} the end pc (exclusive) of the 281 * handler's range. 282 */ getEndPc()283 public int getEndPc() { 284 return endPc; 285 } 286 287 /** 288 * Gets the pc of the exception handler. 289 * 290 * @return {@code >= 0;} the pc of the exception handler 291 */ getHandlerPc()292 public int getHandlerPc() { 293 return handlerPc; 294 } 295 296 /** 297 * Gets the class of exception handled. 298 * 299 * @return {@code non-null;} the exception class; {@link CstType#OBJECT} 300 * if this entry handles all possible exceptions 301 */ getExceptionClass()302 public CstType getExceptionClass() { 303 return (exceptionClass != null) ? 304 exceptionClass : CstType.OBJECT; 305 } 306 307 /** 308 * Returns whether the given address is in the range of this item. 309 * 310 * @param pc the address 311 * @return {@code true} iff this item covers {@code pc} 312 */ covers(int pc)313 public boolean covers(int pc) { 314 return (pc >= startPc) && (pc < endPc); 315 } 316 } 317 } 318