From 75c9072074cb416f62c58e5733a81b68d4416dc9 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 10 May 2022 11:39:03 -0500 Subject: [PATCH 1/5] Add trimming annotations to DotNetObjectReference DotNetDispatcher has some unbounded reflection against DotNetObjectReference Types that the trimmer can't understand. Adding trimming annotations to tell the trimmer to preserve the public methods on the TValue of DotNetObjectReference instances, so the JSInvokable methods won't be trimmed. See https://github.com/dotnet/maui/issues/6965 --- .../Microsoft.JSInterop/src/DotNetObjectReference.cs | 2 +- .../Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs | 6 ++++-- .../src/Infrastructure/DotNetDispatcher.cs | 4 ++-- .../src/Infrastructure/IDotNetObjectReference.cs | 3 +++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs index b792268315a5..f55c9e4db8ac 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs @@ -13,7 +13,7 @@ public static class DotNetObjectReference /// /// The reference type to track. /// An instance of . - public static DotNetObjectReference Create(TValue value) where TValue : class + public static DotNetObjectReference Create<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue>(TValue value) where TValue : class { if (value is null) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs index efe21477ce77..659c8748ac2a 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.JSInterop.Infrastructure; namespace Microsoft.JSInterop; @@ -13,7 +13,8 @@ namespace Microsoft.JSInterop; /// To avoid leaking memory, the reference must later be disposed by JS code or by .NET code. /// /// The type of the value to wrap. -public sealed class DotNetObjectReference : IDotNetObjectReference, IDisposable where TValue : class +public sealed class DotNetObjectReference<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue> : + IDotNetObjectReference, IDisposable where TValue : class { private readonly TValue _value; private long _objectId; @@ -71,6 +72,7 @@ internal JSRuntime? JSRuntime } object IDotNetObjectReference.Value => Value; + Type IDotNetObjectReference.Type => typeof(TValue); internal bool Disposed { get; private set; } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index b14c166b2843..059ae85bfdf8 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -383,7 +383,7 @@ private static Task GetTaskByType(Type type, object obj) private static (MethodInfo methodInfo, Type[] parameterTypes) GetCachedMethodInfo(IDotNetObjectReference objectReference, string methodIdentifier) { - var type = objectReference.Value.GetType(); + var type = objectReference.Type; var assemblyMethods = _cachedMethodsByType.GetOrAdd(type, ScanTypeForCallableMethods); if (assemblyMethods.TryGetValue(methodIdentifier, out var result)) { @@ -394,7 +394,7 @@ private static (MethodInfo methodInfo, Type[] parameterTypes) GetCachedMethodInf throw new ArgumentException($"The type '{type.Name}' does not contain a public invokable method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); } - static Dictionary ScanTypeForCallableMethods(Type type) + static Dictionary ScanTypeForCallableMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type) { var result = new Dictionary(StringComparer.Ordinal); diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs index a8669174ceb7..1b03abb1f6ab 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs @@ -6,4 +6,7 @@ namespace Microsoft.JSInterop.Infrastructure; internal interface IDotNetObjectReference : IDisposable { object Value { get; } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + Type Type { get; } } From a53d037b1ab008b6b279394af0da954c9ce8c045 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 10 May 2022 16:42:19 +0000 Subject: [PATCH 2/5] Fix build --- src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs | 2 ++ .../Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs | 1 + .../src/Infrastructure/IDotNetObjectReference.cs | 2 ++ 3 files changed, 5 insertions(+) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs index f55c9e4db8ac..f40dbef662ae 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.JSInterop; /// diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs index 659c8748ac2a..8b96ad0536a5 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.JSInterop.Infrastructure; diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs index 1b03abb1f6ab..93abe01eda9a 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.JSInterop.Infrastructure; internal interface IDotNetObjectReference : IDisposable From 542e5124f3aff0855e80b849ca2cb37d264d58df Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 10 May 2022 18:24:42 +0000 Subject: [PATCH 3/5] Resolve ILLink warnings and update the warning baselines. --- ...tCore.Components.Web.WarningSuppressions.xml | 14 +------------- ...embly.Authentication.WarningSuppressions.xml | 10 ++-------- .../src/DotNetObjectReferenceOfT.cs | 2 ++ .../src/Infrastructure/DotNetDispatcher.cs | 17 ++++++++++++----- .../DotNetObjectReferenceJsonConverter.cs | 3 ++- .../Microsoft.JSInterop/src/JSRuntime.cs | 2 +- .../Microsoft.JSInterop.WarningSuppressions.xml | 10 +++++----- 7 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/Components/Web/src/Microsoft.AspNetCore.Components.Web.WarningSuppressions.xml b/src/Components/Web/src/Microsoft.AspNetCore.Components.Web.WarningSuppressions.xml index db0f1efbb1b1..750434411f3c 100644 --- a/src/Components/Web/src/Microsoft.AspNetCore.Components.Web.WarningSuppressions.xml +++ b/src/Components/Web/src/Microsoft.AspNetCore.Components.Web.WarningSuppressions.xml @@ -1,18 +1,6 @@  - - - ILLink - IL2026 - member - M:Microsoft.AspNetCore.Components.Web.Infrastructure.JSComponentInterop.SetRootComponentParameters(System.Int32,System.Int32,System.Text.Json.JsonElement,System.Text.Json.JsonSerializerOptions) - - - ILLink - IL2026 - member - M:Microsoft.AspNetCore.Components.Web.WebEventData.ParseEventArgsJson(Microsoft.AspNetCore.Components.RenderTree.Renderer,System.Text.Json.JsonSerializerOptions,System.UInt64,System.String,System.Text.Json.JsonElement) - + ILLink IL2062 diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.WarningSuppressions.xml b/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.WarningSuppressions.xml index 358d7fae2dda..76c8bc9d8c9c 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.WarningSuppressions.xml +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.WarningSuppressions.xml @@ -1,4 +1,4 @@ - + @@ -7,11 +7,5 @@ member F:Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.<>c__1`1.<>9__1_0 - - ILLink - IL2091 - member - M:Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.<>c__1`1.<AddAuthenticationStateProvider>b__1_0(System.IServiceProvider) - - + \ No newline at end of file diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs index 8b96ad0536a5..d74ff392ad3f 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs @@ -73,6 +73,8 @@ internal JSRuntime? JSRuntime } object IDotNetObjectReference.Value => Value; + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type IDotNetObjectReference.Type => typeof(TValue); internal bool Disposed { get; private set; } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index 059ae85bfdf8..2cdd7f5fd7c4 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -12,7 +12,7 @@ using System.Text.Json; using Microsoft.AspNetCore.Internal; -[assembly: MetadataUpdateHandler(typeof(Microsoft.JSInterop.Infrastructure.DotNetDispatcher))] +[assembly: MetadataUpdateHandler(typeof(Microsoft.JSInterop.Infrastructure.DotNetDispatcher.MetadataUpdateHandler))] namespace Microsoft.JSInterop.Infrastructure; @@ -381,6 +381,8 @@ private static Task GetTaskByType(Type type, object obj) private static Task CreateValueTaskConverter<[DynamicallyAccessedMembers(LinkerFlags.JsonSerialized)] T>(object result) => ((ValueTask)result).AsTask(); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111:ReflectionToDynamicallyAccessedMembers", + Justification = "The Type being passed into GetOrAdd is annotated, but the trimmer warns anyway. See https://github.com/dotnet/linker/issues/2790")] private static (MethodInfo methodInfo, Type[] parameterTypes) GetCachedMethodInfo(IDotNetObjectReference objectReference, string methodIdentifier) { var type = objectReference.Type; @@ -499,11 +501,16 @@ private static Assembly GetRequiredLoadedAssembly(AssemblyKey assemblyKey) ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyKey.AssemblyName}'."); } - private static void ClearCache(Type[]? _) + // don't point the MetadataUpdateHandlerAttribute at the DotNetDispatcher class, since the attribute has + // DynamicallyAccessedMemberTypes.All. This causes unnecessary trim warnings on the non-MetadataUpdateHandler methods. + internal static class MetadataUpdateHandler { - _cachedMethodsByAssembly.Clear(); - _cachedMethodsByType.Clear(); - _cachedConvertToTaskByType.Clear(); + public static void ClearCache(Type[]? _) + { + _cachedMethodsByAssembly.Clear(); + _cachedMethodsByType.Clear(); + _cachedConvertToTaskByType.Clear(); + } } private readonly struct AssemblyKey : IEquatable diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs index e99686cc611f..26f23952add1 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs @@ -1,12 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; namespace Microsoft.JSInterop.Infrastructure; -internal sealed class DotNetObjectReferenceJsonConverter : JsonConverter> where TValue : class +internal sealed class DotNetObjectReferenceJsonConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue> : JsonConverter> where TValue : class { public DotNetObjectReferenceJsonConverter(JSRuntime jsRuntime) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index 9656cd77c833..c2e0cdb0ff18 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -290,7 +290,7 @@ internal long BeginTransmittingStream(DotNetStreamReference dotNetStreamReferenc return streamId; } - internal long TrackObjectReference(DotNetObjectReference dotNetObjectReference) where TValue : class + internal long TrackObjectReference<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue>(DotNetObjectReference dotNetObjectReference) where TValue : class { if (dotNetObjectReference == null) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml index 6306606df515..f5d3799b692c 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml @@ -1,6 +1,6 @@ - + - + ILLink IL2026 @@ -39,9 +39,9 @@ ILLink - IL2060 + IL2055 member - M:Microsoft.JSInterop.Infrastructure.DotNetDispatcher.<>c.<GetTaskByType>b__14_0(System.Type,System.Reflection.MethodInfo) + M:Microsoft.JSInterop.Infrastructure.DotNetObjectReferenceJsonConverterFactory.CreateConverter(System.Type,System.Text.Json.JsonSerializerOptions) ILLink @@ -68,4 +68,4 @@ M:Microsoft.JSInterop.JSRuntimeExtensions.<InvokeAsync>d__4`1.MoveNext - + \ No newline at end of file From e22d203e8a18fdec26219b2ac8fc1dcbaecbf03f Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 10 May 2022 18:35:44 +0000 Subject: [PATCH 4/5] Use LinkerFlags for better readability. --- .../Microsoft.JSInterop/src/DotNetObjectReference.cs | 3 ++- .../Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs | 5 +++-- .../src/Infrastructure/DotNetDispatcher.cs | 4 ++-- .../src/Infrastructure/DotNetObjectReferenceJsonConverter.cs | 3 ++- .../src/Infrastructure/IDotNetObjectReference.cs | 3 ++- src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs | 2 +- src/Shared/LinkerFlags.cs | 5 +++++ 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs index f40dbef662ae..56de56eedb2c 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using static Microsoft.AspNetCore.Internal.LinkerFlags; namespace Microsoft.JSInterop; @@ -15,7 +16,7 @@ public static class DotNetObjectReference /// /// The reference type to track. /// An instance of . - public static DotNetObjectReference Create<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue>(TValue value) where TValue : class + public static DotNetObjectReference Create<[DynamicallyAccessedMembers(JSInvokable)] TValue>(TValue value) where TValue : class { if (value is null) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs index d74ff392ad3f..e9d96796ae42 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.JSInterop.Infrastructure; +using static Microsoft.AspNetCore.Internal.LinkerFlags; namespace Microsoft.JSInterop; @@ -14,7 +15,7 @@ namespace Microsoft.JSInterop; /// To avoid leaking memory, the reference must later be disposed by JS code or by .NET code. /// /// The type of the value to wrap. -public sealed class DotNetObjectReference<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue> : +public sealed class DotNetObjectReference<[DynamicallyAccessedMembers(JSInvokable)] TValue> : IDotNetObjectReference, IDisposable where TValue : class { private readonly TValue _value; @@ -74,7 +75,7 @@ internal JSRuntime? JSRuntime object IDotNetObjectReference.Value => Value; - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + [DynamicallyAccessedMembers(JSInvokable)] Type IDotNetObjectReference.Type => typeof(TValue); internal bool Disposed { get; private set; } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index 2cdd7f5fd7c4..82fe64a057e9 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -11,6 +11,7 @@ using System.Text; using System.Text.Json; using Microsoft.AspNetCore.Internal; +using static Microsoft.AspNetCore.Internal.LinkerFlags; [assembly: MetadataUpdateHandler(typeof(Microsoft.JSInterop.Infrastructure.DotNetDispatcher.MetadataUpdateHandler))] @@ -19,7 +20,6 @@ namespace Microsoft.JSInterop.Infrastructure; /// /// Provides methods that receive incoming calls from JS to .NET. /// -[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070", Justification = "Linker does not propogate annotations to generated state machine. https://github.com/mono/linker/issues/1403")] public static class DotNetDispatcher { private const string DisposeDotNetObjectReferenceMethodName = "__Dispose"; @@ -396,7 +396,7 @@ private static (MethodInfo methodInfo, Type[] parameterTypes) GetCachedMethodInf throw new ArgumentException($"The type '{type.Name}' does not contain a public invokable method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); } - static Dictionary ScanTypeForCallableMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + static Dictionary ScanTypeForCallableMethods([DynamicallyAccessedMembers(JSInvokable)] Type type) { var result = new Dictionary(StringComparer.Ordinal); diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs index 26f23952add1..a5f18229fecb 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs @@ -4,10 +4,11 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; +using static Microsoft.AspNetCore.Internal.LinkerFlags; namespace Microsoft.JSInterop.Infrastructure; -internal sealed class DotNetObjectReferenceJsonConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue> : JsonConverter> where TValue : class +internal sealed class DotNetObjectReferenceJsonConverter<[DynamicallyAccessedMembers(JSInvokable)] TValue> : JsonConverter> where TValue : class { public DotNetObjectReferenceJsonConverter(JSRuntime jsRuntime) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs index 93abe01eda9a..906f02b49b9e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using static Microsoft.AspNetCore.Internal.LinkerFlags; namespace Microsoft.JSInterop.Infrastructure; @@ -9,6 +10,6 @@ internal interface IDotNetObjectReference : IDisposable { object Value { get; } - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + [DynamicallyAccessedMembers(JSInvokable)] Type Type { get; } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index c2e0cdb0ff18..9b0e25f27adf 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -290,7 +290,7 @@ internal long BeginTransmittingStream(DotNetStreamReference dotNetStreamReferenc return streamId; } - internal long TrackObjectReference<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue>(DotNetObjectReference dotNetObjectReference) where TValue : class + internal long TrackObjectReference<[DynamicallyAccessedMembers(JSInvokable)] TValue>(DotNetObjectReference dotNetObjectReference) where TValue : class { if (dotNetObjectReference == null) { diff --git a/src/Shared/LinkerFlags.cs b/src/Shared/LinkerFlags.cs index d0a7c88f6115..1c9954b06616 100644 --- a/src/Shared/LinkerFlags.cs +++ b/src/Shared/LinkerFlags.cs @@ -16,4 +16,9 @@ internal static class LinkerFlags /// Flags for a component /// public const DynamicallyAccessedMemberTypes Component = DynamicallyAccessedMemberTypes.All; + + /// + /// Flags for a JSInvokable type. + /// + public const DynamicallyAccessedMemberTypes JSInvokable = DynamicallyAccessedMemberTypes.PublicMethods; } From d4959cb8caa1da8cf3686fc40df2cadf8decdfb9 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 16 May 2022 18:33:28 +0000 Subject: [PATCH 5/5] Respond to PR feedback. Remove the breaking change to only allow methods on the TValue type of IDotNetObjectReference. If a caller provides a base class to the TValue of DotNetObjectReference, and a derived type as the actual instance, only the base class's methods will be preserved. This allows for the current behavior to be preserved, at the cost of not being fully trim-compatible. In order to make it fully trim-compatible, we will need to introduce a new API that can be made trimming compatible. --- .../Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs | 3 --- .../src/Infrastructure/DotNetDispatcher.cs | 4 +--- .../src/Infrastructure/IDotNetObjectReference.cs | 6 ------ .../src/Microsoft.JSInterop.WarningSuppressions.xml | 6 ++++++ 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs index e9d96796ae42..edf6f612c5b3 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs @@ -75,9 +75,6 @@ internal JSRuntime? JSRuntime object IDotNetObjectReference.Value => Value; - [DynamicallyAccessedMembers(JSInvokable)] - Type IDotNetObjectReference.Type => typeof(TValue); - internal bool Disposed { get; private set; } /// diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index 82fe64a057e9..08821b795ded 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -381,11 +381,9 @@ private static Task GetTaskByType(Type type, object obj) private static Task CreateValueTaskConverter<[DynamicallyAccessedMembers(LinkerFlags.JsonSerialized)] T>(object result) => ((ValueTask)result).AsTask(); - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111:ReflectionToDynamicallyAccessedMembers", - Justification = "The Type being passed into GetOrAdd is annotated, but the trimmer warns anyway. See https://github.com/dotnet/linker/issues/2790")] private static (MethodInfo methodInfo, Type[] parameterTypes) GetCachedMethodInfo(IDotNetObjectReference objectReference, string methodIdentifier) { - var type = objectReference.Type; + var type = objectReference.Value.GetType(); var assemblyMethods = _cachedMethodsByType.GetOrAdd(type, ScanTypeForCallableMethods); if (assemblyMethods.TryGetValue(methodIdentifier, out var result)) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs index 906f02b49b9e..a8669174ceb7 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs @@ -1,15 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; -using static Microsoft.AspNetCore.Internal.LinkerFlags; - namespace Microsoft.JSInterop.Infrastructure; internal interface IDotNetObjectReference : IDisposable { object Value { get; } - - [DynamicallyAccessedMembers(JSInvokable)] - Type Type { get; } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml index f5d3799b692c..1af0e56483dc 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml @@ -67,5 +67,11 @@ member M:Microsoft.JSInterop.JSRuntimeExtensions.<InvokeAsync>d__4`1.MoveNext + + ILLink + IL2111 + member + M:Microsoft.JSInterop.Infrastructure.DotNetDispatcher.GetCachedMethodInfo(Microsoft.JSInterop.Infrastructure.IDotNetObjectReference,System.String) + \ No newline at end of file