1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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.CodeAttribute;
25import proguard.classfile.constant.*;
26import proguard.classfile.constant.visitor.ConstantVisitor;
27import proguard.classfile.instruction.*;
28import proguard.classfile.instruction.visitor.InstructionVisitor;
29import proguard.classfile.util.SimplifiedVisitor;
30import proguard.classfile.visitor.*;
31
32import java.util.*;
33
34/**
35 * This class can tell whether an instruction has any side effects outside of
36 * its method. Return instructions can be included or not.
37 *
38 * @see ReadWriteFieldMarker
39 * @see StaticInitializerContainingClassMarker
40 * @see NoSideEffectMethodMarker
41 * @see SideEffectMethodMarker
42 * @author Eric Lafortune
43 */
44public class SideEffectInstructionChecker
45extends      SimplifiedVisitor
46implements   InstructionVisitor,
47             ConstantVisitor,
48             MemberVisitor
49{
50    private static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null;
51
52
53    private final boolean includeReturnInstructions;
54    private final boolean includeLocalFieldAccess;
55
56    // A return value for the visitor methods.
57    private Clazz   referencingClass;
58    private boolean hasSideEffects;
59
60
61    public SideEffectInstructionChecker(boolean includeReturnInstructions,
62                                        boolean includeLocalFieldAccess)
63    {
64        this.includeReturnInstructions = includeReturnInstructions;
65        this.includeLocalFieldAccess   = includeLocalFieldAccess;
66    }
67
68
69    /**
70     * Returns whether the given instruction has side effects outside of its
71     * method.
72     */
73    public boolean hasSideEffects(Clazz         clazz,
74                                  Method        method,
75                                  CodeAttribute codeAttribute,
76                                  int           offset,
77                                  Instruction   instruction)
78    {
79        hasSideEffects = false;
80
81        instruction.accept(clazz, method, codeAttribute, offset, this);
82
83        return hasSideEffects;
84    }
85
86
87    // Implementations for InstructionVisitor.
88
89    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
90
91
92    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
93    {
94        byte opcode = simpleInstruction.opcode;
95
96        // Check for instructions that might cause side effects.
97        switch (opcode)
98        {
99            case InstructionConstants.OP_IALOAD:
100            case InstructionConstants.OP_LALOAD:
101            case InstructionConstants.OP_FALOAD:
102            case InstructionConstants.OP_DALOAD:
103            case InstructionConstants.OP_AALOAD:
104            case InstructionConstants.OP_BALOAD:
105            case InstructionConstants.OP_CALOAD:
106            case InstructionConstants.OP_SALOAD:
107                // These instructions strictly taken may cause a side effect
108                // (NullPointerException, ArrayIndexOutOfBoundsException).
109                hasSideEffects = OPTIMIZE_CONSERVATIVELY;
110                break;
111
112            case InstructionConstants.OP_IASTORE:
113            case InstructionConstants.OP_LASTORE:
114            case InstructionConstants.OP_FASTORE:
115            case InstructionConstants.OP_DASTORE:
116            case InstructionConstants.OP_AASTORE:
117            case InstructionConstants.OP_BASTORE:
118            case InstructionConstants.OP_CASTORE:
119            case InstructionConstants.OP_SASTORE:
120            case InstructionConstants.OP_ATHROW :
121            case InstructionConstants.OP_MONITORENTER:
122            case InstructionConstants.OP_MONITOREXIT:
123                // These instructions always cause a side effect.
124                hasSideEffects = true;
125                break;
126
127            case InstructionConstants.OP_IRETURN:
128            case InstructionConstants.OP_LRETURN:
129            case InstructionConstants.OP_FRETURN:
130            case InstructionConstants.OP_DRETURN:
131            case InstructionConstants.OP_ARETURN:
132            case InstructionConstants.OP_RETURN:
133                // These instructions may have a side effect.
134                hasSideEffects = includeReturnInstructions;
135                break;
136        }
137    }
138
139
140    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
141    {
142        byte opcode = variableInstruction.opcode;
143
144        // Check for instructions that might cause side effects.
145        switch (opcode)
146        {
147            case InstructionConstants.OP_RET:
148                // This instruction may have a side effect.
149                hasSideEffects = includeReturnInstructions;
150                break;
151        }
152    }
153
154
155    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
156    {
157        byte opcode = constantInstruction.opcode;
158
159        // Check for instructions that might cause side effects.
160        switch (opcode)
161        {
162            case InstructionConstants.OP_GETSTATIC:
163            case InstructionConstants.OP_PUTSTATIC:
164            case InstructionConstants.OP_INVOKESPECIAL:
165            case InstructionConstants.OP_INVOKESTATIC:
166                // Check if the field is write-only or volatile, or if the
167                // invoked method is causing any side effects.
168                clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
169                break;
170
171            case InstructionConstants.OP_GETFIELD:
172            case InstructionConstants.OP_PUTFIELD:
173            case InstructionConstants.OP_INVOKEVIRTUAL:
174            case InstructionConstants.OP_INVOKEINTERFACE:
175            case InstructionConstants.OP_INVOKEDYNAMIC:
176                if (OPTIMIZE_CONSERVATIVELY)
177                {
178                    // These instructions strictly taken may cause a side effect
179                    // (NullPointerException).
180                    hasSideEffects = true;
181                }
182                else
183                {
184                    // Check if the field is write-only or volatile, or if the
185                    // invoked method is causing any side effects.
186                    clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
187                }
188                break;
189
190            case InstructionConstants.OP_CHECKCAST:
191                // This instructions strictly taken may cause a side effect
192                // (ClassCastException).
193                hasSideEffects = OPTIMIZE_CONSERVATIVELY;
194                break;
195        }
196    }
197
198
199    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
200    {
201        byte opcode = branchInstruction.opcode;
202
203        // Check for instructions that might cause side effects.
204        switch (opcode)
205        {
206            case InstructionConstants.OP_JSR:
207            case InstructionConstants.OP_JSR_W:
208                hasSideEffects = includeReturnInstructions;
209                break;
210        }
211    }
212
213
214    // Implementations for ConstantVisitor.
215
216    public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
217    {
218        // We'll have to assume invoking an unknown method has side effects.
219        hasSideEffects = true;
220    }
221
222
223    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
224    {
225        // Pass the referencing class.
226        referencingClass = clazz;
227
228        // We'll have to assume accessing an unknown field has side effects.
229        hasSideEffects = true;
230
231        // Check the referenced field, if known.
232        fieldrefConstant.referencedMemberAccept(this);
233    }
234
235
236    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
237    {
238        // Pass the referencing class.
239        referencingClass = clazz;
240
241        // We'll have to assume invoking an unknown method has side effects.
242        hasSideEffects = true;
243
244        // Check the referenced method, if known.
245        refConstant.referencedMemberAccept(this);
246    }
247
248
249    // Implementations for MemberVisitor.
250
251    public void visitProgramField(ProgramClass programClass, ProgramField programField)
252    {
253        hasSideEffects =
254            (includeLocalFieldAccess || !programClass.equals(referencingClass)) &&
255            ((ReadWriteFieldMarker.isRead(programField) &&
256              ReadWriteFieldMarker.isWritten(programField))                                ||
257             ((programField.getAccessFlags() & ClassConstants.INTERNAL_ACC_VOLATILE) != 0) ||
258             (!programClass.equals(referencingClass) &&
259              !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass))));
260    }
261
262
263    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
264    {
265        // Note that side effects already include synchronization of some
266        // implementation of the method.
267        hasSideEffects =
268            !NoSideEffectMethodMarker.hasNoSideEffects(programMethod) &&
269            (SideEffectMethodMarker.hasSideEffects(programMethod) ||
270             (!programClass.equals(referencingClass) &&
271              !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass))));
272    }
273
274
275    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
276    {
277        hasSideEffects = true;
278    }
279
280
281    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
282    {
283        hasSideEffects =
284            !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod);
285    }
286
287
288    /**
289     * Returns the set of superclasses and interfaces that are initialized.
290     */
291    private Set initializedSuperClasses(Clazz clazz)
292    {
293        Set set = new HashSet();
294
295        // Visit all superclasses and interfaces, collecting the ones that have
296        // static initializers.
297        clazz.hierarchyAccept(true, true, true, false,
298                              new StaticInitializerContainingClassFilter(
299                              new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_CLINIT,
300                                                     ClassConstants.INTERNAL_METHOD_TYPE_CLINIT,
301                              new SideEffectMethodFilter(
302                              new MemberToClassVisitor(
303                              new ClassCollector(set))))));
304
305        return set;
306    }
307}
308