KickJava   Java API By Example, From Geeks To Geeks.

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


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 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  */

21 package proguard.optimize.peephole;
22
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.visitor.AttributeVisitor;
26 import proguard.classfile.editor.CodeAttributeEditor;
27 import proguard.classfile.instruction.*;
28 import proguard.classfile.instruction.visitor.InstructionVisitor;
29 import proguard.classfile.util.SimplifiedVisitor;
30
31 /**
32  * This AttributeVisitor redirects unconditional branches so any common code
33  * is shared, and the code preceding the branch can be removed, in the code
34  * attributes that it visits.
35  *
36  * @author Eric Lafortune
37  */

38 public class GotoCommonCodeReplacer
39 extends SimplifiedVisitor
40 implements AttributeVisitor,
41              InstructionVisitor
42 {
43     private static final boolean DEBUG = false;
44
45
46     private InstructionVisitor extraInstructionVisitor;
47
48     private BranchTargetFinder branchTargetFinder = new BranchTargetFinder();
49     private CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
50
51
52     /**
53      * Creates a new GotoCommonCodeReplacer.
54      * @param extraInstructionVisitor an optional extra visitor for all replaced
55      * goto instructions.
56      */

57     public GotoCommonCodeReplacer(InstructionVisitor extraInstructionVisitor)
58     {
59         this.extraInstructionVisitor = extraInstructionVisitor;
60     }
61
62
63     // Implementations for AttributeVisitor.
64

65     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
66
67
68     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
69     {
70         // Mark all branch targets.
71
branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);
72
73         // Reset the code attribute editor.
74
codeAttributeEditor.reset(codeAttribute.u4codeLength);
75
76         // Remap the variables of the instructions.
77
codeAttribute.instructionsAccept(clazz, method, this);
78
79         // Apply the code atribute editor.
80
codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
81     }
82
83
84     // Implementations for InstructionVisitor.
85

86     public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
87
88
89     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
90     {
91         // Check if the instruction is an unconditional goto instruction that
92
// isn't the target of a branch itself.
93
byte opcode = branchInstruction.opcode;
94         if ((opcode == InstructionConstants.OP_GOTO ||
95              opcode == InstructionConstants.OP_GOTO_W) &&
96             !branchTargetFinder.isBranchTarget(offset))
97         {
98             int branchOffset = branchInstruction.branchOffset;
99             int targetOffset = offset + branchOffset;
100
101             // Get the number of common bytes.
102
int commonCount = commonByteCodeCount(codeAttribute, offset, targetOffset);
103
104             if (commonCount > 0 &&
105                 !exceptionBoundary(codeAttribute, offset, targetOffset))
106             {
107                 if (DEBUG)
108                 {
109                     System.out.println("GotoCommonCodeReplacer: "+clazz.getName()+"."+method.getName(clazz)+" ("+commonCount+" instructions)");
110                 }
111
112                 // Delete the common instructions.
113
for (int delta = 0; delta <= commonCount; delta++)
114                 {
115                     int deleteOffset = offset - delta;
116                     if (branchTargetFinder.isInstruction(deleteOffset))
117                     {
118                         codeAttributeEditor.replaceInstruction( deleteOffset, null);
119                         codeAttributeEditor.insertBeforeInstruction(deleteOffset, null);
120                         codeAttributeEditor.insertAfterInstruction( deleteOffset, null);
121
122                         codeAttributeEditor.deleteInstruction(deleteOffset);
123                     }
124                 }
125
126                 // Redirect the goto instruction, if it is still necessary.
127
int newBranchOffset = branchOffset - commonCount;
128                 if (newBranchOffset != branchInstruction.length(offset))
129                 {
130                     Instruction newGotoInstruction =
131                          new BranchInstruction(opcode, newBranchOffset);
132                     codeAttributeEditor.replaceInstruction(offset,
133                                                            newGotoInstruction);
134                 }
135
136                 // Visit the instruction, if required.
137
if (extraInstructionVisitor != null)
138                 {
139                     extraInstructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction);
140                 }
141             }
142         }
143     }
144
145
146     // Small utility methods.
147

148     /**
149      * Returns the number of common bytes preceding the given offsets,
150      * avoiding branches and exception blocks.
151      */

152     private int commonByteCodeCount(CodeAttribute codeAttribute, int offset1, int offset2)
153     {
154         // Find the block of common instructions preceding it.
155
byte[] code = codeAttribute.code;
156
157         int successfulDelta = 0;
158
159         for (int delta = 1;
160              delta <= offset1 &&
161              delta <= offset2 &&
162              offset2 - delta != offset1;
163              delta++)
164         {
165             int newOffset1 = offset1 - delta;
166             int newOffset2 = offset2 - delta;
167
168             // Is the code identical at both offsets?
169
if (code[newOffset1] != code[newOffset2])
170             {
171                 break;
172             }
173
174             // Are there instructions at either offset but not both?
175
if (branchTargetFinder.isInstruction(newOffset1) ^
176                 branchTargetFinder.isInstruction(newOffset2))
177             {
178                 break;
179             }
180
181             // Are there instructions at both offsets?
182
if (branchTargetFinder.isInstruction(newOffset1) &&
183                 branchTargetFinder.isInstruction(newOffset2))
184             {
185                 // Are the offsets involved in some branches?
186
// Note that the preverifier doesn't like initializer
187
// invocations to be moved around.
188
// Also note that the preverifier doesn't like pop instructions
189
// that work on different operands.
190
if (branchTargetFinder.isBranchOrigin(newOffset1) ||
191                     branchTargetFinder.isBranchTarget(newOffset1) ||
192                     branchTargetFinder.isExceptionStart(newOffset1) ||
193                     branchTargetFinder.isExceptionEnd(newOffset1) ||
194                     branchTargetFinder.isInitializer(newOffset1) ||
195                     branchTargetFinder.isExceptionStart(newOffset2) ||
196                     branchTargetFinder.isExceptionEnd(newOffset2) ||
197                     isPop(code[newOffset1]))
198                 {
199                     break;
200                 }
201
202                 // Make sure the new branch target was a branch target before,
203
// in order not to introduce new entries in the stack map table.
204
if (branchTargetFinder.isBranchTarget(newOffset2))
205                 {
206                     successfulDelta = delta;
207                 }
208
209                 if (branchTargetFinder.isBranchTarget(newOffset1))
210                 {
211                     break;
212                 }
213             }
214         }
215
216         return successfulDelta;
217     }
218
219
220     /**
221      * Returns whether the given opcode represents a pop instruction that must
222      * get a consistent type (pop, pop2, arraylength).
223      */

224     private boolean isPop(byte opcode)
225     {
226         return opcode == InstructionConstants.OP_POP ||
227                opcode == InstructionConstants.OP_POP2 ||
228                opcode == InstructionConstants.OP_ARRAYLENGTH;
229     }
230
231
232     /**
233      * Returns the whether there is a boundary of an exception block between
234      * the given offsets (including both).
235      */

236     private boolean exceptionBoundary(CodeAttribute codeAttribute, int offset1, int offset2)
237     {
238         // Swap the offsets if the second one is smaller than the first one.
239
if (offset2 < offset1)
240         {
241             int offset = offset1;
242             offset1 = offset2;
243             offset2 = offset;
244         }
245
246         // Check if there is a boundary of an exception block.
247
for (int offset = offset1; offset <= offset2; offset++)
248         {
249             if (branchTargetFinder.isExceptionStart(offset) ||
250                 branchTargetFinder.isExceptionEnd(offset))
251             {
252                 return true;
253             }
254         }
255
256         return false;
257     }
258 }
259
Popular Tags