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 }