diff options
Diffstat (limited to 'Xamarin.Forms.Build.Tasks/CompiledConverters')
7 files changed, 342 insertions, 0 deletions
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs new file mode 100644 index 00000000..92a53539 --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; + +using Xamarin.Forms.Build.Tasks; +using Xamarin.Forms.Xaml; + +using static System.String; + +namespace Xamarin.Forms.Core.XamlC +{ + class BindablePropertyConverter : ICompiledTypeConverter + { + public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node) + { + if (IsNullOrEmpty(value)) { + yield return Instruction.Create(OpCodes.Ldnull); + yield break; + } + + FieldReference bpRef = null; + string typeName = null, propertyName = null; + + var parts = value.Split('.'); + if (parts.Length == 1) { + var parent = node.Parent?.Parent as IElementNode; + if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == "http://xamarin.com/schemas/2014/forms" && (node.Parent as ElementNode)?.XmlType.Name == "Setter") { + if (parent.XmlType.NamespaceUri == "http://xamarin.com/schemas/2014/forms" && + (parent.XmlType.Name == "Trigger" || parent.XmlType.Name == "DataTrigger" || parent.XmlType.Name == "MultiTrigger" || parent.XmlType.Name == "Style")) { + var ttnode = (parent as ElementNode).Properties [new XmlName("", "TargetType")]; + if (ttnode is ValueNode) + typeName = (ttnode as ValueNode).Value as string; + else if (ttnode is IElementNode) + typeName = ((ttnode as IElementNode).CollectionItems.FirstOrDefault() as ValueNode)?.Value as string ?? ((ttnode as IElementNode).Properties [new XmlName("", "TypeName")] as ValueNode)?.Value as string; + } + } else if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == "http://xamarin.com/schemas/2014/forms" && (node.Parent as ElementNode)?.XmlType.Name == "Trigger") + typeName = ((node.Parent as ElementNode).Properties [new XmlName("", "TargetType")] as ValueNode).Value as string; + propertyName = parts [0]; + } else if (parts.Length == 2) { + typeName = parts [0]; + propertyName = parts [1]; + } else + throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(BindableProperty)}", node); + + var typeRef = GetTypeReference(typeName, module, node); + if (typeRef == null) + throw new XamlParseException($"Can't resolve {typeName}", node); + bpRef = GetBindablePropertyFieldReference(typeRef, propertyName, module); + if (bpRef == null) + throw new XamlParseException($"Can't resolve {propertyName} on {typeRef.Name}", node); + yield return Instruction.Create(OpCodes.Ldsfld, bpRef); + } + + public static TypeReference GetTypeReference(string xmlType, ModuleDefinition module, BaseNode iNode) + { + var split = xmlType.Split(':'); + if (split.Length > 2) + throw new XamlParseException($"Type \"{xmlType}\" is invalid", iNode); + + string prefix, name; + if (split.Length == 2) { + prefix = split [0]; + name = split [1]; + } else { + prefix = ""; + name = split [0]; + } + var namespaceuri = iNode.NamespaceResolver.LookupNamespace(prefix) ?? ""; + return XmlTypeExtensions.GetTypeReference(namespaceuri, name, module, iNode); + } + + public static FieldReference GetBindablePropertyFieldReference(TypeReference typeRef, string propertyName, ModuleDefinition module) + { + TypeReference declaringTypeReference; + FieldReference bpRef = typeRef.GetField(fd => fd.Name == $"{propertyName}Property" && fd.IsStatic && fd.IsPublic, out declaringTypeReference); + if (bpRef != null) { + bpRef = module.Import(bpRef.ResolveGenericParameters(declaringTypeReference)); + bpRef.FieldType = module.Import(bpRef.FieldType); + } + return bpRef; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/BindingTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindingTypeConverter.cs new file mode 100644 index 00000000..74990a86 --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindingTypeConverter.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Linq; + +using Mono.Cecil; +using Mono.Cecil.Cil; + +using Xamarin.Forms.Xaml; + +using static System.String; + +namespace Xamarin.Forms.Core.XamlC +{ + class BindingTypeConverter : ICompiledTypeConverter + { + public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node) + { + if (IsNullOrEmpty(value)) + throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Binding)}", node); + + var bindingCtor = module.Import(typeof(Binding)).Resolve().Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 6); + var bindingCtorRef = module.Import(bindingCtor); + + yield return Instruction.Create(OpCodes.Ldstr, value); + yield return Instruction.Create(OpCodes.Ldc_I4, (int)BindingMode.Default); + yield return Instruction.Create(OpCodes.Ldnull); + yield return Instruction.Create(OpCodes.Ldnull); + yield return Instruction.Create(OpCodes.Ldnull); + yield return Instruction.Create(OpCodes.Ldnull); + yield return Instruction.Create(OpCodes.Newobj, bindingCtorRef); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/BoundsTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/BoundsTypeConverter.cs new file mode 100644 index 00000000..1cc64fc5 --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/BoundsTypeConverter.cs @@ -0,0 +1,73 @@ +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.Core.XamlC +{ + class BoundsTypeConverter : ICompiledTypeConverter + { + IEnumerable<Instruction> ICompiledTypeConverter.ConvertFromString(string value, ModuleDefinition module, BaseNode node) + { + if (string.IsNullOrEmpty(value)) + throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node); + + double x = -1, y = -1, w = -1, h = -1; + bool hasX, hasY, hasW, hasH; + var xywh = value.Split(','); + + if (xywh.Length != 2 && xywh.Length != 4) + throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node); + + hasX = (xywh.Length == 2 || xywh.Length == 4) && double.TryParse(xywh [0], NumberStyles.Number, CultureInfo.InvariantCulture, out x); + hasY = (xywh.Length == 2 || xywh.Length == 4) && double.TryParse(xywh [1], NumberStyles.Number, CultureInfo.InvariantCulture, out y); + hasW = xywh.Length == 4 && double.TryParse(xywh [2], NumberStyles.Number, CultureInfo.InvariantCulture, out w); + hasH = xywh.Length == 4 && double.TryParse(xywh [3], NumberStyles.Number, CultureInfo.InvariantCulture, out h); + + if (!hasW && xywh.Length == 4 && string.Compare("AutoSize", xywh [2].Trim(), StringComparison.OrdinalIgnoreCase) == 0) { + hasW = true; + w = AbsoluteLayout.AutoSize; + } + + if (!hasH && xywh.Length == 4 && string.Compare("AutoSize", xywh [3].Trim(), StringComparison.OrdinalIgnoreCase) == 0) { + hasH = true; + h = AbsoluteLayout.AutoSize; + } + + if (hasX && hasY && xywh.Length == 2) { + hasW = true; + w = AbsoluteLayout.AutoSize; + hasH = true; + h = AbsoluteLayout.AutoSize; + } + + if (!hasX || !hasY || !hasW || !hasH) + throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node); + + return GenerateIL(x, y, w, h, module); + } + + IEnumerable<Instruction> GenerateIL(double x, double y, double w, double h, ModuleDefinition module) + { +// IL_0000: ldc.r8 3.1000000000000001 +// IL_0009: ldc.r8 4.2000000000000002 +// IL_0012: ldc.r8 5.2999999999999998 +// IL_001b: ldc.r8 6.4000000000000004 +// IL_0024: newobj instance void valuetype Test.Rectangle::'.ctor'(float64, float64, float64, float64) + + yield return Instruction.Create(OpCodes.Ldc_R8, x); + yield return Instruction.Create(OpCodes.Ldc_R8, y); + yield return Instruction.Create(OpCodes.Ldc_R8, w); + yield return Instruction.Create(OpCodes.Ldc_R8, h); + + var rectangleCtor = module.Import(typeof(Rectangle)).Resolve().Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 4); + var rectangleCtorRef = module.Import(rectangleCtor); + yield return Instruction.Create(OpCodes.Newobj, rectangleCtorRef); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/ColorTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/ColorTypeConverter.cs new file mode 100644 index 00000000..39527954 --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/ColorTypeConverter.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Mono.Cecil; +using Mono.Cecil.Cil; + +using Xamarin.Forms.Xaml; + +namespace Xamarin.Forms.Core.XamlC +{ + class ColorTypeConverter : ICompiledTypeConverter + { + public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node) + { + do { + if (string.IsNullOrEmpty(value)) + break; + + value = value.Trim(); + + if (value.StartsWith("#", StringComparison.Ordinal)) { + var color = Color.FromHex(value); + yield return Instruction.Create(OpCodes.Ldc_R8, color.R); + yield return Instruction.Create(OpCodes.Ldc_R8, color.G); + yield return Instruction.Create(OpCodes.Ldc_R8, color.B); + yield return Instruction.Create(OpCodes.Ldc_R8, color.A); + var colorCtor = module.Import(typeof(Color)).Resolve().Methods.FirstOrDefault( + md => md.IsConstructor && md.Parameters.Count == 4 && + md.Parameters.All(p => p.ParameterType.FullName == "System.Double")); + var colorCtorRef = module.Import(colorCtor); + yield return Instruction.Create(OpCodes.Newobj, colorCtorRef); + yield break; + } + var parts = value.Split('.'); + if (parts.Length == 1 || (parts.Length == 2 && parts [0] == "Color")) { + var color = parts [parts.Length - 1]; + + var field = module.Import(typeof(Color)).Resolve().Fields.SingleOrDefault(fd => fd.Name == color && fd.IsStatic); + if (field != null) { + yield return Instruction.Create(OpCodes.Ldsfld, module.Import(field)); + yield break; + } + var propertyGetter = module.Import(typeof(Color)).Resolve().Properties.SingleOrDefault(pd => pd.Name == color && pd.GetMethod.IsStatic)?.GetMethod; + if (propertyGetter != null) { + yield return Instruction.Create(OpCodes.Call, module.Import(propertyGetter)); + yield break; + } + } + } while (false); + + throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Color)}", node); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/ICompiledTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/ICompiledTypeConverter.cs new file mode 100644 index 00000000..5ed88d0a --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/ICompiledTypeConverter.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using Mono.Cecil.Cil; +using Mono.Cecil; +using System.Xml; + +namespace Xamarin.Forms.Xaml +{ + interface ICompiledTypeConverter + { + IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node); + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/LayoutOptionsConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/LayoutOptionsConverter.cs new file mode 100644 index 00000000..e252ed9d --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/LayoutOptionsConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Mono.Cecil; +using Mono.Cecil.Cil; + +using Xamarin.Forms.Xaml; + +namespace Xamarin.Forms.Core.XamlC +{ + class LayoutOptionsConverter : ICompiledTypeConverter + { + public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node) + { + do { + if (string.IsNullOrEmpty(value)) + break; + + value = value.Trim(); + + var parts = value.Split('.'); + if (parts.Length == 1 || (parts.Length == 2 && parts [0] == "LayoutOptions")) { + var options = parts [parts.Length - 1]; + + var field = module.Import(typeof(LayoutOptions)).Resolve().Fields.SingleOrDefault(fd => fd.Name == options && fd.IsStatic); + if (field != null) { + yield return Instruction.Create(OpCodes.Ldsfld, module.Import(field)); + yield break; + } + } + } while (false); + + throw new XamlParseException(String.Format("Cannot convert \"{0}\" into {1}", value, typeof(LayoutOptions)), node); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/RectangleTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/RectangleTypeConverter.cs new file mode 100644 index 00000000..af480667 --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/RectangleTypeConverter.cs @@ -0,0 +1,49 @@ +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.Core.XamlC +{ + class RectangleTypeConverter : ICompiledTypeConverter + { + public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node) + { + if (string.IsNullOrEmpty(value)) + throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node); + double x, y, w, h; + var xywh = value.Split(','); + if (xywh.Length != 4 || + !double.TryParse(xywh [0], NumberStyles.Number, CultureInfo.InvariantCulture, out x) || + !double.TryParse(xywh [1], NumberStyles.Number, CultureInfo.InvariantCulture, out y) || + !double.TryParse(xywh [2], NumberStyles.Number, CultureInfo.InvariantCulture, out w) || + !double.TryParse(xywh [3], NumberStyles.Number, CultureInfo.InvariantCulture, out h)) + throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node); + + return GenerateIL(x, y, w, h, module); + } + + IEnumerable<Instruction> GenerateIL(double x, double y, double w, double h, ModuleDefinition module) + { +// IL_0000: ldc.r8 3.1000000000000001 +// IL_0009: ldc.r8 4.2000000000000002 +// IL_0012: ldc.r8 5.2999999999999998 +// IL_001b: ldc.r8 6.4000000000000004 +// IL_0024: newobj instance void valuetype Test.Rectangle::'.ctor'(float64, float64, float64, float64) + + yield return Instruction.Create(OpCodes.Ldc_R8, x); + yield return Instruction.Create(OpCodes.Ldc_R8, y); + yield return Instruction.Create(OpCodes.Ldc_R8, w); + yield return Instruction.Create(OpCodes.Ldc_R8, h); + + var rectangleCtor = module.Import(typeof(Rectangle)).Resolve().Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 4); + var rectangleCtorRef = module.Import(rectangleCtor); + yield return Instruction.Create(OpCodes.Newobj, rectangleCtorRef); + } + } +}
\ No newline at end of file |