KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > optimize > peephole > MethodInliner


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.optimize.peephole;
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.classfile.constant.visitor.ConstantVisitor;
32 import proguard.classfile.constant.*;
33 import proguard.optimize.info.*;
34
35 import java.util.Stack JavaDoc;
36
37 /**
38  * This AttributeVisitor inlines short methods in the code attributes that it
39  * visits.
40  *
41  * @author Eric Lafortune
42  */

43 public class MethodInliner
44 extends SimplifiedVisitor
45 implements AttributeVisitor,
46              InstructionVisitor,
47              ExceptionInfoVisitor,
48              ConstantVisitor,
49              MemberVisitor
50 {
51     private static final int MAXIMUM_INLINING_CODE_LENGTH = 8;
52     private static final int MAXIMUM_CODE_EXPANSION = 2;
53     private static final int MAXIMUM_EXTRA_CODE_LENGTH = 128;
54
55     //*
56
private static final boolean DEBUG = false;
57     /*/
58     private static boolean DEBUG = true;
59     //*/

60
61
62     private boolean allowAccessModification;
63     private boolean inlineSingleInvocations;
64     private InstructionVisitor extraInlinedInvocationVisitor;
65
66     private CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer();
67     private AccessMethodMarker accessMethodMarker = new AccessMethodMarker();
68     private CatchExceptionMarker catchExceptionMarker = new CatchExceptionMarker();
69     private ConstantAdder constantAdder = new ConstantAdder();
70     private StackSizeComputer stackSizeComputer = new StackSizeComputer();
71
72     private Clazz targetClass;
73     private Method targetMethod;
74     private boolean inlining;
75     private Stack JavaDoc inliningMethods = new Stack JavaDoc();
76     private boolean emptyInvokingStack;
77     private int uninitializedObjectCount;
78     private int variableOffset;
79     private boolean inlined;
80     private boolean inlinedAny;
81
82
83     /**
84      * Creates a new MethodInliner.
85      * @param allowAccessModification indicates whether the access modifiers of
86      * classes and class members can be changed
87      * in order to inline methods.
88      */

89     public MethodInliner(boolean allowAccessModification,
90                          boolean inlineSingleInvocations)
91     {
92         this(allowAccessModification, inlineSingleInvocations, null);
93     }
94
95
96     /**
97      * Creates a new MethodInliner.
98      * @param allowAccessModification indicates whether the access modifiers of
99      * classes and class members can be changed
100      * in order to inline methods.
101      * @param allowAccessModification indicates whether the single invocations
102      * should be inlined, or, alternatively,
103      * short methods.
104      * @param extraInlinedInvocationVisitor an optional extra visitor for all
105      * inlined invocation instructions.
106      */

107     public MethodInliner(boolean allowAccessModification,
108                          boolean inlineSingleInvocations,
109                          InstructionVisitor extraInlinedInvocationVisitor)
110     {
111         this.allowAccessModification = allowAccessModification;
112         this.inlineSingleInvocations = inlineSingleInvocations;
113         this.extraInlinedInvocationVisitor = extraInlinedInvocationVisitor;
114     }
115
116
117     // Implementations for AttributeVisitor.
118

119     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
120
121
122     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
123     {
124         if (!inlining)
125         {
126 // codeAttributeComposer.DEBUG = DEBUG =
127
// clazz.getName().equals("abc/Def") &&
128
// method.getName(clazz).equals("abc");
129
// codeAttributeComposer.DEBUG = DEBUG =
130
// clazz.getName().equals("uibrowser/client/layout/grouplayout/Baseline") &&
131
// method.getName(clazz).equals("abc");
132

133             targetClass = clazz;
134             targetMethod = method;
135             inliningMethods.clear();
136             uninitializedObjectCount = method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 1 : 0;
137             inlinedAny = false;
138             codeAttributeComposer.reset();
139             constantAdder.setTargetClass((ProgramClass)clazz);
140             stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute);
141
142             // Append the body of the code.
143
copyCode(clazz, method, codeAttribute);
144
145             targetClass = null;
146             targetMethod = null;
147             constantAdder.setTargetClass(null);
148
149             // Update the code attribute if any code has been inlined.
150
if (inlinedAny)
151             {
152                 codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);
153
154                 // Update the accessing flags.
155
codeAttribute.instructionsAccept(clazz, method, accessMethodMarker);
156
157                 // Update the exception catching flags.
158
catchExceptionMarker.visitCodeAttribute(clazz, method, codeAttribute);
159             }
160         }
161
162         // Only inline the method if it is invoked once or if it is short.
163
// TODO: Check total code length after inlining.
164
else if (inlineSingleInvocations ?
165             MethodInvocationMarker.getInvocationCount(method) == 1 :
166             codeAttribute.u4codeLength <= MAXIMUM_INLINING_CODE_LENGTH)
167         {
168             if (DEBUG)
169             {
170                 System.out.println("MethodInliner: inlining ["+
171                                    clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] in ["+
172                                    targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]");
173             }
174
175             // Append instructions to store the parameters.
176
storeParameters(clazz, method);
177
178             // Inline the body of the code.
179
copyCode(clazz, method, codeAttribute);
180
181             inlined = true;
182             inlinedAny = true;
183         }
184     }
185
186
187     /**
188      * Appends instructions to pop the parameters for the given method, storing
189      * them in new local variables.
190      */

