View Javadoc

    1   /*
    2    * Copyright  2000-2004 The Apache Software Foundation
    3    *
    4    *  Licensed under the Apache License, Version 2.0 (the "License");
    5    *  you may not use this file except in compliance with the License.
    6    *  You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   *  Unless required by applicable law or agreed to in writing, software
    11   *  distributed under the License is distributed on an "AS IS" BASIS,
    12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *  See the License for the specific language governing permissions and
    14   *  limitations under the License.
    15   *
    16   */
    17  package org.apache.bcel.generic;
    18
    19  import java.io.DataOutputStream;
    20  import java.io.IOException;
    21  import java.io.Serializable;
    22  import java.util.Locale;
    23  import org.apache.bcel.Constants;
    24  import org.apache.bcel.classfile.ConstantPool;
    25  import org.apache.bcel.util.ByteSequence;
    26
    27  /***
    28   * Abstract super class for all Java byte codes.
    29   *
    30   * @version $Id: Instruction.java 386056 2006-03-15 11:31:56Z tcurdt $
    31   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
    32   */
    33  public abstract class Instruction implements Cloneable, Serializable {
    34
    35      protected short length = 1; // Length of instruction in bytes
    36      protected short opcode = -1; // Opcode number
    37      private static InstructionComparator cmp = InstructionComparator.DEFAULT;
    38
    39
    40      /***
    41       * Empty constructor needed for the Class.newInstance() statement in
    42       * Instruction.readInstruction(). Not to be used otherwise.
    43       */
    44      Instruction() {
    45      }
    46
    47
    48      public Instruction(short opcode, short length) {
    49          this.length = length;
    50          this.opcode = opcode;
    51      }
    52
    53
    54      /***
    55       * Dump instruction as byte code to stream out.
    56       * @param out Output stream
    57       */
    58      public void dump( DataOutputStream out ) throws IOException {
    59          out.writeByte(opcode); // Common for all instructions
    60      }
    61
    62
    63      /*** @return name of instruction, i.e., opcode name
    64       */
    65      public String getName() {
    66          return Constants.OPCODE_NAMES[opcode];
    67      }
    68
    69
    70      /***
    71       * Long output format:
    72       *
    73       * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]"
    74       * "("&lt;length of instruction&gt;")"
    75       *
    76       * @param verbose long/short format switch
    77       * @return mnemonic for instruction
    78       */
    79      public String toString( boolean verbose ) {
    80          if (verbose) {
    81              return getName() + "[" + opcode + "](" + length + ")";
    82          } else {
    83              return getName();
    84          }
    85      }
    86
    87
    88      /***
    89       * @return mnemonic for instruction in verbose format
    90       */
    91      public String toString() {
    92          return toString(true);
    93      }
    94
    95
    96      /***
    97       * @return mnemonic for instruction with sumbolic references resolved
    98       */
    99      public String toString( ConstantPool cp ) {
    100         return toString(false);
    101     }
    102
    103
    104     /***
    105      * Use with caution, since `BranchInstruction's have a `target' reference which
    106      * is not copied correctly (only basic types are). This also applies for
    107      * `Select' instructions with their multiple branch targets.
    108      *
    109      * @see BranchInstruction
    110      * @return (shallow) copy of an instruction
    111      */
    112     public Instruction copy() {
    113         Instruction i = null;
    114         // "Constant" instruction, no need to duplicate
    115         if (InstructionConstants.INSTRUCTIONS[this.getOpcode()] != null) {
    116             i = this;
    117         } else {
    118             try {
    119                 i = (Instruction) clone();
    120             } catch (CloneNotSupportedException e) {
    121                 System.err.println(e);
    122             }
    123         }
    124         return i;
    125     }
    126
    127
    128     /***
    129      * Read needed data (e.g. index) from file.
    130      *
    131      * @param bytes byte sequence to read from
    132      * @param wide "wide" instruction flag
    133      */
    134     protected void initFromFile( ByteSequence bytes, boolean wide ) throws IOException {
    135     }
    136
    137
    138     /***
    139      * Read an instruction from (byte code) input stream and return the
    140      * appropiate object.
    141      *
    142      * @param bytes input stream bytes
    143      * @return instruction object being read
    144      */
    145     public static final Instruction readInstruction( ByteSequence bytes ) throws IOException {
    146         boolean wide = false;
    147         short opcode = (short) bytes.readUnsignedByte();
    148         Instruction obj = null;
    149         if (opcode == Constants.WIDE) { // Read next opcode after wide byte
    150             wide = true;
    151             opcode = (short) bytes.readUnsignedByte();
    152         }
    153         if (InstructionConstants.INSTRUCTIONS[opcode] != null) {
    154             return InstructionConstants.INSTRUCTIONS[opcode]; // Used predefined immutable object, if available
    155         }
    156         /* Find appropiate class, instantiate an (empty) instruction object
    157          * and initialize it by hand.
    158          */
    159         Class clazz;
    160         try {
    161             clazz = Class.forName(className(opcode));
    162         } catch (ClassNotFoundException cnfe) {
    163             // If a class by that name does not exist, the opcode is illegal.
    164             // Note that IMPDEP1, IMPDEP2, BREAKPOINT are also illegal in a sense.
    165             throw new ClassGenException("Illegal opcode detected.");
    166         }
    167         try {
    168             obj = (Instruction) clazz.newInstance();
    169             if (wide
    170                     && !((obj instanceof LocalVariableInstruction) || (obj instanceof IINC) || (obj instanceof RET))) {
    171                 throw new Exception("Illegal opcode after wide: " + opcode);
    172             }
    173             obj.setOpcode(opcode);
    174             obj.initFromFile(bytes, wide); // Do further initializations, if any
    175             // Byte code offset set in InstructionList
    176         } catch (Exception e) {
    177             throw new ClassGenException(e.toString());
    178         }
    179         return obj;
    180     }
    181
    182
    183     private static final String className( short opcode ) {
    184         String name = Constants.OPCODE_NAMES[opcode].toUpperCase(Locale.ENGLISH);
    185         /* ICONST_0, etc. will be shortened to ICONST, etc., since ICONST_0 and the like
    186          * are not implemented (directly).
    187          */
    188         try {
    189             int len = name.length();
    190             char ch1 = name.charAt(len - 2), ch2 = name.charAt(len - 1);
    191             if ((ch1 == '_') && (ch2 >= '0') && (ch2 <= '5')) {
    192                 name = name.substring(0, len - 2);
    193             }
    194             if (name.equals("ICONST_M1")) {
    195                 name = "ICONST";
    196             }
    197         } catch (StringIndexOutOfBoundsException e) {
    198             System.err.println(e);
    199         }
    200         return "org.apache.bcel.generic." + name;
    201     }
    202
    203
    204     /***
    205      * This method also gives right results for instructions whose
    206      * effect on the stack depends on the constant pool entry they
    207      * reference.
    208      *  @return Number of words consumed from stack by this instruction,
    209      * or Constants.UNPREDICTABLE, if this can not be computed statically
    210      */
    211     public int consumeStack( ConstantPoolGen cpg ) {
    212         return Constants.CONSUME_STACK[opcode];
    213     }
    214
    215
    216     /***
    217      * This method also gives right results for instructions whose
    218      * effect on the stack depends on the constant pool entry they
    219      * reference.
    220      * @return Number of words produced onto stack by this instruction,
    221      * or Constants.UNPREDICTABLE, if this can not be computed statically
    222      */
    223     public int produceStack( ConstantPoolGen cpg ) {
    224         return Constants.PRODUCE_STACK[opcode];
    225     }
    226
    227
    228     /***
    229      * @return this instructions opcode
    230      */
    231     public short getOpcode() {
    232         return opcode;
    233     }
    234
    235
    236     /***
    237      * @return length (in bytes) of instruction
    238      */
    239     public int getLength() {
    240         return length;
    241     }
    242
    243
    244     /***
    245      * Needed in readInstruction.
    246      */
    247     private void setOpcode( short opcode ) {
    248         this.opcode = opcode;
    249     }
    250
    251
    252     /*** Some instructions may be reused, so don't do anything by default.
    253      */
    254     void dispose() {
    255     }
    256
    257
    258     /***
    259      * Call corresponding visitor method(s). The order is:
    260      * Call visitor methods of implemented interfaces first, then
    261      * call methods according to the class hierarchy in descending order,
    262      * i.e., the most specific visitXXX() call comes last.
    263      *
    264      * @param v Visitor object
    265      */
    266     public abstract void accept( Visitor v );
    267
    268
    269     /*** Get Comparator object used in the equals() method to determine
    270      * equality of instructions.
    271      *
    272      * @return currently used comparator for equals()
    273      */
    274     public static InstructionComparator getComparator() {
    275         return cmp;
    276     }
    277
    278
    279     /*** Set comparator to be used for equals().
    280      */
    281     public static void setComparator( InstructionComparator c ) {
    282         cmp = c;
    283     }
    284
    285
    286     /*** Check for equality, delegated to comparator
    287      * @return true if that is an Instruction and has the same opcode
    288      */
    289     public boolean equals( Object that ) {
    290         return (that instanceof Instruction) ? cmp.equals(this, (Instruction) that) : false;
    291     }
    292 }