Home | History | Annotate | Download | only in editor
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 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. The caller still has to update the frame sizes.
     35  *
     36  * @author Eric Lafortune
     37  */
     38 public class InstructionWriter
     39 extends      SimplifiedVisitor
     40 implements   InstructionVisitor,
     41              AttributeVisitor
     42 {
     43     //*
     44     private static final boolean DEBUG = false;
     45     /*/
     46     public  static       boolean DEBUG = false;
     47     //*/
     48 
     49 
     50     private int codeLength;
     51 
     52     private CodeAttributeEditor codeAttributeEditor;
     53 
     54 
     55     /**
     56      * Resets the accumulated code.
     57      * @param codeLength the length of the code that will be edited next.
     58      */
     59     public void reset(int codeLength)
     60     {
     61         this.codeLength = codeLength;
     62 
     63         if (codeAttributeEditor != null)
     64         {
     65             codeAttributeEditor.reset(codeLength);
     66         }
     67     }
     68 
     69 
     70     /**
     71      * Extends the size of the accumulated code.
     72      * @param codeLength the length of the code that will be edited next.
     73      */
     74     public void extend(int codeLength)
     75     {
     76         this.codeLength = codeLength;
     77 
     78         if (codeAttributeEditor != null)
     79         {
     80             codeAttributeEditor.extend(codeLength);
     81         }
     82     }
     83 
     84 
     85     // Implementations for InstructionVisitor.
     86 
     87     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     88     {
     89         // Try to write out the instruction.
     90         // Simple instructions should always fit.
     91         simpleInstruction.write(codeAttribute, offset);
     92     }
     93 
     94 
     95     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     96     {
     97         try
     98         {
     99             // Try to write out the instruction.
    100             constantInstruction.write(codeAttribute, offset);
    101         }
    102         catch (IllegalArgumentException exception)
    103         {
    104             // Create a new constant instruction that will fit.
    105             Instruction replacementInstruction =
    106                 new ConstantInstruction(constantInstruction.opcode,
    107                                         constantInstruction.constantIndex,
    108                                         constantInstruction.constant);
    109 
    110             if (DEBUG)
    111             {
    112                 System.out.println("  "+constantInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString());
    113             }
    114 
    115             replaceInstruction(offset, replacementInstruction);
    116 
    117             // Write out a dummy constant instruction for now.
    118             constantInstruction.constantIndex = 0;
    119             constantInstruction.constant      = 0;
    120             constantInstruction.write(codeAttribute, offset);
    121         }
    122     }
    123 
    124 
    125     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
    126     {
    127         try
    128         {
    129             // Try to write out the instruction.
    130             variableInstruction.write(codeAttribute, offset);
    131         }
    132         catch (IllegalArgumentException exception)
    133         {
    134             // Create a new variable instruction that will fit.
    135             Instruction replacementInstruction =
    136                 new VariableInstruction(variableInstruction.opcode,
    137                                         variableInstruction.variableIndex,
    138                                         variableInstruction.constant);
    139 
    140             replaceInstruction(offset, replacementInstruction);
    141 
    142             if (DEBUG)
    143             {
    144                 System.out.println("  "+variableInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString());
    145             }
    146 
    147             // Write out a dummy variable instruction for now.
    148             variableInstruction.variableIndex = 0;
    149             variableInstruction.constant      = 0;
    150             variableInstruction.write(codeAttribute, offset);
    151         }
    152     }
    153 
    154 
    155     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
    156     {
    157         try
    158         {
    159             // Try to write out the instruction.
    160             branchInstruction.write(codeAttribute, offset);
    161         }
    162         catch (IllegalArgumentException exception)
    163         {
    164             // Create a new unconditional branch that will fit.
    165             Instruction replacementInstruction =
    166                 new BranchInstruction(InstructionConstants.OP_GOTO_W,
    167                                       branchInstruction.branchOffset);
    168 
    169             // Create a new instruction that will fit.
    170             switch (branchInstruction.opcode)
    171             {
    172                 default:
    173                 {
    174                     // Create a new branch instruction that will fit.
    175                     replacementInstruction =
    176                         new BranchInstruction(branchInstruction.opcode,
    177                                               branchInstruction.branchOffset);
    178 
    179                     break;
    180                 }
    181 
    182                 // Some special cases, for which a wide branch doesn't exist.
    183                 case InstructionConstants.OP_IFEQ:
    184                 case InstructionConstants.OP_IFNE:
    185                 case InstructionConstants.OP_IFLT:
    186                 case InstructionConstants.OP_IFGE:
    187                 case InstructionConstants.OP_IFGT:
    188                 case InstructionConstants.OP_IFLE:
    189                 case InstructionConstants.OP_IFICMPEQ:
    190                 case InstructionConstants.OP_IFICMPNE:
    191                 case InstructionConstants.OP_IFICMPLT:
    192                 case InstructionConstants.OP_IFICMPGE:
    193                 case InstructionConstants.OP_IFICMPGT:
    194                 case InstructionConstants.OP_IFICMPLE:
    195                 case InstructionConstants.OP_IFACMPEQ:
    196                 case InstructionConstants.OP_IFACMPNE:
    197                 {
    198                     // Insert the complementary conditional branch.
    199                     Instruction complementaryConditionalBranch =
    200                         new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1),
    201                                               (1+2) + (1+4));
    202 
    203                     insertBeforeInstruction(offset, complementaryConditionalBranch);
    204 
    205                     // Create a new unconditional branch that will fit.
    206                     break;
    207                 }
    208 
    209                 case InstructionConstants.OP_IFNULL:
    210                 case InstructionConstants.OP_IFNONNULL:
    211                 {
    212                     // Insert the complementary conditional branch.
    213                     Instruction complementaryConditionalBranch =
    214                         new BranchInstruction((byte)(branchInstruction.opcode ^ 1),
    215                                               (1+2) + (1+4));
    216 
    217                     insertBeforeInstruction(offset, complementaryConditionalBranch);
    218 
    219                     // Create a new unconditional branch that will fit.
    220                     break;
    221                 }
    222             }
    223 
    224             if (DEBUG)
    225             {
    226                 System.out.println("  "+branchInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString());
    227             }
    228 
    229             replaceInstruction(offset, replacementInstruction);
    230 
    231             // Write out a dummy branch instruction for now.
    232             branchInstruction.branchOffset = 0;
    233             branchInstruction.write(codeAttribute, offset);
    234         }
    235     }
    236 
    237 
    238     public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
    239     {
    240         // Try to write out the instruction.
    241         // Switch instructions should always fit.
    242         switchInstruction.write(codeAttribute, offset);
    243     }
    244 
    245 
    246     // Implementations for AttributeVisitor.
    247 
    248     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    249     {
    250         // Avoid doing any work if nothing is changing anyway.
    251         if (codeAttributeEditor != null)
    252         {
    253             if (DEBUG)
    254             {
    255                 System.out.println("InstructionWriter: widening instructions in "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
    256             }
    257 
    258             // Apply the collected expansions.
    259             codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
    260 
    261             // Don't keep the editor around. We're assuming it won't be needed
    262             // very often, so we don't want to be resetting it all the time.
    263             codeAttributeEditor = null;
    264         }
    265     }
    266 
    267 
    268     // Small utility methods.
    269 
    270     /**
    271      * Remembers to place the given instruction right before the instruction
    272      * at the given offset.
    273      */
    274     private void insertBeforeInstruction(int instructionOffset, Instruction instruction)
    275     {
    276         ensureCodeAttributeEditor();
    277 
    278         // Replace the instruction.
    279         codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction);
    280     }
    281 
    282 
    283     /**
    284      * Remembers to replace the instruction at the given offset by the given
    285      * instruction.
    286      */
    287     private void replaceInstruction(int instructionOffset, Instruction instruction)
    288     {
    289         ensureCodeAttributeEditor();
    290 
    291         // Replace the instruction.
    292         codeAttributeEditor.replaceInstruction(instructionOffset, instruction);
    293     }
    294 
    295 
    296     /**
    297      * Remembers to place the given instruction right after the instruction
    298      * at the given offset.
    299      */
    300     private void insertAfterInstruction(int instructionOffset, Instruction instruction)
    301     {
    302         ensureCodeAttributeEditor();
    303 
    304         // Replace the instruction.
    305         codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction);
    306     }
    307 
    308 
    309     /**
    310      * Makes sure there is a code attribute editor for the given code attribute.
    311      */
    312     private void ensureCodeAttributeEditor()
    313     {
    314         if (codeAttributeEditor == null)
    315         {
    316             codeAttributeEditor = new CodeAttributeEditor(false, true);
    317             codeAttributeEditor.reset(codeLength);
    318         }
    319     }
    320 }
    321