1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 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 */
21package proguard.classfile.editor;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.CodeAttribute;
25import proguard.classfile.attribute.visitor.AttributeVisitor;
26import proguard.classfile.instruction.*;
27import proguard.classfile.instruction.visitor.InstructionVisitor;
28import 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.
35 *
36 * @author Eric Lafortune
37 */
38public class InstructionWriter
39extends      SimplifiedVisitor
40implements   InstructionVisitor,
41             AttributeVisitor
42{
43    private int codeLength;
44
45    private CodeAttributeEditor codeAttributeEditor;
46
47
48    /**
49     * Resets the accumulated code changes.
50     * @param codeLength the length of the code that will be edited next.
51     */
52    public void reset(int codeLength)
53    {
54        this.codeLength = codeLength;
55
56        // The code attribute editor has to be created lazily.
57        if (codeAttributeEditor != null)
58        {
59            codeAttributeEditor.reset(codeLength);
60        }
61    }
62
63
64    // Implementations for InstructionVisitor.
65
66    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
67    {
68        // Try to write out the instruction.
69        // Simple instructions should always fit.
70        simpleInstruction.write(codeAttribute, offset);
71    }
72
73
74    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
75    {
76        try
77        {
78            // Try to write out the instruction.
79            constantInstruction.write(codeAttribute, offset);
80        }
81        catch (IllegalArgumentException exception)
82        {
83            // Create a new constant instruction that will fit.
84            Instruction replacementInstruction =
85                new ConstantInstruction(constantInstruction.opcode,
86                                        constantInstruction.constantIndex,
87                                        constantInstruction.constant).shrink();
88
89            replaceInstruction(offset, replacementInstruction);
90
91            // Write out a dummy constant instruction for now.
92            constantInstruction.constantIndex = 0;
93            constantInstruction.constant      = 0;
94            constantInstruction.write(codeAttribute, offset);
95        }
96    }
97
98
99    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
100    {
101        try
102        {
103            // Try to write out the instruction.
104            variableInstruction.write(codeAttribute, offset);
105        }
106        catch (IllegalArgumentException exception)
107        {
108            // Create a new variable instruction that will fit.
109            Instruction replacementInstruction =
110                new VariableInstruction(variableInstruction.opcode,
111                                        variableInstruction.variableIndex,
112                                        variableInstruction.constant).shrink();
113
114            replaceInstruction(offset, replacementInstruction);
115
116            // Write out a dummy variable instruction for now.
117            variableInstruction.variableIndex = 0;
118            variableInstruction.constant      = 0;
119            variableInstruction.write(codeAttribute, offset);
120        }
121    }
122
123
124    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
125    {
126        try
127        {
128            // Try to write out the instruction.
129            branchInstruction.write(codeAttribute, offset);
130        }
131        catch (IllegalArgumentException exception)
132        {
133            // Create a new unconditional branch that will fit.
134            Instruction replacementInstruction =
135                new BranchInstruction(InstructionConstants.OP_GOTO_W,
136                                      branchInstruction.branchOffset);
137
138            // Create a new instruction that will fit.
139            switch (branchInstruction.opcode)
140            {
141                default:
142                {
143                    // Create a new branch instruction that will fit.
144                    replacementInstruction =
145                        new BranchInstruction(branchInstruction.opcode,
146                                              branchInstruction.branchOffset).shrink();
147
148                    break;
149                }
150
151                // Some special cases, for which a wide branch doesn't exist.
152                case InstructionConstants.OP_IFEQ:
153                case InstructionConstants.OP_IFNE:
154                case InstructionConstants.OP_IFLT:
155                case InstructionConstants.OP_IFGE:
156                case InstructionConstants.OP_IFGT:
157                case InstructionConstants.OP_IFLE:
158                case InstructionConstants.OP_IFICMPEQ:
159                case InstructionConstants.OP_IFICMPNE:
160                case InstructionConstants.OP_IFICMPLT:
161                case InstructionConstants.OP_IFICMPGE:
162                case InstructionConstants.OP_IFICMPGT:
163                case InstructionConstants.OP_IFICMPLE:
164                case InstructionConstants.OP_IFACMPEQ:
165                case InstructionConstants.OP_IFACMPNE:
166                {
167                    // Insert the complementary conditional branch.
168                    Instruction complementaryConditionalBranch =
169                        new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1),
170                                              (1+2) + (1+4));
171
172                    insertBeforeInstruction(offset, complementaryConditionalBranch);
173
174                    // Create a new unconditional branch that will fit.
175                    break;
176                }
177
178                case InstructionConstants.OP_IFNULL:
179                case InstructionConstants.OP_IFNONNULL:
180                {
181                    // Insert the complementary conditional branch.
182                    Instruction complementaryConditionalBranch =
183                        new BranchInstruction((byte)(branchInstruction.opcode ^ 1),
184                                              (1+2) + (1+4));
185
186                    insertBeforeInstruction(offset, complementaryConditionalBranch);
187
188                    // Create a new unconditional branch that will fit.
189                    break;
190                }
191            }
192
193            replaceInstruction(offset, replacementInstruction);
194
195            // Write out a dummy branch instruction for now.
196            branchInstruction.branchOffset = 0;
197            branchInstruction.write(codeAttribute, offset);
198        }
199    }
200
201
202    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
203    {
204        // Try to write out the instruction.
205        // Switch instructions should always fit.
206        switchInstruction.write(codeAttribute, offset);
207    }
208
209
210    // Implementations for AttributeVisitor.
211
212    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
213    {
214        // Avoid doing any work if nothing is changing anyway.
215        if (codeAttributeEditor != null)
216        {
217            // Apply the collected expansions.
218            codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
219
220            // Clear the modifications for the next run.
221            codeAttributeEditor = null;
222        }
223    }
224
225
226    // Small utility methods.
227
228    /**
229     * Remembers to place the given instruction right before the instruction
230     * at the given offset.
231     */
232    private void insertBeforeInstruction(int instructionOffset, Instruction instruction)
233    {
234        ensureCodeAttributeEditor();
235
236        // Replace the instruction.
237        codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction);
238    }
239
240
241    /**
242     * Remembers to replace the instruction at the given offset by the given
243     * instruction.
244     */
245    private void replaceInstruction(int instructionOffset, Instruction instruction)
246    {
247        ensureCodeAttributeEditor();
248
249        // Replace the instruction.
250        codeAttributeEditor.replaceInstruction(instructionOffset, instruction);
251    }
252
253
254    /**
255     * Remembers to place the given instruction right after the instruction
256     * at the given offset.
257     */
258    private void insertAfterInstruction(int instructionOffset, Instruction instruction)
259    {
260        ensureCodeAttributeEditor();
261
262        // Replace the instruction.
263        codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction);
264    }
265
266
267    /**
268     * Makes sure there is a code attribute editor for the given code attribute.
269     */
270    private void ensureCodeAttributeEditor()
271    {
272        if (codeAttributeEditor == null)
273        {
274            codeAttributeEditor = new CodeAttributeEditor();
275            codeAttributeEditor.reset(codeLength);
276        }
277    }
278}
279