summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/TypedReference.cs
blob: 7c68c4164fb538454327d45aa53f0ab17e014eac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System {

    // TypedReference is basically only ever seen on the call stack, and in param arrays.
    //  These are blob that must be dealt with by the compiler.
    using System;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using CultureInfo = System.Globalization.CultureInfo;
    using FieldInfo = System.Reflection.FieldInfo;
    using System.Security.Permissions;
    using System.Runtime.Versioning;
    using System.Diagnostics.Contracts;

    [CLSCompliant(false)] 
    [System.Runtime.InteropServices.ComVisible(true)]
    [System.Runtime.Versioning.NonVersionable] // This only applies to field layout
    public struct TypedReference
    {
        private IntPtr Value;
        private IntPtr Type;

        [System.Security.SecurityCritical]  // auto-generated_required
        [CLSCompliant(false)]
        public static TypedReference MakeTypedReference(Object target, FieldInfo[] flds) {
            if (target == null)
                throw new ArgumentNullException("target");
            if (flds == null)
                throw new ArgumentNullException("flds");
            Contract.EndContractBlock();
            if (flds.Length == 0)
                throw new ArgumentException(Environment.GetResourceString("Arg_ArrayZeroError"));

            IntPtr[] fields = new IntPtr[flds.Length];
            // For proper handling of Nullable<T> don't change GetType() to something like 'IsAssignableFrom'
            // Currently we can't make a TypedReference to fields of Nullable<T>, which is fine.  
            RuntimeType targetType = (RuntimeType)target.GetType();
            for (int i = 0; i < flds.Length; i++)
            {
                RuntimeFieldInfo field = flds[i] as RuntimeFieldInfo;
                if (field == null)
                    throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeFieldInfo"));

                if (field.IsInitOnly || field.IsStatic)
                    throw new ArgumentException(Environment.GetResourceString("Argument_TypedReferenceInvalidField"));
                
                if (targetType != field.GetDeclaringTypeInternal() && !targetType.IsSubclassOf(field.GetDeclaringTypeInternal()))
                    throw new MissingMemberException(Environment.GetResourceString("MissingMemberTypeRef"));

                RuntimeType fieldType = (RuntimeType)field.FieldType;
                if (fieldType.IsPrimitive)
                    throw new ArgumentException(Environment.GetResourceString("Arg_TypeRefPrimitve"));
                
                if (i < (flds.Length - 1) && !fieldType.IsValueType)
                    throw new MissingMemberException(Environment.GetResourceString("MissingMemberNestErr"));
                
                fields[i] = field.FieldHandle.Value;
                targetType = fieldType;
            }

            TypedReference result = new TypedReference ();

            // reference to TypedReference is banned, so have to pass result as pointer
            unsafe 
            {
                InternalMakeTypedReference(&result, target, fields, targetType);
            }
            return result;
        }

        [System.Security.SecurityCritical]  // auto-generated
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        // reference to TypedReference is banned, so have to pass result as pointer
        private unsafe static extern void InternalMakeTypedReference(void* result, Object target, IntPtr[] flds, RuntimeType lastFieldType);

        public override int GetHashCode()
        {
            if (Type == IntPtr.Zero)
                return 0;
            else
                return __reftype(this).GetHashCode();
        }

        public override bool Equals(Object o)
        {
            throw new NotSupportedException(Environment.GetResourceString("NotSupported_NYI"));
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe static Object ToObject(TypedReference value)
        {
            return InternalToObject(&value);
        }

        [System.Security.SecurityCritical]  // auto-generated
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal unsafe extern static Object InternalToObject(void * value);

        internal bool IsNull 
        { 
            get
            {
                return Value.IsNull() && Type.IsNull(); 
            }
        }

        public static Type GetTargetType (TypedReference value)
        {
            return __reftype(value);
        }

        public static RuntimeTypeHandle TargetTypeToken (TypedReference value)
        {
            return __reftype(value).TypeHandle;
        }

        //  This may cause the type to be changed.
        [System.Security.SecuritySafeCritical]  // auto-generated
        [CLSCompliant(false)]
        public unsafe static void SetTypedReference(TypedReference target, Object value)
        {
            InternalSetTypedReference(&target, value);
        }

        [System.Security.SecurityCritical]  // auto-generated
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal unsafe extern static void InternalSetTypedReference(void * target, Object value);
    }

}