InstructionSequenceReplacer.java revision db267bc191f906f55eaef21a27110cce2ec57fdf
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.optimize.peephole;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.CodeAttribute;
25import proguard.classfile.constant.Constant;
26import proguard.classfile.constant.visitor.ConstantVisitor;
27import proguard.classfile.editor.CodeAttributeEditor;
28import proguard.classfile.instruction.*;
29import proguard.classfile.instruction.visitor.InstructionVisitor;
30import proguard.classfile.util.*;
31
32/**
33 * This InstructionVisitor replaces a given pattern instruction sequence by
34 * another given replacement instruction sequence. The arguments of the
35 * instruction sequences can be wildcards that are matched and replaced.
36 *
37 * @see InstructionSequenceMatcher
38 * @author Eric Lafortune
39 */
40public class InstructionSequenceReplacer
41extends      SimplifiedVisitor
42implements   InstructionVisitor,
43             ConstantVisitor
44{
45    private static final boolean DEBUG = false;
46
47
48    private final InstructionSequenceMatcher instructionSequenceMatcher;
49    private final Instruction[]              replacementInstructions;
50    private final BranchTargetFinder         branchTargetFinder;
51    private final CodeAttributeEditor        codeAttributeEditor;
52    private final InstructionVisitor         extraInstructionVisitor;
53
54    private final MyReplacementInstructionFactory replacementInstructionFactory = new MyReplacementInstructionFactory();
55
56
57    /**
58     * Creates a new InstructionSequenceReplacer.
59     * @param patternConstants        any constants referenced by the pattern
60     *                                instruction.
61     * @param patternInstructions     the pattern instruction sequence.
62     * @param replacementInstructions the replacement instruction sequence.
63     * @param branchTargetFinder      a branch target finder that has been
64     *                                initialized to indicate branch targets
65     *                                in the visited code.
66     * @param codeAttributeEditor     a code editor that can be used for
67     *                                accumulating changes to the code.
68     */
69    public InstructionSequenceReplacer(Constant[]          patternConstants,
70                                       Instruction[]       patternInstructions,
71                                       Instruction[]       replacementInstructions,
72                                       BranchTargetFinder  branchTargetFinder,
73                                       CodeAttributeEditor codeAttributeEditor)
74    {
75        this(patternConstants,
76             patternInstructions,
77             replacementInstructions,
78             branchTargetFinder,
79             codeAttributeEditor,
80             null);
81    }
82
83
84    /**
85     * Creates a new InstructionSequenceReplacer.
86     * @param patternConstants        any constants referenced by the pattern
87     *                                instruction.
88     * @param branchTargetFinder      a branch target finder that has been
89     *                                initialized to indicate branch targets
90     *                                in the visited code.
91     * @param codeAttributeEditor     a code editor that can be used for
92     *                                accumulating changes to the code.
93     * @param extraInstructionVisitor an optional extra visitor for all deleted
94     *                                load instructions.
95     */
96    public InstructionSequenceReplacer(Constant[]          patternConstants,
97                                       Instruction[]       patternInstructions,
98                                       Instruction[]       replacementInstructions,
99                                       BranchTargetFinder  branchTargetFinder,
100                                       CodeAttributeEditor codeAttributeEditor,
101                                       InstructionVisitor  extraInstructionVisitor)
102    {
103        this.instructionSequenceMatcher = new InstructionSequenceMatcher(patternConstants, patternInstructions);
104        this.replacementInstructions    = replacementInstructions;
105        this.branchTargetFinder         = branchTargetFinder;
106        this.codeAttributeEditor        = codeAttributeEditor;
107        this.extraInstructionVisitor    = extraInstructionVisitor;
108    }
109
110
111    // Implementations for InstructionVisitor.
112
113    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
114    {
115        // Reset the instruction sequence matcher if the instruction is a branch
116        // target or if it has already been modified.
117        if (branchTargetFinder.isTarget(offset) ||
118            codeAttributeEditor.isModified(offset))
119        {
120            instructionSequenceMatcher.reset();
121        }
122
123        // Try to match the instruction.
124        instruction.accept(clazz, method, codeAttribute, offset, instructionSequenceMatcher);
125
126        // Did the instruction sequence match and is it still unmodified?
127        if (instructionSequenceMatcher.isMatching() &&
128            matchedInstructionsUnmodified())
129        {
130            if (DEBUG)
131            {
132                System.out.println("InstructionSequenceReplacer: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
133                System.out.println("  Matched:");
134                for (int index = 0; index < instructionSequenceMatcher.instructionCount(); index++)
135                {
136                    int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index);
137                    System.out.println("    "+InstructionFactory.create(codeAttribute.code, matchedOffset).toString(matchedOffset));
138                }
139                System.out.println("  Replacement:");
140                for (int index = 0; index < replacementInstructions.length; index++)
141                {
142                    int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index);
143                    System.out.println("    "+replacementInstructionFactory.create(index).shrink().toString(matchedOffset));
144                }
145            }
146
147            // Replace the instruction sequence.
148            for (int index = 0; index < replacementInstructions.length; index++)
149            {
150                codeAttributeEditor.replaceInstruction(instructionSequenceMatcher.matchedInstructionOffset(index),
151                                                       replacementInstructionFactory.create(index).shrink());
152            }
153
154            // Delete any remaining instructions in the from sequence.
155            for (int index = replacementInstructions.length; index < instructionSequenceMatcher.instructionCount(); index++)
156            {
157                codeAttributeEditor.deleteInstruction(instructionSequenceMatcher.matchedInstructionOffset(index));
158            }
159
160            // Visit the instruction, if required.
161            if (extraInstructionVisitor != null)
162            {
163                instruction.accept(clazz,
164                                   method,
165                                   codeAttribute,
166                                   offset,
167                                   extraInstructionVisitor);
168            }
169        }
170    }
171
172
173    // Small utility methods.
174
175    /**
176     * Returns whether the matched pattern instructions haven't been modified
177     * before.
178     */
179    private boolean matchedInstructionsUnmodified()
180    {
181        for (int index = 0; index < instructionSequenceMatcher.instructionCount(); index++)
182        {
183            if (codeAttributeEditor.isModified(instructionSequenceMatcher.matchedInstructionOffset(index)))
184            {
185                return false;
186            }
187        }
188
189        return true;
190    }
191
192
193    /**
194     * This class creates replacement instructions for matched sequences, with
195     * any matched arguments filled out.
196     */
197    private class MyReplacementInstructionFactory
198    implements    InstructionVisitor
199    {
200        private Instruction replacementInstruction;
201
202
203        /**
204         * Creates the replacement instruction for the given index in the
205         * instruction sequence.
206         */
207        public Instruction create(int index)
208        {
209            // Create the instruction.
210            replacementInstructions[index].accept(null,
211                                                  null,
212                                                  null,
213                                                  instructionSequenceMatcher.matchedInstructionOffset(index),
214                                                  this);
215
216            // Return it.
217            return replacementInstruction.shrink();
218        }
219
220
221        // Implementations for InstructionVisitor.
222
223        public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
224        {
225            replacementInstruction =
226                new SimpleInstruction(simpleInstruction.opcode,
227                                      instructionSequenceMatcher.matchedArgument(simpleInstruction.constant));
228        }
229
230
231        public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
232        {
233            replacementInstruction =
234                new VariableInstruction(variableInstruction.opcode,
235                                        instructionSequenceMatcher.matchedArgument(variableInstruction.variableIndex),
236                                        instructionSequenceMatcher.matchedArgument(variableInstruction.constant));
237        }
238
239
240        public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
241        {
242            replacementInstruction =
243                new ConstantInstruction(constantInstruction.opcode,
244                                        instructionSequenceMatcher.matchedConstantIndex(constantInstruction.constantIndex),
245                                        instructionSequenceMatcher.matchedArgument(constantInstruction.constant));
246        }
247
248
249        public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
250        {
251            replacementInstruction =
252                new BranchInstruction(branchInstruction.opcode,
253                                      instructionSequenceMatcher.matchedBranchOffset(offset, branchInstruction.branchOffset));
254        }
255
256
257        public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
258        {
259            replacementInstruction =
260                new TableSwitchInstruction(tableSwitchInstruction.opcode,
261                                           instructionSequenceMatcher.matchedBranchOffset(offset, tableSwitchInstruction.defaultOffset),
262                                           instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.lowCase),
263                                           instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.highCase),
264                                           instructionSequenceMatcher.matchedJumpOffsets(offset, tableSwitchInstruction.jumpOffsets));
265
266        }
267
268
269        public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
270        {
271            replacementInstruction =
272                new LookUpSwitchInstruction(lookUpSwitchInstruction.opcode,
273                                            instructionSequenceMatcher.matchedBranchOffset(offset, lookUpSwitchInstruction.defaultOffset),
274                                            instructionSequenceMatcher.matchedArguments(lookUpSwitchInstruction.cases),
275                                            instructionSequenceMatcher.matchedJumpOffsets(offset, lookUpSwitchInstruction.jumpOffsets));
276        }
277    }
278}
279