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.peephole;
022:
023: import proguard.classfile.*;
024: import proguard.classfile.attribute.CodeAttribute;
025: import proguard.classfile.editor.CodeAttributeEditor;
026: import proguard.classfile.instruction.*;
027: import proguard.classfile.instruction.visitor.InstructionVisitor;
028: import proguard.classfile.util.SimplifiedVisitor;
029:
030: /**
031: * This InstructionVisitor deletes all push/pop instruction pairs. In this
032: * context, push instructions are instructions that push values onto the stack,
033: * like dup and load instructions.
034: *
035: * @author Eric Lafortune
036: */
037: public class PushPopRemover extends SimplifiedVisitor implements
038: InstructionVisitor {
039: private final BranchTargetFinder branchTargetFinder;
040: private final CodeAttributeEditor codeAttributeEditor;
041: private final InstructionVisitor extraInstructionVisitor;
042:
043: /**
044: * Creates a new PushPopRemover.
045: * @param branchTargetFinder a branch target finder that has been
046: * initialized to indicate branch targets
047: * in the visited code.
048: * @param codeAttributeEditor a code editor that can be used for
049: * accumulating changes to the code.
050: */
051: public PushPopRemover(BranchTargetFinder branchTargetFinder,
052: CodeAttributeEditor codeAttributeEditor) {
053: this (branchTargetFinder, codeAttributeEditor, null);
054: }
055:
056: /**
057: * Creates a new PushPopRemover.
058: * @param branchTargetFinder a branch target finder that has been
059: * initialized to indicate branch targets
060: * in the visited code.
061: * @param codeAttributeEditor a code editor that can be used for
062: * accumulating changes to the code.
063: * @param extraInstructionVisitor an optional extra visitor for all deleted
064: * push instructions.
065: */
066: public PushPopRemover(BranchTargetFinder branchTargetFinder,
067: CodeAttributeEditor codeAttributeEditor,
068: InstructionVisitor extraInstructionVisitor) {
069: this .branchTargetFinder = branchTargetFinder;
070: this .codeAttributeEditor = codeAttributeEditor;
071: this .extraInstructionVisitor = extraInstructionVisitor;
072: }
073:
074: // Implementations for InstructionVisitor.
075:
076: public void visitAnyInstruction(Clazz clazz, Method method,
077: CodeAttribute codeAttribute, int offset,
078: Instruction instruction) {
079: }
080:
081: public void visitSimpleInstruction(Clazz clazz, Method method,
082: CodeAttribute codeAttribute, int offset,
083: SimpleInstruction simpleInstruction) {
084: switch (simpleInstruction.opcode) {
085: case InstructionConstants.OP_ICONST_M1:
086: case InstructionConstants.OP_ICONST_0:
087: case InstructionConstants.OP_ICONST_1:
088: case InstructionConstants.OP_ICONST_2:
089: case InstructionConstants.OP_ICONST_3:
090: case InstructionConstants.OP_ICONST_4:
091: case InstructionConstants.OP_ICONST_5:
092: case InstructionConstants.OP_LCONST_0:
093: case InstructionConstants.OP_LCONST_1:
094: case InstructionConstants.OP_FCONST_0:
095: case InstructionConstants.OP_FCONST_1:
096: case InstructionConstants.OP_FCONST_2:
097: case InstructionConstants.OP_DCONST_0:
098: case InstructionConstants.OP_DCONST_1:
099:
100: case InstructionConstants.OP_DUP:
101: case InstructionConstants.OP_DUP2:
102: case InstructionConstants.OP_BIPUSH:
103: case InstructionConstants.OP_SIPUSH:
104: case InstructionConstants.OP_LDC:
105: case InstructionConstants.OP_LDC_W:
106: case InstructionConstants.OP_LDC2_W:
107: // All these simple instructions are pushing instructions.
108: deleteWithSubsequentPop(clazz, method, codeAttribute,
109: offset, simpleInstruction);
110: break;
111: }
112: }
113:
114: public void visitVariableInstruction(Clazz clazz, Method method,
115: CodeAttribute codeAttribute, int offset,
116: VariableInstruction variableInstruction) {
117: if (variableInstruction.isLoad()
118: && variableInstruction.opcode != InstructionConstants.OP_RET) {
119: // All load instructions are pushing instructions.
120: deleteWithSubsequentPop(clazz, method, codeAttribute,
121: offset, variableInstruction);
122: }
123: }
124:
125: // Small utility methods.
126:
127: /**
128: * Deletes the given instruction and its subsequent compatible pop instruction,
129: * if any, and if the latter is not a branch target.
130: */
131: private void deleteWithSubsequentPop(Clazz clazz, Method method,
132: CodeAttribute codeAttribute, int offset,
133: Instruction instruction) {
134: boolean isCategory2 = instruction.isCategory2();
135:
136: int nextOffset = offset + instruction.length(offset);
137:
138: if (!codeAttributeEditor.isModified(offset)
139: && !codeAttributeEditor.isModified(nextOffset)
140: && !branchTargetFinder.isTarget(nextOffset)) {
141: Instruction nextInstruction = InstructionFactory.create(
142: codeAttribute.code, nextOffset);
143: int nextOpcode = nextInstruction.opcode;
144: if ((nextOpcode == InstructionConstants.OP_POP || nextOpcode == InstructionConstants.OP_POP2)
145: && nextInstruction.isCategory2() == isCategory2) {
146: // Delete the pushing instruction and the pop instruction.
147: codeAttributeEditor.deleteInstruction(offset);
148: codeAttributeEditor.deleteInstruction(nextOffset);
149:
150: // Visit the instruction, if required.
151: if (extraInstructionVisitor != null) {
152: instruction.accept(clazz, method, codeAttribute,
153: offset, extraInstructionVisitor);
154: }
155: }
156: }
157: }
158: }
|