Welcome to mirror list, hosted at ThFree Co, Russian Federation.

CustomAttributeInstantiator.cs « NonPortable « Extensions « Reflection « Internal « src « System.Private.CoreLib « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: c19686db27ff86db181bf0ef3556fd2cba82f0f8 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// 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.

using System;
using System.Reflection;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using Internal.Runtime.Augments;

//==================================================================================================================
// Dependency note:
//   This class must depend only on the CustomAttribute properties that return IEnumerable<CustomAttributeData>.
//   All of the other custom attribute api route back here so calls to them will cause an infinite recursion.
//==================================================================================================================

namespace Internal.Reflection.Extensions.NonPortable
{
    public static class CustomAttributeInstantiator
    {
        //
        // Turn a CustomAttributeData into a live Attribute object. There's nothing actually non-portable about this one,
        // however, it is included as a concession to that the fact the Reflection.Execution which implements this contract
        // also needs this functionality to implement default values, and we don't want to duplicate this code.
        //
        public static Attribute Instantiate(this CustomAttributeData cad)
        {
            if (cad == null)
                return null;
            Type attributeType = cad.AttributeType;

            //
            // Find the public constructor that matches the supplied arguments.
            //
            ConstructorInfo matchingCtor = null;
            ParameterInfo[] matchingParameters = null;
            IList<CustomAttributeTypedArgument> constructorArguments = cad.ConstructorArguments;
            foreach (ConstructorInfo ctor in attributeType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
            {
                ParameterInfo[] parameters = ctor.GetParametersNoCopy();
                if (parameters.Length != constructorArguments.Count)
                    continue;
                int i;
                for (i = 0; i < parameters.Length; i++)
                {
                    Type parameterType = parameters[i].ParameterType;
                    if (!(parameterType.Equals(constructorArguments[i].ArgumentType) ||
                          parameterType.Equals(typeof(object))))
                        break;
                }
                if (i == parameters.Length)
                {
                    matchingCtor = ctor;
                    matchingParameters = parameters;
                    break;
                }
            }
            if (matchingCtor == null)
                throw new MissingMethodException(attributeType.FullName, ConstructorInfo.ConstructorName);

            //
            // Found the right constructor. Instantiate the Attribute.
            //
            int arity = matchingParameters.Length;
            object[] invokeArguments = new object[arity];
            for (int i = 0; i < arity; i++)
            {
                invokeArguments[i] = constructorArguments[i].Convert();
            }
            Attribute newAttribute = (Attribute)(matchingCtor.Invoke(invokeArguments));

            //
            // If there any named arguments, evaluate them and set the appropriate field or property.
            //
            foreach (CustomAttributeNamedArgument namedArgument in cad.NamedArguments)
            {
                object argumentValue = namedArgument.TypedValue.Convert();
                Type walk = attributeType;
                string name = namedArgument.MemberName;
                if (namedArgument.IsField)
                {
                    // Field
                    for (;;)
                    {
                        FieldInfo fieldInfo = walk.GetField(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
                        if (fieldInfo != null)
                        {
                            fieldInfo.SetValue(newAttribute, argumentValue);
                            break;
                        }
                        Type baseType = walk.BaseType;
                        if (baseType == null)
                            throw new CustomAttributeFormatException(SR.Format(SR.CustomAttributeFormat_InvalidFieldFail, name));
                        walk = baseType;
                    }
                }
                else
                {
                    // Property
                    for (;;)
                    {
                        PropertyInfo propertyInfo = walk.GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
                        if (propertyInfo != null)
                        {
                            propertyInfo.SetValue(newAttribute, argumentValue);
                            break;
                        }
                        Type baseType = walk.BaseType;
                        if (baseType == null)
                            throw new CustomAttributeFormatException(SR.Format(SR.CustomAttributeFormat_InvalidPropertyFail, name));
                        walk = baseType;
                    }
                }
            }

            return newAttribute;
        }

        //
        // Convert the argument value reported by Reflection into an actual object.
        //
        private static object Convert(this CustomAttributeTypedArgument typedArgument)
        {
            Type argumentType = typedArgument.ArgumentType;
            if (!argumentType.IsArray)
            {
                bool isEnum = argumentType.IsEnum;
                object argumentValue = typedArgument.Value;
                if (isEnum)
                    argumentValue = Enum.ToObject(argumentType, argumentValue);
                return argumentValue;
            }
            else
            {
                IList<CustomAttributeTypedArgument> typedElements = (IList<CustomAttributeTypedArgument>)(typedArgument.Value);
                if (typedElements == null)
                    return null;
                Type elementType = argumentType.GetElementType();
                Array array = Array.CreateInstance(elementType, typedElements.Count);
                for (int i = 0; i < typedElements.Count; i++)
                {
                    object elementValue = typedElements[i].Convert();
                    array.SetValue(elementValue, i);
                }
                return array;
            }
        }

        //
        // Only public instance fields can be targets of named arguments.
        //
        private static bool IsValidNamedArgumentTarget(this FieldInfo fieldInfo)
        {
            if ((fieldInfo.Attributes & (FieldAttributes.FieldAccessMask | FieldAttributes.Static | FieldAttributes.Literal)) !=
                FieldAttributes.Public)
                return false;
            return true;
        }

        //
        // Only public read/write instance properties can be targets of named arguments.
        //
        private static bool IsValidNamedArgumentTarget(this PropertyInfo propertyInfo)
        {
            MethodInfo getter = propertyInfo.GetMethod;
            MethodInfo setter = propertyInfo.SetMethod;
            if (getter == null)
                return false;
            if ((getter.Attributes & (MethodAttributes.Static | MethodAttributes.MemberAccessMask)) != MethodAttributes.Public)
                return false;
            if (setter == null)
                return false;
            if ((setter.Attributes & (MethodAttributes.Static | MethodAttributes.MemberAccessMask)) != MethodAttributes.Public)
                return false;
            return true;
        }
    }
}