File: system\variant.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--==
/*============================================================
**
** Class:  Variant
**
**
** Purpose: The CLR implementation of Variant.
**
**
===========================================================*/
namespace System {
 
    using System;
    using System.Reflection;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Runtime.Versioning;
    using System.Diagnostics.Contracts;
 
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    internal struct Variant {
 
        //Do Not change the order of these fields.
        //They are mapped to the native VariantData * data structure.
        private Object m_objref;
        private int m_data1;
        private int m_data2;
        private int m_flags;
 
        // The following bits have been taken up as follows
        // bits 0-15    - Type code
        // bit  16      - Array
        // bits 19-23   - Enums
        // bits 24-31   - Optional VT code (for roundtrip VT preservation)
 
 
        //What are the consequences of making this an enum?
        ///////////////////////////////////////////////////////////////////////
        // If you update this, update the corresponding stuff in OAVariantLib.cs,
        // COMOAVariant.cpp (2 tables, forwards and reverse), and perhaps OleVariant.h
        ///////////////////////////////////////////////////////////////////////
        internal const int CV_EMPTY=0x0;
        internal const int CV_VOID=0x1;
        internal const int CV_BOOLEAN=0x2;
        internal const int CV_CHAR=0x3;
        internal const int CV_I1=0x4;
        internal const int CV_U1=0x5;
        internal const int CV_I2=0x6;
        internal const int CV_U2=0x7;
        internal const int CV_I4=0x8;
        internal const int CV_U4=0x9;
        internal const int CV_I8=0xa;
        internal const int CV_U8=0xb;
        internal const int CV_R4=0xc;
        internal const int CV_R8=0xd;
        internal const int CV_STRING=0xe;
        internal const int CV_PTR=0xf;
        internal const int CV_DATETIME = 0x10;
        internal const int CV_TIMESPAN = 0x11;
        internal const int CV_OBJECT=0x12;
        internal const int CV_DECIMAL = 0x13;
        internal const int CV_ENUM=0x15;
        internal const int CV_MISSING=0x16;
        internal const int CV_NULL=0x17;
        internal const int CV_LAST=0x18;
 
        internal const int TypeCodeBitMask=0xffff;
        internal const int VTBitMask=unchecked((int)0xff000000);
        internal const int VTBitShift=24;
        internal const int ArrayBitMask =0x10000;
 
        // Enum enum and Mask
        internal const int EnumI1           =0x100000;
        internal const int EnumU1           =0x200000;
        internal const int EnumI2           =0x300000;
        internal const int EnumU2           =0x400000;
        internal const int EnumI4           =0x500000;
        internal const int EnumU4           =0x600000;
        internal const int EnumI8           =0x700000;
        internal const int EnumU8           =0x800000;
        internal const int EnumMask     =0xF00000;
 
        internal static readonly Type [] ClassTypes = {
            typeof(System.Empty),
            typeof(void),
            typeof(Boolean),
            typeof(Char),
            typeof(SByte),
            typeof(Byte),
            typeof(Int16),
            typeof(UInt16),
            typeof(Int32),
            typeof(UInt32),
            typeof(Int64),
            typeof(UInt64),
            typeof(Single),
            typeof(Double),
            typeof(String),
            typeof(void),           // ptr for the moment
            typeof(DateTime),
            typeof(TimeSpan),
            typeof(Object),
            typeof(Decimal),
            typeof(Object),     // Treat enum as Object
            typeof(System.Reflection.Missing),
            typeof(System.DBNull),
        };
 
        internal static readonly Variant Empty = new Variant();
        internal static readonly Variant Missing = new Variant(Variant.CV_MISSING, Type.Missing, 0, 0);
        internal static readonly Variant DBNull = new Variant(Variant.CV_NULL, System.DBNull.Value, 0, 0);
 
        //
        // Native Methods
        //
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal extern double GetR8FromVar();
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal extern float  GetR4FromVar();
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal extern void   SetFieldsR4(float val);
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal extern void   SetFieldsR8(double val);
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal extern void SetFieldsObject(Object val);
 
        // Use this function instead of an ECALL - saves about 150 clock cycles
        // by avoiding the ecall transition and because the JIT inlines this.
        // Ends up only taking about 1/8 the time of the ECALL version.
        internal long GetI8FromVar()
        {
            return ((long)m_data2<<32 | ((long)m_data1 & 0xFFFFFFFFL));
        }
 
        //
        // Constructors
        //
 
        internal Variant(int flags, Object or, int data1, int data2) {
            m_flags = flags;
            m_objref=or;
            m_data1=data1;
            m_data2=data2;
        }
 
        public Variant(bool val) {
            m_objref= null;
            m_flags = CV_BOOLEAN;
            m_data1 = (val)?Boolean.True:Boolean.False;
            m_data2 = 0;
        }
 
        public Variant(sbyte val) {
            m_objref=null;
            m_flags=CV_I1;
            m_data1=(int)val;
            m_data2=(int)(((long)val)>>32);
        }
 
 
        public Variant(byte val) {
            m_objref=null;
            m_flags=CV_U1;
            m_data1=(int)val;
            m_data2=0;
        }
 
        public Variant(short val) {
            m_objref=null;
            m_flags=CV_I2;
            m_data1=(int)val;
            m_data2=(int)(((long)val)>>32);
        }
 
        public Variant(ushort val) {
            m_objref=null;
            m_flags=CV_U2;
            m_data1=(int)val;
            m_data2=0;
        }
 
        public Variant(char val) {
            m_objref=null;
            m_flags=CV_CHAR;
            m_data1=(int)val;
            m_data2=0;
        }
 
        public Variant(int val) {
            m_objref=null;
            m_flags=CV_I4;
            m_data1=val;
            m_data2=val >> 31;
        }
 
        public Variant(uint val) {
            m_objref=null;
            m_flags=CV_U4;
            m_data1=(int)val;
            m_data2=0;
        }
 
        public Variant(long val) {
            m_objref=null;
            m_flags=CV_I8;
            m_data1 = (int)val;
            m_data2 = (int)(val >> 32);
        }
 
        public Variant(ulong val) {
            m_objref=null;
            m_flags=CV_U8;
            m_data1 = (int)val;
            m_data2 = (int)(val >> 32);
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public Variant(float val) {
            m_objref=null;
            m_flags=CV_R4;
            m_data1=0;
            m_data2=0;
            SetFieldsR4(val);
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        public Variant(double val) {
            m_objref=null;
            m_flags=CV_R8;
            m_data1=0;
            m_data2=0;
            SetFieldsR8(val);
        }
 
        public Variant(DateTime val) {
            m_objref=null;
            m_flags=CV_DATETIME;
            ulong ticks = (ulong)val.Ticks;
            m_data1 = (int)ticks;
            m_data2 = (int)(ticks>>32);
        }
 
        public Variant(Decimal val) {
            m_objref = (Object)val;
            m_flags = CV_DECIMAL;
            m_data1=0;
            m_data2=0;
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public Variant(Object obj) {
            m_data1=0;
            m_data2=0;
 
            VarEnum vt = VarEnum.VT_EMPTY;
 
            if (obj is DateTime) {
                m_objref=null;
                m_flags=CV_DATETIME;
                ulong ticks = (ulong)((DateTime)obj).Ticks;
                m_data1 = (int)ticks;
                m_data2 = (int)(ticks>>32);
                return;
            }
 
            if (obj is String) {
                m_flags=CV_STRING;
                m_objref=obj;
                return;
            }
 
            if (obj == null) {
                this = Empty;
                return;
            }
            if (obj == System.DBNull.Value) {
                this = DBNull;
                return;
            }
            if (obj == Type.Missing) {
                this = Missing;
                return;
            }
 
            if (obj is Array) {
                m_flags=CV_OBJECT | ArrayBitMask;
                m_objref=obj;
                return;
            }
 
            // Compiler appeasement
            m_flags = CV_EMPTY;
            m_objref = null;
 
            // Check to see if the object passed in is a wrapper object.
            if (obj is UnknownWrapper)
            {
                vt = VarEnum.VT_UNKNOWN;
                obj = ((UnknownWrapper)obj).WrappedObject;
            }
            else if (obj is DispatchWrapper)
            {
                vt = VarEnum.VT_DISPATCH;
                obj = ((DispatchWrapper)obj).WrappedObject;
            }
            else if (obj is ErrorWrapper)
            {
                vt = VarEnum.VT_ERROR;
                obj = (Object)(((ErrorWrapper)obj).ErrorCode);
                Contract.Assert(obj != null, "obj != null");
            }
            else if (obj is CurrencyWrapper)
            {
                vt = VarEnum.VT_CY;
                obj = (Object)(((CurrencyWrapper)obj).WrappedObject);
                Contract.Assert(obj != null, "obj != null");
            }
            else if (obj is BStrWrapper)
            {
                vt = VarEnum.VT_BSTR;
                obj = (Object)(((BStrWrapper)obj).WrappedObject);
            }
 
            if (obj != null)
            {
                SetFieldsObject(obj);
            }
 
            // If the object passed in is one of the wrappers then set the VARIANT type.
            if (vt != VarEnum.VT_EMPTY)
                m_flags |= ((int)vt << VTBitShift);
        }
 
 
        [System.Security.SecurityCritical]  // auto-generated
        unsafe public Variant(void* voidPointer,Type pointerType) {
            if (pointerType == null)
                throw new ArgumentNullException("pointerType");
            if (!pointerType.IsPointer)
                throw new ArgumentException(Environment.GetResourceString("Arg_MustBePointer"),"pointerType");
            Contract.EndContractBlock();
 
            m_objref = pointerType;
            m_flags=CV_PTR;
            m_data1=(int)voidPointer;
            m_data2=0;
 
        }
 
        //This is a family-only accessor for the CVType.
        //This is never to be exposed externally.
        internal int CVType {
            get {
                return (m_flags&TypeCodeBitMask);
            }
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public Object ToObject() {
            switch (CVType) {
            case CV_EMPTY:
                return null;
            case CV_BOOLEAN:
                return (Object)(m_data1!=0);
            case CV_I1:
                return (Object)((sbyte)m_data1);
            case CV_U1:
                return (Object)((byte)m_data1);
            case CV_CHAR:
                return (Object)((char)m_data1);
            case CV_I2:
                return (Object)((short)m_data1);
            case CV_U2:
                return (Object)((ushort)m_data1);
            case CV_I4:
                return (Object)(m_data1);
            case CV_U4:
                return (Object)((uint)m_data1);
            case CV_I8:
                return (Object)(GetI8FromVar());
            case CV_U8:
                return (Object)((ulong)GetI8FromVar());
            case CV_R4:
                return (Object)(GetR4FromVar());
            case CV_R8:
                return (Object)(GetR8FromVar());
            case CV_DATETIME:
                return new DateTime(GetI8FromVar());
            case CV_TIMESPAN:
                return new TimeSpan(GetI8FromVar());
            case CV_ENUM:
                return BoxEnum();
            case CV_MISSING:
                return Type.Missing;
            case CV_NULL:
                return System.DBNull.Value;
            case CV_DECIMAL:
            case CV_STRING:
            case CV_OBJECT:
            default:
                return m_objref;
            }
        }
 
        // This routine will return an boxed enum.
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern Object BoxEnum();
 
 
        // Helper code for marshaling managed objects to VARIANT's (we use
        // managed variants as an intermediate type.
        [System.Security.SecuritySafeCritical]  // auto-generated
        internal static void MarshalHelperConvertObjectToVariant(Object o, ref Variant v)
        {
#if FEATURE_REMOTING
            IConvertible ic = System.Runtime.Remoting.RemotingServices.IsTransparentProxy(o) ? null : o as IConvertible;
#else
            IConvertible ic = o as IConvertible;
#endif
            if (o == null)
            {
                v = Empty;
            }
            else if (ic == null)
            {
                // This path should eventually go away. But until
                // the work is done to have all of our wrapper types implement
                // IConvertible, this is a cheapo way to get the work done.
                v = new Variant(o);
            }
            else
            {
                IFormatProvider provider = CultureInfo.InvariantCulture;
                switch (ic.GetTypeCode())
                {
                    case TypeCode.Empty:
                        v = Empty;
                        break;
 
                    case TypeCode.Object:
                        v = new Variant((Object)o);
                        break;
 
                    case TypeCode.DBNull:
                        v = DBNull;
                        break;
 
                    case TypeCode.Boolean:
                        v = new Variant(ic.ToBoolean(provider));
                        break;
 
                    case TypeCode.Char:
                        v = new Variant(ic.ToChar(provider));
                        break;
 
                    case TypeCode.SByte:
                        v = new Variant(ic.ToSByte(provider));
                        break;
 
                    case TypeCode.Byte:
                        v = new Variant(ic.ToByte(provider));
                        break;
 
                    case TypeCode.Int16:
                        v = new Variant(ic.ToInt16(provider));
                        break;
 
                    case TypeCode.UInt16:
                        v = new Variant(ic.ToUInt16(provider));
                        break;
 
                    case TypeCode.Int32:
                        v = new Variant(ic.ToInt32(provider));
                        break;
 
                    case TypeCode.UInt32:
                        v = new Variant(ic.ToUInt32(provider));
                        break;
 
                    case TypeCode.Int64:
                        v = new Variant(ic.ToInt64(provider));
                        break;
 
                    case TypeCode.UInt64:
                        v = new Variant(ic.ToUInt64(provider));
                        break;
 
                    case TypeCode.Single:
                        v = new Variant(ic.ToSingle(provider));
                        break;
 
                    case TypeCode.Double:
                        v = new Variant(ic.ToDouble(provider));
                        break;
 
                    case TypeCode.Decimal:
                        v = new Variant(ic.ToDecimal(provider));
                        break;
 
                    case TypeCode.DateTime:
                        v = new Variant(ic.ToDateTime(provider));
                        break;
 
                    case TypeCode.String:
                        v = new Variant(ic.ToString(provider));
                        break;
 
                    default:
                        throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnknownTypeCode", ic.GetTypeCode()));
                }
            }
        }
 
        // Helper code for marshaling VARIANTS to managed objects (we use
        // managed variants as an intermediate type.
        internal static Object MarshalHelperConvertVariantToObject(ref Variant v)
        {
            return v.ToObject();
        }
 
        // Helper code: on the back propagation path where a VT_BYREF VARIANT*
        // is marshaled to a "ref Object", we use this helper to force the
        // updated object back to the original type.
        [System.Security.SecurityCritical]  // auto-generated
        internal static void MarshalHelperCastVariant(Object pValue, int vt, ref Variant v)
        {
            IConvertible iv = pValue as IConvertible;
            if (iv == null)
            {
                switch (vt)
                {
                    case 9: /*VT_DISPATCH*/
                        v = new Variant(new DispatchWrapper(pValue));
                        break;
 
                    case 12: /*VT_VARIANT*/
                        v = new Variant(pValue);
                        break;
 
                    case 13: /*VT_UNKNOWN*/
                        v = new Variant(new UnknownWrapper(pValue));
                        break;
 
                    case 36: /*VT_RECORD*/
                        v = new Variant(pValue);
                        break;
 
                    case 8: /*VT_BSTR*/
                        if (pValue == null)
                        {
                            v = new Variant(null);
                            v.m_flags = CV_STRING;
                        }
                        else
                        {
                            throw new InvalidCastException(Environment.GetResourceString("InvalidCast_CannotCoerceByRefVariant"));
                        }
                        break;
 
                    default:
                        throw new InvalidCastException(Environment.GetResourceString("InvalidCast_CannotCoerceByRefVariant"));
                }
            }
            else
            {
                IFormatProvider provider = CultureInfo.InvariantCulture;
                switch (vt)
                {
                    case 0: /*VT_EMPTY*/
                        v = Empty;
                        break;
 
                    case 1: /*VT_NULL*/
                        v = DBNull;
                        break;
 
                    case 2: /*VT_I2*/
                        v = new Variant(iv.ToInt16(provider));
                        break;
 
                    case 3: /*VT_I4*/
                        v = new Variant(iv.ToInt32(provider));
                        break;
 
                    case 4: /*VT_R4*/
                        v = new Variant(iv.ToSingle(provider));
                        break;
 
                    case 5: /*VT_R8*/
                        v = new Variant(iv.ToDouble(provider));
                        break;
 
                    case 6: /*VT_CY*/
                        v = new Variant(new CurrencyWrapper(iv.ToDecimal(provider)));
                        break;
 
                    case 7: /*VT_DATE*/
                        v = new Variant(iv.ToDateTime(provider));
                        break;
 
                    case 8: /*VT_BSTR*/
                        v = new Variant(iv.ToString(provider));
                        break;
 
                    case 9: /*VT_DISPATCH*/
                        v = new Variant(new DispatchWrapper((Object)iv));
                        break;
 
                    case 10: /*VT_ERROR*/
                        v = new Variant(new ErrorWrapper(iv.ToInt32(provider)));
                        break;
 
                    case 11: /*VT_BOOL*/
                        v = new Variant(iv.ToBoolean(provider));
                        break;
 
                    case 12: /*VT_VARIANT*/
                        v = new Variant((Object)iv);
                        break;
 
                    case 13: /*VT_UNKNOWN*/
                        v = new Variant(new UnknownWrapper((Object)iv));
                        break;
 
                    case 14: /*VT_DECIMAL*/
                        v = new Variant(iv.ToDecimal(provider));
                        break;
 
                    // case 15: /*unused*/
                    //  NOT SUPPORTED
 
                    case 16: /*VT_I1*/
                        v = new Variant(iv.ToSByte(provider));
                        break;
 
                    case 17: /*VT_UI1*/
                        v = new Variant(iv.ToByte(provider));
                        break;
 
                    case 18: /*VT_UI2*/
                        v = new Variant(iv.ToUInt16(provider));
                        break;
 
                    case 19: /*VT_UI4*/
                        v = new Variant(iv.ToUInt32(provider));
                        break;
 
                    case 20: /*VT_I8*/
                        v = new Variant(iv.ToInt64(provider));
                        break;
 
                    case 21: /*VT_UI8*/
                        v = new Variant(iv.ToUInt64(provider));
                        break;
 
                    case 22: /*VT_INT*/
                        v = new Variant(iv.ToInt32(provider));
                        break;
 
                    case 23: /*VT_UINT*/
                        v = new Variant(iv.ToUInt32(provider));
                        break;
 
                    default:
                        throw new InvalidCastException(Environment.GetResourceString("InvalidCast_CannotCoerceByRefVariant"));
                }
            }
        }
    }
}