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.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 and local field accesses can be included or
37 * not.
38 *
39 * @see ReadWriteFieldMarker
40 * @see StaticInitializerContainingClassMarker
41 * @see NoSideEffectMethodMarker
42 * @see SideEffectMethodMarker
43 * @author Eric Lafortune
44 */
45public class SideEffectInstructionChecker
46extends      SimplifiedVisitor
47implements   InstructionVisitor,
48             ConstantVisitor,
49             MemberVisitor
50{
51    static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null;
52
53
54    private final boolean includeReturnInstructions;
55    private final boolean includeLocalFieldAccess;
56
57    // A return value for the visitor methods.
58    private boolean writingField;
59    private Clazz   referencingClass;
60    private boolean hasSideEffects;
61
62
63    /**
64     * Creates a new SideEffectInstructionChecker
65     * @param includeReturnInstructions specifies whether return instructions
66     *                                  count as side effects.
67     * @param includeLocalFieldAccess   specifies whether reading or writing
68     *                                  local fields counts as side effects.
69     */
70    public SideEffectInstructionChecker(boolean includeReturnInstructions,
71                                        boolean includeLocalFieldAccess)
72    {
73        this.includeReturnInstructions = includeReturnInstructions;
74        this.includeLocalFieldAccess   = includeLocalFieldAccess;
75    }
76
77
78    /**
79     * Returns whether the given instruction has side effects outside of its
80     * method.
81     */
82    public boolean hasSideEffects(Clazz         clazz,
83                                  Method        method,
84                                  CodeAttribute codeAttribute,
85                                  int           offset,
86                                  Instruction   instruction)
87    {
88        hasSideEffects = false;
89
90        instruction.accept(clazz, method, codeAttribute, offset, this);
91
92        return hasSideEffects;
93    }
94
95
96    // Implementations for InstructionVisitor.
97
98    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
99
100
101    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
102    {
103        byte opcode = simpleInstruction.opcode;
104
105        // Check for instructions that might cause side effects.
106        switch (opcode)
107        {
108            case InstructionConstants.OP_IDIV:
109            case InstructionConstants.OP_LDIV:
110            case InstructionConstants.OP_IREM:
111            case InstructionConstants.OP_LREM:
112            case InstructionConstants.OP_IALOAD:
113            case InstructionConstants.OP_LALOAD:
114            case InstructionConstants.OP_FALOAD:
115            case InstructionConstants.OP_DALOAD:
116            case InstructionConstants.OP_AALOAD:
117            case InstructionConstants.OP_BALOAD:
118            case InstructionConstants.OP_CALOAD:
119            case InstructionConstants.OP_SALOAD:
120            case InstructionConstants.OP_NEWARRAY:
121            case InstructionConstants.OP_ARRAYLENGTH:
122            case InstructionConstants.OP_ANEWARRAY:
123            case InstructionConstants.OP_MULTIANEWARRAY:
124                // These instructions strictly taken may cause a side effect
125                // (ArithmeticException, NullPointerException,
126                // ArrayIndexOutOfBoundsException, NegativeArraySizeException).
127                hasSideEffects = OPTIMIZE_CONSERVATIVELY;
128                break;
129
130            case InstructionConstants.OP_IASTORE:
131            case InstructionConstants.OP_LASTORE:
132            case InstructionConstants.OP_FASTORE:
133            case InstructionConstants.OP_DASTORE:
134            case InstructionConstants.OP_AASTORE:
135            case InstructionConstants.OP_BASTORE:
136            case InstructionConstants.OP_CASTORE:
137            case InstructionConstants.OP_SASTORE:
138            case InstructionConstants.OP_ATHROW :
139            case InstructionConstants.OP_MONITORENTER:
140            case InstructionConstants.OP_MONITOREXIT:
141                // These instructions always cause a side effect.
142                hasSideEffects = true;
143                break;
144
145            case InstructionConstants.OP_IRETURN:
146            case InstructionConstants.OP_LRETURN:
147            case InstructionConstants.OP_FRETURN:
148            case InstructionConstants.OP_DRETURN:
149            case InstructionConstants.OP_ARETURN:
150            case InstructionConstants.OP_RETURN:
151                // These instructions may have a side effect.
152                hasSideEffects = includeReturnInstructions;
153                break;
154        }
155    }
156
157
158    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
159    {
160        byte opcode = variableInstruction.opcode;
161
162        // Check for instructions that might cause side effects.
163        switch (opcode)
164        {
165            case InstructionConstants.OP_RET:
166                // This instruction may have a side effect.
167                hasSideEffects = includeReturnInstructions;
168                break;
169        }
170    }
171
172
173    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
174    {
175        byte opcode = constantInstruction.opcode;
176
177        // Check for instructions that might cause side effects.
178        switch (opcode)
179        {
180            case InstructionConstants.OP_GETSTATIC:
181                // Check if accessing the field might cause any side effects.
182                writingField = false;
183                clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
184                break;
185
186            case InstructionConstants.OP_PUTSTATIC:
187                // Check if accessing the field might cause any side effects.
188                writingField = true;
189                clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
190                break;
191
192            case InstructionConstants.OP_GETFIELD:
193                if (OPTIMIZE_CONSERVATIVELY)
194                {
195                    // These instructions strictly taken may cause a side effect
196                    // (NullPointerException).
197                    hasSideEffects = true;
198                }
199                else
200                {
201                    // Check if the field is write-only or volatile.
202                    writingField = false;
203                    clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
204                }
205                break;
206
207            case InstructionConstants.OP_PUTFIELD:
208                if (OPTIMIZE_CONSERVATIVELY)
209                {
210                    // These instructions strictly taken may cause a side effect
211                    // (NullPointerException).
212                    hasSideEffects = true;
213                }
214                else
215                {
216                    // Check if the field is write-only or volatile.
217                    writingField = true;
218                    clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
219                }
220                break;
221
222            case InstructionConstants.OP_INVOKESPECIAL:
223            case InstructionConstants.OP_INVOKESTATIC:
224                // Check if the invoked method is causing any side effects.
225                clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
226                break;
227
228            case InstructionConstants.OP_INVOKEVIRTUAL:
229            case InstructionConstants.OP_INVOKEINTERFACE:
230            case InstructionConstants.OP_INVOKEDYNAMIC:
231                if (OPTIMIZE_CONSERVATIVELY)
232                {
233                    // These instructions strictly taken may cause a side effect
234                    // (NullPointerException).
235                    hasSideEffects = true;
236                }
237                else
238                {
239                    // Check if the invoked method is causing any side effects.
240                    clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
241                }
242                break;
243
244            case InstructionConstants.OP_ANEWARRAY:
245            case InstructionConstants.OP_CHECKCAST:
246            case InstructionConstants.OP_MULTIANEWARRAY:
247                // This instructions strictly taken may cause a side effect
248                // (ClassCastException, NegativeArraySizeException).
249                hasSideEffects = OPTIMIZE_CONSERVATIVELY;
250                break;
251        }
252    }
253
254
255    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
256    {
257        byte opcode = branchInstruction.opcode;
258
259        // Check for instructions that might cause side effects.
260        switch (opcode)
261        {
262            case InstructionConstants.OP_JSR:
263            case InstructionConstants.OP_JSR_W:
264                hasSideEffects = includeReturnInstructions;
265                break;
266        }
267    }
268
269
270    // Implementations for ConstantVisitor.
271
272    public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
273    {
274        // We'll have to assume invoking an unknown method has side effects.
275        hasSideEffects = true;
276    }
277
278
279    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
280    {
281        // Pass the referencing class.
282        referencingClass = clazz;
283
284        // We'll have to assume accessing an unknown field has side effects.
285        hasSideEffects = true;
286
287        // Check the referenced field, if known.
288        fieldrefConstant.referencedMemberAccept(this);
289    }
290
291
292    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
293    {
294        // Pass the referencing class.
295        referencingClass = clazz;
296
297        // We'll have to assume invoking an unknown method has side effects.
298        hasSideEffects = true;
299
300        // Check the referenced method, if known.
301        refConstant.referencedMemberAccept(this);
302    }
303
304
305    // Implementations for MemberVisitor.
306
307    public void visitProgramField(ProgramClass programClass, ProgramField programField)
308    {
309        hasSideEffects =
310            (includeLocalFieldAccess || !programClass.equals(referencingClass)) &&
311            ((writingField && ReadWriteFieldMarker.isRead(programField))        ||
312             (programField.getAccessFlags() & ClassConstants.ACC_VOLATILE) != 0 ||
313             mayHaveSideEffects(referencingClass, programClass));
314    }
315
316
317    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
318    {
319        // Note that side effects already include synchronization of some
320        // implementation of the method.
321        hasSideEffects =
322            !NoSideEffectMethodMarker.hasNoSideEffects(programMethod) &&
323            (SideEffectMethodMarker.hasSideEffects(programMethod) ||
324             mayHaveSideEffects(referencingClass, programClass));
325    }
326
327
328    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
329    {
330        hasSideEffects = true;
331    }
332
333
334    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
335    {
336        hasSideEffects =
337            !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod);
338    }
339
340
341    // Small utility methods.
342
343    /**
344     * Returns whether a field reference or method invocation from the
345     * referencing class to the referenced class might have any side
346     * effects.
347     */
348    private boolean mayHaveSideEffects(Clazz referencingClass, Clazz referencedClass)
349    {
350        return
351            !referencedClass.equals(referencingClass) &&
352            !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(referencedClass));
353    }
354
355
356    /**
357     * Returns the set of superclasses and interfaces that are initialized.
358     */
359    private Set initializedSuperClasses(Clazz clazz)
360    {
361        Set set = new HashSet();
362
363        // Visit all superclasses and interfaces, collecting the ones that have
364        // static initializers.
365        clazz.hierarchyAccept(true, true, true, false,
366                              new StaticInitializerContainingClassFilter(
367                              new NamedMethodVisitor(ClassConstants.METHOD_NAME_CLINIT,
368                                                     ClassConstants.METHOD_TYPE_CLINIT,
369                              new SideEffectMethodFilter(
370                              new MemberToClassVisitor(
371                              new ClassCollector(set))))));
372
373        return set;
374    }
375}
376