1
/* $Id: MethodInvocationFixer.java,v 1.4 2005/08/13 21:01:04 eric Exp $
3
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
5
* Copyright (c) 2002-2005 Eric Lafortune (eric@graphics.cornell.edu)
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)
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
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
21
package proguard.classfile.editor;
23
import proguard.classfile.*;
24
import proguard.classfile.util.ClassUtil;
25
import proguard.classfile.attribute.CodeAttrInfo;
26
import proguard.classfile.instruction.*;
27
import proguard.classfile.visitor.*;
30
* This InstructionVisitor fixes all inappropriate special/virtual/static/interface
33
* @author Eric Lafortune
35
public class MethodInvocationFixer
36
implements InstructionVisitor,
40
private static final boolean DEBUG = false;
42
private CodeAttrInfoEditor codeAttrInfoEditor;
44
// Return values for the visitor methods.
45
private boolean isMethodInvocation;
46
private int accessFlags;
47
private boolean isInitializer;
48
private boolean isInterfaceMethod;
49
private int parameterSize;
53
* Creates a new MethodInvocationFixer.
54
* @param codeAttrInfoEditor a code editor that can be used for
55
* accumulating changes to the code.
57
public MethodInvocationFixer(CodeAttrInfoEditor codeAttrInfoEditor)
59
this.codeAttrInfoEditor = codeAttrInfoEditor;
63
// Implementations for InstructionVisitor.
65
public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
66
public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
67
public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
68
public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
69
public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
72
public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
74
int cpIndex = cpInstruction.cpIndex;
75
int constant = cpInstruction.constant;
77
// Get the constant pool entry's information.
78
isMethodInvocation = false;
79
isInterfaceMethod = false;
81
parameterSize = constant;
83
classFile.constantPoolEntryAccept(cpIndex, this);
85
// Is it a method invocation?
86
if (isMethodInvocation)
88
// Do we need to update the opcode?
89
byte opcode = cpInstruction.opcode;
91
// Is the method static?
92
if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0)
94
// But is it not a static invocation?
95
if (opcode != InstructionConstants.OP_INVOKESTATIC)
97
// Replace the invocation by an invokestatic instruction.
98
Instruction replacementInstruction =
99
new CpInstruction(InstructionConstants.OP_INVOKESTATIC,
102
codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
106
debug(classFile, methodInfo, offset, cpInstruction, replacementInstruction);
111
// Is the method private, or an instance initializer?
112
else if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0 ||
115
// But is it not a special invocation?
116
if (opcode != InstructionConstants.OP_INVOKESPECIAL)
118
// Replace the invocation by an invokespecial instruction.
119
Instruction replacementInstruction =
120
new CpInstruction(InstructionConstants.OP_INVOKESPECIAL,
123
codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
127
debug(classFile, methodInfo, offset, cpInstruction, replacementInstruction);
132
// Is the method an interface method?
133
else if (isInterfaceMethod)
135
// But is it not an interface invocation, or is the parameter
137
if (opcode != InstructionConstants.OP_INVOKEINTERFACE ||
138
parameterSize != constant)
140
// Fix the parameter size of the interface invocation.
141
Instruction replacementInstruction =
142
new CpInstruction(InstructionConstants.OP_INVOKEINTERFACE,
144
parameterSize).shrink();
146
codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
150
debug(classFile, methodInfo, offset, cpInstruction, replacementInstruction);
155
// The method is not static, private, an instance initializer, or
156
// an interface method.
159
// But is it not a virtual invocation (or a special invocation,
160
// which is allowed for super calls)?
161
if (opcode != InstructionConstants.OP_INVOKEVIRTUAL &&
162
opcode != InstructionConstants.OP_INVOKESPECIAL)
164
// Replace the invocation by an invokevirtual instruction.
165
Instruction replacementInstruction =
166
new CpInstruction(InstructionConstants.OP_INVOKEVIRTUAL,
169
codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
173
debug(classFile, methodInfo, offset, cpInstruction, replacementInstruction);
181
// Implementations for CpInfoVisitor.
183
public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
184
public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
185
public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
186
public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
187
public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
188
public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
189
public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
190
public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
193
public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
195
// Check if this is an interface method.
196
classFile.constantPoolEntryAccept(interfaceMethodrefCpInfo.u2classIndex, this);
198
// Get the referenced method's access flags.
199
interfaceMethodrefCpInfo.referencedMemberInfoAccept(this);
203
public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
205
// Check if this is an interface method.
206
classFile.constantPoolEntryAccept(methodrefCpInfo.u2classIndex, this);
208
// Get the referenced method's access flags.
209
methodrefCpInfo.referencedMemberInfoAccept(this);
213
public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
215
// Check if this class entry refers to an interface class.
216
ClassFile referencedClassFile = classCpInfo.referencedClassFile;
217
if (referencedClassFile != null)
219
isInterfaceMethod = (referencedClassFile.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0;
224
// Implementations for MemberInfoVisitor.
226
public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
228
public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
230
visitMethodInfo(programClassFile, programMethodInfo);
234
public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
236
public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
238
visitMethodInfo(libraryClassFile, libraryMethodInfo);
242
private void visitMethodInfo(ClassFile classFile, MethodInfo methodInfo)
244
// We've found a method definition.
245
isMethodInvocation = true;
247
// Get the method's access flags.
248
accessFlags = methodInfo.getAccessFlags();
250
// Check if this is an instance initializer.
251
isInitializer = methodInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
253
// Remember the parameter size of interface methods.
254
if (isInterfaceMethod)
256
parameterSize = ClassUtil.internalMethodParameterSize(methodInfo.getDescriptor(classFile)) + 1 << 8;
261
// Small utility methods.
263
private void debug(ClassFile classFile,
264
MethodInfo methodInfo,
266
CpInstruction cpInstruction,
267
Instruction replacementInstruction)
269
System.out.println("MethodInvocationFixer:");
270
System.out.println(" Class file = "+classFile.getName());
271
System.out.println(" Method = "+methodInfo.getName(classFile)+methodInfo.getDescriptor(classFile));
272
System.out.println(" Instruction = "+cpInstruction.toString(offset));
273
System.out.println(" Interface method = "+isInterfaceMethod);
274
if (isInterfaceMethod)
276
System.out.println(" Parameter size = "+parameterSize);
278
System.out.println(" Replacement instruction = "+replacementInstruction.toString(offset));