File: CreateObjectVisitor.cs
Web Access
Project: src\src\Controls\src\Build.Tasks\Controls.Build.Tasks.csproj (Microsoft.Maui.Controls.Build.Tasks)
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml;
using Microsoft.Maui.Controls.Xaml;
using Mono.Cecil;
using Mono.Cecil.Cil;
using static Mono.Cecil.Cil.Instruction;
using static Mono.Cecil.Cil.OpCodes;
 
namespace Microsoft.Maui.Controls.Build.Tasks
{
	class CreateObjectVisitor : IXamlNodeVisitor
	{
		public CreateObjectVisitor(ILContext context)
		{
			Context = context;
			Module = context.Body.Method.Module;
		}
 
		public ILContext Context { get; }
 
		ModuleDefinition Module { get; }
 
		public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
		public bool StopOnDataTemplate => true;
		public bool StopOnResourceDictionary => false;
		public bool VisitNodeOnDataTemplate => false;
		public bool SkipChildren(INode node, INode parentNode) => false;
 
		public bool IsResourceDictionary(ElementNode node)
		{
			var parentVar = Context.Variables[(IElementNode)node];
			return parentVar.VariableType.FullName == "Microsoft.Maui.Controls.ResourceDictionary"
				|| parentVar.VariableType.Resolve().BaseType?.FullName == "Microsoft.Maui.Controls.ResourceDictionary";
		}
 
		public void Visit(ValueNode node, INode parentNode)
		{
			Context.Values[node] = node.Value;
		}
 
		public void Visit(MarkupNode node, INode parentNode)
		{
			//At this point, all MarkupNodes are expanded to ElementNodes
		}
 
		public void Visit(ElementNode node, INode parentNode)
		{
			var typeref = Module.ImportReference(node.XmlType.GetTypeReference(Context.Cache, Module, node));
			TypeDefinition typedef = typeref.ResolveCached(Context.Cache);
 
			if (typeref.FullName == "Microsoft.Maui.Controls.Xaml.ArrayExtension")
			{
				var visitor = new SetPropertiesVisitor(Context);
				var children = node.Properties.Values.ToList();
				children.AddRange(node.CollectionItems);
				foreach (var cnode in children)
				{
					if (cnode is not IElementNode en)
						continue;
					foreach (var n in en.Properties.Values.ToList())
						n.Accept(visitor, cnode);
					foreach (var n in en.CollectionItems)
						n.Accept(visitor, cnode);
				}
 
				var il = new ArrayExtension().ProvideValue(node, Module, Context, out typeref);
				var vardef = new VariableDefinition(typeref);
				Context.Variables[node] = vardef;
				Context.Body.Variables.Add(vardef);
 
				Context.IL.Append(il);
				Context.IL.Emit(OpCodes.Stloc, vardef);
 
				//clean the node as it has been fully exhausted
				foreach (var prop in node.Properties)
					if (!node.SkipProperties.Contains(prop.Key))
						node.SkipProperties.Add(prop.Key);
				node.CollectionItems.Clear();
 
				return;
			}
 
			if (IsXaml2009LanguagePrimitive(node))
			{
				var vardef = new VariableDefinition(typeref);
				Context.Variables[node] = vardef;
				Context.Body.Variables.Add(vardef);
 
				Context.IL.Append(PushValueFromLanguagePrimitive(typedef, node));
				Context.IL.Emit(Stloc, vardef);
				return;
			}
 
			//if this is a MarkupExtension that can be compiled directly, compile and returns the value
			var compiledMarkupExtensionName = typeref
				.GetCustomAttribute(Context.Cache, Module, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "ProvideCompiledAttribute"))
				?.ConstructorArguments?[0].Value as string;
			Type compiledMarkupExtensionType;
			ICompiledMarkupExtension markupProvider;
			if (compiledMarkupExtensionName != null &&
				(compiledMarkupExtensionType = Type.GetType(compiledMarkupExtensionName)) != null &&
				(markupProvider = Activator.CreateInstance(compiledMarkupExtensionType) as ICompiledMarkupExtension) != null)
			{
 
				var il = markupProvider.ProvideValue(node, Module, Context, out typeref);
				typeref = Module.ImportReference(typeref);
 
				var vardef = new VariableDefinition(typeref);
				Context.Variables[node] = vardef;
				Context.Body.Variables.Add(vardef);
 
				Context.IL.Append(il);
				Context.IL.Emit(Stloc, vardef);
 
				//clean the node as it has been fully exhausted
				foreach (var prop in node.Properties)
					if (!node.SkipProperties.Contains(prop.Key))
						node.SkipProperties.Add(prop.Key);
				node.CollectionItems.Clear();
 
				return;
			}
 
