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