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 }