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