191     private void storeParameters(Clazz clazz, Method method)
192     {
193
194         String JavaDoc descriptor = method.getDescriptor(clazz);
195
196         boolean isStatic =
197             (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0;
198
199         // Count the number of parameters, taking into account their categories.
200
int parameterCount = ClassUtil.internalMethodParameterCount(descriptor);
201         int parameterSize = ClassUtil.internalMethodParameterSize(descriptor);
202         int parameterOffset = isStatic ? 0 : 1;
203
204         // Store the parameter types.
205
String JavaDoc[] parameterTypes = new String JavaDoc[parameterSize];
206
207         InternalTypeEnumeration internalTypeEnumeration =
208             new InternalTypeEnumeration(descriptor);
209
210         for (int parameterIndex = 0; parameterIndex < parameterSize; parameterIndex++)
211         {
212             String JavaDoc parameterType = internalTypeEnumeration.nextType();
213             parameterTypes[parameterIndex] = parameterType;
214             if (ClassUtil.internalTypeSize(parameterType) == 2)
215             {
216                 parameterIndex++;
217             }
218         }
219
220         codeAttributeComposer.beginCodeFragment((parameterOffset + parameterCount) * 4);
221
222         // Go over the parameter types backward, storing the stack entries
223
// in their corresponding variables.
224
for (int parameterIndex = parameterSize-1; parameterIndex >= 0; parameterIndex--)
225         {
226             String JavaDoc parameterType = parameterTypes[parameterIndex];
227             if (parameterType != null)
228             {
229                 byte opcode;
230                 switch (parameterType.charAt(0))
231                 {
232                     case ClassConstants.INTERNAL_TYPE_BOOLEAN:
233                     case ClassConstants.INTERNAL_TYPE_BYTE:
234                     case ClassConstants.INTERNAL_TYPE_CHAR:
235                     case ClassConstants.INTERNAL_TYPE_SHORT:
236                     case ClassConstants.INTERNAL_TYPE_INT:
237                         opcode = InstructionConstants.OP_ISTORE;
238                         break;
239
240                     case ClassConstants.INTERNAL_TYPE_LONG:
241                         opcode = InstructionConstants.OP_LSTORE;
242                         break;
243
244                     case ClassConstants.INTERNAL_TYPE_FLOAT:
245                         opcode = InstructionConstants.OP_FSTORE;
246                         break;
247
248                     case ClassConstants.INTERNAL_TYPE_DOUBLE:
249                         opcode = InstructionConstants.OP_DSTORE;
250                         break;
251
252                     default:
253                         opcode = InstructionConstants.OP_ASTORE;
254                         break;
255                 }
256
257                 codeAttributeComposer.appendInstruction(parameterSize-parameterIndex-1,
258                                                         new VariableInstruction(opcode, variableOffset + parameterOffset + parameterIndex).shrink());
259             }
260         }
261
262         // Put the 'this' reference in variable 0 (plus offset).
263
if (!isStatic)
264         {
265             codeAttributeComposer.appendInstruction(parameterSize,
266                                                     new VariableInstruction(InstructionConstants.OP_ASTORE, variableOffset).shrink());
267         }
268
269         codeAttributeComposer.endCodeFragment();
270     }
271
272
273     /**
274      * Appends the code of the given code attribute.
275      */

276     private void copyCode(Clazz clazz, Method method, CodeAttribute codeAttribute)
277     {
278         // The code may expand, due to expanding constant and variable
279
// instructions.
280
codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength * MAXIMUM_CODE_EXPANSION +
281                                                 MAXIMUM_EXTRA_CODE_LENGTH);
282
283         // Copy the instructions.
284
codeAttribute.instructionsAccept(clazz, method, this);
285
286         // Copy the exceptions.
287
codeAttribute.exceptionsAccept(clazz, method, this);
288
289         // Append a label just after the code.
290
codeAttributeComposer.appendLabel(codeAttribute.u4codeLength);
291
292         codeAttributeComposer.endCodeFragment();
293     }
294
295
296     // Implementations for InstructionVisitor.
297

