You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
503 lines
16 KiB
503 lines
16 KiB
/* Instruction Copyright (C) 1999 Jochen Hoenicke.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
package net.sf.jode.bytecode;
|
|
|
|
/**
|
|
* <p> This class represents an instruction in the byte code.
|
|
* Instructions can be created with the static {@link #forOpcode}
|
|
* methods. </p>
|
|
*
|
|
* <p> We only allow a subset of opcodes. Other opcodes are mapped to
|
|
* their simpler version. Don't worry about this, when writing the
|
|
* bytecode the shortest possible bytecode is produced. </p>
|
|
*
|
|
* The opcodes we map are:
|
|
* <pre>
|
|
* [iflda]load_x -> [iflda]load
|
|
* [iflda]store_x -> [iflda]store
|
|
* [ifa]const_xx, ldc_w -> ldc
|
|
* [dl]const_xx -> ldc2_w
|
|
* wide opcode -> opcode
|
|
* tableswitch -> lookupswitch
|
|
* [a]newarray -> multianewarray
|
|
* </pre>
|
|
*/
|
|
public class Instruction implements Opcodes{
|
|
/**
|
|
* The opcode and lineNr of the instruction.
|
|
* opcode is <code>(lineAndOpcode & 0xff)</code>, while
|
|
* lineNr is <code>(lineAndOpcode >> 8)</code>.
|
|
* If line number is not known or unset, it is -1.
|
|
*/
|
|
private int lineAndOpcode;
|
|
|
|
/**
|
|
* Creates a new simple Instruction with no parameters. We map
|
|
* some opcodes, so you must always use the mapped opcode.
|
|
* @param opcode the opcode of this instruction.
|
|
* @exception IllegalArgumentException if opcode is not in our subset
|
|
* or if opcode needs a parameter. */
|
|
public static Instruction forOpcode(int opcode) {
|
|
switch (opcode) {
|
|
case opc_nop:
|
|
case opc_iaload: case opc_laload: case opc_faload:
|
|
case opc_daload: case opc_aaload:
|
|
case opc_baload: case opc_caload: case opc_saload:
|
|
case opc_iastore: case opc_lastore: case opc_fastore:
|
|
case opc_dastore: case opc_aastore:
|
|
case opc_bastore: case opc_castore: case opc_sastore:
|
|
case opc_pop: case opc_pop2:
|
|
case opc_dup: case opc_dup_x1: case opc_dup_x2:
|
|
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
|
|
case opc_swap:
|
|
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
|
|
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
|
|
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
|
|
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
|
|
case opc_irem: case opc_lrem: case opc_frem: case opc_drem:
|
|
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg:
|
|
case opc_ishl: case opc_lshl:
|
|
case opc_ishr: case opc_lshr:
|
|
case opc_iushr: case opc_lushr:
|
|
case opc_iand: case opc_land:
|
|
case opc_ior: case opc_lor:
|
|
case opc_ixor: case opc_lxor:
|
|
case opc_i2l: case opc_i2f: case opc_i2d:
|
|
case opc_l2i: case opc_l2f: case opc_l2d:
|
|
case opc_f2i: case opc_f2l: case opc_f2d:
|
|
case opc_d2i: case opc_d2l: case opc_d2f:
|
|
case opc_i2b: case opc_i2c: case opc_i2s:
|
|
case opc_lcmp: case opc_fcmpl: case opc_fcmpg:
|
|
case opc_dcmpl: case opc_dcmpg:
|
|
case opc_ireturn: case opc_lreturn:
|
|
case opc_freturn: case opc_dreturn: case opc_areturn:
|
|
case opc_return:
|
|
case opc_athrow:
|
|
case opc_arraylength:
|
|
case opc_monitorenter: case opc_monitorexit:
|
|
case opc_goto:
|
|
case opc_jsr:
|
|
case opc_ifeq: case opc_ifne:
|
|
case opc_iflt: case opc_ifge:
|
|
case opc_ifgt: case opc_ifle:
|
|
case opc_if_icmpeq: case opc_if_icmpne:
|
|
case opc_if_icmplt: case opc_if_icmpge:
|
|
case opc_if_icmpgt: case opc_if_icmple:
|
|
case opc_if_acmpeq: case opc_if_acmpne:
|
|
case opc_ifnull: case opc_ifnonnull:
|
|
return new Instruction(opcode);
|
|
default:
|
|
throw new IllegalArgumentException("Instruction has a parameter");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a new ldc Instruction.
|
|
* @param opcode the opcode of this instruction.
|
|
* @param constant the constant parameter.
|
|
* @exception IllegalArgumentException if opcode is not opc_ldc or
|
|
* opc_ldc2_w.
|
|
*/
|
|
public static Instruction forOpcode(int opcode, Object constant) {
|
|
if (opcode == opc_ldc || opcode == opc_ldc2_w)
|
|
return new ConstantInstruction(opcode, constant);
|
|
throw new IllegalArgumentException("Instruction has no constant");
|
|
}
|
|
|
|
/**
|
|
* Creates a new Instruction with a local variable as parameter.
|
|
* @param opcode the opcode of this instruction.
|
|
* @param lvi the local variable parameter.
|
|
* @exception IllegalArgumentException if opcode is not in our subset
|
|
* or if opcode doesn't need a single local variable as parameter.
|
|
*/
|
|
public static Instruction forOpcode(int opcode, LocalVariableInfo lvi) {
|
|
if (opcode == opc_ret
|
|
|| opcode >= opc_iload && opcode <= opc_aload
|
|
|| opcode >= opc_istore && opcode <= opc_astore)
|
|
return new SlotInstruction(opcode, lvi);
|
|
throw new IllegalArgumentException("Instruction has no slot");
|
|
}
|
|
|
|
/**
|
|
* Creates a new Instruction with reference as parameter.
|
|
* @param opcode the opcode of this instruction.
|
|
* @param reference the reference parameter.
|
|
* @exception IllegalArgumentException if opcode is not in our subset
|
|
* or if opcode doesn't need a reference as parameter.
|
|
*/
|
|
public static Instruction forOpcode(int opcode, Reference reference) {
|
|
if (opcode >= opc_getstatic && opcode <= opc_invokeinterface)
|
|
return new ReferenceInstruction(opcode, reference);
|
|
throw new IllegalArgumentException("Instruction has no reference");
|
|
}
|
|
|
|
/**
|
|
* Creates a new Instruction with type signature as parameter.
|
|
* @param opcode the opcode of this instruction.
|
|
* @param typeSig the type signature parameter.
|
|
* @exception IllegalArgumentException if opcode is not in our subset
|
|
* or if opcode doesn't need a type signature as parameter.
|
|
*/
|
|
public static Instruction forOpcode(int opcode, String typeSig) {
|
|
switch (opcode) {
|
|
case opc_new:
|
|
case opc_checkcast:
|
|
case opc_instanceof:
|
|
return new TypeInstruction(opcode, typeSig);
|
|
default:
|
|
throw new IllegalArgumentException("Instruction has no type");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new switch Instruction.
|
|
* @param opcode the opcode of this instruction must be opc_lookupswitch.
|
|
* @param values an array containing the different cases.
|
|
* @exception IllegalArgumentException if opcode is not opc_lookupswitch.
|
|
*/
|
|
public static Instruction forOpcode(int opcode, int[] values) {
|
|
if (opcode == opc_lookupswitch)
|
|
return new SwitchInstruction(opcode, values);
|
|
throw new IllegalArgumentException("Instruction has no values");
|
|
}
|
|
|
|
/**
|
|
* Creates a new increment Instruction.
|
|
* @param opcode the opcode of this instruction.
|
|
* @param lvi the local variable parameter.
|
|
* @param increment the increment parameter.
|
|
* @exception IllegalArgumentException if opcode is not opc_iinc.
|
|
*/
|
|
public static Instruction forOpcode(int opcode,
|
|
LocalVariableInfo lvi, int increment) {
|
|
if (opcode == opc_iinc)
|
|
return new IncInstruction(opcode, lvi, increment);
|
|
throw new IllegalArgumentException("Instruction has no increment");
|
|
}
|
|
|
|
/**
|
|
* Creates a new Instruction with type signature and a dimension
|
|
* as parameter.
|
|
* @param opcode the opcode of this instruction.
|
|
* @param typeSig the type signature parameter.
|
|
* @param dimension the array dimension parameter.
|
|
* @exception IllegalArgumentException if opcode is not
|
|
* opc_multianewarray.
|
|
*/
|
|
public static Instruction forOpcode(int opcode,
|
|
String typeSig, int dimension) {
|
|
if (opcode == opc_multianewarray)
|
|
return new TypeDimensionInstruction(opcode, typeSig, dimension);
|
|
throw new IllegalArgumentException("Instruction has no dimension");
|
|
}
|
|
|
|
/**
|
|
* Creates a simple opcode, without any parameters.
|
|
*/
|
|
Instruction(int opcode) {
|
|
this.lineAndOpcode = (-1 << 8) | opcode;
|
|
}
|
|
|
|
/**
|
|
* Gets the opcode of the instruction.
|
|
* @return the opcode of the instruction.
|
|
*/
|
|
public final int getOpcode() {
|
|
return lineAndOpcode & 0xff;
|
|
}
|
|
|
|
/**
|
|
* Tells whether there is a line number information for this
|
|
* instruction.
|
|
* @return true if there is a line number information for this
|
|
* instruction.
|
|
*/
|
|
public final boolean hasLineNr() {
|
|
return lineAndOpcode >= 0;
|
|
}
|
|
|
|
/**
|
|
* Gets the line number of this instruction.
|
|
* @return the line number, or -1 if there isn't one.
|
|
*/
|
|
public final int getLineNr() {
|
|
return lineAndOpcode >> 8;
|
|
}
|
|
|
|
/**
|
|
* Sets the line number of this instruction.
|
|
* @param nr the line number; use -1 to clear it.
|
|
*/
|
|
public final void setLineNr(int nr) {
|
|
lineAndOpcode = (nr << 8) | (lineAndOpcode & 0xff);
|
|
}
|
|
|
|
/**
|
|
* Checks whether this instruction is a local store instruction, i.e.
|
|
* one of <code>astore</code>, <code>istore</code>, <code>lstore</code>,
|
|
* <code>fstore</code> or <code>dstore</code>.
|
|
*/
|
|
public boolean isStore() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks whether this instruction accesses a local slot.
|
|
*/
|
|
public boolean hasLocal() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets the slot number of the local this instruction accesses.
|
|
* @return the slot number.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* access a local slot.
|
|
*/
|
|
public int getLocalSlot()
|
|
{
|
|
throw new IllegalArgumentException();
|
|
// UnsupportedOperationException would be more appropriate
|
|
}
|
|
|
|
/**
|
|
* Gets the information of the local this instruction accesses.
|
|
* @return the local variable info.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* access a local.
|
|
*/
|
|
public LocalVariableInfo getLocalInfo()
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Sets the information of the local this instruction accesses.
|
|
* @param info the local variable info.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* access a local.
|
|
*/
|
|
public void setLocalInfo(LocalVariableInfo info)
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Sets the slot of the local this instruction accesses.
|
|
* @param slot the local slot
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* access a local.
|
|
*/
|
|
public void setLocalSlot(int slot)
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Gets the increment for an opc_iinc instruction.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public int getIncrement()
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Sets the increment for an opc_iinc instruction.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public void setIncrement(int incr)
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Gets the dimensions for an opc_multianewarray opcode.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public int getDimensions()
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Sets the dimensions for an opc_multianewarray opcode.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public void setDimensions(int dims)
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Gets the constant for a opc_ldc or opc_ldc2_w opcode.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public Object getConstant()
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Sets the constant for a opc_ldc or opc_ldc2_w opcode.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public void setConstant(Object constant)
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Gets the reference of the field or method this instruction accesses.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public Reference getReference()
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Sets the reference of the field or method this instruction accesses.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public void setReference(Reference ref)
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Gets the class type this instruction uses, e.g if its a class cast.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public String getClazzType()
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Sets the class type this instruction uses, e.g if its a class cast.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public void setClazzType(String type)
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Gets the values of a opc_lookupswitch opcode.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public int[] getValues()
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Sets the values of a opc_lookupswitch opcode.
|
|
* @throws IllegalArgumentException if this instruction doesn't
|
|
* support this.
|
|
*/
|
|
public void setValues(int[] values)
|
|
{
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
/**
|
|
* Checks whether this instruction always changes program flow.
|
|
* Returns false for opc_jsr it.
|
|
* @return true if this instruction always changes flow, i.e. if
|
|
* its an unconditional jump, a return, a throw, a ret or a switch.
|
|
*/
|
|
public final boolean doesAlwaysJump() {
|
|
switch (getOpcode()) {
|
|
case opc_ret:
|
|
case opc_goto:
|
|
case opc_lookupswitch:
|
|
case opc_ireturn:
|
|
case opc_lreturn:
|
|
case opc_freturn:
|
|
case opc_dreturn:
|
|
case opc_areturn:
|
|
case opc_return:
|
|
case opc_athrow:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This returns the number of stack entries this instruction
|
|
* pushes and pops from the stack. The result fills the given
|
|
* array.
|
|
*
|
|
* @param poppush an array of two ints. The first element will
|
|
* get the number of pops, the second the number of pushes.
|
|
*/
|
|
public void getStackPopPush(int[] poppush)
|
|
/*{ require { poppush != null && poppush.length == 2
|
|
:: "poppush must be an array of two ints" } } */
|
|
{
|
|
byte delta = (byte) stackDelta.charAt(getOpcode());
|
|
poppush[0] = delta & 7;
|
|
poppush[1] = delta >> 3;
|
|
}
|
|
|
|
/**
|
|
* Gets a printable representation of the opcode with its
|
|
* parameters. This will not include the destination for jump
|
|
* instructions, since this information is not stored inside the
|
|
* instruction.
|
|
*/
|
|
public final String getDescription() {
|
|
return toString();
|
|
}
|
|
|
|
/**
|
|
* Gets a printable representation of the opcode with its
|
|
* parameters. This will not include the destination for jump
|
|
* instructions, since this information is not stored inside the
|
|
* instruction.
|
|
*/
|
|
public String toString() {
|
|
return opcodeString[getOpcode()];
|
|
}
|
|
|
|
/**
|
|
* stackDelta contains \100 if stack count of opcode is variable
|
|
* \177 if opcode is illegal, or 8*stack_push + stack_pop otherwise
|
|
* The string is created by scripts/createStackDelta.pl
|
|
*/
|
|
final static String stackDelta =
|
|
"\000\010\010\010\010\010\010\010\010\020\020\010\010\010\020\020\010\010\010\010\020\010\020\010\020\010\010\010\010\010\020\020\020\020\010\010\010\010\020\020\020\020\010\010\010\010\012\022\012\022\012\012\012\012\001\002\001\002\001\001\001\001\001\002\002\002\002\001\001\001\001\002\002\002\002\001\001\001\001\003\004\003\004\003\003\003\003\001\002\021\032\043\042\053\064\022\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\011\022\011\022\012\023\012\023\012\023\012\024\012\024\012\024\000\021\011\021\012\012\022\011\021\021\012\022\012\011\011\011\014\012\012\014\014\001\001\001\001\001\001\002\002\002\002\002\002\002\002\000\000\000\001\001\001\002\001\002\001\000\100\100\100\100\100\100\100\100\177\010\011\011\011\001\011\011\001\001\177\100\001\001\000\000";
|
|
}
|
|
|