KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > preverify > CodeSubroutineInliner


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.preverify;
22
23 import proguard.classfile.util.*;
24 import proguard.classfile.attribute.visitor.*;
25 import proguard.classfile.attribute.*;
26 import proguard.classfile.instruction.visitor.InstructionVisitor;
27 import proguard.classfile.instruction.*;
28 import proguard.classfile.editor.*;
29 import proguard.classfile.*;
30 import proguard.classfile.visitor.*;
31 import proguard.optimize.peephole.BranchTargetFinder;
32
33 /**
34  * This AttributeVisitor inlines local subroutines (jsr/ret) in the code
35  * attributes that it visits.
36  *
37  * @author Eric Lafortune
38  */

39 public class CodeSubroutineInliner
40 extends SimplifiedVisitor
41 implements AttributeVisitor,
42              InstructionVisitor,
43              ExceptionInfoVisitor
44 {
45     //*
46
private static final boolean DEBUG = false;
47     /*/
48     private static boolean DEBUG = true;
49     //*/

50
51
52     private BranchTargetFinder branchTargetFinder = new BranchTargetFinder();
53     private CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer();
54
55     private boolean inlinedAny;
56     private ExceptionInfoVisitor subroutineExceptionInliner;
57
58
59     // Implementations for AttributeVisitor.
60

61     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
62
63
64     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
65     {
66 // DEBUG =
67
// clazz.getName().equals("abc/Def") &&
68
// method.getName(clazz).equals("abc");
69

70         branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);
71
72         // Don't bother if there aren't any subroutines anyway.
73
if (!containsSubroutines(codeAttribute))
74         {
75             return;
76         }
77
78         inlinedAny = false;
79         subroutineExceptionInliner = this;
80         codeAttributeComposer.reset();
81
82         // Append the body of the code.
83
copyCode(clazz, method, codeAttribute);
84
85         // Update the code attribute if any code has been inlined.
86
if (inlinedAny)
87         {
88             codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);
89         }
90     }
91
92
93     /**
94      * Returns whether the given code attribute contains any subroutines.
95      */

96     private boolean containsSubroutines(CodeAttribute codeAttribute)
97     {
98         for (int offset = 0; offset < codeAttribute.u4codeLength; offset++)
99         {
100             if (branchTargetFinder.isSubroutineInvocation(offset))
101             {
102                 return true;
103             }
104         }
105
106         return false;
107     }
108
109
110     /**
111      * Appends the code of the given code attribute.
112      */

113     private void copyCode(Clazz clazz,
114                           Method method,
115                           CodeAttribute codeAttribute)
116     {
117         if (DEBUG)
118         {
119             System.out.println("SubroutineInliner: processing ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
120         }
121
122         codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
123
124         // Copy the non-subroutine instructions.
125
int offset = 0;
126         while (offset < codeAttribute.u4codeLength)
127         {
128             Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
129             int instructionLength = instruction.length(offset);
130
131             // Is this returning subroutine?
132
if (branchTargetFinder.isSubroutine(offset) &&
133                 branchTargetFinder.isSubroutineReturning(offset))
134             {
135                 // Skip the subroutine.
136
if (DEBUG && branchTargetFinder.isSubroutineStart(offset))
137                 {
138                     System.out.println("Skipping subroutine at ["+offset+"]");
139                 }
140
141                 // Append a label at this offset instead.
142
codeAttributeComposer.appendLabel(offset);
143             }
144             else
145             {
146                 // Copy the instruction, inlining any subroutine call recursively.
147
instruction.accept(clazz, method, codeAttribute, offset, this);
148             }
149
150             offset += instructionLength;
151         }
152
153         // Copy the exceptions. Note that exceptions with empty try blocks
154
// are automatically removed.
155
codeAttribute.exceptionsAccept(clazz, method, this);
156
157         if (DEBUG)
158         {
159             System.out.println("Appending label after code at ["+offset+"]");
160         }
161
162         // Append a label just after the code.
163
codeAttributeComposer.appendLabel(codeAttribute.u4codeLength);
164
165         codeAttributeComposer.endCodeFragment();
166     }
167
168
169     /**
170      * Appends the specified subroutine.
171      */

172     private void inlineSubroutine(Clazz clazz,
173                                   Method method,
174                                   CodeAttribute codeAttribute,
175                                   int subroutineInvocationOffset,
176                                   int subroutineStart)
177     {
178         int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart);
179
180         if (DEBUG)
181         {
182             System.out.println("Inlining subroutine ["+subroutineStart+" -> "+subroutineEnd+"] at ["+subroutineInvocationOffset+"]");
183         }
184
185         // Don't go inlining exceptions that are already applicable to this
186
// subroutine invocation.
187
ExceptionInfoVisitor oldSubroutineExceptionInliner =
188             subroutineExceptionInliner;
189
190         subroutineExceptionInliner =
191             new ExceptionExcludedOffsetFilter(subroutineInvocationOffset,
192                                               subroutineExceptionInliner);
193
194         codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
195
196         // Copy the subroutine instructions, inlining any subroutine calls
197
// recursively.
198
codeAttribute.instructionsAccept(clazz,
199                                          method,
200                                          subroutineStart,
201                                          subroutineEnd,
202                                          this);
203
204         if (DEBUG)
205         {
206             System.out.println("Appending label after inlined subroutine at ["+subroutineEnd+"]");
207         }
208
209         // Append a label just after the code.
210
codeAttributeComposer.appendLabel(subroutineEnd);
211
212         // We can again inline exceptions that are applicable to this
213
// subroutine invocation.
214
subroutineExceptionInliner = oldSubroutineExceptionInliner;
215
216         // Inline the subroutine exceptions.
217
codeAttribute.exceptionsAccept(clazz,
218                                        method,
219                                        subroutineStart,
220                                        subroutineEnd,
221                                        subroutineExceptionInliner);
222
223         codeAttributeComposer.endCodeFragment();
224
225         inlinedAny = true;
226     }
227
228
229     // Implementations for InstructionVisitor.
230

