View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   package com.sun.org.apache.bcel.internal.generic;
6   
7   /* ====================================================================
8    * The Apache Software License, Version 1.1
9    *
10   * Copyright (c) 2001 The Apache Software Foundation.  All rights
11   * reserved.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions
15   * are met:
16   *
17   * 1. Redistributions of source code must retain the above copyright
18   *    notice, this list of conditions and the following disclaimer.
19   *
20   * 2. Redistributions in binary form must reproduce the above copyright
21   *    notice, this list of conditions and the following disclaimer in
22   *    the documentation and/or other materials provided with the
23   *    distribution.
24   *
25   * 3. The end-user documentation included with the redistribution,
26   *    if any, must include the following acknowledgment:
27   *       "This product includes software developed by the
28   *        Apache Software Foundation (http://www.apache.org/)."
29   *    Alternately, this acknowledgment may appear in the software itself,
30   *    if and wherever such third-party acknowledgments normally appear.
31   *
32   * 4. The names "Apache" and "Apache Software Foundation" and
33   *    "Apache BCEL" must not be used to endorse or promote products
34   *    derived from this software without prior written permission. For
35   *    written permission, please contact apache@apache.org.
36   *
37   * 5. Products derived from this software may not be called "Apache",
38   *    "Apache BCEL", nor may "Apache" appear in their name, without
39   *    prior written permission of the Apache Software Foundation.
40   *
41   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52   * SUCH DAMAGE.
53   * ====================================================================
54   *
55   * This software consists of voluntary contributions made by many
56   * individuals on behalf of the Apache Software Foundation.  For more
57   * information on the Apache Software Foundation, please see
58   * <http://www.apache.org/>.
59   */
60  
61  import com.sun.org.apache.bcel.internal.Constants;
62  import com.sun.org.apache.bcel.internal.classfile.Utility;
63  import com.sun.org.apache.bcel.internal.classfile.ConstantPool;
64  import java.io.*;
65  import com.sun.org.apache.bcel.internal.util.ByteSequence;
66  
67  /**
68   * Abstract super class for all Java byte codes.
69   *
70   * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
71   */
72  public abstract class Instruction implements Cloneable, Serializable {
73    protected short length = 1;  // Length of instruction in bytes
74    protected short opcode = -1; // Opcode number
75  
76    private static InstructionComparator cmp = InstructionComparator.DEFAULT;
77  
78    /**
79     * Empty constructor needed for the Class.newInstance() statement in
80     * Instruction.readInstruction(). Not to be used otherwise.
81     */
82    Instruction() {}
83  
84    public Instruction(short opcode, short length) {
85      this.length = length;
86      this.opcode = opcode;
87    }
88  
89    /**
90     * Dump instruction as byte code to stream out.
91     * @param out Output stream
92     */
93    public void dump(DataOutputStream out) throws IOException {
94      out.writeByte(opcode); // Common for all instructions
95    }
96  
97    /** @return name of instruction, i.e., opcode name
98     */
99    public String getName() {
100     return Constants.OPCODE_NAMES[opcode];
101   }
102 
103   /**
104    * Long output format:
105    *
106    * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]"
107    * "("&lt;length of instruction&gt;")"
108    *
109    * @param verbose long/short format switch
110    * @return mnemonic for instruction
111    */
112   public String toString(boolean verbose) {
113     if(verbose)
114       return getName() + "[" + opcode + "](" + length + ")";
115     else
116       return getName();
117   }
118 
119   /**
120    * @return mnemonic for instruction in verbose format
121    */
122   public String toString() {
123     return toString(true);
124   }
125 
126   /**
127    * @return mnemonic for instruction with sumbolic references resolved
128    */
129   public String toString(ConstantPool cp) {
130     return toString(false);
131   }
132 
133   /**
134    * Use with caution, since `BranchInstruction's have a `target' reference which
135    * is not copied correctly (only basic types are). This also applies for
136    * `Select' instructions with their multiple branch targets.
137    *
138    * @see BranchInstruction
139    * @return (shallow) copy of an instruction
140    */
141   public Instruction copy() {
142     Instruction i = null;
143 
144     // "Constant" instruction, no need to duplicate
145     if(InstructionConstants.INSTRUCTIONS[this.getOpcode()] != null)
146       i = this;
147     else {
148       try {
149         i = (Instruction)clone();
150       } catch(CloneNotSupportedException e) {
151         System.err.println(e);
152       }
153     }
154 
155     return i;
156   }
157 
158   /**
159    * Read needed data (e.g. index) from file.
160    *
161    * @param bytes byte sequence to read from
162    * @param wide "wide" instruction flag
163    */
164   protected void initFromFile(ByteSequence bytes, boolean wide)
165     throws IOException
166   {}
167 
168   /**
169    * Read an instruction from (byte code) input stream and return the
170    * appropiate object.
171    *
172    * @param file file to read from
173    * @return instruction object being read
174    */
175   public static final Instruction readInstruction(ByteSequence bytes)
176     throws IOException
177   {
178     boolean     wide   = false;
179     short       opcode = (short)bytes.readUnsignedByte();
180     Instruction obj    = null;
181 
182     if(opcode == Constants.WIDE) { // Read next opcode after wide byte
183       wide = true;
184       opcode  = (short)bytes.readUnsignedByte();
185     }
186 
187     if(InstructionConstants.INSTRUCTIONS[opcode] != null)
188       return InstructionConstants.INSTRUCTIONS[opcode]; // Used predefined immutable object, if available
189 
190     /* Find appropiate class, instantiate an (empty) instruction object
191      * and initialize it by hand.
192      */
193     Class clazz;
194 
195     try {
196       clazz = Class.forName(className(opcode));
197     } catch (ClassNotFoundException cnfe){
198       // If a class by that name does not exist, the opcode is illegal.
199       // Note that IMPDEP1, IMPDEP2, BREAKPOINT are also illegal in a sense.
200       throw new ClassGenException("Illegal opcode detected.");
201     }
202 
203     try {
204       obj = (Instruction)clazz.newInstance();
205 
206       if(wide && !((obj instanceof LocalVariableInstruction) ||
207                    (obj instanceof IINC) ||
208                    (obj instanceof RET)))
209         throw new Exception("Illegal opcode after wide: " + opcode);
210 
211       obj.setOpcode(opcode);
212       obj.initFromFile(bytes, wide); // Do further initializations, if any
213       // Byte code offset set in InstructionList
214     } catch(Exception e) { throw new ClassGenException(e.toString()); }
215 
216     return obj;
217   }
218 
219   private static final String className(short opcode) {
220     String name = Constants.OPCODE_NAMES[opcode].toUpperCase();
221 
222     /* ICONST_0, etc. will be shortened to ICONST, etc., since ICONST_0 and the like
223      * are not implemented (directly).
224      */
225     try {
226       int  len = name.length();
227       char ch1 = name.charAt(len - 2), ch2 = name.charAt(len - 1);
228 
229       if((ch1 == '_') && (ch2 >= '0')  && (ch2 <= '5'))
230         name = name.substring(0, len - 2);
231 
232       if(name.equals("ICONST_M1")) // Special case
233         name = "ICONST";
234     } catch(StringIndexOutOfBoundsException e) { System.err.println(e); }
235 
236     return "com.sun.org.apache.bcel.internal.generic." + name;
237   }
238 
239   /**
240    * This method also gives right results for instructions whose
241    * effect on the stack depends on the constant pool entry they
242    * reference.
243    *  @return Number of words consumed from stack by this instruction,
244    * or Constants.UNPREDICTABLE, if this can not be computed statically
245    */
246   public int consumeStack(ConstantPoolGen cpg) {
247     return Constants.CONSUME_STACK[opcode];
248   }
249 
250   /**
251    * This method also gives right results for instructions whose
252    * effect on the stack depends on the constant pool entry they
253    * reference.
254    * @return Number of words produced onto stack by this instruction,
255    * or Constants.UNPREDICTABLE, if this can not be computed statically
256    */
257   public int produceStack(ConstantPoolGen cpg) {
258     return Constants.PRODUCE_STACK[opcode];
259   }
260 
261   /**
262    * @return this instructions opcode
263    */
264   public short getOpcode()    { return opcode; }
265 
266   /**
267    * @return length (in bytes) of instruction
268    */
269   public int getLength()   { return length; }
270 
271   /**
272    * Needed in readInstruction.
273    */
274   private void setOpcode(short opcode) { this.opcode = opcode; }
275 
276   /** Some instructions may be reused, so don't do anything by default.
277    */
278   void dispose() {}
279 
280   /**
281    * Call corresponding visitor method(s). The order is:
282    * Call visitor methods of implemented interfaces first, then
283    * call methods according to the class hierarchy in descending order,
284    * i.e., the most specific visitXXX() call comes last.
285    *
286    * @param v Visitor object
287    */
288   public abstract void accept(Visitor v);
289 
290   /** Get Comparator object used in the equals() method to determine
291    * equality of instructions.
292    *
293    * @return currently used comparator for equals()
294    */
295   public static InstructionComparator getComparator() { return cmp; }
296 
297   /** Set comparator to be used for equals().
298    */
299   public static void setComparator(InstructionComparator c) { cmp = c; }
300 
301   /** Check for equality, delegated to comparator
302    * @return true if that is an Instruction and has the same opcode
303    */
304   public boolean equals(Object that) {
305     return (that instanceof Instruction)?
306       cmp.equals(this, (Instruction)that) : false;
307   }
308 }