/**
 * An Insn is a generic instruction that is added to a
 * CodeAttr to build up the code for a method.
 * @see CodeAttr
 * @see RuntimeConstants
 * @author $Author: jonmeyerny $
 * @version $Revision: 1.1 $
 */


package jas;

import java.io.*;
import java.util.*;


public class Insn implements RuntimeConstants
{
  int opc;
  InsnOperand operand;

                                // private constructor, for the
                                // "strange" opcodes
  Insn() { return; }
  /**
   * Instructions with no arguments are built with
   * this constructor.
   */
  public Insn(int opc)
    throws jasError
  {
    if (opcLengths[opc] == 1)
      { operand = null; this.opc = opc; return; }
    throw new jasError
      (opcNames[opc] + " cannot be used without more parameters");
  }

  private void check_short(int val, int opc) throws jasError
  {
    if (val > 32767 || val < -32768)
      throw new jasError
        (opcNames[opc] + " numeric value exceed size for short");
  }

  /**
   * Instructions that take a single numeric argument. These are
   * opc_bipush,
   * opc_sipush,
   * opc_ret,
   * opc_iload,
   * opc_lload,
   * opc_fload,
   * opc_dload,
   * opc_aload,
   * opc_istore,
   * opc_lstore,
   * opc_fstore,
   * opc_dstore,
   * opc_astore,
   * opc_newarray
   *
   * Note that an extra wide prefix is automatically added
   * for the following instructions if the numeric argument
   * is larger than 256. Also note that while the spec makes
   * no mention of opc_ret as being a "wideable" opcode, thats
   * how the VM is implemented.
   *
   * opc_ret:
   * opc_iload:
   * opc_lload:
   * opc_fload:
   * opc_dload:
   * opc_aload:
   * opc_istore:
   * opc_lstore:
   * opc_fstore:
   * opc_dstore:
   * opc_astore:
   *
   */

  /**
   * Added branch instructions
   */

  public Insn(int opc, int val, boolean Wide)
    throws jasError
  {
    this.opc = opc;
    switch (opc)
      {
      case opc_bipush:
          if(val > 127 || val < -128)
            throw new jasError("bipush value exceed size of byte", true);
          operand = new ByteOperand(val);
          break;

      case opc_sipush:
      case opc_goto:
      case opc_if_acmpeq:
      case opc_if_acmpne:
      case opc_if_icmpeq:
      case opc_if_icmpge:
      case opc_if_icmpgt:
      case opc_if_icmple:
      case opc_if_icmplt:
      case opc_if_icmpne:
      case opc_ifeq:
      case opc_ifge:
      case opc_ifgt:
      case opc_ifle:
      case opc_iflt:
      case opc_ifne:
      case opc_ifnonnull:
      case opc_ifnull:
      case opc_jsr:
        check_short(val, opc);
        operand = new OffsetOperand(this, val); break;

      case opc_goto_w:
      case opc_jsr_w:
        operand = new OffsetOperand(this, val, true); break;

      case opc_newarray:
        if(val < 0 || val > 255)
            throw new jasError("newarray counter is illegal", true);
        operand = new UnsignedByteOperand(val);
        break;

      case opc_ret:
      case opc_iload:
      case opc_lload:
      case opc_fload:
      case opc_dload:
      case opc_aload:
      case opc_istore:
      case opc_lstore:
      case opc_fstore:
      case opc_dstore:
      case opc_astore:
        operand = new UnsignedByteWideOperand(val, Wide);
        break;

      default:
        throw new jasError
          (opcNames[opc] + " does not take a numeric argument");
      }
  }


// used for relative offsets (ex : goto $+5)

  public Insn(int opc, int val, char relative)
    throws jasError
  {
    this.opc = opc;
    switch (opc)
      {
      case opc_goto:
      case opc_if_acmpeq:
      case opc_if_acmpne:
      case opc_if_icmpeq:
      case opc_if_icmpge:
      case opc_if_icmpgt:
      case opc_if_icmple:
      case opc_if_icmplt:
      case opc_if_icmpne:
      case opc_ifeq:
      case opc_ifge:
      case opc_ifgt:
      case opc_ifle:
      case opc_iflt:
      case opc_ifne:
      case opc_ifnonnull:
      case opc_ifnull:
      case opc_jsr:
        check_short(val, opc);
        operand = new RelativeOffsetOperand(this, val); break;

      case opc_goto_w:
      case opc_jsr_w:
        operand = new RelativeOffsetOperand(this, val, true); break;

      default:
        throw new jasError
          (opcNames[opc] + " does not take a relative numeric argument");
      }
  }

