[XamlC] use op_implicit where we should (#580)
authorStephane Delcroix <stephane@delcroix.org>
Thu, 1 Dec 2016 21:34:39 +0000 (22:34 +0100)
committerGitHub <noreply@github.com>
Thu, 1 Dec 2016 21:34:39 +0000 (22:34 +0100)
* [XamlC] (passing) test for 48242

* [XamlC] (failing) test for 48242

* [XamlC] more op_implicit conversions

* additional test

* fix rebase

Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs
Xamarin.Forms.Core/ConstraintTypeConverter.cs
Xamarin.Forms.Xaml.UnitTests/Issues/Unreported007.xaml [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/Issues/Unreported007.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/SetValue.xaml
Xamarin.Forms.Xaml.UnitTests/SetValue.xaml.cs
Xamarin.Forms.Xaml.UnitTests/XStatic.xaml
Xamarin.Forms.Xaml.UnitTests/XStatic.xaml.cs
Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj

index 397ea52..44678d2 100644 (file)
@@ -857,6 +857,10 @@ namespace Xamarin.Forms.Build.Tasks
                        // Worst case scenario ? InvalidCastException at runtime
                        if (attached && varValue.VariableType.FullName == "System.Object") 
                                return true;
+                       var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(bpTypeRef, module);
+                       if (implicitOperator != null)
+                               return true;
+
                        return varValue.VariableType.InheritsFromOrImplements(bpTypeRef);
                }
 
@@ -879,9 +883,17 @@ namespace Xamarin.Forms.Build.Tasks
                                foreach (var instruction in valueNode.PushConvertedValue(context, bpRef, valueNode.PushServiceProvider(context, bpRef:bpRef), true, false))
                                        yield return instruction;
                        } else if (elementNode != null) {
-                               yield return Instruction.Create(OpCodes.Ldloc, context.Variables [elementNode]);
-                               if (context.Variables [elementNode].VariableType.IsValueType)
-                                       yield return Instruction.Create(OpCodes.Box, context.Variables [elementNode].VariableType);
+                               var bpTypeRef = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
+                               var varDef = context.Variables[elementNode];
+                               var varType = varDef.VariableType;
+                               var implicitOperator = varDef.VariableType.GetImplicitOperatorTo(bpTypeRef, module);
+                               yield return Instruction.Create(OpCodes.Ldloc, varDef);
+                               if (implicitOperator != null) {
+                                       yield return Instruction.Create(OpCodes.Call, module.Import(implicitOperator));
+                                       varType = module.Import(bpTypeRef);
+                               }
+                               if (varType.IsValueType)
+                                       yield return Instruction.Create(OpCodes.Box, varType);
                        }
 
                        yield return Instruction.Create(OpCodes.Callvirt, module.Import(setValue));
index f0a6cef..d0ccbc5 100644 (file)
@@ -204,9 +204,10 @@ namespace Xamarin.Forms.Build.Tasks
                }
 
                public static MethodReference GetImplicitOperatorTo(this TypeReference fromType, TypeReference toType, ModuleDefinition module)
-               { 
-                       var implicitOperators = fromType.GetMethods(md => md.IsPublic && md.IsStatic && md.IsSpecialName && md.Name == "op_Implicit",
-                                                                                               module).ToList();
+               {
+                       var implicitOperatorsOnFromType = fromType.GetMethods(md => md.IsPublic && md.IsStatic && md.IsSpecialName && md.Name == "op_Implicit", module);
+                       var implicitOperatorsOnToType = toType.GetMethods(md => md.IsPublic && md.IsStatic && md.IsSpecialName && md.Name == "op_Implicit", module);
+                       var implicitOperators = implicitOperatorsOnFromType.Concat(implicitOperatorsOnToType).ToList();
                        if (implicitOperators.Any()) {
                                foreach (var op in implicitOperators) {
                                        var cast = op.Item1;
index 8cc4522..fb0be51 100644 (file)
@@ -11,7 +11,7 @@ namespace Xamarin.Forms
                        if (value != null && double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out size))
                                return Constraint.Constant(size);
 
-                       throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Color)));
+                       throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Constraint)));
                }
        }
 }
\ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Unreported007.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Unreported007.xaml
new file mode 100644 (file)
index 0000000..4132f90
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+               xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+               x:Class="Xamarin.Forms.Xaml.UnitTests.Unreported007">
+
+       <Label Text="Foo" x:Name="label">
+               <RelativeLayout.XConstraint>
+                       <OnPlatform x:TypeArguments="Constraint" Android="{ConstraintExpression Type=Constant,Constant=6}" iOS="{ConstraintExpression Type=Constant,Constant=3}" />
+               </RelativeLayout.XConstraint>
+       </Label>
+</ContentPage>
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Unreported007.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Unreported007.xaml.cs
new file mode 100644 (file)
index 0000000..279deed
--- /dev/null
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+using Xamarin.Forms;
+using Xamarin.Forms.Core.UnitTests;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+       public partial class Unreported007 : ContentPage
+       {
+               public Unreported007()
+               {
+                       InitializeComponent();
+               }
+               public Unreported007(bool useCompiledXaml)
+               {
+                       //this stub will be replaced at compile time
+               }
+
+               [TestFixture]
+               class Tests
+               {
+                       [SetUp]
+                       public void Setup()
+                       {
+                               Device.PlatformServices = new MockPlatformServices();
+                       }
+
+                       [TearDown]
+                       public void TearDown()
+                       {
+                               Device.PlatformServices = null;
+                       }
+
+                       [TestCase(true), TestCase(false)]
+                       public void ConstraintsAreEvaluatedWithOnPlatform(bool useCompiledXaml)
+                       {
+                               if (useCompiledXaml)
+                                       MockCompiler.Compile(typeof(Unreported007));
+                               Device.OS = TargetPlatform.iOS;
+                               var page = new Unreported007(useCompiledXaml);
+                               Assert.That(RelativeLayout.GetXConstraint(page.label), Is.TypeOf<Constraint>());
+                               Assert.AreEqual(3, RelativeLayout.GetXConstraint(page.label).Compute(null));
+                       }
+               }
+       }
+}
index 4beec04..fd11bd5 100644 (file)
                </ContentView>
                <local:MockViewWithValues x:Name="mockView0" UShort="32" ADecimal="42" />
                <local:ViewWithEnums x:Name="enums" IntEnum="Foo" ByteEnum="Bar" />
+               <local:MockViewWithValues x:Name="implicit0">
+                       <local:MockViewWithValues.BPBar>
+                               <local:SV_Foo Value="Bar"/>
+                       </local:MockViewWithValues.BPBar>
+               </local:MockViewWithValues>
+               <!--<local:MockViewWithValues x:Name="implicit1" BPFoo="Foo" />-->
+               <local:MockViewWithValues x:Name="implicit2">
+                       <local:MockViewWithValues.Bar>
+                               <local:SV_Foo Value="Bar"/>
+                       </local:MockViewWithValues.Bar>
+               </local:MockViewWithValues>
+               <!--<local:MockViewWithValues x:Name="implicit3" Foo="Foo" />-->
        </StackLayout>
-</ContentPage>
\ No newline at end of file
+</ContentPage>
index c6c362c..0f632e3 100644 (file)
@@ -18,6 +18,38 @@ namespace Xamarin.Forms.Xaml.UnitTests
        { 
                public UInt16 UShort { get; set; }
                public decimal ADecimal { get; set; }
+               public SV_Foo Foo { get; set; }
+               public string Bar { get; set; }
+
+               public static readonly BindableProperty BPFooProperty =
+                       BindableProperty.Create("BPFoo", typeof(SV_Foo), typeof(MockViewWithValues), default(SV_Foo));
+
+               public SV_Foo BPFoo {
+                       get { throw new NotImplementedException(); }
+                       set { throw new NotImplementedException(); }
+               }
+
+               public static readonly BindableProperty BPBarProperty =
+                       BindableProperty.Create("BPBar", typeof(string), typeof(MockViewWithValues), default(string));
+
+               public string BPBar {
+                       get { throw new NotImplementedException(); }
+                       set { throw new NotImplementedException(); }
+               }
+       }
+
+       public class SV_Foo
+       {
+               public string Value { get; set; }
+               public static implicit operator SV_Foo(string value)
+               {
+                       return new SV_Foo { Value = value };
+               }
+
+               public static implicit operator string(SV_Foo foo)
+               {
+                       return foo.Value;
+               }
        }
 
        public enum IntEnum
@@ -276,6 +308,36 @@ namespace Xamarin.Forms.Xaml.UnitTests
                                Assert.AreEqual(IntEnum.Foo, page.enums.IntEnum);
                                Assert.AreEqual(ByteEnum.Bar, page.enums.ByteEnum);
                        }
