KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > classfile > attribute > visitor > StackSizeComputer


1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  * of Java bytecode.
4  *
5  * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
6  *
7  * This library 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 library 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 Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */

21 package proguard.classfile.attribute.visitor;
22
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.instruction.*;
26 import proguard.classfile.instruction.visitor.InstructionVisitor;
27 import proguard.classfile.util.SimplifiedVisitor;
28
29 /**
30  * This AttributeVisitor computes the stack sizes at all instruction offsets
31  * of the code attributes that it visits.
32  *
33  * @author Eric Lafortune
34  */

35 public class StackSizeComputer
36 extends SimplifiedVisitor
37 implements AttributeVisitor,
38              InstructionVisitor,
39              ExceptionInfoVisitor
40 {
41     //*
42
private static final boolean DEBUG = false;
43     /*/
44     private static boolean DEBUG = true;
45     //*/

46
47
48     private boolean[] evaluated = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
49     private int[] stackSizes = new int[ClassConstants.TYPICAL_CODE_LENGTH];
50
51     private boolean exitInstructionBlock;
52
53     private int stackSize;
54     private int maxStackSize;
55
56
57     /**
58      * Returns whether the instruction at the given offset is reachable in the
59      * most recently visited code attribute.
60      */

61     public boolean isReachable(int instructionOffset)
62     {
63         return evaluated[instructionOffset];
64     }
65
66
67     /**
68      * Returns the stack size at the given instruction offset of the most
69      * recently visited code attribute.
70      */

71     public int getStackSize(int instructionOffset)
72     {
73         if (!evaluated[instructionOffset])
74         {
75             throw new IllegalArgumentException JavaDoc("Unknown stack size at unreachable instruction offset ["+instructionOffset+"]");
76         }
77
78         return stackSizes[instructionOffset];
79     }
80
81
82     /**
83      * Returns the maximum stack size of the most recently visited code attribute.
84      */

85     public int getMaxStackSize()
86     {
87         return maxStackSize;
88     }
89
90
91     // Implementations for AttributeVisitor.
92

93     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
94     {
95 // DEBUG =
96
// clazz.getName().equals("abc/Def") &&
97
// method.getName(clazz).equals("abc");
98

99         // TODO: Remove this when the code has stabilized.
100
// Catch any unexpected exceptions from the actual visiting method.
101
try
102         {
103             // Process the code.
104
visitCodeAttribute0(clazz, method, codeAttribute);
105         }
106         catch (RuntimeException JavaDoc ex)
107         {
108             System.err.println("Unexpected error while computing stack sizes:");
109             System.err.println(" Class = ["+clazz.getName()+"]");
110             System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
111             System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
112
113             throw ex;
114         }
115     }
116
117
118     public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
119     {
120         if (DEBUG)
121         {
122             System.out.println("StackSizeComputer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
123         }
124
125         // Try to reuse the previous array.
126
int codeLength = codeAttribute.u4codeLength;
127         if (evaluated.length < codeLength)
128         {
129             evaluated = new boolean[codeLength];
130             stackSizes = new int[codeLength];
131         }
132         else
133         {
134             for (int index = 0; index < codeLength; index++)
135             {
136                 evaluated[index] = false;
137             }
138         }
139
140         // The initial stack is always empty.
141
stackSize = 0;
142         maxStackSize = 0;
143
144         // Evaluate the instruction block starting at the entry point of the method.
145
evaluateInstructionBlock(clazz, method, codeAttribute, 0);
146
147         // Evaluate the exception handlers.
148
codeAttribute.exceptionsAccept(clazz, method, this);
149     }
150
151
152     // Implementations for InstructionVisitor.
153

154     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
155     {
156         byte opcode = simpleInstruction.opcode;
157
158         // Some simple instructions exit from the current instruction block.
159
exitInstructionBlock =
160             opcode == InstructionConstants.OP_IRETURN ||
161             opcode == InstructionConstants.OP_LRETURN ||
162             opcode == InstructionConstants.OP_FRETURN ||
163             opcode == InstructionConstants.OP_DRETURN ||
164             opcode == InstructionConstants.OP_ARETURN ||
165             opcode == InstructionConstants.OP_RETURN ||
166             opcode == InstructionConstants.OP_ATHROW;
167     }
168
169     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
170     {
171         // Constant pool instructions never end the current instruction block.
172
exitInstructionBlock = false;
173     }
174
175     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
176     {
177         byte opcode = variableInstruction.opcode;
178
179         // The ret instruction end the current instruction block.
180
exitInstructionBlock =
181             opcode == InstructionConstants.OP_RET;
182     }
183
184     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
185     {
186         byte opcode = branchInstruction.opcode;
187
188         // Evaluate the target instruction blocks.
189
evaluateInstructionBlock(clazz,
190                                  method,
191                                  codeAttribute,
192                                  offset +
193                                  branchInstruction.branchOffset);
194
195         // Evaluate the instructions after a subroutine branch.
196
if (opcode == InstructionConstants.OP_JSR ||
197             opcode == InstructionConstants.OP_JSR_W)
198         {
199             // We assume subroutine calls (jsr and jsr_w instructions) don't
200
// change the stack, other than popping the return value.
201
stackSize -= 1;
202
203             evaluateInstructionBlock(clazz,
204                                      method,
205                                      codeAttribute,
206                                      offset + branchInstruction.length(offset));
207         }
208
209         // Some branch instructions always end the current instruction block.
210
exitInstructionBlock =
211             opcode == InstructionConstants.OP_GOTO ||
212             opcode == InstructionConstants.OP_GOTO_W ||
213             opcode == InstructionConstants.OP_JSR ||
214             opcode == InstructionConstants.OP_JSR_W;
215     }
216
217
218     public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
219     {
220         // Evaluate the target instruction blocks.
221

222         // Loop over all jump offsets.
223
int[] jumpOffsets = switchInstruction.jumpOffsets;
224
225         for (int index = 0; index < jumpOffsets.length; index++)
226         {
227             // Evaluate the jump instruction block.
228
evaluateInstructionBlock(clazz,
229                                      method,
230                                      codeAttribute,
231                                      offset + jumpOffsets[index]);
232         }
233
234         // Also evaluate the default instruction block.
235
evaluateInstructionBlock(clazz,
236                                  method,
237                                  codeAttribute,
238                                  offset + switchInstruction.defaultOffset);
239
240         // The switch instruction always ends the current instruction block.
241
exitInstructionBlock = true;
242     }
243
244
245     // Implementations for ExceptionInfoVisitor.
246

247     public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
248     {
249         if (DEBUG)
250         {
251             System.out.println("Exception:");
252         }
253
254         // The stack size when entering the exception handler is always 1.
255
stackSize = 1;
256
257         // Evaluate the instruction block starting at the entry point of the
258
// exception handler.
259
evaluateInstructionBlock(clazz,
260                                  method,
261                                  codeAttribute,
262                                  exceptionInfo.u2handlerPC);
263     }
264
265
266     // Small utility methods.
267

268     /**
269      * Evaluates a block of instructions that hasn't been handled before,
270      * starting at the given offset and ending a branch instruction, a return
271      * instruction, or a throw instruction. Branch instructions are handled
272      * recursively.
273      */

274     private void evaluateInstructionBlock(Clazz clazz,
275                                           Method method,
276                                           CodeAttribute codeAttribute,
277                                           int instructionOffset)
278     {
279         if (DEBUG)
280         {
281             if (evaluated[instructionOffset])
282             {
283                 System.out.println("-- (instruction block at "+instructionOffset+" already evaluated)");
284             }
285             else
286             {
287                 System.out.println("-- instruction block:");
288             }
289         }
290
291         // Remember the initial stack size.
292
int initialStackSize = stackSize;
293
294         // Remember the maximum stack size.
295
if (maxStackSize < stackSize)
296         {
297             maxStackSize = stackSize;
298         }
299
300         // Evaluate any instructions that haven't been evaluated before.
301
while (!evaluated[instructionOffset])
302         {
303             // Mark the instruction as evaluated.
304
evaluated[instructionOffset] = true;
305
306             Instruction instruction = InstructionFactory.create(codeAttribute.code,
307                                                                 instructionOffset);
308
309             if (DEBUG)
310             {
311                 int stackPushCount = instruction.stackPushCount(clazz);
312                 int stackPopCount = instruction.stackPopCount(clazz);
313                 System.out.println("["+instructionOffset+"]: "+
314                                    stackSize+" - "+
315                                    stackPopCount+" + "+
316                                    stackPushCount+" = "+
317                                    (stackSize+stackPushCount-stackPopCount)+": "+
318                                    instruction.toString(instructionOffset));
319             }
320
321             // Compute the instruction's effect on the stack size.
322
stackSizes[instructionOffset] =
323             stackSize += instruction.stackPushCount(clazz) -
324                          instruction.stackPopCount(clazz);
325
326             if (stackSize < 0)
327             {
328                 throw new IllegalArgumentException JavaDoc("Stack size becomes negative after instruction "+
329                                                    instruction.toString(instructionOffset)+" in ["+
330                                                    clazz.getName()+"."+
331                                                    method.getName(clazz)+
332                                                    method.getDescriptor(clazz)+"]");
333             }
334
335             // Remember the maximum stack size.
336
if (maxStackSize < stackSize)
337             {
338                 maxStackSize = stackSize;
339             }
340
341             // Remember the next instruction offset.
342
int nextInstructionOffset = instructionOffset +
343                                         instruction.length(instructionOffset);
344
345             // Visit the instruction, in order to handle branches.
346
instruction.accept(clazz, method, codeAttribute, instructionOffset, this);
347
348             // Stop evaluating after a branch.
349
if (exitInstructionBlock)
350             {
351                 break;
352             }
353
354             // Continue with the next instruction.
355
instructionOffset = nextInstructionOffset;
356
357             if (DEBUG)
358             {
359                 if (evaluated[instructionOffset])
360                 {
361                     System.out.println("-- (instruction at "+instructionOffset+" already evaluated)");
362                 }
363             }
364         }
365
366         // Restore the stack size for possible subsequent instruction blocks.
367
this.stackSize = initialStackSize;
368     }
369 }
370
Popular Tags