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 */
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    // 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