001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This program is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
015: * more details.
016: *
017: * You should have received a copy of the GNU General Public License along
018: * with this program; if not, write to the Free Software Foundation, Inc.,
019: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.optimize.info;
022:
023: import proguard.classfile.*;
024: import proguard.classfile.attribute.CodeAttribute;
025: import proguard.classfile.constant.*;
026: import proguard.classfile.constant.visitor.ConstantVisitor;
027: import proguard.classfile.instruction.*;
028: import proguard.classfile.instruction.visitor.InstructionVisitor;
029: import proguard.classfile.util.SimplifiedVisitor;
030: import proguard.classfile.visitor.MemberVisitor;
031:
032: /**
033: * This class can tell whether an instruction has any side effects. Return
034: * instructions can be included or not.
035: *
036: * @see ReadWriteFieldMarker
037: * @see NoSideEffectMethodMarker
038: * @see SideEffectMethodMarker
039: * @author Eric Lafortune
040: */
041: public class SideEffectInstructionChecker extends SimplifiedVisitor
042: implements InstructionVisitor, ConstantVisitor, MemberVisitor {
043: private final boolean includeReturnInstructions;
044:
045: // A return value for the visitor methods.
046: private boolean hasSideEffects;
047:
048: public SideEffectInstructionChecker(
049: boolean includeReturnInstructions) {
050: this .includeReturnInstructions = includeReturnInstructions;
051: }
052:
053: public boolean hasSideEffects(Clazz clazz, Method method,
054: CodeAttribute codeAttribute, int offset,
055: Instruction instruction) {
056: hasSideEffects = false;
057:
058: instruction.accept(clazz, method, codeAttribute, offset, this );
059:
060: return hasSideEffects;
061: }
062:
063: // Implementations for InstructionVisitor.
064:
065: public void visitAnyInstruction(Clazz clazz, Method method,
066: CodeAttribute codeAttribute, int offset,
067: Instruction instruction) {
068: }
069:
070: public void visitSimpleInstruction(Clazz clazz, Method method,
071: CodeAttribute codeAttribute, int offset,
072: SimpleInstruction simpleInstruction) {
073: byte opcode = simpleInstruction.opcode;
074:
075: // Check for instructions that might cause side effects.
076: if (opcode == InstructionConstants.OP_IASTORE
077: || opcode == InstructionConstants.OP_LASTORE
078: || opcode == InstructionConstants.OP_FASTORE
079: || opcode == InstructionConstants.OP_DASTORE
080: || opcode == InstructionConstants.OP_AASTORE
081: || opcode == InstructionConstants.OP_BASTORE
082: || opcode == InstructionConstants.OP_CASTORE
083: || opcode == InstructionConstants.OP_SASTORE
084: || opcode == InstructionConstants.OP_ATHROW
085: || opcode == InstructionConstants.OP_MONITORENTER
086: || opcode == InstructionConstants.OP_MONITOREXIT
087: || (includeReturnInstructions && (opcode == InstructionConstants.OP_IRETURN
088: || opcode == InstructionConstants.OP_LRETURN
089: || opcode == InstructionConstants.OP_FRETURN
090: || opcode == InstructionConstants.OP_DRETURN
091: || opcode == InstructionConstants.OP_ARETURN || opcode == InstructionConstants.OP_RETURN))) {
092: // These instructions always cause a side effect.
093: hasSideEffects = true;
094: }
095:
096: }
097:
098: public void visitVariableInstruction(Clazz clazz, Method method,
099: CodeAttribute codeAttribute, int offset,
100: VariableInstruction variableInstruction) {
101: byte opcode = variableInstruction.opcode;
102:
103: // Check for instructions that might cause side effects.
104: if (includeReturnInstructions
105: && opcode == InstructionConstants.OP_RET) {
106: hasSideEffects = true;
107: }
108: }
109:
110: public void visitConstantInstruction(Clazz clazz, Method method,
111: CodeAttribute codeAttribute, int offset,
112: ConstantInstruction constantInstruction) {
113: byte opcode = constantInstruction.opcode;
114:
115: // Check for instructions that might cause side effects.
116: if (opcode == InstructionConstants.OP_PUTSTATIC
117: || opcode == InstructionConstants.OP_PUTFIELD
118: || opcode == InstructionConstants.OP_INVOKEVIRTUAL
119: || opcode == InstructionConstants.OP_INVOKESPECIAL
120: || opcode == InstructionConstants.OP_INVOKESTATIC
121: || opcode == InstructionConstants.OP_INVOKEINTERFACE) {
122: // Check if the field is write-only or volatile, or if the invoked
123: // method is causing any side effects.
124: clazz.constantPoolEntryAccept(
125: constantInstruction.constantIndex, this );
126: }
127: }
128:
129: public void visitBranchInstruction(Clazz clazz, Method method,
130: CodeAttribute codeAttribute, int offset,
131: BranchInstruction branchInstruction) {
132: byte opcode = branchInstruction.opcode;
133:
134: // Check for instructions that might cause side effects.
135: if (includeReturnInstructions
136: && (opcode == InstructionConstants.OP_JSR || opcode == InstructionConstants.OP_JSR_W)) {
137: hasSideEffects = true;
138: }
139: }
140:
141: // Implementations for ConstantVisitor.
142:
143: public void visitFieldrefConstant(Clazz clazz,
144: FieldrefConstant fieldrefConstant) {
145: // We'll have to assume accessing an unknown field has side effects.
146: hasSideEffects = true;
147:
148: // Check the referenced field.
149: fieldrefConstant.referencedMemberAccept(this );
150: }
151:
152: public void visitAnyMethodrefConstant(Clazz clazz,
153: RefConstant refConstant) {
154: Member referencedMember = refConstant.referencedMember;
155:
156: // Do we have a reference to the method?
157: if (referencedMember == null) {
158: // We'll have to assume invoking the unknown method has side effects.
159: hasSideEffects = true;
160: } else {
161: // First check the referenced method itself.
162: refConstant.referencedMemberAccept(this );
163:
164: // If the result isn't conclusive, check down the hierarchy.
165: if (!hasSideEffects) {
166: Clazz referencedClass = refConstant.referencedClass;
167: Method referencedMethod = (Method) referencedMember;
168:
169: // Check all other implementations of the method in the class
170: // hierarchy.
171: referencedClass.methodImplementationsAccept(
172: referencedMethod, false, this );
173: }
174: }
175: }
176:
177: // Implementations for MemberVisitor.
178:
179: public void visitProgramField(ProgramClass programClass,
180: ProgramField programField) {
181: hasSideEffects = ReadWriteFieldMarker.isRead(programField);
182: }
183:
184: public void visitProgramMethod(ProgramClass programClass,
185: ProgramMethod programMethod) {
186: hasSideEffects = hasSideEffects
187: || SideEffectMethodMarker.hasSideEffects(programMethod);
188: }
189:
190: public void visitLibraryField(LibraryClass libraryClass,
191: LibraryField libraryField) {
192: hasSideEffects = true;
193: }
194:
195: public void visitLibraryMethod(LibraryClass libraryClass,
196: LibraryMethod libraryMethod) {
197: hasSideEffects = hasSideEffects
198: || !NoSideEffectMethodMarker
199: .hasNoSideEffects(libraryMethod);
200: }
201: }
|