[XamlC] compiled TypeExtension (#739)

This commit is contained in:
Stephane Delcroix 2017-02-13 09:08:21 +01:00 коммит произвёл GitHub
Родитель 7ffc19b2b5
Коммит d108dfe176
9 изменённых файлов: 106 добавлений и 91 удалений

Просмотреть файл

@ -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;

Просмотреть файл

@ -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;
}
}
}
}

Просмотреть файл

@ -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))
};
}
}
}

Просмотреть файл

@ -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;
}
}
}

Просмотреть файл

@ -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">

Просмотреть файл

@ -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)

Просмотреть файл

@ -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; }

Просмотреть файл

@ -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;
}
}
}
}

Просмотреть файл

@ -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");