298     public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
299     {
300         codeAttributeComposer.appendInstruction(offset, instruction.shrink());
301     }
302
303
304     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
305     {
306         // Are we inlining this instruction?
307
if (inlining)
308         {
309             // Replace any return instructions by branches to the end of the code.
310
switch (simpleInstruction.opcode)
311             {
312                 case InstructionConstants.OP_IRETURN:
313                 case InstructionConstants.OP_LRETURN:
314                 case InstructionConstants.OP_FRETURN:
315                 case InstructionConstants.OP_DRETURN:
316                 case InstructionConstants.OP_ARETURN:
317                 case InstructionConstants.OP_RETURN:
318                     // Are we not at the last instruction?
319
if (offset < codeAttribute.u4codeLength-1)
320                     {
321                         // Replace the return instruction by a branch instruction.
322
Instruction branchInstruction =
323                             new BranchInstruction(InstructionConstants.OP_GOTO_W,
324                                                   codeAttribute.u4codeLength - offset);
325
326                         codeAttributeComposer.appendInstruction(offset,
327                                                                 branchInstruction.shrink());
328                     }
329                     else
330                     {
331                         // Just leave out the instruction, but put in a label,
332
// for the sake of any other branch instructions.
333
codeAttributeComposer.appendLabel(offset);
334                     }
335
336                     return;
337             }
338         }
339
340         codeAttributeComposer.appendInstruction(offset, simpleInstruction.shrink());
341     }
342
343
344     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
345     {
346         // Are we inlining this instruction?
347
if (inlining)
348         {
349             // Update the variable index.
350
variableInstruction.variableIndex += variableOffset;
351         }
352
353         codeAttributeComposer.appendInstruction(offset, variableInstruction.shrink());
354     }
355
356
357     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
358     {
359         // Is it a method invocation?
360
switch (constantInstruction.opcode)
361         {
362             case InstructionConstants.OP_NEW:
363                 uninitializedObjectCount++;
364                 break;
365
366             case InstructionConstants.OP_INVOKEVIRTUAL:
367             case InstructionConstants.OP_INVOKESPECIAL:
368             case InstructionConstants.OP_INVOKESTATIC:
369             case InstructionConstants.OP_INVOKEINTERFACE:
370                 // See if we can inline it.
371
inlined = false;
372
373                 // Append a label, in case the invocation will be inlined.
374
codeAttributeComposer.appendLabel(offset);
375
376                 emptyInvokingStack =
377                     !inlining &&
378                     stackSizeComputer.isReachable(offset) &&
379                     stackSizeComputer.getStackSize(offset) == 0;
380
381                 variableOffset += codeAttribute.u2maxLocals;
382
383                 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
384
385                 variableOffset -= codeAttribute.u2maxLocals;
386
387                 // Was the method inlined?
388
if (inlined)
389                 {
390                     if (extraInlinedInvocationVisitor != null)
391                     {
392                         extraInlinedInvocationVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction);
393                     }
394
395                     // The invocation itself is no longer necessary.
396
return;
397                 }
398
399                 break;
400         }
401
402         // Are we inlining this instruction?
403
if (inlining)
404         {
405             // Make sure the constant is present in the constant pool of the
406
// target class.
407
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantAdder);
408
409             // Let the instruction point to this constant.
410
constantInstruction.constantIndex = constantAdder.getConstantIndex();
411         }
412
413         codeAttributeComposer.appendInstruction(offset, constantInstruction.shrink());
414     }
415
416
417     // Implementations for ExceptionInfoVisitor.
418

419     public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
420     {
421         int catchType = exceptionInfo.u2catchType;
422
423         if (inlining && catchType != 0)
424         {
425             // Make sure the constant is present in the constant pool of the
426
// target class.
427
clazz.constantPoolEntryAccept(catchType, constantAdder);
428
429             // Let the exception point to this constant.
430
catchType = constantAdder.getConstantIndex();
431         }
432
433         codeAttributeComposer.appendException(new ExceptionInfo(exceptionInfo.u2startPC,
434                                                                 exceptionInfo.u2endPC,
435                                                                 exceptionInfo.u2handlerPC,
436                                                                 catchType));
437     }
438
439
440     // Implementations for ConstantVisitor.
441

442     public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) {}
443
444
445     public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
446     {
447         methodrefConstant.referencedMemberAccept(this);
448     }
449
450
451     // Implementations for MemberVisitor.
452

