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 using System;
6 using System.Collections;
7 using System.Diagnostics;
8 using System.Reflection;
9 using System.Reflection.Emit;
10 using System.Text.RegularExpressions;
11 using System.Xml.Extensions;
12 
13 #if !FEATURE_SERIALIZATION_UAPAOT
14 namespace System.Xml.Serialization
15 {
16     internal class SourceInfo
17     {
18         //a[ia]
19         //((global::System.Xml.Serialization.XmlSerializerNamespaces)p[0])
20         private static Regex s_regex = new Regex("([(][(](?<t>[^)]+)[)])?(?<a>[^[]+)[[](?<ia>.+)[]][)]?");
21         //((global::Microsoft.CFx.Test.Common.TypeLibrary.IXSType_9)o), @"IXSType_9", @"", true, true);
22         private static Regex s_regex2 = new Regex("[(][(](?<cast>[^)]+)[)](?<arg>[^)]+)[)]");
23 
24         private static readonly Lazy<MethodInfo> s_iListGetItemMethod = new Lazy<MethodInfo>(
25             () =>
26             {
27                 return typeof(IList).GetMethod(
28                     "get_Item",
29                     new Type[] { typeof(Int32) }
30                 );
31             });
32 
33         public string Source;
34         public readonly string Arg;
35         public readonly MemberInfo MemberInfo;
36         public readonly Type Type;
37         public readonly CodeGenerator ILG;
38 
SourceInfo(string source, string arg, MemberInfo memberInfo, Type type, CodeGenerator ilg)39         public SourceInfo(string source, string arg, MemberInfo memberInfo, Type type, CodeGenerator ilg)
40         {
41             this.Source = source;
42             this.Arg = arg ?? source;
43             this.MemberInfo = memberInfo;
44             this.Type = type;
45             this.ILG = ilg;
46         }
47 
CastTo(TypeDesc td)48         public SourceInfo CastTo(TypeDesc td)
49         {
50             return new SourceInfo("((" + td.CSharpName + ")" + Source + ")", Arg, MemberInfo, td.Type, ILG);
51         }
52 
LoadAddress(Type elementType)53         public void LoadAddress(Type elementType)
54         {
55             InternalLoad(elementType, asAddress: true);
56         }
57 
Load(Type elementType)58         public void Load(Type elementType)
59         {
60             InternalLoad(elementType);
61         }
62 
InternalLoad(Type elementType, bool asAddress = false)63         private void InternalLoad(Type elementType, bool asAddress = false)
64         {
65             Match match = s_regex.Match(Arg);
66             if (match.Success)
67             {
68                 object varA = ILG.GetVariable(match.Groups["a"].Value);
69                 Type varType = ILG.GetVariableType(varA);
70                 object varIA = ILG.GetVariable(match.Groups["ia"].Value);
71                 if (varType.IsArray)
72                 {
73                     ILG.Load(varA);
74                     ILG.Load(varIA);
75                     Type eType = varType.GetElementType();
76                     if (CodeGenerator.IsNullableGenericType(eType))
77                     {
78                         ILG.Ldelema(eType);
79                         ConvertNullableValue(eType, elementType);
80                     }
81                     else
82                     {
83                         if (eType.IsValueType)
84                         {
85                             ILG.Ldelema(eType);
86                             if (!asAddress)
87                             {
88                                 ILG.Ldobj(eType);
89                             }
90                         }
91                         else
92                             ILG.Ldelem(eType);
93                         if (elementType != null)
94                             ILG.ConvertValue(eType, elementType);
95                     }
96                 }
97                 else
98                 {
99                     ILG.Load(varA);
100                     ILG.Load(varIA);
101                     MethodInfo get_Item = varType.GetMethod(
102                         "get_Item",
103                         CodeGenerator.InstanceBindingFlags,
104                         new Type[] { typeof(Int32) }
105                         );
106 
107                     if (get_Item == null && typeof(IList).IsAssignableFrom(varType))
108                     {
109                         get_Item = s_iListGetItemMethod.Value;
110                     }
111 
112                     Debug.Assert(get_Item != null);
113                     ILG.Call(get_Item);
114                     Type eType = get_Item.ReturnType;
115                     if (CodeGenerator.IsNullableGenericType(eType))
116                     {
117                         LocalBuilder localTmp = ILG.GetTempLocal(eType);
118                         ILG.Stloc(localTmp);
119                         ILG.Ldloca(localTmp);
120                         ConvertNullableValue(eType, elementType);
121                     }
122                     else if ((elementType != null) && !(eType.IsAssignableFrom(elementType) || elementType.IsAssignableFrom(eType)))
123                     {
124                         throw new CodeGeneratorConversionException(eType, elementType, asAddress, "IsNotAssignableFrom");
125                     }
126                     else
127                     {
128                         Convert(eType, elementType, asAddress);
129                     }
130                 }
131             }
132             else if (Source == "null")
133             {
134                 ILG.Load(null);
135             }
136             else
137             {
138                 object var;
139                 Type varType;
140                 if (Arg.StartsWith("o.@", StringComparison.Ordinal) || MemberInfo != null)
141                 {
142                     var = ILG.GetVariable(Arg.StartsWith("o.@", StringComparison.Ordinal) ? "o" : Arg);
143                     varType = ILG.GetVariableType(var);
144                     if (varType.IsValueType)
145                         ILG.LoadAddress(var);
146                     else
147                         ILG.Load(var);
148                 }
149                 else
150                 {
151                     var = ILG.GetVariable(Arg);
152                     varType = ILG.GetVariableType(var);
153 
154                     if (CodeGenerator.IsNullableGenericType(varType) &&
155                         varType.GetGenericArguments()[0] == elementType)
156                     {
157                         ILG.LoadAddress(var);
158                         ConvertNullableValue(varType, elementType);
159                     }
160                     else
161                     {
162                         if (asAddress)
163                             ILG.LoadAddress(var);
164                         else
165                             ILG.Load(var);
166                     }
167                 }
168 
169                 if (MemberInfo != null)
170                 {
171                     Type memberType = (MemberInfo is FieldInfo) ?
172                         ((FieldInfo)MemberInfo).FieldType : ((PropertyInfo)MemberInfo).PropertyType;
173                     if (CodeGenerator.IsNullableGenericType(memberType))
174                     {
175                         ILG.LoadMemberAddress(MemberInfo);
176                         ConvertNullableValue(memberType, elementType);
177                     }
178                     else
179                     {
180                         ILG.LoadMember(MemberInfo);
181                         Convert(memberType, elementType, asAddress);
182                     }
183                 }
184                 else
185                 {
186                     match = s_regex2.Match(Source);
187                     if (match.Success)
188                     {
189                         Debug.Assert(match.Groups["arg"].Value == Arg);
190                         Debug.Assert(match.Groups["cast"].Value == CodeIdentifier.GetCSharpName(Type));
191                         if (asAddress)
192                             ILG.ConvertAddress(varType, Type);
193                         else
194                             ILG.ConvertValue(varType, Type);
195                         varType = Type;
196                     }
197                     Convert(varType, elementType, asAddress);
198                 }
199             }
200         }
201 
Convert(Type sourceType, Type targetType, bool asAddress)202         private void Convert(Type sourceType, Type targetType, bool asAddress)
203         {
204             if (targetType != null)
205             {
206                 if (asAddress)
207                     ILG.ConvertAddress(sourceType, targetType);
208                 else
209                     ILG.ConvertValue(sourceType, targetType);
210             }
211         }
212 
ConvertNullableValue(Type nullableType, Type targetType)213         private void ConvertNullableValue(Type nullableType, Type targetType)
214         {
215             System.Diagnostics.Debug.Assert(targetType == nullableType || targetType.IsAssignableFrom(nullableType.GetGenericArguments()[0]));
216             if (targetType != nullableType)
217             {
218                 MethodInfo Nullable_get_Value = nullableType.GetMethod(
219                     "get_Value",
220                     CodeGenerator.InstanceBindingFlags,
221                     CodeGenerator.EmptyTypeArray
222                     );
223                 ILG.Call(Nullable_get_Value);
224                 if (targetType != null)
225                 {
226                     ILG.ConvertValue(Nullable_get_Value.ReturnType, targetType);
227                 }
228             }
229         }
230 
operator string(SourceInfo source)231         public static implicit operator string (SourceInfo source)
232         {
233             return source.Source;
234         }
235 
operator !=(SourceInfo a, SourceInfo b)236         public static bool operator !=(SourceInfo a, SourceInfo b)
237         {
238             if ((object)a != null)
239                 return !a.Equals(b);
240             return (object)b != null;
241         }
242 
operator ==(SourceInfo a, SourceInfo b)243         public static bool operator ==(SourceInfo a, SourceInfo b)
244         {
245             if ((object)a != null)
246                 return a.Equals(b);
247             return (object)b == null;
248         }
249 
Equals(object obj)250         public override bool Equals(object obj)
251         {
252             if (obj == null)
253                 return Source == null;
254             SourceInfo info = obj as SourceInfo;
255             if (info != null)
256                 return Source == info.Source;
257             return false;
258         }
259 
GetHashCode()260         public override int GetHashCode()
261         {
262             return (Source == null) ? 0 : Source.GetHashCode();
263         }
264     }
265 }
266 #endif
267