MethodAnalyzer.java revision 122eba5cecf44f6c819854e204779c83124abdf8
1package org.jf.dexlib.Code.Analysis; 2 3import org.jf.dexlib.*; 4import org.jf.dexlib.Code.*; 5import org.jf.dexlib.Code.Format.ArrayDataPseudoInstruction; 6import org.jf.dexlib.Code.Format.Format; 7import org.jf.dexlib.Util.*; 8 9import java.util.*; 10 11public class MethodAnalyzer { 12 private final ClassDataItem.EncodedMethod encodedMethod; 13 14 private SparseArray<AnalyzedInstruction> instructions; 15 16 private boolean analyzed = false; 17 18 private BitSet instructionsToVerify; 19 20 //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the 21 //register types for this instruction to the parameter types, in order to have them propagate to all of its 22 //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first 23 //instruction, etc. 24 private AnalyzedInstruction startOfMethod; 25 26 public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod) { 27 if (encodedMethod == null) { 28 throw new IllegalArgumentException("encodedMethod cannot be null"); 29 } 30 if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) { 31 throw new IllegalArgumentException("The method has no code"); 32 } 33 this.encodedMethod = encodedMethod; 34 buildInstructionList(); 35 36 //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't 37 //have to handle the case this special case of instruction being null, in the main class 38 startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) { 39 public boolean setsRegister() { 40 return false; 41 } 42 43 @Override 44 public boolean setsWideRegister() { 45 return false; 46 } 47 48 @Override 49 public boolean setsRegister(int registerNumber) { 50 return false; 51 } 52 53 @Override 54 public int getDestinationRegister() { 55 assert false; 56 return -1; 57 }; 58 }; 59 60 instructionsToVerify = new BitSet(instructions.size()); 61 } 62 63 public AnalyzedInstruction[] analyze() { 64 assert encodedMethod != null; 65 assert encodedMethod.codeItem != null; 66 67 if (analyzed) { 68 return makeInstructionArray(); 69 } 70 71 CodeItem codeItem = encodedMethod.codeItem; 72 MethodIdItem methodIdItem = encodedMethod.method; 73 74 int totalRegisters = codeItem.getRegisterCount(); 75 int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); 76 77 //if this isn't a static method, determine which register is the "this" register and set the type to the 78 //current class 79 if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) { 80 int thisRegister = totalRegisters - parameterRegisters - 1; 81 82 //if this is a constructor, then set the "this" register to an uninitialized reference of the current class 83 if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) { 84 //TODO: it would probably make more sense to validate this somewhere else, and just put an assert here. Also, need to do a similar check for static constructor 85 if (!encodedMethod.method.getMethodName().equals("<init>")) { 86 throw new ValidationException("The constructor flag can only be used with an <init> method."); 87 } 88 89 setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, 90 RegisterType.getRegisterType(RegisterType.Category.UninitRef, 91 ClassPath.getClassDef(methodIdItem.getContainingClass()))); 92 } else { 93 if (encodedMethod.method.getMethodName().equals("<init>")) { 94 throw new ValidationException("An <init> method must have the \"constructor\" access flag"); 95 } 96 97 setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, 98 RegisterType.getRegisterType(RegisterType.Category.Reference, 99 ClassPath.getClassDef(methodIdItem.getContainingClass()))); 100 } 101 } 102 103 TypeListItem parameters = methodIdItem.getPrototype().getParameters(); 104 if (parameters != null) { 105 RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters); 106 for (int i=0; i<parameterTypes.length; i++) { 107 RegisterType registerType = parameterTypes[i]; 108 int registerNum = (totalRegisters - parameterRegisters) + i; 109 setRegisterTypeAndPropagateChanges(startOfMethod, registerNum, registerType); 110 } 111 } 112 113 BitSet analyzedInstructions = new BitSet(instructionsToVerify.size()); 114 115 //make sure all of the "first instructions" are marked for processing 116 for (AnalyzedInstruction successor: startOfMethod.successors) { 117 instructionsToVerify.set(successor.instructionIndex); 118 } 119 120 while (!instructionsToVerify.isEmpty()) { 121 for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) { 122 analyzeInstruction(instructions.get(i)); 123 instructionsToVerify.clear(i); 124 } 125 } 126 127 analyzed = true; 128 return makeInstructionArray(); 129 } 130 131 private int getThisRegister() { 132 assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0; 133 134 CodeItem codeItem = encodedMethod.codeItem; 135 assert codeItem != null; 136 137 MethodIdItem methodIdItem = encodedMethod.method; 138 assert methodIdItem != null; 139 140 int totalRegisters = codeItem.getRegisterCount(); 141 if (totalRegisters == 0) { 142 throw new ValidationException("A non-static method must have at least 1 register"); 143 } 144 145 int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); 146 147 return totalRegisters - parameterRegisters - 1; 148 } 149 150 private boolean isInstanceConstructor() { 151 return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 && 152 (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; 153 } 154 155 private boolean isStaticConstructor() { 156 return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 && 157 (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; 158 } 159 160 public AnalyzedInstruction[] makeInstructionArray() { 161 AnalyzedInstruction[] instructionArray = new AnalyzedInstruction[instructions.size()]; 162 for (int i=0; i<instructions.size(); i++) { 163 instructionArray[i] = instructions.valueAt(i); 164 } 165 return instructionArray; 166 } 167 168 private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) { 169 assert typeListItem != null; 170 assert parameterRegisterCount == typeListItem.getRegisterCount(); 171 172 RegisterType[] registerTypes = new RegisterType[parameterRegisterCount]; 173 174 int registerNum = 0; 175 for (TypeIdItem type: typeListItem.getTypes()) { 176 if (type.getRegisterCount() == 2) { 177 registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true); 178 registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false); 179 } else { 180 registerTypes[registerNum] = RegisterType.getRegisterTypeForTypeIdItem(type); 181 } 182 } 183 184 return registerTypes; 185 } 186 187 private int getInstructionAddress(AnalyzedInstruction instruction) { 188 return instructions.keyAt(instruction.instructionIndex); 189 } 190 191 private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, 192 RegisterType registerType) { 193 setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(), 194 registerType); 195 } 196 197 private void setRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber, 198 RegisterType registerType) { 199 200 BitSet changedInstructions = new BitSet(instructions.size()); 201 202 boolean changed = analyzedInstruction.setPostRegisterType(registerNumber, registerType); 203 204 if (!changed || analyzedInstruction.setsRegister(registerNumber)) { 205 return; 206 } 207 208 propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions); 209 210 //using a for loop inside the while loop optimizes for the common case of the successors of an instruction 211 //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on 212 //the next iteration of the while loop. 213 //this could also be done recursively, but in large methods it would likely cause very deep recursion, 214 //which would requires the user to specify a larger stack size. This isn't really a problem, but it is 215 //slightly annoying. 216 while (!changedInstructions.isEmpty()) { 217 for (int instructionIndex=changedInstructions.nextSetBit(0); 218 instructionIndex>=0; 219 instructionIndex=changedInstructions.nextSetBit(instructionIndex)) { 220 221 changedInstructions.clear(instructionIndex); 222 223 propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber, 224 changedInstructions); 225 } 226 } 227 228 if (registerType.category == RegisterType.Category.LongLo) { 229 checkWidePair(registerNumber, analyzedInstruction); 230 setRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, 231 RegisterType.getRegisterType(RegisterType.Category.LongHi, null)); 232 } else if (registerType.category == RegisterType.Category.DoubleLo) { 233 checkWidePair(registerNumber, analyzedInstruction); 234 setRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, 235 RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null)); 236 } 237 } 238 239 private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber, 240 BitSet changedInstructions) { 241 for (AnalyzedInstruction successor: instruction.successors) { 242 if (!successor.setsRegister(registerNumber)) { 243 RegisterType registerType = successor.getMergedRegisterTypeFromPredecessors(registerNumber); 244 245 if (registerType.category == RegisterType.Category.UninitRef && instruction.isInvokeInit()) { 246 continue; 247 } 248 249 if (successor.setPostRegisterType(registerNumber, registerType)) { 250 changedInstructions.set(successor.instructionIndex); 251 instructionsToVerify.set(successor.instructionIndex); 252 } 253 } 254 } 255 } 256 257 258 259 private void buildInstructionList() { 260 assert encodedMethod != null; 261 assert encodedMethod.codeItem != null; 262 int registerCount = encodedMethod.codeItem.getRegisterCount(); 263 264 startOfMethod = new AnalyzedInstruction(null, -1, registerCount); 265 266 Instruction[] insns = encodedMethod.codeItem.getInstructions(); 267 268 instructions = new SparseArray<AnalyzedInstruction>(insns.length); 269 270 //first, create all the instructions and populate the instructionAddresses array 271 int currentCodeAddress = 0; 272 for (int i=0; i<insns.length; i++) { 273 instructions.append(currentCodeAddress, new AnalyzedInstruction(insns[i], i, registerCount)); 274 assert instructions.indexOfKey(currentCodeAddress) == i; 275 currentCodeAddress += insns[i].getSize(currentCodeAddress); 276 } 277 278 //next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception 279 //and is covered by a try block should be set to a list of the first instructions of each exception handler 280 //for the try block covering the instruction 281 CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); 282 int triesIndex = 0; 283 CodeItem.TryItem currentTry = null; 284 AnalyzedInstruction[] currentExceptionHandlers = null; 285 AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][]; 286 287 for (int i=0; i<instructions.size(); i++) { 288 AnalyzedInstruction instruction = instructions.valueAt(i); 289 Opcode instructionOpcode = instruction.instruction.opcode; 290 291 //check if we have gone past the end of the current try 292 if (currentTry != null) { 293 if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) { 294 currentTry = null; 295 triesIndex++; 296 } 297 } 298 299 //check if the next try is applicable yet 300 if (currentTry == null && triesIndex < tries.length) { 301 CodeItem.TryItem tryItem = tries[triesIndex]; 302 if (tryItem.getStartCodeAddress() <= currentCodeAddress) { 303 assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress); 304 305 currentTry = tryItem; 306 307 currentExceptionHandlers = buildExceptionHandlerArray(tryItem); 308 } 309 } 310 311 //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers 312 //for the current instruction 313 if (currentTry != null && instructionOpcode.canThrow()) { 314 exceptionHandlers[i] = currentExceptionHandlers; 315 } 316 } 317 318 //finally, populate the successors and predecessors for each instruction 319 assert instructions.size() > 0; 320 addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers); 321 startOfMethod.addSuccessor(instructions.valueAt(0)); 322 323 for (int i=0; i<instructions.size(); i++) { 324 AnalyzedInstruction instruction = instructions.valueAt(i); 325 Opcode instructionOpcode = instruction.instruction.opcode; 326 int instructionCodeAddress = getInstructionAddress(instruction); 327 328 if (instruction.instruction.opcode.canContinue()) { 329 if (i == instructions.size() - 1) { 330 throw new ValidationException("Execution can continue past the last instruction"); 331 } 332 AnalyzedInstruction nextInstruction = instructions.valueAt(i+1); 333 addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers); 334 } 335 336 if (instruction instanceof OffsetInstruction) { 337 OffsetInstruction offsetInstruction = (OffsetInstruction)instruction; 338 339 if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) { 340 MultiOffsetInstruction switchDataInstruction = 341 (MultiOffsetInstruction)instructions.get(instructionCodeAddress + 342 offsetInstruction.getTargetAddressOffset()).instruction; 343 for (int targetAddressOffset: switchDataInstruction.getTargets()) { 344 AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + 345 targetAddressOffset); 346 347 addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers); 348 } 349 } else { 350 int targetAddressOffset = offsetInstruction.getTargetAddressOffset(); 351 AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + 352 targetAddressOffset); 353 addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers); 354 } 355 } 356 } 357 } 358 359 private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, 360 AnalyzedInstruction[][] exceptionHandlers) { 361 addPredecessorSuccessor(predecessor, successor, exceptionHandlers, false); 362 } 363 364 private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, 365 AnalyzedInstruction[][] exceptionHandlers, boolean allowMoveException) { 366 367 if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) { 368 throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name + 369 " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) + 370 " to the move-exception instruction at address 0x" + 371 Integer.toHexString(getInstructionAddress(successor))); 372 } 373 374 if (!predecessor.addSuccessor(successor)) { 375 //if predecessor already had successor as a successor, then there's nothing else to do 376 return; 377 } 378 379 successor.addPredecessor(predecessor); 380 381 //TODO: need to handle the case of monitor-exit as a special case - the exception is thrown *after* the instruction executes 382 //if the successor can throw an instruction, then we need to add the exception handlers as additional 383 //successors to the predecessor (and then apply this same logic recursively if needed) 384 AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex]; 385 if (exceptionHandlersForSuccessor != null) { 386 //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction 387 //can throw an exception 388 assert predecessor.instruction.opcode.canThrow(); 389 390 for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { 391 addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, true); 392 } 393 } 394 } 395 396 private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) { 397 int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length; 398 int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress(); 399 if (catchAllHandler != -1) { 400 exceptionHandlerCount++; 401 } 402 403 AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount]; 404 for (int i=0; i<tryItem.encodedCatchHandler.handlers.length; i++) { 405 exceptionHandlers[i] = instructions.get(tryItem.encodedCatchHandler.handlers[i].getHandlerAddress()); 406 } 407 408 if (catchAllHandler != -1) { 409 exceptionHandlers[exceptionHandlers.length - 1] = instructions.get(catchAllHandler); 410 } 411 412 return exceptionHandlers; 413 } 414 415 private void analyzeInstruction(AnalyzedInstruction analyzedInstruction) { 416 Instruction instruction = analyzedInstruction.instruction; 417 418 switch (instruction.opcode) { 419 case NOP: 420 return; 421 case MOVE: 422 case MOVE_FROM16: 423 case MOVE_16: 424 handleMove(analyzedInstruction, Primitive32BitCategories); 425 return; 426 case MOVE_WIDE: 427 case MOVE_WIDE_FROM16: 428 case MOVE_WIDE_16: 429 handleMove(analyzedInstruction, WideLowCategories); 430 return; 431 case MOVE_OBJECT: 432 case MOVE_OBJECT_FROM16: 433 case MOVE_OBJECT_16: 434 handleMove(analyzedInstruction, ReferenceCategories); 435 return; 436 case MOVE_RESULT: 437 handleMoveResult(analyzedInstruction, Primitive32BitCategories); 438 return; 439 case MOVE_RESULT_WIDE: 440 handleMoveResult(analyzedInstruction, WideLowCategories); 441 return; 442 case MOVE_RESULT_OBJECT: 443 handleMoveResult(analyzedInstruction, ReferenceCategories); 444 return; 445 case MOVE_EXCEPTION: 446 handleMoveException(analyzedInstruction); 447 return; 448 case RETURN_VOID: 449 handleReturnVoid(analyzedInstruction); 450 return; 451 case RETURN: 452 handleReturn(analyzedInstruction, Primitive32BitCategories); 453 return; 454 case RETURN_WIDE: 455 handleReturn(analyzedInstruction, WideLowCategories); 456 return; 457 case RETURN_OBJECT: 458 handleReturn(analyzedInstruction, ReferenceCategories); 459 return; 460 case CONST_4: 461 case CONST_16: 462 case CONST: 463 handleConst(analyzedInstruction); 464 return; 465 case CONST_HIGH16: 466 handleConstHigh16(analyzedInstruction); 467 return; 468 case CONST_WIDE_16: 469 case CONST_WIDE_32: 470 case CONST_WIDE: 471 case CONST_WIDE_HIGH16: 472 handleWideConst(analyzedInstruction); 473 return; 474 case CONST_STRING: 475 case CONST_STRING_JUMBO: 476 handleConstString(analyzedInstruction); 477 return; 478 case CONST_CLASS: 479 handleConstClass(analyzedInstruction); 480 return; 481 case MONITOR_ENTER: 482 case MONITOR_EXIT: 483 handleMonitor(analyzedInstruction); 484 return; 485 case CHECK_CAST: 486 handleCheckCast(analyzedInstruction); 487 return; 488 case INSTANCE_OF: 489 handleInstanceOf(analyzedInstruction); 490 return; 491 case ARRAY_LENGTH: 492 handleArrayLength(analyzedInstruction); 493 return; 494 case NEW_INSTANCE: 495 handleNewInstance(analyzedInstruction); 496 return; 497 case NEW_ARRAY: 498 handleNewArray(analyzedInstruction); 499 return; 500 case FILLED_NEW_ARRAY: 501 handleFilledNewArray(analyzedInstruction); 502 return; 503 case FILLED_NEW_ARRAY_RANGE: 504 handleFilledNewArrayRange(analyzedInstruction); 505 return; 506 case FILL_ARRAY_DATA: 507 handleFillArrayData(analyzedInstruction); 508 return; 509 case THROW: 510 handleThrow(analyzedInstruction); 511 return; 512 case GOTO: 513 case GOTO_16: 514 case GOTO_32: 515 //nothing to do 516 return; 517 case PACKED_SWITCH: 518 handleSwitch(analyzedInstruction, Format.PackedSwitchData); 519 return; 520 case SPARSE_SWITCH: 521 handleSwitch(analyzedInstruction, Format.SparseSwitchData); 522 return; 523 case CMPL_FLOAT: 524 case CMPG_FLOAT: 525 handleFloatWideCmp(analyzedInstruction, Primitive32BitCategories); 526 return; 527 case CMPL_DOUBLE: 528 case CMPG_DOUBLE: 529 case CMP_LONG: 530 handleFloatWideCmp(analyzedInstruction, WideLowCategories); 531 return; 532 case IF_EQ: 533 case IF_NE: 534 handleIfEqNe(analyzedInstruction); 535 return; 536 case IF_LT: 537 case IF_GE: 538 case IF_GT: 539 case IF_LE: 540 handleIf(analyzedInstruction); 541 return; 542 case IF_EQZ: 543 case IF_NEZ: 544 handleIfEqzNez(analyzedInstruction); 545 return; 546 case IF_LTZ: 547 case IF_GEZ: 548 case IF_GTZ: 549 case IF_LEZ: 550 handleIfz(analyzedInstruction); 551 return; 552 case AGET: 553 handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer); 554 return; 555 case AGET_BOOLEAN: 556 handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean); 557 return; 558 case AGET_BYTE: 559 handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte); 560 return; 561 case AGET_CHAR: 562 handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char); 563 return; 564 case AGET_SHORT: 565 handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short); 566 return; 567 case AGET_WIDE: 568 handleAgetWide(analyzedInstruction); 569 return; 570 case AGET_OBJECT: 571 handleAgetObject(analyzedInstruction); 572 return; 573 case APUT: 574 handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Integer); 575 return; 576 case APUT_BOOLEAN: 577 handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Boolean); 578 return; 579 case APUT_BYTE: 580 handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Byte); 581 return; 582 case APUT_CHAR: 583 handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Char); 584 return; 585 case APUT_SHORT: 586 handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Short); 587 return; 588 case APUT_WIDE: 589 handleAputWide(analyzedInstruction); 590 return; 591 case APUT_OBJECT: 592 handleAputObject(analyzedInstruction); 593 return; 594 case IGET: 595 handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer); 596 return; 597 case IGET_BOOLEAN: 598 handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean); 599 return; 600 case IGET_BYTE: 601 handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte); 602 return; 603 case IGET_CHAR: 604 handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char); 605 return; 606 case IGET_SHORT: 607 handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short); 608 return; 609 case IGET_WIDE: 610 handleIgetWide(analyzedInstruction); 611 return; 612 case IGET_OBJECT: 613 handleIgetObject(analyzedInstruction); 614 return; 615 case IPUT: 616 handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Integer); 617 return; 618 case IPUT_BOOLEAN: 619 handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Boolean); 620 return; 621 case IPUT_BYTE: 622 handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Byte); 623 return; 624 case IPUT_CHAR: 625 handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Char); 626 return; 627 case IPUT_SHORT: 628 handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Short); 629 return; 630 case IPUT_WIDE: 631 handleIputWide(analyzedInstruction); 632 return; 633 case IPUT_OBJECT: 634 handleIputObject(analyzedInstruction); 635 return; 636 case SGET: 637 handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer); 638 return; 639 case SGET_BOOLEAN: 640 handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean); 641 return; 642 case SGET_BYTE: 643 handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte); 644 return; 645 case SGET_CHAR: 646 handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char); 647 return; 648 case SGET_SHORT: 649 handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short); 650 return; 651 case SGET_WIDE: 652 handleSgetWide(analyzedInstruction); 653 return; 654 case SGET_OBJECT: 655 handleSgetObject(analyzedInstruction); 656 return; 657 case SPUT: 658 handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Integer); 659 return; 660 case SPUT_BOOLEAN: 661 handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Boolean); 662 return; 663 case SPUT_BYTE: 664 handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Byte); 665 return; 666 case SPUT_CHAR: 667 handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Char); 668 return; 669 case SPUT_SHORT: 670 handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Short); 671 return; 672 case SPUT_WIDE: 673 handleSputWide(analyzedInstruction); 674 return; 675 case SPUT_OBJECT: 676 handleSputObject(analyzedInstruction); 677 return; 678 case INVOKE_VIRTUAL: 679 handleInvoke(analyzedInstruction, INVOKE_VIRTUAL); 680 return; 681 case INVOKE_SUPER: 682 handleInvoke(analyzedInstruction, INVOKE_SUPER); 683 return; 684 case INVOKE_DIRECT: 685 handleInvoke(analyzedInstruction, INVOKE_DIRECT); 686 return; 687 case INVOKE_STATIC: 688 handleInvoke(analyzedInstruction, INVOKE_STATIC); 689 return; 690 case INVOKE_INTERFACE: 691 handleInvoke(analyzedInstruction, INVOKE_INTERFACE); 692 return; 693 case INVOKE_VIRTUAL_RANGE: 694 handleInvokeRange(analyzedInstruction, INVOKE_VIRTUAL); 695 return; 696 case INVOKE_SUPER_RANGE: 697 handleInvokeRange(analyzedInstruction, INVOKE_SUPER); 698 return; 699 case INVOKE_DIRECT_RANGE: 700 handleInvokeRange(analyzedInstruction, INVOKE_DIRECT); 701 return; 702 case INVOKE_STATIC_RANGE: 703 handleInvokeRange(analyzedInstruction, INVOKE_STATIC); 704 return; 705 case INVOKE_INTERFACE_RANGE: 706 handleInvokeRange(analyzedInstruction, INVOKE_INTERFACE); 707 return; 708 case NEG_INT: 709 case NOT_INT: 710 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Integer); 711 return; 712 case NEG_LONG: 713 case NOT_LONG: 714 handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.LongLo); 715 return; 716 case NEG_FLOAT: 717 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Float); 718 return; 719 case NEG_DOUBLE: 720 handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.DoubleLo); 721 return; 722 case INT_TO_LONG: 723 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.LongLo); 724 return; 725 case INT_TO_FLOAT: 726 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Float); 727 return; 728 case INT_TO_DOUBLE: 729 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.DoubleLo); 730 return; 731 case LONG_TO_INT: 732 case DOUBLE_TO_INT: 733 handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.Integer); 734 return; 735 case LONG_TO_FLOAT: 736 case DOUBLE_TO_FLOAT: 737 handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.Float); 738 return; 739 case LONG_TO_DOUBLE: 740 handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.DoubleLo); 741 return; 742 case FLOAT_TO_INT: 743 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Integer); 744 return; 745 case FLOAT_TO_LONG: 746 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.LongLo); 747 return; 748 case FLOAT_TO_DOUBLE: 749 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.DoubleLo); 750 return; 751 case DOUBLE_TO_LONG: 752 handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.LongLo); 753 return; 754 case INT_TO_BYTE: 755 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Byte); 756 return; 757 case INT_TO_CHAR: 758 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Char); 759 return; 760 case INT_TO_SHORT: 761 handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Short); 762 return; 763 case ADD_INT: 764 case SUB_INT: 765 case MUL_INT: 766 case DIV_INT: 767 case REM_INT: 768 case SHL_INT: 769 case SHR_INT: 770 case USHR_INT: 771 handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories, 772 RegisterType.Category.Integer, false); 773 return; 774 case AND_INT: 775 case OR_INT: 776 case XOR_INT: 777 handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories, 778 RegisterType.Category.Integer, true); 779 780 case ADD_LONG: 781 case SUB_LONG: 782 case MUL_LONG: 783 case DIV_LONG: 784 case REM_LONG: 785 case AND_LONG: 786 case OR_LONG: 787 case XOR_LONG: 788 handleBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories, RegisterType.Category.LongLo, 789 false); 790 return; 791 case SHL_LONG: 792 case SHR_LONG: 793 case USHR_LONG: 794 handleBinaryOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories, 795 RegisterType.Category.LongLo, false); 796 return; 797 case ADD_FLOAT: 798 case SUB_FLOAT: 799 case MUL_FLOAT: 800 case DIV_FLOAT: 801 case REM_FLOAT: 802 handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories, 803 RegisterType.Category.Float, false); 804 return; 805 case ADD_DOUBLE: 806 case SUB_DOUBLE: 807 case MUL_DOUBLE: 808 case DIV_DOUBLE: 809 case REM_DOUBLE: 810 handleBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories, 811 RegisterType.Category.DoubleLo, false); 812 return; 813 case ADD_INT_2ADDR: 814 case SUB_INT_2ADDR: 815 case MUL_INT_2ADDR: 816 case DIV_INT_2ADDR: 817 case REM_INT_2ADDR: 818 case SHL_INT_2ADDR: 819 case SHR_INT_2ADDR: 820 case USHR_INT_2ADDR: 821 handleBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories, 822 RegisterType.Category.Integer, false); 823 return; 824 case AND_INT_2ADDR: 825 case OR_INT_2ADDR: 826 case XOR_INT_2ADDR: 827 handleBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories, 828 RegisterType.Category.Integer, true); 829 return; 830 case ADD_LONG_2ADDR: 831 case SUB_LONG_2ADDR: 832 case MUL_LONG_2ADDR: 833 case DIV_LONG_2ADDR: 834 case REM_LONG_2ADDR: 835 case AND_LONG_2ADDR: 836 case OR_LONG_2ADDR: 837 case XOR_LONG_2ADDR: 838 handleBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories, 839 RegisterType.Category.LongLo, false); 840 return; 841 case SHL_LONG_2ADDR: 842 case SHR_LONG_2ADDR: 843 case USHR_LONG_2ADDR: 844 handleBinary2AddrOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories, 845 RegisterType.Category.LongLo, false); 846 return; 847 case ADD_FLOAT_2ADDR: 848 case SUB_FLOAT_2ADDR: 849 case MUL_FLOAT_2ADDR: 850 case DIV_FLOAT_2ADDR: 851 case REM_FLOAT_2ADDR: 852 handleBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories, 853 RegisterType.Category.Float, false); 854 return; 855 case ADD_DOUBLE_2ADDR: 856 case SUB_DOUBLE_2ADDR: 857 case MUL_DOUBLE_2ADDR: 858 case DIV_DOUBLE_2ADDR: 859 case REM_DOUBLE_2ADDR: 860 handleBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories, 861 RegisterType.Category.DoubleLo, false); 862 return; 863 } 864 } 865 866 private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of( 867 RegisterType.Category.Null, 868 RegisterType.Category.Boolean, 869 RegisterType.Category.Byte, 870 RegisterType.Category.Short, 871 RegisterType.Category.Char, 872 RegisterType.Category.Integer, 873 RegisterType.Category.Float); 874 875 private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of( 876 RegisterType.Category.LongLo, 877 RegisterType.Category.DoubleLo); 878 879 private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of( 880 RegisterType.Category.LongHi, 881 RegisterType.Category.DoubleHi); 882 883 private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of( 884 RegisterType.Category.Null, 885 RegisterType.Category.Reference); 886 887 private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of( 888 RegisterType.Category.Null, 889 RegisterType.Category.Boolean, 890 RegisterType.Category.Byte, 891 RegisterType.Category.Short, 892 RegisterType.Category.Char, 893 RegisterType.Category.Integer, 894 RegisterType.Category.Float, 895 RegisterType.Category.Reference); 896 897 private static final EnumSet<RegisterType.Category> BooleanCategories = EnumSet.of( 898 RegisterType.Category.Null, 899 RegisterType.Category.One, 900 RegisterType.Category.Boolean); 901 902 private void handleMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { 903 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 904 905 RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 906 validCategories); 907 908 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); 909 } 910 911 private void handleMoveResult(AnalyzedInstruction analyzedInstruction, 912 EnumSet<RegisterType.Category> allowedCategories) { 913 914 //TODO: handle the case when the previous instruction is an odexed instruction 915 916 if (analyzedInstruction.instructionIndex == 0) { 917 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " + 918 "instruction in a method. It must occur after an invoke-*/fill-new-array instruction"); 919 } 920 921 AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); 922 923 if (!previousInstruction.instruction.opcode.setsResult()) { 924 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + 925 "invoke-*/fill-new-array instruction"); 926 } 927 928 //TODO: does dalvik allow a move-result after an invoke with a void return type? 929 RegisterType resultRegisterType; 930 931 InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction; 932 Item item = invokeInstruction.getReferencedItem(); 933 934 if (item instanceof MethodIdItem) { 935 resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( 936 ((MethodIdItem)item).getPrototype().getReturnType()); 937 } else { 938 assert item instanceof TypeIdItem; 939 resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 940 } 941 942 if (!allowedCategories.contains(resultRegisterType.category)) { 943 throw new ValidationException(String.format("Wrong move-result* instruction for return value %s", 944 resultRegisterType.toString())); 945 } 946 947 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType); 948 } 949 950 private void handleMoveException(AnalyzedInstruction analyzedInstruction) { 951 CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); 952 int instructionAddress = getInstructionAddress(analyzedInstruction); 953 954 if (tries == null) { 955 throw new ValidationException("move-exception must be the first instruction in an exception handler block"); 956 } 957 958 RegisterType exceptionType = null; 959 960 for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { 961 if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { 962 exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, 963 ClassPath.getClassDef("Ljava/lang/Throwable;")); 964 break; 965 } 966 for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { 967 if (handler.getHandlerAddress() == instructionAddress) { 968 exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) 969 .merge(exceptionType); 970 } 971 } 972 } 973 974 if (exceptionType == null) { 975 throw new ValidationException("move-exception must be the first instruction in an exception handler block"); 976 } 977 978 //TODO: check if the type is a throwable. Should we throw a ValidationException or print a warning? (does dalvik validate that it's a throwable? It doesn't in CodeVerify.c, but it might check in DexSwapVerify.c) 979 if (exceptionType.category != RegisterType.Category.Reference) { 980 throw new ValidationException(String.format("Exception type %s is not a reference type", 981 exceptionType.toString())); 982 } 983 984 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); 985 } 986 987 private void checkConstructorReturn(AnalyzedInstruction analyzedInstruction) { 988 assert this.isInstanceConstructor(); 989 990 //if we're in an instance constructor (an <init> method), then the superclass <init> must have been called. 991 //When execution enters the method, the "this" register is set as an uninitialized reference to the containing 992 //class. Once the superclass' <init> is called, the "this" register is upgraded to a full-blown reference type, 993 //so we need to ensure that the "this" register isn't an uninitialized reference 994 995 int thisRegister = getThisRegister(); 996 RegisterType thisRegisterType = analyzedInstruction.postRegisterMap[thisRegister]; 997 998 if (thisRegisterType.category == RegisterType.Category.UninitRef) { 999 throw new ValidationException("Returning from constructor without calling the superclass' <init>"); 1000 } 1001 assert thisRegisterType.category == RegisterType.Category.Reference; 1002 assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass()); 1003 } 1004 1005 private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) { 1006 if (this.isInstanceConstructor()) { 1007 checkConstructorReturn(analyzedInstruction); 1008 } 1009 1010 TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); 1011 if (returnType.getTypeDescriptor().charAt(0) != 'V') { 1012 //TODO: could add which return-* variation should be used instead 1013 throw new ValidationException("Cannot use return-void with a non-void return type (" + 1014 returnType.getTypeDescriptor() + ")"); 1015 } 1016 } 1017 1018 private void handleReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { 1019 if (this.isInstanceConstructor()) { 1020 checkConstructorReturn(analyzedInstruction); 1021 } 1022 1023 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1024 int returnRegister = instruction.getRegisterA(); 1025 RegisterType returnRegisterType = getAndCheckSourceRegister(analyzedInstruction, returnRegister, 1026 validCategories); 1027 1028 TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); 1029 if (returnType.getTypeDescriptor().charAt(0) == 'V') { 1030 throw new ValidationException("Cannot use return with a void return type. Use return-void instead"); 1031 } 1032 1033 RegisterType methodReturnRegisterType = RegisterType.getRegisterTypeForTypeIdItem(returnType); 1034 1035 if (!validCategories.contains(methodReturnRegisterType.category)) { 1036 //TODO: could add which return-* variation should be used instead 1037 throw new ValidationException(String.format("Cannot use %s with return type %s", 1038 analyzedInstruction.instruction.opcode.name, returnType.getTypeDescriptor())); 1039 } 1040 1041 if (validCategories == ReferenceCategories) { 1042 if (methodReturnRegisterType.type.isInterface()) { 1043 if (!returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) { 1044 //TODO: how to handle warnings? 1045 } 1046 } else { 1047 if (!returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) { 1048 throw new ValidationException(String.format("The return value in register v%d (%s) is not " + 1049 "compatible with the method's return type %s", returnRegister, 1050 returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType())); 1051 } 1052 } 1053 } 1054 } 1055 1056 private void handleConst(AnalyzedInstruction analyzedInstruction) { 1057 LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; 1058 1059 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral()); 1060 1061 //we assume that the literal value is a valid value for the given instruction type, because it's impossible 1062 //to store an invalid literal with the instruction. so we don't need to check the type of the literal 1063 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 1064 } 1065 1066 private void handleConstHigh16(AnalyzedInstruction analyzedInstruction) { 1067 LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; 1068 1069 //TODO: test this 1070 long literalValue = instruction.getLiteral() << 16; 1071 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(literalValue); 1072 1073 //we assume that the literal value is a valid value for the given instruction type, because it's impossible 1074 //to store an invalid literal with the instruction. so we don't need to check the type of the literal 1075 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 1076 } 1077 1078 private void handleWideConst(AnalyzedInstruction analyzedInstruction) { 1079 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1080 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 1081 } 1082 1083 private void handleConstString(AnalyzedInstruction analyzedInstruction) { 1084 ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;"); 1085 RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef); 1086 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType); 1087 } 1088 1089 private void handleConstClass(AnalyzedInstruction analyzedInstruction) { 1090 ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); 1091 RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); 1092 1093 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1094 Item item = instruction.getReferencedItem(); 1095 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1096 1097 //make sure the referenced class is resolvable 1098 //TODO: need to check class access 1099 ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item); 1100 } 1101 1102 private void handleMonitor(AnalyzedInstruction analyzedInstruction) { 1103 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1104 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceCategories); 1105 } 1106 1107 private void handleCheckCast(AnalyzedInstruction analyzedInstruction) { 1108 { 1109 //ensure the "source" register is a reference type 1110 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1111 1112 RegisterType registerType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), 1113 ReferenceCategories); 1114 } 1115 1116 { 1117 //resolve and verify the class that we're casting to 1118 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1119 1120 Item item = instruction.getReferencedItem(); 1121 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1122 1123 //TODO: need to check class access 1124 RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1125 if (castRegisterType.category != RegisterType.Category.Reference) { 1126 //TODO: verify that dalvik allows a non-reference type.. 1127 //TODO: print a warning, but don't re-throw the exception. dalvik allows a non-reference type during validation (but throws an exception at runtime) 1128 } 1129 1130 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType); 1131 } 1132 } 1133 1134 private void handleInstanceOf(AnalyzedInstruction analyzedInstruction) { 1135 { 1136 //ensure the register that is being checks is a reference type 1137 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1138 1139 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), ReferenceCategories); 1140 } 1141 1142 { 1143 //resolve and verify the class that we're checking against 1144 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1145 1146 Item item = instruction.getReferencedItem(); 1147 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1148 RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1149 if (registerType.category != RegisterType.Category.Reference) { 1150 throw new ValidationException(String.format("Cannot use instance-of with a non-reference type %s", 1151 registerType.toString())); 1152 } 1153 1154 //TODO: is it valid to use an array type? 1155 1156 //TODO: could probably do an even more sophisticated check, where we check the possible register types against the specified type. In some cases, we could determine that it always fails, and print a warning to that effect. 1157 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1158 RegisterType.getRegisterType(RegisterType.Category.Boolean, null)); 1159 } 1160 } 1161 1162 private void handleArrayLength(AnalyzedInstruction analyzedInstruction) { 1163 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1164 1165 int arrayRegisterNumber = instruction.getRegisterB(); 1166 RegisterType arrayRegisterType = getAndCheckSourceRegister(analyzedInstruction, arrayRegisterNumber, 1167 ReferenceCategories); 1168 1169 if (arrayRegisterType.type != null) { 1170 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1171 throw new ValidationException(String.format("Cannot use array-length with non-array type %s", 1172 arrayRegisterType.type.getClassType())); 1173 } 1174 } 1175 1176 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1177 1178 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1179 RegisterType.getRegisterType(RegisterType.Category.Integer, null)); 1180 } 1181 1182 private void handleNewInstance(AnalyzedInstruction analyzedInstruction) { 1183 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1184 1185 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1186 RegisterType destRegisterType = analyzedInstruction.postRegisterMap[register]; 1187 if (destRegisterType.category != RegisterType.Category.Unknown) { 1188 assert destRegisterType.category == RegisterType.Category.UninitRef; 1189 1190 //the "post-instruction" destination register will only be set if we've gone over 1191 //this instruction at least once before. If this is the case, then we need to check 1192 //all the other registers, and make sure that none of them contain the same 1193 //uninitialized reference that is in the destination register. 1194 1195 for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) { 1196 if (i==register) { 1197 continue; 1198 } 1199 1200 if (analyzedInstruction.getPreInstructionRegisterType(i) == destRegisterType) { 1201 throw new ValidationException(String.format("Register v%d contains an uninitialized reference " + 1202 "that was created by this new-instance instruction.", i)); 1203 } 1204 } 1205 } 1206 1207 Item item = instruction.getReferencedItem(); 1208 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1209 1210 //TODO: need to check class access 1211 RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1212 if (classType.category != RegisterType.Category.Reference) { 1213 throw new ValidationException(String.format("Cannot use new-instance with a non-reference type %s", 1214 classType.toString())); 1215 } 1216 1217 if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') { 1218 throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() + 1219 "\" with new-instance. Use new-array instead."); 1220 } 1221 1222 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1223 RegisterType.getUnitializedReference(classType.type)); 1224 } 1225 1226 private void handleNewArray(AnalyzedInstruction analyzedInstruction) { 1227 { 1228 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1229 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); 1230 } 1231 1232 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1233 1234 Item item = instruction.getReferencedItem(); 1235 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1236 1237 RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1238 assert arrayType.type instanceof ClassPath.ArrayClassDef; 1239 1240 if (arrayType.category != RegisterType.Category.Reference) { 1241 throw new ValidationException(String.format("Cannot use new-array with a non-reference type %s", 1242 arrayType.toString())); 1243 } 1244 if (arrayType.type.getClassType().charAt(0) != '[') { 1245 throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() + 1246 "\" with new-array. Use new-instance instead."); 1247 } 1248 1249 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); 1250 } 1251 1252 private static interface RegisterIterator { 1253 int getRegister(); 1254 boolean moveNext(); 1255 int getCount(); 1256 boolean pastEnd(); 1257 } 1258 1259 private static class Format35cRegisterIterator implements RegisterIterator { 1260 private final int registerCount; 1261 private final int[] registers; 1262 private int currentRegister = 0; 1263 1264 public Format35cRegisterIterator(FiveRegisterInstruction instruction) { 1265 registerCount = instruction.getRegCount(); 1266 registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(), 1267 instruction.getRegisterF(), instruction.getRegisterG(), 1268 instruction.getRegisterA()}; 1269 } 1270 1271 public int getRegister() { 1272 return registers[currentRegister]; 1273 } 1274 1275 public boolean moveNext() { 1276 currentRegister++; 1277 return pastEnd(); 1278 } 1279 1280 public int getCount() { 1281 return registerCount; 1282 } 1283 1284 public boolean pastEnd() { 1285 return currentRegister >= registerCount; 1286 } 1287 } 1288 1289 private static class Format3rcRegisterIterator implements RegisterIterator { 1290 private final int startRegister; 1291 private final int registerCount; 1292 private int currentRegister = 0; 1293 1294 public Format3rcRegisterIterator(RegisterRangeInstruction instruction) { 1295 startRegister = instruction.getStartRegister(); 1296 registerCount = instruction.getRegCount(); 1297 } 1298 1299 public int getRegister() { 1300 return startRegister + currentRegister; 1301 } 1302 1303 public boolean moveNext() { 1304 currentRegister++; 1305 return pastEnd(); 1306 } 1307 1308 public int getCount() { 1309 return registerCount; 1310 } 1311 1312 public boolean pastEnd() { 1313 return currentRegister >= registerCount; 1314 } 1315 } 1316 1317 private void handleFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction, 1318 RegisterIterator registerIterator) { 1319 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1320 1321 RegisterType arrayType; 1322 RegisterType arrayImmediateElementType; 1323 1324 Item item = instruction.getReferencedItem(); 1325 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1326 1327 ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item); 1328 1329 if (classDef.getClassType().charAt(0) != '[') { 1330 throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() + 1331 "\" with new-array. Use new-instance instead."); 1332 } 1333 1334 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef; 1335 arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef); 1336 arrayImmediateElementType = RegisterType.getRegisterTypeForType( 1337 arrayClassDef.getImmediateElementClass().getClassType()); 1338 String baseElementType = arrayClassDef.getBaseElementClass().getClassType(); 1339 if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') { 1340 throw new ValidationException("Cannot use filled-new-array to create an array of wide values " + 1341 "(long or double)"); 1342 } 1343 1344 do { 1345 int register = registerIterator.getRegister(); 1346 RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register); 1347 assert elementType != null; 1348 1349 if (!elementType.canBeAssignedTo(arrayImmediateElementType)) { 1350 throw new ValidationException("Register v" + Integer.toString(register) + " is of type " + 1351 elementType.toString() + " and is incompatible with the array type " + 1352 arrayType.type.getClassType()); 1353 } 1354 } while (registerIterator.moveNext()); 1355 1356 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); 1357 } 1358 1359 private void handleFilledNewArray(AnalyzedInstruction analyzedInstruction) { 1360 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; 1361 handleFilledNewArrayCommon(analyzedInstruction, new Format35cRegisterIterator(instruction)); 1362 } 1363 1364 private void handleFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) { 1365 RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; 1366 1367 //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually 1368 //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together 1369 if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) { 1370 throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " + 1371 "is larger than the largest allowed register of v65535.", 1372 instruction.getStartRegister(), 1373 instruction.getStartRegister() + instruction.getRegCount() - 1)); 1374 } 1375 1376 handleFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); 1377 } 1378 1379 private void handleFillArrayData(AnalyzedInstruction analyzedInstruction) { 1380 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1381 1382 int register = instruction.getRegisterA(); 1383 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 1384 assert registerType != null; 1385 1386 if (registerType.category == RegisterType.Category.Null) { 1387 return; 1388 } 1389 1390 if (registerType.category != RegisterType.Category.Reference) { 1391 throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " + 1392 "type %s", register, registerType.toString())); 1393 } 1394 1395 assert registerType.type instanceof ClassPath.ArrayClassDef; 1396 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type; 1397 1398 if (arrayClassDef.getArrayDimensions() != 1) { 1399 throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " + 1400 "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); 1401 } 1402 1403 int elementWidth; 1404 switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) { 1405 case 'Z': 1406 case 'B': 1407 elementWidth = 1; 1408 break; 1409 case 'C': 1410 case 'S': 1411 elementWidth = 2; 1412 break; 1413 case 'I': 1414 case 'F': 1415 elementWidth = 4; 1416 break; 1417 case 'J': 1418 case 'D': 1419 elementWidth = 8; 1420 break; 1421 default: 1422 throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " + 1423 "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); 1424 } 1425 1426 1427 int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 1428 int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset; 1429 AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress); 1430 if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) { 1431 throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x", 1432 arrayDataCodeAddress)); 1433 } 1434 1435 ArrayDataPseudoInstruction arrayDataPseudoInstruction = 1436 (ArrayDataPseudoInstruction)arrayDataInstruction.instruction; 1437 1438 if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) { 1439 throw new ValidationException(String.format("The array data at code address 0x%x does not have the " + 1440 "correct element width for array type %s. Expecting element width %d, got element width %d.", 1441 arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth, 1442 arrayDataPseudoInstruction.getElementWidth())); 1443 } 1444 } 1445 1446 private void handleThrow(AnalyzedInstruction analyzedInstruction) { 1447 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1448 1449 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 1450 assert registerType != null; 1451 1452 if (registerType.category == RegisterType.Category.Null) { 1453 return; 1454 } 1455 1456 if (registerType.category != RegisterType.Category.Reference) { 1457 throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d", 1458 registerType.toString(), register)); 1459 } 1460 1461 assert registerType.type != null; 1462 1463 if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) { 1464 throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d", 1465 registerType.type.getClassType(), register)); 1466 } 1467 } 1468 1469 private void handleSwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) { 1470 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1471 int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 1472 1473 getAndCheckSourceRegister(analyzedInstruction, register, Primitive32BitCategories); 1474 1475 int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset; 1476 AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress); 1477 1478 if (switchDataAnalyzedInstruction == null || 1479 switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) { 1480 throw new ValidationException(String.format("There is no %s structure at code address 0x%x", 1481 expectedSwitchDataFormat.name(), switchDataCodeAddress)); 1482 } 1483 } 1484 1485 private void handleFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { 1486 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1487 1488 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); 1489 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories); 1490 1491 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1492 RegisterType.getRegisterType(RegisterType.Category.Byte, null)); 1493 } 1494 1495 private void handleIfEqNe(AnalyzedInstruction analyzedInstruction) { 1496 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1497 1498 RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1499 assert registerType1 != null; 1500 1501 RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1502 assert registerType2 != null; 1503 1504 if (!( 1505 (ReferenceCategories.contains(registerType1.category) && 1506 ReferenceCategories.contains(registerType2.category)) 1507 || 1508 (Primitive32BitCategories.contains(registerType1.category) && 1509 Primitive32BitCategories.contains(registerType2.category)) 1510 )) { 1511 1512 throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " + 1513 "%s. They must both be a reference type or a primitive 32 bit type.", 1514 analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString())); 1515 } 1516 } 1517 1518 private void handleIf(AnalyzedInstruction analyzedInstruction) { 1519 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1520 1521 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); 1522 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); 1523 } 1524 1525 private void handleIfEqzNez(AnalyzedInstruction analyzedInstruction) { 1526 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1527 1528 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), 1529 ReferenceAndPrimitive32BitCategories); 1530 } 1531 1532 private void handleIfz(AnalyzedInstruction analyzedInstruction) { 1533 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1534 1535 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); 1536 } 1537 1538 private void handle32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, 1539 RegisterType.Category instructionCategory) { 1540 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1541 1542 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 1543 1544 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1545 assert arrayRegisterType != null; 1546 1547 if (arrayRegisterType.category != RegisterType.Category.Null) { 1548 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1549 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 1550 analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); 1551 } 1552 1553 assert arrayRegisterType.type != null; 1554 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1555 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 1556 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 1557 } 1558 1559 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1560 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1561 1562 if (arrayClassDef.getArrayDimensions() != 1) { 1563 throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", 1564 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 1565 } 1566 1567 RegisterType arrayBaseType = 1568 RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); 1569 if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { 1570 throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + 1571 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1572 arrayRegisterType.type.getClassType())); 1573 } 1574 } 1575 1576 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1577 RegisterType.getRegisterType(instructionCategory, null)); 1578 } 1579 1580 private void handleAgetWide(AnalyzedInstruction analyzedInstruction) { 1581 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1582 1583 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 1584 1585 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1586 assert arrayRegisterType != null; 1587 1588 if (arrayRegisterType.category != RegisterType.Category.Null) { 1589 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1590 throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", 1591 arrayRegisterType.category.toString())); 1592 } 1593 1594 assert arrayRegisterType.type != null; 1595 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1596 throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", 1597 arrayRegisterType.type.getClassType())); 1598 } 1599 1600 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1601 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1602 1603 if (arrayClassDef.getArrayDimensions() != 1) { 1604 throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s", 1605 arrayRegisterType.type.getClassType())); 1606 } 1607 1608 char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); 1609 if (arrayBaseType == 'J') { 1610 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1611 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 1612 } else if (arrayBaseType == 'D') { 1613 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1614 RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null)); 1615 } else { 1616 throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + 1617 "array type for the instruction.", arrayRegisterType.type.getClassType())); 1618 } 1619 } else { 1620 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1621 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 1622 } 1623 } 1624 1625 private void handleAgetObject(AnalyzedInstruction analyzedInstruction) { 1626 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1627 1628 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 1629 1630 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1631 assert arrayRegisterType != null; 1632 1633 if (arrayRegisterType.category != RegisterType.Category.Null) { 1634 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1635 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 1636 arrayRegisterType.category.toString())); 1637 } 1638 1639 assert arrayRegisterType.type != null; 1640 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1641 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 1642 arrayRegisterType.type.getClassType())); 1643 } 1644 1645 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1646 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1647 1648 ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); 1649 char elementTypePrefix = elementClassDef.getClassType().charAt(0); 1650 if (elementTypePrefix != 'L' && elementTypePrefix != '[') { 1651 throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + 1652 "array type for the instruction.", arrayRegisterType.type.getClassType())); 1653 } 1654 1655 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1656 RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef)); 1657 } else { 1658 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1659 RegisterType.getRegisterType(RegisterType.Category.Null, null)); 1660 } 1661 } 1662 1663 private void handle32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction, 1664 RegisterType.Category instructionCategory) { 1665 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1666 1667 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 1668 1669 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1670 assert sourceRegisterType != null; 1671 RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); 1672 if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { 1673 throw new ValidationException(String.format("Cannot use %s with source register type %s.", 1674 analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); 1675 } 1676 1677 1678 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1679 assert arrayRegisterType != null; 1680 1681 if (arrayRegisterType.category != RegisterType.Category.Null) { 1682 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1683 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 1684 analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); 1685 } 1686 1687 assert arrayRegisterType.type != null; 1688 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1689 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 1690 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 1691 } 1692 1693 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1694 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1695 1696 if (arrayClassDef.getArrayDimensions() != 1) { 1697 throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", 1698 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 1699 } 1700 1701 RegisterType arrayBaseType = 1702 RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); 1703 if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { 1704 throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + 1705 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1706 arrayRegisterType.type.getClassType())); 1707 } 1708 } 1709 } 1710 1711 private void handleAputWide(AnalyzedInstruction analyzedInstruction) { 1712 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1713 1714 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 1715 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); 1716 1717 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1718 assert arrayRegisterType != null; 1719 1720 if (arrayRegisterType.category != RegisterType.Category.Null) { 1721 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1722 throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", 1723 arrayRegisterType.category.toString())); 1724 } 1725 1726 assert arrayRegisterType.type != null; 1727 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1728 throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", 1729 arrayRegisterType.type.getClassType())); 1730 } 1731 1732 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1733 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1734 1735 if (arrayClassDef.getArrayDimensions() != 1) { 1736 throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s", 1737 arrayRegisterType.type.getClassType())); 1738 } 1739 1740 char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); 1741 if (arrayBaseType != 'J' && arrayBaseType != 'D') { 1742 throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " + 1743 "array type for the instruction.", arrayRegisterType.type.getClassType())); 1744 } 1745 } 1746 } 1747 1748 private void handleAputObject(AnalyzedInstruction analyzedInstruction) { 1749 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1750 1751 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 1752 1753 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1754 assert sourceRegisterType != null; 1755 1756 //TODO: ensure sourceRegisterType is a Reference type? 1757 1758 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1759 assert arrayRegisterType != null; 1760 1761 if (arrayRegisterType.category != RegisterType.Category.Null) { 1762 //don't check the source type against the array type, just make sure it is an array of reference types 1763 1764 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1765 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 1766 arrayRegisterType.category.toString())); 1767 } 1768 1769 assert arrayRegisterType.type != null; 1770 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1771 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 1772 arrayRegisterType.type.getClassType())); 1773 } 1774 1775 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1776 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1777 1778 ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); 1779 char elementTypePrefix = elementClassDef.getClassType().charAt(0); 1780 if (elementTypePrefix != 'L' && elementTypePrefix != '[') { 1781 throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + 1782 "array type for the instruction.", arrayRegisterType.type.getClassType())); 1783 } 1784 } 1785 } 1786 1787 private void handle32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, 1788 RegisterType.Category instructionCategory) { 1789 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1790 1791 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 1792 ReferenceCategories); 1793 1794 //TODO: check access 1795 //TODO: allow an uninitialized "this" reference, if the current method is an <init> method 1796 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 1797 assert referencedItem instanceof FieldIdItem; 1798 FieldIdItem field = (FieldIdItem)referencedItem; 1799 1800 if (objectRegisterType.category != RegisterType.Category.Null && 1801 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 1802 throw new ValidationException(String.format("Cannot access field %s through type %s", 1803 field.getFieldString(), objectRegisterType.type.getClassType())); 1804 } 1805 1806 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 1807 1808 if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { 1809 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 1810 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1811 field.getFieldString())); 1812 } 1813 1814 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1815 RegisterType.getRegisterType(instructionCategory, null)); 1816 } 1817 1818 private void handleIgetWide(AnalyzedInstruction analyzedInstruction) { 1819 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1820 1821 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 1822 ReferenceCategories); 1823 1824 //TODO: check access 1825 //TODO: allow an uninitialized "this" reference, if the current method is an <init> method 1826 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 1827 assert referencedItem instanceof FieldIdItem; 1828 FieldIdItem field = (FieldIdItem)referencedItem; 1829 1830 if (objectRegisterType.category != RegisterType.Category.Null && 1831 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 1832 throw new ValidationException(String.format("Cannot access field %s through type %s", 1833 field.getFieldString(), objectRegisterType.type.getClassType())); 1834 } 1835 1836 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 1837 1838 if (!WideLowCategories.contains(fieldType.category)) { 1839 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 1840 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1841 field.getFieldString())); 1842 } 1843 1844 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); 1845 } 1846 1847 private void handleIgetObject(AnalyzedInstruction analyzedInstruction) { 1848 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1849 1850 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 1851 ReferenceCategories); 1852 1853 //TODO: check access 1854 //TODO: allow an uninitialized "this" reference, if the current method is an <init> method 1855 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 1856 assert referencedItem instanceof FieldIdItem; 1857 FieldIdItem field = (FieldIdItem)referencedItem; 1858 1859 if (objectRegisterType.category != RegisterType.Category.Null && 1860 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 1861 throw new ValidationException(String.format("Cannot access field %s through type %s", 1862 field.getFieldString(), objectRegisterType.type.getClassType())); 1863 } 1864 1865 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 1866 1867 if (fieldType.category != RegisterType.Category.Reference) { 1868 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 1869 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1870 field.getFieldString())); 1871 } 1872 1873 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); 1874 } 1875 1876 private void handle32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction, 1877 RegisterType.Category instructionCategory) { 1878 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1879 1880 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 1881 ReferenceCategories); 1882 1883 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1884 assert sourceRegisterType != null; 1885 1886 //per CodeVerify.c in dalvik: 1887 //java generates synthetic functions that write byte values into boolean fields 1888 if (sourceRegisterType.category == RegisterType.Category.Byte && 1889 instructionCategory == RegisterType.Category.Boolean) { 1890 1891 sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null); 1892 } 1893 1894 RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); 1895 if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { 1896 throw new ValidationException(String.format("Cannot use %s with source register type %s.", 1897 analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); 1898 } 1899 1900 1901 //TODO: check access 1902 //TODO: allow an uninitialized "this" reference, if the current method is an <init> method 1903 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 1904 assert referencedItem instanceof FieldIdItem; 1905 FieldIdItem field = (FieldIdItem)referencedItem; 1906 1907 if (objectRegisterType.category != RegisterType.Category.Null && 1908 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 1909 throw new ValidationException(String.format("Cannot access field %s through type %s", 1910 field.getFieldString(), objectRegisterType.type.getClassType())); 1911 } 1912 1913 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 1914 1915 if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { 1916 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 1917 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1918 field.getFieldString())); 1919 } 1920 } 1921 1922 private void handleIputWide(AnalyzedInstruction analyzedInstruction) { 1923 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1924 1925 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 1926 ReferenceCategories); 1927 1928 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); 1929 1930 //TODO: check access 1931 //TODO: allow an uninitialized "this" reference, if the current method is an <init> method 1932 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 1933 assert referencedItem instanceof FieldIdItem; 1934 FieldIdItem field = (FieldIdItem)referencedItem; 1935 1936 if (objectRegisterType.category != RegisterType.Category.Null && 1937 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 1938 throw new ValidationException(String.format("Cannot access field %s through type %s", 1939 field.getFieldString(), objectRegisterType.type.getClassType())); 1940 } 1941 1942 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 1943 1944 if (!WideLowCategories.contains(fieldType.category)) { 1945 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 1946 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1947 field.getFieldString())); 1948 } 1949 } 1950 1951 private void handleIputObject(AnalyzedInstruction analyzedInstruction) { 1952 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1953 1954 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 1955 ReferenceCategories); 1956 1957 RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 1958 ReferenceCategories); 1959 1960 //TODO: check access 1961 //TODO: allow an uninitialized "this" reference, if the current method is an <init> method 1962 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 1963 assert referencedItem instanceof FieldIdItem; 1964 FieldIdItem field = (FieldIdItem)referencedItem; 1965 1966 if (objectRegisterType.category != RegisterType.Category.Null && 1967 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 1968 throw new ValidationException(String.format("Cannot access field %s through type %s", 1969 field.getFieldString(), objectRegisterType.type.getClassType())); 1970 } 1971 1972 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 1973 1974 if (fieldType.category != RegisterType.Category.Reference) { 1975 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 1976 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1977 field.getFieldString())); 1978 } 1979 1980 if (sourceRegisterType.category != RegisterType.Category.Null && 1981 !fieldType.type.isInterface() && 1982 !sourceRegisterType.type.extendsClass(fieldType.type)) { 1983 1984 throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s", 1985 sourceRegisterType.type.getClassType(), fieldType.type.getClassType())); 1986 } 1987 } 1988 1989 private void handle32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, 1990 RegisterType.Category instructionCategory) { 1991 //TODO: check access 1992 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 1993 assert referencedItem instanceof FieldIdItem; 1994 FieldIdItem field = (FieldIdItem)referencedItem; 1995 1996 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 1997 1998 if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { 1999 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2000 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2001 field.getFieldString())); 2002 } 2003 2004 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2005 RegisterType.getRegisterType(instructionCategory, null)); 2006 } 2007 2008 private void handleSgetWide(AnalyzedInstruction analyzedInstruction) { 2009 //TODO: check access 2010 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2011 assert referencedItem instanceof FieldIdItem; 2012 FieldIdItem field = (FieldIdItem)referencedItem; 2013 2014 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2015 2016 2017 if (fieldType.category != RegisterType.Category.LongLo && 2018 fieldType.category != RegisterType.Category.DoubleLo) { 2019 2020 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2021 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2022 field.getFieldString())); 2023 } 2024 2025 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); 2026 } 2027 2028 private void handleSgetObject(AnalyzedInstruction analyzedInstruction) { 2029 //TODO: check access 2030 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2031 assert referencedItem instanceof FieldIdItem; 2032 FieldIdItem field = (FieldIdItem)referencedItem; 2033 2034 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2035 2036 if (fieldType.category != RegisterType.Category.Reference) { 2037 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2038 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2039 field.getFieldString())); 2040 } 2041 2042 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); 2043 } 2044 2045 private void handle32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction, 2046 RegisterType.Category instructionCategory) { 2047 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2048 2049 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 2050 assert sourceRegisterType != null; 2051 2052 //per CodeVerify.c in dalvik: 2053 //java generates synthetic functions that write byte values into boolean fields 2054 if (sourceRegisterType.category == RegisterType.Category.Byte && 2055 instructionCategory == RegisterType.Category.Boolean) { 2056 2057 sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null); 2058 } 2059 2060 RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); 2061 if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { 2062 throw new ValidationException(String.format("Cannot use %s with source register type %s.", 2063 analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); 2064 } 2065 2066 //TODO: check access 2067 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2068 assert referencedItem instanceof FieldIdItem; 2069 FieldIdItem field = (FieldIdItem)referencedItem; 2070 2071 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2072 2073 if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { 2074 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2075 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2076 field.getFieldString())); 2077 } 2078 } 2079 2080 private void handleSputWide(AnalyzedInstruction analyzedInstruction) { 2081 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2082 2083 2084 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); 2085 2086 //TODO: check access 2087 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2088 assert referencedItem instanceof FieldIdItem; 2089 FieldIdItem field = (FieldIdItem)referencedItem; 2090 2091 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2092 2093 if (!WideLowCategories.contains(fieldType.category)) { 2094 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2095 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2096 field.getFieldString())); 2097 } 2098 2099 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); 2100 } 2101 2102 private void handleSputObject(AnalyzedInstruction analyzedInstruction) { 2103 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2104 2105 RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), 2106 ReferenceCategories); 2107 2108 //TODO: check access 2109 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2110 assert referencedItem instanceof FieldIdItem; 2111 FieldIdItem field = (FieldIdItem)referencedItem; 2112 2113 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2114 2115 if (fieldType.category != RegisterType.Category.Reference) { 2116 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2117 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2118 field.getFieldString())); 2119 } 2120 2121 if (sourceRegisterType.category != RegisterType.Category.Null && 2122 !fieldType.type.isInterface() && 2123 !sourceRegisterType.type.extendsClass(fieldType.type)) { 2124 2125 throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s", 2126 sourceRegisterType.type.getClassType(), fieldType.type.getClassType())); 2127 } 2128 } 2129 2130 private void handleInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) { 2131 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; 2132 handleInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction)); 2133 } 2134 2135 private void handleInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) { 2136 RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; 2137 handleInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction)); 2138 } 2139 2140 private static final int INVOKE_VIRTUAL = 0x01; 2141 private static final int INVOKE_SUPER = 0x02; 2142 private static final int INVOKE_DIRECT = 0x04; 2143 private static final int INVOKE_INTERFACE = 0x08; 2144 private static final int INVOKE_STATIC = 0x10; 2145 2146 private void handleInvokeCommon(AnalyzedInstruction analyzedInstruction, boolean isRange, int invokeType, 2147 RegisterIterator registers) { 2148 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 2149 2150 //TODO: check access 2151 //TODO: allow uninitialized reference if this in an <init> method 2152 2153 Item item = instruction.getReferencedItem(); 2154 assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; 2155 MethodIdItem methodIdItem = (MethodIdItem)item; 2156 2157 TypeIdItem methodClass = methodIdItem.getContainingClass(); 2158 boolean isInit = false; 2159 2160 if (methodIdItem.getMethodName().getStringValue().charAt(0) == '<') { 2161 if ((invokeType & INVOKE_DIRECT) != 0) { 2162 isInit = true; 2163 } else { 2164 throw new ValidationException(String.format("Cannot call constructor %s with %s", 2165 methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name)); 2166 } 2167 } 2168 2169 ClassPath.ClassDef methodClassDef = ClassPath.getClassDef(methodClass); 2170 if ((invokeType & INVOKE_INTERFACE) != 0) { 2171 if (!methodClassDef.isInterface()) { 2172 throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an interface " + 2173 "class.", methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name, 2174 methodClassDef.getClassType())); 2175 } 2176 } else { 2177 if (methodClassDef.isInterface()) { 2178 throw new ValidationException(String.format("Cannot call method %s with %s. %s is an interface class." + 2179 " Use invoke-interface or invoke-interface/range instead.", methodIdItem.getMethodString(), 2180 analyzedInstruction.instruction.opcode.name, methodClassDef.getClassType())); 2181 } 2182 } 2183 2184 if ((invokeType & INVOKE_SUPER) != 0) { 2185 if (methodClassDef.getSuperclass() == null) { 2186 throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass", 2187 methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name, 2188 methodClassDef.getSuperclass().getClassType())); 2189 } 2190 2191 if (!methodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getMethodString())) { 2192 throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" + 2193 "no such method", methodIdItem.getMethodString(), 2194 analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType())); 2195 } 2196 } 2197 2198 assert isRange || registers.getCount() <= 5; 2199 2200 TypeListItem typeListItem = methodIdItem.getPrototype().getParameters(); 2201 int methodParameterRegisterCount; 2202 if (typeListItem == null) { 2203 methodParameterRegisterCount = 0; 2204 } else { 2205 methodParameterRegisterCount = typeListItem.getRegisterCount(); 2206 } 2207 2208 if ((invokeType & INVOKE_STATIC) == 0) { 2209 methodParameterRegisterCount++; 2210 } 2211 2212 if (methodParameterRegisterCount != registers.getCount()) { 2213 throw new ValidationException(String.format("The number of registers does not match the number of " + 2214 "parameters for method %s. Expecting %d registers, got %d.", methodIdItem.getMethodString(), 2215 methodParameterRegisterCount + 1, registers.getCount())); 2216 } 2217 2218 RegisterType objectRegisterType = null; 2219 int objectRegister = 0; 2220 if ((invokeType & INVOKE_STATIC) == 0) { 2221 objectRegister = registers.getRegister(); 2222 registers.moveNext(); 2223 2224 objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister); 2225 assert objectRegisterType != null; 2226 if (objectRegisterType.category == RegisterType.Category.UninitRef) { 2227 if (!isInit) { 2228 throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " + 2229 "reference type %s", methodIdItem.getMethodString(), 2230 objectRegisterType.type.getClassType())); 2231 } 2232 } else if (objectRegisterType.category == RegisterType.Category.Reference) { 2233 if (isInit) { 2234 throw new ValidationException(String.format("Cannot invoke %s on initialized reference type %s", 2235 methodIdItem.getMethodString(), objectRegisterType.type.getClassType())); 2236 } 2237 } else if (objectRegisterType.category == RegisterType.Category.Null) { 2238 if (isInit) { 2239 throw new ValidationException(String.format("Cannot invoke %s on a null reference", 2240 methodIdItem.getMethodString())); 2241 } 2242 } 2243 else { 2244 throw new ValidationException(String.format("Cannot invoke %s on non-reference type %s", 2245 methodIdItem.getMethodString(), objectRegisterType.toString())); 2246 } 2247 2248 if (isInit) { 2249 if (objectRegisterType.type == methodClassDef.getSuperclass()) { 2250 if (!encodedMethod.method.getMethodName().equals("<init>")) { 2251 throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " + 2252 "match the method type exactly", methodIdItem.getMethodString(), 2253 objectRegisterType.type.getClassType())); 2254 } 2255 } 2256 } 2257 2258 2259 if ((invokeType & INVOKE_INTERFACE) == 0 && !objectRegisterType.type.extendsClass(methodClassDef)) { 2260 throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " + 2261 "does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(), 2262 methodClassDef.getClassType())); 2263 } 2264 } 2265 2266 List<TypeIdItem> parameterTypes = typeListItem.getTypes(); 2267 int parameterTypeIndex = 0; 2268 while (!registers.pastEnd()) { 2269 assert parameterTypeIndex < parameterTypes.size(); 2270 RegisterType parameterType = 2271 RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex)); 2272 2273 int register = registers.getRegister(); 2274 2275 RegisterType parameterRegisterType; 2276 if (WideLowCategories.contains(parameterType.category)) { 2277 parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories); 2278 2279 if (!registers.moveNext()) { 2280 throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d", 2281 parameterTypeIndex+1)); 2282 } 2283 int nextRegister = registers.getRegister(); 2284 2285 if (nextRegister != register + 1) { 2286 throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " + 2287 "must be consecutive.", register, nextRegister)); 2288 } 2289 } else { 2290 parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register); 2291 } 2292 2293 assert parameterRegisterType != null; 2294 2295 if (!parameterRegisterType.canBeAssignedTo(parameterType)) { 2296 throw new ValidationException( 2297 String.format("Invalid register type %s for parameter %d %s.", 2298 parameterRegisterType.toString(), parameterTypeIndex+1, 2299 parameterType.toString())); 2300 } 2301 2302 parameterTypeIndex++; 2303 registers.moveNext(); 2304 } 2305 2306 2307 //TODO: need to ensure the "this" register is initialized, in a constructor method 2308 if (isInit) { 2309 setRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister, 2310 RegisterType.getRegisterType(RegisterType.Category.Reference, objectRegisterType.type)); 2311 2312 for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) { 2313 RegisterType postInstructionRegisterType = analyzedInstruction.postRegisterMap[i]; 2314 if (postInstructionRegisterType.category == RegisterType.Category.Unknown) { 2315 RegisterType preInstructionRegisterType = 2316 analyzedInstruction.getMergedRegisterTypeFromPredecessors(i); 2317 2318 if (preInstructionRegisterType.category == RegisterType.Category.UninitRef) { 2319 RegisterType registerType = null; 2320 if (preInstructionRegisterType == objectRegisterType) { 2321 registerType = analyzedInstruction.postRegisterMap[objectRegister]; 2322 } else { 2323 registerType = preInstructionRegisterType; 2324 } 2325 2326 setRegisterTypeAndPropagateChanges(analyzedInstruction, i, registerType); 2327 } 2328 } 2329 } 2330 } 2331 } 2332 2333 private void handleUnaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSourceCategories, 2334 RegisterType.Category destRegisterCategory) { 2335 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2336 2337 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSourceCategories); 2338 2339 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2340 RegisterType.getRegisterType(destRegisterCategory, null)); 2341 } 2342 2343 private void handleBinaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories, 2344 EnumSet validSource2Categories, RegisterType.Category destRegisterCategory, 2345 boolean checkForBoolean) { 2346 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2347 2348 RegisterType source1RegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 2349 validSource1Categories); 2350 RegisterType source2RegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), 2351 validSource2Categories); 2352 2353 if (checkForBoolean) { 2354 if (BooleanCategories.contains(source1RegisterType.category) && 2355 BooleanCategories.contains(source2RegisterType.category)) { 2356 2357 destRegisterCategory = RegisterType.Category.Boolean; 2358 } 2359 } 2360 2361 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2362 RegisterType.getRegisterType(destRegisterCategory, null)); 2363 } 2364 2365 private void handleBinary2AddrOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories, 2366 EnumSet validSource2Categories, RegisterType.Category destRegisterCategory, 2367 boolean checkForBoolean) { 2368 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2369 2370 RegisterType source1RegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), 2371 validSource1Categories); 2372 RegisterType source2RegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 2373 validSource2Categories); 2374 2375 if (checkForBoolean) { 2376 if (BooleanCategories.contains(source1RegisterType.category) && 2377 BooleanCategories.contains(source2RegisterType.category)) { 2378 2379 destRegisterCategory = RegisterType.Category.Boolean; 2380 } 2381 } 2382 2383 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2384 RegisterType.getRegisterType(destRegisterCategory, null)); 2385 } 2386 2387 private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory, 2388 RegisterType.Category instructionCategory) { 2389 if (arrayFieldCategory == instructionCategory) { 2390 return true; 2391 } 2392 2393 if ((arrayFieldCategory == RegisterType.Category.Integer && 2394 instructionCategory == RegisterType.Category.Float) || 2395 (arrayFieldCategory == RegisterType.Category.Float && 2396 instructionCategory == RegisterType.Category.Integer)) { 2397 return true; 2398 } 2399 return false; 2400 } 2401 2402 private static RegisterType getAndCheckSourceRegister(AnalyzedInstruction analyzedInstruction, int registerNumber, 2403 EnumSet validCategories) { 2404 assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length; 2405 2406 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber); 2407 assert registerType != null; 2408 2409 checkRegister(registerType, registerNumber, validCategories); 2410 2411 if (validCategories == WideLowCategories) { 2412 checkRegister(registerType, registerNumber, WideLowCategories); 2413 checkWidePair(registerNumber, analyzedInstruction); 2414 2415 RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1); 2416 assert secondRegisterType != null; 2417 checkRegister(secondRegisterType, registerNumber+1, WideHighCategories); 2418 } 2419 2420 return registerType; 2421 } 2422 2423 private static void checkRegister(RegisterType registerType, int registerNumber, EnumSet validCategories) { 2424 if (!validCategories.contains(registerType.category)) { 2425 //TODO: add expected categories to error message 2426 throw new ValidationException(String.format("Invalid register type for register v%d. Expecting one of: " + 2427 "but got %s", registerNumber, registerType.toString())); 2428 } 2429 } 2430 2431 private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) { 2432 if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) { 2433 throw new ValidationException(String.format("v%d is the last register and not a valid wide register " + 2434 "pair.", registerNumber)); 2435 } 2436 } 2437} 2438