maui-linux/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs

681 строка
27 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml;
namespace Xamarin.Forms.Build.Tasks
{
class SetPropertiesVisitor : IXamlNodeVisitor
{
static int dtcount;
readonly IList<XmlName> skips = new List<XmlName>
{
XmlName.xKey,
XmlName.xTypeArguments,
XmlName.xArguments,
XmlName.xFactoryMethod,
XmlName.xName
};
public SetPropertiesVisitor(ILContext context, bool stopOnResourceDictionary = false)
{
Context = context;
Module = context.Body.Method.Module;
StopOnResourceDictionary = stopOnResourceDictionary;
}
public ILContext Context { get; }
ModuleDefinition Module { get; }
public bool VisitChildrenFirst
{
get { return true; }
}
public bool StopOnDataTemplate
{
get { return true; }
}
public bool StopOnResourceDictionary { get; }
public void Visit(ValueNode node, INode parentNode)
{
//TODO support Label text as element
XmlName propertyName;
if (!TryGetPropertyName(node, parentNode, out propertyName))
{
if (!IsCollectionItem(node, parentNode))
return;
string contentProperty;
if (!Context.Variables.ContainsKey((IElementNode)parentNode))
return;
var parentVar = Context.Variables[(IElementNode)parentNode];
if ((contentProperty = GetContentProperty(parentVar.VariableType)) != null)
propertyName = new XmlName(((IElementNode)parentNode).NamespaceURI, contentProperty);
else
return;
}
if (skips.Contains(propertyName))
return;
if (node.SkipPrefix((node.NamespaceResolver ?? parentNode.NamespaceResolver)?.LookupPrefix(propertyName.NamespaceURI)))
return;
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(','));
return;
}
SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node);
}
public void Visit(MarkupNode node, INode parentNode)
{
}
public void Visit(ElementNode node, INode parentNode)
{
if (node.SkipPrefix((node.NamespaceResolver ?? parentNode.NamespaceResolver)?.LookupPrefix(node.NamespaceURI)))
return;
//if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
var vardef = Context.Variables[node];
var vardefref = new VariableDefinitionReference(vardef);
Context.IL.Append(ProvideValue(vardefref, Context, Module, node));
if (vardef != vardefref.VariableDefinition)
{
vardef = vardefref.VariableDefinition;
Context.Body.Variables.Add(vardef);
Context.Variables[node] = vardef;
}
XmlName propertyName;
if (TryGetPropertyName(node, parentNode, out propertyName))
{
if (skips.Contains(propertyName))
return;
if (propertyName == XmlName._CreateContent)
SetDataTemplate((IElementNode)parentNode, node, Context, node);
else
SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node);
}
else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
{
// Collection element, implicit content, or implicit collection element.
string contentProperty;
var parentVar = Context.Variables[(IElementNode)parentNode];
if (parentVar.VariableType.ImplementsInterface(Module.Import(typeof (IEnumerable))))
{
var elementType = parentVar.VariableType;
if (elementType.FullName != "Xamarin.Forms.ResourceDictionary")
{
var adderTuple = elementType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).First();
var adderRef = Module.Import(adderTuple.Item1);
adderRef = Module.Import(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
Context.IL.Emit(OpCodes.Ldloc, parentVar);
Context.IL.Emit(OpCodes.Ldloc, vardef);
Context.IL.Emit(OpCodes.Callvirt, adderRef);
if (adderRef.ReturnType.FullName != "System.Void")
Context.IL.Emit(OpCodes.Pop);
}
}
else if ((contentProperty = GetContentProperty(parentVar.VariableType)) != null)
{
var name = new XmlName(node.NamespaceURI, contentProperty);
if (skips.Contains(name))
return;
SetPropertyValue(Context.Variables[(IElementNode)parentNode], name, node, Context, node);
}
}
else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
{
// IL_000d: ldloc.2
// IL_000e: callvirt instance class [mscorlib]System.Collections.Generic.IList`1<!0> class [Xamarin.Forms.Core]Xamarin.Forms.Layout`1<class [Xamarin.Forms.Core]Xamarin.Forms.View>::get_Children()
// IL_0013: ldloc.0
// IL_0014: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1<class [Xamarin.Forms.Core]Xamarin.Forms.View>::Add(!0)
var parentList = (ListNode)parentNode;
var parent = Context.Variables[((IElementNode)parentNode.Parent)];
if (skips.Contains(parentList.XmlName))
return;
var elementType = parent.VariableType;
var localname = parentList.XmlName.LocalName;
GetNameAndTypeRef(ref elementType, parentList.XmlName.NamespaceURI, ref localname, Context, node);
TypeReference propertyDeclaringType;
var property = elementType.GetProperty(pd => pd.Name == localname, out propertyDeclaringType);
MethodDefinition propertyGetter;
if (property != null && (propertyGetter = property.GetMethod) != null && propertyGetter.IsPublic)
{
var propertyGetterRef = Module.Import(propertyGetter);
propertyGetterRef = Module.Import(propertyGetterRef.ResolveGenericParameters(propertyDeclaringType, Module));
var propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(propertyDeclaringType);
var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).First();
var adderRef = Module.Import(adderTuple.Item1);
adderRef = Module.Import(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
Context.IL.Emit(OpCodes.Ldloc, parent);
Context.IL.Emit(OpCodes.Callvirt, propertyGetterRef);
Context.IL.Emit(OpCodes.Ldloc, vardef);
Context.IL.Emit(OpCodes.Callvirt, adderRef);
if (adderRef.ReturnType.FullName != "System.Void")
Context.IL.Emit(OpCodes.Pop);
}
}
}
public void Visit(RootNode node, INode parentNode)
{
}
public void Visit(ListNode node, INode parentNode)
{
}
public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
{
name = default(XmlName);
var parentElement = parentNode as IElementNode;
if (parentElement == null)
return false;
foreach (var kvp in parentElement.Properties)
{
if (kvp.Value != node)
continue;
name = kvp.Key;
return true;
}
return false;
}
static bool IsCollectionItem(INode node, INode parentNode)
{
var parentList = parentNode as IListNode;
if (parentList == null)
return false;
return parentList.CollectionItems.Contains(node);
}
static string GetContentProperty(TypeReference typeRef)
{
var typeDef = typeRef.Resolve();
var attributes = typeDef.CustomAttributes;
var attr =
attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
if (attr != null)
return attr.ConstructorArguments[0].Value as string;
if (typeDef.BaseType == null)
return null;
return GetContentProperty(typeDef.BaseType);
}
public static IEnumerable<Instruction> ProvideValue(VariableDefinitionReference vardefref, ILContext context,
ModuleDefinition module, ElementNode node)
{
GenericInstanceType markupExtension;
IList<TypeReference> genericArguments;
if (vardefref.VariableDefinition.VariableType.FullName == "Xamarin.Forms.Xaml.ArrayExtension" &&
vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
out markupExtension, out genericArguments))
{
var markExt = markupExtension.Resolve();
var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
var provideValue = module.Import(provideValueInfo);
provideValue =
module.Import(provideValue.MakeGeneric(markupExtension.GenericArguments.Select(tr => module.Import(tr)).ToArray()));
var typeNode = node.Properties[new XmlName("", "Type")];
TypeReference arrayTypeRef;
if (context.TypeExtensions.TryGetValue(typeNode, out arrayTypeRef))
vardefref.VariableDefinition = new VariableDefinition(module.Import(arrayTypeRef.MakeArrayType()));
else
vardefref.VariableDefinition = new VariableDefinition(module.Import(genericArguments.First()));
yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
foreach (var instruction in node.PushServiceProvider(context))
yield return instruction;
yield return Instruction.Create(OpCodes.Callvirt, provideValue);
if (arrayTypeRef != null)
yield return Instruction.Create(OpCodes.Castclass, module.Import(arrayTypeRef.MakeArrayType()));
yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
}
else if (vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
out markupExtension, out genericArguments))
{
var markExt = markupExtension.Resolve();
var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
var provideValue = module.Import(provideValueInfo);
provideValue =
module.Import(provideValue.MakeGeneric(markupExtension.GenericArguments.Select(tr => module.Import(tr)).ToArray()));
vardefref.VariableDefinition = new VariableDefinition(module.Import(genericArguments.First()));
yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
foreach (var instruction in node.PushServiceProvider(context))
yield return instruction;
yield return Instruction.Create(OpCodes.Callvirt, provideValue);
yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
}
else if (context.Variables[node].VariableType.ImplementsInterface(module.Import(typeof (IMarkupExtension))))
{
var markExt = module.Import(typeof (IMarkupExtension)).Resolve();
var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
var provideValue = module.Import(provideValueInfo);
vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
foreach (var instruction in node.PushServiceProvider(context))
yield return instruction;
yield return Instruction.Create(OpCodes.Callvirt, provideValue);
yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
}
else if (context.Variables[node].VariableType.ImplementsInterface(module.Import(typeof (IValueProvider))))
{
var markExt = module.Import(typeof (IValueProvider)).Resolve();
var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
var provideValue = module.Import(provideValueInfo);
vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
foreach (var instruction in node.PushServiceProvider(context))
yield return instruction;
yield return Instruction.Create(OpCodes.Callvirt, provideValue);
yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
}
}
public static void SetPropertyValue(VariableDefinition parent, XmlName propertyName, INode valueNode,
ILContext context, IXmlLineInfo iXmlLineInfo)
{
var elementType = parent.VariableType;
var localName = propertyName.LocalName;
var module = context.Body.Method.Module;
var br = Instruction.Create(OpCodes.Nop);
TypeReference declaringTypeReference;
var handled = false;
//If it's an attached BP, update elementType and propertyName
var attached = GetNameAndTypeRef(ref elementType, propertyName.NamespaceURI, ref localName, context, iXmlLineInfo);
//If the target is an event, connect
// IL_0007: ldloc.0
// IL_0008: ldarg.0
// IL_0009: ldftn instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
// IL_000f: newobj instance void class [mscorlib]System.EventHandler::'.ctor'(object, native int)
// IL_0014: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.Button::add_Clicked(class [mscorlib]System.EventHandler)
var eventinfo = elementType.GetEvent(ed => ed.Name == localName);
if (eventinfo != null)
{
var value = ((ValueNode)valueNode).Value;
context.IL.Emit(OpCodes.Ldloc, parent);
if (context.Root is VariableDefinition)
context.IL.Emit(OpCodes.Ldloc, context.Root as VariableDefinition);
else if (context.Root is FieldDefinition)
{
context.IL.Emit(OpCodes.Ldarg_0);
context.IL.Emit(OpCodes.Ldfld, context.Root as FieldDefinition);
}
else
throw new InvalidProgramException();
var declaringType = context.Body.Method.DeclaringType;
if (declaringType.IsNested)
declaringType = declaringType.DeclaringType;
var handler = declaringType.AllMethods().FirstOrDefault(md => md.Name == value as string);
if (handler == null)
{
throw new XamlParseException(
string.Format("EventHandler \"{0}\" not found in type \"{1}\"", value, context.Body.Method.DeclaringType.FullName),
iXmlLineInfo);
}
context.IL.Emit(OpCodes.Ldftn, handler);
//FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
var ctor = module.Import(eventinfo.EventType.Resolve().GetConstructors().First());
ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
context.IL.Emit(OpCodes.Newobj, module.Import(ctor));
context.IL.Emit(OpCodes.Callvirt, module.Import(eventinfo.AddMethod));
return;
}
FieldReference bpRef = elementType.GetField(fd => fd.Name == localName + "Property" && fd.IsStatic && fd.IsPublic,
out declaringTypeReference);
if (bpRef != null)
{
bpRef = module.Import(bpRef.ResolveGenericParameters(declaringTypeReference));
bpRef.FieldType = module.Import(bpRef.FieldType);
}
//If Value is DynamicResource, SetDynamicResource
VariableDefinition varValue;
if (bpRef != null && valueNode is IElementNode &&
context.Variables.TryGetValue(valueNode as IElementNode, out varValue) &&
varValue.VariableType.FullName == typeof (DynamicResource).FullName)
{
var setDynamicResource =
module.Import(typeof (IDynamicResourceHandler)).Resolve().Methods.First(m => m.Name == "SetDynamicResource");
var getKey = typeof (DynamicResource).GetProperty("Key").GetMethod;
context.IL.Emit(OpCodes.Ldloc, parent);
context.IL.Emit(OpCodes.Ldsfld, bpRef);
context.IL.Emit(OpCodes.Ldloc, varValue);
context.IL.Emit(OpCodes.Callvirt, module.Import(getKey));
context.IL.Emit(OpCodes.Callvirt, module.Import(setDynamicResource));
return;
}
//If Value is a BindingBase and target is a BP, SetBinding
if (bpRef != null && valueNode is IElementNode &&
context.Variables.TryGetValue(valueNode as IElementNode, out varValue) &&
varValue.VariableType.InheritsFromOrImplements(module.Import(typeof (BindingBase))))
{
//TODO: check if parent is a BP
var setBinding = typeof (BindableObject).GetMethod("SetBinding",
new[] { typeof (BindableProperty), typeof (BindingBase) });
context.IL.Emit(OpCodes.Ldloc, parent);
context.IL.Emit(OpCodes.Ldsfld, bpRef);
context.IL.Emit(OpCodes.Ldloc, varValue);
context.IL.Emit(OpCodes.Callvirt, module.Import(setBinding));
return;
}
//If it's a BP, SetValue ()
// IL_0007: ldloc.0
// IL_0008: ldsfld class [Xamarin.Forms.Core]Xamarin.Forms.BindableProperty [Xamarin.Forms.Core]Xamarin.Forms.Label::TextProperty
// IL_000d: ldstr "foo"
// IL_0012: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.BindableObject::SetValue(class [Xamarin.Forms.Core]Xamarin.Forms.BindableProperty, object)
if (bpRef != null)
{
//TODO: check if parent is a BP
var setValue = typeof (BindableObject).GetMethod("SetValue", new[] { typeof (BindableProperty), typeof (object) });
if (valueNode is ValueNode)
{
context.IL.Emit(OpCodes.Ldloc, parent);
context.IL.Emit(OpCodes.Ldsfld, bpRef);
context.IL.Append(((ValueNode)valueNode).PushConvertedValue(context, bpRef, valueNode.PushServiceProvider(context),
true, false));
context.IL.Emit(OpCodes.Callvirt, module.Import(setValue));
return;
}
if (valueNode is IElementNode)
{
var getPropertyReturnType = module.Import(typeof (BindableProperty).GetProperty("ReturnType").GetGetMethod());
//FIXME: this should check for inheritance too
var isInstanceOfType = module.Import(typeof (Type).GetMethod("IsInstanceOfType", new[] { typeof (object) }));
var brfalse = Instruction.Create(OpCodes.Nop);
context.IL.Emit(OpCodes.Ldsfld, bpRef);
context.IL.Emit(OpCodes.Call, getPropertyReturnType);
context.IL.Emit(OpCodes.Ldloc, context.Variables[valueNode as IElementNode]);
if (context.Variables[valueNode as IElementNode].VariableType.IsValueType)
context.IL.Emit(OpCodes.Box, context.Variables[valueNode as IElementNode].VariableType);
context.IL.Emit(OpCodes.Callvirt, isInstanceOfType);
context.IL.Emit(OpCodes.Brfalse, brfalse);
context.IL.Emit(OpCodes.Ldloc, parent);
context.IL.Emit(OpCodes.Ldsfld, bpRef);
context.IL.Emit(OpCodes.Ldloc, context.Variables[(IElementNode)valueNode]);
if (context.Variables[valueNode as IElementNode].VariableType.IsValueType)
context.IL.Emit(OpCodes.Box, context.Variables[valueNode as IElementNode].VariableType);
context.IL.Emit(OpCodes.Callvirt, module.Import(setValue));
context.IL.Emit(OpCodes.Br, br);
context.IL.Append(brfalse);
handled = true;
}
}
//If it's a property, set it
// IL_0007: ldloc.0
// IL_0008: ldstr "foo"
// IL_000d: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.Label::set_Text(string)
PropertyDefinition property = elementType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
MethodDefinition propertySetter;
if (property != null && (propertySetter = property.SetMethod) != null && propertySetter.IsPublic)
{
module.Import(elementType.Resolve());
var propertySetterRef =
module.Import(module.Import(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
propertySetterRef.ImportTypes(module);
var propertyType = property.ResolveGenericPropertyType(declaringTypeReference);
ValueNode vnode = null;
if ((vnode = valueNode as ValueNode) != null)
{
context.IL.Emit(OpCodes.Ldloc, parent);
context.IL.Append(vnode.PushConvertedValue(context,
propertyType,
new ICustomAttributeProvider[] { propertyType.Resolve() },
valueNode.PushServiceProvider(context), false, true));
context.IL.Emit(OpCodes.Callvirt, propertySetterRef);
context.IL.Append(br);
return;
}
if (valueNode is IElementNode)
{
var vardef = context.Variables[(ElementNode)valueNode];
var implicitOperators =
vardef.VariableType.GetMethods(md => md.IsPublic && md.IsStatic && md.IsSpecialName && md.Name == "op_Implicit",
module).ToList();
MethodReference implicitOperator = null;
if (implicitOperators.Any())
{
foreach (var op in implicitOperators)
{
var cast = op.Item1;
var opDeclTypeRef = op.Item2;
var castDef = module.Import(cast).ResolveGenericParameters(opDeclTypeRef, module);
var returnType = castDef.ReturnType;
if (returnType.IsGenericParameter)
returnType = ((GenericInstanceType)opDeclTypeRef).GenericArguments[((GenericParameter)returnType).Position];
if (returnType.FullName == propertyType.FullName &&
cast.Parameters[0].ParameterType.Name == vardef.VariableType.Name)
{
implicitOperator = castDef;
break;
}
}
}
//TODO replace latest check by a runtime type check
if (implicitOperator != null || vardef.VariableType.InheritsFromOrImplements(propertyType) ||
propertyType.FullName == "System.Object" || vardef.VariableType.FullName == "System.Object")
{
context.IL.Emit(OpCodes.Ldloc, parent);
context.IL.Emit(OpCodes.Ldloc, vardef);
if (implicitOperator != null)
{
// IL_000f: call !0 class [Xamarin.Forms.Core]Xamarin.Forms.OnPlatform`1<bool>::op_Implicit(class [Xamarin.Forms.Core]Xamarin.Forms.OnPlatform`1<!0>)
context.IL.Emit(OpCodes.Call, module.Import(implicitOperator));
}
else if (!vardef.VariableType.IsValueType && propertyType.IsValueType)
context.IL.Emit(OpCodes.Unbox_Any, module.Import(propertyType));
else if (vardef.VariableType.IsValueType && propertyType.FullName == "System.Object")
context.IL.Emit(OpCodes.Box, vardef.VariableType);
context.IL.Emit(OpCodes.Callvirt, propertySetterRef);
context.IL.Append(br);
return;
}
handled = true;
}
}
//If it's an already initialized property, add to it
MethodDefinition propertyGetter;
//TODO: check if property is assigned
if (property != null && (propertyGetter = property.GetMethod) != null && propertyGetter.IsPublic)
{
var propertyGetterRef = module.Import(propertyGetter);
propertyGetterRef = module.Import(propertyGetterRef.ResolveGenericParameters(declaringTypeReference, module));
var propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
//TODO check md.Parameters[0] type
var adderTuple =
propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
if (adderTuple != null)
{
var adderRef = module.Import(adderTuple.Item1);
adderRef = module.Import(adderRef.ResolveGenericParameters(adderTuple.Item2, module));
if (valueNode is IElementNode)
{
context.IL.Emit(OpCodes.Ldloc, parent);
context.IL.Emit(OpCodes.Callvirt, propertyGetterRef);
context.IL.Emit(OpCodes.Ldloc, context.Variables[(ElementNode)valueNode]);
context.IL.Emit(OpCodes.Callvirt, adderRef);
if (adderRef.ReturnType.FullName != "System.Void")
context.IL.Emit(OpCodes.Pop);
context.IL.Append(br);
return;
}
}
}
if (!handled)
{
throw new XamlParseException(string.Format("No property, bindable property, or event found for '{0}'", localName),
iXmlLineInfo);
}
context.IL.Append(br);
}
static bool GetNameAndTypeRef(ref TypeReference elementType, string namespaceURI, ref string localname,
ILContext context, IXmlLineInfo lineInfo)
{
var dotIdx = localname.IndexOf('.');
if (dotIdx > 0)
{
var typename = localname.Substring(0, dotIdx);
localname = localname.Substring(dotIdx + 1);
elementType = new XmlType(namespaceURI, typename, null).GetTypeReference(context.Body.Method.Module, lineInfo);
return true;
}
return false;
}
static void SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext parentContext,
IXmlLineInfo xmlLineInfo)
{
var parentVar = parentContext.Variables[parentNode];
//Push the DataTemplate to the stack, for setting the template
parentContext.IL.Emit(OpCodes.Ldloc, parentVar);
//Create nested class
// .class nested private auto ansi sealed beforefieldinit '<Main>c__AnonStorey0'
// extends [mscorlib]System.Object
var module = parentContext.Body.Method.Module;
var anonType = new TypeDefinition(
null,
"<" + parentContext.Body.Method.Name + ">_anonXamlCDataTemplate_" + dtcount++,
TypeAttributes.BeforeFieldInit |
TypeAttributes.Sealed |
TypeAttributes.NestedPrivate)
{
BaseType = module.TypeSystem.Object
};
parentContext.Body.Method.DeclaringType.NestedTypes.Add(anonType);
var ctor = anonType.AddDefaultConstructor();
var loadTemplate = new MethodDefinition("LoadDataTemplate",
MethodAttributes.Assembly | MethodAttributes.HideBySig,
module.TypeSystem.Object);
anonType.Methods.Add(loadTemplate);
var parentValues = new FieldDefinition("parentValues", FieldAttributes.Assembly, module.Import(typeof (object[])));
anonType.Fields.Add(parentValues);
TypeReference rootType = null;
var vdefRoot = parentContext.Root as VariableDefinition;
if (vdefRoot != null)
rootType = vdefRoot.VariableType;
var fdefRoot = parentContext.Root as FieldDefinition;
if (fdefRoot != null)
rootType = fdefRoot.FieldType;
var root = new FieldDefinition("root", FieldAttributes.Assembly, rootType);
anonType.Fields.Add(root);
//Fill the loadTemplate Body
var templateIl = loadTemplate.Body.GetILProcessor();
templateIl.Emit(OpCodes.Nop);
var templateContext = new ILContext(templateIl, loadTemplate.Body, parentValues)
{
Root = root
};
node.Accept(new CreateObjectVisitor(templateContext), null);
node.Accept(new SetNamescopesAndRegisterNamesVisitor(templateContext), null);
node.Accept(new SetFieldVisitor(templateContext), null);
node.Accept(new SetResourcesVisitor(templateContext), null);
node.Accept(new SetPropertiesVisitor(templateContext), null);
templateIl.Emit(OpCodes.Ldloc, templateContext.Variables[node]);
templateIl.Emit(OpCodes.Ret);
//Instanciate nested class
var parentIl = parentContext.IL;
parentIl.Emit(OpCodes.Newobj, ctor);
//Copy required local vars
parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
parentIl.Append(node.PushParentObjectsArray(parentContext));
parentIl.Emit(OpCodes.Stfld, parentValues);
parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
if (parentContext.Root is VariableDefinition)
parentIl.Emit(OpCodes.Ldloc, parentContext.Root as VariableDefinition);
else if (parentContext.Root is FieldDefinition)
{
parentIl.Emit(OpCodes.Ldarg_0);
parentIl.Emit(OpCodes.Ldfld, parentContext.Root as FieldDefinition);
}
else
throw new InvalidProgramException();
parentIl.Emit(OpCodes.Stfld, root);
//SetDataTemplate
parentIl.Emit(OpCodes.Ldftn, loadTemplate);
var funcCtor =
module.Import(typeof (Func<>))
.MakeGenericInstanceType(module.TypeSystem.Object)
.Resolve()
.Methods.First(md => md.IsConstructor && md.Parameters.Count == 2)
.MakeGeneric(module.TypeSystem.Object);
parentIl.Emit(OpCodes.Newobj, module.Import(funcCtor));
var propertySetter =
module.Import(typeof (IDataTemplate)).Resolve().Properties.First(p => p.Name == "LoadTemplate").SetMethod;
parentContext.IL.Emit(OpCodes.Callvirt, module.Import(propertySetter));
}
}
class VariableDefinitionReference
{
public VariableDefinitionReference(VariableDefinition vardef)
{
VariableDefinition = vardef;
}
public VariableDefinition VariableDefinition { get; set; }
public static implicit operator VariableDefinition(VariableDefinitionReference vardefref)
{
return vardefref.VariableDefinition;
}
}
}