/**
     * Emit an implicit conversion for an argument which must be of the given pclass.
     * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface.
     *
     * @param ptype type of value present on stack
     * @param pclass type of value required on stack
     * @param arg compile-time representation of value on stack (Node, constant) or null if none
     */
    private void emitImplicitConversion(BasicType ptype, Class pclass, object arg)
    {
        //assert(basicType(pclass) == ptype);  // boxing/unboxing handled by caller
        if (pclass == ptype.basicTypeClass() && ptype != BasicType.L_TYPE)
        {
            return;   // nothing to do
        }
        switch (ptype.name())
        {
        case "L_TYPE":
            if (VerifyType.isNullConversion(CoreClasses.java.lang.Object.Wrapper.ClassObject, pclass, false))
            {
                //if (PROFILE_LEVEL > 0)
                //    emitReferenceCast(Object.class, arg);
                return;
            }
            emitReferenceCast(pclass, arg);
            return;

        case "I_TYPE":
            if (!VerifyType.isNullConversion(java.lang.Integer.TYPE, pclass, false))
            {
                emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass));
            }
            return;
        }
        throw new BailoutException(Bailout.PreconditionViolated, "bad implicit conversion: tc=" + ptype + ": " + pclass);
    }
    private void emitPushArgument(Class ptype, object arg)
    {
        BasicType bptype = BasicType.basicType(ptype);

        if (arg is Name)
        {
            Name n = (Name)arg;
            emitLoadInsn(n._type(), n.index());
            emitImplicitConversion(n._type(), ptype, n);
        }
        else if ((arg == null || arg is string) && bptype == BasicType.L_TYPE)
        {
            emitConst(arg);
        }
        else
        {
            if (Wrapper.isWrapperType(ikvm.extensions.ExtensionMethods.getClass(arg)) && bptype != BasicType.L_TYPE)
            {
                emitConst(arg);
            }
            else
            {
                EmitConstant(arg);
                emitImplicitConversion(BasicType.L_TYPE, ptype, arg);
            }
        }
    }
    private void emitI2X(Wrapper type)
    {
        switch (type.name())
        {
        case "BYTE":    ilgen.Emit(OpCodes.Conv_I1);  break;

        case "SHORT":   ilgen.Emit(OpCodes.Conv_I2);  break;

        case "CHAR":    ilgen.Emit(OpCodes.Conv_U2);  break;

        case "INT": /* naught */ break;

        case "LONG":    ilgen.Emit(OpCodes.Conv_I8);  break;

        case "FLOAT":   ilgen.Emit(OpCodes.Conv_R4);  break;

        case "DOUBLE":  ilgen.Emit(OpCodes.Conv_R8);  break;

        case "BOOLEAN":
            // For compatibility with ValueConversions and explicitCastArguments:
            ilgen.EmitLdc_I4(1);
            ilgen.Emit(OpCodes.And);
            break;

        default:   throw new BailoutException(Bailout.PreconditionViolated, "unknown type: " + type);
        }
    }
    private byte arrayTypeCode(Wrapper elementType)
    {
        switch (elementType.name())
        {
        case "BOOLEAN": return(Opcodes.T_BOOLEAN);

        case "BYTE":    return(Opcodes.T_BYTE);

        case "CHAR":    return(Opcodes.T_CHAR);

        case "SHORT":   return(Opcodes.T_SHORT);

        case "INT":     return(Opcodes.T_INT);

        case "LONG":    return(Opcodes.T_LONG);

        case "FLOAT":   return(Opcodes.T_FLOAT);

        case "DOUBLE":  return(Opcodes.T_DOUBLE);

        case "OBJECT":  return(0);    // in place of Opcodes.T_OBJECT

        default:        throw new BailoutException(Bailout.PreconditionViolated, "elemendType = " + elementType);
        }
    }
    void emitNewArray(Name name)
    {
        Class rtype = name.function.methodType().returnType();

        if (name.arguments.Length == 0)
        {
            // The array will be a constant.
            object emptyArray;
            try {
                emptyArray = name.function._resolvedHandle().invoke();
            } catch (Exception ex) {
                throw new java.lang.InternalError(ex);
            }
            //assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
            //assert(emptyArray.getClass() == rtype);  // exact typing
            //mv.visitLdcInsn(constantPlaceholder(emptyArray));
            EmitConstant(emptyArray);
            emitReferenceCast(rtype, emptyArray);
            return;
        }
        Class arrayElementType = rtype.getComponentType();

        //assert(arrayElementType != null);
        emitIconstInsn(name.arguments.Length);
        OpCode xas = OpCodes.Stelem_Ref;

        if (!arrayElementType.isPrimitive())
        {
            TypeWrapper tw = TypeWrapper.FromClass(arrayElementType);
            if (tw.IsUnloadable || tw.IsGhost || tw.IsGhostArray || tw.IsNonPrimitiveValueType)
            {
                throw new BailoutException(Bailout.UnsupportedArrayType, tw);
            }
            ilgen.Emit(OpCodes.Newarr, tw.TypeAsArrayType);
        }
        else
        {
            byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
            xas = arrayInsnOpcode(tc);
            //mv.visitIntInsn(Opcodes.NEWARRAY, tc);
            ilgen.Emit(OpCodes.Newarr, TypeWrapper.FromClass(arrayElementType).TypeAsArrayType);
        }
        // store arguments
        for (int i = 0; i < name.arguments.Length; i++)
        {
            //mv.visitInsn(Opcodes.DUP);
            ilgen.Emit(OpCodes.Dup);
            emitIconstInsn(i);
            emitPushArgument(name, i);
            //mv.visitInsn(xas);
            ilgen.Emit(xas);
        }
        // the array is left on the stack
        assertStaticType(rtype, name);
    }
    void emitArrayLoad(Name name)
    {
        OpCode arrayOpcode = OpCodes.Ldelem_Ref;
        Class  elementType = name.function.methodType().parameterType(0).getComponentType();

        emitPushArguments(name);
        if (elementType.isPrimitive())
        {
            Wrapper w = Wrapper.forPrimitiveType(elementType);
            arrayOpcode = arrayLoadOpcode(arrayTypeCode(w));
        }
        ilgen.Emit(arrayOpcode);
    }
    private void emitX2I(Wrapper type)
    {
        switch (type.name())
        {
        case "LONG":    ilgen.Emit(OpCodes.Conv_I4);  break;

        case "FLOAT":   ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.f2i);  break;

        case "DOUBLE":  ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.d2i);  break;

        default:        throw new BailoutException(Bailout.PreconditionViolated, "unknown type: " + type);
        }
    }
    /**
     * Emit a type conversion bytecode casting from "from" to "to".
     */
    private void emitPrimCast(Wrapper from, Wrapper to)
    {
        // Here's how.
        // -   indicates forbidden
        // <-> indicates implicit
        //      to ----> boolean  byte     short    char     int      long     float    double
        // from boolean    <->        -        -        -        -        -        -        -
        //      byte        -       <->       i2s      i2c      <->      i2l      i2f      i2d
        //      short       -       i2b       <->      i2c      <->      i2l      i2f      i2d
        //      char        -       i2b       i2s      <->      <->      i2l      i2f      i2d
        //      int         -       i2b       i2s      i2c      <->      i2l      i2f      i2d
        //      long        -     l2i,i2b   l2i,i2s  l2i,i2c    l2i      <->      l2f      l2d
        //      float       -     f2i,i2b   f2i,i2s  f2i,i2c    f2i      f2l      <->      f2d
        //      double      -     d2i,i2b   d2i,i2s  d2i,i2c    d2i      d2l      d2f      <->
        if (from == to)
        {
            // no cast required, should be dead code anyway
            return;
        }
        if (from.isSubwordOrInt())
        {
            // cast from {byte,short,char,int} to anything
            emitI2X(to);
        }
        else
        {
            // cast from {long,float,double} to anything
            if (to.isSubwordOrInt())
            {
                // cast to {byte,short,char,int}
                emitX2I(from);
                if (to.bitWidth() < 32)
                {
                    // targets other than int require another conversion
                    emitI2X(to);
                }
            }
            else
            {
                // cast to {long,float,double} - this is verbose
                bool error = false;
                switch (from.name())
                {
                case "LONG":
                    switch (to.name())
                    {
                    case "FLOAT":   ilgen.Emit(OpCodes.Conv_R4);  break;

                    case "DOUBLE":  ilgen.Emit(OpCodes.Conv_R8);  break;

                    default:        error = true;                 break;
                    }
                    break;

                case "FLOAT":
                    switch (to.name())
                    {
                    case "LONG":    ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.f2l); break;

                    case "DOUBLE":  ilgen.Emit(OpCodes.Conv_R8);  break;

                    default:        error = true;                 break;
                    }
                    break;

                case "DOUBLE":
                    switch (to.name())
                    {
                    case "LONG":   ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.d2l); break;

                    case "FLOAT":   ilgen.Emit(OpCodes.Conv_R4);  break;

                    default:        error = true;                 break;
                    }
                    break;

                default:
                    error = true;
                    break;
                }
                if (error)
                {
                    throw new BailoutException(Bailout.PreconditionViolated, "unhandled prim cast: " + from + "2" + to);
                }
            }
        }
    }
 private void emitX2I(Wrapper type) {
     switch (type.name()) {
     case "LONG":    ilgen.Emit(OpCodes.Conv_I4);  break;
     case "FLOAT":   ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.f2i);  break;
     case "DOUBLE":  ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.d2i);  break;
     default:        throw new BailoutException(Bailout.PreconditionViolated, "unknown type: " + type);
     }
 }
 private void emitI2X(Wrapper type) {
     switch (type.name()) {
     case "BYTE":    ilgen.Emit(OpCodes.Conv_I1);  break;
     case "SHORT":   ilgen.Emit(OpCodes.Conv_I2);  break;
     case "CHAR":    ilgen.Emit(OpCodes.Conv_U2);  break;
     case "INT":     /* naught */                  break;
     case "LONG":    ilgen.Emit(OpCodes.Conv_I8);  break;
     case "FLOAT":   ilgen.Emit(OpCodes.Conv_R4);  break;
     case "DOUBLE":  ilgen.Emit(OpCodes.Conv_R8);  break;
     case "BOOLEAN":
         // For compatibility with ValueConversions and explicitCastArguments:
         ilgen.EmitLdc_I4(1);
         ilgen.Emit(OpCodes.And);
         break;
     default:   throw new BailoutException(Bailout.PreconditionViolated, "unknown type: " + type);
     }
 }
 /**
  * Emit a type conversion bytecode casting from "from" to "to".
  */
 private void emitPrimCast(Wrapper from, Wrapper to) {
     // Here's how.
     // -   indicates forbidden
     // <-> indicates implicit
     //      to ----> boolean  byte     short    char     int      long     float    double
     // from boolean    <->        -        -        -        -        -        -        -
     //      byte        -       <->       i2s      i2c      <->      i2l      i2f      i2d
     //      short       -       i2b       <->      i2c      <->      i2l      i2f      i2d
     //      char        -       i2b       i2s      <->      <->      i2l      i2f      i2d
     //      int         -       i2b       i2s      i2c      <->      i2l      i2f      i2d
     //      long        -     l2i,i2b   l2i,i2s  l2i,i2c    l2i      <->      l2f      l2d
     //      float       -     f2i,i2b   f2i,i2s  f2i,i2c    f2i      f2l      <->      f2d
     //      double      -     d2i,i2b   d2i,i2s  d2i,i2c    d2i      d2l      d2f      <->
     if (from == to) {
         // no cast required, should be dead code anyway
         return;
     }
     if (from.isSubwordOrInt()) {
         // cast from {byte,short,char,int} to anything
         emitI2X(to);
     } else {
         // cast from {long,float,double} to anything
         if (to.isSubwordOrInt()) {
             // cast to {byte,short,char,int}
             emitX2I(from);
             if (to.bitWidth() < 32) {
                 // targets other than int require another conversion
                 emitI2X(to);
             }
         } else {
             // cast to {long,float,double} - this is verbose
             bool error = false;
             switch (from.name()) {
             case "LONG":
                 switch (to.name()) {
                 case "FLOAT":   ilgen.Emit(OpCodes.Conv_R4);  break;
                 case "DOUBLE":  ilgen.Emit(OpCodes.Conv_R8);  break;
                 default:        error = true;                 break;
                 }
                 break;
             case "FLOAT":
                 switch (to.name()) {
                 case "LONG":    ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.f2l); break;
                 case "DOUBLE":  ilgen.Emit(OpCodes.Conv_R8);  break;
                 default:        error = true;                 break;
                 }
                 break;
             case "DOUBLE":
                 switch (to.name()) {
                 case "LONG" :   ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.d2l); break;
                 case "FLOAT":   ilgen.Emit(OpCodes.Conv_R4);  break;
                 default:        error = true;                 break;
                 }
                 break;
             default:
                 error = true;
                 break;
             }
             if (error) {
                 throw new BailoutException(Bailout.PreconditionViolated, "unhandled prim cast: " + from + "2" + to);
             }
         }
     }
 }
 private byte arrayTypeCode(Wrapper elementType) {
     switch (elementType.name()) {
         case "BOOLEAN": return Opcodes.T_BOOLEAN;
         case "BYTE":    return Opcodes.T_BYTE;
         case "CHAR":    return Opcodes.T_CHAR;
         case "SHORT":   return Opcodes.T_SHORT;
         case "INT":     return Opcodes.T_INT;
         case "LONG":    return Opcodes.T_LONG;
         case "FLOAT":   return Opcodes.T_FLOAT;
         case "DOUBLE":  return Opcodes.T_DOUBLE;
         case "OBJECT":  return 0; // in place of Opcodes.T_OBJECT
         default:        throw new BailoutException(Bailout.PreconditionViolated, "elemendType = " + elementType);
     }
 }