From fc63d757e85000dd230b7172b8e705eb1a6201f0 Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:22:24 +1100 Subject: [PATCH 1/6] feature: Add central nuget package management --- src/Benchmarks/Directory.Packages.props | 52 ++++++++++ src/Benchmarks/ReactiveUI.Benchmarks.csproj | 9 +- src/Directory.Packages.props | 98 +++++++++++++++++++ src/Directory.build.props | 46 ++++----- .../ReactiveUI.AndroidSupport.csproj | 26 +++-- .../ReactiveUI.AndroidX.csproj | 24 ++--- .../ReactiveUI.Blazor.csproj | 18 ++-- src/ReactiveUI.Blend/ReactiveUI.Blend.csproj | 9 +- .../ReactiveUI.Drawing.csproj | 26 +++-- .../ReactiveUI.Fody.Analyzer.Tests.csproj | 8 +- .../ReactiveUI.Fody.Analyzer.csproj | 10 +- .../ReactiveUI.Fody.Helpers.csproj | 9 +- .../ReactiveUI.Fody.Tests.csproj | 10 +- src/ReactiveUI.Fody/ReactiveUI.Fody.csproj | 5 +- .../ReactiveUI.LeakTests.csproj | 4 +- src/ReactiveUI.Maui/ReactiveUI.Maui.csproj | 25 ++--- .../ReactiveUI.Splat.Tests.csproj | 11 +-- .../ReactiveUI.Testing.csproj | 13 +-- src/ReactiveUI.Tests/ReactiveUI.Tests.csproj | 15 +-- src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj | 9 +- .../ReactiveUI.XamForms.Tests.csproj | 6 +- .../ReactiveUI.XamForms.csproj | 6 +- src/ReactiveUI/ReactiveUI.csproj | 73 +++++--------- 23 files changed, 284 insertions(+), 228 deletions(-) create mode 100644 src/Benchmarks/Directory.Packages.props create mode 100644 src/Directory.Packages.props diff --git a/src/Benchmarks/Directory.Packages.props b/src/Benchmarks/Directory.Packages.props new file mode 100644 index 0000000000..b0efc6eb1a --- /dev/null +++ b/src/Benchmarks/Directory.Packages.props @@ -0,0 +1,52 @@ + + + true + true + + + 0.1.0 + 2.0.0-rc4-build2924 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Benchmarks/ReactiveUI.Benchmarks.csproj b/src/Benchmarks/ReactiveUI.Benchmarks.csproj index 6747ab71c8..ed9548aff9 100644 --- a/src/Benchmarks/ReactiveUI.Benchmarks.csproj +++ b/src/Benchmarks/ReactiveUI.Benchmarks.csproj @@ -1,5 +1,4 @@ - false net472;netcoreapp3.1 @@ -11,15 +10,13 @@ ;1591;1701;1702;1705;CA1822 A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the base package with the base platform implementations - - - - + + @@ -32,4 +29,4 @@ PreserveNewest - + \ No newline at end of file diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000000..c001fd2662 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,98 @@ + + + true + true + + + 6.8.0 + 14.8.12 + 28.0.0.3 + 2.6.6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Directory.build.props b/src/Directory.build.props index 80b2fc2cfb..dc78fa3389 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -18,9 +18,8 @@ https://github.com/reactiveui/reactiveui git $(NoWarn);SA1010;RCS1198;RCS1158;RCS1163;RCS1256;IDE0060;IDE1006;VSSpell001 - - true + true true @@ -38,49 +37,42 @@ - portable - true - - - - - - - - - - - + + + + + + + + + + - - + - - + $(MSBuildThisFileDirectory) - - + - - - - - + + + + - + \ No newline at end of file diff --git a/src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj b/src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj index 19acb364b0..fecc1eaa86 100644 --- a/src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj +++ b/src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj @@ -1,32 +1,28 @@  - MonoAndroid13.0 Provides ReactiveUI extensions for the Android Support Library ReactiveUI.AndroidSupport mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;xamarin;android;forms;monodroid;monotouch;xamarin.android;net; - - - ..\Java.Interop.dll - + + ..\Java.Interop.dll + - - - - - - - - + + + + + + + - - + \ No newline at end of file diff --git a/src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj b/src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj index a2cc9c3307..371833524b 100644 --- a/src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj +++ b/src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj @@ -1,31 +1,27 @@  - MonoAndroid13.0 Provides ReactiveUI extensions for the AndroidX Library ReactiveUI.AndroidX mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;xamarin;android;forms;monodroid;monotouch;xamarin.android;net; - - - ..\Java.Interop.dll - + + ..\Java.Interop.dll + - - - - - - - + + + + + + - - + \ No newline at end of file diff --git a/src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj b/src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj index 109687840f..b6d6b51649 100644 --- a/src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj +++ b/src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj @@ -5,28 +5,22 @@ mvvm;reactiveui;rx;reactive extensions;observable;LINQ;eventsnet;netstandard;blazor;web; $(NoWarn);BL0007; - - + - - + - - + - - + - - + - - + \ No newline at end of file diff --git a/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj b/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj index d424bf5cd1..34ef945ac0 100644 --- a/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj +++ b/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj @@ -1,5 +1,4 @@  - net462;net472;net6.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net8.0-windows10.0.17763.0 ReactiveUI.Blend @@ -9,22 +8,18 @@ mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;Blend;wpf;net; true - - - - + - - + \ No newline at end of file diff --git a/src/ReactiveUI.Drawing/ReactiveUI.Drawing.csproj b/src/ReactiveUI.Drawing/ReactiveUI.Drawing.csproj index a141b3a168..7fba8e3276 100644 --- a/src/ReactiveUI.Drawing/ReactiveUI.Drawing.csproj +++ b/src/ReactiveUI.Drawing/ReactiveUI.Drawing.csproj @@ -7,32 +7,28 @@ A extension to the ReactiveUI platform that provides Splat bitmap operation support. ReactiveUI.Drawing 14.2 - 14.0 - 21.0 - 10.0.17763.0 - 10.0.17763.0 - 6.5 - - + 14.0 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + - - ..\Java.Interop.dll - + + ..\Java.Interop.dll + - - - - + - + \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Analyzer.Test/ReactiveUI.Fody.Analyzer.Tests.csproj b/src/ReactiveUI.Fody.Analyzer.Test/ReactiveUI.Fody.Analyzer.Tests.csproj index 225e1c4d7f..42fa028738 100644 --- a/src/ReactiveUI.Fody.Analyzer.Test/ReactiveUI.Fody.Analyzer.Tests.csproj +++ b/src/ReactiveUI.Fody.Analyzer.Test/ReactiveUI.Fody.Analyzer.Tests.csproj @@ -5,11 +5,11 @@ $(NoWarn);MSB3243 - - + + - + @@ -17,4 +17,4 @@ - + \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Analyzer/ReactiveUI.Fody.Analyzer.csproj b/src/ReactiveUI.Fody.Analyzer/ReactiveUI.Fody.Analyzer.csproj index d8735a8c99..553dee9298 100644 --- a/src/ReactiveUI.Fody.Analyzer/ReactiveUI.Fody.Analyzer.csproj +++ b/src/ReactiveUI.Fody.Analyzer/ReactiveUI.Fody.Analyzer.csproj @@ -8,11 +8,9 @@ False true - - - - + + + - - + \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj index a0e6601084..36331aa0b3 100644 --- a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj +++ b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj @@ -4,20 +4,17 @@ $(TargetFrameworks);net462;net472 Fody extension to generate RaisePropertyChange notifications for properties and ObservableAsPropertyHelper properties. mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;xamarin;android;ios;mac;forms;monodroid;monotouch;xamarin.android;xamarin.ios;xamarin.forms;xamarin.mac;xamarin.tvos;wpf;net;netstandard;net472;uwp;tizen;unoplatform;fody; - ReactiveUI.Fody ..\$(PackageId)\bin\$(Configuration)\ False - - - + + - - + \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj index ffde27361d..30a3f6b216 100644 --- a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj +++ b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj @@ -4,33 +4,27 @@ netstandard2.0 false - - - + - - - - - + \ No newline at end of file diff --git a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj index b3cbca782e..9a45093b85 100644 --- a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj +++ b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj @@ -6,8 +6,7 @@ False mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;xamarin;android;ios;mac;forms;monodroid;monotouch;xamarin.android;xamarin.ios;xamarin.forms;xamarin.mac;xamarin.tvos;wpf;net;netstandard;net472;uwp;tizen;unoplatform;fody; - - + - + \ No newline at end of file diff --git a/src/ReactiveUI.LeakTests/ReactiveUI.LeakTests.csproj b/src/ReactiveUI.LeakTests/ReactiveUI.LeakTests.csproj index 110df71dc7..c5de82f509 100644 --- a/src/ReactiveUI.LeakTests/ReactiveUI.LeakTests.csproj +++ b/src/ReactiveUI.LeakTests/ReactiveUI.LeakTests.csproj @@ -4,9 +4,9 @@ false - + - + \ No newline at end of file diff --git a/src/ReactiveUI.Maui/ReactiveUI.Maui.csproj b/src/ReactiveUI.Maui/ReactiveUI.Maui.csproj index 65896bf468..5034a22788 100644 --- a/src/ReactiveUI.Maui/ReactiveUI.Maui.csproj +++ b/src/ReactiveUI.Maui/ReactiveUI.Maui.csproj @@ -1,5 +1,4 @@  - net7.0;net8.0 $(TargetFrameworks);net7.0-windows10.0.19041.0;net8.0-windows10.0.19041.0 @@ -7,7 +6,6 @@ mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;maui;android;ios;mac;windows;net true $(DefineConstants);IS_MAUI - 11.0 13.1 21.0 @@ -15,24 +13,19 @@ 10.0.17763.0 6.5 - - - $(DefineConstants);WINUI_TARGET - - - - - - + + $(DefineConstants);WINUI_TARGET + + + + - - + + - - - + \ No newline at end of file diff --git a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj index 5580c54709..9f17c551ab 100644 --- a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj +++ b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj @@ -1,17 +1,14 @@  - net472;net6.0 false - - - - + + + - - + \ No newline at end of file diff --git a/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj b/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj index a1609fdfeb..e70d0365ba 100644 --- a/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj +++ b/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj @@ -8,21 +8,18 @@ ReactiveUI.Testing mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;test; - - - ..\Java.Interop.dll - + + ..\Java.Interop.dll + - - + - - + \ No newline at end of file diff --git a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj index 017db2d0d3..28b4b7af6d 100644 --- a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj +++ b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj @@ -1,25 +1,20 @@  - net6.0;net7.0;net8.0 net472;net6.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net8.0-windows10.0.17763.0 $(NoWarn);CS1591 - - + - - - @@ -45,7 +40,7 @@ - + @@ -53,7 +48,6 @@ - @@ -65,7 +59,6 @@ - True @@ -81,11 +74,9 @@ TestFormNotCanActivate.resx - $(DefaultXamlRuntime) - - + \ No newline at end of file diff --git a/src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj b/src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj index 5a6c0c7b9d..b525e902d3 100644 --- a/src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj +++ b/src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj @@ -12,12 +12,10 @@ 10.0.19041.0 10.0.19041.0 - - - + + - @@ -27,5 +25,4 @@ - - + \ No newline at end of file diff --git a/src/ReactiveUI.XamForms.Tests/ReactiveUI.XamForms.Tests.csproj b/src/ReactiveUI.XamForms.Tests/ReactiveUI.XamForms.Tests.csproj index e4117e6d89..ee4dd5d253 100644 --- a/src/ReactiveUI.XamForms.Tests/ReactiveUI.XamForms.Tests.csproj +++ b/src/ReactiveUI.XamForms.Tests/ReactiveUI.XamForms.Tests.csproj @@ -3,12 +3,10 @@ net6.0 false - - + - - + \ No newline at end of file diff --git a/src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj b/src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj index 19bbee81be..f6ec4b7ef3 100644 --- a/src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj +++ b/src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj @@ -5,12 +5,10 @@ ReactiveUI.XamForms mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;xamarin;android;ios;mac;forms;monodroid;monotouch;xamarin.forms;net - - + - - + \ No newline at end of file diff --git a/src/ReactiveUI/ReactiveUI.csproj b/src/ReactiveUI/ReactiveUI.csproj index f19d426158..3fa438d7a4 100644 --- a/src/ReactiveUI/ReactiveUI.csproj +++ b/src/ReactiveUI/ReactiveUI.csproj @@ -9,13 +9,12 @@ $(NoWarn);SYSLIB0011;IDE1006 14.2 - 14.0 - 21.0 - 10.0.17763.0 - 10.0.17763.0 - 6.5 - - + 14.0 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + @@ -23,7 +22,6 @@ - @@ -31,17 +29,15 @@ ..\Java.Interop.dll - - - - - - + + + + + - @@ -49,15 +45,12 @@ - - - @@ -65,20 +58,17 @@ - - - @@ -86,61 +76,52 @@ - - - - - + - - - - - - - - - - - - - - + + + + + + + + + + + - - - + + + - - - + \ No newline at end of file From ecfd10244bf1472ba01490ea18b544ef83dc9211 Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:30:53 +1100 Subject: [PATCH 2/6] Simplify the benchmark import --- src/Benchmarks/Directory.Packages.props | 49 +++------------------ src/Benchmarks/ReactiveUI.Benchmarks.csproj | 10 +---- 2 files changed, 7 insertions(+), 52 deletions(-) diff --git a/src/Benchmarks/Directory.Packages.props b/src/Benchmarks/Directory.Packages.props index b0efc6eb1a..4a39f3d824 100644 --- a/src/Benchmarks/Directory.Packages.props +++ b/src/Benchmarks/Directory.Packages.props @@ -3,50 +3,11 @@ true true - - 0.1.0 - 2.0.0-rc4-build2924 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + diff --git a/src/Benchmarks/ReactiveUI.Benchmarks.csproj b/src/Benchmarks/ReactiveUI.Benchmarks.csproj index ed9548aff9..34ab2ff5fd 100644 --- a/src/Benchmarks/ReactiveUI.Benchmarks.csproj +++ b/src/Benchmarks/ReactiveUI.Benchmarks.csproj @@ -1,11 +1,8 @@ false - net472;netcoreapp3.1 - BenchmarkDotNet.Samples + net8.0 AnyCPU - pdbonly - true Exe ;1591;1701;1702;1705;CA1822 A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the base package with the base platform implementations @@ -18,9 +15,6 @@ - - - @@ -29,4 +23,4 @@ PreserveNewest - \ No newline at end of file + From 231304a3c7b570ba5ac0579aa0d4dbba3d8481e2 Mon Sep 17 00:00:00 2001 From: Glenn <5834289+glennawatson@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:34:20 +1100 Subject: [PATCH 3/6] Update Directory.build.props --- src/Directory.build.props | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index dc78fa3389..331fd93c9f 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -32,7 +32,6 @@ true true true - 2.6.6 - ReactiveUI.Fody - ..\$(PackageId)\bin\$(Configuration)\ - False - - - - - - - - - - \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.DotNet6_0.verified.txt b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.DotNet6_0.verified.txt deleted file mode 100644 index 591b73b116..0000000000 --- a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.DotNet6_0.verified.txt +++ /dev/null @@ -1,33 +0,0 @@ -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.AndroidSupport")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.Tests")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.Winforms")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.Wpf")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.XamForms")] -[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName=".NET 6.0")] -namespace ReactiveUI.Fody.Helpers -{ - [System.AttributeUsage(System.AttributeTargets.Method | System.AttributeTargets.Property)] - public sealed class ObservableAsPropertyAttribute : System.Attribute - { - public ObservableAsPropertyAttribute() { } - } - public static class ObservableAsPropertyExtensions - { - public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable item, TObj source, System.Linq.Expressions.Expression> property, bool deferSubscription = false, System.Reactive.Concurrency.IScheduler? scheduler = null) - where TObj : ReactiveUI.ReactiveObject { } - public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable item, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue, bool deferSubscription = false, System.Reactive.Concurrency.IScheduler? scheduler = null) - where TObj : ReactiveUI.ReactiveObject { } - } - [System.AttributeUsage(System.AttributeTargets.Property)] - public sealed class ReactiveAttribute : System.Attribute - { - public ReactiveAttribute() { } - } - [System.AttributeUsage(System.AttributeTargets.Property)] - public sealed class ReactiveDependencyAttribute : System.Attribute - { - public ReactiveDependencyAttribute(string targetName) { } - public string Target { get; } - public string? TargetProperty { get; set; } - } -} \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.Net4_7.verified.txt b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.Net4_7.verified.txt deleted file mode 100644 index 20ec941c3a..0000000000 --- a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.Net4_7.verified.txt +++ /dev/null @@ -1,33 +0,0 @@ -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.AndroidSupport")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.Tests")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.Winforms")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.Wpf")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ReactiveUI.XamForms")] -[assembly: System.Runtime.Versioning.TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName=".NET Framework 4.7.2")] -namespace ReactiveUI.Fody.Helpers -{ - [System.AttributeUsage(System.AttributeTargets.Method | System.AttributeTargets.Property)] - public sealed class ObservableAsPropertyAttribute : System.Attribute - { - public ObservableAsPropertyAttribute() { } - } - public static class ObservableAsPropertyExtensions - { - public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable item, TObj source, System.Linq.Expressions.Expression> property, bool deferSubscription = false, System.Reactive.Concurrency.IScheduler? scheduler = null) - where TObj : ReactiveUI.ReactiveObject { } - public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable item, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue, bool deferSubscription = false, System.Reactive.Concurrency.IScheduler? scheduler = null) - where TObj : ReactiveUI.ReactiveObject { } - } - [System.AttributeUsage(System.AttributeTargets.Property)] - public sealed class ReactiveAttribute : System.Attribute - { - public ReactiveAttribute() { } - } - [System.AttributeUsage(System.AttributeTargets.Property)] - public sealed class ReactiveDependencyAttribute : System.Attribute - { - public ReactiveDependencyAttribute(string targetName) { } - public string Target { get; } - public string? TargetProperty { get; set; } - } -} \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.cs b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.cs deleted file mode 100644 index 4fe4d74f1c..0000000000 --- a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; -using ReactiveUI.Fody.Helpers; -using ReactiveUI.Tests; -using Xunit; - -namespace ReactiveUI.Fody.Tests.API; - -/// -/// Tests for checking if the Fody public API is not changing. -/// We have a file checked into the repository with the approved public API. -/// If it is changing you'll need to override to make it obvious the API has changed -/// for version changing reasons. -/// -[ExcludeFromCodeCoverage] -public class ApiApprovalTests -{ - /// - /// Checks the version API. - /// - /// A task to monitor the process. - [Fact] - public Task ReactiveUIFody() => typeof(ReactiveAttribute).Assembly.CheckApproval(["ReactiveUI"]); -} diff --git a/src/ReactiveUI.Fody.Tests/ApiExtensions.cs b/src/ReactiveUI.Fody.Tests/ApiExtensions.cs deleted file mode 100644 index 9b65359686..0000000000 --- a/src/ReactiveUI.Fody.Tests/ApiExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using PublicApiGenerator; -using VerifyXunit; - -namespace ReactiveUI.Tests; - -/// -/// A helper for doing API approvals. -/// -[ExcludeFromCodeCoverage] -public static class ApiExtensions -{ - /// - /// Checks to make sure the API is approved. - /// - /// The assembly that is being checked. - /// The namespaces. - /// The caller file path. - /// - /// A Task. - /// - public static async Task CheckApproval(this Assembly assembly, string[] namespaces, [CallerFilePath] string filePath = "") - { - var generatorOptions = new ApiGeneratorOptions { AllowNamespacePrefixes = namespaces }; - var apiText = assembly.GeneratePublicApi(generatorOptions); - var result = await Verifier.Verify(apiText, null, filePath) - .UniqueForRuntimeAndVersion() - .ScrubEmptyLines() - .ScrubLines(l => - l.StartsWith("[assembly: AssemblyVersion(", StringComparison.InvariantCulture) || - l.StartsWith("[assembly: AssemblyFileVersion(", StringComparison.InvariantCulture) || - l.StartsWith("[assembly: AssemblyInformationalVersion(", StringComparison.InvariantCulture) || - l.StartsWith("[assembly: System.Reflection.AssemblyMetadata(", StringComparison.InvariantCulture)); - } -} diff --git a/src/ReactiveUI.Fody.Tests/FodyWeavers.xml b/src/ReactiveUI.Fody.Tests/FodyWeavers.xml deleted file mode 100644 index a1ede75480..0000000000 --- a/src/ReactiveUI.Fody.Tests/FodyWeavers.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/src/ReactiveUI.Fody.Tests/FodyWeavers.xsd b/src/ReactiveUI.Fody.Tests/FodyWeavers.xsd deleted file mode 100644 index 5cedd530b9..0000000000 --- a/src/ReactiveUI.Fody.Tests/FodyWeavers.xsd +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. - - - - - A comma-separated list of error codes that can be safely ignored in assembly verification. - - - - - 'false' to turn off automatic generation of the XML Schema file. - - - - - \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/Issues/ChainExpressionTests.cs b/src/ReactiveUI.Fody.Tests/Issues/ChainExpressionTests.cs deleted file mode 100644 index cb3d0b4eb5..0000000000 --- a/src/ReactiveUI.Fody.Tests/Issues/ChainExpressionTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.Reactive.Linq; -using ReactiveUI.Fody.Helpers; -using Xunit; - -namespace ReactiveUI.Fody.Tests.Issues; - -/// -/// Tests for determining if a chain expression works. -/// -public class ChainExpressionTests -{ - /// - /// Checks to make sure that if double property chaining doesn't cause a exception. - /// - [Fact] - public void AccessingAChainedObservableAsPropertyOfDoubleDoesntThrow() - { - var vm = new TestModel(); - Assert.Equal(0.0, vm.P2); - } - - private class TestModel : ReactiveObject - { - public TestModel() - { - Observable.Return(0.0).ToPropertyEx(this, vm => vm.P1); - this.WhenAnyValue(vm => vm.P1).ToPropertyEx(this, vm => vm.P2); - } - - [ObservableAsProperty] - public double P1 { get; } - - [ObservableAsProperty] - public double P2 { get; } - } -} \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/Issues/ExternPropertyTests.cs b/src/ReactiveUI.Fody.Tests/Issues/ExternPropertyTests.cs deleted file mode 100644 index 62ce3c93dd..0000000000 --- a/src/ReactiveUI.Fody.Tests/Issues/ExternPropertyTests.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.Reactive.Linq; - -using ReactiveUI.Fody.Helpers; - -using Xunit; - -namespace ReactiveUI.Fody.Tests.Issues; - -/// -/// Checks to make sure that extern properties work. -/// -public class ExternPropertyTests -{ - /// - /// Checks that observables passed as a parameter are supported. - /// - [Fact] - public void AllowObservableAsPropertyAttributeOnAccessor() - { - var model = new TestModel("foo"); - Assert.Equal("foo", model.MyProperty); - } - - private class TestModel : ReactiveObject - { - public TestModel(string myProperty) => Observable.Return(myProperty).ToPropertyEx(this, x => x.MyProperty); - - public extern string MyProperty - { - [ObservableAsProperty] - get; - } - } -} \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/Issues/NumericValueWorkTests.cs b/src/ReactiveUI.Fody.Tests/Issues/NumericValueWorkTests.cs deleted file mode 100644 index c5e74a28cd..0000000000 --- a/src/ReactiveUI.Fody.Tests/Issues/NumericValueWorkTests.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using FluentAssertions; - -using ReactiveUI.Fody.Helpers; - -namespace ReactiveUI.Fody.Tests.Issues; - -/// -/// A set of tests to make sure that they produce valid numeric values for different types. -/// -public static class NumericValueWorkTests -{ - /// - /// A test to make sure that all the default values are kept after generation. - /// - public static void KeepsDefaultValuesTest() - { - var testModel = new TestModel(); - - testModel.DoubleProperty.Should().Be(default); - testModel.IntProperty.Should().Be(default); - testModel.FloatProperty.Should().Be(default); - testModel.LongProperty.Should().Be(default); - } - - /// - /// The "test" here is simply for these to compile - /// Tests ObservableAsPropertyWeaver.EmitDefaultValue. - /// - private class TestModel : ReactiveObject - { - [ObservableAsProperty] - public int IntProperty { get; } - - [ObservableAsProperty] - public double DoubleProperty { get; } - - [ObservableAsProperty] - public float FloatProperty { get; } - - [ObservableAsProperty] - public long LongProperty { get; } - } -} diff --git a/src/ReactiveUI.Fody.Tests/Issues/UninitializedValuesWorkTests.cs b/src/ReactiveUI.Fody.Tests/Issues/UninitializedValuesWorkTests.cs deleted file mode 100644 index 278b6344fc..0000000000 --- a/src/ReactiveUI.Fody.Tests/Issues/UninitializedValuesWorkTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System; -using ReactiveUI.Fody.Helpers; -using Xunit; - -namespace ReactiveUI.Fody.Tests.Issues; - -/// -/// Makes sure that uninitialized values, which don't have ToPropertyEx called work. -/// -public class UninitializedValuesWorkTests -{ - /// - /// Test to make sure that properties without PropertyHelper return the correct value. - /// - [Fact] - public void UninitializedObservableAsPropertyHelperDoesntThrowAndReturnsDefaultValue() - { - var model = new TestModel(); - Assert.Equal(null, model.MyProperty); - Assert.Equal(0, model.MyIntProperty); - Assert.Equal(default, model.MyDateTimeProperty); - } - - private class TestModel : ReactiveObject - { - public TestModel() => OtherProperty = MyProperty; - - [ObservableAsProperty] - public string? MyProperty { get; } - - [ObservableAsProperty] - public int MyIntProperty { get; } - - [ObservableAsProperty] - public DateTime MyDateTimeProperty { get; private set; } - - public string? OtherProperty { get; } - } -} diff --git a/src/ReactiveUI.Fody.Tests/Mocks/BaseModel.cs b/src/ReactiveUI.Fody.Tests/Mocks/BaseModel.cs deleted file mode 100644 index 5db178a223..0000000000 --- a/src/ReactiveUI.Fody.Tests/Mocks/BaseModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -namespace ReactiveUI.Fody.Tests; - -/// -/// A base model for the mocks. -/// -public class BaseModel : ReactiveObject -{ - /// - /// Gets or sets a integer property with a initial value. - /// - public virtual int IntProperty { get; set; } = 5; - - /// - /// Gets or sets a string property with a initial value. - /// - public virtual string? StringProperty { get; set; } = "Initial Value"; -} \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/Mocks/DecoratorModel.cs b/src/ReactiveUI.Fody.Tests/Mocks/DecoratorModel.cs deleted file mode 100644 index c4dd7540ae..0000000000 --- a/src/ReactiveUI.Fody.Tests/Mocks/DecoratorModel.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using ReactiveUI.Fody.Helpers; - -namespace ReactiveUI.Fody.Tests; - -/// -/// A view model that is decorated with rx fody attributes. -/// -public class DecoratorModel : BaseModel -{ - private readonly BaseModel _model; - - /// - /// Initializes a new instance of the class. - /// - public DecoratorModel() => _model = new(); - - /// - /// Initializes a new instance of the class. - /// - /// The base model which to do ReactiveDependency off. - public DecoratorModel(BaseModel baseModel) => _model = baseModel; - - /// - /// Gets or sets a property decorated with the ReactiveAttribute. - /// - [Reactive] - public string? SomeCoolNewProperty { get; set; } - - /// - /// Gets or sets a property decorated with the ReactiveDependencyAttribute. - /// - [ReactiveDependency(nameof(_model))] - public override string? StringProperty { get; set; } - - /// - /// Gets or sets a property which interacts with the base model. - /// - public override int IntProperty - { - get => _model.IntProperty * 2; - set - { - _model.IntProperty = value; - this.RaisePropertyChanged(); - } - } - - /// - /// Sets the string. This is independent of the fody generation. - /// - /// The new value to set. - public void UpdateCoolProperty(string coolNewProperty) => SomeCoolNewProperty = coolNewProperty; -} \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/Mocks/FacadeModel.cs b/src/ReactiveUI.Fody.Tests/Mocks/FacadeModel.cs deleted file mode 100644 index 51e9a660b8..0000000000 --- a/src/ReactiveUI.Fody.Tests/Mocks/FacadeModel.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using ReactiveUI.Fody.Helpers; - -namespace ReactiveUI.Fody.Tests; - -/// -/// A model which is facading another object. -/// -public class FacadeModel : ReactiveObject -{ - /// - /// Initializes a new instance of the class. - /// - public FacadeModel() => Dependency = new(); - - /// - /// Initializes a new instance of the class. - /// - /// The dependency to base again. - public FacadeModel(BaseModel dependency) => Dependency = dependency; - - /// - /// Gets the base dependency. - /// - public BaseModel Dependency { get; } - - /// - /// Gets or sets a property with the same name in the dependency. - /// - [ReactiveDependency(nameof(Dependency))] - public int IntProperty { get; set; } - - /// - /// Gets or sets a string value that will be generated to pass through and from the dependency. - /// - [ReactiveDependency(nameof(Dependency), TargetProperty = "StringProperty")] - public string? AnotherStringProperty { get; set; } -} diff --git a/src/ReactiveUI.Fody.Tests/Mocks/ObservableAsTestModel.cs b/src/ReactiveUI.Fody.Tests/Mocks/ObservableAsTestModel.cs deleted file mode 100644 index 2e6bb4077e..0000000000 --- a/src/ReactiveUI.Fody.Tests/Mocks/ObservableAsTestModel.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.Reactive.Linq; -using ReactiveUI.Fody.Helpers; - -namespace ReactiveUI.Fody.Tests; - -/// -/// A test model for the ObservabeAsPropertyAttribute. -/// -public class ObservableAsTestModel : ReactiveObject -{ - /// - /// Initializes a new instance of the class. - /// - public ObservableAsTestModel() => Observable.Return("foo").ToPropertyEx(this, x => x.TestProperty); - - /// - /// Gets the test property which will reference our generated observable. - /// - [ObservableAsProperty] - public string? TestProperty { get; } -} diff --git a/src/ReactiveUI.Fody.Tests/ObservableAsPropertyTests.cs b/src/ReactiveUI.Fody.Tests/ObservableAsPropertyTests.cs deleted file mode 100644 index 9c7abc00e1..0000000000 --- a/src/ReactiveUI.Fody.Tests/ObservableAsPropertyTests.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using Xunit; - -namespace ReactiveUI.Fody.Tests; - -/// -/// Tests for the ObservableAsPropertyAttribute. -/// -public class ObservableAsPropertyTests -{ - /// - /// Confirms the mock generated class returns the correct value. - /// - [Fact] - public void TestPropertyReturnsFoo() - { - var model = new ObservableAsTestModel(); - Assert.Equal("foo", model.TestProperty); - } -} \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/ReactiveDependencyTests.cs b/src/ReactiveUI.Fody.Tests/ReactiveDependencyTests.cs deleted file mode 100644 index 25eaf53534..0000000000 --- a/src/ReactiveUI.Fody.Tests/ReactiveDependencyTests.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.ComponentModel; -using Xunit; - -namespace ReactiveUI.Fody.Tests; - -/// -/// Tests for the ReactiveDependencyAttribute. -/// -public class ReactiveDependencyTests -{ - /// - /// Tests to make sure that the facade returns the same valid as the dependency for the int property. - /// - [Fact] - public void IntPropertyOnWeavedFacadeReturnsBaseModelIntPropertyDefaultValueTest() - { - var model = new BaseModel(); - var expectedResult = model.IntProperty; - - var facade = new FacadeModel(model); - - Assert.Equal(expectedResult, facade.IntProperty); - } - - /// - /// Tests to make sure that the facade returns the same valid as the dependency for the string property. - /// - [Fact] - public void AnotherStringPropertyOnFacadeReturnsBaseModelStringPropertyDefaultValueTest() - { - var model = new BaseModel(); - var expectedResult = model.StringProperty; - - var facade = new FacadeModel(model); - - Assert.Equal(expectedResult, facade.AnotherStringProperty); - } - - /// - /// Tests to make sure that the facade returns the same valid as the dependency for the string property after being updated. - /// - [Fact] - public void SettingAnotherStringPropertyUpdatesTheDependencyStringProperty() - { - const string? expectedResult = "New String Value"; - var facade = new FacadeModel(new()) { AnotherStringProperty = expectedResult }; - - Assert.Equal(expectedResult, facade.Dependency.StringProperty); - } - - /// - /// Tests to make sure that the facade returns the same valid as the dependency for the int property after being updated. - /// - [Fact] - public void SettingFacadeIntPropertyUpdatesDependencyIntProperty() - { - const int expectedResult = 999; - var facade = new FacadeModel(new()) { IntProperty = expectedResult }; - - Assert.Equal(expectedResult, facade.Dependency.IntProperty); - } - - /// - /// Checks to make sure that the property changed event is fired after first assignment. - /// - [Fact] - public void FacadeIntPropertyChangedEventFiresOnAssignmentTest() - { - const string? expectedPropertyChanged = "IntProperty"; - var resultPropertyChanged = string.Empty; - - var facade = new FacadeModel(new()); - - var obj = (INotifyPropertyChanged)facade; - obj.PropertyChanged += (_, args) => resultPropertyChanged = args.PropertyName; - - facade.IntProperty = 999; - - Assert.Equal(expectedPropertyChanged, resultPropertyChanged); - } - - /// - /// Checks to make sure that the property changed event is fired after first assignment. - /// - [Fact] - public void FacadeAnotherStringPropertyChangedEventFiresOnAssignmentTest() - { - const string? expectedPropertyChanged = "AnotherStringProperty"; - var resultPropertyChanged = string.Empty; - - var facade = new FacadeModel(new()); - - var obj = (INotifyPropertyChanged)facade; - obj.PropertyChanged += (_, args) => resultPropertyChanged = args.PropertyName; - - facade.AnotherStringProperty = "Some New Value"; - - Assert.Equal(expectedPropertyChanged, resultPropertyChanged); - } - - /// - /// Checks to make sure that the facade and the decorate return the same value. - /// - [Fact] - public void StringPropertyOnWeavedDecoratorReturnsBaseModelDefaultStringValue() - { - var model = new BaseModel(); - var expectedResult = model.StringProperty; - - var decorator = new DecoratorModel(model); - - Assert.Equal(expectedResult, decorator.StringProperty); - } - - /// - /// Checks to make sure that the decorator property changed is fired. - /// - [Fact] - public void DecoratorStringPropertyRaisesPropertyChanged() - { - const string? expectedPropertyChanged = "StringProperty"; - var resultPropertyChanged = string.Empty; - - var decorator = new DecoratorModel(new()); - - var obj = (INotifyPropertyChanged)decorator; - obj.PropertyChanged += (_, args) => resultPropertyChanged = args.PropertyName; - - decorator.StringProperty = "Some New Value"; - - Assert.Equal(expectedPropertyChanged, resultPropertyChanged); - } - - /// - /// Checks to make sure that the decorator property changed is fired. - /// - [Fact] - public void DecoratorReactiveStringPropertyRaisesPropertyChanged() - { - const string? expectedPropertyChanged = "SomeCoolNewProperty"; - var resultPropertyChanged = string.Empty; - - var decorator = new DecoratorModel(new()); - - var obj = (INotifyPropertyChanged)decorator; - obj.PropertyChanged += (_, args) => resultPropertyChanged = args.PropertyName; - - decorator.UpdateCoolProperty("Some Cool Property"); - Assert.Equal(expectedPropertyChanged, resultPropertyChanged); - } -} diff --git a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj deleted file mode 100644 index 30a3f6b216..0000000000 --- a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - net472;net6.0 - netstandard2.0 - false - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ReactiveUI.Fody/CecilExtensions.cs b/src/ReactiveUI.Fody/CecilExtensions.cs deleted file mode 100644 index d41e3ca8b2..0000000000 --- a/src/ReactiveUI.Fody/CecilExtensions.cs +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ReactiveUI.Fody; - -/// -/// Mono.Cecil extension methods. -/// -public static class CecilExtensions -{ - /// - /// Emits the specified il. - /// - /// The body. - /// The il. - public static void Emit(this MethodBody body, Action il) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(body); - ArgumentNullException.ThrowIfNull(il); -#else - if (body is null) - { - throw new ArgumentNullException(nameof(body)); - } - - if (il is null) - { - throw new ArgumentNullException(nameof(il)); - } -#endif - - il(body.GetILProcessor()); - } - - /// - /// Makes the method generic. - /// - /// The method. - /// The generic arguments. - /// A generic method with generic typed arguments. - public static GenericInstanceMethod MakeGenericMethod(this MethodReference method, params TypeReference[] genericArguments) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(genericArguments); -#else - if (genericArguments is null) - { - throw new ArgumentNullException(nameof(genericArguments)); - } -#endif - - var result = new GenericInstanceMethod(method); - foreach (var argument in genericArguments) - { - result.GenericArguments.Add(argument); - } - - return result; - } - - /// - /// Determines whether [is assignable from] [the specified type]. - /// - /// Type of the base. - /// The type. - /// The logger. - /// - /// true if [is assignable from] [the specified type]; otherwise, false. - /// - public static bool IsAssignableFrom(this TypeReference baseType, TypeReference type, Action? logger = null) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(baseType); - ArgumentNullException.ThrowIfNull(type); -#else - if (baseType is null) - { - throw new ArgumentNullException(nameof(baseType)); - } - - if (type is null) - { - throw new ArgumentNullException(nameof(type)); - } -#endif - - return baseType.Resolve().IsAssignableFrom(type.Resolve(), logger); - } - - /// - /// Determines whether [is assignable from] [the specified type]. - /// - /// Type of the base. - /// The type. - /// The logger. - /// - /// true if [is assignable from] [the specified type]; otherwise, false. - /// - public static bool IsAssignableFrom(this TypeDefinition baseType, TypeDefinition type, Action? logger = null) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(baseType); -#else - if (baseType is null) - { - throw new ArgumentNullException(nameof(baseType)); - } -#endif - - logger ??= _ => { }; - - var queue = new Queue(); - queue.Enqueue(type); - - while (queue.Count > 0) - { - var current = queue.Dequeue(); - logger(current.FullName); - - if (baseType.FullName == current.FullName) - { - return true; - } - - if (current.BaseType is not null) - { - queue.Enqueue(current.BaseType.Resolve()); - } - - foreach (var @interface in current.Interfaces) - { - queue.Enqueue(@interface.InterfaceType.Resolve()); - } - } - - return false; - } - - /// - /// Determines whether the specified attribute type is defined. - /// - /// The member. - /// Type of the attribute. - /// - /// true if the specified attribute type is defined; otherwise, false. - /// - public static bool IsDefined(this IMemberDefinition member, TypeReference attributeType) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(member); -#else - if (member is null) - { - throw new ArgumentNullException(nameof(member)); - } -#endif - - return member.HasCustomAttributes && member.CustomAttributes.Any(x => x.AttributeType.FullName == attributeType.FullName); - } - - /// - /// Binds the method to the specified generic type. - /// - /// The method. - /// Type of the generic. - /// The method bound to the generic type. - public static MethodReference Bind(this MethodReference method, GenericInstanceType genericType) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(method); -#else - if (method is null) - { - throw new ArgumentNullException(nameof(method)); - } -#endif - - var reference = new MethodReference(method.Name, method.ReturnType, genericType) - { - HasThis = method.HasThis, - ExplicitThis = method.ExplicitThis, - CallingConvention = method.CallingConvention - }; - - foreach (var parameter in method.Parameters) - { - reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); - } - - return reference; - } - - /// - /// Binds the generic type definition to a field. - /// - /// The field. - /// The generic type definition. - /// The field bound to the generic type. - public static FieldReference BindDefinition(this FieldReference field, TypeReference genericTypeDefinition) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(field); - ArgumentNullException.ThrowIfNull(genericTypeDefinition); -#else - if (field is null) - { - throw new ArgumentNullException(nameof(field)); - } - - if (genericTypeDefinition is null) - { - throw new ArgumentNullException(nameof(genericTypeDefinition)); - } -#endif - - if (!genericTypeDefinition.HasGenericParameters) - { - return field; - } - - var genericDeclaration = new GenericInstanceType(genericTypeDefinition); - foreach (var parameter in genericTypeDefinition.GenericParameters) - { - genericDeclaration.GenericArguments.Add(parameter); - } - - return new(field.Name, field.FieldType, genericDeclaration); - } - - /// - /// Finds an assembly in a module. - /// - /// The current module. - /// Name of the assembly. - /// The assembly if found, null if not. - public static AssemblyNameReference? FindAssembly(this ModuleDefinition currentModule, string assemblyName) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(currentModule); -#else - if (currentModule is null) - { - throw new ArgumentNullException(nameof(currentModule)); - } -#endif - - var assemblyReferences = currentModule.AssemblyReferences; - - return assemblyReferences.SingleOrDefault(x => x.Name == assemblyName); - } - - /// - /// Finds a type reference in the module. - /// - /// The current module. - /// The namespace. - /// Name of the type. - /// The scope. - /// The type parameters. - /// The type reference. - public static TypeReference FindType(this ModuleDefinition currentModule, string @namespace, string typeName, IMetadataScope? scope = null, params string[] typeParameters) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(typeParameters); -#else - if (typeParameters is null) - { - throw new ArgumentNullException(nameof(typeParameters)); - } -#endif - - var result = new TypeReference(@namespace, typeName, currentModule, scope); - foreach (var typeParameter in typeParameters) - { - result.GenericParameters.Add(new GenericParameter(typeParameter, result)); - } - - return result; - } - - /// - /// Compares two type references for equality. - /// - /// The type. - /// The compare to. - /// A value indicating the result of the comparison. - public static bool CompareTo(this TypeReference type, TypeReference compareTo) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(compareTo); -#else - if (type is null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (compareTo is null) - { - throw new ArgumentNullException(nameof(compareTo)); - } -#endif - - return type.FullName == compareTo.FullName; - } -} diff --git a/src/ReactiveUI.Fody/ModuleWeaver.cs b/src/ReactiveUI.Fody/ModuleWeaver.cs deleted file mode 100644 index 763f84d623..0000000000 --- a/src/ReactiveUI.Fody/ModuleWeaver.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.Collections.Generic; -using Fody; - -namespace ReactiveUI.Fody; - -/// -/// ReactiveUI module weaver. -/// -/// -public class ModuleWeaver : BaseModuleWeaver -{ - /// - public override void Execute() - { - var propertyWeaver = new ReactiveUIPropertyWeaver - { - ModuleDefinition = ModuleDefinition, - LogInfo = WriteInfo, - LogError = WriteError - }; - propertyWeaver.Execute(); - - var observableAsPropertyWeaver = new ObservableAsPropertyWeaver - { - ModuleDefinition = ModuleDefinition, - LogInfo = WriteInfo, - FindType = FindTypeDefinition - }; - observableAsPropertyWeaver.Execute(); - - var reactiveDependencyWeaver = new ReactiveDependencyPropertyWeaver - { - ModuleDefinition = ModuleDefinition, - LogInfo = WriteInfo, - LogError = WriteError - }; - reactiveDependencyWeaver.Execute(); - } - - /// - public override IEnumerable GetAssembliesForScanning() - { - yield return "mscorlib"; - yield return "netstandard"; - yield return "System"; - yield return "System.Runtime"; - yield return "ReactiveUI"; - yield return "ReactiveUI.Fody.Helpers"; - } -} \ No newline at end of file diff --git a/src/ReactiveUI.Fody/ObservableAsPropertyWeaver.cs b/src/ReactiveUI.Fody/ObservableAsPropertyWeaver.cs deleted file mode 100644 index d6f657a5c6..0000000000 --- a/src/ReactiveUI.Fody/ObservableAsPropertyWeaver.cs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; - -namespace ReactiveUI.Fody; - -/// -/// Weaver that converts observables as property helper. -/// -public class ObservableAsPropertyWeaver -{ - /// - /// Gets or sets the module definition. - /// - /// - /// The module definition. - /// - public ModuleDefinition? ModuleDefinition { get; set; } - - /// - /// Gets or sets a action that will log an MessageImportance.High message to MSBuild. OPTIONAL. - /// - /// - /// The log information. - /// - public Action? LogInfo { get; set; } - - /// - /// Gets a function that will find a type from referenced assemblies by name. - /// - public Func? FindType { get; internal set; } - - /// - /// Executes this property weaver. - /// - public void Execute() - { - if (ModuleDefinition is null) - { - LogInfo?.Invoke("The module definition has not been defined."); - return; - } - - var reactiveUI = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI").OrderByDescending(x => x.Version).FirstOrDefault(); - if (reactiveUI is null) - { - LogInfo?.Invoke("Could not find assembly: ReactiveUI (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); - return; - } - - LogInfo?.Invoke($"{reactiveUI.Name} {reactiveUI.Version}"); - var helpers = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI.Fody.Helpers").OrderByDescending(x => x.Version).FirstOrDefault(); - if (helpers is null) - { - LogInfo?.Invoke("Could not find assembly: ReactiveUI.Fody.Helpers (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); - return; - } - - LogInfo?.Invoke($"{helpers.Name} {helpers.Version}"); - - var exceptionName = typeof(Exception).FullName; - - if (exceptionName is null) - { - LogInfo?.Invoke("Could not find the full name for System.Exception"); - return; - } - - var reactiveObject = ModuleDefinition.FindType("ReactiveUI", "ReactiveObject", reactiveUI); - - // The types we will scan are subclasses of ReactiveObject - var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.BaseType is not null && reactiveObject.IsAssignableFrom(x.BaseType)); - - var observableAsPropertyHelper = ModuleDefinition.FindType("ReactiveUI", "ObservableAsPropertyHelper`1", reactiveUI, "T"); - var observableAsPropertyAttribute = ModuleDefinition.FindType("ReactiveUI.Fody.Helpers", "ObservableAsPropertyAttribute", helpers); - var observableAsPropertyHelperGetValue = ModuleDefinition.ImportReference(observableAsPropertyHelper.Resolve().Properties.Single(x => x.Name == "Value").GetMethod); - var exceptionDefinition = FindType?.Invoke(exceptionName); - var constructorDefinition = exceptionDefinition.GetConstructors().Single(x => x.Parameters.Count == 1); - var exceptionConstructor = ModuleDefinition.ImportReference(constructorDefinition); - - foreach (var targetType in targetTypes) - { - foreach (var property in targetType.Properties.Where(x => x.IsDefined(observableAsPropertyAttribute) || (x.GetMethod?.IsDefined(observableAsPropertyAttribute) ?? false)).ToArray()) - { - var genericObservableAsPropertyHelper = observableAsPropertyHelper.MakeGenericInstanceType(property.PropertyType); - var genericObservableAsPropertyHelperGetValue = observableAsPropertyHelperGetValue.Bind(genericObservableAsPropertyHelper); - ModuleDefinition.ImportReference(genericObservableAsPropertyHelperGetValue); - - // Declare a field to store the property value - var field = new FieldDefinition("$" + property.Name, FieldAttributes.Private, genericObservableAsPropertyHelper); - targetType.Fields.Add(field); - - // It's an auto-property, so remove the generated field - if (property.SetMethod?.HasBody == true) - { - // Remove old field (the generated backing field for the auto property) - var oldField = (FieldReference)property.GetMethod.Body.Instructions.Single(x => x.Operand is FieldReference).Operand; - var oldFieldDefinition = oldField.Resolve(); - targetType.Fields.Remove(oldFieldDefinition); - - // Re-implement setter to throw an exception - property.SetMethod.Body = new MethodBody(property.SetMethod); - property.SetMethod.Body.Emit(il => - { - il.Emit(OpCodes.Ldstr, "Never call the setter of an ObservableAsPropertyHelper property."); - il.Emit(OpCodes.Newobj, exceptionConstructor); - il.Emit(OpCodes.Throw); - il.Emit(OpCodes.Ret); - }); - } - - property.GetMethod.Body = new MethodBody(property.GetMethod); - property.GetMethod.Body.Emit(il => - { - var isValid = il.Create(OpCodes.Nop); - il.Emit(OpCodes.Ldarg_0); // this - il.Emit(OpCodes.Ldfld, field.BindDefinition(targetType)); // pop -> this.$PropertyName - il.Emit(OpCodes.Dup); // Put an extra copy of this.$PropertyName onto the stack - il.Emit(OpCodes.Brtrue, isValid); // If the helper is null, return the default value for the property - il.Emit(OpCodes.Pop); // Drop this.$PropertyName - EmitDefaultValue(property.GetMethod.Body, il, property.PropertyType); // Put the default value onto the stack - il.Emit(OpCodes.Ret); // Return that default value - il.Append(isValid); // Add a marker for if the helper is not null - il.Emit(OpCodes.Callvirt, genericObservableAsPropertyHelperGetValue); // pop -> this.$PropertyName.Value - il.Emit(OpCodes.Ret); // Return the value that is on the stack - }); - } - } - } - - /// - /// Emits the default value. - /// - /// The method body. - /// The il. - /// The type. - public void EmitDefaultValue(MethodBody methodBody, ILProcessor il, TypeReference type) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(methodBody); - ArgumentNullException.ThrowIfNull(il); -#else - if (methodBody is null) - { - throw new ArgumentNullException(nameof(methodBody)); - } - - if (il is null) - { - throw new ArgumentNullException(nameof(il)); - } -#endif - - if (ModuleDefinition is not null) - { - if (type.CompareTo(ModuleDefinition.TypeSystem.Boolean) || type.CompareTo(ModuleDefinition.TypeSystem.Byte) || - type.CompareTo(ModuleDefinition.TypeSystem.Int16) || type.CompareTo(ModuleDefinition.TypeSystem.Int32)) - { - il.Emit(OpCodes.Ldc_I4_0); - } - else if (type.CompareTo(ModuleDefinition.TypeSystem.Single)) - { - il.Emit(OpCodes.Ldc_R4, 0F); - } - else if (type.CompareTo(ModuleDefinition.TypeSystem.Int64)) - { - il.Emit(OpCodes.Ldc_I8, 0L); - } - else if (type.CompareTo(ModuleDefinition.TypeSystem.Double)) - { - il.Emit(OpCodes.Ldc_R8, 0D); - } - else if (type.IsGenericParameter || type.IsValueType) - { - methodBody.InitLocals = true; - var local = new VariableDefinition(type); - il.Body.Variables.Add(local); - il.Emit(OpCodes.Ldloca_S, local); - il.Emit(OpCodes.Initobj, type); - il.Emit(OpCodes.Ldloc, local); - } - else - { - il.Emit(OpCodes.Ldnull); - } - } - } -} diff --git a/src/ReactiveUI.Fody/Properties/AssemblyInfo.cs b/src/ReactiveUI.Fody/Properties/AssemblyInfo.cs deleted file mode 100644 index 4700ec8817..0000000000 --- a/src/ReactiveUI.Fody/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("ReactiveUI.Tests")] -[assembly: InternalsVisibleTo("ReactiveUI.Winforms")] -[assembly: InternalsVisibleTo("ReactiveUI.Wpf")] -[assembly: InternalsVisibleTo("ReactiveUI.XamForms")] -[assembly: InternalsVisibleTo("ReactiveUI.AndroidSupport")] diff --git a/src/ReactiveUI.Fody/ReactiveDependencyPropertyWeaver.cs b/src/ReactiveUI.Fody/ReactiveDependencyPropertyWeaver.cs deleted file mode 100644 index e66f7d8ddf..0000000000 --- a/src/ReactiveUI.Fody/ReactiveDependencyPropertyWeaver.cs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; - -namespace ReactiveUI.Fody; - -/// -/// Weaver that generates an ObservableAsPropertyHelper. -/// -public class ReactiveDependencyPropertyWeaver -{ - /// - /// Gets or sets the module definition. - /// - /// - /// The module definition. - /// - public ModuleDefinition? ModuleDefinition { get; set; } - - /// - /// Gets or sets a action that will log an MessageImportance.High message to MSBuild. OPTIONAL. - /// - /// - /// The log information. - /// - public Action? LogInfo { get; set; } - - /// - /// Gets or sets a action which will log an error message to MSBuild. OPTIONAL. - /// - /// - /// The log error. - /// - public Action? LogError { get; set; } - - /// - /// Executes this instance. - /// - /// - /// reactiveObjectExtensions is null - /// or - /// raisePropertyChangedMethod is null - /// or - /// reactiveDecoratorAttribute is null. - /// - public void Execute() - { - if (ModuleDefinition is null) - { - LogInfo?.Invoke("The module definition has not been defined."); - return; - } - - var reactiveUI = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI").OrderByDescending(x => x.Version).FirstOrDefault(); - if (reactiveUI is null) - { - LogInfo?.Invoke("Could not find assembly: ReactiveUI (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); - return; - } - - LogInfo?.Invoke($"{reactiveUI.Name} {reactiveUI.Version}"); - var helpers = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI.Fody.Helpers").OrderByDescending(x => x.Version).FirstOrDefault(); - if (helpers is null) - { - LogInfo?.Invoke("Could not find assembly: ReactiveUI.Fody.Helpers (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); - return; - } - - LogInfo?.Invoke($"{helpers.Name} {helpers.Version}"); - var reactiveObject = new TypeReference("ReactiveUI", "IReactiveObject", ModuleDefinition, reactiveUI); - - var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.BaseType is not null && reactiveObject.IsAssignableFrom(x.BaseType)).ToArray(); - var reactiveObjectExtensions = new TypeReference("ReactiveUI", "IReactiveObjectExtensions", ModuleDefinition, reactiveUI).Resolve() ?? throw new Exception("reactiveObjectExtensions is null"); - var raisePropertyChangedMethod = ModuleDefinition.ImportReference(reactiveObjectExtensions.Methods.Single(x => x.Name == "RaisePropertyChanged")) ?? throw new Exception("raisePropertyChangedMethod is null"); - var reactiveDependencyAttribute = ModuleDefinition.FindType("ReactiveUI.Fody.Helpers", "ReactiveDependencyAttribute", helpers) ?? throw new Exception("reactiveDecoratorAttribute is null"); - foreach (var targetType in targetTypes.Where(x => x.Properties.Any(y => y.IsDefined(reactiveDependencyAttribute))).ToArray()) - { - foreach (var facadeProperty in targetType.Properties.Where(x => x.IsDefined(reactiveDependencyAttribute)).ToArray()) - { - // If the property already has a body then do not weave to prevent loss of instructions - if (!facadeProperty.GetMethod.Body.Instructions.Any(x => x.Operand is FieldReference) || facadeProperty.GetMethod.Body.HasVariables) - { - LogError?.Invoke($"Property {facadeProperty.Name} is not an auto property and therefore not suitable for ReactiveDependency weaving"); - continue; - } - - var attribute = facadeProperty.CustomAttributes.First(x => x.AttributeType.FullName == reactiveDependencyAttribute.FullName); - - var targetNamedArgument = attribute.ConstructorArguments.FirstOrDefault(); - var targetValue = targetNamedArgument.Value?.ToString(); - if (string.IsNullOrEmpty(targetValue)) - { - LogError?.Invoke("No target property defined on the object"); - continue; - } - - if (targetType.Properties.All(x => x.Name != targetValue) && targetType.Fields.All(x => x.Name != targetValue)) - { - LogError?.Invoke($"dependency object property/field name '{targetValue}' not found on target type {targetType.Name}"); - continue; - } - - var objPropertyTarget = targetType.Properties.FirstOrDefault(x => x.Name == targetValue); - var objFieldTarget = targetType.Fields.FirstOrDefault(x => x.Name == targetValue); - - var objDependencyTargetType = objPropertyTarget is not null - ? objPropertyTarget.PropertyType.Resolve() - : objFieldTarget?.FieldType.Resolve(); - - if (objDependencyTargetType is null) - { - LogError?.Invoke("Couldn't result the dependency type"); - continue; - } - - // Look for the target property on the member obj - var destinationPropertyNamedArgument = attribute.Properties.FirstOrDefault(x => x.Name == "TargetProperty"); - var destinationPropertyName = destinationPropertyNamedArgument.Argument.Value?.ToString(); - - // If no target property was specified use this property's name as the target on the decorated object (ala a decorated property) - if (string.IsNullOrEmpty(destinationPropertyName)) - { - destinationPropertyName = facadeProperty.Name; - } - - if (objDependencyTargetType.Properties.All(x => x.Name != destinationPropertyName)) - { - LogError?.Invoke($"Target property {destinationPropertyName} on dependency of type {objDependencyTargetType.DeclaringType.Name} not found"); - continue; - } - - var destinationProperty = objDependencyTargetType.Properties.First(x => x.Name == destinationPropertyName); - - // The property on the facade/decorator should have a setter - if (facadeProperty.SetMethod is null) - { - LogError?.Invoke($"Property {facadeProperty.DeclaringType.FullName}.{facadeProperty.Name} has no setter, therefore it is not possible for the property to change, and thus should not be marked with [ReactiveDecorator]"); - continue; - } - - // The property on the dependency should have a setter e.g. Dependency.SomeProperty = value; - if (destinationProperty.SetMethod is null) - { - LogError?.Invoke($"Dependency object's property {destinationProperty.DeclaringType.FullName}.{destinationProperty.Name} has no setter, therefore it is not possible for the property to change, and thus should not be marked with [ReactiveDecorator]"); - continue; - } - - // Remove old field (the generated backing field for the auto property) - var oldField = (FieldReference)facadeProperty.GetMethod.Body.Instructions.Single(x => x.Operand is FieldReference).Operand; - var oldFieldDefinition = oldField.Resolve(); - targetType.Fields.Remove(oldFieldDefinition); - - // See if there exists an initializer for the auto-property - var constructors = targetType.Methods.Where(x => x.IsConstructor); - foreach (var constructor in constructors) - { - var fieldAssignment = constructor.Body.Instructions.SingleOrDefault(x => Equals(x.Operand, oldFieldDefinition) || Equals(x.Operand, oldField)); - if (fieldAssignment is not null) - { - // Replace field assignment with a property set (the stack semantics are the same for both, - // so happily we don't have to manipulate the byte code any further.) - var setterCall = constructor.Body.GetILProcessor().Create(facadeProperty.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, facadeProperty.SetMethod); - constructor.Body.GetILProcessor().Replace(fieldAssignment, setterCall); - } - } - - // Build out the getter which simply returns the value of the generated field - facadeProperty.GetMethod.Body = new MethodBody(facadeProperty.GetMethod); - facadeProperty.GetMethod.Body.Emit(il => - { - il.Emit(OpCodes.Ldarg_0); - if (objPropertyTarget is not null) - { - il.Emit(objPropertyTarget.GetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, objPropertyTarget.GetMethod); - } - else - { - il.Emit(OpCodes.Ldfld, objFieldTarget); - } - - il.Emit(destinationProperty.GetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, destinationProperty.GetMethod); - il.Emit(OpCodes.Ret); - }); - - TypeReference genericTargetType = targetType; - if (targetType.HasGenericParameters) - { - var genericDeclaration = new GenericInstanceType(targetType); - foreach (var parameter in targetType.GenericParameters) - { - genericDeclaration.GenericArguments.Add(parameter); - } - - genericTargetType = genericDeclaration; - } - - var methodReference = raisePropertyChangedMethod.MakeGenericMethod(genericTargetType); - facadeProperty.SetMethod.Body = new MethodBody(facadeProperty.SetMethod); - facadeProperty.SetMethod.Body.Emit(il => - { - il.Emit(OpCodes.Ldarg_0); - if (objPropertyTarget is not null) - { - il.Emit(objPropertyTarget.GetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, objPropertyTarget.GetMethod); - } - else - { - il.Emit(OpCodes.Ldfld, objFieldTarget); - } - - il.Emit(OpCodes.Ldarg_1); - il.Emit(destinationProperty.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, destinationProperty.SetMethod); // Set the nested property - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldstr, facadeProperty.Name); // "PropertyName" - il.Emit(OpCodes.Call, methodReference); // this.RaisePropertyChanged("PropertyName") - il.Emit(OpCodes.Ret); - }); - } - } - } -} diff --git a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj deleted file mode 100644 index 9a45093b85..0000000000 --- a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - netstandard2.0;net6.0;net7.0;net8.0 - $(TargetFrameworks);net472 - Fody extension that will generate RaisePropertyChange notifications for properties and ObservableAsPropertyHelper properties - False - mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;xamarin;android;ios;mac;forms;monodroid;monotouch;xamarin.android;xamarin.ios;xamarin.forms;xamarin.mac;xamarin.tvos;wpf;net;netstandard;net472;uwp;tizen;unoplatform;fody; - - - - - \ No newline at end of file diff --git a/src/ReactiveUI.Fody/ReactiveUIPropertyWeaver.cs b/src/ReactiveUI.Fody/ReactiveUIPropertyWeaver.cs deleted file mode 100644 index 1df8800e34..0000000000 --- a/src/ReactiveUI.Fody/ReactiveUIPropertyWeaver.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; - -namespace ReactiveUI.Fody; - -/// -/// Weaver that replaces properties marked with `[DataMember]` on subclasses of `ReactiveObject` with an -/// implementation that invokes `RaisePropertyChanged` as is required for ReactiveUI. -/// -public class ReactiveUIPropertyWeaver -{ - /// - /// Gets or sets the module definition. - /// - /// - /// The module definition. - /// - public ModuleDefinition? ModuleDefinition { get; set; } - - /// - /// Gets or sets a action that will log an MessageImportance.High message to MSBuild. OPTIONAL. - /// - /// - /// The log information. - /// - public Action? LogInfo { get; set; } - - /// - /// Gets or sets an action that will log an error message to MSBuild. OPTIONAL. - /// - /// - /// The log error. - /// - public Action? LogError { get; set; } - - /// - /// Executes this property weaver. - /// - /// - /// reactiveObjectExtensions is null - /// or - /// raiseAndSetIfChangedMethod is null - /// or - /// reactiveAttribute is null - /// or - /// [Reactive] is decorating " + property.DeclaringType.FullName + "." + property.Name + ", but the property has no setter so there would be nothing to react to. Consider removing the attribute. - /// - public void Execute() - { - if (ModuleDefinition is null) - { - LogInfo?.Invoke("The module definition has not been defined."); - return; - } - - var reactiveUI = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI").OrderByDescending(x => x.Version).FirstOrDefault(); - if (reactiveUI is null) - { - LogInfo?.Invoke("Could not find assembly: ReactiveUI (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); - return; - } - - LogInfo?.Invoke($"{reactiveUI.Name} {reactiveUI.Version}"); - var helpers = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI.Fody.Helpers").OrderByDescending(x => x.Version).FirstOrDefault(); - if (helpers is null) - { - LogInfo?.Invoke("Could not find assembly: ReactiveUI.Fody.Helpers (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); - return; - } - - LogInfo?.Invoke($"{helpers.Name} {helpers.Version}"); - var reactiveObject = new TypeReference("ReactiveUI", "IReactiveObject", ModuleDefinition, reactiveUI); - var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.BaseType is not null && reactiveObject.IsAssignableFrom(x.BaseType)).ToArray(); - var reactiveObjectExtensions = new TypeReference("ReactiveUI", "IReactiveObjectExtensions", ModuleDefinition, reactiveUI).Resolve() ?? throw new Exception("reactiveObjectExtensions is null"); - var raiseAndSetIfChangedMethod = ModuleDefinition.ImportReference(reactiveObjectExtensions.Methods.Single(x => x.Name == "RaiseAndSetIfChanged")) ?? throw new Exception("raiseAndSetIfChangedMethod is null"); - var reactiveAttribute = ModuleDefinition.FindType("ReactiveUI.Fody.Helpers", "ReactiveAttribute", helpers) ?? throw new Exception("reactiveAttribute is null"); - foreach (var targetType in targetTypes) - { - foreach (var property in targetType.Properties.Where(x => x.IsDefined(reactiveAttribute)).ToArray()) - { - if (property.SetMethod is null) - { - LogError?.Invoke($"Property {property.DeclaringType.FullName}.{property.Name} has no setter, therefore it is not possible for the property to change, and thus should not be marked with [Reactive]"); - continue; - } - - // Declare a field to store the property value - var field = new FieldDefinition("$" + property.Name, FieldAttributes.Private, property.PropertyType); - targetType.Fields.Add(field); - - // Remove old field (the generated backing field for the auto property) - var oldField = (FieldReference)property.GetMethod.Body.Instructions.Single(x => x.Operand is FieldReference).Operand; - var oldFieldDefinition = oldField.Resolve(); - targetType.Fields.Remove(oldFieldDefinition); - - // See if there exists an initializer for the auto-property - var constructors = targetType.Methods.Where(x => x.IsConstructor); - foreach (var constructor in constructors) - { - var fieldAssignment = constructor.Body.Instructions.SingleOrDefault(x => Equals(x.Operand, oldFieldDefinition) || Equals(x.Operand, oldField)); - if (fieldAssignment is not null) - { - // Replace field assignment with a property set (the stack semantics are the same for both, - // so happily we don't have to manipulate the bytecode any further.) - var setterCall = constructor.Body.GetILProcessor().Create(property.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, property.SetMethod); - constructor.Body.GetILProcessor().Replace(fieldAssignment, setterCall); - } - } - - // Build out the getter which simply returns the value of the generated field - property.GetMethod.Body = new MethodBody(property.GetMethod); - property.GetMethod.Body.Emit(il => - { - il.Emit(OpCodes.Ldarg_0); // this - il.Emit(OpCodes.Ldfld, field.BindDefinition(targetType)); // pop -> this.$PropertyName - il.Emit(OpCodes.Ret); // Return the field value that is lying on the stack - }); - - TypeReference genericTargetType = targetType; - if (targetType.HasGenericParameters) - { - var genericDeclaration = new GenericInstanceType(targetType); - foreach (var parameter in targetType.GenericParameters) - { - genericDeclaration.GenericArguments.Add(parameter); - } - - genericTargetType = genericDeclaration; - } - - var methodReference = raiseAndSetIfChangedMethod.MakeGenericMethod(genericTargetType, property.PropertyType); - - // Build out the setter which fires the RaiseAndSetIfChanged method - if (property.SetMethod is null) - { - throw new Exception("[Reactive] is decorating " + property.DeclaringType.FullName + "." + property.Name + ", but the property has no setter so there would be nothing to react to. Consider removing the attribute."); - } - - property.SetMethod.Body = new MethodBody(property.SetMethod); - property.SetMethod.Body.Emit(il => - { - il.Emit(OpCodes.Ldarg_0); // this - il.Emit(OpCodes.Ldarg_0); // this - il.Emit(OpCodes.Ldflda, field.BindDefinition(targetType)); // pop -> this.$PropertyName - il.Emit(OpCodes.Ldarg_1); // value - il.Emit(OpCodes.Ldstr, property.Name); // "PropertyName" - il.Emit(OpCodes.Call, methodReference); // pop * 4 -> this.RaiseAndSetIfChanged(this.$PropertyName, value, "PropertyName") - il.Emit(OpCodes.Pop); // We don't care about the result of RaiseAndSetIfChanged, so pop it off the stack (stack is now empty) - il.Emit(OpCodes.Ret); // Return out of the function - }); - } - } - } -}