diff options
author | Stephane Delcroix <stephane@delcroix.org> | 2017-02-13 09:08:21 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-13 09:08:21 +0100 |
commit | d108dfe17652d3f6a18bf76d0b7f955b74244998 (patch) | |
tree | 08e09945ac6fb1caf1933a84ae775f0cc6be1ff9 | |
parent | 7ffc19b2b5b70f32a8d865573824da29249ebf7a (diff) | |
download | xamarin-forms-d108dfe17652d3f6a18bf76d0b7f955b74244998.tar.gz xamarin-forms-d108dfe17652d3f6a18bf76d0b7f955b74244998.tar.bz2 xamarin-forms-d108dfe17652d3f6a18bf76d0b7f955b74244998.zip |
[XamlC] compiled TypeExtension (#739)
9 files changed, 106 insertions, 91 deletions
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs index b46d22dc..bcec1a51 100644 --- a/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs +++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs @@ -52,7 +52,7 @@ namespace Xamarin.Forms.Core.XamlC if (typeName == null || propertyName == null) throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(BindableProperty)}", node); - var typeRef = GetTypeReference(typeName, module, node); + var typeRef = XmlTypeExtensions.GetTypeReference(typeName, module, node); if (typeRef == null) throw new XamlParseException($"Can't resolve {typeName}", node); bpRef = GetBindablePropertyFieldReference(typeRef, propertyName, module); @@ -61,24 +61,6 @@ namespace Xamarin.Forms.Core.XamlC return 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; diff --git a/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/StaticExtension.cs b/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/StaticExtension.cs index 4565c64f..896c03fd 100644 --- a/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/StaticExtension.cs +++ b/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/StaticExtension.cs @@ -26,7 +26,7 @@ namespace Xamarin.Forms.Build.Tasks var typename = member.Substring(0, dotIdx); var membername = member.Substring(dotIdx + 1); - var typeRef = module.ImportReference(GetTypeReference(typename, module, node)); + var typeRef = module.ImportReference(XmlTypeExtensions.GetTypeReference(typename, module, node as BaseNode)); var fieldRef = GetFieldReference(typeRef, membername, module); var propertyDef = GetPropertyDefinition(typeRef, membername, module); @@ -79,25 +79,6 @@ namespace Xamarin.Forms.Build.Tasks return new [] { Instruction.Create(OpCodes.Call, getterDef) }; } - - public static TypeReference GetTypeReference(string xmlType, ModuleDefinition module, IElementNode node) - { - var split = xmlType.Split(':'); - if (split.Length > 2) - throw new XamlParseException($"Type \"{xmlType}\" is invalid", node as IXmlLineInfo); - - string prefix, name; - if (split.Length == 2) { - prefix = split [0]; - name = split [1]; - } else { - prefix = ""; - name = split [0]; - } - var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix) ?? ""; - return XmlTypeExtensions.GetTypeReference(new XmlType(namespaceuri, name, null), module, node as IXmlLineInfo); - } - public static FieldReference GetFieldReference(TypeReference typeRef, string fieldName, ModuleDefinition module) { TypeReference declaringTypeReference; @@ -120,4 +101,4 @@ namespace Xamarin.Forms.Build.Tasks return pDef; } } -}
\ No newline at end of file +} diff --git a/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/TypeExtension.cs b/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/TypeExtension.cs new file mode 100644 index 00000000..8d31bfc1 --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/TypeExtension.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Xamarin.Forms.Xaml; +using System.Xml; + +namespace Xamarin.Forms.Build.Tasks +{ + class TypeExtension : ICompiledMarkupExtension + { + public IEnumerable<Instruction> ProvideValue(IElementNode node, ModuleDefinition module, ILContext context, out TypeReference memberRef) + { + memberRef = module.ImportReference(typeof(Type)); + INode typeNameNode; + + var name = new XmlName("", "TypeName"); + if (!node.Properties.TryGetValue(name, out typeNameNode) && node.CollectionItems.Any()) + typeNameNode = node.CollectionItems[0]; + + var valueNode = typeNameNode as ValueNode; + if (valueNode == null) + throw new XamlParseException("TypeName isn't set.", node as XmlLineInfo); + + if (!node.Properties.ContainsKey(name)) { + node.Properties[name] = typeNameNode; + node.CollectionItems.Clear(); + } + + var typeref = module.ImportReference(XmlTypeExtensions.GetTypeReference(valueNode.Value as string, module, node as BaseNode)); + if (typeref == null) + throw new XamlParseException($"Can't resolve type `{valueNode.Value}'.", node as IXmlLineInfo); + + context.TypeExtensions[node] = typeref; + + var getTypeFromHandle = module.ImportReference(typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) })); + return new List<Instruction> { + Instruction.Create(OpCodes.Ldtoken, module.ImportReference(typeref)), + Instruction.Create(OpCodes.Call, module.ImportReference(getTypeFromHandle)) + }; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs index b73926fd..d7b05ac4 100644 --- a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs @@ -52,21 +52,28 @@ namespace Xamarin.Forms.Build.Tasks return; } - if (typeref.FullName == "Xamarin.Forms.Xaml.StaticExtension") { - var markupProvider = new StaticExtension(); + //if this is a MarkupExtension that can be compiled directly, compile and returns the value + var compiledMarkupExtensionName = typeref.GetCustomAttribute(Module.ImportReference(typeof(ProvideCompiledAttribute)))?.ConstructorArguments?[0].Value as string; + Type compiledMarkupExtensionType; + ICompiledMarkupExtension markupProvider; + if (compiledMarkupExtensionName != null && + (compiledMarkupExtensionType = Type.GetType(compiledMarkupExtensionName)) != null && + (markupProvider = Activator.CreateInstance(compiledMarkupExtensionType) as ICompiledMarkupExtension) != null) { var il = markupProvider.ProvideValue(node, Module, Context, out typeref); typeref = Module.ImportReference(typeref); var vardef = new VariableDefinition(typeref); - Context.Variables [node] = vardef; + Context.Variables[node] = vardef; Context.Body.Variables.Add(vardef); Context.IL.Append(il); Context.IL.Emit(OpCodes.Stloc, vardef); //clean the node as it has been fully exhausted - node.Properties.Clear(); + foreach (var prop in node.Properties) + if (!node.SkipProperties.Contains(prop.Key)) + node.SkipProperties.Add(prop.Key); node.CollectionItems.Clear(); return; } @@ -171,47 +178,14 @@ namespace Xamarin.Forms.Build.Tasks Context.IL.Emit(OpCodes.Initobj, Module.ImportReference(typedef)); } - if (typeref.FullName == "Xamarin.Forms.Xaml.TypeExtension") { + if (typeref.FullName == "Xamarin.Forms.Xaml.ArrayExtension") { 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 prefix = ""; - if (type.Contains(":")) { - prefix = type.Split(':') [0].Trim(); - type = type.Split(':') [1].Trim(); - } - var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix); - Context.TypeExtensions [node] = new XmlType(namespaceuri, type, null).GetTypeReference(Module, node); - - if (!node.SkipProperties.Contains(new XmlName("", "TypeName"))) - node.SkipProperties.Add(new XmlName("", "TypeName")); - - 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); - } - } - - if (typeref.FullName == "Xamarin.Forms.Xaml.ArrayExtension") - { - 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); - - var markupProvider = new ArrayExtension(); + markupProvider = new ArrayExtension(); var il = markupProvider.ProvideValue(node, Module, Context, out typeref); @@ -223,8 +197,12 @@ namespace Xamarin.Forms.Build.Tasks Context.IL.Emit(OpCodes.Stloc, vardef); //clean the node as it has been fully exhausted - node.Properties.Remove(new XmlName("","Type")); + foreach (var prop in node.Properties) + if (!node.SkipProperties.Contains(prop.Key)) + node.SkipProperties.Add(prop.Key); node.CollectionItems.Clear(); + + return; } } } diff --git a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj index c1c62eff..0175a4b8 100644 --- a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj +++ b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj @@ -110,6 +110,7 @@ <Compile Include="CompiledValueProviders\TriggerValueProvider.cs" /> <Compile Include="CompiledValueProviders\PassthroughValueProvider.cs" /> <Compile Include="CompiledConverters\ListStringTypeConverter.cs" /> + <Compile Include="CompiledMarkupExtensions\TypeExtension.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Target Name="AfterBuild"> diff --git a/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs b/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs index 59b72acc..3dd1d739 100644 --- a/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs +++ b/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs @@ -10,11 +10,6 @@ namespace Xamarin.Forms.Build.Tasks { static class XmlTypeExtensions { - public static TypeReference GetTypeReference (string namespaceURI, string typename, ModuleDefinition module, IXmlLineInfo xmlInfo) - { - return new XmlType (namespaceURI, typename, null).GetTypeReference (module, xmlInfo); - } - static IList<XmlnsDefinitionAttribute> s_xmlnsDefinitions; static void GatherXmlnsDefinitionAttributes() @@ -34,6 +29,29 @@ namespace Xamarin.Forms.Build.Tasks } } + public static TypeReference GetTypeReference(string xmlType, ModuleDefinition module, BaseNode node) + { + var split = xmlType.Split(':'); + if (split.Length > 2) + throw new XamlParseException($"Type \"{xmlType}\" is invalid", node as IXmlLineInfo); + + string prefix, name; + if (split.Length == 2) { + prefix = split[0]; + name = split[1]; + } else { + prefix = ""; + name = split[0]; + } + var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix) ?? ""; + return GetTypeReference(new XmlType(namespaceuri, name, null), module, node as IXmlLineInfo); + } + + public static TypeReference GetTypeReference(string namespaceURI, string typename, ModuleDefinition module, IXmlLineInfo xmlInfo) + { + return new XmlType(namespaceURI, typename, null).GetTypeReference(module, xmlInfo); + } + public static TypeReference GetTypeReference(this XmlType xmlType, ModuleDefinition module, IXmlLineInfo xmlInfo) { if (s_xmlnsDefinitions == null) diff --git a/Xamarin.Forms.Xaml/MarkupExtensions/StaticExtension.cs b/Xamarin.Forms.Xaml/MarkupExtensions/StaticExtension.cs index 8de0ebf4..418f2551 100644 --- a/Xamarin.Forms.Xaml/MarkupExtensions/StaticExtension.cs +++ b/Xamarin.Forms.Xaml/MarkupExtensions/StaticExtension.cs @@ -6,6 +6,7 @@ using System.Xml; namespace Xamarin.Forms.Xaml { [ContentProperty(nameof(Member))] + [ProvideCompiled("Xamarin.Forms.Build.Tasks.StaticExtension")] public class StaticExtension : IMarkupExtension { public string Member { get; set; } diff --git a/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs b/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs index 34f1c569..fc00d308 100644 --- a/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using System.Xml; namespace Xamarin.Forms.Xaml { @@ -32,9 +33,7 @@ namespace Xamarin.Forms.Xaml if (resDict.TryGetMergedValue(Key, out resource)) break; } - if (resource == null && (Application.Current == null || Application.Current.Resources == null || - !Application.Current.Resources.TryGetMergedValue(Key, out resource))) - throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo); + resource = resource ?? GetApplicationLevelResource(Key, xmlLineInfo); var bp = valueProvider.TargetProperty as BindableProperty; var pi = valueProvider.TargetProperty as PropertyInfo; @@ -55,5 +54,13 @@ namespace Xamarin.Forms.Xaml return resource; } + + internal object GetApplicationLevelResource(string key, IXmlLineInfo xmlLineInfo) + { + object resource; + if (Application.Current == null || Application.Current.Resources == null || !Application.Current.Resources.TryGetMergedValue(Key, out resource)) + throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo); + return resource; + } } -} +}
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml/MarkupExtensions/TypeExtension.cs b/Xamarin.Forms.Xaml/MarkupExtensions/TypeExtension.cs index f4bbf842..ae6b8f37 100644 --- a/Xamarin.Forms.Xaml/MarkupExtensions/TypeExtension.cs +++ b/Xamarin.Forms.Xaml/MarkupExtensions/TypeExtension.cs @@ -2,15 +2,18 @@ using System; namespace Xamarin.Forms.Xaml { - [ContentProperty("TypeName")] + [ContentProperty(nameof(TypeName))] + [ProvideCompiled("Xamarin.Forms.Build.Tasks.TypeExtension")] public class TypeExtension : IMarkupExtension<Type> { public string TypeName { get; set; } public Type ProvideValue(IServiceProvider serviceProvider) { + if (string.IsNullOrEmpty(TypeName)) + throw new InvalidOperationException("TypeName isn't set."); if (serviceProvider == null) - throw new ArgumentNullException("serviceProvider"); + throw new ArgumentNullException(nameof(serviceProvider)); var typeResolver = serviceProvider.GetService(typeof (IXamlTypeResolver)) as IXamlTypeResolver; if (typeResolver == null) throw new ArgumentException("No IXamlTypeResolver in IServiceProvider"); |