Home | History | Annotate | Download | only in editor
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2009 Eric Lafortune (eric (at) 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 import 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  */
     39 public class MethodInvocationFixer
     40 extends      SimplifiedVisitor
     41 implements   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