/*
* Copyright 2000-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.bcel.generic;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Locale;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.util.ByteSequence;
/**
* Abstract super class for all Java byte codes.
*
* @version $Id: Instruction.java 386056 2006-03-15 11:31:56Z tcurdt $
* @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
*/
public abstract class Instruction implements Cloneable, Serializable {
protected short length = 1; // Length of instruction in bytes
protected short opcode = -1; // Opcode number
private static InstructionComparator cmp = InstructionComparator.DEFAULT;
/**
* Empty constructor needed for the Class.newInstance() statement in
* Instruction.readInstruction(). Not to be used otherwise.
*/
Instruction() {
}
public Instruction(short opcode, short length) {
this.length = length;
this.opcode = opcode;
}
/**
* Dump instruction as byte code to stream out.
* @param out Output stream
*/
public void dump( DataOutputStream out ) throws IOException {
out.writeByte(opcode); // Common for all instructions
}
/** @return name of instruction, i.e., opcode name
*/
public String getName() {
return Constants.OPCODE_NAMES[opcode];
}
/**
* Long output format:
*
* <name of opcode> "["<opcode number>"]"
* "("<length of instruction>")"
*
* @param verbose long/short format switch
* @return mnemonic for instruction
*/
public String toString( boolean verbose ) {
if (verbose) {
return getName() + "[" + opcode + "](" + length + ")";
} else {
return getName();
}
}
/**
* @return mnemonic for instruction in verbose format
*/
public String toString() {
return toString(true);
}
/**
* @return mnemonic for instruction with sumbolic references resolved
*/
public String toString( ConstantPool cp ) {
return toString(false);
}
/**
* Use with caution, since `BranchInstruction's have a `target' reference which
* is not copied correctly (only basic types are). This also applies for
* `Select' instructions with their multiple branch targets.
*
* @see BranchInstruction
* @return (shallow) copy of an instruction
*/
public Instruction copy() {
Instruction i = null;
// "Constant" instruction, no need to duplicate
if (InstructionConstants.INSTRUCTIONS[this.getOpcode()] != null) {
i = this;
} else {
try {
i = (Instruction) clone();
} catch (CloneNotSupportedException e) {
System.err.println(e);
}
}
return i;
}
/**
* Read needed data (e.g. index) from file.
*
* @param bytes byte sequence to read from
* @param wide "wide" instruction flag
*/
protected void initFromFile( ByteSequence bytes, boolean wide ) throws IOException {
}
/**
* Read an instruction from (byte code) input stream and return the
* appropiate object.
*
* @param bytes input stream bytes
* @return instruction object being read
*/
public static final Instruction readInstruction( ByteSequence bytes ) throws IOException {
boolean wide = false;
short opcode = (short) bytes.readUnsignedByte();
Instruction obj = null;
if (opcode == Constants.WIDE) { // Read next opcode after wide byte
wide = true;
opcode = (short) bytes.readUnsignedByte();
}
if (InstructionConstants.INSTRUCTIONS[opcode] != null) {
return InstructionConstants.INSTRUCTIONS[opcode]; // Used predefined immutable object, if available
}
/* Find appropiate class, instantiate an (empty) instruction object
* and initialize it by hand.
*/
Class clazz;
try {
clazz = Class.forName(className(opcode));
} catch (ClassNotFoundException cnfe) {
// If a class by that name does not exist, the opcode is illegal.
// Note that IMPDEP1, IMPDEP2, BREAKPOINT are also illegal in a sense.
throw new ClassGenException("Illegal opcode detected.");
}
try {
obj = (Instruction) clazz.newInstance();
if (wide
&& !((obj instanceof LocalVariableInstruction) || (obj instanceof IINC) || (obj instanceof RET))) {
throw new Exception("Illegal opcode after wide: " + opcode);
}
obj.setOpcode(opcode);
obj.initFromFile(bytes, wide); // Do further initializations, if any
// Byte code offset set in InstructionList
} catch (Exception e) {
throw new ClassGenException(e.toString());
}
return obj;
}
private static final String className( short opcode ) {
String name = Constants.OPCODE_NAMES[opcode].toUpperCase(Locale.ENGLISH);
/* ICONST_0, etc. will be shortened to ICONST, etc., since ICONST_0 and the like
* are not implemented (directly).
*/
try {
int len = name.length();
char ch1 = name.charAt(len - 2), ch2 = name.charAt(len - 1);
if ((ch1 == '_') && (ch2 >= '0') && (ch2 <= '5')) {
name = name.substring(0, len - 2);
}
if (name.equals("ICONST_M1")) {
name = "ICONST";
}
} catch (StringIndexOutOfBoundsException e) {
System.err.println(e);
}
return "org.apache.bcel.generic." + name;
}
/**
* This method also gives right results for instructions whose
* effect on the stack depends on the constant pool entry they
* reference.
* @return Number of words consumed from stack by this instruction,
* or Constants.UNPREDICTABLE, if this can not be computed statically
*/
public int consumeStack( ConstantPoolGen cpg ) {
return Constants.CONSUME_STACK[opcode];
}
/**
* This method also gives right results for instructions whose
* effect on the stack depends on the constant pool entry they
* reference.
* @return Number of words produced onto stack by this instruction,
* or Constants.UNPREDICTABLE, if this can not be computed statically
*/
public int produceStack( ConstantPoolGen cpg ) {
return Constants.PRODUCE_STACK[opcode];
}
/**
* @return this instructions opcode
*/
public short getOpcode() {
return opcode;
}
/**
* @return length (in bytes) of instruction
*/
public int getLength() {
return length;
}
/**
* Needed in readInstruction.
*/
private void setOpcode( short opcode ) {
this.opcode = opcode;
}
/** Some instructions may be reused, so don't do anything by default.
*/
void dispose() {
}
/**
* Call corresponding visitor method(s). The order is:
* Call visitor methods of implemented interfaces first, then
* call methods according to the class hierarchy in descending order,
* i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
public abstract void accept( Visitor v );
/** Get Comparator object used in the equals() method to determine
* equality of instructions.
*
* @return currently used comparator for equals()
*/
public static InstructionComparator getComparator() {
return cmp;
}
/** Set comparator to be used for equals().
*/
public static void setComparator( InstructionComparator c ) {
cmp = c;
}
/** Check for equality, delegated to comparator
* @return true if that is an Instruction and has the same opcode
*/
public boolean equals( Object that ) {
return (that instanceof Instruction) ? cmp.equals(this, (Instruction) that) : false;
}
}