dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
21.97k stars 1.71k forks source link

AbsoluteLayout.LayoutBounds bound to a StaticResource Rect gives InvalidCastException when XamlC enabled on Android #20838

Open KRA2008 opened 6 months ago

KRA2008 commented 6 months ago

Description

I get an "InvalidCastException" when I have an element inside an AbsoluteLayout with its LayoutBounds bound to a Rect defined in a ResourceDictionary and XamlC is enabled on Android. This only happens on Android, it does not happen on iOS. Here is an extremely simple code example that causes the problem:

<AbsoluteLayout BackgroundColor="Green"
                           HeightRequest="200">
        <AbsoluteLayout.Resources>
        <ResourceDictionary>
            <Rect x:Key="iBreakTheApp" X="0" Y="1" Width="1" Height="1"/>
        </ResourceDictionary>
    </AbsoluteLayout.Resources>
    <Label AbsoluteLayout.LayoutFlags="All"
           AbsoluteLayout.LayoutBounds="{StaticResource iBreakTheApp}"
           Text="My bounds breaks the app in release configuration on Android."/>
</AbsoluteLayout>

It happens in Release configuration, but it also happens in Debug configuration with defaults except with XamlC enabled.

Steps to Reproduce

No response

Link to public reproduction project repository

https://github.com/KRA2008/MAUIBoundsBoundToRectOnReleaseAndroid.git

Version with bug

8.0.7 SR2

Is this a regression from previous behavior?

Yes, this used to work in Xamarin.Forms

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

**System.InvalidCastException:** 'Specified cast is not valid.'
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidCastException: Specified cast is not valid.
[mono-rt]    at ExampleApp.MainPage.InitializeComponent() in D:\Repos\PristineExample\ExampleApp\Microsoft.Maui.Controls.SourceGen\Microsoft.Maui.Controls.SourceGen.CodeBehindGenerator\MainPage.xaml.sg.cs:line 23
[mono-rt]    at ExampleApp.MainPage..ctor() in D:\Repos\PristineExample\ExampleApp\MainPage.xaml.cs:line 9
[mono-rt]    at ExampleApp.App..ctor() in D:\Repos\PristineExample\ExampleApp\App.xaml.cs:line 9
[mono-rt]    at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Constructor(Object obj, IntPtr* args)
[mono-rt]    at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
[mono-rt]    at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSiteMain(ServiceCallSite callSite, RuntimeResolverContext argument)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSite(ServiceCallSite callSite, RuntimeResolverContext argument)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
[mono-rt]    at System.Collections.Concurrent.ConcurrentDictionary`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceIdentifier, Microsoft.Extensions.DependencyInjection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.Extensions.DependencyInjection.ServiceProvider.ServiceAccessor, Microsoft.Extensions.DependencyInjection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].GetOrAdd(ServiceIdentifier key, Func`2 valueFactory)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
[mono-rt]    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type serviceType) in D:\a\_work\1\s\src\Core\src\MauiContext.cs:line 68
[mono-rt]    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type serviceType) in D:\a\_work\1\s\src\Core\src\MauiContext.cs:line 68
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[IApplication](IServiceProvider provider)
[mono-rt]    at Microsoft.Maui.MauiApplication.OnCreate() in D:\a\_work\1\s\src\Core\src\Platform\Android\MauiApplication.cs:line 46
[mono-rt]    at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.App.Application.cs:line 1087
[mono-rt]    at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:line 26
ghost commented 6 months ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

jsuarezruiz commented 6 months ago

cc @StephaneDelcroix

simonrozsival commented 6 months ago

XamlC generates the following code:

// Microsoft.Maui.Graphics.Rect rect = default(Microsoft.Maui.Graphics.Rect);
IL_0001: ldloca.s 0
IL_0003: initobj [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect
// ResourceDictionary resourceDictionary = new ResourceDictionary();
IL_0009: newobj instance void [Microsoft.Maui.Controls]Microsoft.Maui.Controls.ResourceDictionary::.ctor()
IL_000e: stloc.1
// ...
// rect.X = 0.0;
IL_0055: ldloca.s 0
IL_0057: ldc.r8 0.0
IL_0060: call instance void [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect::set_X(float64)
// rect.Y = 1.0;
IL_0065: ldloca.s 0
IL_0067: ldc.r8 1
IL_0070: call instance void [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect::set_Y(float64)
// rect.Width = 1.0;
IL_0075: ldloca.s 0
IL_0077: ldc.r8 1
IL_0080: call instance void [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect::set_Width(float64)
// rect.Height = 1.0;
IL_0085: ldloca.s 0
IL_0087: ldc.r8 1
IL_0090: call instance void [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect::set_Height(float64)
// resourceDictionary.Add("iBreakTheApp", (Android.Graphics.Rect)rect);
IL_0095: ldloc.1
IL_0096: ldstr "iBreakTheApp"
IL_009b: ldloc.0
IL_009c: call class [Mono.Android]Android.Graphics.Rect [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect::op_Implicit(valuetype [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect)
IL_00a1: callvirt instance void [Microsoft.Maui.Controls]Microsoft.Maui.Controls.ResourceDictionary::Add(string, object)
// ...
// staticResourceExtension.Key = "iBreakTheApp";
IL_012c: ldloc.2
IL_012d: ldstr "iBreakTheApp"
IL_0132: callvirt instance void [Microsoft.Maui.Controls.Xaml]Microsoft.Maui.Controls.Xaml.StaticResourceExtension::set_Key(string)
// object obj = ((IMarkupExtension)staticResourceExtension).ProvideValue((IServiceProvider)xamlServiceProvider);
IL_0137: ldloc.2
// ...
// label.SetValue(AbsoluteLayout.LayoutBoundsProperty, (Microsoft.Maui.Graphics.Rect)obj);
IL_021f: ldloc.3
IL_0220: ldsfld class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.BindableProperty [Microsoft.Maui.Controls]Microsoft.Maui.Controls.AbsoluteLayout::LayoutBoundsProperty
IL_0225: ldloc.s 10
IL_0227: unbox.any [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect
IL_022c: box [Microsoft.Maui.Graphics]Microsoft.Maui.Graphics.Rect
IL_0231: callvirt instance void [Microsoft.Maui.Controls]Microsoft.Maui.Controls.BindableObject::SetValue(class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.BindableProperty, object)
// ...

For some reason it creates Microsoft.Maui.Graphics.Rect but it converts it into Android.Graphics.Rect using implicit operator before storing it in ResourceDictionary. When it's about to call SetValue, it tries to convert it back to Microsoft.Maui.Graphics.Rect using unbox.any and I assume that's what fails.