chore: BindableType preserves properties and more - #22017
Conversation
|
🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-22017/docs/index.html |
|
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-22017/wasm-skia-net9/index.html |
|
The build 186025 found UI Test snapshots differences: Details
|
|
|
47c9883 to
5845044
Compare
|
🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-22017/docs/index.html |
1 similar comment
|
🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-22017/docs/index.html |
|
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-22017/wasm-skia-net9/index.html |
|
|
5845044 to
3890a29
Compare
|
🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-22017/docs/index.html |
3890a29 to
31b1df5
Compare
Context: unoplatform/uno#22017 While running the uno.chefs app on macOS under NativeAOT: dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop -bl \ Chefs/Chefs.csproj -p:SelfContained=true -p:PublishAot=true -p:IsAotCompatible=true -p:UseSkiaRendering=true \ -p:IlcGenerateMapFile=true -p:IlcGenerateMstatFile=true -p:IlcGenerateDgmlFile=true -p:IlcGenerateMetadataLog=true \ -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs Console output would contain the following errors: fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [Uno.Toolkit.UI:ProgressExtensions.IsActive] property getter does not exist on type [Microsoft.UI.Xaml.Controls.ContentControl] The `Uno.Toolkit.UI:ProgressExtensions.IsActive` property is an *attached* property, from `ProgressExtensions.IsActive`: static partial class ProgressExtensions { public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached( "IsActive", typeof(bool), typeof(ProgressExtensions), new PropertyMetadata(false, IsActiveChanged)); public static bool GetIsActive(FrameworkElement element) => …; public static void SetIsActive(FrameworkElement element, bool value) => …; } Attached properties work by using Reflection to look for methods with `Get` and `Set` prefixes before the property name, in this case `GetIsActive()` and `SetIsActive()`. NativeAOT cannot statically determine that `GetIsActive()` and `SetIsActive()` are used, and thus doesn't emit any reflection metadata for these members. This can be verified by consulting the generated `Chefs.metadata.csv` file, which only contains these entries for `GetIsActive` and `SetIsActive`: 3424256a, ConstantStringValue, "GetIsActive", "" The [`[DynamicDependency]`][0] custom attribute can be used to inform NativeAOT of this dependency, by listing the names of members which should be preserved: static partial class ProgressExtensions { [DynamicDependency(nameof(GetIsActive))] [DynamicDependency(nameof(SetIsActive))] public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached( "IsActive", typeof(bool), typeof(ProgressExtensions), new PropertyMetadata(false, IsActiveChanged)); } With this change in place, the originating failure message is no longer emitted, and `Chefs.metadata.csv` contains: 500b1f0b, Method, "System.Boolean GetIsActive(Microsoft.UI.Xaml.FrameworkElement)", "34141647 56141653 620c0acd" 500b1f1a, Method, "System.Void SetIsActive(Microsoft.UI.Xaml.FrameworkElement, System.Boolean)", "3414165f 5614166b 620c0acd 6206708f" 34141647, ConstantStringValue, "GetIsActive", "" 3414165f, ConstantStringValue, "SetIsActive", "" 4214168b, CustomAttribute, "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.[HasThis] System.Void .ctor(System.String)(\"GetIsActive\")(ctor: Internal.Metadata.NativeFormat.Handle", "6c1c41df 34141647" 42141695, CustomAttribute, "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.[HasThis] System.Void .ctor(System.String)(\"SetIsActive\")(ctor: Internal.Metadata.NativeFormat.Handle", "6c1c41df 3414165f" Review all usage of `DependencyProperty.RegisterAttached()` and add `[DynamicDependency]` attributes as appropriate. [0]: https://learn.microsoft.comdotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute?view=net-10.0
|
🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-22017/docs/index.html |
|
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-22017/wasm-skia-net9/index.html |
|
The build 186212 found UI Test snapshots differences: Details
|
|
|
Context: unoplatform/uno.chefs#1712 Context: #21920 Context: unoplatform/uno.extensions#2966 Context: unoplatform/uno.extensions#2969 Context: https://discord.com/channels/732297728826277939/732297837953679412/1444909702512119899 While running the uno.chefs app on macOS under NativeAOT: dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop -bl \ Chefs/Chefs.csproj \ -p:SelfContained=true -p:PublishAot=true -p:IsAotCompatible=true -p:UseSkiaRendering=true \ -p:IlcGenerateMapFile=true -p:IlcGenerateMstatFile=true -p:IlcGenerateDgmlFile=true \ -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs Console output would contain the following errors: fail: Uno.UI.Dispatching.NativeDispatcher[0] The [TabNavigation] property getter does not exist on type [Microsoft.UI.Xaml.Controls.FlipView] fail: Uno.UI.Dispatching.NativeDispatcher[0] The [Y] property getter does not exist on type [Windows.Foundation.Point] fail: Uno.UI.Dispatching.NativeDispatcher[0] The [X] property getter does not exist on type [Windows.Foundation.Point] At this point, a description of the relation between NativeAOT and Reflection would be useful. *Parts* of System.Reflection can be used. Which parts *can* be used, and how to use them, is oddly *not* well specified in the [Native AOT deployment][0] documentation. What doesn't work is outlined in the [limitations][1] section: > * No dynamic loading, for example, `Assembly.LoadFile`. > * No run-time code generation, for example, `System.Reflection.Emit`. What *always* appears to work is: * `Type.GetType()` returns a `Type` instance. What ***can*** work is: * `Type.GetType(string)`, *so long as* an assembly-qualified name is used as a *string constant* // bad string name = GetSomeTypeName(); Type type = Type.GetType(name); // good Type type = Type.GetType("My.Example.TypeName, ExampleAssembly"); * `Type.GetMethod(string)`, but see fine print below. * `Type.GetProperty(string)`, but see fine print below. * `MethodInfo.Invoke()` * … **The Fine Print**: in order for many Reflection-based APIs such as `Type.GetMethod(string)` to work, the program must contain "reflection metadata." We can now describe, briefly, what NativeAOT does: 1. Accept IL/assemblies as input 2. Internally trim the IL, optimizes them, etc. 3. Generates a native binary containing two separate bits of data: * Native machine code for execution * "reflection metadata" You can get an inkling of what methods survived trimming and become native code by using `dotnet publish -p:IlcGenerateMstatFile=true …`, which will produce a `.mstat` file, a PE file which references every type and member which is in the native binary, ***after*** inlining. (Meaning if a member is inlined, it *won't* be present!) Use e.g. `monodis --memberref App.mstat` to list post-trimmed members. You can get an inkling of the "reflection metadata" that is present by using `dotnet publish -p:IlcGenerateMetadataLog=true …`, which produces a `.metadata.csv` file. It's not particularly scrutible, but can be used to verify observed behavior. Consider one of the above failure messages: The [TabNavigation] property getter does not exist on type [Microsoft.UI.Xaml.Controls.FlipView] An obvious question to ask: is the `TabNavigation` property in the reflection metadata? Search for `"TabNavigation"` (with quotes!), and it's *not* in there. Compare to when this commit is in use, and: 66087019, Property, "TabNavigation", "3410be9a 6810bea8 5410beae 5410beb2" 3410be9a, ConstantStringValue, "TabNavigation", "" The 3rd column is the property name `TabNavigation`, while the 2nd column is the type; for properties, we want `Property`. The 4th column is "Children", one of which is: 5410beae, MethodSemantics, "Getter : [HasThis] Microsoft.UI.Xaml.Input.KeyboardNavigationMode get_TabNavigation()", "50086c65" which at least verifies that `TabNavigation` is in play. Programs can control what is contained within reflection metadata by using string constants with Reflection APIs, for example: typeof(SomeKnownType).GetMethod("ConstantName") will ensure that reflection metadata contains `SomeKnownType.ConstantName()`, and `ConstantName()` will be returned from `typeof(SomeKnownType).GetMethods()` and can be invoked with `MethodInfo.Invoke()`. If you *don't* use `typeof(SomeKnownType)` or string constants with `Type.GetType(string)` or `type.GetMethod("constant")`, then you need to "suggest" that some pieces of information be added to reflection metadata. The two predominant ways to do this are via the custom attributes: * [`DynamicallyAccessedMembersAttribute`][2] * [`DynamicDependencyAttribute`][3] From the Uno perspective, enter `BindingPropertyHelper`: *everything* is based on Reflection! Fortunately NativeAOT "supports" Reflection! (See above.) *Unfortunately*, we now need to convince NativeAOT to store the reflection metadata Uno requires in order to work! Various other PRs have dealt with some of this, such as #21920. Returning to the original failure messages, e.g. fail: Uno.UI.Dispatching.NativeDispatcher[0] The [TabNavigation] property getter does not exist on type [Microsoft.UI.Xaml.Controls.FlipView] During the app build, `src/SourceGenerators/Uno.UI.SourceGenerators` generates a `BindableMetadata.g.cs` file which will mention `[Bindable]` types, e.g. for `Microsoft.UI.Xaml.Controls.FlipView`: /// <summary> /// Builder for Microsoft.UI.Xaml.Controls.FlipView /// </summary> [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute] static class MetadataBuilder_239 { internal static global::Uno.UI.DataBinding.IBindableType Build(global::Uno.UI.DataBinding.BindableType parent) { var bindableType = parent ?? new global::Uno.UI.DataBinding.BindableType(151, typeof(global::Microsoft.UI.Xaml.Controls.FlipView)); // … } private static object CreateInstance() => new global::Microsoft.UI.Xaml.Controls.FlipView(); } `BindableMetadata.g.cs` was needed in the ye olde Xamarin world order to tell *its* linker to e.g. "preserve the `FlipView` default ctor!" *Now*, we can build upon this infrastructure and update `BindableType` to request that Reflection information regarding properties, fields, and constructors be retained. We can do this by updating the `BindableType` constructor: partial class BindableType { internal const DynamicallyAccessedMemberTypes TypeRequirements = DynamicallyAccessedMemberTypes.PublicProperties | …; public BindableType(int estimatedPropertySize, [DynamicallyAccessedMembers(TypeRequirements)] Type sourceType) => … } *Because* we have `BindableMetadata.g.cs` *and* because it provides `typeof(FlipView)`, this directs NativeAOT to make property and other reflection metadata available to the app, *fixing* the error messages. Expand this addition of `[DynamicallyAccessedMembers]` to also include `BindableProperty` and related types. Note: the IL2111 warning "Method 'System.Type.TypeInitializer.get' …" is *caused by* `typeof(Type)` (?!), which is particularly odd given that we don't use `Type.TypeInitializer`! Thanks to Alexander Köplinger for [looking into this][4]. [0]: https://learn.microsoft.comdotnet/core/deploying/native-aot [1]: https://learn.microsoft.comdotnet/core/deploying/native-aot/#limitations-of-native-aot-deployment [2]: https://learn.microsoft.comdotnet/api/system.diagnostics.codeanalysis.dynamicallyaccessedmembersattribute?view=net-10.0 [3]: https://learn.microsoft.comdotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute?view=net-10.0 [4]: https://discord.com/channels/732297728826277939/732297837953679412/1445773677152174150
31b1df5 to
559b1f7
Compare
|
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-22017/wasm-skia-net9/index.html |
|
🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-22017/docs/index.html |
|
The build 186269 found UI Test snapshots differences: Details
|
Context: unoplatform/uno#22017 Context: 1f09dd1 While running the uno.chefs app on macOS under NativeAOT: dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop -bl \ Chefs/Chefs.csproj -p:SelfContained=true -p:PublishAot=true -p:IsAotCompatible=true -p:UseSkiaRendering=true \ -p:IlcGenerateMapFile=true -p:IlcGenerateMstatFile=true -p:IlcGenerateDgmlFile=true -p:IlcGenerateMetadataLog=true \ -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs Console output would contain the following errors: fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [Uno.Toolkit.UI:ProgressExtensions.IsActive] property getter does not exist on type [Microsoft.UI.Xaml.Controls.ContentControl] The `Uno.Toolkit.UI:ProgressExtensions.IsActive` property is an *attached* property, from `ProgressExtensions.IsActive`: static partial class ProgressExtensions { public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached( "IsActive", typeof(bool), typeof(ProgressExtensions), new PropertyMetadata(false, IsActiveChanged)); public static bool GetIsActive(FrameworkElement element) => …; public static void SetIsActive(FrameworkElement element, bool value) => …; } Attached properties work by using Reflection to look for methods with `Get` and `Set` prefixes before the property name, in this case `GetIsActive()` and `SetIsActive()`. NativeAOT cannot statically determine that `GetIsActive()` and `SetIsActive()` are used, and thus doesn't emit any reflection metadata for these members. This can be verified by consulting the generated `Chefs.metadata.csv` file, which only contains these entries for `GetIsActive` and `SetIsActive`: 3424256a, ConstantStringValue, "GetIsActive", "" The [`[DynamicDependency]`][0] custom attribute can be used to inform NativeAOT of this dependency, by listing the names of members which should be preserved: static partial class ProgressExtensions { [DynamicDependency(nameof(GetIsActive))] [DynamicDependency(nameof(SetIsActive))] public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached( "IsActive", typeof(bool), typeof(ProgressExtensions), new PropertyMetadata(false, IsActiveChanged)); } With this change in place, the originating failure message is no longer emitted, and `Chefs.metadata.csv` contains: 500b1f0b, Method, "System.Boolean GetIsActive(Microsoft.UI.Xaml.FrameworkElement)", "34141647 56141653 620c0acd" 500b1f1a, Method, "System.Void SetIsActive(Microsoft.UI.Xaml.FrameworkElement, System.Boolean)", "3414165f 5614166b 620c0acd 6206708f" 34141647, ConstantStringValue, "GetIsActive", "" 3414165f, ConstantStringValue, "SetIsActive", "" 4214168b, CustomAttribute, "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.[HasThis] System.Void .ctor(System.String)(\"GetIsActive\")(ctor: Internal.Metadata.NativeFormat.Handle", "6c1c41df 34141647" 42141695, CustomAttribute, "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.[HasThis] System.Void .ctor(System.String)(\"SetIsActive\")(ctor: Internal.Metadata.NativeFormat.Handle", "6c1c41df 3414165f" Review all usage of `DependencyProperty.RegisterAttached()` and add `[DynamicDependency]` attributes as appropriate. Of note: * Commit 1f09dd1 *commented out* many of the `[DynamicDependency]` annotations within `AutoLayout.Properties.cs`. Some of these have already been undone, e.g. f16d514, and this commit uncomments the remaining `[DynamicDependency]` annotations. * There are now two separate patterns for providing `[DynamicDependency]`: 1. Provide a `[DynamicDependency]` on the attached property for the `Get` method, and then `[DynamicDependency]` on the `Get` method (to the `Set` method), and a `[DynamicDependency]` on the `Set` method (to the `Get` method): partial class DeclType { [DynamicDependency(nameof(GetAttached))] public static readonly DependencyProperty AttachedProperty = DependencyProperty.RegisterAttached( "Attached" typeof(T), typeof(DeclType), new PropertyMetadata(…)); [DynamicDependency(nameof(SetAttached))] public static T GetAttached(DependencyObject element) => … [DynamicDependency(nameof(SetAttached))] public static void SetAttached(DependencyObject element, T value) => … } 2. Provide `[DynamicDependency]` for *both* the `Get` and `Set` methods on the attached property: partial class DeclType { [DynamicDependency(nameof(GetAttached))] [DynamicDependency(nameof(SetAttached))] public static readonly DependencyProperty AttachedProperty = DependencyProperty.RegisterAttached( "Attached" typeof(T), typeof(DeclType), new PropertyMetadata(…)); public static T GetAttached(DependencyObject element) => … public static void SetAttached(DependencyObject element, T value) => … } Both of these are fine, but @jonpryor personally finds the latter easier to review. At least one "typo" was found in the current review. [0]: https://learn.microsoft.comdotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute?view=net-10.0
Context: unoplatform/uno#22017 Context: 1f09dd1 While running the uno.chefs app on macOS under NativeAOT: dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop -bl \ Chefs/Chefs.csproj -p:SelfContained=true -p:PublishAot=true -p:IsAotCompatible=true -p:UseSkiaRendering=true \ -p:IlcGenerateMapFile=true -p:IlcGenerateMstatFile=true -p:IlcGenerateDgmlFile=true -p:IlcGenerateMetadataLog=true \ -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs Console output would contain the following errors: fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [Uno.Toolkit.UI:ProgressExtensions.IsActive] property getter does not exist on type [Microsoft.UI.Xaml.Controls.ContentControl] The `Uno.Toolkit.UI:ProgressExtensions.IsActive` property is an *attached* property, from `ProgressExtensions.IsActive`: static partial class ProgressExtensions { public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached( "IsActive", typeof(bool), typeof(ProgressExtensions), new PropertyMetadata(false, IsActiveChanged)); public static bool GetIsActive(FrameworkElement element) => …; public static void SetIsActive(FrameworkElement element, bool value) => …; } Attached properties work by using Reflection to look for methods with `Get` and `Set` prefixes before the property name, in this case `GetIsActive()` and `SetIsActive()`. NativeAOT cannot statically determine that `GetIsActive()` and `SetIsActive()` are used, and thus doesn't emit any reflection metadata for these members. This can be verified by consulting the generated `Chefs.metadata.csv` file, which only contains these entries for `GetIsActive` and `SetIsActive`: 3424256a, ConstantStringValue, "GetIsActive", "" The [`[DynamicDependency]`][0] custom attribute can be used to inform NativeAOT of this dependency, by listing the names of members which should be preserved: static partial class ProgressExtensions { [DynamicDependency(nameof(GetIsActive))] [DynamicDependency(nameof(SetIsActive))] public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached( "IsActive", typeof(bool), typeof(ProgressExtensions), new PropertyMetadata(false, IsActiveChanged)); } With this change in place, the originating failure message is no longer emitted, and `Chefs.metadata.csv` contains: 500b1f0b, Method, "System.Boolean GetIsActive(Microsoft.UI.Xaml.FrameworkElement)", "34141647 56141653 620c0acd" 500b1f1a, Method, "System.Void SetIsActive(Microsoft.UI.Xaml.FrameworkElement, System.Boolean)", "3414165f 5614166b 620c0acd 6206708f" 34141647, ConstantStringValue, "GetIsActive", "" 3414165f, ConstantStringValue, "SetIsActive", "" 4214168b, CustomAttribute, "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.[HasThis] System.Void .ctor(System.String)(\"GetIsActive\")(ctor: Internal.Metadata.NativeFormat.Handle", "6c1c41df 34141647" 42141695, CustomAttribute, "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.[HasThis] System.Void .ctor(System.String)(\"SetIsActive\")(ctor: Internal.Metadata.NativeFormat.Handle", "6c1c41df 3414165f" Review all usage of `DependencyProperty.RegisterAttached()` and add `[DynamicDependency]` attributes as appropriate. Of note: * Commit 1f09dd1 *commented out* many of the `[DynamicDependency]` annotations within `AutoLayout.Properties.cs`. Some of these have already been undone, e.g. f16d514, and this commit uncomments the remaining `[DynamicDependency]` annotations. * There are now two separate patterns for providing `[DynamicDependency]`: 1. Provide a `[DynamicDependency]` on the attached property for the `Get` method, and then `[DynamicDependency]` on the `Get` method (to the `Set` method), and a `[DynamicDependency]` on the `Set` method (to the `Get` method): partial class DeclType { [DynamicDependency(nameof(GetAttached))] public static readonly DependencyProperty AttachedProperty = DependencyProperty.RegisterAttached( "Attached" typeof(T), typeof(DeclType), new PropertyMetadata(…)); [DynamicDependency(nameof(SetAttached))] public static T GetAttached(DependencyObject element) => … [DynamicDependency(nameof(SetAttached))] public static void SetAttached(DependencyObject element, T value) => … } 2. Provide `[DynamicDependency]` for *both* the `Get` and `Set` methods on the attached property: partial class DeclType { [DynamicDependency(nameof(GetAttached))] [DynamicDependency(nameof(SetAttached))] public static readonly DependencyProperty AttachedProperty = DependencyProperty.RegisterAttached( "Attached" typeof(T), typeof(DeclType), new PropertyMetadata(…)); public static T GetAttached(DependencyObject element) => … public static void SetAttached(DependencyObject element, T value) => … } Both of these are fine, but @jonpryor personally finds the latter easier to review. At least one "typo" was found in the current review. [0]: https://learn.microsoft.comdotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute?view=net-10.0
chore: BindableType preserves properties and more
Context: unoplatform/uno.chefs#1712
Context: #21920
Context: unoplatform/uno.extensions#2966
Context: unoplatform/uno.extensions#2969
Context: https://discord.com/channels/732297728826277939/732297837953679412/1444909702512119899
While running the uno.chefs app on macOS under NativeAOT:
Console output would contain the following errors:
At this point, a description of the relation between NativeAOT and
Reflection would be useful.
Parts of System.Reflection can be used. Which parts can be used,
and how to use them, is oddly not well specified in the
Native AOT deployment documentation.
What doesn't work is outlined in the limitations section:
What always appears to work is:
Type.GetType()returns aTypeinstance.What can work is:
Type.GetType(string), so long as an assembly-qualified nameis used as a string constant
Type.GetMethod(string), but see fine print below.Type.GetProperty(string), but see fine print below.MethodInfo.Invoke()…
The Fine Print: in order for many Reflection-based APIs such as
Type.GetMethod(string)to work, the program must contain"reflection metadata."
We can now describe, briefly, what NativeAOT does:
Accept IL/assemblies as input
Internally trim the IL, optimizes them, etc.
Generates a native binary containing two separate bits of data:
You can get an inkling of what methods survived trimming and become
native code by using
dotnet publish -p:IlcGenerateMapFile=true …,which will produce a
.mstatfile, a PE file which references everytype and member which is in the native binary, after inlining.
(Meaning if a member is inlined, it won't be present!)
Use e.g.
monodis --memberref App.mstatto list post-trimmed members.You can get an inkling of the "reflection metadata" that is present
by using
dotnet publish -p:IlcGenerateMetadataLog=true …, whichproduces a
.metadata.csvfile. It's not particularly scrutible,but can be used to verify observed behavior. Consider one of the above
failure messages:
An obvious question to ask: is the
TabNavigationproperty in thereflection metadata? Search for
"TabNavigation"(with quotes!),and it's not in there. Compare to when this commit is in use, and:
The 3rd column is the property name
TabNavigation, while the 2ndcolumn is the type; for properties, we want
Property. The 4thcolumn is "Children", one of which is:
which at least verifies that
TabNavigationis in play.Programs can control what is contained within reflection metadata
by using string constants with Reflection APIs, for example:
will ensure that reflection metadata contains
SomeKnownType.ConstantName(), andConstantName()will be returnedfrom
typeof(SomeKnownType).GetMethods()and can be invoked withMethodInfo.Invoke().If you don't use
typeof(SomeKnownType)or string constants withType.GetType(string)ortype.GetMethod("constant"), then you needto "suggest" that some pieces of information be added to
reflection metadata.
The two predominant ways to do this are via the custom attributes:
DynamicallyAccessedMembersAttributeDynamicDependencyAttributeFrom the Uno perspective, enter
BindingPropertyHelper: everythingis based on Reflection!
Fortunately NativeAOT "supports" Reflection! (See above.)
Unfortunately, we now need to convince NativeAOT to store the
reflection metadata Uno requires in order to work!
Various other PRs have dealt with some of this, such as #21920.
Returning to the original failure messages, e.g.
During the app build,
src/SourceGenerators/Uno.UI.SourceGeneratorsgenerates a
BindableMetadata.g.csfile which will mention[Bindable]types, e.g. forMicrosoft.UI.Xaml.Controls.FlipView:BindableMetadata.g.cswas needed in the ye olde Xamarin world orderto tell its linker to e.g. "preserve the
FlipViewdefault ctor!"Now, we can build upon this infrastructure and update
BindableTypeto request that Reflection information regarding properties, fields,
and constructors be retained. We can do this by updating the
BindableTypeconstructor:Because we have
BindableMetadata.g.csand because it providestypeof(FlipView), this directs NativeAOT to make property and otherreflection metadata available to the app, fixing the error messages.
Expand this addition of
[DynamicallyAccessedMembers]to also includeBindablePropertyand related types.Note: the IL2111 warning "Method 'System.Type.TypeInitializer.get' …"
is caused by
typeof(Type)(?!), which is particularly odd giventhat we don't use
Type.TypeInitializer!Thanks to Alexander Köplinger for looking into this.