Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[Xaml[C]] ResourceDictionary.Source (#1229)
Browse files Browse the repository at this point in the history
* [Xaml[C]] ResourceDictionary.Source

* [Xaml[C]] update to the new XamlResourceIdAttribute

* [Xaml[C]] load RD with codebehind even when referenced through Source

* [Xaml] make sure we do not generate file:// uri

* fix typo
  • Loading branch information
StephaneDelcroix committed Nov 3, 2017
1 parent c341958 commit 43143bd
Show file tree
Hide file tree
Showing 32 changed files with 552 additions and 39 deletions.
Expand Up @@ -12,8 +12,10 @@ namespace Xamarin.Forms.Core.XamlC
{
class BindablePropertyConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

if (IsNullOrEmpty(value)) {
yield return Instruction.Create(OpCodes.Ldnull);
yield break;
Expand Down
Expand Up @@ -7,13 +7,16 @@
using Xamarin.Forms.Xaml;

using static System.String;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Core.XamlC
{
class BindingTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

if (IsNullOrEmpty(value))
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Binding)}", node);

Expand Down
Expand Up @@ -7,13 +7,16 @@
using Mono.Cecil.Cil;

using Xamarin.Forms.Xaml;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Core.XamlC
{
class BoundsTypeConverter : ICompiledTypeConverter
{
IEnumerable<Instruction> ICompiledTypeConverter.ConvertFromString(string value, ModuleDefinition module, BaseNode node)
IEnumerable<Instruction> ICompiledTypeConverter.ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

if (string.IsNullOrEmpty(value))
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);

Expand Down
Expand Up @@ -6,13 +6,16 @@
using Mono.Cecil.Cil;

using Xamarin.Forms.Xaml;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Core.XamlC
{
class ColorTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

do {
if (string.IsNullOrEmpty(value))
break;
Expand Down
Expand Up @@ -6,13 +6,16 @@
using Mono.Cecil.Cil;

using Xamarin.Forms.Xaml;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Core.XamlC
{
class ConstraintTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

double size;

if (string.IsNullOrEmpty(value) || !double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out size))
Expand Down
Expand Up @@ -3,12 +3,13 @@
using Mono.Cecil;
using Xamarin.Forms.Xaml;
using System;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Xaml
{
interface ICompiledTypeConverter
{
IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node);
IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node);
}
}

Expand All @@ -17,7 +18,7 @@ namespace Xamarin.Forms.Core.XamlC
//only used in unit tests to make sure the compiled InitializeComponent is invoked
class IsCompiledTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
if (value != "IsCompiled?")
throw new Exception();
Expand Down
Expand Up @@ -6,13 +6,16 @@
using Mono.Cecil.Cil;

using Xamarin.Forms.Xaml;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Core.XamlC
{
class LayoutOptionsConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

do {
if (string.IsNullOrEmpty(value))
break;
Expand Down
Expand Up @@ -12,8 +12,10 @@ namespace Xamarin.Forms.Core.XamlC
{
class ListStringTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

if (value == null) {
yield return Instruction.Create(OpCodes.Ldnull);
yield break;
Expand Down
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;

using Mono.Cecil;
using Mono.Cecil.Cil;

using static Mono.Cecil.Cil.Instruction;
using static Mono.Cecil.Cil.OpCodes;

using Xamarin.Forms.Build.Tasks;
using Xamarin.Forms.Xaml;

namespace Xamarin.Forms.Core.XamlC
{
class RDSourceTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;
var body = context.Body;

INode rootNode = node;
while (!(rootNode is ILRootNode))
rootNode = rootNode.Parent;

var rdNode = node.Parent as IElementNode;

var rootTargetPath = GetPathForType(module, ((ILRootNode)rootNode).TypeReference);
var uri = new Uri(value, UriKind.Relative);

var resourceId = ResourceDictionary.RDSourceTypeConverter.GetResourceId(uri, rootTargetPath, s => GetResourceIdForPath(module, s));
//abuse the converter, produce some side effect, but leave the stack untouched
//public void SetAndLoadSource(Uri value, string resourceID, Assembly assembly, System.Xml.IXmlLineInfo lineInfo)
yield return Create(Ldloc, context.Variables[rdNode]); //the resourcedictionary
foreach (var instruction in (new UriTypeConverter()).ConvertFromString(value, context, node))
yield return instruction; //the Uri

//keep the Uri for later
yield return Create(Dup);
var uriVarDef = new VariableDefinition(module.ImportReference(typeof(Uri)));
body.Variables.Add(uriVarDef);
yield return Create(Stloc, uriVarDef);

yield return Create(Ldstr, resourceId); //resourceId

var getTypeFromHandle = module.ImportReference(typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) }));
var getAssembly = module.ImportReference(typeof(Type).GetProperty("Assembly").GetGetMethod());
yield return Create(Ldtoken, module.ImportReference(((ILRootNode)rootNode).TypeReference));
yield return Create(Call, module.ImportReference(getTypeFromHandle));
yield return Create(Callvirt, module.ImportReference(getAssembly)); //assembly

foreach (var instruction in node.PushXmlLineInfo(context))
yield return instruction; //lineinfo

var setAndLoadSource = module.ImportReference(typeof(ResourceDictionary).GetMethod("SetAndLoadSource"));
yield return Create(Callvirt, module.ImportReference(setAndLoadSource));

//ldloc the stored uri as return value
yield return Create(Ldloc, uriVarDef);
}

static string GetPathForType(ModuleDefinition module, TypeReference type)
{
foreach (var ca in type.Module.GetCustomAttributes()) {
if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference(typeof(XamlResourceIdAttribute))))
continue;
if (!TypeRefComparer.Default.Equals(ca.ConstructorArguments[2].Value as TypeReference, type))
continue;
return ca.ConstructorArguments[1].Value as string;
}
return null;
}

static string GetResourceIdForPath(ModuleDefinition module, string path)
{
foreach (var ca in module.GetCustomAttributes()) {
if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference(typeof(XamlResourceIdAttribute))))
continue;
if (ca.ConstructorArguments[1].Value as string != path)
continue;
return ca.ConstructorArguments[0].Value as string;
}
return null;
}
}
}
Expand Up @@ -6,13 +6,16 @@
using Mono.Cecil.Cil;

using Xamarin.Forms.Xaml;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Core.XamlC
{
class RectangleTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

if (string.IsNullOrEmpty(value))
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
double x, y, w, h;
Expand Down
Expand Up @@ -6,13 +6,16 @@
using Mono.Cecil.Cil;

using Xamarin.Forms.Xaml;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Core.XamlC
{
class ThicknessTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

if (!string.IsNullOrEmpty(value)) {
double l, t, r, b;
var thickness = value.Split(',');
Expand Down
Expand Up @@ -12,8 +12,10 @@ namespace Xamarin.Forms.Core.XamlC
{
class TypeTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

if (string.IsNullOrEmpty(value))
goto error;

Expand All @@ -40,5 +42,4 @@ public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition
throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Type)}", node);
}
}

}
17 changes: 12 additions & 5 deletions Xamarin.Forms.Build.Tasks/CompiledConverters/UriTypeConverter.cs
Expand Up @@ -4,25 +4,32 @@

using Mono.Cecil;
using Mono.Cecil.Cil;

using static Mono.Cecil.Cil.Instruction;
using static Mono.Cecil.Cil.OpCodes;

using Xamarin.Forms.Xaml;
using Xamarin.Forms.Build.Tasks;

namespace Xamarin.Forms.Core.XamlC
{
class UriTypeConverter : ICompiledTypeConverter
{
public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;

if (string.IsNullOrWhiteSpace(value)) {
yield return Instruction.Create(OpCodes.Ldnull);
yield return Create(Ldnull);
yield break;
}

var uriCtor = module.ImportReference(typeof(Uri)).Resolve().Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 2 && md.Parameters[1].ParameterType.FullName == "System.UriKind");
var uriCtorRef = module.ImportReference(uriCtor);

yield return Instruction.Create(OpCodes.Ldstr, value);
yield return Instruction.Create(OpCodes.Ldc_I4_0); //UriKind.RelativeOrAbsolute
yield return Instruction.Create(OpCodes.Newobj, uriCtorRef);
yield return Create(Ldstr, value);
yield return Create(Ldc_I4_0); //UriKind.RelativeOrAbsolute
yield return Create(Newobj, uriCtorRef);
}
}
}
12 changes: 5 additions & 7 deletions Xamarin.Forms.Build.Tasks/NodeILExtensions.cs
Expand Up @@ -58,7 +58,7 @@ static class NodeILExtensions
var compiledConverter = Activator.CreateInstance (compiledConverterType);
var converter = typeof(ICompiledTypeConverter).GetMethods ().FirstOrDefault (md => md.Name == "ConvertFromString");
var instructions = (IEnumerable<Instruction>)converter.Invoke (compiledConverter, new object[] {
node.Value as string, context.Body.Method.Module, node as BaseNode});
node.Value as string, context, node as BaseNode});
foreach (var i in instructions)
yield return i;
if (targetTypeRef.IsValueType && boxValueTypes)
Expand Down Expand Up @@ -310,20 +310,18 @@ public static IEnumerable<Instruction> PushXmlLineInfo(this INode node, ILContex
var module = context.Body.Method.Module;

var xmlLineInfo = node as IXmlLineInfo;
if (xmlLineInfo == null)
{
if (xmlLineInfo == null) {
yield return Instruction.Create(OpCodes.Ldnull);
yield break;
}
MethodReference ctor;
if (xmlLineInfo.HasLineInfo())
{
if (xmlLineInfo.HasLineInfo()) {
yield return Instruction.Create(OpCodes.Ldc_I4, xmlLineInfo.LineNumber);
yield return Instruction.Create(OpCodes.Ldc_I4, xmlLineInfo.LinePosition);
ctor = module.ImportReference(typeof (XmlLineInfo).GetConstructor(new[] { typeof (int), typeof (int) }));
ctor = module.ImportReference(typeof(XmlLineInfo).GetConstructor(new[] { typeof(int), typeof(int) }));
}
else
ctor = module.ImportReference(typeof (XmlLineInfo).GetConstructor(new Type[] { }));
ctor = module.ImportReference(typeof(XmlLineInfo).GetConstructor(new Type[] { }));
yield return Instruction.Create(OpCodes.Newobj, ctor);
}

Expand Down
1 change: 1 addition & 0 deletions Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
Expand Up @@ -101,6 +101,7 @@
<Compile Include="CompiledMarkupExtensions\NullExtension.cs" />
<Compile Include="GetTasksAbi.cs" />
<Compile Include="CompiledConverters\UriTypeConverter.cs" />
<Compile Include="CompiledConverters\RDSourceTypeConverter.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Target Name="AfterBuild">
Expand Down

0 comments on commit 43143bd

Please sign in to comment.