summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs')
-rw-r--r--Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs373
1 files changed, 369 insertions, 4 deletions
diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
index f481cbee..8b4bf7ad 100644
--- a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
+++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
@@ -16,6 +16,7 @@ namespace Xamarin.Forms.Build.Tasks
class SetPropertiesVisitor : IXamlNodeVisitor
{
static int dtcount;
+ static int typedBindingCount;
static readonly IList<XmlName> skips = new List<XmlName>
{
@@ -23,7 +24,8 @@ namespace Xamarin.Forms.Build.Tasks
XmlName.xTypeArguments,
XmlName.xArguments,
XmlName.xFactoryMethod,
- XmlName.xName
+ XmlName.xName,
+ XmlName.xDataType
};
public SetPropertiesVisitor(ILContext context, bool stopOnResourceDictionary = false)
@@ -271,6 +273,10 @@ namespace Xamarin.Forms.Build.Tasks
else if (vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
out markupExtension, out genericArguments))
{
+ if (vardefref.VariableDefinition.VariableType.FullName == "Xamarin.Forms.Xaml.BindingExtension")
+ foreach (var instruction in CompileBindingPath(node, context, vardefref.VariableDefinition))
+ yield return instruction;
+
var markExt = markupExtension.Resolve();
var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
var provideValue = module.Import(provideValueInfo);
@@ -312,6 +318,359 @@ namespace Xamarin.Forms.Build.Tasks
}
}
+ //Once we get compiled IValueProvider, this will move to the BindingExpression
+ static IEnumerable<Instruction> CompileBindingPath(ElementNode node, ILContext context, VariableDefinition bindingExt)
+ {
+ //TODO implement handlers[]
+ //TODO support casting operators
+
+ INode pathNode;
+ if (!node.Properties.TryGetValue(new XmlName("", "Path"), out pathNode) && node.CollectionItems.Any())
+ pathNode = node.CollectionItems [0];
+ var path = (pathNode as ValueNode)?.Value as string;
+
+ INode dataTypeNode = null;
+ IElementNode n = node;
+ while (n != null) {
+ if (n.Properties.TryGetValue(XmlName.xDataType, out dataTypeNode))
+ break;
+ n = n.Parent as IElementNode;
+ }
+ var dataType = (dataTypeNode as ValueNode)?.Value as string;
+ if (dataType == null)
+ yield break; //throw
+
+ var namespaceuri = dataType.Contains(":") ? node.NamespaceResolver.LookupNamespace(dataType.Split(':') [0].Trim()) : "";
+ var dtXType = new XmlType(namespaceuri, dataType, null);
+
+ var tSourceRef = dtXType.GetTypeReference(context.Module, (IXmlLineInfo)node);
+ if (tSourceRef == null)
+ yield break; //throw
+
+ var properties = ParsePath(path, tSourceRef, node as IXmlLineInfo, context.Module);
+ var tPropertyRef = properties != null && properties.Any() ? properties.Last().Item1.PropertyType : tSourceRef;
+
+ var funcRef = context.Module.Import(context.Module.Import(typeof(Func<,>)).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
+ var actionRef = context.Module.Import(context.Module.Import(typeof(Action<,>)).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
+ var funcObjRef = context.Module.Import(context.Module.Import(typeof(Func<,>)).MakeGenericInstanceType(new [] { tSourceRef, context.Module.TypeSystem.Object }));
+ var tupleRef = context.Module.Import(context.Module.Import(typeof(Tuple<,>)).MakeGenericInstanceType(new [] { funcObjRef, context.Module.TypeSystem.String}));
+ var typedBindingRef = context.Module.Import(context.Module.Import(typeof(TypedBinding<,>)).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef}));
+
+ TypeReference _;
+ var ctorInfo = context.Module.Import(typedBindingRef.Resolve().Methods.FirstOrDefault(md => md.IsConstructor && !md.IsStatic && md.Parameters.Count == 3 ));
+ var ctorinforef = ctorInfo.MakeGeneric(typedBindingRef, funcRef, actionRef, tupleRef);
+ var setTypedBinding = context.Module.Import(typeof(BindingExtension)).GetProperty(pd => pd.Name == "TypedBinding", out _).SetMethod;
+
+ yield return Instruction.Create(OpCodes.Ldloc, bindingExt);
+ foreach (var instruction in CompiledBindingGetGetter(tSourceRef, tPropertyRef, properties, node, context))
+ yield return instruction;
+ foreach (var instruction in CompiledBindingGetSetter(tSourceRef, tPropertyRef, properties, node, context))
+ yield return instruction;
+ foreach (var instruction in CompiledBindingGetHandlers(tSourceRef, tPropertyRef, properties, node, context))
+ yield return instruction;
+ yield return Instruction.Create(OpCodes.Newobj, context.Module.Import(ctorinforef));
+ yield return Instruction.Create(OpCodes.Callvirt, context.Module.Import(setTypedBinding));
+ }
+
+ static IList<Tuple<PropertyDefinition, string>> ParsePath(string path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ return null;
+ path = path.Trim(' ', '.'); //trim leading or trailing dots
+ var parts = path.Split(new [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
+ var properties = new List<Tuple<PropertyDefinition, string>>();
+
+ var previousPartTypeRef = tSourceRef;
+ TypeReference _;
+ foreach (var part in parts) {
+ var p = part;
+ string indexArg = null;
+ var lbIndex = p.IndexOf('[');
+ if (lbIndex != -1) {
+ var rbIndex = p.LastIndexOf(']');
+ if (rbIndex == -1)
+ throw new XamlParseException("Binding: Indexer did not contain closing bracket", lineInfo);
+
+ var argLength = rbIndex - lbIndex - 1;
+ if (argLength == 0)
+ throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
+
+ indexArg = p.Substring(lbIndex + 1, argLength).Trim();
+ if (indexArg.Length == 0)
+ throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
+
+ p = p.Substring(0, lbIndex);
+ p = p.Trim();
+ }
+
+ if (p.Length > 0) {
+ var property = previousPartTypeRef.GetProperty(pd => pd.Name == p && pd.GetMethod != null && pd.GetMethod.IsPublic, out _);
+ properties.Add(new Tuple<PropertyDefinition, string>(property,null));
+ previousPartTypeRef = property.PropertyType;
+ }
+ if (indexArg != null) {
+ var defaultMemberAttribute = previousPartTypeRef.GetCustomAttribute(module.Import(typeof(System.Reflection.DefaultMemberAttribute)));
+ var indexerName = defaultMemberAttribute?.ConstructorArguments?.FirstOrDefault().Value as string ?? "Item";
+ var indexer = previousPartTypeRef.GetProperty(pd => pd.Name == indexerName && pd.GetMethod != null && pd.GetMethod.IsPublic, out _);
+ properties.Add(new Tuple<PropertyDefinition, string>(indexer, indexArg));
+ if (indexer.PropertyType != module.TypeSystem.String && indexer.PropertyType != module.TypeSystem.Int32)
+ throw new XamlParseException($"Binding: Unsupported indexer index type: {indexer.PropertyType.FullName}", lineInfo);
+ previousPartTypeRef = indexer.PropertyType;
+ }
+ }
+ return properties;
+ }
+
+ static IEnumerable<Instruction> CompiledBindingGetGetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
+ {
+// .method private static hidebysig default string '<Main>m__0' (class ViewModel vm) cil managed
+// {
+// .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ...
+//
+// IL_0000: ldarg.0
+// IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
+// IL_0006: callvirt instance string class ViewModel::get_Text()
+// IL_0006: ret
+// }
+
+ var module = context.Module;
+ var compilerGeneratedCtor = module.Import(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)).GetMethods(md => md.IsConstructor, module).First().Item1;
+ var getter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
+ MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
+ tPropertyRef) {
+ Parameters = {
+ new ParameterDefinition(tSourceRef)
+ },
+ CustomAttributes = {
+ new CustomAttribute (context.Module.Import(compilerGeneratedCtor))
+ }
+ };
+ var il = getter.Body.GetILProcessor();
+
+ il.Emit(OpCodes.Ldarg_0);
+ if (properties != null && properties.Count != 0) {
+ foreach (var propTuple in properties) {
+ var property = propTuple.Item1;
+ var indexerArg = propTuple.Item2;
+ if (indexerArg != null) {
+ if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
+ il.Emit(OpCodes.Ldstr, indexerArg);
+ else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
+ int index;
+ if (!int.TryParse(indexerArg, out index))
+ throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
+ il.Emit(OpCodes.Ldc_I4, index);
+ }
+ }
+ il.Emit(OpCodes.Callvirt, module.Import(property.GetMethod));
+ }
+ }
+
+ il.Emit(OpCodes.Ret);
+
+ context.Body.Method.DeclaringType.Methods.Add(getter);
+
+ var funcRef = module.Import(typeof(Func<,>));
+ funcRef = module.Import(funcRef.MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
+ var funcCtor = module.Import(funcRef.Resolve().GetConstructors().First());
+ funcCtor = funcCtor.MakeGeneric(funcRef, new [] { tSourceRef, tPropertyRef });
+
+// IL_0007: ldnull
+// IL_0008: ldftn string class Test::'<Main>m__0'(class ViewModel)
+// IL_000e: newobj instance void class [mscorlib]System.Func`2<class ViewModel, string>::'.ctor'(object, native int)
+
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldftn, getter);
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(funcCtor));
+ }
+
+ static IEnumerable<Instruction> CompiledBindingGetSetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
+ {
+ if (properties == null || properties.Count == 0) {
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield break;
+ }
+
+ // .method private static hidebysig default void '<Main>m__1' (class ViewModel vm, string s) cil managed
+ // {
+ // .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
+ //
+ // IL_0000: ldarg.0
+ // IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
+ // IL_0006: ldarg.1
+ // IL_0007: callvirt instance void class ViewModel::set_Text(string)
+ // IL_000c: ret
+ // }
+
+ var module = context.Module;
+ var compilerGeneratedCtor = module.Import(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)).GetMethods(md => md.IsConstructor, module).First().Item1;
+ var setter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
+ MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
+ module.TypeSystem.Void) {
+ Parameters = {
+ new ParameterDefinition(tSourceRef),
+ new ParameterDefinition(tPropertyRef)
+ },
+ CustomAttributes = {
+ new CustomAttribute (module.Import(compilerGeneratedCtor))
+ }
+ };
+
+ var il = setter.Body.GetILProcessor();
+ var lastProperty = properties.LastOrDefault();
+ var setterRef = lastProperty?.Item1.SetMethod;
+ if (setterRef == null) {
+ yield return Instruction.Create(OpCodes.Ldnull); //throw or not ?
+ yield break;
+ }
+
+ il.Emit(OpCodes.Ldarg_0);
+ for (int i = 0; i < properties.Count - 1; i++) {
+ var property = properties[i].Item1;
+ var indexerArg = properties[i].Item2;
+ if (indexerArg != null) {
+ if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
+ il.Emit(OpCodes.Ldstr, indexerArg);
+ else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
+ int index;
+ if (!int.TryParse(indexerArg, out index))
+ throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
+ il.Emit(OpCodes.Ldc_I4, index);
+ }
+ }
+ il.Emit(OpCodes.Callvirt, module.Import(property.GetMethod));
+ }
+
+ var indexer = properties.Last().Item2;
+ if (indexer != null) {
+ if (lastProperty.Item1.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
+ il.Emit(OpCodes.Ldstr, indexer);
+ else if (lastProperty.Item1.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
+ int index;
+ if (!int.TryParse(indexer, out index))
+ throw new XamlParseException($"Binding: {indexer} could not be parsed as an index for a {lastProperty.Item1.Name}", node as IXmlLineInfo);
+ il.Emit(OpCodes.Ldc_I4, index);
+ }
+ }
+ il.Emit(OpCodes.Ldarg_1);
+ il.Emit(OpCodes.Callvirt, module.Import(setterRef));
+ il.Emit(OpCodes.Ret);
+
+ context.Body.Method.DeclaringType.Methods.Add(setter);
+
+ var actionRef = module.Import(typeof(Action<,>));
+ actionRef = module.Import(actionRef.MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
+ var actionCtor = module.Import(actionRef.Resolve().GetConstructors().First());
+ actionCtor = actionCtor.MakeGeneric(actionRef, new [] { tSourceRef, tPropertyRef });
+
+// IL_0024: ldnull
+// IL_0025: ldftn void class Test::'<Main>m__1'(class ViewModel, string)
+// IL_002b: newobj instance void class [mscorlib]System.Action`2<class ViewModel, string>::'.ctor'(object, native int)
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldftn, setter);
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(actionCtor));
+ }
+
+ static IEnumerable<Instruction> CompiledBindingGetHandlers(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
+ {
+// .method private static hidebysig default object '<Main>m__2'(class ViewModel vm) cil managed {
+// .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
+// IL_0000: ldarg.0
+// IL_0001: ret
+// } // end of method Test::<Main>m__2
+
+// .method private static hidebysig default object '<Main>m__3' (class ViewModel vm) cil managed {
+// .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
+// IL_0000: ldarg.0
+// IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
+// IL_0006: ret
+// }
+
+ var module = context.Module;
+ var compilerGeneratedCtor = module.Import(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)).GetMethods(md => md.IsConstructor, module).First().Item1;
+
+ var partGetters = new List<MethodDefinition>();
+ if (properties == null || properties.Count == 0) {
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield break;
+ }
+
+ for (int i = 0; i < properties.Count; i++) {
+ var tuple = properties [i];
+ var partGetter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, tPropertyRef) {
+ Parameters = {
+ new ParameterDefinition(tSourceRef)
+ },
+ CustomAttributes = {
+ new CustomAttribute (context.Module.Import(compilerGeneratedCtor))
+ }
+ };
+ var il = partGetter.Body.GetILProcessor();
+ il.Emit(OpCodes.Ldarg_0);
+ for (int j = 0; j < i; j++) {
+ var propTuple = properties [j];
+ var property = propTuple.Item1;
+ var indexerArg = propTuple.Item2;
+ if (indexerArg != null) {
+ if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
+ il.Emit(OpCodes.Ldstr, indexerArg);
+ else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
+ int index;
+ if (!int.TryParse(indexerArg, out index))
+ throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
+ il.Emit(OpCodes.Ldc_I4, index);
+ }
+ }
+ il.Emit(OpCodes.Callvirt, module.Import(property.GetMethod));
+ }
+ il.Emit(OpCodes.Ret);
+ context.Body.Method.DeclaringType.Methods.Add(partGetter);
+ partGetters.Add(partGetter);
+ }
+
+ var funcObjRef = context.Module.Import(module.Import(typeof(Func<,>)).MakeGenericInstanceType(new [] { tSourceRef, module.TypeSystem.Object }));
+ var tupleRef = context.Module.Import(module.Import(typeof(Tuple<,>)).MakeGenericInstanceType(new [] { funcObjRef, module.TypeSystem.String }));
+ var funcCtor = module.Import(funcObjRef.Resolve().GetConstructors().First());
+ funcCtor = funcCtor.MakeGeneric(funcObjRef, new [] { tSourceRef, module.TypeSystem.Object });
+ var tupleCtor = module.Import(tupleRef.Resolve().GetConstructors().First());
+ tupleCtor = tupleCtor.MakeGeneric(tupleRef, new [] { funcObjRef, module.TypeSystem.String});
+
+// IL_003a: ldc.i4.2
+// IL_003b: newarr class [mscorlib] System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel,object>,string>
+
+// IL_0040: dup
+// IL_0041: ldc.i4.0
+// IL_0049: ldnull
+// IL_004a: ldftn object class Test::'<Main>m__2'(class ViewModel)
+// IL_0050: newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
+// IL_005f: ldstr "Model"
+// IL_0064: newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
+// IL_0069: stelem.ref
+
+// IL_006a: dup
+// IL_006b: ldc.i4.1
+// IL_0073: ldnull
+// IL_0074: ldftn object class Test::'<Main>m__3'(class ViewModel)
+// IL_007a: newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
+// IL_0089: ldstr "Text"
+// IL_008e: newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
+// IL_0093: stelem.ref
+
+ yield return Instruction.Create(OpCodes.Ldc_I4, properties.Count);
+ yield return Instruction.Create(OpCodes.Newarr, tupleRef);
+
+ for (var i = 0; i < properties.Count; i++) {
+ yield return Instruction.Create(OpCodes.Dup);
+ yield return Instruction.Create(OpCodes.Ldc_I4, i);
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldftn, partGetters [i]);
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(funcCtor));
+ yield return Instruction.Create(OpCodes.Ldstr, properties [i].Item1.Name);
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(tupleCtor));
+ yield return Instruction.Create(OpCodes.Stelem_Ref);
+ }
+ }
+
public static IEnumerable<Instruction> SetPropertyValue(VariableDefinition parent, XmlName propertyName, INode valueNode, ILContext context, IXmlLineInfo iXmlLineInfo)
{
var module = context.Body.Method.Module;
@@ -685,7 +1044,9 @@ namespace Xamarin.Forms.Build.Tasks
// .class nested private auto ansi sealed beforefieldinit '<Main>c__AnonStorey0'
// extends [mscorlib]System.Object
- var module = parentContext.Body.Method.Module;
+
+ var module = parentContext.Module;
+ var compilerGeneratedCtor = module.Import(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)).GetMethods(md => md.IsConstructor, module).First().Item1;
var anonType = new TypeDefinition(
null,
"<" + parentContext.Body.Method.Name + ">_anonXamlCDataTemplate_" + dtcount++,
@@ -693,7 +1054,10 @@ namespace Xamarin.Forms.Build.Tasks
TypeAttributes.Sealed |
TypeAttributes.NestedPrivate)
{
- BaseType = module.TypeSystem.Object
+ BaseType = module.TypeSystem.Object,
+ CustomAttributes = {
+ new CustomAttribute (module.Import(compilerGeneratedCtor))
+ }
};
parentContext.Body.Method.DeclaringType.NestedTypes.Add(anonType);
@@ -721,7 +1085,7 @@ namespace Xamarin.Forms.Build.Tasks
//Fill the loadTemplate Body
var templateIl = loadTemplate.Body.GetILProcessor();
templateIl.Emit(OpCodes.Nop);
- var templateContext = new ILContext(templateIl, loadTemplate.Body, parentValues)
+ var templateContext = new ILContext(templateIl, loadTemplate.Body, module, parentValues)
{
Root = root
};
@@ -730,6 +1094,7 @@ namespace Xamarin.Forms.Build.Tasks
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);