			MethodDefinition factoryCtorInfo = null;
			MethodDefinition factoryMethodInfo = null;
			MethodDefinition parameterizedCtorInfo = null;
			MethodDefinition ctorInfo = null;
 
			if (node.Properties.ContainsKey(XmlName.xArguments) && !node.Properties.ContainsKey(XmlName.xFactoryMethod))
			{
				factoryCtorInfo = typedef.AllMethods(Context.Cache).FirstOrDefault(md => md.methodDef.IsConstructor &&
																			!md.methodDef.IsStatic &&
																			md.methodDef.HasParameters &&
																			md.methodDef.MatchXArguments(node, typeref, Module, Context)).methodDef;
				ctorInfo = factoryCtorInfo ?? throw new BuildException(BuildExceptionCode.ConstructorXArgsMissing, node, null, typedef.FullName);
				if (!typedef.IsValueType) //for ctor'ing typedefs, we first have to ldloca before the params
					Context.IL.Append(PushCtorXArguments(factoryCtorInfo.ResolveGenericParameters(typeref, Module), node));
			}
			else if (node.Properties.ContainsKey(XmlName.xFactoryMethod))
			{
				var factoryMethod = (string)(node.Properties[XmlName.xFactoryMethod] as ValueNode).Value;
				factoryMethodInfo = typedef.AllMethods(Context.Cache).FirstOrDefault(md => !md.methodDef.IsConstructor &&
																			  md.methodDef.Name == factoryMethod &&
																			  md.methodDef.IsStatic &&
																			  md.methodDef.MatchXArguments(node, typeref, Module, Context)).methodDef;
				if (factoryMethodInfo == null)
					throw new BuildException(BuildExceptionCode.MethodStaticMissing, node, null, typedef.FullName, factoryMethod, null);
 
				Context.IL.Append(PushCtorXArguments(factoryMethodInfo.ResolveGenericParameters(typeref, Module), node));
			}
			if (ctorInfo == null && factoryMethodInfo == null)
			{
				parameterizedCtorInfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor &&
																			 !md.IsStatic &&
																			 md.HasParameters &&
																			 md.Parameters.All(
																				 pd =>
																					 pd.CustomAttributes.Any(
																						 ca =>
																							 ca.AttributeType.FullName ==
																							 "Microsoft.Maui.Controls.ParameterAttribute")));
			}
			string missingCtorParameter = null;
			if (parameterizedCtorInfo != null && ValidateCtorArguments(parameterizedCtorInfo, node, out missingCtorParameter))
			{
				ctorInfo = parameterizedCtorInfo;
				//				IL_0000:  ldstr "foo"
				Context.IL.Append(PushCtorArguments(parameterizedCtorInfo.ResolveGenericParameters(typeref, Module), node));
			}
			ctorInfo = ctorInfo ?? typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters && !md.IsStatic);
			if (parameterizedCtorInfo != null && ctorInfo == null)
				//there was a parameterized ctor, we didn't use it
				throw new BuildException(BuildExceptionCode.PropertyMissing, node, null, missingCtorParameter, typedef.FullName);
			var ctorinforef = ctorInfo?.ResolveGenericParameters(typeref, Module);
			var factorymethodinforef = factoryMethodInfo?.ResolveGenericParameters(typeref, Module);
			var implicitOperatorref = typedef.Methods.FirstOrDefault(md =>
				md.IsPublic &&
				md.IsStatic &&
				md.IsSpecialName &&
				md.Name == "op_Implicit" && md.Parameters[0].ParameterType.FullName == "System.String");
 
			// The old Forms.Color was a struct, so the following BuildException wasn't thrown because IsValueType returned true.
			// MGColor is a class, so IsValueType is false, but there's no parameterless constructor or factory; there's a 
			// TypeConverter. So adding this bool as a check for this condition temporarily.
			bool isColor = typedef.FullName == "Microsoft.Maui.Graphics.Color";
 
			if (!isColor && !typedef.IsValueType && ctorInfo == null && factoryMethodInfo == null)
			{
				throw new BuildException(BuildExceptionCode.ConstructorDefaultMissing, node, null, typedef.FullName);
			}
 
			if (isColor || ctorinforef != null || factorymethodinforef != null || typedef.IsValueType)
			{
				VariableDefinition vardef = new VariableDefinition(typeref);
				Context.Variables[node] = vardef;
				Context.Body.Variables.Add(vardef);
 
				ValueNode vnode = null;
				if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null &&
					(vardef.VariableType.IsValueType || isColor))
				{
					//<Color>Purple</Color>
					Context.IL.Append(vnode.PushConvertedValue(Context, typeref, [typedef],
						(requiredServices) => node.PushServiceProvider(Context, requiredServices),
						false, true));
					Context.IL.Emit(OpCodes.Stloc, vardef);
				}
				else if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null &&
						 implicitOperatorref != null)
				{
					//<FileImageSource>path.png</FileImageSource>
					var implicitOperator = Module.ImportReference(implicitOperatorref);
					Context.IL.Emit(OpCodes.Ldstr, ((ValueNode)(node.CollectionItems.First())).Value as string);
					Context.IL.Emit(OpCodes.Call, implicitOperator);
					Context.IL.Emit(OpCodes.Stloc, vardef);
				}
				else if (factorymethodinforef != null)
				{
					Context.IL.Emit(OpCodes.Call, Module.ImportReference(factorymethodinforef));
					Context.IL.Emit(OpCodes.Stloc, vardef);
				}
				else if (!typedef.IsValueType)
				{
					var ctor = Module.ImportReference(ctorinforef);
					//					IL_0001:  newobj instance void class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.Button::'.ctor'()
					//					IL_0006:  stloc.0 
					Context.IL.Emit(OpCodes.Newobj, ctor);
					Context.IL.Emit(OpCodes.Stloc, vardef);
				}
				else if (ctorInfo != null && node.Properties.ContainsKey(XmlName.xArguments) &&
						 !node.Properties.ContainsKey(XmlName.xFactoryMethod) && ctorInfo.MatchXArguments(node, typeref, Module, Context))
				{
					//					IL_0008:  ldloca.s 1
					//					IL_000a:  ldc.i4.1 
					//					IL_000b:  call instance void valuetype Test/Foo::'.ctor'(bool)
 
					var ctor = Module.ImportReference(ctorinforef);
					Context.IL.Emit(OpCodes.Ldloca, vardef);
					Context.IL.Append(PushCtorXArguments(ctor, node));
					Context.IL.Emit(OpCodes.Call, ctor);
				}
				else
				{
					//					IL_0000:  ldloca.s 0
					//					IL_0002:  initobj Test/Foo
					Context.IL.Emit(OpCodes.Ldloca, vardef);
					Context.IL.Emit(OpCodes.Initobj, Module.ImportReference(typedef));
				}
			}
		}
 
		public void Visit(RootNode node, INode parentNode)
		{
			//			IL_0013:  ldarg.0 
			//			IL_0014:  stloc.3 
 
			var ilnode = (ILRootNode)node;
			var typeref = ilnode.TypeReference;
			var vardef = new VariableDefinition(typeref);
			Context.Variables[node] = vardef;
			Context.Root = vardef;
			Context.Body.Variables.Add(vardef);
			Context.IL.Emit(OpCodes.Ldarg_0);
			Context.IL.Emit(OpCodes.Stloc, vardef);
		}
 
		public void Visit(ListNode node, INode parentNode)
		{
			XmlName name;
			if (SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
				node.XmlName = name;
		}
 
		bool ValidateCtorArguments(MethodDefinition ctorinfo, ElementNode enode, out string firstMissingProperty)
		{
			firstMissingProperty = null;
			foreach (var parameter in ctorinfo.Parameters)
			{
				var propname =
					parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Microsoft.Maui.Controls.ParameterAttribute")
						.ConstructorArguments.First()
						.Value as string;
				if (!enode.Properties.ContainsKey(new XmlName("", propname)))
				{
					firstMissingProperty = propname;
					return false;
				}
			}
			return true;
		}
 
		IEnumerable<Instruction> PushCtorArguments(MethodReference ctorinfo, ElementNode enode)
		{
			foreach (var parameter in ctorinfo.Parameters)
			{
				var propname =
					parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Microsoft.Maui.Controls.ParameterAttribute")
						.ConstructorArguments.First()
						.Value as string;
				var node = enode.Properties[new XmlName("", propname)];
				if (!enode.SkipProperties.Contains(new XmlName("", propname)))
					enode.SkipProperties.Add(new XmlName("", propname));
				VariableDefinition vardef;
				ValueNode vnode = null;
 
				if (node is IElementNode && (vardef = Context.Variables[node as IElementNode]) != null)
					foreach (var instruction in vardef.LoadAs(Context.Cache, parameter.ParameterType.ResolveGenericParameters(ctorinfo), Module))
						yield return instruction;
				else if ((vnode = node as ValueNode) != null)
				{
					foreach (var instruction in vnode.PushConvertedValue(Context,
						parameter.ParameterType,
						[parameter, parameter.ParameterType.ResolveCached(Context.Cache)],
						(requiredServices) => enode.PushServiceProvider(Context, requiredServices),
						false, true))
						yield return instruction;
				}
			}
		}
 
		IEnumerable<Instruction> PushCtorXArguments(MethodReference factoryCtorInfo, ElementNode enode)
		{
			if (!enode.Properties.ContainsKey(XmlName.xArguments))
				yield break;
 
			var arguments = new List<INode>();
			var node = enode.Properties[XmlName.xArguments] as ElementNode;
			if (node != null)
				arguments.Add(node);
			var list = enode.Properties[XmlName.xArguments] as ListNode;
			if (list != null)
			{
				foreach (var n in list.CollectionItems)
					arguments.Add(n);
			}
 
			for (var i = 0; i < factoryCtorInfo.Parameters.Count; i++)
			{
				var parameter = factoryCtorInfo.Parameters[i];
				var arg = arguments[i];
				VariableDefinition vardef;
				ValueNode vnode = null;
 
				if (arg is IElementNode && (vardef = Context.Variables[arg as IElementNode]) != null)
					foreach (var instruction in vardef.LoadAs(Context.Cache, parameter.ParameterType.ResolveGenericParameters(factoryCtorInfo), Module))
						yield return instruction;
				else if ((vnode = arg as ValueNode) != null)
				{
					foreach (var instruction in vnode.PushConvertedValue(Context,
						parameter.ParameterType,
						[parameter, parameter.ParameterType.ResolveCached(Context.Cache)],
						(requiredServices) => enode.PushServiceProvider(Context, requiredServices),
						false, true))
						yield return instruction;
				}
			}
		}
 
		static bool IsXaml2009LanguagePrimitive(IElementNode node)
		{
			if (node.NamespaceURI == XamlParser.X2009Uri)
			{
				var n = node.XmlType.Name.Split(':')[1];
				return n != "Array";
			}
			if (node.NamespaceURI != "clr-namespace:System;assembly=mscorlib")
				return false;
			var name = node.XmlType.Name.Split(':')[1];
			if (name == "SByte" ||
				name == "Int16" ||
				name == "Int32" ||
				name == "Int64" ||
				name == "Byte" ||
				name == "UInt16" ||
				name == "UInt32" ||
				name == "UInt64" ||
				name == "Single" ||
				name == "Double" ||
				name == "Boolean" ||
				name == "String" ||
				name == "Char" ||
				name == "Decimal" ||
				name == "TimeSpan" ||
				name == "Uri")
				return true;
			return false;
		}
 
		IEnumerable<Instruction> PushValueFromLanguagePrimitive(TypeDefinition typedef, ElementNode node)
		{
			var module = Context.Body.Method.Module;
			var hasValue = node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
						   ((ValueNode)node.CollectionItems[0]).Value is string;
			var valueString = hasValue ? ((ValueNode)node.CollectionItems[0]).Value as string : string.Empty;
			switch (typedef.FullName)
			{
				case "System.SByte":
					if (hasValue && sbyte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out sbyte outsbyte))
						yield return Create(Ldc_I4, (int)outsbyte);
					else
						yield return Create(Ldc_I4, 0x00);
					break;
				case "System.Int16":
					if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out short outshort))
						yield return Create(Ldc_I4, outshort);
					else
						yield return Create(Ldc_I4, 0x00);
					break;
				case "System.Int32":
					if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out int outint))
						yield return Create(Ldc_I4, outint);
					else
						yield return Create(Ldc_I4, 0x00);
					break;
				case "System.Int64":
					if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out long outlong))
						yield return Create(Ldc_I8, outlong);
					else
						yield return Create(Ldc_I8, 0L);
					break;
				case "System.Byte":
					if (hasValue && byte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out byte outbyte))
						yield return Create(Ldc_I4, (int)outbyte);
					else
						yield return Create(Ldc_I4, 0x00);
					break;
				case "System.UInt16":
					if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out short outushort))
						yield return Create(Ldc_I4, outushort);
					else
						yield return Create(Ldc_I4, 0x00);
					break;
				case "System.UInt32":
					if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out int outuint))
						yield return Create(Ldc_I4, outuint);
					else
						yield return Create(Ldc_I4, 0x00);
					break;
				case "System.UInt64":
					if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out long outulong))
						yield return Create(Ldc_I8, outulong);
					else
						yield return Create(Ldc_I8, 0L);
					break;
				case "System.Boolean":
					if (hasValue && bool.TryParse(valueString, out bool outbool))
						yield return Create(outbool ? Ldc_I4_1 : Ldc_I4_0);
					else
						yield return Create(Ldc_I4_0);
					break;
				case "System.String":
					yield return Create(Ldstr, valueString);
					break;
				case "System.Object":
					var ctorinfo =
						module.TypeSystem.Object.ResolveCached(Context.Cache)
							.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters);
					var ctor = module.ImportReference(ctorinfo);
					yield return Create(Newobj, ctor);
					break;
				case "System.Char":
					if (hasValue && char.TryParse(valueString, out char outchar))
						yield return Create(Ldc_I4, outchar);
					else
						yield return Create(Ldc_I4, 0x00);
					break;
				case "System.Decimal":
					decimal outdecimal;
					if (hasValue && decimal.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdecimal))
					{
						var vardef = new VariableDefinition(module.ImportReference(Context.Cache, ("mscorlib", "System", "Decimal")));
						Context.Body.Variables.Add(vardef);
						//Use an extra temp var so we can push the value to the stack, just like other cases
						//					IL_0003:  ldstr "adecimal"
						//					IL_0008:  ldc.i4.s 0x6f
						//					IL_000a:  call class [mscorlib]System.Globalization.CultureInfo class [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture()
						//					IL_000f:  ldloca.s 0
						//					IL_0011:  call bool valuetype [mscorlib]System.Decimal::TryParse(string, valuetype [mscorlib]System.Globalization.NumberStyles, class [mscorlib]System.IFormatProvider, [out] valuetype [mscorlib]System.Decimal&)
						//					IL_0016:  pop
						yield return Create(Ldstr, valueString);
						yield return Create(Ldc_I4, 0x6f); //NumberStyles.Number
						yield return Create(Call, module.ImportPropertyGetterReference(Context.Cache, ("mscorlib", "System.Globalization", "CultureInfo"),
																				propertyName: "InvariantCulture",
																				isStatic: true));
						yield return Create(Ldloca, vardef);
						yield return Create(Call, module.ImportMethodReference(Context.Cache, ("mscorlib", "System", "Decimal"),
																			   methodName: "TryParse",
																			   parameterTypes: new[] {
																			   ("mscorlib", "System", "String"),
																			   ("mscorlib", "System.Globalization", "NumberStyles"),
																			   ("mscorlib", "System", "IFormatProvider"),
																			   ("mscorlib", "System", "Decimal"),
																			   },
																			   isStatic: true));
						yield return Create(Pop);
						yield return Create(Ldloc, vardef);
					}
					else
					{
						yield return Create(Ldc_I4_0);
						yield return Create(Newobj, module.ImportCtorReference(Context.Cache, ("mscorlib", "System", "Decimal"), parameterTypes: new[] { ("mscorlib", "System", "Int32") }));
					}
					break;
				case "System.Single":
					if (hasValue && float.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out float outfloat))
						yield return Create(Ldc_R4, outfloat);
					else
						yield return Create(Ldc_R4, 0f);
					break;
				case "System.Double":
					if (hasValue && double.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out double outdouble))
						yield return Create(Ldc_R8, outdouble);
					else
						yield return Create(Ldc_R8, 0d);
					break;
				case "System.TimeSpan":
					if (hasValue && TimeSpan.TryParse(valueString, CultureInfo.InvariantCulture, out TimeSpan outspan))
					{
						var vardef = new VariableDefinition(module.ImportReference(Context.Cache, ("mscorlib", "System", "TimeSpan")));
						Context.Body.Variables.Add(vardef);
						//Use an extra temp var so we can push the value to the stack, just like other cases
						yield return Create(Ldstr, valueString);
						yield return Create(Call, module.ImportPropertyGetterReference(Context.Cache, ("mscorlib", "System.Globalization", "CultureInfo"),
																					   propertyName: "InvariantCulture", isStatic: true));
						yield return Create(Ldloca, vardef);
						yield return Create(Call, module.ImportMethodReference(Context.Cache, ("mscorlib", "System", "TimeSpan"),
																			   methodName: "TryParse",
																			   parameterTypes: new[] {
																			   ("mscorlib", "System", "String"),
																			   ("mscorlib", "System", "IFormatProvider"),
																			   ("mscorlib", "System", "TimeSpan"),
																			   },
																			   isStatic: true));
						yield return Create(Pop);
						yield return Create(Ldloc, vardef);
					}
					else
					{
						yield return Create(Ldc_I8, 0L);
						yield return Create(Newobj, module.ImportCtorReference(Context.Cache, ("mscorlib", "System", "TimeSpan"), parameterTypes: new[] { ("mscorlib", "System", "Int64") }));
					}
					break;
				case "System.Uri":
					if (hasValue && Uri.TryCreate(valueString, UriKind.RelativeOrAbsolute, out _))
					{
						var vardef = new VariableDefinition(module.ImportReference(Context.Cache, ("System", "System", "Uri")));
						Context.Body.Variables.Add(vardef);
						//Use an extra temp var so we can push the value to the stack, just like other cases
						yield return Create(Ldstr, valueString);
						yield return Create(Ldc_I4, (int)UriKind.RelativeOrAbsolute);
						yield return Create(Ldloca, vardef);
						yield return Create(Call, module.ImportMethodReference(Context.Cache, ("System", "System", "Uri"),
																			   methodName: "TryCreate",
																			   parameterTypes: new[] {
																			   ("mscorlib", "System", "String"),
																			   ("System", "System", "UriKind"),
																			   ("System", "System", "Uri"),
																			   },
																			   isStatic: true));
						yield return Create(Pop);
						yield return Create(Ldloc, vardef);
					}
					else
						yield return Create(Ldnull);
					break;
				default:
					var defaultCtor = module.ImportCtorReference(Context.Cache, typedef, parameterTypes: null);
					if (defaultCtor != null)
						yield return Create(Newobj, defaultCtor);
					else
					{
						//should never happen. but if it does, this prevents corrupting the IL stack
						yield return Create(Ldnull);
					}
					break;
			}
		}
	}
}