summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs')
-rw-r--r--Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs544
1 files changed, 544 insertions, 0 deletions
diff --git a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs
new file mode 100644
index 00000000..0dabb5d9
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs
@@ -0,0 +1,544 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class CreateObjectVisitor : IXamlNodeVisitor
+ {
+ public CreateObjectVisitor(ILContext context)
+ {
+ Context = context;
+ Module = context.Body.Method.Module;
+ }
+
+ public ILContext Context { get; }
+
+ ModuleDefinition Module { get; }
+
+ public bool VisitChildrenFirst
+ {
+ get { return true; }
+ }
+
+ public bool StopOnDataTemplate
+ {
+ get { return true; }
+ }
+
+ public bool StopOnResourceDictionary
+ {
+ get { return false; }
+ }
+
+ public void Visit(ValueNode node, INode parentNode)
+ {
+ Context.Values[node] = node.Value;
+
+ XmlName propertyName;
+ if (SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName))
+ {
+ if (propertyName.NamespaceURI == "http://schemas.openxmlformats.org/markup-compatibility/2006" &&
+ propertyName.LocalName == "Ignorable")
+ {
+ (parentNode.IgnorablePrefixes ?? (parentNode.IgnorablePrefixes = new List<string>())).AddRange(
+ (node.Value as string).Split(','));
+ }
+ }
+ }
+
+ public void Visit(MarkupNode node, INode parentNode)
+ {
+ //At this point, all MarkupNodes are expanded to ElementNodes
+ }
+
+ public void Visit(ElementNode node, INode parentNode)
+ {
+ if (node.SkipPrefix((node.NamespaceResolver ?? parentNode.NamespaceResolver).LookupPrefix(node.NamespaceURI)))
+ return;
+
+ var typeref = node.XmlType.GetTypeReference(Module, node);
+ TypeDefinition typedef = typeref.Resolve();
+
+ 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(OpCodes.Stloc, vardef);
+ }
+ else
+ {
+ 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().FirstOrDefault(md => md.IsConstructor &&
+ !md.IsStatic &&
+ md.HasParameters &&
+ md.MatchXArguments(node, Module));
+ if (factoryCtorInfo == null)
+ {
+ throw new XamlParseException(
+ string.Format("No constructors found for {0} with matching x:Arguments", typedef.FullName), node);
+ }
+ ctorInfo = factoryCtorInfo;
+ if (!typedef.IsValueType) //for ctor'ing typedefs, we first have to ldloca before the params
+ Context.IL.Append(PushCtorXArguments(factoryCtorInfo, node));
+ }
+ else if (node.Properties.ContainsKey(XmlName.xFactoryMethod))
+ {
+ var factoryMethod = (string)(node.Properties[XmlName.xFactoryMethod] as ValueNode).Value;
+ factoryMethodInfo = typedef.AllMethods().FirstOrDefault(md => !md.IsConstructor &&
+ md.Name == factoryMethod &&
+ md.IsStatic &&
+ md.MatchXArguments(node, Module));
+ if (factoryMethodInfo == null)
+ {
+ throw new XamlParseException(
+ String.Format("No static method found for {0}::{1} ({2})", typedef.FullName, factoryMethod, null), node);
+ }
+ Context.IL.Append(PushCtorXArguments(factoryMethodInfo, 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 ==
+ "Xamarin.Forms.ParameterAttribute")));
+ }
+ if (parameterizedCtorInfo != null && ValidateCtorArguments(parameterizedCtorInfo, node))
+ {
+ ctorInfo = parameterizedCtorInfo;
+ // IL_0000: ldstr "foo"
+ Context.IL.Append(PushCtorArguments(parameterizedCtorInfo, node));
+ }
+ ctorInfo = ctorInfo ?? typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters && !md.IsStatic);
+
+ 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");
+
+ if (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)
+ {
+ //<Color>Purple</Color>
+ Context.IL.Append(vnode.PushConvertedValue(Context, typeref, new ICustomAttributeProvider[] { typedef },
+ node.PushServiceProvider(Context), 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.Import(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)
+ {
+ var factory = Module.Import(factorymethodinforef);
+ Context.IL.Emit(OpCodes.Call, factory);
+ Context.IL.Emit(OpCodes.Stloc, vardef);
+ }
+ else if (!typedef.IsValueType)
+ {
+ var ctor = Module.Import(ctorinforef);
+ // IL_0001: newobj instance void class [Xamarin.Forms.Core]Xamarin.Forms.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, Module))
+ {
+ // IL_0008: ldloca.s 1
+ // IL_000a: ldc.i4.1
+ // IL_000b: call instance void valuetype Test/Foo::'.ctor'(bool)
+
+ var ctor = Module.Import(ctorinforef);
+ Context.IL.Emit(OpCodes.Ldloca, vardef);
+ Context.IL.Append(PushCtorXArguments(factoryCtorInfo, 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.Import(typedef));
+ }
+
+ if (typeref.FullName == "Xamarin.Forms.Xaml.TypeExtension")
+ {
+ var visitor = new SetPropertiesVisitor(Context);
+ foreach (var cnode in node.Properties.Values.ToList())
+ cnode.Accept(visitor, node);
+ foreach (var cnode in node.CollectionItems)
+ cnode.Accept(visitor, node);
+
+ //As we're stripping the TypeExtension bare, keep the type if we need it later (hint: we do need it)
+ INode ntype;
+ if (!node.Properties.TryGetValue(new XmlName("", "TypeName"), out ntype))
+ ntype = node.CollectionItems[0];
+
+ var type = ((ValueNode)ntype).Value as string;
+ var namespaceuri = type.Contains(":") ? node.NamespaceResolver.LookupNamespace(type.Split(':')[0].Trim()) : "";
+ type = type.Contains(":") ? type.Split(':')[1].Trim() : type;
+ Context.TypeExtensions[node] = new XmlType(namespaceuri, type, null).GetTypeReference(Module, node);
+
+ node.Properties.Clear();
+ node.CollectionItems.Clear();
+
+ var vardefref = new VariableDefinitionReference(vardef);
+ Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node));
+ if (vardef != vardefref.VariableDefinition)
+ {
+ Context.Variables[node] = vardefref.VariableDefinition;
+ Context.Body.Variables.Add(vardefref.VariableDefinition);
+ }
+ }
+ }
+ }
+ }
+
+ 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)
+ {
+ foreach (var parameter in ctorinfo.Parameters)
+ {
+ var propname =
+ parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Xamarin.Forms.ParameterAttribute")
+ .ConstructorArguments.First()
+ .Value as string;
+ if (!enode.Properties.ContainsKey(new XmlName("", propname)))
+ return false;
+ }
+ return true;
+ }
+
+ IEnumerable<Instruction> PushCtorArguments(MethodDefinition ctorinfo, ElementNode enode)
+ {
+ foreach (var parameter in ctorinfo.Parameters)
+ {
+ var propname =
+ parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Xamarin.Forms.ParameterAttribute")
+ .ConstructorArguments.First()
+ .Value as string;
+ var node = enode.Properties[new XmlName("", propname)];
+ enode.Properties.Remove(new XmlName("", propname));
+ VariableDefinition vardef;
+ ValueNode vnode = null;
+
+ if (node is IElementNode && (vardef = Context.Variables[node as IElementNode]) != null)
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ else if ((vnode = node as ValueNode) != null)
+ {
+ foreach (var instruction in vnode.PushConvertedValue(Context,
+ parameter.ParameterType,
+ new ICustomAttributeProvider[] { parameter, parameter.ParameterType.Resolve() },
+ enode.PushServiceProvider(Context), false, true))
+ yield return instruction;
+ }
+ }
+ }
+
+ IEnumerable<Instruction> PushCtorXArguments(MethodDefinition 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)
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ else if ((vnode = arg as ValueNode) != null)
+ {
+ foreach (var instruction in vnode.PushConvertedValue(Context,
+ parameter.ParameterType,
+ new ICustomAttributeProvider[] { parameter, parameter.ParameterType.Resolve() },
+ enode.PushServiceProvider(Context), false, true))
+ yield return instruction;
+ }
+ }
+ }
+
+ static bool IsXaml2009LanguagePrimitive(IElementNode node)
+ {
+ if (node.NamespaceURI == "http://schemas.microsoft.com/winfx/2009/xaml")
+ return true;
+ if (node.NamespaceURI != "clr-namespace:System;assembly=mscorlib")
+ return false;
+ var name = node.XmlType.Name.Split(':')[1];
+ if (name == "Boolean" ||
+ name == "String" ||
+ name == "Char" ||
+ name == "Decimal" ||
+ name == "Single" ||
+ name == "Double" ||
+ name == "Byte" ||
+ name == "Int16" ||
+ name == "Int32" ||
+ name == "Int64" ||
+ name == "TimeSpan" ||
+ name == "Uri")
+ return true;
+ return false;
+ }
+
+ IEnumerable<Instruction> PushValueFromLanguagePrimitive(TypeDefinition typedef, ElementNode node)
+ {
+ 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.Boolean":
+ bool outbool;
+ if (hasValue && bool.TryParse(valueString, out outbool))
+ yield return Instruction.Create(outbool ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4_0);
+ break;
+ case "System.String":
+ yield return Instruction.Create(OpCodes.Ldstr, valueString);
+ break;
+ case "System.Object":
+ var ctorinfo =
+ Context.Body.Method.Module.TypeSystem.Object.Resolve()
+ .Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters);
+ var ctor = Context.Body.Method.Module.Import(ctorinfo);
+ yield return Instruction.Create(OpCodes.Newobj, ctor);
+ break;
+ case "System.Char":
+ char outchar;
+ if (hasValue && char.TryParse(valueString, out outchar))
+ yield return Instruction.Create(OpCodes.Ldc_I4, outchar);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x00);
+ break;
+ case "System.Decimal":
+ decimal outdecimal;
+ if (hasValue && decimal.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdecimal))
+ {
+ var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (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 Instruction.Create(OpCodes.Ldstr, valueString);
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x6f); //NumberStyles.Number
+ var getInvariantInfo =
+ Context.Body.Method.Module.Import(typeof (CultureInfo))
+ .Resolve()
+ .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture")
+ .GetMethod;
+ var getInvariant = Context.Body.Method.Module.Import(getInvariantInfo);
+ yield return Instruction.Create(OpCodes.Call, getInvariant);
+ yield return Instruction.Create(OpCodes.Ldloca, vardef);
+ var tryParseInfo =
+ Context.Body.Method.Module.Import(typeof (decimal))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 4);
+ var tryParse = Context.Body.Method.Module.Import(tryParseInfo);
+ yield return Instruction.Create(OpCodes.Call, tryParse);
+ yield return Instruction.Create(OpCodes.Pop);
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ }
+ else
+ {
+ yield return Instruction.Create(OpCodes.Ldc_I4_0);
+ var decimalctorinfo =
+ Context.Body.Method.Module.Import(typeof (decimal))
+ .Resolve()
+ .Methods.FirstOrDefault(
+ md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == "System.Int32");
+ var decimalctor = Context.Body.Method.Module.Import(decimalctorinfo);
+ yield return Instruction.Create(OpCodes.Newobj, decimalctor);
+ }
+ break;
+ case "System.Single":
+ float outfloat;
+ if (hasValue && float.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outfloat))
+ yield return Instruction.Create(OpCodes.Ldc_R4, outfloat);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_R4, 0f);
+ break;
+ case "System.Double":
+ double outdouble;
+ if (hasValue && double.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdouble))
+ yield return Instruction.Create(OpCodes.Ldc_R8, outdouble);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_R8, 0d);
+ break;
+ case "System.Byte":
+ byte outbyte;
+ if (hasValue && byte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outbyte))
+ yield return Instruction.Create(OpCodes.Ldc_I4, (int)outbyte);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x00);
+ break;
+ case "System.Int16":
+ short outshort;
+ if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outshort))
+ yield return Instruction.Create(OpCodes.Ldc_I4, outshort);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x00);
+ break;
+ case "System.Int32":
+ int outint;
+ if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outint))
+ yield return Instruction.Create(OpCodes.Ldc_I4, outint);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x00);
+ break;
+ case "System.Int64":
+ long outlong;
+ if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outlong))
+ yield return Instruction.Create(OpCodes.Ldc_I8, outlong);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I8, 0L);
+ break;
+ case "System.TimeSpan":
+ TimeSpan outspan;
+ if (hasValue && TimeSpan.TryParse(valueString, CultureInfo.InvariantCulture, out outspan))
+ {
+ var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (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 Instruction.Create(OpCodes.Ldstr, valueString);
+ var getInvariantInfo =
+ Context.Body.Method.Module.Import(typeof (CultureInfo))
+ .Resolve()
+ .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture")
+ .GetMethod;
+ var getInvariant = Context.Body.Method.Module.Import(getInvariantInfo);
+ yield return Instruction.Create(OpCodes.Call, getInvariant);
+ yield return Instruction.Create(OpCodes.Ldloca, vardef);
+ var tryParseInfo =
+ Context.Body.Method.Module.Import(typeof (TimeSpan))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 3);
+ var tryParse = Context.Body.Method.Module.Import(tryParseInfo);
+ yield return Instruction.Create(OpCodes.Call, tryParse);
+ yield return Instruction.Create(OpCodes.Pop);
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ }
+ else
+ {
+ yield return Instruction.Create(OpCodes.Ldc_I8, 0L);
+ var timespanctorinfo =
+ Context.Body.Method.Module.Import(typeof (TimeSpan))
+ .Resolve()
+ .Methods.FirstOrDefault(
+ md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == "System.Int64");
+ var timespanctor = Context.Body.Method.Module.Import(timespanctorinfo);
+ yield return Instruction.Create(OpCodes.Newobj, timespanctor);
+ }
+ break;
+ case "System.Uri":
+ Uri outuri;
+ if (hasValue && Uri.TryCreate(valueString, UriKind.RelativeOrAbsolute, out outuri))
+ {
+ var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (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 Instruction.Create(OpCodes.Ldstr, valueString);
+ yield return Instruction.Create(OpCodes.Ldc_I4, (int)UriKind.RelativeOrAbsolute);
+ yield return Instruction.Create(OpCodes.Ldloca, vardef);
+ var tryCreateInfo =
+ Context.Body.Method.Module.Import(typeof (Uri))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.Name == "TryCreate" && md.Parameters.Count == 3);
+ var tryCreate = Context.Body.Method.Module.Import(tryCreateInfo);
+ yield return Instruction.Create(OpCodes.Call, tryCreate);
+ yield return Instruction.Create(OpCodes.Pop);
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ }
+ else
+ yield return Instruction.Create(OpCodes.Ldnull);
+ break;
+ default:
+ var defaultctorinfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters);
+ if (defaultctorinfo != null)
+ {
+ var defaultctor = Context.Body.Method.Module.Import(defaultctorinfo);
+ yield return Instruction.Create(OpCodes.Newobj, defaultctor);
+ }
+ else
+ {
+ //should never happen. but if it does, this prevents corrupting the IL stack
+ yield return Instruction.Create(OpCodes.Ldnull);
+ }
+ break;
+ }
+ }
+ }
+} \ No newline at end of file