1 /* 2 * Copyright 2013, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.dexlib2.writer.util; 33 34 import com.google.common.collect.Lists; 35 import org.jf.dexlib2.base.BaseTryBlock; 36 import org.jf.dexlib2.iface.ExceptionHandler; 37 import org.jf.dexlib2.iface.TryBlock; 38 import org.jf.util.ExceptionWithContext; 39 40 import javax.annotation.Nonnull; 41 import javax.annotation.Nullable; 42 import java.util.Iterator; 43 import java.util.List; 44 import java.util.NoSuchElementException; 45 46 public class TryListBuilder<EH extends ExceptionHandler> 47 { 48 // Linked list sentinels that don't represent an actual try block 49 // Their values are never modified, only their links 50 private final MutableTryBlock<EH> listStart; 51 private final MutableTryBlock<EH> listEnd; 52 TryListBuilder()53 public TryListBuilder() { 54 listStart = new MutableTryBlock<EH>(0, 0); 55 listEnd = new MutableTryBlock<EH>(0, 0); 56 listStart.next = listEnd; 57 listEnd.prev = listStart; 58 } 59 massageTryBlocks( List<? extends TryBlock<? extends EH>> tryBlocks)60 public static <EH extends ExceptionHandler> List<TryBlock<EH>> massageTryBlocks( 61 List<? extends TryBlock<? extends EH>> tryBlocks) { 62 TryListBuilder<EH> tlb = new TryListBuilder<EH>(); 63 64 for (TryBlock<? extends EH> tryBlock: tryBlocks) { 65 int startAddress = tryBlock.getStartCodeAddress(); 66 int endAddress = startAddress + tryBlock.getCodeUnitCount(); 67 68 for (EH exceptionHandler: tryBlock.getExceptionHandlers()) { 69 tlb.addHandler(startAddress, endAddress, exceptionHandler); 70 } 71 } 72 return tlb.getTryBlocks(); 73 } 74 75 private static class TryBounds<EH extends ExceptionHandler> { 76 @Nonnull public final MutableTryBlock<EH> start; 77 @Nonnull public final MutableTryBlock<EH> end; 78 TryBounds(@onnull MutableTryBlock<EH> start, @Nonnull MutableTryBlock<EH> end)79 public TryBounds(@Nonnull MutableTryBlock<EH> start, @Nonnull MutableTryBlock<EH> end) { 80 this.start = start; 81 this.end = end; 82 } 83 } 84 85 public static class InvalidTryException extends ExceptionWithContext { InvalidTryException(Throwable cause)86 public InvalidTryException(Throwable cause) { 87 super(cause); 88 } 89 InvalidTryException(Throwable cause, String message, Object... formatArgs)90 public InvalidTryException(Throwable cause, String message, Object... formatArgs) { 91 super(cause, message, formatArgs); 92 } 93 InvalidTryException(String message, Object... formatArgs)94 public InvalidTryException(String message, Object... formatArgs) { 95 super(message, formatArgs); 96 } 97 } 98 99 private static class MutableTryBlock<EH extends ExceptionHandler> extends BaseTryBlock<EH> { 100 public MutableTryBlock<EH> prev = null; 101 public MutableTryBlock<EH> next = null; 102 103 public int startCodeAddress; 104 public int endCodeAddress; 105 @Nonnull public List<EH> exceptionHandlers = Lists.newArrayList(); 106 MutableTryBlock(int startCodeAddress, int endCodeAddress)107 public MutableTryBlock(int startCodeAddress, int endCodeAddress) { 108 this.startCodeAddress = startCodeAddress; 109 this.endCodeAddress = endCodeAddress; 110 } 111 MutableTryBlock(int startCodeAddress, int endCodeAddress, @Nonnull List<EH> exceptionHandlers)112 public MutableTryBlock(int startCodeAddress, int endCodeAddress, 113 @Nonnull List<EH> exceptionHandlers) { 114 this.startCodeAddress = startCodeAddress; 115 this.endCodeAddress = endCodeAddress; 116 this.exceptionHandlers = Lists.newArrayList(exceptionHandlers); 117 } 118 getStartCodeAddress()119 @Override public int getStartCodeAddress() { 120 return startCodeAddress; 121 } 122 getCodeUnitCount()123 @Override public int getCodeUnitCount() { 124 return endCodeAddress - startCodeAddress; 125 } 126 getExceptionHandlers()127 @Nonnull @Override public List<EH> getExceptionHandlers() { 128 return exceptionHandlers; 129 } 130 131 @Nonnull split(int splitAddress)132 public MutableTryBlock<EH> split(int splitAddress) { 133 MutableTryBlock<EH> newTryBlock = new MutableTryBlock<EH>(splitAddress, endCodeAddress, exceptionHandlers); 134 endCodeAddress = splitAddress; 135 append(newTryBlock); 136 return newTryBlock; 137 } 138 delete()139 public void delete() { 140 next.prev = prev; 141 prev.next = next; 142 } 143 mergeNext()144 public void mergeNext() { 145 //assert next.startCodeAddress == this.endCodeAddress; 146 this.endCodeAddress = next.endCodeAddress; 147 next.delete(); 148 } 149 append(@onnull MutableTryBlock<EH> tryBlock)150 public void append(@Nonnull MutableTryBlock<EH> tryBlock) { 151 next.prev = tryBlock; 152 tryBlock.next = next; 153 tryBlock.prev = this; 154 next = tryBlock; 155 } 156 prepend(@onnull MutableTryBlock<EH> tryBlock)157 public void prepend(@Nonnull MutableTryBlock<EH> tryBlock) { 158 prev.next = tryBlock; 159 tryBlock.prev = prev; 160 tryBlock.next = this; 161 prev = tryBlock; 162 } 163 addHandler(@onnull EH handler)164 public void addHandler(@Nonnull EH handler) { 165 for (ExceptionHandler existingHandler: exceptionHandlers) { 166 String existingType = existingHandler.getExceptionType(); 167 String newType = handler.getExceptionType(); 168 169 if (existingType == null) { 170 if (newType == null) { 171 if (existingHandler.getHandlerCodeAddress() != handler.getHandlerCodeAddress()) { 172 throw new InvalidTryException( 173 "Multiple overlapping catch all handlers with different handlers"); 174 } 175 return; 176 } 177 } else if (existingType.equals(newType)) { 178 // dalvik doesn't reject cases when there are multiple catches with the same exception 179 // but different handlers. In practice, the first handler "wins". Since the later 180 // handler will never be used, we don't add it. 181 return; 182 } 183 } 184 185 exceptionHandlers.add(handler); 186 } 187 } 188 getBoundingRanges(int startAddress, int endAddress)189 private TryBounds<EH> getBoundingRanges(int startAddress, int endAddress) { 190 MutableTryBlock<EH> startBlock = null; 191 192 MutableTryBlock<EH> tryBlock = listStart.next; 193 while (tryBlock != listEnd) { 194 int currentStartAddress = tryBlock.startCodeAddress; 195 int currentEndAddress = tryBlock.endCodeAddress; 196 197 if (startAddress == currentStartAddress) { 198 //|-----| 199 //^------ 200 /*Bam. We hit the start of the range right on the head*/ 201 startBlock = tryBlock; 202 break; 203 } else if (startAddress > currentStartAddress && startAddress < currentEndAddress) { 204 //|-----| 205 // ^---- 206 /*Almost. The start of the range being added is in the middle 207 of an existing try range. We need to split the existing range 208 at the start address of the range being added*/ 209 startBlock = tryBlock.split(startAddress); 210 break; 211 }else if (startAddress < currentStartAddress) { 212 if (endAddress <= currentStartAddress) { 213 // |-----| 214 //^--^ 215 /*Oops, totally too far! The new range doesn't overlap any existing 216 ones, so we just add it and return*/ 217 startBlock = new MutableTryBlock<EH>(startAddress, endAddress); 218 tryBlock.prepend(startBlock); 219 return new TryBounds<EH>(startBlock, startBlock); 220 } else { 221 // |-----| 222 //^--------- 223 /*Oops, too far! We've passed the start of the range being added, but 224 the new range does overlap this one. We need to add a new range just 225 before this one*/ 226 startBlock = new MutableTryBlock<EH>(startAddress, currentStartAddress); 227 tryBlock.prepend(startBlock); 228 break; 229 } 230 } 231 232 tryBlock = tryBlock.next; 233 } 234 235 //|-----| 236 // ^----- 237 /*Either the list of tries is blank, or all the tries in the list 238 end before the range being added starts. In either case, we just need 239 to add a new range at the end of the list*/ 240 if (startBlock == null) { 241 startBlock = new MutableTryBlock<EH>(startAddress, endAddress); 242 listEnd.prepend(startBlock); 243 return new TryBounds<EH>(startBlock, startBlock); 244 } 245 246 tryBlock = startBlock; 247 while (tryBlock != listEnd) { 248 int currentStartAddress = tryBlock.startCodeAddress; 249 int currentEndAddress = tryBlock.endCodeAddress; 250 251 if (endAddress == currentEndAddress) { 252 //|-----| 253 //------^ 254 /*Bam! We hit the end right on the head... err, tail.*/ 255 return new TryBounds<EH>(startBlock, tryBlock); 256 } else if (endAddress > currentStartAddress && endAddress < currentEndAddress) { 257 //|-----| 258 //--^ 259 /*Almost. The range being added ends in the middle of an 260 existing range. We need to split the existing range 261 at the end of the range being added.*/ 262 tryBlock.split(endAddress); 263 return new TryBounds<EH>(startBlock, tryBlock); 264 } else if (endAddress <= currentStartAddress) { 265 //|-----| |-----| 266 //-----------^ 267 /*Oops, too far! The current range starts after the range being added 268 ends. We need to create a new range that starts at the end of the 269 previous range, and ends at the end of the range being added*/ 270 MutableTryBlock<EH> endBlock = new MutableTryBlock<EH>(tryBlock.prev.endCodeAddress, endAddress); 271 tryBlock.prepend(endBlock); 272 return new TryBounds<EH>(startBlock, endBlock); 273 } 274 tryBlock = tryBlock.next; 275 } 276 277 //|-----| 278 //--------^ 279 /*The last range in the list ended before the end of the range being added. 280 We need to add a new range that starts at the end of the last range in the 281 list, and ends at the end of the range being added.*/ 282 MutableTryBlock<EH> endBlock = new MutableTryBlock<EH>(listEnd.prev.endCodeAddress, endAddress); 283 listEnd.prepend(endBlock); 284 return new TryBounds<EH>(startBlock, endBlock); 285 } 286 addHandler(int startAddress, int endAddress, EH handler)287 public void addHandler(int startAddress, int endAddress, EH handler) { 288 TryBounds<EH> bounds = getBoundingRanges(startAddress, endAddress); 289 290 MutableTryBlock<EH> startBlock = bounds.start; 291 MutableTryBlock<EH> endBlock = bounds.end; 292 293 int previousEnd = startAddress; 294 MutableTryBlock<EH> tryBlock = startBlock; 295 296 /*Now we have the start and end ranges that exactly match the start and end 297 of the range being added. We need to iterate over all the ranges from the start 298 to end range inclusively, and append the handler to the end of each range's handler 299 list. We also need to create a new range for any "holes" in the existing ranges*/ 300 do 301 { 302 //is there a hole? If so, add a new range to fill the hole 303 if (tryBlock.startCodeAddress > previousEnd) { 304 MutableTryBlock<EH> newBlock = new MutableTryBlock<EH>(previousEnd, tryBlock.startCodeAddress); 305 tryBlock.prepend(newBlock); 306 tryBlock = newBlock; 307 } 308 309 tryBlock.addHandler(handler); 310 previousEnd = tryBlock.endCodeAddress; 311 tryBlock = tryBlock.next; 312 } while (tryBlock.prev != endBlock); 313 } 314 getTryBlocks()315 public List<TryBlock<EH>> getTryBlocks() { 316 return Lists.newArrayList(new Iterator<TryBlock<EH>>() { 317 // The next TryBlock to return. This has already been merged, if needed. 318 @Nullable private MutableTryBlock<EH> next; 319 320 { 321 next = listStart; 322 next = readNextItem(); 323 } 324 325 /** 326 * Read the item that comes after the current value of the next field. 327 * @return The next item, or null if there is no next item 328 */ 329 @Nullable protected MutableTryBlock<EH> readNextItem() { 330 // We can assume that next is not null, due to the way iteration happens 331 MutableTryBlock<EH> ret = next.next; 332 333 if (ret == listEnd) { 334 return null; 335 } 336 337 while (ret.next != listEnd) { 338 if (ret.endCodeAddress == ret.next.startCodeAddress && 339 ret.getExceptionHandlers().equals(ret.next.getExceptionHandlers())) { 340 ret.mergeNext(); 341 } else { 342 break; 343 } 344 } 345 return ret; 346 } 347 348 @Override public boolean hasNext() { 349 return next != null; 350 } 351 352 @Override @Nonnull public TryBlock<EH> next() { 353 if (!hasNext()) { 354 throw new NoSuchElementException(); 355 } 356 TryBlock<EH> ret = next; 357 next = readNextItem(); 358 // ret can't be null (ret=next and hasNext returned true) 359 return ret; 360 } 361 362 @Override public void remove() { 363 throw new UnsupportedOperationException(); 364 } 365 }); 366 } 367 } 368