blob: 6a1d87250e7970b3c56eef4a0f83094f15586dd7 [file] [log] [blame]
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.optimize.peephole;
import proguard.classfile.*;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.constant.*;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.*;
import proguard.classfile.instruction.*;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.util.*;
/**
* This InstructionVisitor replaces a given pattern instruction sequence by
* another given replacement instruction sequence. The arguments of the
* instruction sequences can be wildcards that are matched and replaced.
*
* @see InstructionSequenceMatcher
* @author Eric Lafortune
*/
public class InstructionSequenceReplacer
extends SimplifiedVisitor
implements InstructionVisitor,
ConstantVisitor
{
//*
private static final boolean DEBUG = false;
/*/
public static boolean DEBUG = true;
//*/
public static final int X = InstructionSequenceMatcher.X;
public static final int Y = InstructionSequenceMatcher.Y;
public static final int Z = InstructionSequenceMatcher.Z;
public static final int A = InstructionSequenceMatcher.A;
public static final int B = InstructionSequenceMatcher.B;
public static final int C = InstructionSequenceMatcher.C;
public static final int D = InstructionSequenceMatcher.D;
private static final int BOOLEAN_STRING = 0x1;
private static final int CHAR_STRING = 0x2;
private static final int INT_STRING = 0x3;
private static final int LONG_STRING = 0x4;
private static final int FLOAT_STRING = 0x5;
private static final int DOUBLE_STRING = 0x6;
private static final int STRING_STRING = 0x7;
public static final int STRING_A_LENGTH = 0x20000000;
public static final int BOOLEAN_A_STRING = 0x20000001;
public static final int CHAR_A_STRING = 0x20000002;
public static final int INT_A_STRING = 0x20000003;
public static final int LONG_A_STRING = 0x20000004;
public static final int FLOAT_A_STRING = 0x20000005;
public static final int DOUBLE_A_STRING = 0x20000006;
public static final int STRING_A_STRING = 0x20000007;
public static final int BOOLEAN_B_STRING = 0x20000010;
public static final int CHAR_B_STRING = 0x20000020;
public static final int INT_B_STRING = 0x20000030;
public static final int LONG_B_STRING = 0x20000040;
public static final int FLOAT_B_STRING = 0x20000050;
public static final int DOUBLE_B_STRING = 0x20000060;
public static final int STRING_B_STRING = 0x20000070;
private final InstructionSequenceMatcher instructionSequenceMatcher;
private final Constant[] patternConstants;
private final Instruction[] replacementInstructions;
private final BranchTargetFinder branchTargetFinder;
private final CodeAttributeEditor codeAttributeEditor;
private final InstructionVisitor extraInstructionVisitor;
private final MyReplacementInstructionFactory replacementInstructionFactory = new MyReplacementInstructionFactory();
/**
* Creates a new InstructionSequenceReplacer.
* @param patternConstants any constants referenced by the pattern
* instruction.
* @param patternInstructions the pattern instruction sequence.
* @param replacementInstructions the replacement instruction sequence.
* @param branchTargetFinder a branch target finder that has been
* initialized to indicate branch targets
* in the visited code.
* @param codeAttributeEditor a code editor that can be used for
* accumulating changes to the code.
*/
public InstructionSequenceReplacer(Constant[] patternConstants,
Instruction[] patternInstructions,
Instruction[] replacementInstructions,
BranchTargetFinder branchTargetFinder,
CodeAttributeEditor codeAttributeEditor)
{
this(patternConstants,
patternInstructions,
replacementInstructions,
branchTargetFinder,
codeAttributeEditor,
null);
}
/**
* Creates a new InstructionSequenceReplacer.
* @param patternConstants any constants referenced by the pattern
* instruction.
* @param branchTargetFinder a branch target finder that has been
* initialized to indicate branch targets
* in the visited code.
* @param codeAttributeEditor a code editor that can be used for
* accumulating changes to the code.
* @param extraInstructionVisitor an optional extra visitor for all deleted
* load instructions.
*/
public InstructionSequenceReplacer(Constant[] patternConstants,
Instruction[] patternInstructions,
Instruction[] replacementInstructions,
BranchTargetFinder branchTargetFinder,
CodeAttributeEditor codeAttributeEditor,
InstructionVisitor extraInstructionVisitor)
{
this.instructionSequenceMatcher = new InstructionSequenceMatcher(patternConstants, patternInstructions);
this.patternConstants = patternConstants;
this.replacementInstructions = replacementInstructions;
this.branchTargetFinder = branchTargetFinder;
this.codeAttributeEditor = codeAttributeEditor;
this.extraInstructionVisitor = extraInstructionVisitor;
}
// Implementations for InstructionVisitor.
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
{
// Reset the instruction sequence matcher if the instruction is a branch
// target or if it has already been modified.
if ((branchTargetFinder != null &&
branchTargetFinder.isTarget(offset)) ||
codeAttributeEditor.isModified(offset))
{
instructionSequenceMatcher.reset();
}
// Try to match the instruction.
instruction.accept(clazz, method, codeAttribute, offset, instructionSequenceMatcher);
// Did the instruction sequence match and is it still unmodified?
if (instructionSequenceMatcher.isMatching() &&
matchedInstructionsUnmodified())
{
if (DEBUG)
{
System.out.println("InstructionSequenceReplacer: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
System.out.println(" Matched:");
for (int index = 0; index < instructionSequenceMatcher.instructionCount(); index++)
{
int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index);
System.out.println(" "+InstructionFactory.create(codeAttribute.code, matchedOffset).toString(matchedOffset));
}
System.out.println(" Replacement:");
for (int index = 0; index < replacementInstructions.length; index++)
{
int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index);
System.out.println(" "+replacementInstructionFactory.create(clazz, index).shrink().toString(matchedOffset));
}
}
// Replace the instruction sequence.
for (int index = 0; index < replacementInstructions.length; index++)
{
codeAttributeEditor.replaceInstruction(instructionSequenceMatcher.matchedInstructionOffset(index),
replacementInstructionFactory.create(clazz, index));
}
// Delete any remaining instructions in the from sequence.
for (int index = replacementInstructions.length; index < instructionSequenceMatcher.instructionCount(); index++)
{
codeAttributeEditor.deleteInstruction(instructionSequenceMatcher.matchedInstructionOffset(index));
}
// Visit the instruction, if required.
if (extraInstructionVisitor != null)
{
instruction.accept(clazz,
method,
codeAttribute,
offset,
extraInstructionVisitor);
}
}
}
// Small utility methods.
/**
* Returns whether the matched pattern instructions haven't been modified
* before.
*/
private boolean matchedInstructionsUnmodified()
{
for (int index = 0; index < instructionSequenceMatcher.instructionCount(); index++)
{
if (codeAttributeEditor.isModified(instructionSequenceMatcher.matchedInstructionOffset(index)))
{
return false;
}
}
return true;
}
/**
* This class creates replacement instructions for matched sequences, with
* any matched arguments filled out.
*/
private class MyReplacementInstructionFactory
implements InstructionVisitor
{
private Instruction replacementInstruction;
/**
* Creates the replacement instruction for the given index in the
* instruction sequence.
*/
public Instruction create(Clazz clazz, int index)
{
// Create the instruction.
replacementInstructions[index].accept(clazz,
null,
null,
instructionSequenceMatcher.matchedInstructionOffset(index),
this);
// Return it.
return replacementInstruction;
}
// Implementations for InstructionVisitor.
public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
{
replacementInstruction =
new SimpleInstruction(simpleInstruction.opcode,
matchedArgument(clazz, simpleInstruction.constant));
}
public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
{
replacementInstruction =
new VariableInstruction(variableInstruction.opcode,
instructionSequenceMatcher.matchedArgument(variableInstruction.variableIndex),
instructionSequenceMatcher.matchedArgument(variableInstruction.constant));
}
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
{
replacementInstruction =
new ConstantInstruction(constantInstruction.opcode,
matchedConstantIndex((ProgramClass)clazz,
constantInstruction.constantIndex),
instructionSequenceMatcher.matchedArgument(constantInstruction.constant));
}
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
{
replacementInstruction =
new BranchInstruction(branchInstruction.opcode,
instructionSequenceMatcher.matchedBranchOffset(offset,
branchInstruction.branchOffset));
}
public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
{
replacementInstruction =
new TableSwitchInstruction(tableSwitchInstruction.opcode,
instructionSequenceMatcher.matchedBranchOffset(offset, tableSwitchInstruction.defaultOffset),
instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.lowCase),
instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.highCase),
instructionSequenceMatcher.matchedJumpOffsets(offset,
tableSwitchInstruction.jumpOffsets));
}
public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
{
replacementInstruction =
new LookUpSwitchInstruction(lookUpSwitchInstruction.opcode,
instructionSequenceMatcher.matchedBranchOffset(offset, lookUpSwitchInstruction.defaultOffset),
instructionSequenceMatcher.matchedArguments(lookUpSwitchInstruction.cases),
instructionSequenceMatcher.matchedJumpOffsets(offset, lookUpSwitchInstruction.jumpOffsets));
}
/**
* Returns the matched argument for the given pattern argument.
*/
private int matchedArgument(Clazz clazz, int argument)
{
// Special case: do we have to compute the string length?
if (argument == STRING_A_LENGTH)
{
// Return the string length.
return clazz.getStringString(instructionSequenceMatcher.matchedArgument(A)).length();
}
// Otherwise, just return the matched argument.
return instructionSequenceMatcher.matchedArgument(argument);
}
/**
* Returns the matched or newly created constant index for the given
* pattern constant index.
*/
private int matchedConstantIndex(ProgramClass programClass, int constantIndex)
{
// Special case: do we have to create a concatenated string?
if (constantIndex >= BOOLEAN_A_STRING &&
constantIndex <= (STRING_A_STRING | STRING_B_STRING))
{
// Create a new string constant and return its index.
return new ConstantPoolEditor(programClass).addStringConstant(
argumentAsString(programClass, constantIndex & 0xf, A) +
argumentAsString(programClass, (constantIndex >>> 4) & 0xf, B),
null,
null);
}
int matchedConstantIndex =
instructionSequenceMatcher.matchedConstantIndex(constantIndex);
// Do we have a matched constant index?
if (matchedConstantIndex > 0)
{
// Return its index.
return matchedConstantIndex;
}
// Otherwise, we still have to create a new constant.
// This currently only works for constants without any wildcards.
ProgramClass dummyClass = new ProgramClass();
dummyClass.constantPool = patternConstants;
return new ConstantAdder(programClass).addConstant(dummyClass, constantIndex);
}
private String argumentAsString(ProgramClass programClass,
int valueType,
int argument)
{
switch (valueType)
{
case BOOLEAN_STRING:
return Boolean.toString((instructionSequenceMatcher.wasConstant(argument) ?
((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() :
instructionSequenceMatcher.matchedArgument(argument)) != 0);
case CHAR_STRING:
return Character.toString((char)(instructionSequenceMatcher.wasConstant(argument) ?
((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() :
instructionSequenceMatcher.matchedArgument(argument)));
case INT_STRING:
return Integer.toString(instructionSequenceMatcher.wasConstant(argument) ?
((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() :
instructionSequenceMatcher.matchedArgument(argument));
case LONG_STRING:
return Long.toString(instructionSequenceMatcher.wasConstant(argument) ?
((LongConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() :
instructionSequenceMatcher.matchedArgument(argument));
case FLOAT_STRING:
return Float.toString(instructionSequenceMatcher.wasConstant(argument) ?
((FloatConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() :
instructionSequenceMatcher.matchedArgument(argument));
case DOUBLE_STRING:
return Double.toString(instructionSequenceMatcher.wasConstant(argument) ?
((DoubleConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() :
instructionSequenceMatcher.matchedArgument(argument));
case STRING_STRING:
return
programClass.getStringString(instructionSequenceMatcher.matchedConstantIndex(argument));
default:
return "";
}
}
}
}