summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephane Delcroix <stephane@delcroix.org>2017-02-13 09:08:21 +0100
committerGitHub <noreply@github.com>2017-02-13 09:08:21 +0100
commitd108dfe17652d3f6a18bf76d0b7f955b74244998 (patch)
tree08e09945ac6fb1caf1933a84ae775f0cc6be1ff9
parent7ffc19b2b5b70f32a8d865573824da29249ebf7a (diff)
downloadxamarin-forms-d108dfe17652d3f6a18bf76d0b7f955b74244998.tar.gz
xamarin-forms-d108dfe17652d3f6a18bf76d0b7f955b74244998.tar.bz2
xamarin-forms-d108dfe17652d3f6a18bf76d0b7f955b74244998.zip
[XamlC] compiled TypeExtension (#739)
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs20
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/StaticExtension.cs23
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/TypeExtension.cs44
-rw-r--r--Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs58
-rw-r--r--Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj1
-rw-r--r--Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs28
-rw-r--r--Xamarin.Forms.Xaml/MarkupExtensions/StaticExtension.cs1
-rw-r--r--Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs15
-rw-r--r--Xamarin.Forms.Xaml/MarkupExtensions/TypeExtension.cs7
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");