Home | History | Annotate | Download | only in editor
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2009 Eric Lafortune (eric (at) 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  */
     21 package proguard.classfile.editor;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.attribute.CodeAttribute;
     25 import proguard.classfile.attribute.visitor.AttributeVisitor;
     26 import proguard.classfile.instruction.*;
     27 import proguard.classfile.instruction.visitor.InstructionVisitor;
     28 import proguard.classfile.util.SimplifiedVisitor;
     29 
     30 /**
     31  * This InstructionVisitor writes out the instructions that it visits,
     32  * collecting instructions that have to be widened. As an AttributeVisitor,
     33  * it then applies the collected changes. The process will be repeated
     34  * recursively, if necessary.
     35  *
     36  * @author Eric Lafortune
     37  */
     38 public class InstructionWriter
     39 extends      SimplifiedVisitor
     40 implements   InstructionVisitor,
     41              AttributeVisitor
     42 {
     43     private int codeLength;
     44 
     45     private CodeAttributeEditor codeAttributeEditor;
     46 
     47 
     48     /**
     49      * Resets the accumulated code changes.
     50      * @param codeLength the length of the code that will be edited next.
     51      */
     52     public void reset(int codeLength)
     53     {
     54         this.codeLength = codeLength;
     55 
     56         // The code attribute editor has to be created lazily.
     57         if (codeAttributeEditor != null)
     58         {
     59             codeAttributeEditor.reset(codeLength);
     60         }
     61     }
     62 
     63 
     64     // Implementations for InstructionVisitor.
     65 
     66     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     67     {
     68         // Try to write out the instruction.
     69         // Simple instructions should always fit.
     70         simpleInstruction.write(codeAttribute, offset);
     71     }
     72 
     73 
     74     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     75     {
     76         try
     77         {
     78             // Try to write out the instruction.
     79             constantInstruction.write(codeAttribute, offset);
     80         }
     81         catch (IllegalArgumentException exception)
     82         {
     83             // Create a new constant instruction that will fit.
     84             Instruction replacementInstruction =
     85                 new ConstantInstruction(constantInstruction.opcode,
     86                                         constantInstruction.constantIndex,
     87                                         constantInstruction.constant).shrink();
     88 
     89             replaceInstruction(offset, replacementInstruction);
     90 
     91             // Write out a dummy constant instruction for now.
     92             constantInstruction.constantIndex = 0;
     93             constantInstruction.constant      = 0;
     94             constantInstruction.write(codeAttribute, offset);
     95         }
     96     }
     97 
     98 
     99     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
    100     {
    101         try
    102         {
    103             // Try to write out the instruction.
    104             variableInstruction.write(codeAttribute, offset);
    105         }
    106         catch (IllegalArgumentException exception)
    107         {
    108             // Create a new variable instruction that will fit.
    109             Instruction replacementInstruction =
    110                 new VariableInstruction(variableInstruction.opcode,
    111                                         variableInstruction.variableIndex,
    112                                         variableInstruction.constant).shrink();
    113 
    114             replaceInstruction(offset, replacementInstruction);
    115 
    116             // Write out a dummy variable instruction for now.
    117             variableInstruction.variableIndex = 0;
    118             variableInstruction.constant      = 0;
    119             variableInstruction.write(codeAttribute, offset);
    120         }
    121     }
    122 
    123 
    124     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
    125     {
    126         try
    127         {
    128             // Try to write out the instruction.
    129             branchInstruction.write(codeAttribute, offset);
    130         }
    131         catch (IllegalArgumentException exception)
    132         {
    133             // Create a new unconditional branch that will fit.
    134             Instruction replacementInstruction =
    135                 new BranchInstruction(InstructionConstants.OP_GOTO_W,
    136                                       branchInstruction.branchOffset);
    137 
    138             // Create a new instruction that will fit.
    139             switch (branchInstruction.opcode)
    140             {
    141                 default:
    142                 {
    143                     // Create a new branch instruction that will fit.
    144                     replacementInstruction =
    145                         new BranchInstruction(branchInstruction.opcode,
    146                                               branchInstruction.branchOffset).shrink();
    147 
    148                     break;
    149                 }
    150 
    151                 // Some special cases, for which a wide branch doesn't exist.
    152                 case InstructionConstants.OP_IFEQ:
    153                 case InstructionConstants.OP_IFNE:
    154                 case InstructionConstants.OP_IFLT:
    155                 case InstructionConstants.OP_IFGE:
    156                 case InstructionConstants.OP_IFGT:
    157                 case InstructionConstants.OP_IFLE:
    158                 case InstructionConstants.OP_IFICMPEQ:
    159                 case InstructionConstants.OP_IFICMPNE:
    160                 case InstructionConstants.OP_IFICMPLT:
    161                 case InstructionConstants.OP_IFICMPGE:
    162                 case InstructionConstants.OP_IFICMPGT:
    163                 case InstructionConstants.OP_IFICMPLE:
    164                 case InstructionConstants.OP_IFACMPEQ:
    165                 case InstructionConstants.OP_IFACMPNE:
    166                 {
    167                     // Insert the complementary conditional branch.
    168                     Instruction complementaryConditionalBranch =
    169                         new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1),
    170                                               (1+2) + (1+4));
    171 
    172                     insertBeforeInstruction(offset, complementaryConditionalBranch);
    173 
    174                     // Create a new unconditional branch that will fit.
    175                     break;
    176                 }
    177 
    178                 case InstructionConstants.OP_IFNULL:
    179                 case InstructionConstants.OP_IFNONNULL:
    180                 {
    181                     // Insert the complementary conditional branch.
    182                     Instruction complementaryConditionalBranch =
    183                         new BranchInstruction((byte)(branchInstruction.opcode ^ 1),
    184                                               (1+2) + (1+4));
    185 
    186                     insertBeforeInstruction(offset, complementaryConditionalBranch);
    187 
    188                     // Create a new unconditional branch that will fit.
    189                     break;
    190                 }
    191             }
    192 
    193             replaceInstruction(offset, replacementInstruction);
    194 
    195             // Write out a dummy branch instruction for now.
    196             branchInstruction.branchOffset = 0;
    197             branchInstruction.write(codeAttribute, offset);
    198         }
    199     }
    200 
    201 
    202     public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
    203     {
    204         // Try to write out the instruction.
    205         // Switch instructions should always fit.
    206         switchInstruction.write(codeAttribute, offset);
    207     }
    208 
    209 
    210     // Implementations for AttributeVisitor.
    211 
    212     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    213     {
    214         // Avoid doing any work if nothing is changing anyway.
    215         if (codeAttributeEditor != null)
    216         {
    217             // Apply the collected expansions.
    218             codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
    219 
    220             // Clear the modifications for the next run.
    221             codeAttributeEditor = null;
    222         }
    223     }
    224 
    225 
    226     // Small utility methods.
    227 
    228     /**
    229      * Remembers to place the given instruction right before the instruction
    230      * at the given offset.
    231      */
    232     private void insertBeforeInstruction(int instructionOffset, Instruction instruction)
    233     {
    234         ensureCodeAttributeEditor();
    235 
    236         // Replace the instruction.
    237         codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction);
    238     }
    239 
    240 
    241     /**
    242      * Remembers to replace the instruction at the given offset by the given
    243      * instruction.
    244      */
    245     private void replaceInstruction(int instructionOffset, Instruction instruction)
    246     {
    247         ensureCodeAttributeEditor();
    248 
    249         // Replace the instruction.
    250         codeAttributeEditor.replaceInstruction(instructionOffset, instruction);
    251     }
    252 
    253 
    254     /**
    255      * Remembers to place the given instruction right after the instruction
    256      * at the given offset.
    257      */
    258     private void insertAfterInstruction(int instructionOffset, Instruction instruction)
    259     {
    260         ensureCodeAttributeEditor();
    261 
    262         // Replace the instruction.
    263         codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction);
    264     }
    265 
    266 
    267     /**
    268      * Makes sure there is a code attribute editor for the given code attribute.
    269      */
    270     private void ensureCodeAttributeEditor()
    271     {
    272         if (codeAttributeEditor == null)
    273         {
    274             codeAttributeEditor = new CodeAttributeEditor();
    275             codeAttributeEditor.reset(codeLength);
    276         }
    277     }
    278 }
    279