MethodInvocationFixer.java revision b72c5c2e5482cf10117b2b25f642f7616b2326c3
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.*;
25import proguard.classfile.attribute.visitor.AttributeVisitor;
26import proguard.classfile.constant.*;
27import proguard.classfile.constant.visitor.ConstantVisitor;
28import proguard.classfile.instruction.*;
29import proguard.classfile.instruction.visitor.InstructionVisitor;
30import proguard.classfile.util.*;
31import proguard.classfile.visitor.*;
32
33/**
34 * This AttributeVisitor fixes all inappropriate special/virtual/static/interface
35 * invocations of the code attributes that it visits.
36 *
37 * @author Eric Lafortune
38 */
39public class MethodInvocationFixer
40extends      SimplifiedVisitor
41implements   AttributeVisitor,
42             InstructionVisitor,
43             ConstantVisitor,
44             ClassVisitor,
45             MemberVisitor
46{
47    private static final boolean DEBUG = false;
48
49
50    private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
51
52    // Return values for the visitor methods.
53    private Clazz  referencedClass;
54    private Clazz  referencedMethodClass;
55    private Member referencedMethod;
56
57
58    // Implementations for AttributeVisitor.
59
60    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
61
62
63    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
64    {
65        // Reset the code attribute editor.
66        codeAttributeEditor.reset(codeAttribute.u4codeLength);
67
68        // Remap the variables of the instructions.
69        codeAttribute.instructionsAccept(clazz, method, this);
70
71        // Apply the code atribute editor.
72        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
73    }
74
75
76    // Implementations for InstructionVisitor.
77
78    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
79
80
81    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
82    {
83        int constantIndex = constantInstruction.constantIndex;
84
85        // Get information on the called class and method, if present.
86        referencedMethod = null;
87
88        clazz.constantPoolEntryAccept(constantIndex, this);
89
90        // Did we find the called class and method?
91        if (referencedMethod != null)
92        {
93            // Do we need to update the opcode?
94            byte opcode = constantInstruction.opcode;
95
96            // Is the method static?
97            if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0)
98            {
99                // But is it not a static invocation?
100                if (opcode != InstructionConstants.OP_INVOKESTATIC)
101                {
102                    // Replace the invocation by an invokestatic instruction.
103                    Instruction replacementInstruction =
104                        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC,
105                                                constantIndex).shrink();
106
107                    codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
108
109                    if (DEBUG)
110                    {
111                        debug(clazz, method, offset, constantInstruction, replacementInstruction);
112                    }
113                }
114            }
115
116            // Is the method private, or an instance initializer?
117            else if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) != 0 ||
118                     referencedMethod.getName(referencedMethodClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
119            {
120                // But is it not a special invocation?
121                if (opcode != InstructionConstants.OP_INVOKESPECIAL)
122                {
123                    // Replace the invocation by an invokespecial instruction.
124                    Instruction replacementInstruction =
125                        new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL,
126                                                constantIndex).shrink();
127
128                    codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
129
130                    if (DEBUG)
131                    {
132                        debug(clazz, method, offset, constantInstruction, replacementInstruction);
133                    }
134                }
135            }
136
137            // Is the method an interface method?
138            else if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
139            {
140                int invokeinterfaceConstant =
141                    (ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false)) << 8;
142
143                // But is it not an interface invocation, or is the parameter
144                // size incorrect?
145                if (opcode != InstructionConstants.OP_INVOKEINTERFACE ||
146                    constantInstruction.constant != invokeinterfaceConstant)
147                {
148                    // Fix the parameter size of the interface invocation.
149                    Instruction replacementInstruction =
150                        new ConstantInstruction(InstructionConstants.OP_INVOKEINTERFACE,
151                                                constantIndex,
152                                                invokeinterfaceConstant).shrink();
153
154                    codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
155
156                    if (DEBUG)
157                    {
158                        debug(clazz, method, offset, constantInstruction, replacementInstruction);
159                    }
160                }
161            }
162
163            // The method is not static, private, an instance initializer, or
164            // an interface method.
165            else
166            {
167                // But is it not a virtual invocation (or a special invocation,
168                // but not a super call)?
169                if (opcode != InstructionConstants.OP_INVOKEVIRTUAL &&
170                    (opcode != InstructionConstants.OP_INVOKESPECIAL ||
171                     !clazz.extends_(referencedClass)))
172                {
173                    // Replace the invocation by an invokevirtual instruction.
174                    Instruction replacementInstruction =
175                        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL,
176                                                constantIndex).shrink();
177
178                    codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
179
180                    if (DEBUG)
181                    {
182                        debug(clazz, method, offset, constantInstruction, replacementInstruction);
183                    }
184                }
185            }
186        }
187    }
188
189
190    // Implementations for ConstantVisitor.
191
192    public void visitAnyConstant(Clazz clazz, Constant constant) {}
193
194
195    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
196    {
197        // Check if this is an interface method. Note that we're interested in
198        // the class of the method reference, not in the class in which the
199        // method was actually found.
200        //refConstant.referencedClassAccept(this);
201        clazz.constantPoolEntryAccept(refConstant.u2classIndex, this);
202
203        // Get the referenced access flags and names.
204        refConstant.referencedMemberAccept(this);
205    }
206
207
208    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
209    {
210        // Check if this is an interface class.
211       classConstant.referencedClassAccept(this);
212    }
213
214
215    // Implementations for ClassVisitor.
216
217    public void visitAnyClass(Clazz clazz)
218    {
219        // Remember the referenced class.
220        referencedClass = clazz;
221    }
222
223
224    // Implementations for MemberVisitor.
225
226    public void visitAnyMember(Clazz clazz, Member member)
227    {
228        // Remember the referenced method.
229        referencedMethodClass = clazz;
230        referencedMethod      = member;
231    }
232
233
234    // Small utility methods.
235
236    private void debug(Clazz               clazz,
237                       Method              method,
238                       int                 offset,
239                       ConstantInstruction constantInstruction,
240                       Instruction         replacementInstruction)
241    {
242        System.out.println("MethodInvocationFixer:");
243        System.out.println("  Class       = "+clazz.getName());
244        System.out.println("  Method      = "+method.getName(clazz)+method.getDescriptor(clazz));
245        System.out.println("  Instruction = "+constantInstruction.toString(offset));
246        System.out.println("  -> Class    = "+referencedClass);
247        System.out.println("     Method   = "+referencedMethod);
248        if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
249        {
250            System.out.println("     Parameter size   = "+(ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false)));
251        }
252        System.out.println("  Replacement instruction = "+replacementInstruction.toString(offset));
253    }
254}
255