/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */


package com.sun.jdo.api.persistence.enhancer.classfile;


import java.io.PrintStream;

/**
 * An instruction which requires a single constant from the constant
 * pool as an immediate operand 
 */

public class InsnConstOp extends Insn {
  /* The constant from the constant pool */
  private ConstBasic constValue;

  /* public accessors */

  public int nStackArgs() {
    int n = VMOp.ops[opcode()].nStackArgs();
    if (n >= 0) 
      return n;
    switch (opcode()) {
    case opc_putstatic:
    case opc_putfield:
      {
	ConstFieldRef fld = (ConstFieldRef) constValue;
	String sig = fld.nameAndType().signature().asString();
	if (sig.equals("J") || sig.equals("D"))//NOI18N
	  return (opcode() == opc_putfield) ? 3 : 2;
	return (opcode() == opc_putfield) ? 2 : 1;
      }
    case opc_invokevirtual:
    case opc_invokespecial:
    case opc_invokestatic:
      /* handle interface invoke too */
    case opc_invokeinterface:
      {
	ConstBasicMemberRef meth = (ConstBasicMemberRef) constValue;
	String sig = meth.nameAndType().signature().asString();
	int nMethodArgWords = Descriptor.countMethodArgWords(sig);
	return nMethodArgWords +
	  ((opcode() == opc_invokestatic) ? 0 : 1);
      }
    default:
        throw new InsnError("unexpected variable opcode");//NOI18N
    }
  }

  public int nStackResults() {
    int n = VMOp.ops[opcode()].nStackResults();
    if (n >= 0) 
      return n;
    switch (opcode()) {
    case opc_getstatic:
    case opc_getfield:
      {
	ConstFieldRef fld = (ConstFieldRef) constValue;
	String sig = fld.nameAndType().signature().asString();
	if (sig.equals("J") || sig.equals("D"))//NOI18N
	  return 2;
	return 1;
      }
    case opc_invokevirtual:
    case opc_invokespecial:
    case opc_invokestatic:
      /* handle interface invoke too */
    case opc_invokeinterface:
      {
	ConstBasicMemberRef meth = (ConstBasicMemberRef) constValue;
	return Descriptor.countMethodReturnWords(
	  meth.nameAndType().signature().asString());
      }
    default:
        throw new InsnError("unexpected variable opcode");//NOI18N
    }
  }

  public String argTypes() {
    switch (opcode()) {
    case opc_putstatic:
    case opc_putfield:
      {
	ConstFieldRef fld = (ConstFieldRef) constValue;
	String sig = fld.nameAndType().signature().asString();
	if (opcode() == opc_putstatic)
	  return sig;
	else
	  return descriptorTypeOfObject(fld) + sig;
      }
    case opc_invokevirtual:
    case opc_invokespecial:
    case opc_invokestatic:
      /* handle interface invoke too */
    case opc_invokeinterface:
      {
	ConstBasicMemberRef meth = (ConstBasicMemberRef) constValue;
	String argSig =
	  Descriptor.extractArgSig(meth.nameAndType().signature().asString());
	if (opcode() == opc_invokestatic)
	  return argSig;
	else
	  return descriptorTypeOfObject(meth) + argSig;
      }
    default:
      return VMOp.ops[opcode()].argTypes();
    }
  }

  public String resultTypes() {
    switch (opcode()) {
    case opc_invokevirtual:
    case opc_invokespecial:
    case opc_invokestatic:
      /* handle interface invoke too */
    case opc_invokeinterface:
      {
	ConstBasicMemberRef meth = (ConstBasicMemberRef) constValue;
	String resultSig = Descriptor.extractResultSig(
	  meth.nameAndType().signature().asString());
	if (resultSig.equals("V"))//NOI18N
            return "";//NOI18N
	return resultSig;
      }
    case opc_getstatic:
    case opc_getfield:
      {
	ConstFieldRef fld = (ConstFieldRef) constValue;
	return fld.nameAndType().signature().asString();
      }
    case opc_ldc:
    case opc_ldc_w:
    case opc_ldc2_w:
      {
	ConstValue constVal = (ConstValue) constValue;
	return constVal.descriptor();
      }
    default:
      return VMOp.ops[opcode()].resultTypes();
    }
  }

  public boolean branches() {
    /* invokes don't count as a branch */
    return false;
  }

