/* * 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; } }