MethodDefinition.java revision 5f98a2926093cd9a6c2ea64848c47fc5e39e018d
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2009 Ben Gruver 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29package org.jf.baksmali.Adaptors; 30 31import org.jf.baksmali.Adaptors.Format.*; 32import org.jf.dexlib.*; 33import org.jf.dexlib.Debug.DebugInstructionIterator; 34import org.jf.dexlib.Code.Format.*; 35import org.jf.dexlib.Code.Instruction; 36import org.jf.dexlib.Code.Opcode; 37import org.jf.dexlib.Code.InstructionIterator; 38import org.jf.dexlib.Util.AccessFlags; 39import org.antlr.stringtemplate.StringTemplateGroup; 40import org.antlr.stringtemplate.StringTemplate; 41 42import java.util.*; 43 44public class MethodDefinition { 45 public static StringTemplate makeTemplate(StringTemplateGroup stg, ClassDataItem.EncodedMethod encodedMethod, 46 AnnotationSetItem annotationSet, 47 AnnotationSetRefList parameterAnnotations) { 48 49 CodeItem codeItem = encodedMethod.codeItem; 50 51 StringTemplate template = stg.getInstanceOf("method"); 52 53 template.setAttribute("AccessFlags", getAccessFlags(encodedMethod)); 54 template.setAttribute("MethodName", encodedMethod.method.getMethodName().getStringValue()); 55 template.setAttribute("Prototype", encodedMethod.method.getPrototype().getPrototypeString()); 56 template.setAttribute("HasCode", codeItem != null); 57 template.setAttribute("RegisterCount", codeItem==null?"0":Integer.toString(codeItem.getRegisterCount())); 58 template.setAttribute("Parameters", getParameters(stg, codeItem, parameterAnnotations)); 59 template.setAttribute("Annotations", getAnnotations(stg, annotationSet)); 60 template.setAttribute("MethodItems", getMethodItems(encodedMethod.method.getDexFile(), stg, codeItem)); 61 62 return template; 63 } 64 65 private static List<String> getAccessFlags(ClassDataItem.EncodedMethod encodedMethod) { 66 List<String> accessFlags = new ArrayList<String>(); 67 68 for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) { 69 accessFlags.add(accessFlag.toString()); 70 } 71 72 return accessFlags; 73 } 74 75 private static List<StringTemplate> getParameters(StringTemplateGroup stg, CodeItem codeItem, 76 AnnotationSetRefList parameterAnnotations) { 77 DebugInfoItem debugInfoItem = null; 78 if (codeItem != null) { 79 debugInfoItem = codeItem.getDebugInfo(); 80 } 81 82 int parameterCount = 0; 83 84 List<AnnotationSetItem> annotations = new ArrayList<AnnotationSetItem>(); 85 if (parameterAnnotations != null) { 86 AnnotationSetItem[] _annotations = parameterAnnotations.getAnnotationSets(); 87 if (_annotations != null) { 88 annotations.addAll(Arrays.asList(_annotations)); 89 } 90 91 parameterCount = annotations.size(); 92 } 93 94 List<String> parameterNames = new ArrayList<String>(); 95 if (debugInfoItem != null) { 96 StringIdItem[] _parameterNames = debugInfoItem.getParameterNames(); 97 if (_parameterNames != null) { 98 for (StringIdItem parameterName: _parameterNames) { 99 parameterNames.add(parameterName==null?null:parameterName.getStringValue()); 100 } 101 } 102 103 if (parameterCount < parameterNames.size()) { 104 parameterCount = parameterNames.size(); 105 } 106 } 107 108 List<StringTemplate> parameters = new ArrayList<StringTemplate>(); 109 for (int i=0; i<parameterCount; i++) { 110 AnnotationSetItem annotationSet = null; 111 if (i < annotations.size()) { 112 annotationSet = annotations.get(i); 113 } 114 115 String parameterName = null; 116 if (i < parameterNames.size()) { 117 parameterName = parameterNames.get(i); 118 } 119 120 parameters.add(ParameterAdaptor.makeTemplate(stg, parameterName, annotationSet)); 121 } 122 123 return parameters; 124 } 125 126 private static List<StringTemplate> getAnnotations(StringTemplateGroup stg, AnnotationSetItem annotationSet) { 127 if (annotationSet == null) { 128 return null; 129 } 130 131 List<StringTemplate> annotationAdaptors = new ArrayList<StringTemplate>(); 132 133 for (AnnotationItem annotationItem: annotationSet.getAnnotations()) { 134 annotationAdaptors.add(AnnotationAdaptor.makeTemplate(stg, annotationItem)); 135 } 136 return annotationAdaptors; 137 } 138 139 private static List<MethodItem> getMethodItems(DexFile dexFile, StringTemplateGroup stg, CodeItem codeItem) { 140 List<MethodItem> methodItems = new ArrayList<MethodItem>(); 141 142 MethodItemList methodItemList = new MethodItemList(dexFile, stg, codeItem); 143 methodItemList.generateMethodItemList(); 144 145 methodItems.addAll(methodItemList.labels); 146 methodItems.addAll(methodItemList.instructions); 147 methodItems.addAll(methodItemList.blanks); 148 methodItems.addAll(methodItemList.catches); 149 methodItems.addAll(methodItemList.debugItems); 150 Collections.sort(methodItems); 151 152 return methodItems; 153 } 154 155 156 private static class MethodItemList { 157 private final DexFile dexFile; 158 private final StringTemplateGroup stg; 159 private final CodeItem codeItem; 160 161 public HashSet<LabelMethodItem> labels = new HashSet<LabelMethodItem>(); 162 public List<MethodItem> instructions = new ArrayList<MethodItem>(); 163 public List<BlankMethodItem> blanks = new ArrayList<BlankMethodItem>(); 164 public List<CatchMethodItem> catches = new ArrayList<CatchMethodItem>(); 165 public List<MethodItem> debugItems = new ArrayList<MethodItem>(); 166 167 private HashMap<Integer, Integer> packedSwitchMap = new HashMap<Integer, Integer>(); 168 private HashMap<Integer, Integer> sparseSwitchMap = new HashMap<Integer, Integer>(); 169 170 public MethodItemList(DexFile dexFile, StringTemplateGroup stg, CodeItem codeItem) { 171 this.dexFile = dexFile; 172 this.stg = stg; 173 this.codeItem = codeItem; 174 } 175 176 public void generateMethodItemList() { 177 if (codeItem == null) { 178 return; 179 } 180 181 final byte[] encodedInstructions = codeItem.getEncodedInstructions(); 182 183 InstructionIterator.IterateInstructions(encodedInstructions, 184 new InstructionIterator.ProcessRawInstructionDelegate() { 185 public void ProcessNormalInstruction(Opcode opcode, int index) { 186 if (opcode == Opcode.PACKED_SWITCH) { 187 Instruction31t ins = (Instruction31t)opcode.format.Factory.makeInstruction( 188 dexFile, opcode, encodedInstructions, index); 189 packedSwitchMap.put(index/2 + ins.getOffset(), index/2); 190 } else if (opcode == Opcode.SPARSE_SWITCH) { 191 Instruction31t ins = (Instruction31t)opcode.format.Factory.makeInstruction( 192 dexFile, opcode, encodedInstructions, index); 193 sparseSwitchMap.put(index/2 + ins.getOffset(), index/2); 194 } 195 } 196 197 public void ProcessReferenceInstruction(Opcode opcode, int index) { 198 } 199 200 public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) { 201 } 202 203 public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) { 204 } 205 206 public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, 207 int instructionLength) { 208 } 209 }); 210 211 InstructionIterator.IterateInstructions(dexFile, encodedInstructions, 212 new InstructionIterator.ProcessInstructionDelegate() { 213 public void ProcessInstruction(int index, Instruction instruction) { 214 int offset = index/2; 215 addMethodItemsForInstruction(offset, instruction); 216 blanks.add(new BlankMethodItem(stg, offset)); 217 } 218 }); 219 220 blanks.remove(blanks.size()-1); 221 222 addTries(); 223 224 addDebugInfo(); 225 } 226 227 private void addMethodItemsForInstruction(int offset, Instruction instruction) { 228 switch (instruction.getFormat()) { 229 case Format10t: 230 instructions.add(new Instruction10tMethodItem(codeItem, offset, stg,(Instruction10t)instruction)); 231 labels.add(new LabelMethodItem(offset + ((Instruction10t)instruction).getOffset(), stg, "goto_")); 232 return; 233 case Format10x: 234 instructions.add(new Instruction10xMethodItem(codeItem, offset, stg, (Instruction10x)instruction)); 235 return; 236 case Format11n: 237 instructions.add(new Instruction11nMethodItem(codeItem, offset, stg, (Instruction11n)instruction)); 238 return; 239 case Format11x: 240 instructions.add(new Instruction11xMethodItem(codeItem, offset, stg, (Instruction11x)instruction)); 241 return; 242 case Format12x: 243 instructions.add(new Instruction12xMethodItem(codeItem, offset, stg, (Instruction12x)instruction)); 244 return; 245 case Format20t: 246 instructions.add(new Instruction20tMethodItem(codeItem, offset, stg, (Instruction20t)instruction)); 247 labels.add(new LabelMethodItem(offset + ((Instruction20t)instruction).getOffset(), stg, "goto_")); 248 return; 249 case Format21c: 250 instructions.add(new Instruction21cMethodItem(codeItem, offset, stg, (Instruction21c)instruction)); 251 return; 252 case Format21h: 253 instructions.add(new Instruction21hMethodItem(codeItem, offset, stg, (Instruction21h)instruction)); 254 return; 255 case Format21s: 256 instructions.add(new Instruction21sMethodItem(codeItem, offset, stg, (Instruction21s)instruction)); 257 return; 258 case Format21t: 259 instructions.add(new Instruction21tMethodItem(codeItem, offset, stg, (Instruction21t)instruction)); 260 labels.add(new LabelMethodItem(offset + ((Instruction21t)instruction).getOffset(), stg, "cond_")); 261 return; 262 case Format22b: 263 instructions.add(new Instruction22bMethodItem(codeItem, offset, stg, (Instruction22b)instruction)); 264 return; 265 case Format22c: 266 instructions.add(new Instruction22cMethodItem(codeItem, offset, stg, (Instruction22c)instruction)); 267 return; 268 case Format22cs: 269 instructions.add(new Instruction22csMethodItem(codeItem, offset, stg, 270 (Instruction22cs)instruction)); 271 return; 272 case Format22s: 273 instructions.add(new Instruction22sMethodItem(codeItem, offset, stg, (Instruction22s)instruction)); 274 return; 275 case Format22t: 276 instructions.add(new Instruction22tMethodItem(codeItem, offset, stg, (Instruction22t)instruction)); 277 labels.add(new LabelMethodItem(offset + ((Instruction22t)instruction).getOffset(), stg, "cond_")); 278 return; 279 case Format22x: 280 instructions.add(new Instruction22xMethodItem(codeItem, offset, stg, (Instruction22x)instruction)); 281 return; 282 case Format23x: 283 instructions.add(new Instruction23xMethodItem(codeItem, offset, stg, (Instruction23x)instruction)); 284 return; 285 case Format30t: 286 instructions.add(new Instruction30tMethodItem(codeItem, offset, stg, (Instruction30t)instruction)); 287 labels.add(new LabelMethodItem(offset + ((Instruction30t)instruction).getOffset(), stg, "goto_")); 288 return; 289 case Format31c: 290 instructions.add(new Instruction31cMethodItem(codeItem, offset, stg, (Instruction31c)instruction)); 291 return; 292 case Format31i: 293 instructions.add(new Instruction31iMethodItem(codeItem, offset, stg, (Instruction31i)instruction)); 294 return; 295 case Format31t: 296 instructions.add(new Instruction31tMethodItem(codeItem, offset, stg, (Instruction31t)instruction)); 297 if (instruction.opcode == Opcode.FILL_ARRAY_DATA) { 298 labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, 299 "array_")); 300 } else if (instruction.opcode == Opcode.PACKED_SWITCH) { 301 labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, 302 "pswitch_data_")); 303 } else if (instruction.opcode == Opcode.SPARSE_SWITCH) { 304 labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, 305 "sswitch_data_")); 306 } 307 return; 308 case Format32x: 309 instructions.add(new Instruction32xMethodItem(codeItem, offset, stg, (Instruction32x)instruction)); 310 return; 311 case Format35c: 312 instructions.add(new Instruction35cMethodItem(codeItem, offset, stg, (Instruction35c)instruction)); 313 return; 314 case Format35s: 315 instructions.add(new Instruction35sMethodItem(codeItem, offset, stg, (Instruction35s)instruction)); 316 return; 317 case Format35ms: 318 instructions.add(new Instruction35msMethodItem(codeItem, offset, stg, 319 (Instruction35ms)instruction)); 320 return; 321 case Format3rc: 322 instructions.add(new Instruction3rcMethodItem(codeItem, offset, stg, (Instruction3rc)instruction)); 323 return; 324 case Format3rms: 325 instructions.add(new Instruction3rmsMethodItem(codeItem, offset, stg, 326 (Instruction3rms)instruction)); 327 return; 328 case Format51l: 329 instructions.add(new Instruction51lMethodItem(codeItem, offset, stg, (Instruction51l)instruction)); 330 return; 331 case ArrayData: 332 instructions.add(new ArrayDataMethodItem(codeItem, offset, stg, (ArrayDataPseudoInstruction)instruction)); 333 return; 334 case PackedSwitchData: 335 { 336 final Integer baseAddress = packedSwitchMap.get(offset); 337 338 if (baseAddress != null) { 339 PackedSwitchDataPseudoInstruction packedSwitchInstruction = 340 (PackedSwitchDataPseudoInstruction)instruction; 341 342 instructions.add(new PackedSwitchMethodItem(codeItem, offset, stg, 343 packedSwitchInstruction, baseAddress)); 344 345 Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator = 346 packedSwitchInstruction.getTargets(); 347 while (iterator.hasNext()) { 348 PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next(); 349 labels.add(new LabelMethodItem(baseAddress + target.target, stg, "pswitch_")); 350 } 351 } 352 return; 353 } 354 case SparseSwitchData: 355 { 356 final Integer baseAddress = sparseSwitchMap.get(offset); 357 358 if (baseAddress != null) { 359 SparseSwitchDataPseudoInstruction sparseSwitchInstruction = 360 (SparseSwitchDataPseudoInstruction)instruction; 361 362 instructions.add(new SparseSwitchMethodItem(codeItem, offset, stg, 363 sparseSwitchInstruction, baseAddress)); 364 365 Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator = 366 sparseSwitchInstruction.getTargets(); 367 while (iterator.hasNext()) { 368 SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next(); 369 labels.add(new LabelMethodItem(baseAddress + target.target, stg, "sswitch_")); 370 } 371 } 372 } 373 } 374 } 375 376 private void addTries() { 377 if (codeItem.getTries() == null) { 378 return; 379 } 380 for (CodeItem.TryItem tryItem: codeItem.getTries()) { 381 int startAddress = tryItem.startAddress; 382 int endAddress = tryItem.startAddress + tryItem.instructionCount; 383 384 /** 385 * The end address points to the address immediately after the end of the last 386 * instruction that the try block covers. We want the .catch directive and end_try 387 * label to be associated with the last covered instruction, so we need to get 388 * the offset for that instruction 389 */ 390 int index = Collections.binarySearch(instructions, new BlankMethodItem(stg, endAddress)); 391 if (index < 0) { 392 index = (index * -1) - 1; 393 } 394 //index should never by 0, so this should be safe 395 if (index == instructions.size()) { 396 //if the end address is the same as the address of the last instruction, then 397 //this try item ends at the next to last instruction. 398 //otherwise, if the end address is past the address of the last instruction, 399 //thin this try item ends at the last instruction 400 if (instructions.get(instructions.size() - 1).getOffset() == endAddress) { 401 //get the address for the next to last instruction 402 index -= 2; 403 } else { 404 //get the address for the last instruction 405 index--; 406 } 407 } else { 408 index -= 2; 409 } 410 411 int lastInstructionOffset = instructions.get(index).getOffset(); 412 413 //add the catch all handler if it exists 414 int catchAllAddress = tryItem.encodedCatchHandler.catchAllHandlerAddress; 415 if (catchAllAddress != -1) { 416 CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset, stg, null, 417 startAddress, endAddress, catchAllAddress) { 418 public String getTemplateName() { 419 return "CatchAll"; 420 } 421 }; 422 catches.add(catchMethodItem); 423 424 labels.add(new LabelMethodItem(startAddress, stg, "try_start_")); 425 //use the offset from the last covered instruction, but make the label 426 //name refer to the address of the next instruction 427 labels.add(new EndTryLabelMethodItem(lastInstructionOffset, stg, endAddress)); 428 labels.add(new LabelMethodItem(catchAllAddress, stg, "handler_")); 429 430 } 431 432 //add the rest of the handlers 433 //TODO: find adjacent handlers for the same type and combine them 434 for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { 435 //use the offset from the last covered instruction 436 CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset, stg, 437 handler.exceptionType, startAddress, endAddress, handler.handlerAddress); 438 catches.add(catchMethodItem); 439 440 labels.add(new LabelMethodItem(startAddress, stg, "try_start_")); 441 //use the offset from the last covered instruction, but make the label 442 //name refer to the address of the next instruction 443 labels.add(new EndTryLabelMethodItem(lastInstructionOffset, stg, endAddress)); 444 labels.add(new LabelMethodItem(handler.handlerAddress, stg, "handler_")); 445 } 446 } 447 } 448 449 private void addDebugInfo() { 450 DebugInfoItem debugInfoItem = codeItem.getDebugInfo(); 451 if (debugInfoItem == null) { 452 return; 453 } 454 455 DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(), 456 new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() { 457 @Override 458 public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name, 459 TypeIdItem type) { 460 debugItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal", -1, 461 registerNum, name, type, null)); 462 } 463 464 @Override 465 public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, 466 StringIdItem name, TypeIdItem type, 467 StringIdItem signature) { 468 debugItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal", -1, 469 registerNum, name, type, signature)); 470 } 471 472 @Override 473 public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, 474 TypeIdItem type, StringIdItem signature) { 475 debugItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "EndLocal", -1, 476 registerNum, name, type, signature)); 477 } 478 479 @Override 480 public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name, 481 TypeIdItem type, StringIdItem signature) { 482 debugItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "RestartLocal", -1, 483 registerNum, name, type, signature)); 484 } 485 486 @Override 487 public void ProcessSetPrologueEnd(int codeAddress) { 488 debugItems.add(new DebugMethodItem(codeAddress, stg, "EndPrologue", -4)); 489 } 490 491 @Override 492 public void ProcessSetEpilogueBegin(int codeAddress) { 493 debugItems.add(new DebugMethodItem(codeAddress, stg, "StartEpilogue", -4)); 494 } 495 496 @Override 497 public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) { 498 debugItems.add(new DebugMethodItem(codeAddress, stg, "SetFile", -3) { 499 @Override 500 protected void setAttributes(StringTemplate template) { 501 template.setAttribute("FileName", name.getStringValue()); 502 } 503 }); 504 } 505 506 @Override 507 public void ProcessLineEmit(int codeAddress, final int line) { 508 debugItems.add(new DebugMethodItem(codeAddress, stg, "Line", -2) { 509 @Override 510 protected void setAttributes(StringTemplate template) { 511 template.setAttribute("Line", line); 512 } 513 }); 514 } 515 }); 516 } 517 } 518} 519