  /**
   * Return the constant pool entry which is the immediate operand
   */
  public ConstBasic value() {
    return constValue;
  }
    
  /**
   * Modify the referenced constant
   */
  public void setValue(ConstBasic newValue) {
    checkConstant(newValue);
    constValue = newValue;
  }
    
  /* package local methods */

  void print (PrintStream out, int indent) {
    ClassPrint.spaces(out, indent);
    out.println(offset() + "  " + opName(opcode()) + "  pool(" + //NOI18N
		constValue.getIndex() + ")");//NOI18N
  }

  int store(byte[] buf, int index) {
    if (opcode() == opc_ldc && !isNarrowldc())
      buf[index++] = (byte) opc_ldc_w;
    else
      buf[index++] = (byte) opcode();
    int constIndex = constValue.getIndex();
    if (size() == 3)
      buf[index++] = (byte) (constIndex >> 8);
    buf[index++] = (byte)(constIndex & 0xff);
    return index;
  }

  int size() {
    return isNarrowldc() ? 2 : 3;
  }

  private boolean isNarrowldc() {
    return (opcode() == opc_ldc && constValue.getIndex() < 256);
  }
    

  InsnConstOp (int theOpcode, ConstBasic theOperand) {
    this(theOpcode, theOperand, NO_OFFSET);
  }

  InsnConstOp (int theOpcode, ConstBasic theOperand, int pc) {
    super(theOpcode, pc);
    constValue = theOperand;
    checkConstant(theOperand);
    if (theOpcode == opc_invokeinterface) 
        throw new InsnError("attempt to create an " + opName(theOpcode) +//NOI18N
                            " as an InsnConstOp instead of InsnInterfaceInvoke");//NOI18N
  }

  /* used only by InsnInterfaceInvoke, to make sure that opc_invokeinterface cannot
   * come through the wrong path and miss its extra nArgsOp */
  InsnConstOp (int theOpcode, ConstInterfaceMethodRef theOperand, int pc) {
    super(theOpcode, pc);
    constValue = theOperand;
    checkConstant(theOperand);
  }

  private void checkConstant (ConstBasic operand) {
    switch(opcode()) {
    case opc_ldc:
    case opc_ldc_w:
    case opc_ldc2_w:
      /* ConstValue */
      if (operand == null ||
	  (! (operand instanceof ConstValue)))
          throw new InsnError ("attempt to create an " + opName(opcode()) +//NOI18N
                               " without a ConstValue operand");//NOI18N
      break;

    case opc_getstatic:
    case opc_putstatic:
    case opc_getfield:
    case opc_putfield:
      /* ConstFieldRef */
      if (operand == null ||
	  (! (operand instanceof ConstFieldRef)))
          throw new InsnError ("attempt to create an " + opName(opcode()) +//NOI18N
                               " without a ConstFieldRef operand");//NOI18N
      break;

    case opc_invokevirtual:
    case opc_invokespecial:
    case opc_invokestatic:
      /* ConstMethodRef */
      if (operand == null ||
	  (! (operand instanceof ConstMethodRef)))
          throw new InsnError ("attempt to create an " + opName(opcode()) +//NOI18N
                               " without a ConstMethodRef operand");//NOI18N
      break;
      
    case opc_invokeinterface:
      /* ConstInterfaceMethodRef */
      if (operand == null ||
	  (! (operand instanceof ConstInterfaceMethodRef)))
          throw new InsnError("Attempt to create an " + opName(opcode()) +//NOI18N
                              " without a ConstInterfaceMethodRef operand");//NOI18N
      break;

    case opc_new:
    case opc_anewarray:
    case opc_checkcast:
    case opc_instanceof:
      /* ConstClass */
      if (operand == null ||
	  (! (operand instanceof ConstClass)))
          throw new InsnError ("attempt to create an " + opName(opcode()) +//NOI18N
                               " without a ConstClass operand");//NOI18N
      break;

    default:
        throw new InsnError ("attempt to create an " + opName(opcode()) +//NOI18N
                             " with a constant operand");//NOI18N
    }
  }

  private final String descriptorTypeOfObject(ConstBasicMemberRef memRef) {
    String cname = memRef.className().className().asString();
    return "L" + cname + ";";//NOI18N
  }

}