231     public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
232     {
233         // Append the instruction.
234
codeAttributeComposer.appendInstruction(offset, instruction.shrink());
235     }
236
237
238     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
239     {
240         byte opcode = variableInstruction.opcode;
241         if (opcode == InstructionConstants.OP_RET)
242         {
243             // Is the return instruction the last instruction of the subroutine?
244
if (branchTargetFinder.subroutineEnd(offset) == offset + variableInstruction.length(offset))
245             {
246                 if (DEBUG)
247                 {
248                     System.out.println("Replacing subroutine return at ["+offset+"] by a label");
249                 }
250
251                 // Append a label at this offset instead of the subroutine return.
252
codeAttributeComposer.appendLabel(offset);
253             }
254             else
255             {
256                 if (DEBUG)
257                 {
258                     System.out.println("Replacing subroutine return at ["+offset+"] by a simple branch");
259                 }
260
261                 // Replace the instruction by a branch.
262
Instruction replacementInstruction =
263                     new BranchInstruction(InstructionConstants.OP_GOTO,
264                                           branchTargetFinder.subroutineEnd(offset) - offset).shrink();
265
266                 codeAttributeComposer.appendInstruction(offset, replacementInstruction);
267             }
268         }
269         else if (branchTargetFinder.isSubroutineStart(offset))
270         {
271             if (DEBUG)
272             {
273                 System.out.println("Replacing first subroutine instruction at ["+offset+"] by a label");
274             }
275
276             // Append a label at this offset instead of saving the subroutine
277
// return address.
278
codeAttributeComposer.appendLabel(offset);
279         }
280         else
281         {
282             // Append the instruction.
283
codeAttributeComposer.appendInstruction(offset, variableInstruction);
284         }
285     }
286
287
288     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
289     {
290         byte opcode = branchInstruction.opcode;
291         if (opcode == InstructionConstants.OP_JSR ||
292             opcode == InstructionConstants.OP_JSR_W)
293         {
294             int branchOffset = branchInstruction.branchOffset;
295             int branchTarget = offset + branchOffset;
296
297             // Is the subroutine ever returning?
298
if (branchTargetFinder.isSubroutineReturning(branchTarget))
299             {
300                 // Append a label at this offset instead of the subroutine invocation.
301
codeAttributeComposer.appendLabel(offset);
302
303                 // Inline the invoked subroutine.
304
inlineSubroutine(clazz,
305                                  method,
306                                  codeAttribute,
307                                  offset,
308                                  branchTarget);
309             }
310             else
311             {
312                 if (DEBUG)
313                 {
314                     System.out.println("Replacing subroutine invocation at ["+offset+"] by a simple branch");
315                 }
316
317                 // Replace the subroutine invocation by a simple branch.
318
Instruction replacementInstruction =
319                     new BranchInstruction(InstructionConstants.OP_GOTO,
320                                           branchOffset).shrink();
321
322                 codeAttributeComposer.appendInstruction(offset, replacementInstruction);
323
324                 inlinedAny = true;
325             }
326         }
327         else
328         {
329             // Append the instruction.
330
codeAttributeComposer.appendInstruction(offset, branchInstruction);
331         }
332     }
333
334
335     // Implementations for ExceptionInfoVisitor.
336

337     public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
338     {
339         int startPC = exceptionInfo.u2startPC;
340         int endPC = exceptionInfo.u2endPC;
341         int handlerPC = exceptionInfo.u2handlerPC;
342         int catchType = exceptionInfo.u2catchType;
343
344         // Exclude any subroutine invocations that jump out of the try block,
345
// by adding a try block before (and later on, after) each invocation.
346
int offset = startPC;
347         while (offset < endPC)
348         {
349             Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
350             int instructionLength = instruction.length(offset);
351
352             // Is it a subroutine invocation?
353
if (branchTargetFinder.isSubroutineInvocation(offset) &&
354                 !exceptionInfo.isApplicable(offset + ((BranchInstruction)instruction).branchOffset))
355             {
356                 // Append a try block that ends before the subroutine invocation.
357
codeAttributeComposer.appendException(new ExceptionInfo(startPC,
358                                                                         offset,
359                                                                         handlerPC,
360                                                                         catchType));
361
362                 // The next try block will start after the subroutine invocation.
363
startPC = offset + instructionLength;
364             }
365
366             offset += instructionLength;
367         }
368
369         // Append the exception. Note that exceptions with empty try blocks
370
// are automatically ignored.
371
codeAttributeComposer.appendException(new ExceptionInfo(startPC,
372                                                                 endPC,
373                                                                 handlerPC,
374                                                                 catchType));
375
376         // TODO: While inlining a subroutine, its exception handler code may have to be inlined too.
377
}
378 }
379
Popular Tags