1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
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; 
36      protected short opcode = -1; 
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); 
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       * <name of opcode> "["<opcode number>"]" 
74       * "("<length of instruction>")"
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         
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) { 
150             wide = true;
151             opcode = (short) bytes.readUnsignedByte();
152         }
153         if (InstructionConstants.INSTRUCTIONS[opcode] != null) {
154             return InstructionConstants.INSTRUCTIONS[opcode]; 
155         }
156         
157 
158 
159         Class clazz;
160         try {
161             clazz = Class.forName(className(opcode));
162         } catch (ClassNotFoundException cnfe) {
163             
164             
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); 
175             
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         
186 
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 }