diff options
Diffstat (limited to 'src/proguard/classfile/editor/MethodInvocationFixer.java')
-rw-r--r-- | src/proguard/classfile/editor/MethodInvocationFixer.java | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/proguard/classfile/editor/MethodInvocationFixer.java b/src/proguard/classfile/editor/MethodInvocationFixer.java new file mode 100644 index 0000000..ef76012 --- /dev/null +++ b/src/proguard/classfile/editor/MethodInvocationFixer.java @@ -0,0 +1,254 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +/** + * This AttributeVisitor fixes all inappropriate special/virtual/static/interface + * invocations of the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class MethodInvocationFixer +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + ClassVisitor, + MemberVisitor +{ + private static final boolean DEBUG = false; + + + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + // Return values for the visitor methods. + private Clazz referencedClass; + private Clazz referencedMethodClass; + private Member referencedMethod; + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Reset the code attribute editor. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // Remap the variables of the instructions. + codeAttribute.instructionsAccept(clazz, method, this); + + // Apply the code atribute editor. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + int constantIndex = constantInstruction.constantIndex; + + // Get information on the called class and method, if present. + referencedMethod = null; + + clazz.constantPoolEntryAccept(constantIndex, this); + + // Did we find the called class and method? + if (referencedMethod != null) + { + // Do we need to update the opcode? + byte opcode = constantInstruction.opcode; + + // Is the method static? + if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + // But is it not a static invocation? + if (opcode != InstructionConstants.OP_INVOKESTATIC) + { + // Replace the invocation by an invokestatic instruction. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, + constantIndex).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // Is the method private, or an instance initializer? + else if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) != 0 || + referencedMethod.getName(referencedMethodClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + // But is it not a special invocation? + if (opcode != InstructionConstants.OP_INVOKESPECIAL) + { + // Replace the invocation by an invokespecial instruction. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, + constantIndex).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // Is the method an interface method? + else if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + int invokeinterfaceConstant = + (ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false)) << 8; + + // But is it not an interface invocation, or is the parameter + // size incorrect? + if (opcode != InstructionConstants.OP_INVOKEINTERFACE || + constantInstruction.constant != invokeinterfaceConstant) + { + // Fix the parameter size of the interface invocation. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKEINTERFACE, + constantIndex, + invokeinterfaceConstant).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // The method is not static, private, an instance initializer, or + // an interface method. + else + { + // But is it not a virtual invocation (or a special invocation, + // but not a super call)? + if (opcode != InstructionConstants.OP_INVOKEVIRTUAL && + (opcode != InstructionConstants.OP_INVOKESPECIAL || + !clazz.extends_(referencedClass))) + { + // Replace the invocation by an invokevirtual instruction. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, + constantIndex).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + // Check if this is an interface method. Note that we're interested in + // the class of the method reference, not in the class in which the + // method was actually found. + //refConstant.referencedClassAccept(this); + clazz.constantPoolEntryAccept(refConstant.u2classIndex, this); + + // Get the referenced access flags and names. + refConstant.referencedMemberAccept(this); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Check if this is an interface class. + classConstant.referencedClassAccept(this); + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + // Remember the referenced class. + referencedClass = clazz; + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + // Remember the referenced method. + referencedMethodClass = clazz; + referencedMethod = member; + } + + + // Small utility methods. + + private void debug(Clazz clazz, + Method method, + int offset, + ConstantInstruction constantInstruction, + Instruction replacementInstruction) + { + System.out.println("MethodInvocationFixer:"); + System.out.println(" Class = "+clazz.getName()); + System.out.println(" Method = "+method.getName(clazz)+method.getDescriptor(clazz)); + System.out.println(" Instruction = "+constantInstruction.toString(offset)); + System.out.println(" -> Class = "+referencedClass); + System.out.println(" Method = "+referencedMethod); + if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + System.out.println(" Parameter size = "+(ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false))); + } + System.out.println(" Replacement instruction = "+replacementInstruction.toString(offset)); + } +} |