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