  /**
   * Instructions that take a Label as an argument. These are
   * opc_jsr,
   * opc_goto,
   * opc_if_acmpne,
   * opc_if_acmpeq,
   * opc_if_icmpge,
   * opc_if_icmple,
   * opc_if_icmpgt,
   * opc_if_icmplt,
   * opc_if_icmpne,
   * opc_if_icmpeq,
   * opc_ifge,
   * opc_ifgt,
   * opc_ifne,
   * opc_ifle,
   * opc_iflt,
   * opc_ifeq,
   * opc_ifnull,
   * opc_ifnonnull,
   * opc_goto_w,
   * opc_jsr_w
   */
  public Insn(int opc, Label target, int line)
    throws jasError
  {
    this.opc = opc;
    switch(opc)
      {
      case opc_jsr:
      case opc_goto:
      case opc_if_acmpne:
      case opc_if_acmpeq:
      case opc_if_icmpge:
      case opc_if_icmple:
      case opc_if_icmpgt:
      case opc_if_icmplt:
      case opc_if_icmpne:
      case opc_if_icmpeq:
      case opc_ifge:
      case opc_ifgt:
      case opc_ifne:
      case opc_ifle:
      case opc_iflt:
      case opc_ifeq:
      case opc_ifnull:
      case opc_ifnonnull:
        operand = new LabelOperand(target, this, line);
        break;
      case opc_goto_w:
      case opc_jsr_w:
        operand = new LabelOperand(target, this, true, line);
        break;
      default:
        throw new jasError
          (opcNames[opc] + " does not take a label as its argument");
      }
  }
  /**
   * This constructor is used for instructions that take a CP item
   * as their argument. These are
   * opc_anewarray,
   * opc_ldc_w,
   * opc_ldc2_w,
   * opc_invokedynamic,
   * opc_invokenonvirtual,
   * opc_invokestatic,
   * opc_invokevirtual,
   * opc_new,
   * opc_checkcast,
   * opc_instanceof,
   * opc_getstatic,
   * opc_putstatic,
   * opc_getfield,
   * opc_putfield,
   * opc_ldc
   */
  public Insn(int opc, CP arg)
    throws jasError
  {
    this.opc = opc;
    switch(opc)
      {
      case opc_anewarray:
      case opc_invokedynamic:
      case opc_invokenonvirtual:
      case opc_invokestatic:
      case opc_invokevirtual:
      case opc_new:
      case opc_checkcast:
      case opc_instanceof:
      case opc_getstatic:
      case opc_putstatic:
      case opc_getfield:
      case opc_putfield:
        operand = new CPOperand(arg);
        break;
      case opc_ldc2_w:
      case opc_ldc_w:
        operand = new LdcOperand(this, arg);
        break;
      case opc_ldc:
        operand = new LdcOperand(this, arg, false);
        break;
      default:
        throw new jasError
          (opcNames[opc] + " does not take a CP item as an argument");
      }
  }

                                // This allows the Insn a chance to
                                // add things to the global env if
                                // necessary. The CPInsnOperands
                                // use  this to add the CP to the
                                // classEnv
  void resolve(ClassEnv e)
  { if (operand != null) { operand.resolve(e); } }

  void write(ClassEnv e, CodeAttr ce, DataOutputStream out)
    throws IOException, jasError
  {
    if (operand != null)
      operand.writePrefix(e, ce, out);
    out.writeByte((byte) opc);
    if (operand != null)
      operand.write(e, ce, out);
  }

  int size(ClassEnv e, CodeAttr ce)
    throws jasError
  {
    if (operand == null) return 1;
    return (1 + operand.size(e, ce));
  }

  public String toString() {
    return "instruction "+opc+" "+((operand!=null)?operand.toString():"");
  }
}

/* --- Revision History ---------------------------------------------------
--- Iouri Kharon, Aug 10 2006
    Added 'wide' prefix to some instructions
*/
