[NUI][XamlBuild] Set ExitXaml() as internal method.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.XamlBuild / src / public / XamlBuild / CreateObjectVisitor.cs
1 /*
2  * Copyright(c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.Collections.Generic;
19 using System.Globalization;
20 using System.Linq;
21 using Mono.Cecil;
22 using Mono.Cecil.Cil;
23 using Tizen.NUI.Xaml;
24 using System.Xml;
25
26 using static Mono.Cecil.Cil.Instruction;
27 using static Mono.Cecil.Cil.OpCodes;
28
29 namespace Tizen.NUI.Xaml.Build.Tasks
30 {
31     class CreateObjectVisitor : IXamlNodeVisitor
32     {
33         public CreateObjectVisitor(ILContext context)
34         {
35             Context = context;
36             Module = context.Body.Method.Module;
37         }
38
39         public ILContext Context { get; }
40
41         ModuleDefinition Module { get; }
42
43         public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
44         public bool StopOnDataTemplate => true;
45         public bool StopOnResourceDictionary => false;
46         public bool VisitNodeOnDataTemplate => false;
47         public bool SkipChildren(INode node, INode parentNode) => false;
48
49         public bool IsResourceDictionary(ElementNode node)
50         {
51             var parentVar = Context.Variables[(IElementNode)node];
52             return parentVar.VariableType.FullName == "Tizen.NUI.Binding.ResourceDictionary"
53                 || parentVar.VariableType.Resolve().BaseType?.FullName == "Tizen.NUI.Binding.ResourceDictionary";
54         }
55
56         public void Visit(ValueNode node, INode parentNode)
57         {
58             Context.Values[node] = node.Value;
59         }
60
61         public void Visit(MarkupNode node, INode parentNode)
62         {
63             //At this point, all MarkupNodes are expanded to ElementNodes
64         }
65
66         public void Visit(ElementNode node, INode parentNode)
67         {
68             var typeref = Module.ImportReference(node.XmlType.GetTypeReference(XmlTypeExtensions.ModeOfGetType.Both, Module, node));
69             TypeDefinition typedef = typeref.ResolveCached();
70
71             if (IsXaml2009LanguagePrimitive(node))
72             {
73                 var vardef = new VariableDefinition(typeref);
74                 Context.Variables[node] = vardef;
75                 Context.Body.Variables.Add(vardef);
76
77                 Context.IL.Append(PushValueFromLanguagePrimitive(typeref, node));
78                 Context.IL.Emit(OpCodes.Stloc, vardef);
79                 return;
80             }
81
82             //if this is a MarkupExtension that can be compiled directly, compile and returns the value
83             var compiledMarkupExtensionName = typeref
84                 .GetCustomAttribute(Module, (XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "ProvideCompiledAttribute"))
85                 ?.ConstructorArguments?[0].Value as string;
86             Type compiledMarkupExtensionType;
87             ICompiledMarkupExtension markupProvider;
88             if (compiledMarkupExtensionName != null &&
89                 (compiledMarkupExtensionType = Type.GetType(compiledMarkupExtensionName)) != null &&
90                 (markupProvider = Activator.CreateInstance(compiledMarkupExtensionType) as ICompiledMarkupExtension) != null)
91             {
92
93                 var il = markupProvider.ProvideValue(node, Module, Context, out typeref);
94                 typeref = Module.ImportReference(typeref);
95
96                 var vardef = new VariableDefinition(typeref);
97                 Context.Variables[node] = vardef;
98                 Context.Body.Variables.Add(vardef);
99
100                 Context.IL.Append(il);
101                 Context.IL.Emit(OpCodes.Stloc, vardef);
102
103                 //clean the node as it has been fully exhausted
104                 foreach (var prop in node.Properties)
105                     if (!node.SkipProperties.Contains(prop.Key))
106                         node.SkipProperties.Add(prop.Key);
107                 node.CollectionItems.Clear();
108                 return;
109             }
110
111             MethodDefinition factoryCtorInfo = null;
112             MethodDefinition factoryMethodInfo = null;
113             TypeDefinition ownerTypeOfFactoryMethod = null;
114             MethodDefinition parameterizedCtorInfo = null;
115             MethodDefinition ctorInfo = null;
116
117             if (node.Properties.ContainsKey(XmlName.xArguments) && !node.Properties.ContainsKey(XmlName.xFactoryMethod))
118             {
119                 factoryCtorInfo = typedef.AllMethods().FirstOrDefault(md => md.IsConstructor &&
120                                                                             !md.IsStatic &&
121                                                                             md.HasParameters &&
122                                                                             md.MatchXArguments(node, typeref, Module, Context));
123                 if (factoryCtorInfo == null)
124                 {
125                     throw new XamlParseException(
126                         string.Format("No constructors found for {0} with matching x:Arguments", typedef.FullName), node);
127                 }
128                 ctorInfo = factoryCtorInfo;
129                 if (!typedef.IsValueType) //for ctor'ing typedefs, we first have to ldloca before the params
130                     Context.IL.Append(PushCtorXArguments(factoryCtorInfo, node));
131             }
132             else if (node.Properties.ContainsKey(XmlName.xFactoryMethod))
133             {
134                 var factoryMethod = (string)(node.Properties[XmlName.xFactoryMethod] as ValueNode).Value;
135                 factoryMethodInfo = typedef.AllMethods().FirstOrDefault(md => !md.IsConstructor &&
136                                                                               md.Name == factoryMethod &&
137                                                                               md.IsStatic &&
138                                                                               md.MatchXArguments(node, typeref, Module, Context));
139                 if (factoryMethodInfo == null)
140                 {
141                     var typeExtensionRef = Module.ImportReference(node.XmlType.GetTypeReference(XmlTypeExtensions.ModeOfGetType.OnlyGetTypeExtension, Module, node));
142                     typeExtensionRef = typeExtensionRef?.ResolveCached();
143
144                     if (null != typeExtensionRef?.Resolve())
145                     {
146                         factoryMethodInfo = typeExtensionRef.Resolve().AllMethods().FirstOrDefault(md => !md.IsConstructor &&
147                                                                               md.Name == factoryMethod &&
148                                                                               md.IsStatic &&
149                                                                               md.MatchXArguments(node, typeref, Module, Context));
150
151                         if (null != factoryMethod)
152                         {
153                             ownerTypeOfFactoryMethod = typeExtensionRef.ResolveCached();
154                         }
155                     }
156                 }
157                 else
158                 {
159                     ownerTypeOfFactoryMethod = typedef;
160
161                 }
162
163                 if (factoryMethodInfo == null)
164                 {
165                     throw new XamlParseException(
166                         String.Format("No static method found for {0}::{1} ({2})", typedef.FullName, factoryMethod, null), node);
167                 }
168                 Context.IL.Append(PushCtorXArguments(factoryMethodInfo, node));
169             }
170             if (ctorInfo == null && factoryMethodInfo == null)
171             {
172                 parameterizedCtorInfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor &&
173                                                                              !md.IsStatic &&
174                                                                              md.HasParameters &&
175                                                                              md.Parameters.All(
176                                                                                  pd =>
177                                                                                      pd.CustomAttributes.Any(
178                                                                                          ca =>
179                                                                                              ca.AttributeType.FullName ==
180                                                                                              "Tizen.NUI.Binding.ParameterAttribute")));
181             }
182             string missingCtorParameter = null;
183             if (parameterizedCtorInfo != null && ValidateCtorArguments(parameterizedCtorInfo, node, out missingCtorParameter))
184             {
185                 ctorInfo = parameterizedCtorInfo;
186                 //                IL_0000:  ldstr "foo"
187                 Context.IL.Append(PushCtorArguments(parameterizedCtorInfo, node));
188             }
189
190             ctorInfo = ctorInfo ?? typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters && !md.IsStatic);
191
192             if (null == ctorInfo && null == factoryMethodInfo)
193             {
194                 foreach (var method in typedef.Methods)
195                 {
196                     if (method.IsConstructor && !method.IsStatic)
197                     {
198                         bool areAllParamsDefault = true;
199
200                         foreach (var param in method.Parameters)
201                         {
202                             if (!param.HasDefault)
203                             {
204                                 areAllParamsDefault = false;
205                                 break;
206                             }
207                         }
208
209                         if (areAllParamsDefault)
210                         {
211                             if (null == ctorInfo)
212                             {
213                                 ctorInfo = method;
214                             }
215                             else
216                             {
217                                 throw new XamlParseException($"{typedef.FullName} has more than one constructor which params are all default.", node);
218                             }
219                         }
220                     }
221                 }
222
223                 if (null == ctorInfo)
224                 {
225                     if (!typedef.IsValueType)
226                     {
227                         throw new XamlParseException($"{typedef.FullName} has no constructor which params are all default.", node);
228                     }
229                 }
230                 else
231                 {
232                     factoryCtorInfo = ctorInfo;
233
234                     if (!typedef.IsValueType) //for ctor'ing typedefs, we first have to ldloca before the params
235                     {
236                         Context.IL.Append(PushCtorDefaultArguments(factoryCtorInfo, node));
237                     }
238                 }
239             }
240
241             if (parameterizedCtorInfo != null && ctorInfo == null)
242                 //there was a parameterized ctor, we didn't use it
243                 throw new XamlParseException($"The Property '{missingCtorParameter}' is required to create a '{typedef.FullName}' object.", node);
244             var ctorinforef = ctorInfo?.ResolveGenericParameters(typeref, Module);
245
246             var factorymethodinforef = factoryMethodInfo?.ResolveGenericParameters(ownerTypeOfFactoryMethod, Module);
247             var implicitOperatorref = typedef.Methods.FirstOrDefault(md =>
248                 md.IsPublic &&
249                 md.IsStatic &&
250                 md.IsSpecialName &&
251                 md.Name == "op_Implicit" && md.Parameters[0].ParameterType.FullName == "System.String");
252
253             if (ctorinforef != null || factorymethodinforef != null || typedef.IsValueType)
254             {
255                 VariableDefinition vardef = new VariableDefinition(typeref);
256                 Context.Variables[node] = vardef;
257                 Context.Body.Variables.Add(vardef);
258
259                 ValueNode vnode = null;
260                 if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null &&
261                     vardef.VariableType.IsValueType)
262                 {
263                     //<Color>Purple</Color>
264                     Context.IL.Append(vnode.PushConvertedValue(Context, typeref, new ICustomAttributeProvider[] { typedef },
265                         node.PushServiceProvider(Context), false, true));
266                     Context.IL.Emit(OpCodes.Stloc, vardef);
267                 }
268                 else if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null &&
269                          implicitOperatorref != null)
270                 {
271                     //<FileImageSource>path.png</FileImageSource>
272                     var implicitOperator = Module.ImportReference(implicitOperatorref);
273                     Context.IL.Emit(OpCodes.Ldstr, ((ValueNode)(node.CollectionItems.First())).Value as string);
274                     Context.IL.Emit(OpCodes.Call, implicitOperator);
275                     Context.IL.Emit(OpCodes.Stloc, vardef);
276                 }
277                 else if (factorymethodinforef != null)
278                 {
279                     Context.IL.Emit(OpCodes.Call, Module.ImportReference(factorymethodinforef));
280                     Context.IL.Emit(OpCodes.Stloc, vardef);
281                 }
282                 else if (!typedef.IsValueType)
283                 {
284                     var ctor = Module.ImportReference(ctorinforef);
285                     //                    IL_0001:  newobj instance void class [Tizen.NUI.Xaml.UIComponents]Tizen.NUI.Xaml.UIComponents.Button::'.ctor'()
286                     //                    IL_0006:  stloc.0 
287                     bool isConvertValue = false;
288                     if (node.CollectionItems.Count == 1 && node.CollectionItems.First() is ValueNode valueNode)
289                     {
290                         if (valueNode.CanConvertValue(Context.Module, typeref, (TypeReference)null))
291                         {
292                             var converterType = valueNode.GetConverterType(new ICustomAttributeProvider[] { typeref.Resolve() });
293                             if (null != converterType)
294                             {
295                                 isConvertValue = true;
296                                 Context.IL.Append(vnode.PushConvertedValue(Context, typeref, new ICustomAttributeProvider[] { typedef },
297                                     node.PushServiceProvider(Context), false, true));
298                             }
299                         }
300                     }
301                     
302                     if (false == isConvertValue)
303                     {
304                         Context.IL.Emit(OpCodes.Newobj, ctor);
305                     }
306                     Context.IL.Emit(OpCodes.Stloc, vardef);
307                 }
308                 else if (ctorInfo != null && node.Properties.ContainsKey(XmlName.xArguments) &&
309                          !node.Properties.ContainsKey(XmlName.xFactoryMethod) && ctorInfo.MatchXArguments(node, typeref, Module, Context))
310                 {
311                     //                    IL_0008:  ldloca.s 1
312                     //                    IL_000a:  ldc.i4.1 
313                     //                    IL_000b:  call instance void valuetype Test/Foo::'.ctor'(bool)
314
315                     var ctor = Module.ImportReference(ctorinforef);
316                     Context.IL.Emit(OpCodes.Ldloca, vardef);
317                     Context.IL.Append(PushCtorXArguments(factoryCtorInfo, node));
318                     Context.IL.Emit(OpCodes.Call, ctor);
319                 }
320                 else
321                 {
322                     //                    IL_0000:  ldloca.s 0
323                     //                    IL_0002:  initobj Test/Foo
324                     Context.IL.Emit(OpCodes.Ldloca, vardef);
325                     Context.IL.Emit(OpCodes.Initobj, Module.ImportReference(typedef));
326                 }
327
328                 if (null != XamlCTask.BaseTypeDefiniation && typedef.InheritsFromOrImplements(XamlCTask.BaseTypeDefiniation))
329                 {
330                     var field = XamlCTask.BaseTypeDefiniation.Properties.SingleOrDefault(fd => fd.Name == "IsCreateByXaml");
331                     if (field == null)
332                         return;
333
334                     ValueNode value = new ValueNode("true", node.NamespaceResolver);
335                     Set(Context.Variables[node], "IsCreateByXaml", value, null);
336                 }
337
338                 if (typeref.FullName == "Tizen.NUI.Xaml.ArrayExtension")
339                 {
340                     var visitor = new SetPropertiesVisitor(Context);
341                     foreach (var cnode in node.Properties.Values.ToList())
342                         cnode.Accept(visitor, node);
343                     foreach (var cnode in node.CollectionItems)
344                         cnode.Accept(visitor, node);
345
346                     markupProvider = new ArrayExtension();
347
348                     var il = markupProvider.ProvideValue(node, Module, Context, out typeref);
349
350                     vardef = new VariableDefinition(typeref);
351                     Context.Variables[node] = vardef;
352                     Context.Body.Variables.Add(vardef);
353
354                     Context.IL.Append(il);
355                     Context.IL.Emit(OpCodes.Stloc, vardef);
356
357                     //clean the node as it has been fully exhausted
358                     foreach (var prop in node.Properties)
359                         if (!node.SkipProperties.Contains(prop.Key))
360                             node.SkipProperties.Add(prop.Key);
361
362                     return;
363                 }
364             }
365         }
366
367         private void Set(VariableDefinition parent, string localName, INode node, IXmlLineInfo iXmlLineInfo)
368         {
369             var module = Context.Body.Method.Module;
370             TypeReference declaringTypeReference;
371             var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
372             var propertySetter = property.SetMethod;
373
374             module.ImportReference(parent.VariableType.ResolveCached());
375             var propertySetterRef = module.ImportReference(module.ImportReference(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
376             propertySetterRef.ImportTypes(module);
377             var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
378             var valueNode = node as ValueNode;
379             var elementNode = node as IElementNode;
380
381             if (parent.VariableType.IsValueType)
382                 Context.IL.Emit(OpCodes.Ldloca, parent);
383             else
384                 Context.IL.Emit(OpCodes.Ldloc, parent);
385
386             if (valueNode != null)
387             {
388                 foreach (var instruction in valueNode.PushConvertedValue(Context, propertyType, new ICustomAttributeProvider[] { property, propertyType.ResolveCached() }, valueNode.PushServiceProvider(Context, propertyRef: property), false, true))
389                 {
390                     Context.IL.Append(instruction);
391                 }
392
393                 if (parent.VariableType.IsValueType)
394                     Context.IL.Emit(OpCodes.Call, propertySetterRef);
395                 else
396                     Context.IL.Emit(OpCodes.Callvirt, propertySetterRef);
397             }
398         }
399
400         public void Visit(RootNode node, INode parentNode)
401         {
402             //            IL_0013:  ldarg.0 
403             //            IL_0014:  stloc.3 
404
405             var ilnode = (ILRootNode)node;
406             var typeref = ilnode.TypeReference;
407             var vardef = new VariableDefinition(typeref);
408             Context.Variables[node] = vardef;
409             Context.Root = vardef;
410             Context.Body.Variables.Add(vardef);
411             Context.IL.Emit(OpCodes.Ldarg_0);
412             Context.IL.Emit(OpCodes.Stloc, vardef);
413         }
414
415         public void Visit(ListNode node, INode parentNode)
416         {
417             XmlName name;
418             if (SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
419                 node.XmlName = name;
420         }
421
422         bool ValidateCtorArguments(MethodDefinition ctorinfo, ElementNode enode, out string firstMissingProperty)
423         {
424             firstMissingProperty = null;
425             foreach (var parameter in ctorinfo.Parameters)
426             {
427                 var propname =
428                     parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Tizen.NUI.Binding.ParameterAttribute")
429                         .ConstructorArguments.First()
430                         .Value as string;
431                 if (!enode.Properties.ContainsKey(new XmlName("", propname)))
432                 {
433                     firstMissingProperty = propname;
434                     return false;
435                 }
436             }
437             return true;
438         }
439
440         IEnumerable<Instruction> PushCtorArguments(MethodDefinition ctorinfo, ElementNode enode)
441         {
442             foreach (var parameter in ctorinfo.Parameters)
443             {
444                 var propname =
445                     parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Tizen.NUI.Binding.ParameterAttribute")
446                         .ConstructorArguments.First()
447                         .Value as string;
448                 var node = enode.Properties[new XmlName("", propname)];
449                 if (!enode.SkipProperties.Contains(new XmlName("", propname)))
450                     enode.SkipProperties.Add(new XmlName("", propname));
451                 VariableDefinition vardef;
452                 ValueNode vnode = null;
453
454                 if (node is IElementNode && (vardef = Context.Variables[node as IElementNode]) != null)
455                     yield return Instruction.Create(OpCodes.Ldloc, vardef);
456                 else if ((vnode = node as ValueNode) != null)
457                 {
458                     foreach (var instruction in vnode.PushConvertedValue(Context,
459                         parameter.ParameterType,
460                         new ICustomAttributeProvider[] { parameter, parameter.ParameterType.ResolveCached() },
461                         enode.PushServiceProvider(Context), false, true))
462                         yield return instruction;
463                 }
464             }
465         }
466
467         IEnumerable<Instruction> PushCtorDefaultArguments(MethodDefinition factoryCtorInfo, ElementNode enode)
468         {
469             var arguments = new List<INode>();
470
471             for (var i = 0; i < factoryCtorInfo.Parameters.Count; i++)
472             {
473                 var parameter = factoryCtorInfo.Parameters[i];
474
475                 ValueNode arg = new ValueNode(parameter.Constant?.ToString(), enode.NamespaceResolver);
476
477                 if (arg != null)
478                 {
479                     foreach (var instruction in arg.PushConvertedValue(Context,
480                         parameter.ParameterType,
481                         new ICustomAttributeProvider[] { parameter, parameter.ParameterType.ResolveCached() },
482                         enode.PushServiceProvider(Context), false, true))
483                         yield return instruction;
484                 }
485             }
486         }
487
488
489         IEnumerable<Instruction> PushCtorXArguments(MethodDefinition factoryCtorInfo, ElementNode enode)
490         {
491             if (!enode.Properties.ContainsKey(XmlName.xArguments))
492                 yield break;
493
494             var arguments = new List<INode>();
495             var node = enode.Properties[XmlName.xArguments] as ElementNode;
496             if (node != null)
497             {
498                 node.Accept(new SetPropertiesVisitor(Context, true), null);
499                 arguments.Add(node);
500             }
501
502             var list = enode.Properties[XmlName.xArguments] as ListNode;
503             if (list != null)
504             {
505                 foreach (var n in list.CollectionItems)
506                     arguments.Add(n);
507             }
508
509             for (var i = 0; i < arguments.Count; i++)
510             {
511                 var parameter = factoryCtorInfo.Parameters[i];
512                 var arg = arguments[i];
513                 VariableDefinition vardef;
514                 ValueNode vnode = null;
515
516                 if (arg is IElementNode && (vardef = Context.Variables[arg as IElementNode]) != null)
517                     yield return Instruction.Create(OpCodes.Ldloc, vardef);
518                 else if ((vnode = arg as ValueNode) != null)
519                 {
520                     foreach (var instruction in vnode.PushConvertedValue(Context,
521                         parameter.ParameterType,
522                         new ICustomAttributeProvider[] { parameter, parameter.ParameterType.ResolveCached() },
523                         enode.PushServiceProvider(Context), false, true))
524                         yield return instruction;
525                 }
526             }
527
528             for (var i = arguments.Count; i < factoryCtorInfo.Parameters.Count; i++)
529             {
530                 var parameter = factoryCtorInfo.Parameters[i];
531                 var arg = new ValueNode(parameter.Constant?.ToString(), node?.NamespaceResolver);
532
533                 foreach (var instruction in arg.PushConvertedValue(Context,
534                         parameter.ParameterType,
535                         new ICustomAttributeProvider[] { parameter, parameter.ParameterType.ResolveCached() },
536                         enode.PushServiceProvider(Context), false, true))
537                     yield return instruction;
538             }
539         }
540
541         static bool IsXaml2009LanguagePrimitive(IElementNode node)
542         {
543             if (node.NamespaceURI == XamlParser.X2009Uri)
544             {
545                 var n = node.XmlType.Name.Split(':')[1];
546                 return n != "Array" && n != "Nullable";
547             }
548             if (node.NamespaceURI != "clr-namespace:System;assembly=mscorlib")
549                 return false;
550             var name = node.XmlType.Name.Split(':')[1];
551             if (name == "SByte" ||
552                 name == "Int16" ||
553                 name == "Int32" ||
554                 name == "Int64" ||
555                 name == "Byte" ||
556                 name == "UInt16" ||
557                 name == "UInt32" ||
558                 name == "UInt64" ||
559                 name == "Single" ||
560                 name == "Double" ||
561                 name == "Boolean" ||
562                 name == "String" ||
563                 name == "Char" ||
564                 name == "Decimal" ||
565                 name == "TimeSpan" ||
566                 name == "Uri")
567                 return true;
568             return false;
569         }
570
571         IEnumerable<Instruction> PushValueFromLanguagePrimitive(TypeReference typeRef, ElementNode node)
572         {
573             var module = Context.Body.Method.Module;
574             var hasValue = node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
575                            ((ValueNode)node.CollectionItems[0]).Value is string;
576             var valueString = hasValue ? ((ValueNode)node.CollectionItems[0]).Value as string : string.Empty;
577             switch (typeRef.FullName)
578             {
579                 case "System.SByte":
580                     if (hasValue && sbyte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out sbyte outsbyte))
581                         yield return Create(Ldc_I4, (int)outsbyte);
582                     else
583                         yield return Create(Ldc_I4, 0x00);
584                     break;
585                 case "System.Int16":
586                     if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out short outshort))
587                         yield return Create(Ldc_I4, outshort);
588                     else
589                         yield return Create(Ldc_I4, 0x00);
590                     break;
591                 case "System.Int32":
592                     if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out int outint))
593                         yield return Create(Ldc_I4, outint);
594                     else
595                         yield return Create(Ldc_I4, 0x00);
596                     break;
597                 case "System.Int64":
598                     if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out long outlong))
599                         yield return Create(Ldc_I8, outlong);
600                     else
601                         yield return Create(Ldc_I8, 0L);
602                     break;
603                 case "System.Byte":
604                     if (hasValue && byte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out byte outbyte))
605                         yield return Create(Ldc_I4, (int)outbyte);
606                     else
607                         yield return Create(Ldc_I4, 0x00);
608                     break;
609                 case "System.UInt16":
610                     if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out short outushort))
611                         yield return Create(Ldc_I4, outushort);
612                     else
613                         yield return Create(Ldc_I4, 0x00);
614                     break;
615                 case "System.UInt32":
616                     if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out int outuint))
617                         yield return Create(Ldc_I4, outuint);
618                     else
619                         yield return Create(Ldc_I4, 0x00);
620                     break;
621                 case "System.UInt64":
622                     if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out long outulong))
623                         yield return Create(Ldc_I8, outulong);
624                     else
625                         yield return Create(Ldc_I8, 0L);
626                     break;
627                 case "System.Boolean":
628                     if (hasValue && bool.TryParse(valueString, out bool outbool))
629                         yield return Create(outbool ? Ldc_I4_1 : Ldc_I4_0);
630                     else
631                         yield return Create(Ldc_I4_0);
632                     break;
633                 case "System.String":
634                     yield return Create(Ldstr, valueString);
635                     break;
636                 case "System.Object":
637                     var ctorinfo =
638                         module.TypeSystem.Object.ResolveCached()
639                             .Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters);
640                     var ctor = module.ImportReference(ctorinfo);
641                     yield return Create(Newobj, ctor);
642                     break;
643                 case "System.Char":
644                     if (hasValue && char.TryParse(valueString, out char outchar))
645                         yield return Create(Ldc_I4, outchar);
646                     else
647                         yield return Create(Ldc_I4, 0x00);
648                     break;
649                 case "System.Decimal":
650                     decimal outdecimal;
651                     if (hasValue && decimal.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdecimal))
652                     {
653                         var vardef = new VariableDefinition(module.ImportReference(("mscorlib", "System", "Decimal")));
654                         Context.Body.Variables.Add(vardef);
655                         //Use an extra temp var so we can push the value to the stack, just like other cases
656                         //                    IL_0003:  ldstr "adecimal"
657                         //                    IL_0008:  ldc.i4.s 0x6f
658                         //                    IL_000a:  call class [mscorlib]System.Globalization.CultureInfo class [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture()
659                         //                    IL_000f:  ldloca.s 0
660                         //                    IL_0011:  call bool valuetype [mscorlib]System.Decimal::TryParse(string, valuetype [mscorlib]System.Globalization.NumberStyles, class [mscorlib]System.IFormatProvider, [out] valuetype [mscorlib]System.Decimal&)
661                         //                    IL_0016:  pop
662                         yield return Create(Ldstr, valueString);
663                         yield return Create(Ldc_I4, 0x6f); //NumberStyles.Number
664                         yield return Create(Call, module.ImportPropertyGetterReference(("mscorlib", "System.Globalization", "CultureInfo"),
665                                                                                 propertyName: "InvariantCulture",
666                                                                                 isStatic: true));
667                         yield return Create(Ldloca, vardef);
668                         yield return Create(Call, module.ImportMethodReference(("mscorlib", "System", "Decimal"),
669                                                                                methodName: "TryParse",
670                                                                                parameterTypes: new[] {
671                                                                                ("mscorlib", "System", "String"),
672                                                                                ("mscorlib", "System.Globalization", "NumberStyles"),
673                                                                                ("mscorlib", "System", "IFormatProvider"),
674                                                                                ("mscorlib", "System", "Decimal"),
675                                                                                },
676                                                                                isStatic: true));
677                         yield return Create(Pop);
678                         yield return Create(Ldloc, vardef);
679                     }
680                     else
681                     {
682                         yield return Create(Ldc_I4_0);
683                         yield return Create(Newobj, module.ImportCtorReference(("mscorlib", "System", "Decimal"), parameterTypes: new[] { ("mscorlib", "System", "Int32") }));
684                     }
685                     break;
686                 case "System.Single":
687                     if (hasValue && float.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out float outfloat))
688                         yield return Create(Ldc_R4, outfloat);
689                     else
690                         yield return Create(Ldc_R4, 0f);
691                     break;
692                 case "System.Double":
693                     if (hasValue && double.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out double outdouble))
694                         yield return Create(Ldc_R8, outdouble);
695                     else
696                         yield return Create(Ldc_R8, 0d);
697                     break;
698                 case "System.TimeSpan":
699                     if (hasValue && TimeSpan.TryParse(valueString, CultureInfo.InvariantCulture, out TimeSpan outspan))
700                     {
701                         var vardef = new VariableDefinition(module.ImportReference(("mscorlib", "System", "TimeSpan")));
702                         Context.Body.Variables.Add(vardef);
703                         //Use an extra temp var so we can push the value to the stack, just like other cases
704                         yield return Create(Ldstr, valueString);
705                         yield return Create(Call, module.ImportPropertyGetterReference(("mscorlib", "System.Globalization", "CultureInfo"),
706                                                                                        propertyName: "InvariantCulture", isStatic: true));
707                         yield return Create(Ldloca, vardef);
708                         yield return Create(Call, module.ImportMethodReference(("mscorlib", "System", "TimeSpan"),
709                                                                                methodName: "TryParse",
710                                                                                parameterTypes: new[] {
711                                                                                ("mscorlib", "System", "String"),
712                                                                                ("mscorlib", "System", "IFormatProvider"),
713                                                                                ("mscorlib", "System", "TimeSpan"),
714                                                                                },
715                                                                                isStatic: true));
716                         yield return Create(Pop);
717                         yield return Create(Ldloc, vardef);
718                     }
719                     else
720                     {
721                         yield return Create(Ldc_I8, 0L);
722                         yield return Create(Newobj, module.ImportCtorReference(("mscorlib", "System", "TimeSpan"), parameterTypes: new[] { ("mscorlib", "System", "Int64") }));
723                     }
724                     break;
725                 case "System.Uri":
726                     if (hasValue && Uri.TryCreate(valueString, UriKind.RelativeOrAbsolute, out Uri outuri))
727                     {
728                         var vardef = new VariableDefinition(module.ImportReference(("System", "System", "Uri")));
729                         Context.Body.Variables.Add(vardef);
730                         //Use an extra temp var so we can push the value to the stack, just like other cases
731                         yield return Create(Ldstr, valueString);
732                         yield return Create(Ldc_I4, (int)UriKind.RelativeOrAbsolute);
733                         yield return Create(Ldloca, vardef);
734                         yield return Create(Call, module.ImportMethodReference(("System", "System", "Uri"),
735                                                                                methodName: "TryCreate",
736                                                                                parameterTypes: new[] {
737                                                                                ("mscorlib", "System", "String"),
738                                                                                ("System", "System", "UriKind"),
739                                                                                ("System", "System", "Uri"),
740                                                                                },
741                                                                                isStatic: true));
742                         yield return Create(Pop);
743                         yield return Create(Ldloc, vardef);
744                     }
745                     else
746                         yield return Create(Ldnull);
747                     break;
748                 default:
749                     var defaultCtor = module.ImportCtorReference(typeRef, parameterTypes: null);
750                     if (defaultCtor != null)
751                         yield return Create(Newobj, defaultCtor);
752                     else
753                     {
754                         //should never happen. but if it does, this prevents corrupting the IL stack
755                         yield return Create(Ldnull);
756                     }
757                     break;
758             }
759         }
760     }
761 }
762