453     public void visitAnyMember(Clazz Clazz, Member member) {}
454
455
456     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
457     {
458         int accessFlags = programMethod.getAccessFlags();
459
460         if (// Only inline the method if it is private, static, or final.
461
(accessFlags & (ClassConstants.INTERNAL_ACC_PRIVATE |
462                             ClassConstants.INTERNAL_ACC_STATIC |
463                             ClassConstants.INTERNAL_ACC_FINAL)) != 0 &&
464
465             // Only inline the method if it is not synchronized, etc.
466
(accessFlags & (ClassConstants.INTERNAL_ACC_SYNCHRONIZED |
467                             ClassConstants.INTERNAL_ACC_NATIVE |
468                             ClassConstants.INTERNAL_ACC_INTERFACE |
469                             ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 &&
470
471             // Don't inline an <init> method, except in an <init> method in the
472
// same class.
473
// (!programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ||
474
// (programClass.equals(targetClass) &&
475
// targetMethod.getName(targetClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))) &&
476
!programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) &&
477
478             // Don't inline a method into itself.
479
(!programMethod.equals(targetMethod) ||
480              !programClass.equals(targetClass)) &&
481
482             // Only inline the method if it isn't recursing.
483
!inliningMethods.contains(programMethod) &&
484
485             // Only inline the method if it doesn't invoke a super method, or if
486
// it is in the same class.
487
(!SuperInvocationMarker.invokesSuperMethods(programMethod) ||
488              programClass.equals(targetClass)) &&
489
490             // Only inline the method if it doesn't branch backward while there
491
// are uninitialized objects.
492
(!BackwardBranchMarker.branchesBackward(programMethod) ||
493              uninitializedObjectCount == 0) &&
494
495             // Only inline if the code access of the inlined method allows it.
496
(allowAccessModification ||
497              ((!AccessMethodMarker.accessesPrivateCode(programMethod) ||
498                programClass.equals(targetClass)) &&
499
500               (!AccessMethodMarker.accessesPackageCode(programMethod) ||
501                ClassUtil.internalPackageName(programClass.getName()).equals(
502                ClassUtil.internalPackageName(targetClass.getName()))))) &&
503
504 // (!AccessMethodMarker.accessesProtectedCode(programMethod) ||
505
// targetClass.extends_(programClass) ||
506
// targetClass.implements_(programClass)) ||
507
(!AccessMethodMarker.accessesProtectedCode(programMethod) ||
508              programClass.equals(targetClass)) &&
509
510             // Only inline the method if it doesn't catch exceptions, or if it
511
// is invoked with an empty stack.
512
(!CatchExceptionMarker.catchesExceptions(programMethod) ||
513              emptyInvokingStack) &&
514
515             // Only inline the method if it comes from the same class or from
516
// a class with a static initializer.
517
(programClass.equals(targetClass) ||
518              programClass.findMethod(ClassConstants.INTERNAL_METHOD_NAME_CLINIT,
519                                      ClassConstants.INTERNAL_METHOD_TYPE_CLINIT) == null))
520         {
521 // System.out.print("MethodInliner: inlining ");
522
// programMethod.accept(programClass, new SimpleClassPrinter(true));
523
// System.out.print(" in ");
524
// targetMethod.accept(targetClass, new SimpleClassPrinter(true));
525
//
526
// System.out.println(" Private: "+
527
// (!AccessMethodMarker.accessesPrivateCode(programMethod) ||
528
// programClass.equals(targetClass)));
529
//
530
// System.out.println(" Package: "+
531
// (!AccessMethodMarker.accessesPackageCode(programMethod) ||
532
// ClassUtil.internalPackageName(programClass.getName()).equals(
533
// ClassUtil.internalPackageName(targetClass.getName()))));
534
//
535
// System.out.println(" Protected: "+
536
// ((!AccessMethodMarker.accessesProtectedCode(programMethod) ||
537
// targetClass.extends_(programClass) ||
538
// targetClass.implements_(programClass)) ||
539
// ClassUtil.internalPackageName(programClass.getName()).equals(
540
// ClassUtil.internalPackageName(targetClass.getName()))));
541

542             boolean oldInlining = inlining;
543             inlining = true;
544             inliningMethods.push(programMethod);
545
546             // Inline the method body.
547
programMethod.attributesAccept(programClass, this);
548
549             inlining = oldInlining;
550             inliningMethods.pop();
551         }
552         else if (programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
553         {
554             uninitializedObjectCount--;
555         }
556     }
557 }
558
Popular Tags