1b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato/*
2b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * ProGuard -- shrinking, optimization, obfuscation, and preverification
3b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *             of Java bytecode.
4b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
5b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
6b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
7b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * This program is free software; you can redistribute it and/or modify it
8b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * under the terms of the GNU General Public License as published by the Free
9b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * Software Foundation; either version 2 of the License, or (at your option)
10b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * any later version.
11b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
12b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * This program is distributed in the hope that it will be useful, but WITHOUT
13b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * more details.
16b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
17b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * You should have received a copy of the GNU General Public License along
18b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * with this program; if not, write to the Free Software Foundation, Inc.,
19b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato */
21b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratopackage proguard.classfile.attribute.visitor;
22b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
23b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoimport proguard.classfile.*;
24b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoimport proguard.classfile.visitor.ClassPrinter;
25b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoimport proguard.classfile.attribute.*;
26b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoimport proguard.classfile.instruction.*;
27b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoimport proguard.classfile.instruction.visitor.InstructionVisitor;
28b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoimport proguard.classfile.util.SimplifiedVisitor;
29b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
30b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato/**
31b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * This AttributeVisitor computes the stack sizes at all instruction offsets
32b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * of the code attributes that it visits.
33b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
34b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * @author Eric Lafortune
35b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato */
36b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratopublic class StackSizeComputer
37b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoextends      SimplifiedVisitor
38b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoimplements   AttributeVisitor,
39b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato             InstructionVisitor,
40b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato             ExceptionInfoVisitor
41b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato{
42b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    //*
43b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private static final boolean DEBUG = false;
44b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /*/
45b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private static       boolean DEBUG = true;
46b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    //*/
47b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
48b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
49b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private boolean[] evaluated  = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
50b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private int[]     stackSizes = new int[ClassConstants.TYPICAL_CODE_LENGTH];
51b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
52b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private boolean exitInstructionBlock;
53b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
54b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private int stackSize;
55b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private int maxStackSize;
56b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
57b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
58b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
59b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Returns whether the instruction at the given offset is reachable in the
60b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * most recently visited code attribute.
61b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
62b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public boolean isReachable(int instructionOffset)
63b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
64b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        return evaluated[instructionOffset];
65b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
66b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
67b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
68b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
69b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Returns the stack size at the given instruction offset of the most
70b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * recently visited code attribute.
71b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
72b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public int getStackSize(int instructionOffset)
73b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
74b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (!evaluated[instructionOffset])
75b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
76b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            throw new IllegalArgumentException("Unknown stack size at unreachable instruction offset ["+instructionOffset+"]");
77b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
78b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
79b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        return stackSizes[instructionOffset];
80b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
81b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
82b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
83b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
84b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Returns the maximum stack size of the most recently visited code attribute.
85b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
86b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public int getMaxStackSize()
87b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
88b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        return maxStackSize;
89b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
90b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
91b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
92b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    // Implementations for AttributeVisitor.
93b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
94b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
95b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
96b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
97b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
98b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
99b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato//        DEBUG =
100b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato//            clazz.getName().equals("abc/Def") &&
101b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato//            method.getName(clazz).equals("abc");
102b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
103b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // TODO: Remove this when the code has stabilized.
104b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Catch any unexpected exceptions from the actual visiting method.
105b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        try
106b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
107b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Process the code.
108b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            visitCodeAttribute0(clazz, method, codeAttribute);
109b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
110b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        catch (RuntimeException ex)
111b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
112b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            System.err.println("Unexpected error while computing stack sizes:");
113b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            System.err.println("  Class       = ["+clazz.getName()+"]");
114b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
115b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
116b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
117b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (DEBUG)
118b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
119b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                method.accept(clazz, new ClassPrinter());
120b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
121b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
122b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            throw ex;
123b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
124b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
125b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
126b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
127b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
128b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
129b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (DEBUG)
130b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
131b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            System.out.println("StackSizeComputer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
132b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
133b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
134b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Try to reuse the previous array.
135b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        int codeLength = codeAttribute.u4codeLength;
136b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (evaluated.length < codeLength)
137b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
138b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            evaluated  = new boolean[codeLength];
139b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            stackSizes = new int[codeLength];
140b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
141b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        else
142b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
143b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            for (int index = 0; index < codeLength; index++)
144b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
145b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                evaluated[index] = false;
146b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
147b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
148b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
149b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // The initial stack is always empty.
150b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        stackSize    = 0;
151b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        maxStackSize = 0;
152b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
153b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Evaluate the instruction block starting at the entry point of the method.
154b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        evaluateInstructionBlock(clazz, method, codeAttribute, 0);
155b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
156b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Evaluate the exception handlers.
157b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        codeAttribute.exceptionsAccept(clazz, method, this);
158b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
159b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
160b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
161b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    // Implementations for InstructionVisitor.
162b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
163b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
164b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
165b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        byte opcode = simpleInstruction.opcode;
166b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
167b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Some simple instructions exit from the current instruction block.
168b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        exitInstructionBlock =
169b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_IRETURN ||
170b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_LRETURN ||
171b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_FRETURN ||
172b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_DRETURN ||
173b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_ARETURN ||
174b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_RETURN  ||
175b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_ATHROW;
176b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
177b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
178b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
179b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
180b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Constant pool instructions never end the current instruction block.
181b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        exitInstructionBlock = false;
182b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
183b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
184b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
185b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
186b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        byte opcode = variableInstruction.opcode;
187b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
188b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // The ret instruction end the current instruction block.
189b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        exitInstructionBlock =
190b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_RET;
191b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
192b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
193b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
194b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
195b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        byte opcode = branchInstruction.opcode;
196b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
197b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Evaluate the target instruction blocks.
198b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        evaluateInstructionBlock(clazz,
199b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 method,
200b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 codeAttribute,
201b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 offset +
202b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 branchInstruction.branchOffset);
203b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
204b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Evaluate the instructions after a subroutine branch.
205b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (opcode == InstructionConstants.OP_JSR ||
206b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_JSR_W)
207b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
208b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // We assume subroutine calls (jsr and jsr_w instructions) don't
209b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // change the stack, other than popping the return value.
210b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            stackSize -= 1;
211b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
212b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            evaluateInstructionBlock(clazz,
213b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                     method,
214b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                     codeAttribute,
215b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                     offset + branchInstruction.length(offset));
216b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
217b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
218b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Some branch instructions always end the current instruction block.
219b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        exitInstructionBlock =
220b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_GOTO   ||
221b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_GOTO_W ||
222b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_JSR    ||
223b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            opcode == InstructionConstants.OP_JSR_W;
224b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
225b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
226b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
227b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
228b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
229b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Evaluate the target instruction blocks.
230b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
231b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Loop over all jump offsets.
232b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        int[] jumpOffsets = switchInstruction.jumpOffsets;
233b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
234b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        for (int index = 0; index < jumpOffsets.length; index++)
235b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
236b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Evaluate the jump instruction block.
237b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            evaluateInstructionBlock(clazz,
238b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                     method,
239b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                     codeAttribute,
240b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                     offset + jumpOffsets[index]);
241b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
242b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
243b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Also evaluate the default instruction block.
244b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        evaluateInstructionBlock(clazz,
245b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 method,
246b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 codeAttribute,
247b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 offset + switchInstruction.defaultOffset);
248b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
249b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // The switch instruction always ends the current instruction block.
250b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        exitInstructionBlock = true;
251b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
252b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
253b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
254b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    // Implementations for ExceptionInfoVisitor.
255b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
256b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
257b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
258b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (DEBUG)
259b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
260b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            System.out.println("Exception:");
261b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
262b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
263b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // The stack size when entering the exception handler is always 1.
264b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        stackSize = 1;
265b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
266b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Evaluate the instruction block starting at the entry point of the
267b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // exception handler.
268b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        evaluateInstructionBlock(clazz,
269b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 method,
270b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 codeAttribute,
271b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                 exceptionInfo.u2handlerPC);
272b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
273b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
274b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
275b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    // Small utility methods.
276b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
277b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
278b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Evaluates a block of instructions that hasn't been handled before,
279b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * starting at the given offset and ending at a branch instruction, a return
280b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * instruction, or a throw instruction. Branch instructions are handled
281b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * recursively.
282b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
283b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private void evaluateInstructionBlock(Clazz         clazz,
284b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                          Method        method,
285b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                          CodeAttribute codeAttribute,
286b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                          int           instructionOffset)
287b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
288b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (DEBUG)
289b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
290b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (evaluated[instructionOffset])
291b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
292b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                System.out.println("-- (instruction block at "+instructionOffset+" already evaluated)");
293b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
294b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            else
295b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
296b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                System.out.println("-- instruction block:");
297b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
298b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
299b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
300b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Remember the initial stack size.
301b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        int initialStackSize = stackSize;
302b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
303b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Remember the maximum stack size.
304b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (maxStackSize < stackSize)
305b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
306b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            maxStackSize = stackSize;
307b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
308b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
309b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Evaluate any instructions that haven't been evaluated before.
310b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        while (!evaluated[instructionOffset])
311b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
312b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Mark the instruction as evaluated.
313b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            evaluated[instructionOffset] = true;
314b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
315b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            Instruction instruction = InstructionFactory.create(codeAttribute.code,
316b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                                                instructionOffset);
317b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
318b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (DEBUG)
319b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
320b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                int stackPushCount = instruction.stackPushCount(clazz);
321b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                int stackPopCount  = instruction.stackPopCount(clazz);
322b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                System.out.println("["+instructionOffset+"]: "+
323b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                   stackSize+" - "+
324b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                   stackPopCount+" + "+
325b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                   stackPushCount+" = "+
326b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                   (stackSize+stackPushCount-stackPopCount)+": "+
327b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                   instruction.toString(instructionOffset));
328b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
329b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
330b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Compute the instruction's effect on the stack size.
331b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            stackSize -= instruction.stackPopCount(clazz);
332b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
333b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (stackSize < 0)
334b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
335b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                throw new IllegalArgumentException("Stack size becomes negative after instruction "+
336b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                                   instruction.toString(instructionOffset)+" in ["+
337b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                                   clazz.getName()+"."+
338b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                                   method.getName(clazz)+
339b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                                   method.getDescriptor(clazz)+"]");
340b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
341b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
342b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            stackSizes[instructionOffset] =
343b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            stackSize += instruction.stackPushCount(clazz);
344b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
345b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Remember the maximum stack size.
346b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (maxStackSize < stackSize)
347b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
348b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                maxStackSize = stackSize;
349b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
350b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
351b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Remember the next instruction offset.
352b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            int nextInstructionOffset = instructionOffset +
353b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                                        instruction.length(instructionOffset);
354b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
355b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Visit the instruction, in order to handle branches.
356b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            instruction.accept(clazz, method, codeAttribute, instructionOffset, this);
357b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
358b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Stop evaluating after a branch.
359b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (exitInstructionBlock)
360b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
361b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                break;
362b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
363b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
364b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Continue with the next instruction.
365b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            instructionOffset = nextInstructionOffset;
366b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
367b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (DEBUG)
368b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
369b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                if (evaluated[instructionOffset])
370b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                {
371b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                    System.out.println("-- (instruction at "+instructionOffset+" already evaluated)");
372b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                }
373b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
374b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
375b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
376b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Restore the stack size for possible subsequent instruction blocks.
377b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        this.stackSize = initialStackSize;
378b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
379b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato}
380