blob: 113be06ee501f762a60375daa4f8626d176c1d71 [file] [log] [blame]
package org.aspectj.apache.bcel.generic;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache BCEL" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache BCEL", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.util.ByteSequence;
/**
* Abstract super class for all Java byte codes.
*
* @version $Id: Instruction.java,v 1.10 2011/04/05 15:15:33 aclement Exp $
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
*/
public class Instruction implements Cloneable, Serializable, Constants {
public short opcode = -1;
public Instruction(short opcode) {
this.opcode = opcode;
}
public void dump(DataOutputStream out) throws IOException {
out.writeByte(opcode);
}
public String getName() {
return Constants.OPCODE_NAMES[opcode];
}
/**
* 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.
*
* @return (shallow) copy of an instruction
*/
// GET RID OF THIS - make it throw an exception and track the callers
final public Instruction copy() {
// if overridden correctly can just return 'this' here
if (InstructionConstants.INSTRUCTIONS[opcode] != null) { // immutable instructions do not need copying
return this;
} else {
Instruction i = null;
try {// OPTIMIZE is clone the right thing to do here? it is horrible
i = (Instruction) clone();
} catch (CloneNotSupportedException e) {
System.err.println(e);
}
return i;
}
}
/**
* Read an instruction bytecode from an input stream and return the appropriate object.
*
* @param file file to read from
* @return instruction object being read
*/
public static final Instruction readInstruction(ByteSequence bytes) throws IOException {
boolean wide = false;
short opcode = (short) bytes.readUnsignedByte();
if (opcode == Constants.WIDE) {
wide = true;
opcode = (short) bytes.readUnsignedByte();
}
Instruction constantInstruction = InstructionConstants.INSTRUCTIONS[opcode];
if (constantInstruction != null) {
return constantInstruction;
}
Instruction obj = null;
try {
switch (opcode) {
case Constants.BIPUSH:
obj = new InstructionByte(Constants.BIPUSH, bytes.readByte());
break;
case Constants.SIPUSH:
obj = new InstructionShort(Constants.SIPUSH, bytes.readShort());
break;
case Constants.LDC:
obj = new InstructionCP(Constants.LDC, bytes.readUnsignedByte());
break;
case Constants.LDC_W:
case Constants.LDC2_W:
obj = new InstructionCP(opcode, bytes.readUnsignedShort());
break;
case Constants.ILOAD:
case Constants.LLOAD:
case Constants.FLOAD:
case Constants.DLOAD:
case Constants.ALOAD:
case Constants.ISTORE:
case Constants.LSTORE:
case Constants.FSTORE:
case Constants.DSTORE:
case Constants.ASTORE:
obj = new InstructionLV(opcode, wide ? bytes.readUnsignedShort() : bytes.readUnsignedByte());
break;
case Constants.IINC:
obj = new IINC(wide ? bytes.readUnsignedShort() : bytes.readUnsignedByte(), wide ? bytes.readShort()
: bytes.readByte(), wide);
break;
case Constants.IFNULL:
case Constants.IFNONNULL:
case Constants.IFEQ:
case Constants.IFNE:
case Constants.IFLT:
case Constants.IFGE:
case Constants.IFGT:
case Constants.IFLE:
case Constants.IF_ICMPEQ:
case Constants.IF_ICMPNE:
case Constants.IF_ICMPLT:
case Constants.IF_ICMPGE:
case Constants.IF_ICMPGT:
case Constants.IF_ICMPLE:
case Constants.IF_ACMPEQ:
case Constants.IF_ACMPNE:
case Constants.GOTO:
case Constants.JSR:
obj = new InstructionBranch(opcode, bytes.readShort());
break;
case Constants.GOTO_W:
case Constants.JSR_W:
obj = new InstructionBranch(opcode, bytes.readInt());
break;
case Constants.TABLESWITCH:
obj = new TABLESWITCH(bytes);
break;
case Constants.LOOKUPSWITCH:
obj = new LOOKUPSWITCH(bytes);
break;
case Constants.RET:
obj = new RET(wide ? bytes.readUnsignedShort() : bytes.readUnsignedByte(), wide);
break;
case Constants.NEW:
obj = new InstructionCP(Constants.NEW, bytes.readUnsignedShort());
break;
case Constants.GETSTATIC:
case Constants.PUTSTATIC:
case Constants.GETFIELD:
case Constants.PUTFIELD:
obj = new FieldInstruction(opcode, bytes.readUnsignedShort());
break;
case Constants.INVOKEVIRTUAL:
case Constants.INVOKESPECIAL:
case Constants.INVOKESTATIC:
obj = new InvokeInstruction(opcode, bytes.readUnsignedShort());
break;
case Constants.INVOKEINTERFACE:
obj = new INVOKEINTERFACE(bytes.readUnsignedShort(), bytes.readUnsignedByte(), bytes.readByte());
break;
case Constants.INVOKEDYNAMIC:
obj = new InvokeDynamic(bytes.readUnsignedShort(),bytes.readUnsignedShort());
break;
case Constants.NEWARRAY:
obj = new InstructionByte(Constants.NEWARRAY, bytes.readByte());
break;
case Constants.ANEWARRAY:
case Constants.CHECKCAST:
obj = new InstructionCP(opcode, bytes.readUnsignedShort());
break;
case Constants.INSTANCEOF:
obj = new InstructionCP(Constants.INSTANCEOF, bytes.readUnsignedShort());
break;
case Constants.MULTIANEWARRAY:
obj = new MULTIANEWARRAY(bytes.readUnsignedShort(), bytes.readByte());
break;
default:
throw new ClassGenException("Illegal opcode detected");
}
} catch (ClassGenException e) {
throw e;
} catch (Exception e) {
throw new ClassGenException(e.toString());
}
return obj;
}
/**
* @return Number of words consumed from stack by this instruction, or Constants.UNPREDICTABLE, if this can not be computed
* statically
*/
public int consumeStack(ConstantPool cpg) {
return Constants.CONSUME_STACK[opcode];
}
/**
* @return Number of words produced onto stack by this instruction, or Constants.UNPREDICTABLE, if this can not be computed
* statically
*/
public int produceStack(ConstantPool cpg) {
return Constants.stackEntriesProduced[opcode];
}
public short getOpcode() {
return opcode;
}
public int getLength() {
// if it is zero, it should have been provided by an overriding implementation of getLength()
int len = Constants.iLen[opcode];
assert len != 0;
// if (len == 0) {
// throw new IllegalStateException("Length not right for " + getName().toUpperCase());
// }
return len;
}
/** Some instructions may be reused, so don't do anything by default */
void dispose() {
}
@Override
public boolean equals(Object other) {
if (this.getClass() != Instruction.class) {
throw new RuntimeException("NO WAY " + this.getClass());
}
if (!(other instanceof Instruction)) {
return false;
}
return ((Instruction) other).opcode == opcode;
// IMPLEMENT EQUALS AND HASHCODE IN THE SUBTYPES!
// Instruction i1 = this;
// Instruction i2 = (Instruction) that;
// if (i1.opcode == i2.opcode) {
// if (i1.isConstantInstruction()) {
// return i1.getValue().equals(i2.getValue());
// } else if (i1.isIndexedInstruction()) {
// return i1.getIndex() == i2.getIndex();
// } else if (i1.opcode == Constants.NEWARRAY) {
// return ((InstructionByte) i1).getTypecode() == ((InstructionByte) i2).getTypecode();
// } else {
// return true;
// }
// }
//
// return false;
}
@Override
public int hashCode() {
if (this.getClass() != Instruction.class) {
throw new RuntimeException("NO WAY " + this.getClass());
}
return opcode * 37;
// int result = 17 + opcode * 37;
// if (isConstantInstruction()) {
// result = 37 * getValue().hashCode() + result;
// } else if (isIndexedInstruction()) {
// result = 37 * getIndex() + result;
// } else if (opcode == Constants.NEWARRAY) {
// result = 37 * ((InstructionByte) this).getTypecode() + result;
// }
// return result;
}
public Type getType() {
return getType(null);
}
public Type getType(ConstantPool cp) {
// if (types[opcode]==null) throw new RuntimeException(getName()+" is not a typed instruction");
Type t = Constants.types[opcode];
if (t != null) {
return t;
}
throw new RuntimeException("Do not know type for instruction " + getName() + "(" + opcode + ")");
}
public Number getValue() {
assert (instFlags[opcode] & CONSTANT_INST) == 0;
// if ((instFlags[opcode] & CONSTANT_INST) == 0) {
// throw new RuntimeException(getName() + " is not a constant instruction");
// }
switch (opcode) {
case ICONST_M1:
case ICONST_0:
case ICONST_1:
case ICONST_2:
case ICONST_3:
case ICONST_4:
case ICONST_5:
return new Integer(opcode - ICONST_0);
default:
throw new IllegalStateException("Not implemented yet for " + getName());
}
}
public int getIndex() {
return -1;
}
public void setIndex(int i) {
throw new IllegalStateException("Shouldnt be asking " + getName().toUpperCase());
}
public Object getValue(ConstantPool cpg) {
throw new IllegalStateException("Shouldnt be asking " + getName().toUpperCase());
}
public boolean isLoadInstruction() {
return (Constants.instFlags[opcode] & LOAD_INST) != 0;
}
// remove these from here, leave them in the InstructionLV
public boolean isASTORE() {
return false;
}
public boolean isALOAD() {
return false;
}
public boolean isStoreInstruction() {
return (Constants.instFlags[opcode] & STORE_INST) != 0;
}
// public boolean containsTarget(InstructionHandle ih) {
// throw new IllegalStateException("Dont ask!!");
// }
public boolean isJsrInstruction() {
return (Constants.instFlags[opcode] & JSR_INSTRUCTION) != 0;
}
public boolean isConstantInstruction() {
return (Constants.instFlags[opcode] & CONSTANT_INST) != 0;
}
public boolean isConstantPoolInstruction() {
return (Constants.instFlags[opcode] & CP_INST) != 0;
}
public boolean isStackProducer() {
return Constants.stackEntriesProduced[opcode] != 0;
}
public boolean isStackConsumer() {
return Constants.CONSUME_STACK[opcode] != 0;
}
public boolean isIndexedInstruction() {
return (Constants.instFlags[opcode] & INDEXED) != 0;
}
public boolean isArrayCreationInstruction() {
return opcode == NEWARRAY || opcode == ANEWARRAY || opcode == MULTIANEWARRAY;
}
public ObjectType getLoadClassType(ConstantPool cpg) {
assert (Constants.instFlags[opcode] & Constants.LOADCLASS_INST) == 0;
// if ((Constants.instFlags[opcode] & Constants.LOADCLASS_INST) == 0) {
// throw new IllegalStateException("This opcode " + opcode + " does not have the property "
// + Long.toHexString(Constants.LOADCLASS_INST));
// }
Type t = getType(cpg);
if (t instanceof ArrayType) {
t = ((ArrayType) t).getBasicType();
}
return t instanceof ObjectType ? (ObjectType) t : null;
}
public boolean isReturnInstruction() {
return (Constants.instFlags[opcode] & RET_INST) != 0;
}
// public boolean isGoto() {
// return opcode == GOTO || opcode == GOTO_W;
// }
public boolean isLocalVariableInstruction() {
return (Constants.instFlags[opcode] & LV_INST) != 0;
}
/**
* Long output format: 'name of opcode' "[" 'opcode number' "]" "(" 'length of instruction' ")"
*/
public String toString(boolean verbose) {
if (verbose) {
StringBuffer sb = new StringBuffer();
sb.append(getName()).append("[").append(opcode).append("](size").append(Constants.iLen[opcode]).append(")");
return sb.toString();
} else {
return getName();
}
}
@Override
public String toString() {
return toString(true);
}
}