proguard.classfile.editor.InstructionWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of proguard-core Show documentation
Show all versions of proguard-core Show documentation
ProGuardCORE is a free library to read, analyze, modify, and write Java class files.
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2020 Guardsquare NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package proguard.classfile.editor;
import proguard.classfile.*;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.instruction.*;
import proguard.classfile.instruction.visitor.InstructionVisitor;
/**
* This {@link InstructionVisitor} writes out the instructions that it visits,
* collecting instructions that have to be widened. As an {@link AttributeVisitor},
* it then applies the collected changes. The process will be repeated
* recursively, if necessary. The caller still has to update the frame sizes.
*
* @author Eric Lafortune
*/
public class InstructionWriter
implements InstructionVisitor,
AttributeVisitor
{
//*
private static final boolean DEBUG = false;
/*/
public static boolean DEBUG = System.getProperty("iw") != null;
//*/
private int codeLength;
private CodeAttributeEditor codeAttributeEditor;
/**
* Resets the accumulated code changes for a given anticipated maximum
* code length. If necessary, the size may still be extended while
* editing the code, with {@link #extend(int)}.
* @param codeLength the length of the code that will be edited next.
*/
public void reset(int codeLength)
{
this.codeLength = codeLength;
if (codeAttributeEditor != null)
{
codeAttributeEditor.reset(codeLength);
}
}
/**
* Extends the size of the accumulated code.
* @param codeLength the length of the code that will be edited next.
*/
public void extend(int codeLength)
{
this.codeLength = codeLength;
if (codeAttributeEditor != null)
{
codeAttributeEditor.extend(codeLength);
}
}
// Implementations for InstructionVisitor.
public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
{
// Try to write out the instruction.
// Simple instructions should always fit.
simpleInstruction.write(codeAttribute, offset);
}
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
{
try
{
// Try to write out the instruction.
constantInstruction.write(codeAttribute, offset);
}
catch (IllegalArgumentException exception)
{
// Create a new constant instruction that will fit.
Instruction replacementInstruction =
new ConstantInstruction(constantInstruction.opcode,
constantInstruction.constantIndex,
constantInstruction.constant);
if (DEBUG)
{
System.out.println(" "+constantInstruction.toString(clazz, offset)+" will be widened to "+replacementInstruction.toString());
}
replaceInstruction(offset, replacementInstruction);
// Write out a dummy constant instruction for now.
constantInstruction.constantIndex = 0;
constantInstruction.constant = 0;
constantInstruction.write(codeAttribute, offset);
}
}
public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
{
try
{
// Try to write out the instruction.
variableInstruction.write(codeAttribute, offset);
}
catch (IllegalArgumentException exception)
{
// Create a new variable instruction that will fit.
Instruction replacementInstruction =
new VariableInstruction(variableInstruction.opcode,
variableInstruction.variableIndex,
variableInstruction.constant);
replaceInstruction(offset, replacementInstruction);
if (DEBUG)
{
System.out.println(" "+variableInstruction.toString(clazz, offset)+" will be widened to "+replacementInstruction.toString());
}
// Write out a dummy variable instruction for now.
variableInstruction.variableIndex = 0;
variableInstruction.constant = 0;
variableInstruction.write(codeAttribute, offset);
}
}
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
{
try
{
// Try to write out the instruction.
branchInstruction.write(codeAttribute, offset);
}
catch (IllegalArgumentException exception)
{
// Create a new unconditional branch that will fit.
Instruction replacementInstruction =
new BranchInstruction(Instruction.OP_GOTO_W,
branchInstruction.branchOffset);
// Create a new instruction that will fit.
switch (branchInstruction.opcode)
{
default:
{
// Create a new branch instruction that will fit.
replacementInstruction =
new BranchInstruction(branchInstruction.opcode,
branchInstruction.branchOffset);
break;
}
// Some special cases, for which a wide branch doesn't exist.
case Instruction.OP_IFEQ:
case Instruction.OP_IFNE:
case Instruction.OP_IFLT:
case Instruction.OP_IFGE:
case Instruction.OP_IFGT:
case Instruction.OP_IFLE:
case Instruction.OP_IFICMPEQ:
case Instruction.OP_IFICMPNE:
case Instruction.OP_IFICMPLT:
case Instruction.OP_IFICMPGE:
case Instruction.OP_IFICMPGT:
case Instruction.OP_IFICMPLE:
case Instruction.OP_IFACMPEQ:
case Instruction.OP_IFACMPNE:
{
// Insert the complementary conditional branch.
Instruction complementaryConditionalBranch =
new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1),
(1+2));
insertBeforeInstruction(offset, complementaryConditionalBranch);
// Create a new unconditional branch that will fit.
break;
}
case Instruction.OP_IFNULL:
case Instruction.OP_IFNONNULL:
{
// Insert the complementary conditional branch.
Instruction complementaryConditionalBranch =
new BranchInstruction((byte)(branchInstruction.opcode ^ 1),
(1+2));
insertBeforeInstruction(offset, complementaryConditionalBranch);
// Create a new unconditional branch that will fit.
break;
}
}
if (DEBUG)
{
System.out.println(" "+branchInstruction.toString(clazz, offset)+" will be widened to "+replacementInstruction.toString());
}
replaceInstruction(offset, replacementInstruction);
// Write out a dummy branch instruction for now.
branchInstruction.branchOffset = 0;
branchInstruction.write(codeAttribute, offset);
}
}
public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
{
// Try to write out the instruction.
// Switch instructions should always fit.
switchInstruction.write(codeAttribute, offset);
}
// Implementations for AttributeVisitor.
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
// Avoid doing any work if nothing is changing anyway.
if (codeAttributeEditor != null)
{
if (DEBUG)
{
System.out.println("InstructionWriter: widening instructions in "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
}
// Apply the collected expansions.
codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
// Don't keep the editor around. We're assuming it won't be needed
// very often, so we don't want to be resetting it all the time.
codeAttributeEditor = null;
}
}
// Small utility methods.
/**
* Remembers to place the given instruction right before the instruction
* at the given offset.
*/
private void insertBeforeInstruction(int instructionOffset, Instruction instruction)
{
ensureCodeAttributeEditor();
// Replace the instruction.
codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction);
}
/**
* Remembers to replace the instruction at the given offset by the given
* instruction.
*/
private void replaceInstruction(int instructionOffset, Instruction instruction)
{
ensureCodeAttributeEditor();
// Replace the instruction.
codeAttributeEditor.replaceInstruction(instructionOffset, instruction);
}
/**
* Remembers to place the given instruction right after the instruction
* at the given offset.
*/
private void insertAfterInstruction(int instructionOffset, Instruction instruction)
{
ensureCodeAttributeEditor();
// Replace the instruction.
codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction);
}
/**
* Makes sure there is a code attribute editor for the given code attribute.
*/
private void ensureCodeAttributeEditor()
{
if (codeAttributeEditor == null)
{
codeAttributeEditor = new CodeAttributeEditor(false, true);
codeAttributeEditor.reset(codeLength);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy