[XamlC] compiled TypeExtension (#739)
This commit is contained in:
Родитель
7ffc19b2b5
Коммит
d108dfe176
|
@ -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");
|
||||
|
|
Загрузка…
Ссылка в новой задаче