Home | History | Annotate | Download | only in info
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 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.optimize.info;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.attribute.*;
     25 import proguard.classfile.attribute.visitor.AttributeVisitor;
     26 import proguard.classfile.instruction.*;
     27 import proguard.classfile.util.SimplifiedVisitor;
     28 import proguard.classfile.visitor.*;
     29 
     30 /**
     31  * This ClassPoolVisitor marks all methods that have side effects.
     32  *
     33  * @see ReadWriteFieldMarker
     34  * @see NoSideEffectMethodMarker
     35  * @author Eric Lafortune
     36  */
     37 public class SideEffectMethodMarker
     38 extends      SimplifiedVisitor
     39 implements   ClassPoolVisitor,
     40              ClassVisitor,
     41              MemberVisitor,
     42              AttributeVisitor
     43 {
     44     // Reusable objects for checking whether instructions have side effects.
     45     private final SideEffectInstructionChecker sideEffectInstructionChecker            = new SideEffectInstructionChecker(false, true);
     46     private final SideEffectInstructionChecker initializerSideEffectInstructionChecker = new SideEffectInstructionChecker(false, false);
     47 
     48     // Parameters and values for visitor methods.
     49     private int     newSideEffectCount;
     50     private boolean hasSideEffects;
     51 
     52 
     53     // Implementations for ClassPoolVisitor.
     54 
     55     public void visitClassPool(ClassPool classPool)
     56     {
     57         // Go over all classes and their methods, marking if they have side
     58         // effects, until no new cases can be found.
     59         do
     60         {
     61             newSideEffectCount = 0;
     62 
     63             // Go over all classes and their methods once.
     64             classPool.classesAccept(this);
     65         }
     66         while (newSideEffectCount > 0);
     67     }
     68 
     69 
     70     // Implementations for ClassVisitor.
     71 
     72     public void visitProgramClass(ProgramClass programClass)
     73     {
     74         // Go over all methods.
     75         programClass.methodsAccept(this);
     76     }
     77 
     78 
     79     // Implementations for MemberVisitor.
     80 
     81     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     82     {
     83         if (!hasSideEffects(programMethod) &&
     84             !NoSideEffectMethodMarker.hasNoSideEffects(programMethod))
     85         {
     86             // Initialize the return value.
     87             hasSideEffects =
     88                 (programMethod.getAccessFlags() &
     89                  (ClassConstants.ACC_NATIVE |
     90                   ClassConstants.ACC_SYNCHRONIZED)) != 0;
     91 
     92             // Look further if the method hasn't been marked yet.
     93             if (!hasSideEffects)
     94             {
     95                 // Investigate the actual code.
     96                 programMethod.attributesAccept(programClass, this);
     97             }
     98 
     99             // Mark the method depending on the return value.
    100             if (hasSideEffects)
    101             {
    102                 markSideEffects(programMethod);
    103 
    104                 newSideEffectCount++;
    105             }
    106         }
    107     }
    108 
    109 
    110     // Implementations for AttributeVisitor.
    111 
    112     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
    113 
    114 
    115     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    116     {
    117         // Remember whether the code has any side effects.
    118         hasSideEffects = hasSideEffects(clazz, method, codeAttribute);
    119     }
    120 
    121 
    122     // Small utility methods.
    123 
    124     /**
    125      * Returns whether the given code has any side effects.
    126      */
    127     private boolean hasSideEffects(Clazz         clazz,
    128                                    Method        method,
    129                                    CodeAttribute codeAttribute)
    130     {
    131         byte[] code   = codeAttribute.code;
    132         int    length = codeAttribute.u4codeLength;
    133 
    134         SideEffectInstructionChecker checker =
    135             method.getName(clazz).equals(ClassConstants.METHOD_NAME_CLINIT) ?
    136                 initializerSideEffectInstructionChecker :
    137                 sideEffectInstructionChecker;
    138 
    139         // Go over all instructions.
    140         int offset = 0;
    141         do
    142         {
    143             // Get the current instruction.
    144             Instruction instruction = InstructionFactory.create(code, offset);
    145 
    146             // Check if it may be throwing exceptions.
    147             if (checker.hasSideEffects(clazz,
    148                                        method,
    149                                        codeAttribute,
    150                                        offset,
    151                                        instruction))
    152             {
    153                 return true;
    154             }
    155 
    156             // Go to the next instruction.
    157             offset += instruction.length(offset);
    158         }
    159         while (offset < length);
    160 
    161         return false;
    162     }
    163 
    164 
    165     private static void markSideEffects(Method method)
    166     {
    167         MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
    168         if (info != null)
    169         {
    170             info.setSideEffects();
    171         }
    172     }
    173 
    174 
    175     public static boolean hasSideEffects(Method method)
    176     {
    177         MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
    178         return info == null ||
    179                info.hasSideEffects();
    180     }
    181 }
    182