Nullable: System.Runtime.InteropServices.CustomMarshalers/WindowsRuntime (#23930)
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / src / System / Runtime / InteropServices / WindowsRuntime / CustomPropertyImpl.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 #nullable enable
6 using System.Reflection;
7
8 namespace System.Runtime.InteropServices.WindowsRuntime
9 {
10     //
11     // ICustomProperty implementation - basically a wrapper of PropertyInfo
12     //
13     internal sealed class CustomPropertyImpl : ICustomProperty
14     {
15         private PropertyInfo m_property;
16
17         //
18         // Constructor
19         //
20         public CustomPropertyImpl(PropertyInfo propertyInfo)
21         {
22             if (propertyInfo == null)
23                 throw new ArgumentNullException(nameof(propertyInfo));
24
25             m_property = propertyInfo;
26         }
27
28         //
29         // ICustomProperty interface implementation
30         //
31
32         public string Name
33         {
34             get
35             {
36                 return m_property.Name;
37             }
38         }
39
40         public bool CanRead
41         {
42             get
43             {
44                 // Return false if the getter is not public
45                 return m_property.GetGetMethod() != null;
46             }
47         }
48
49         public bool CanWrite
50         {
51             get
52             {
53                 // Return false if the setter is not public
54                 return m_property.GetSetMethod() != null;
55             }
56         }
57
58         public object GetValue(object target)
59         {
60             return InvokeInternal(target, null, true);
61         }
62
63         // Unlike normal .Net, Jupiter properties can have at most one indexer parameter. A null
64         // indexValue here means that the property has an indexer argument and its value is null.
65         public object GetValue(object target, object indexValue)
66         {
67             return InvokeInternal(target, new object[] { indexValue }, true);
68         }
69
70         public void SetValue(object target, object value)
71         {
72             InvokeInternal(target, new object[] { value }, false);
73         }
74
75         // Unlike normal .Net, Jupiter properties can have at most one indexer parameter. A null
76         // indexValue here means that the property has an indexer argument and its value is null.
77         public void SetValue(object target, object value, object indexValue)
78         {
79             InvokeInternal(target, new object[] { indexValue, value }, false);
80         }
81
82         private object InvokeInternal(object target, object[]? args, bool getValue)
83         {
84             // Forward to the right object if we are dealing with a proxy
85             if (target is IGetProxyTarget proxy)
86             {
87                 target = proxy.GetTarget();
88             }
89
90             // You can get PropertyInfo for properties with a private getter/public setter (or vice versa) 
91             // even if you pass BindingFlags.Public only. And in this case, passing binding flags to 
92             // GetValue/SetValue won't work as the default binder ignores those values
93             // Use GetGetMethod/GetSetMethod instead
94
95             // We get non-public accessors just so that we can throw the correct exception.
96             MethodInfo accessor = getValue ? m_property.GetGetMethod(true) : m_property.GetSetMethod(true);
97
98             if (accessor == null)
99                 throw new ArgumentException(getValue ? SR.Arg_GetMethNotFnd : SR.Arg_SetMethNotFnd);
100
101             if (!accessor.IsPublic)
102                 throw new MethodAccessException(
103                     SR.Format(
104                         SR.Arg_MethodAccessException_WithMethodName,
105                         accessor,
106                         accessor.DeclaringType.FullName));
107
108             RuntimeMethodInfo? rtMethod = accessor as RuntimeMethodInfo;
109             if (rtMethod == null)
110                 throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo);
111
112             // We can safely skip access check because this is only used in full trust scenarios.
113             // And we have already verified that the property accessor is public.
114             return rtMethod.Invoke(target, BindingFlags.Default, null, args, null);
115         }
116
117         public Type Type
118         {
119             get
120             {
121                 return m_property.PropertyType;
122             }
123         }
124     }
125 }