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