• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 package proguard.optimize.peephole;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.visitor.*;
26 import proguard.classfile.constant.*;
27 import proguard.classfile.constant.visitor.ConstantVisitor;
28 import proguard.classfile.instruction.*;
29 import proguard.classfile.instruction.visitor.InstructionVisitor;
30 import proguard.classfile.util.SimplifiedVisitor;
31 
32 import java.util.Arrays;
33 
34 /**
35  * This AttributeVisitor finds all instruction offsets, branch targets, and
36  * exception targets in the CodeAttribute objects that it visits.
37  *
38  * @author Eric Lafortune
39  */
40 public class BranchTargetFinder
41 extends      SimplifiedVisitor
42 implements   AttributeVisitor,
43              InstructionVisitor,
44              ExceptionInfoVisitor,
45              ConstantVisitor
46 {
47     //*
48     private static final boolean DEBUG = false;
49     /*/
50     private static       boolean DEBUG = System.getProperty("btf") != null;
51     //*/
52 
53     public static final int NONE = -1;
54 
55     // We'll explicitly mark instructions that are not part of a subroutine,
56     // with NO_SUBROUTINE. Subroutines may just branch back into normal code
57     // (e.g. due to a break instruction in Java code), and we want to avoid
58     // marking such normal code as subroutine. The first mark wins, so we're
59     // assuming that such code is marked as normal code before it is marked
60     // as subroutine.
61     public static final int UNKNOWN       = -1;
62     public static final int NO_SUBROUTINE = -2;
63 
64     private static final short INSTRUCTION           = 1 << 0;
65     private static final short BRANCH_ORIGIN         = 1 << 1;
66     private static final short BRANCH_TARGET         = 1 << 2;
67     private static final short AFTER_BRANCH          = 1 << 3;
68     private static final short EXCEPTION_START       = 1 << 4;
69     private static final short EXCEPTION_END         = 1 << 5;
70     private static final short EXCEPTION_HANDLER     = 1 << 6;
71     private static final short SUBROUTINE_INVOCATION = 1 << 7;
72     private static final short SUBROUTINE_RETURNING  = 1 << 8;
73 
74     private static final int MAXIMUM_CREATION_OFFSETS = 32;
75 
76 
77     private short[] instructionMarks      = new short[ClassConstants.TYPICAL_CODE_LENGTH + 1];
78     private int[]   subroutineStarts      = new int[ClassConstants.TYPICAL_CODE_LENGTH];
79     private int[]   subroutineEnds        = new int[ClassConstants.TYPICAL_CODE_LENGTH];
80     private int[]   creationOffsets       = new int[ClassConstants.TYPICAL_CODE_LENGTH];
81     private int[]   initializationOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH];
82     private int     superInitializationOffset;
83     private boolean containsSubroutines;
84 
85     private boolean repeat;
86     private int     currentSubroutineStart;
87     private int[]   recentCreationOffsets = new int[MAXIMUM_CREATION_OFFSETS];
88     private int     recentCreationOffsetIndex;
89     private boolean isInitializer;
90 
91 
92     /**
93      * Returns whether there is an instruction at the given offset in the
94      * CodeAttribute that was visited most recently.
95      */
isInstruction(int offset)96     public boolean isInstruction(int offset)
97     {
98         return (instructionMarks[offset] & INSTRUCTION) != 0;
99     }
100 
101 
102     /**
103      * Returns whether the instruction at the given offset is the target of
104      * any kind in the CodeAttribute that was visited most recently.
105      */
isTarget(int offset)106     public boolean isTarget(int offset)
107     {
108         return offset == 0 ||
109                (instructionMarks[offset] & (BRANCH_TARGET   |
110                                             EXCEPTION_START |
111                                             EXCEPTION_END   |
112                                             EXCEPTION_HANDLER)) != 0;
113     }
114 
115 
116     /**
117      * Returns whether the instruction at the given offset is the origin of a
118      * branch instruction in the CodeAttribute that was visited most recently.
119      */
isBranchOrigin(int offset)120     public boolean isBranchOrigin(int offset)
121     {
122         return (instructionMarks[offset] & BRANCH_ORIGIN) != 0;
123     }
124 
125 
126     /**
127      * Returns whether the instruction at the given offset is the target of a
128      * branch instruction in the CodeAttribute that was visited most recently.
129      */
isBranchTarget(int offset)130     public boolean isBranchTarget(int offset)
131     {
132         return (instructionMarks[offset] & BRANCH_TARGET) != 0;
133     }
134 
135 
136     /**
137      * Returns whether the instruction at the given offset comes right after a
138      * definite branch instruction in the CodeAttribute that was visited most
139      * recently.
140      */
isAfterBranch(int offset)141     public boolean isAfterBranch(int offset)
142     {
143         return (instructionMarks[offset] & AFTER_BRANCH) != 0;
144     }
145 
146 
147     /**
148      * Returns whether the instruction at the given offset is the start of an
149      * exception try block in the CodeAttribute that was visited most recently.
150      */
isExceptionStart(int offset)151     public boolean isExceptionStart(int offset)
152     {
153         return (instructionMarks[offset] & EXCEPTION_START) != 0;
154     }
155 
156 
157     /**
158      * Returns whether the instruction at the given offset is the end of an
159      * exception try block in the CodeAttribute that was visited most recently.
160      */
isExceptionEnd(int offset)161     public boolean isExceptionEnd(int offset)
162     {
163         return (instructionMarks[offset] & EXCEPTION_END) != 0;
164     }
165 
166 
167     /**
168      * Returns whether the instruction at the given offset is the start of an
169      * exception catch block in the CodeAttribute that was visited most recently.
170      */
isExceptionHandler(int offset)171     public boolean isExceptionHandler(int offset)
172     {
173         return (instructionMarks[offset] & EXCEPTION_HANDLER) != 0;
174     }
175 
176 
177     /**
178      * Returns whether the instruction at the given offset is a subroutine
179      * invocation in the CodeAttribute that was visited most recently.
180      */
isSubroutineInvocation(int offset)181     public boolean isSubroutineInvocation(int offset)
182     {
183         return (instructionMarks[offset] & SUBROUTINE_INVOCATION) != 0;
184     }
185 
186 
187     /**
188      * Returns whether the instruction at the given offset is the start of a
189      * subroutine in the CodeAttribute that was visited most recently.
190      */
isSubroutineStart(int offset)191     public boolean isSubroutineStart(int offset)
192     {
193         return subroutineStarts[offset] == offset;
194     }
195 
196 
197     /**
198      * Returns whether the instruction at the given offset is part of a
199      * subroutine in the CodeAttribute that was visited most recently.
200      */
isSubroutine(int offset)201     public boolean isSubroutine(int offset)
202     {
203         return subroutineStarts[offset] >= 0;
204     }
205 
206 
207     /**
208      * Returns whether the subroutine at the given offset is ever returning
209      * by means of a regular 'ret' instruction.
210      */
isSubroutineReturning(int offset)211     public boolean isSubroutineReturning(int offset)
212     {
213         return (instructionMarks[offset] & SUBROUTINE_RETURNING) != 0;
214     }
215 
216 
217     /**
218      * Returns the start offset of the subroutine at the given offset, in the
219      * CodeAttribute that was visited most recently.
220      */
subroutineStart(int offset)221     public int subroutineStart(int offset)
222     {
223         return subroutineStarts[offset];
224     }
225 
226 
227     /**
228      * Returns the offset after the subroutine at the given offset, in the
229      * CodeAttribute that was visited most recently.
230      */
subroutineEnd(int offset)231     public int subroutineEnd(int offset)
232     {
233         return subroutineEnds[offset];
234     }
235 
236 
237     /**
238      * Returns whether the instruction at the given offset is a 'new'
239      * instruction, in the CodeAttribute that was visited most recently.
240      */
isNew(int offset)241     public boolean isNew(int offset)
242     {
243         return initializationOffsets[offset] != NONE;
244     }
245 
246 
247     /**
248      * Returns the instruction offset at which the object instance that is
249      * created at the given 'new' instruction offset is initialized, or
250      * <code>NONE</code> if it is not being created.
251      */
initializationOffset(int offset)252     public int initializationOffset(int offset)
253     {
254         return initializationOffsets[offset];
255     }
256 
257 
258     /**
259      * Returns whether the method is an instance initializer, in the
260      * CodeAttribute that was visited most recently.
261      */
isInitializer()262     public boolean isInitializer()
263     {
264         return superInitializationOffset != NONE;
265     }
266 
267 
268     /**
269      * Returns the instruction offset at which this initializer is calling
270      * the "super" or "this" initializer method, or <code>NONE</code> if it is
271      * not an initializer.
272      */
superInitializationOffset()273     public int superInitializationOffset()
274     {
275         return superInitializationOffset;
276     }
277 
278 
279     /**
280      * Returns whether the instruction at the given offset is the special
281      * invocation of an instance initializer, in the CodeAttribute that was
282      * visited most recently.
283      */
isInitializer(int offset)284     public boolean isInitializer(int offset)
285     {
286         return creationOffsets[offset] != NONE;
287     }
288 
289 
290     /**
291      * Returns the offset of the 'new' instruction that corresponds to the
292      * invocation of the instance initializer at the given offset, or
293      * <code>AT_METHOD_ENTRY</code> if the invocation is calling the "super" or
294      * "this" initializer method, , or <code>NONE</code> if it is not a 'new'
295      * instruction.
296      */
creationOffset(int offset)297     public int creationOffset(int offset)
298     {
299         return creationOffsets[offset];
300     }
301 
302 
303     /**
304      * Returns whether the method contains subroutines, in the CodeAttribute
305      * that was visited most recently.
306      */
containsSubroutines()307     public boolean containsSubroutines()
308     {
309         return containsSubroutines;
310     }
311 
312 
313     // Implementations for AttributeVisitor.
314 
visitAnyAttribute(Clazz clazz, Attribute attribute)315     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
316 
317 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)318     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
319     {
320 //        DEBUG =
321 //            clazz.getName().equals("abc/Def") &&
322 //            method.getName(clazz).equals("abc");
323 
324         // Make sure there are sufficiently large arrays.
325         int codeLength = codeAttribute.u4codeLength;
326         if (subroutineStarts.length < codeLength)
327         {
328             // Create new arrays.
329             instructionMarks      = new short[codeLength + 1];
330             subroutineStarts      = new int[codeLength];
331             subroutineEnds        = new int[codeLength];
332             creationOffsets       = new int[codeLength];
333             initializationOffsets = new int[codeLength];
334 
335             // Reset the arrays.
336             Arrays.fill(subroutineStarts,      0, codeLength, UNKNOWN);
337             Arrays.fill(subroutineEnds,        0, codeLength, UNKNOWN);
338             Arrays.fill(creationOffsets,       0, codeLength, NONE);
339             Arrays.fill(initializationOffsets, 0, codeLength, NONE);
340         }
341         else
342         {
343             // Reset the arrays.
344             Arrays.fill(instructionMarks,      0, codeLength, (short)0);
345             Arrays.fill(subroutineStarts,      0, codeLength, UNKNOWN);
346             Arrays.fill(subroutineEnds,        0, codeLength, UNKNOWN);
347             Arrays.fill(creationOffsets,       0, codeLength, NONE);
348             Arrays.fill(initializationOffsets, 0, codeLength, NONE);
349 
350             instructionMarks[codeLength] = 0;
351         }
352 
353         superInitializationOffset = NONE;
354         containsSubroutines       = false;
355 
356         // Iterate until all subroutines have been fully marked.
357         do
358         {
359             repeat                    = false;
360             currentSubroutineStart    = NO_SUBROUTINE;
361             recentCreationOffsetIndex = 0;
362 
363             // Mark branch targets by going over all instructions.
364             codeAttribute.instructionsAccept(clazz, method, this);
365 
366             // Mark branch targets in the exception table.
367             codeAttribute.exceptionsAccept(clazz, method, this);
368         }
369         while (repeat);
370 
371         // The end of the code is a branch target sentinel.
372         instructionMarks[codeLength] = BRANCH_TARGET;
373 
374         if (containsSubroutines)
375         {
376             // Set the subroutine returning flag and the subroutine end at each
377             // subroutine start.
378             int previousSubroutineStart = NO_SUBROUTINE;
379 
380             for (int offset = 0; offset < codeLength; offset++)
381             {
382                 if (isInstruction(offset))
383                 {
384                     int subroutineStart = subroutineStarts[offset];
385 
386                     if (subroutineStart >= 0 &&
387                         isSubroutineReturning(offset))
388                     {
389                         instructionMarks[subroutineStart] |= SUBROUTINE_RETURNING;
390                     }
391 
392                     if (previousSubroutineStart >= 0)
393                     {
394                         subroutineEnds[previousSubroutineStart] = offset;
395                     }
396 
397                     previousSubroutineStart = subroutineStart;
398                 }
399             }
400 
401             if (previousSubroutineStart >= 0)
402             {
403                 subroutineEnds[previousSubroutineStart] = codeLength;
404             }
405 
406             // Set the subroutine returning flag and the subroutine end at each
407             // subroutine instruction, based on the marks at the subroutine
408             // start.
409             for (int offset = 0; offset < codeLength; offset++)
410             {
411                 if (isSubroutine(offset))
412                 {
413                     int subroutineStart = subroutineStarts[offset];
414 
415                     if (isSubroutineReturning(subroutineStart))
416                     {
417                         instructionMarks[offset] |= SUBROUTINE_RETURNING;
418                     }
419 
420                     subroutineEnds[offset] = subroutineEnds[subroutineStart];
421                 }
422             }
423         }
424 
425         if (DEBUG)
426         {
427             System.out.println();
428             System.out.println("Branch targets: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
429 
430             for (int index = 0; index < codeLength; index++)
431             {
432                 if (isInstruction(index))
433                 {
434                     System.out.println("" +
435                                        (isBranchOrigin(index)         ? 'B' : '-') +
436                                        (isAfterBranch(index)          ? 'b' : '-') +
437                                        (isBranchTarget(index)         ? 'T' : '-') +
438                                        (isExceptionStart(index)       ? 'E' : '-') +
439                                        (isExceptionEnd(index)         ? 'e' : '-') +
440                                        (isExceptionHandler(index)     ? 'H' : '-') +
441                                        (isSubroutineInvocation(index) ? 'J' : '-') +
442                                        (isSubroutineStart(index)      ? 'S' : '-') +
443                                        (isSubroutineReturning(index)  ? 'r' : '-') +
444                                        (isSubroutine(index)           ? " ["+subroutineStart(index)+" -> "+subroutineEnd(index)+"]" : "") +
445                                        (isNew(index)                  ? " ["+initializationOffset(index)+"] " : " ---- ") +
446                                        InstructionFactory.create(codeAttribute.code, index).toString(index));
447                 }
448             }
449         }
450     }
451 
452 
453     // Implementations for InstructionVisitor.
454 
visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)455     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
456     {
457         // Mark the instruction.
458         instructionMarks[offset] |= INSTRUCTION;
459 
460         // Check if this is an instruction of a subroutine.
461         checkSubroutine(offset);
462 
463         byte opcode = simpleInstruction.opcode;
464         if (opcode == InstructionConstants.OP_IRETURN ||
465             opcode == InstructionConstants.OP_LRETURN ||
466             opcode == InstructionConstants.OP_FRETURN ||
467             opcode == InstructionConstants.OP_DRETURN ||
468             opcode == InstructionConstants.OP_ARETURN ||
469             opcode == InstructionConstants.OP_ATHROW)
470         {
471             // Mark the branch origin.
472             markBranchOrigin(offset);
473 
474             // Mark the next instruction.
475             markAfterBranchOrigin(offset + simpleInstruction.length(offset));
476         }
477     }
478 
479 
visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)480     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
481     {
482         // Mark the instruction.
483         instructionMarks[offset] |= INSTRUCTION;
484 
485         // Check if this is an instruction of a subroutine.
486         checkSubroutine(offset);
487 
488         byte opcode = constantInstruction.opcode;
489         if (opcode == InstructionConstants.OP_NEW)
490         {
491             // Push the 'new' instruction offset on the stack.
492             recentCreationOffsets[recentCreationOffsetIndex++] = offset;
493         }
494         else if (opcode == InstructionConstants.OP_INVOKESPECIAL)
495         {
496             // Is it calling an instance initializer?
497             isInitializer = false;
498             clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
499             if (isInitializer)
500             {
501                 // Do we have any 'new' instruction offsets on the stack?
502                 if (recentCreationOffsetIndex > 0)
503                 {
504                     // Pop the 'new' instruction offset from the stack.
505                     int recentCreationOffset = recentCreationOffsets[--recentCreationOffsetIndex];
506 
507                     // Link the creation offset and the initialization offset.
508                     // TODO: There could be multiple initialization offsets.
509                     creationOffsets[offset] = recentCreationOffset;
510 
511                     initializationOffsets[recentCreationOffset] = offset;
512                 }
513                 else
514                 {
515                     // Remember the super initialization offset.
516                     // TODO: There could be multiple initialization offsets.
517                     // For instance, in the constructor of the generated class
518                     // groovy.inspect.swingui.GeneratedBytecodeAwareGroovyClassLoader
519                     // in groovy-all-2.2.1.jar.
520                     superInitializationOffset = offset;
521                 }
522             }
523         }
524     }
525 
526 
visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)527     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
528     {
529         // Mark the instruction.
530         instructionMarks[offset] |= INSTRUCTION;
531 
532         // Check if this is an instruction of a subroutine.
533         checkSubroutine(offset);
534 
535         if (variableInstruction.opcode == InstructionConstants.OP_RET)
536         {
537             // Mark the method.
538             containsSubroutines = true;
539 
540             // Mark the branch origin.
541             markBranchOrigin(offset);
542 
543             // Mark the subroutine return at its return instruction.
544             instructionMarks[offset] |= SUBROUTINE_RETURNING;
545 
546             // Mark the next instruction.
547             markAfterBranchOrigin(offset + variableInstruction.length(offset));
548         }
549     }
550 
551 
visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)552     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
553     {
554         int branchOffset = branchInstruction.branchOffset;
555         int targetOffset = offset + branchOffset;
556 
557         // Mark the branch origin.
558         markBranchOrigin(offset);
559 
560         // Check if this is an instruction of a subroutine.
561         checkSubroutine(offset);
562 
563         // Mark the branch target.
564         markBranchTarget(offset, branchOffset);
565 
566         byte opcode = branchInstruction.opcode;
567         if (opcode == InstructionConstants.OP_JSR ||
568             opcode == InstructionConstants.OP_JSR_W)
569         {
570             // Mark the method.
571             containsSubroutines = true;
572 
573             // Mark the subroutine invocation.
574             instructionMarks[offset] |= SUBROUTINE_INVOCATION;
575 
576             // Mark the new subroutine start.
577             markBranchSubroutineStart(offset, branchOffset, targetOffset);
578         }
579         else if (currentSubroutineStart != UNKNOWN)
580         {
581             // Mark the continued subroutine start.
582             markBranchSubroutineStart(offset, branchOffset, currentSubroutineStart);
583         }
584 
585         if (opcode == InstructionConstants.OP_GOTO ||
586             opcode == InstructionConstants.OP_GOTO_W)
587         {
588             // Mark the next instruction.
589             markAfterBranchOrigin(offset + branchInstruction.length(offset));
590         }
591     }
592 
593 
visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)594     public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
595     {
596         // Mark the branch origin.
597         markBranchOrigin(offset);
598 
599         // Check if this is an instruction of a subroutine.
600         checkSubroutine(offset);
601 
602         // Mark the branch targets of the default jump offset.
603         markBranch(offset, switchInstruction.defaultOffset);
604 
605         // Mark the branch targets of the jump offsets.
606         markBranches(offset, switchInstruction.jumpOffsets);
607 
608         // Mark the next instruction.
609         markAfterBranchOrigin(offset + switchInstruction.length(offset));
610     }
611 
612 
613     // Implementations for ConstantVisitor.
614 
visitAnyConstant(Clazz clazz, Constant constant)615     public void visitAnyConstant(Clazz clazz, Constant constant) {}
616 
617 
visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)618     public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
619     {
620         // Remember whether the method is an initializer.
621         isInitializer = methodrefConstant.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT);
622     }
623 
624 
625     // Implementations for ExceptionInfoVisitor.
626 
visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)627     public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
628     {
629         int startPC   = exceptionInfo.u2startPC;
630         int endPC     = exceptionInfo.u2endPC;
631         int handlerPC = exceptionInfo.u2handlerPC;
632 
633         // Mark the exception offsets.
634         instructionMarks[startPC]   |= EXCEPTION_START;
635         instructionMarks[endPC]     |= EXCEPTION_END;
636         instructionMarks[handlerPC] |= EXCEPTION_HANDLER;
637 
638         // Mark the handler as part of a subroutine if necessary.
639         if (subroutineStarts[handlerPC] == UNKNOWN &&
640             subroutineStarts[startPC]   != UNKNOWN)
641         {
642             subroutineStarts[handlerPC] = subroutineStarts[startPC];
643 
644             // We'll have to go over all instructions again.
645             repeat = true;
646         }
647     }
648 
649 
650     // Small utility methods.
651 
652     /**
653      * Marks the branch targets and their subroutine starts at the given
654      * offsets.
655      */
markBranches(int offset, int[] jumpOffsets)656     private void markBranches(int offset, int[] jumpOffsets)
657     {
658         for (int index = 0; index < jumpOffsets.length; index++)
659         {
660             markBranch(offset, jumpOffsets[index]);
661         }
662     }
663 
664 
665     /**
666      * Marks the branch target and its subroutine start at the given offset.
667      */
markBranch(int offset, int jumpOffset)668     private void markBranch(int offset, int jumpOffset)
669     {
670         markBranchTarget(offset, jumpOffset);
671 
672         if (currentSubroutineStart != UNKNOWN)
673         {
674             markBranchSubroutineStart(offset, jumpOffset, currentSubroutineStart);
675         }
676     }
677 
678     /**
679      * Marks the branch origin at the given offset.
680      */
markBranchOrigin(int offset)681     private void markBranchOrigin(int offset)
682     {
683         instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN;
684     }
685 
686 
687     /**
688      * Marks the branch target at the given offset.
689      */
markBranchTarget(int offset, int jumpOffset)690     private void markBranchTarget(int offset, int jumpOffset)
691     {
692         int targetOffset = offset + jumpOffset;
693 
694         instructionMarks[targetOffset] |= BRANCH_TARGET;
695     }
696 
697 
698     /**
699      * Marks the subroutine start at the given offset, if applicable.
700      */
markBranchSubroutineStart(int offset, int jumpOffset, int subroutineStart)701     private void markBranchSubroutineStart(int offset,
702                                            int jumpOffset,
703                                            int subroutineStart)
704     {
705         int targetOffset = offset + jumpOffset;
706 
707         // Are we marking a subroutine and branching to an offset that hasn't
708         // been marked yet?
709         if (subroutineStarts[targetOffset] == UNKNOWN)
710         {
711             // Is it a backward branch?
712             if (jumpOffset < 0)
713             {
714                 // Remember the smallest subroutine start.
715                 if (subroutineStart > targetOffset)
716                 {
717                     subroutineStart = targetOffset;
718                 }
719 
720                 // We'll have to go over all instructions again.
721                 repeat = true;
722             }
723 
724             // Mark the subroutine start of the target.
725             subroutineStarts[targetOffset] = subroutineStart;
726         }
727     }
728 
729 
730     /**
731      * Marks the instruction at the given offset, after a branch.
732      */
markAfterBranchOrigin(int nextOffset)733     private void markAfterBranchOrigin(int nextOffset)
734     {
735         instructionMarks[nextOffset] |= AFTER_BRANCH;
736 
737         // Stop marking a subroutine.
738         currentSubroutineStart = UNKNOWN;
739     }
740 
741 
742     /**
743      * Checks if the specified instruction is inside a subroutine.
744      */
checkSubroutine(int offset)745     private void checkSubroutine(int offset)
746     {
747         // Are we inside a previously marked subroutine?
748         if (subroutineStarts[offset] != UNKNOWN)
749         {
750             // Start marking a subroutine.
751             currentSubroutineStart = subroutineStarts[offset];
752         }
753 
754         // Are we marking a subroutine?
755         else if (currentSubroutineStart != UNKNOWN)
756         {
757             // Mark the subroutine start.
758             subroutineStarts[offset] = currentSubroutineStart;
759         }
760     }
761 }
762