• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013, Google Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 package org.jf.dexlib2.analysis;
33 
34 import com.google.common.base.Objects;
35 import com.google.common.collect.Lists;
36 import com.google.common.collect.Maps;
37 import org.jf.dexlib2.Opcode;
38 import org.jf.dexlib2.iface.instruction.*;
39 import org.jf.dexlib2.iface.instruction.formats.Instruction22c;
40 import org.jf.dexlib2.iface.reference.MethodReference;
41 import org.jf.dexlib2.iface.reference.Reference;
42 import org.jf.dexlib2.iface.reference.TypeReference;
43 import org.jf.util.ExceptionWithContext;
44 
45 import javax.annotation.Nonnull;
46 import javax.annotation.Nullable;
47 import java.util.*;
48 
49 public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
50     /**
51      * The MethodAnalyzer containing this instruction
52      */
53     @Nonnull
54     protected final MethodAnalyzer methodAnalyzer;
55 
56     /**
57      * The actual instruction
58      */
59     @Nonnull
60     protected Instruction instruction;
61 
62     /**
63      * The index of the instruction, where the first instruction in the method is at index 0, and so on
64      */
65     protected final int instructionIndex;
66 
67     /**
68      * Instructions that can pass on execution to this one during normal execution
69      */
70     @Nonnull
71     protected final TreeSet<AnalyzedInstruction> predecessors = new TreeSet<AnalyzedInstruction>();
72 
73     /**
74      * Instructions that can execution could pass on to next during normal execution
75      */
76     @Nonnull
77     protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>();
78 
79     /**
80      * This contains the register types *before* the instruction has executed
81      */
82     @Nonnull
83     protected final RegisterType[] preRegisterMap;
84 
85     /**
86      * This contains the register types *after* the instruction has executed
87      */
88     @Nonnull
89     protected final RegisterType[] postRegisterMap;
90 
91     /**
92      * This contains optional register type overrides for register types from predecessors
93      */
94     @Nullable
95     protected Map<PredecessorOverrideKey, RegisterType> predecessorRegisterOverrides = null;
96 
97     /**
98      * When deodexing, we might need to deodex this instruction multiple times, when we merge in new register
99      * information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again
100      */
101     protected final Instruction originalInstruction;
102 
AnalyzedInstruction(@onnull MethodAnalyzer methodAnalyzer, @Nonnull Instruction instruction, int instructionIndex, int registerCount)103     public AnalyzedInstruction(@Nonnull MethodAnalyzer methodAnalyzer, @Nonnull Instruction instruction,
104                                int instructionIndex, int registerCount) {
105         this.methodAnalyzer = methodAnalyzer;
106         this.instruction = instruction;
107         this.originalInstruction = instruction;
108         this.instructionIndex = instructionIndex;
109         this.postRegisterMap = new RegisterType[registerCount];
110         this.preRegisterMap = new RegisterType[registerCount];
111         RegisterType unknown = RegisterType.getRegisterType(RegisterType.UNKNOWN, null);
112         for (int i=0; i<registerCount; i++) {
113             preRegisterMap[i] = unknown;
114             postRegisterMap[i] = unknown;
115         }
116     }
117 
getInstructionIndex()118     public int getInstructionIndex() {
119         return instructionIndex;
120     }
121 
getPredecessorCount()122     public int getPredecessorCount() {
123         return predecessors.size();
124     }
125 
getPredecessors()126     public SortedSet<AnalyzedInstruction> getPredecessors() {
127         return Collections.unmodifiableSortedSet(predecessors);
128     }
129 
getPredecessorRegisterType(@onnull AnalyzedInstruction predecessor, int registerNumber)130     public RegisterType getPredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber) {
131         if (predecessorRegisterOverrides != null) {
132             RegisterType override = predecessorRegisterOverrides.get(
133                     new PredecessorOverrideKey(predecessor, registerNumber));
134             if (override != null) {
135                 return override;
136             }
137         }
138         return predecessor.postRegisterMap[registerNumber];
139     }
140 
addPredecessor(AnalyzedInstruction predecessor)141     protected boolean addPredecessor(AnalyzedInstruction predecessor) {
142         return predecessors.add(predecessor);
143     }
144 
addSuccessor(AnalyzedInstruction successor)145     protected void addSuccessor(AnalyzedInstruction successor) {
146         successors.add(successor);
147     }
148 
setDeodexedInstruction(Instruction instruction)149     protected void setDeodexedInstruction(Instruction instruction) {
150         assert originalInstruction.getOpcode().odexOnly();
151         this.instruction = instruction;
152     }
153 
restoreOdexedInstruction()154     protected void restoreOdexedInstruction() {
155         assert originalInstruction.getOpcode().odexOnly();
156         instruction = originalInstruction;
157     }
158 
159     @Nonnull
getSuccessors()160     public List<AnalyzedInstruction> getSuccessors() {
161         return Collections.unmodifiableList(successors);
162     }
163 
164     @Nonnull
getInstruction()165     public Instruction getInstruction() {
166         return instruction;
167     }
168 
169     @Nonnull
getOriginalInstruction()170     public Instruction getOriginalInstruction() {
171         return originalInstruction;
172     }
173 
174     /**
175      * Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction
176      * that can be the first successfully executed instruction in the method. The first instruction is always a
177      * beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then
178      * the first instruction of any exception handler for that try block is also a beginning instruction. And likewise,
179      * if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the
180      * corresponding exception handler is a beginning instruction, etc.
181      *
182      * To determine this, we simply check if the first predecessor is the fake "StartOfMethod" instruction, which has
183      * an instruction index of -1.
184      * @return a boolean value indicating whether this instruction is a beginning instruction
185      */
isBeginningInstruction()186     public boolean isBeginningInstruction() {
187         //if this instruction has no predecessors, it is either the fake "StartOfMethod" instruction or it is an
188         //unreachable instruction.
189         if (predecessors.size() == 0) {
190             return false;
191         }
192         return predecessors.first().instructionIndex == -1;
193     }
194 
195     /*
196      * Merges the given register type into the specified pre-instruction register, and also sets the post-instruction
197      * register type accordingly if it isn't a destination register for this instruction
198      * @param registerNumber Which register to set
199      * @param registerType The register type
200      * @returns true If the post-instruction register type was changed. This might be false if either the specified
201      * register is a destination register for this instruction, or if the pre-instruction register type didn't change
202      * after merging in the given register type
203      */
mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions, boolean override)204     protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions,
205                                     boolean override) {
206         assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
207         assert registerType != null;
208 
209         RegisterType oldRegisterType = preRegisterMap[registerNumber];
210 
211         RegisterType mergedRegisterType;
212         if (override) {
213             mergedRegisterType = getMergedPreRegisterTypeFromPredecessors(registerNumber);
214         } else {
215             mergedRegisterType = oldRegisterType.merge(registerType);
216         }
217 
218         if (mergedRegisterType.equals(oldRegisterType)) {
219             return false;
220         }
221 
222         preRegisterMap[registerNumber] = mergedRegisterType;
223         verifiedInstructions.clear(instructionIndex);
224 
225         if (!setsRegister(registerNumber)) {
226             postRegisterMap[registerNumber] = mergedRegisterType;
227             return true;
228         }
229 
230         return false;
231     }
232 
233     /**
234      * Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the
235      * given register. Any dead, unreachable, or odexed predecessor is ignored. This takes into account any overridden
236      * predecessor register types
237      *
238      * @param registerNumber the register number
239      * @return The register type resulting from merging the post-instruction register types from all predecessors
240      */
241     @Nonnull
242     protected RegisterType getMergedPreRegisterTypeFromPredecessors(int registerNumber) {
243         RegisterType mergedRegisterType = null;
244         for (AnalyzedInstruction predecessor: predecessors) {
245             RegisterType predecessorRegisterType = getPredecessorRegisterType(predecessor, registerNumber);
246             if (predecessorRegisterType != null) {
247                 if (mergedRegisterType == null) {
248                     mergedRegisterType = predecessorRegisterType;
249                 } else {
250                     mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType);
251                 }
252             }
253         }
254         if (mergedRegisterType == null) {
255             // This is a start-of-method or unreachable instruction.
256             throw new IllegalStateException();
257         }
258         return mergedRegisterType;
259     }
260     /**
261      * Sets the "post-instruction" register type as indicated.
262      * @param registerNumber Which register to set
263      * @param registerType The "post-instruction" register type
264      * @return true if the given register type is different than the existing post-instruction register type
265      */
266     protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
267         assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
268         assert registerType != null;
269 
270         RegisterType oldRegisterType = postRegisterMap[registerNumber];
271         if (oldRegisterType.equals(registerType)) {
272             return false;
273         }
274 
275         postRegisterMap[registerNumber] = registerType;
276         return true;
277     }
278 
279     /**
280      * Adds an override for a register type from a predecessor.
281      *
282      * This is used to set the register type for only one branch from a conditional jump.
283      *
284      * @param predecessor Which predecessor is being overridden
285      * @param registerNumber The register number of the register being overridden
286      * @param registerType The overridden register type
287      * @param verifiedInstructions A bit vector of instructions that have been verified
288      *
289      * @return true if the post-instruction register type for this instruction changed as a result of this override
290      */
291     protected boolean overridePredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber,
292                                                       @Nonnull RegisterType registerType, BitSet verifiedInstructions) {
293         if (predecessorRegisterOverrides == null) {
294             predecessorRegisterOverrides = Maps.newHashMap();
295         }
296         predecessorRegisterOverrides.put(new PredecessorOverrideKey(predecessor, registerNumber), registerType);
297 
298         RegisterType mergedType = getMergedPreRegisterTypeFromPredecessors(registerNumber);
299 
300         if (preRegisterMap[registerNumber].equals(mergedType)) {
301             return false;
302         }
303 
304         preRegisterMap[registerNumber] = mergedType;
305         verifiedInstructions.clear(instructionIndex);
306 
307         if (!setsRegister(registerNumber)) {
308             if (!postRegisterMap[registerNumber].equals(mergedType)) {
309                 postRegisterMap[registerNumber] = mergedType;
310                 return true;
311             }
312         }
313 
314         return false;
315     }
316 
317     public boolean isInvokeInit() {
318         if (!instruction.getOpcode().canInitializeReference()) {
319             return false;
320         }
321 
322         ReferenceInstruction instruction = (ReferenceInstruction)this.instruction;
323 
324         Reference reference = instruction.getReference();
325         if (reference instanceof MethodReference) {
326             return ((MethodReference)reference).getName().equals("<init>");
327         }
328 
329         return false;
330     }
331 
332     /**
333      * Determines if this instruction sets the given register, or alters its type
334      *
335      * @param registerNumber The register to check
336      * @return true if this instruction sets the given register or alters its type
337      */
338     public boolean setsRegister(int registerNumber) {
339         // This method could be implemented by calling getSetRegisters and checking if registerNumber is in the result
340         // However, this is a frequently called method, and this is a more efficient implementation, because it doesn't
341         // allocate a new list, and it can potentially exit earlier
342 
343         if (isInvokeInit()) {
344             // When constructing a new object, the register type will be an uninitialized reference after the
345             // new-instance instruction, but becomes an initialized reference once the <init> method is called. So even
346             // though invoke instructions don't normally change any registers, calling an <init> method will change the
347             // type of its object register. If the uninitialized reference has been copied to other registers, they will
348             // be initialized as well, so we need to check for that too
349             int destinationRegister;
350             if (instruction instanceof FiveRegisterInstruction) {
351                 assert ((FiveRegisterInstruction)instruction).getRegisterCount() > 0;
352                 destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterC();
353             } else {
354                 assert instruction instanceof RegisterRangeInstruction;
355                 RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
rangeInstruction.getRegisterCount()356                 assert rangeInstruction.getRegisterCount() > 0;
357                 destinationRegister = rangeInstruction.getStartRegister();
358             }
359 
360             RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(destinationRegister);
361             if (preInstructionDestRegisterType.category == RegisterType.UNKNOWN) {
362                 // We never let an uninitialized reference propagate past an invoke-init if the object register type is
363                 // unknown This is because the uninitialized reference may be an alias to the reference being
364                 // initialized, but we can't know that until the object register's type is known
365                 RegisterType preInstructionRegisterType = getPreInstructionRegisterType(registerNumber);
366                 if (preInstructionRegisterType.category == RegisterType.UNINIT_REF ||
367                         preInstructionRegisterType.category == RegisterType.UNINIT_THIS) {
368                     return true;
369                 }
370             }
371 
372             if (preInstructionDestRegisterType.category != RegisterType.UNINIT_REF &&
373                     preInstructionDestRegisterType.category != RegisterType.UNINIT_THIS) {
374                 return false;
375             }
376 
377             if (registerNumber == destinationRegister) {
378                 return true;
379             }
380 
381             //check if the uninit ref has been copied to another register
382             return preInstructionDestRegisterType.equals(getPreInstructionRegisterType(registerNumber));
383         }
384 
385         // On art, the optimizer will often nop out a check-cast instruction after an instance-of instruction.
386         // Normally, check-cast is where the register type actually changes.
387         // In order to correctly handle this case, we have to propagate the narrowed register type to the appropriate
388         // branch of the following if-eqz/if-nez
389         if (instructionIndex > 0 &&
390                 methodAnalyzer.getClassPath().isArt() &&
391                 getPredecessorCount() == 1 &&
392                 (instruction.getOpcode() == Opcode.IF_EQZ || instruction.getOpcode() == Opcode.IF_NEZ)) {
393 
394             AnalyzedInstruction prevInstruction = predecessors.first();
395             if (prevInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF &&
396                     MethodAnalyzer.canPropagateTypeAfterInstanceOf(
397                             prevInstruction, this, methodAnalyzer.getClassPath())) {
398                 Instruction22c instanceOfInstruction = (Instruction22c)prevInstruction.instruction;
399 
400                 if (registerNumber == instanceOfInstruction.getRegisterB()) {
401                     return true;
402                 }
403 
404                 // Additionally, there may be a move instruction just before the instance-of, in order to put the value
405                 // into a register that is addressable by the instance-of. In this case, we also need to propagate the
406                 // new register type for the original register that the value was moved from.
407                 // In some cases, the instance-of may have multiple predecessors. In this case, we should only do the
408                 // propagation if all predecessors are move-object instructions for the same source register
409                 // TODO: do we need to do some sort of additional check that these multiple move-object predecessors actually refer to the same value?
410                 if (instructionIndex > 1) {
411                     int originalSourceRegister = -1;
412 
413                     RegisterType newType = null;
414 
415                     for (AnalyzedInstruction prevPrevAnalyzedInstruction : prevInstruction.predecessors) {
416                         Opcode opcode = prevPrevAnalyzedInstruction.instruction.getOpcode();
417                         if (opcode == Opcode.MOVE_OBJECT || opcode == Opcode.MOVE_OBJECT_16 ||
418                                 opcode == Opcode.MOVE_OBJECT_FROM16) {
419                             TwoRegisterInstruction moveInstruction =
420                                     ((TwoRegisterInstruction)prevPrevAnalyzedInstruction.instruction);
421                             RegisterType originalType =
422                                     prevPrevAnalyzedInstruction.getPostInstructionRegisterType(
423                                             moveInstruction.getRegisterB());
424                             if (moveInstruction.getRegisterA() != instanceOfInstruction.getRegisterB()) {
425                                 originalSourceRegister = -1;
426                                 break;
427                             }
428                             if (originalType.type == null) {
429                                 originalSourceRegister = -1;
430                                 break;
431                             }
432 
433                             if (newType == null) {
434                                 newType = RegisterType.getRegisterType(methodAnalyzer.getClassPath(),
435                                         (TypeReference)instanceOfInstruction.getReference());
436                             }
437 
438                             if (MethodAnalyzer.isNotWideningConversion(originalType, newType)) {
439                                 if (originalSourceRegister != -1) {
440                                     if (originalSourceRegister != moveInstruction.getRegisterB()) {
441                                         originalSourceRegister = -1;
442                                         break;
443                                     }
444                                 } else {
445                                     originalSourceRegister = moveInstruction.getRegisterB();
446                                 }
447                             }
448                         } else {
449                             originalSourceRegister = -1;
450                             break;
451                         }
452                     }
453                     if (originalSourceRegister != -1 && registerNumber == originalSourceRegister) {
454                         return true;
455                     }
456                 }
457             }
458         }
459 
460         if (!instruction.getOpcode().setsRegister()) {
461             return false;
462         }
463         int destinationRegister = getDestinationRegister();
464 
465         if (registerNumber == destinationRegister) {
466             return true;
467         }
468         if (instruction.getOpcode().setsWideRegister() && registerNumber == (destinationRegister + 1)) {
469             return true;
470         }
471         return false;
472     }
473 
474     public List<Integer> getSetRegisters() {
475         List<Integer> setRegisters = Lists.newArrayList();
476 
477         if (instruction.getOpcode().setsRegister()) {
478             setRegisters.add(getDestinationRegister());
479         }
480         if (instruction.getOpcode().setsWideRegister()) {
481             setRegisters.add(getDestinationRegister() + 1);
482         }
483 
484         if (isInvokeInit()) {
485             //When constructing a new object, the register type will be an uninitialized reference after the new-instance
486             //instruction, but becomes an initialized reference once the <init> method is called. So even though invoke
487             //instructions don't normally change any registers, calling an <init> method will change the type of its
488             //object register. If the uninitialized reference has been copied to other registers, they will be initialized
489             //as well, so we need to check for that too
490 
491             int destinationRegister;
492             if (instruction instanceof FiveRegisterInstruction) {
493                 destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterC();
494                 assert ((FiveRegisterInstruction)instruction).getRegisterCount() > 0;
495             } else {
496                 assert instruction instanceof RegisterRangeInstruction;
497                 RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
498                 assert rangeInstruction.getRegisterCount() > 0;
499                 destinationRegister = rangeInstruction.getStartRegister();
500             }
501 
502             RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(destinationRegister);
503             if (preInstructionDestRegisterType.category == RegisterType.UNINIT_REF ||
504                     preInstructionDestRegisterType.category == RegisterType.UNINIT_THIS) {
505                 setRegisters.add(destinationRegister);
506 
507                 RegisterType objectRegisterType = preRegisterMap[destinationRegister];
508                 for (int i = 0; i < preRegisterMap.length; i++) {
509                     if (i == destinationRegister) {
510                         continue;
511                     }
512 
513                     RegisterType preInstructionRegisterType = preRegisterMap[i];
514 
515                     if (preInstructionRegisterType.equals(objectRegisterType)) {
516                         setRegisters.add(i);
517                     } else if (preInstructionRegisterType.category == RegisterType.UNINIT_REF ||
518                             preInstructionRegisterType.category == RegisterType.UNINIT_THIS) {
519                         RegisterType postInstructionRegisterType = postRegisterMap[i];
520                         if (postInstructionRegisterType.category == RegisterType.UNKNOWN) {
521                             setRegisters.add(i);
522                         }
523                     }
524                 }
525             } else if (preInstructionDestRegisterType.category == RegisterType.UNKNOWN) {
526                 // We never let an uninitialized reference propagate past an invoke-init if the object register type is
527                 // unknown This is because the uninitialized reference may be an alias to the reference being
528                 // initialized, but we can't know that until the object register's type is known
529 
530                 for (int i = 0; i < preRegisterMap.length; i++) {
531                     RegisterType registerType = preRegisterMap[i];
532                     if (registerType.category == RegisterType.UNINIT_REF ||
533                             registerType.category == RegisterType.UNINIT_THIS) {
534                         setRegisters.add(i);
535                     }
536                 }
537             }
538         }
539 
540         // On art, the optimizer will often nop out a check-cast instruction after an instance-of instruction.
541         // Normally, check-cast is where the register type actually changes.
542         // In order to correctly handle this case, we have to propagate the narrowed register type to the appropriate
543         // branch of the following if-eqz/if-nez
544         if (instructionIndex > 0 &&
545                 methodAnalyzer.getClassPath().isArt() &&
546                 getPredecessorCount() == 1 &&
547                 (instruction.getOpcode() == Opcode.IF_EQZ || instruction.getOpcode() == Opcode.IF_NEZ)) {
548 
549             AnalyzedInstruction prevInstruction = predecessors.first();
550             if (prevInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF &&
551                     MethodAnalyzer.canPropagateTypeAfterInstanceOf(
552                             prevInstruction, this, methodAnalyzer.getClassPath())) {
553                 Instruction22c instanceOfInstruction = (Instruction22c)prevInstruction.instruction;
554                 setRegisters.add(instanceOfInstruction.getRegisterB());
555 
556                 // Additionally, there may be a move instruction just before the instance-of, in order to put the value
557                 // into a register that is addressable by the instance-of. In this case, we also need to propagate the
558                 // new register type for the original register that the value was moved from.
559                 // In some cases, the instance-of may have multiple predecessors. In this case, we should only do the
560                 // propagation if all predecessors are move-object instructions for the same source register
561                 // TODO: do we need to do some sort of additional check that these multiple move-object predecessors actually refer to the same value?
562                 if (instructionIndex > 1) {
563                     int originalSourceRegister = -1;
564 
565                     RegisterType newType = null;
566 
567                     for (AnalyzedInstruction prevPrevAnalyzedInstruction : prevInstruction.predecessors) {
568                         Opcode opcode = prevPrevAnalyzedInstruction.instruction.getOpcode();
569                         if (opcode == Opcode.MOVE_OBJECT || opcode == Opcode.MOVE_OBJECT_16 ||
570                                 opcode == Opcode.MOVE_OBJECT_FROM16) {
571                             TwoRegisterInstruction moveInstruction =
572                                     ((TwoRegisterInstruction)prevPrevAnalyzedInstruction.instruction);
573                             RegisterType originalType =
574                                     prevPrevAnalyzedInstruction.getPostInstructionRegisterType(
575                                             moveInstruction.getRegisterB());
576                             if (moveInstruction.getRegisterA() != instanceOfInstruction.getRegisterB()) {
577                                 originalSourceRegister = -1;
578                                 break;
579                             }
580                             if (originalType.type == null) {
581                                 originalSourceRegister = -1;
582                                 break;
583                             }
584 
585                             if (newType == null) {
586                                 newType = RegisterType.getRegisterType(methodAnalyzer.getClassPath(),
587                                         (TypeReference)instanceOfInstruction.getReference());
588                             }
589 
590                             if (MethodAnalyzer.isNotWideningConversion(originalType, newType)) {
591                                 if (originalSourceRegister != -1) {
592                                     if (originalSourceRegister != moveInstruction.getRegisterB()) {
593                                         originalSourceRegister = -1;
594                                         break;
595                                     }
596                                 } else {
597                                     originalSourceRegister = moveInstruction.getRegisterB();
598                                 }
599                             }
600                         } else {
601                             originalSourceRegister = -1;
602                             break;
603                         }
604                     }
605                     if (originalSourceRegister != -1) {
606                         setRegisters.add(originalSourceRegister);
607                     }
608                 }
609             }
610         }
611 
612         return setRegisters;
613     }
614 
615     public int getDestinationRegister() {
616         if (!this.instruction.getOpcode().setsRegister()) {
617             throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " +
618                     "store a value");
619         }
620         return ((OneRegisterInstruction)instruction).getRegisterA();
621     }
622 
623     public int getRegisterCount() {
624         return postRegisterMap.length;
625     }
626 
627     @Nonnull
628     public RegisterType getPostInstructionRegisterType(int registerNumber) {
629         return postRegisterMap[registerNumber];
630     }
631 
632     @Nonnull
633     public RegisterType getPreInstructionRegisterType(int registerNumber) {
634         return preRegisterMap[registerNumber];
635     }
636 
637     public int compareTo(@Nonnull AnalyzedInstruction analyzedInstruction) {
638         if (instructionIndex < analyzedInstruction.instructionIndex) {
639             return -1;
640         } else if (instructionIndex == analyzedInstruction.instructionIndex) {
641             return 0;
642         } else {
643             return 1;
644         }
645     }
646 
647     private static class PredecessorOverrideKey {
648         public final AnalyzedInstruction analyzedInstruction;
649         public final int registerNumber;
650 
651         public PredecessorOverrideKey(AnalyzedInstruction analyzedInstruction, int registerNumber) {
652             this.analyzedInstruction = analyzedInstruction;
653             this.registerNumber = registerNumber;
654         }
655 
656         @Override public boolean equals(Object o) {
657             if (this == o) return true;
658             if (o == null || getClass() != o.getClass()) return false;
659             PredecessorOverrideKey that = (PredecessorOverrideKey)o;
660             return com.google.common.base.Objects.equal(registerNumber, that.registerNumber) &&
661                     Objects.equal(analyzedInstruction, that.analyzedInstruction);
662         }
663 
664         @Override public int hashCode() {
665             return Objects.hashCode(analyzedInstruction, registerNumber);
666         }
667     }
668 }
669 
670