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