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