+
+                       public void SetValueWithImplicitOperatorOnSource(bool useCompiledXaml)
+                       {
+                               var page = new SetValue(useCompiledXaml);
+                               Assert.AreEqual("Bar", page.implicit0.GetValue(MockViewWithValues.BPBarProperty));
+                       }
+
+                       //[TestCase(false)]
+                       //[TestCase(true)]
+                       //public void SetValueWithImplicitOperatorOnTarget(bool useCompiledXaml)
+                       //{
+                       //      var page = new SetValue(useCompiledXaml);
+                       //      Assert.AreEqual("Foo", ((SV_Foo)page.implicit1.GetValue(MockViewWithValues.BPFooProperty)).Value);
+                       //}
+
+                       [TestCase(false)]
+                       [TestCase(true)]
+                       public void SetWithImplicitOperatorOnSource(bool useCompiledXaml)
+                       {
+                               var page = new SetValue(useCompiledXaml);
+                               Assert.AreEqual("Bar", page.implicit2.Bar);
+                       }
+
+                       //[TestCase(false)]
+                       //[TestCase(true)]
+                       //public void SetWithImplicitOperatorOnTarget(bool useCompiledXaml)
+                       //{
+                       //      var page = new SetValue(useCompiledXaml);
+                       //      Assert.AreEqual("Foo", page.implicit3.Foo.Value);
+                       //}
                }
        }
-}
\ No newline at end of file
+}
index 32d2945..cf68525 100644 (file)
@@ -3,6 +3,9 @@
                         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                         xmlns:local="clr-namespace:Xamarin.Forms.Xaml.UnitTests"
                         x:Class="Xamarin.Forms.Xaml.UnitTests.XStatic">
+       <ContentPage.ToolbarItems>
+               <ToolbarItem Icon="{x:Static local:MockxStatic.MockFieldRef}" />
+       </ContentPage.ToolbarItems>
        <StackLayout>
                <Label x:Name="staticproperty"
                           Text="{x:Static Member=local:MockxStatic.MockStaticProperty}" />
@@ -16,5 +19,7 @@
                                Text="{x:Static local:MockxStatic.MockField}"/>
                <ScrollView x:Name="enuM"
                                Orientation="{x:Static ScrollOrientation.Both}"/>
+               <Label x:Name="field2"
+                               Text="{x:Static local:MockxStatic.MockFieldRef}" />
        </StackLayout>
 </ContentPage>
\ No newline at end of file
index d8a466e..19c6263 100644 (file)
@@ -3,11 +3,18 @@ using Xamarin.Forms.Core.UnitTests;
 
 namespace Xamarin.Forms.Xaml.UnitTests
 {
+
+       public class Icons
+       {
+               public const string CLOSE = "ic_close.png";
+       }
+
        public class MockxStatic
        {
                public static string MockStaticProperty { get { return "Property"; } }
                public const string MockConstant = "Constant";
                public static string MockField = "Field";
+               public static string MockFieldRef = Icons.CLOSE;
                public string InstanceProperty { get { return "InstanceProperty"; } }
                public static readonly Color BackgroundColor = Color.Fuchsia;
        }
@@ -102,6 +109,23 @@ namespace Xamarin.Forms.Xaml.UnitTests
                                var layout = new XStatic(useCompiledXaml);
                                Assert.AreEqual(ScrollOrientation.Both, layout.enuM.Orientation);
                        }
+
+                       [TestCase(false)]
+                       [TestCase(true)]
+                       public void FieldRef(bool useCompiledXaml)
+                       {
+                               var layout = new XStatic(useCompiledXaml);
+                               Assert.AreEqual("ic_close.png", layout.field2.Text);
+                       }
+
+                       [TestCase(false)]
+                       [TestCase(true)]
+                       // https://bugzilla.xamarin.com/show_bug.cgi?id=48242
+                       public void xStaticAndImplicitOperators(bool useCompiledXaml)
+                       {
+                               var layout = new XStatic(useCompiledXaml);
+                               Assert.AreEqual("ic_close.png", layout.ToolbarItems[0].Icon.File);
+                       }
                }
        }
 }
\ No newline at end of file
index 3708d1f..7886649 100644 (file)
     <Compile Include="Issues\Bz48554.xaml.cs">
       <DependentUpon>Bz48554.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Issues\Unreported007.xaml.cs">
+      <DependentUpon>Unreported007.xaml</DependentUpon>
+    </Compile>
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
     <EmbeddedResource Include="Issues\Bz48554.xaml">
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
+    <EmbeddedResource Include="Issues\Unreported007.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
   </ItemGroup>
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />