• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 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.classfile.editor;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.CodeAttribute;
25 import proguard.classfile.attribute.visitor.AttributeVisitor;
26 import proguard.classfile.instruction.*;
27 import proguard.classfile.instruction.visitor.InstructionVisitor;
28 import proguard.classfile.util.SimplifiedVisitor;
29 
30 /**
31  * This InstructionVisitor writes out the instructions that it visits,
32  * collecting instructions that have to be widened. As an AttributeVisitor,
33  * it then applies the collected changes. The process will be repeated
34  * recursively, if necessary. The caller still has to update the frame sizes.
35  *
36  * @author Eric Lafortune
37  */
38 public class InstructionWriter
39 extends      SimplifiedVisitor
40 implements   InstructionVisitor,
41              AttributeVisitor
42 {
43     //*
44     private static final boolean DEBUG = false;
45     /*/
46     public  static       boolean DEBUG = false;
47     //*/
48 
49 
50     private int codeLength;
51 
52     private CodeAttributeEditor codeAttributeEditor;
53 
54 
55     /**
56      * Resets the accumulated code.
57      * @param codeLength the length of the code that will be edited next.
58      */
reset(int codeLength)59     public void reset(int codeLength)
60     {
61         this.codeLength = codeLength;
62 
63         if (codeAttributeEditor != null)
64         {
65             codeAttributeEditor.reset(codeLength);
66         }
67     }
68 
69 
70     /**
71      * Extends the size of the accumulated code.
72      * @param codeLength the length of the code that will be edited next.
73      */
extend(int codeLength)74     public void extend(int codeLength)
75     {
76         this.codeLength = codeLength;
77 
78         if (codeAttributeEditor != null)
79         {
80             codeAttributeEditor.extend(codeLength);
81         }
82     }
83 
84 
85     // Implementations for InstructionVisitor.
86 
visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)87     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
88     {
89         // Try to write out the instruction.
90         // Simple instructions should always fit.
91         simpleInstruction.write(codeAttribute, offset);
92     }
93 
94 
visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)95     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
96     {
97         try
98         {
99             // Try to write out the instruction.
100             constantInstruction.write(codeAttribute, offset);
101         }
102         catch (IllegalArgumentException exception)
103         {
104             // Create a new constant instruction that will fit.
105             Instruction replacementInstruction =
106                 new ConstantInstruction(constantInstruction.opcode,
107                                         constantInstruction.constantIndex,
108                                         constantInstruction.constant);
109 
110             if (DEBUG)
111             {
112                 System.out.println("  "+constantInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString());
113             }
114 
115             replaceInstruction(offset, replacementInstruction);
116 
117             // Write out a dummy constant instruction for now.
118             constantInstruction.constantIndex = 0;
119             constantInstruction.constant      = 0;
120             constantInstruction.write(codeAttribute, offset);
121         }
122     }
123 
124 
visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)125     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
126     {
127         try
128         {
129             // Try to write out the instruction.
130             variableInstruction.write(codeAttribute, offset);
131         }
132         catch (IllegalArgumentException exception)
133         {
134             // Create a new variable instruction that will fit.
135             Instruction replacementInstruction =
136                 new VariableInstruction(variableInstruction.opcode,
137                                         variableInstruction.variableIndex,
138                                         variableInstruction.constant);
139 
140             replaceInstruction(offset, replacementInstruction);
141 
142             if (DEBUG)
143             {
144                 System.out.println("  "+variableInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString());
145             }
146 
147             // Write out a dummy variable instruction for now.
148             variableInstruction.variableIndex = 0;
149             variableInstruction.constant      = 0;
150             variableInstruction.write(codeAttribute, offset);
151         }
152     }
153 
154 
visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)155     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
156     {
157         try
158         {
159             // Try to write out the instruction.
160             branchInstruction.write(codeAttribute, offset);
161         }
162         catch (IllegalArgumentException exception)
163         {
164             // Create a new unconditional branch that will fit.
165             Instruction replacementInstruction =
166                 new BranchInstruction(InstructionConstants.OP_GOTO_W,
167                                       branchInstruction.branchOffset);
168 
169             // Create a new instruction that will fit.
170             switch (branchInstruction.opcode)
171             {
172                 default:
173                 {
174                     // Create a new branch instruction that will fit.
175                     replacementInstruction =
176                         new BranchInstruction(branchInstruction.opcode,
177                                               branchInstruction.branchOffset);
178 
179                     break;
180                 }
181 
182                 // Some special cases, for which a wide branch doesn't exist.
183                 case InstructionConstants.OP_IFEQ:
184                 case InstructionConstants.OP_IFNE:
185                 case InstructionConstants.OP_IFLT:
186                 case InstructionConstants.OP_IFGE:
187                 case InstructionConstants.OP_IFGT:
188                 case InstructionConstants.OP_IFLE:
189                 case InstructionConstants.OP_IFICMPEQ:
190                 case InstructionConstants.OP_IFICMPNE:
191                 case InstructionConstants.OP_IFICMPLT:
192                 case InstructionConstants.OP_IFICMPGE:
193                 case InstructionConstants.OP_IFICMPGT:
194                 case InstructionConstants.OP_IFICMPLE:
195                 case InstructionConstants.OP_IFACMPEQ:
196                 case InstructionConstants.OP_IFACMPNE:
197                 {
198                     // Insert the complementary conditional branch.
199                     Instruction complementaryConditionalBranch =
200                         new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1),
201                                               (1+2) + (1+4));
202 
203                     insertBeforeInstruction(offset, complementaryConditionalBranch);
204 
205                     // Create a new unconditional branch that will fit.
206                     break;
207                 }
208 
209                 case InstructionConstants.OP_IFNULL:
210                 case InstructionConstants.OP_IFNONNULL:
211                 {
212                     // Insert the complementary conditional branch.
213                     Instruction complementaryConditionalBranch =
214                         new BranchInstruction((byte)(branchInstruction.opcode ^ 1),
215                                               (1+2) + (1+4));
216 
217                     insertBeforeInstruction(offset, complementaryConditionalBranch);
218 
219                     // Create a new unconditional branch that will fit.
220                     break;
221                 }
222             }
223 
224             if (DEBUG)
225             {
226                 System.out.println("  "+branchInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString());
227             }
228 
229             replaceInstruction(offset, replacementInstruction);
230 
231             // Write out a dummy branch instruction for now.
232             branchInstruction.branchOffset = 0;
233             branchInstruction.write(codeAttribute, offset);
234         }
235     }
236 
237 
visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)238     public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
239     {
240         // Try to write out the instruction.
241         // Switch instructions should always fit.
242         switchInstruction.write(codeAttribute, offset);
243     }
244 
245 
246     // Implementations for AttributeVisitor.
247 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)248     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
249     {
250         // Avoid doing any work if nothing is changing anyway.
251         if (codeAttributeEditor != null)
252         {
253             if (DEBUG)
254             {
255                 System.out.println("InstructionWriter: widening instructions in "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
256             }
257 
258             // Apply the collected expansions.
259             codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
260 
261             // Don't keep the editor around. We're assuming it won't be needed
262             // very often, so we don't want to be resetting it all the time.
263             codeAttributeEditor = null;
264         }
265     }
266 
267 
268     // Small utility methods.
269 
270     /**
271      * Remembers to place the given instruction right before the instruction
272      * at the given offset.
273      */
insertBeforeInstruction(int instructionOffset, Instruction instruction)274     private void insertBeforeInstruction(int instructionOffset, Instruction instruction)
275     {
276         ensureCodeAttributeEditor();
277 
278         // Replace the instruction.
279         codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction);
280     }
281 
282 
283     /**
284      * Remembers to replace the instruction at the given offset by the given
285      * instruction.
286      */
replaceInstruction(int instructionOffset, Instruction instruction)287     private void replaceInstruction(int instructionOffset, Instruction instruction)
288     {
289         ensureCodeAttributeEditor();
290 
291         // Replace the instruction.
292         codeAttributeEditor.replaceInstruction(instructionOffset, instruction);
293     }
294 
295 
296     /**
297      * Remembers to place the given instruction right after the instruction
298      * at the given offset.
299      */
insertAfterInstruction(int instructionOffset, Instruction instruction)300     private void insertAfterInstruction(int instructionOffset, Instruction instruction)
301     {
302         ensureCodeAttributeEditor();
303 
304         // Replace the instruction.
305         codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction);
306     }
307 
308 
309     /**
310      * Makes sure there is a code attribute editor for the given code attribute.
311      */
ensureCodeAttributeEditor()312     private void ensureCodeAttributeEditor()
313     {
314         if (codeAttributeEditor == null)
315         {
316             codeAttributeEditor = new CodeAttributeEditor(false, true);
317             codeAttributeEditor.reset(codeLength);
318         }
319     }
320 }
321