summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Build.Tasks/CompiledConverters
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Build.Tasks/CompiledConverters')
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs84
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/BindingTypeConverter.cs32
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/BoundsTypeConverter.cs73
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/ColorTypeConverter.cs55
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/ICompiledTypeConverter.cs12
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/LayoutOptionsConverter.cs37
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/RectangleTypeConverter.cs49
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