Browse Source

Add infrastructure to test WASM linking (#29900)

Pranav K 5 years ago
parent
commit
a28da74c36
68 changed files with 712 additions and 181 deletions
  1. 15 0
      AspNetCore.sln
  2. 1 0
      src/Components/Components.slnf
  3. 8 20
      src/Components/Components/src/BindConverter.cs
  4. 8 6
      src/Components/Components/src/CascadingParameterState.cs
  5. 5 4
      src/Components/Components/src/ComponentFactory.cs
  6. 4 4
      src/Components/Components/src/DefaultComponentActivator.cs
  7. 2 1
      src/Components/Components/src/IComponentActivator.cs
  8. 9 3
      src/Components/Components/src/LayoutView.cs
  9. 3 2
      src/Components/Components/src/Lifetime/ComponentApplicationState.cs
  10. 71 0
      src/Components/Components/src/Microsoft.AspNetCore.Components.WarningSuppressions.xml
  11. 2 1
      src/Components/Components/src/Microsoft.AspNetCore.Components.csproj
  12. 1 1
      src/Components/Components/src/ParameterView.cs
  13. 6 4
      src/Components/Components/src/Reflection/ComponentProperties.cs
  14. 5 3
      src/Components/Components/src/Reflection/MemberAssignment.cs
  15. 5 0
      src/Components/Components/src/Reflection/PropertySetter.cs
  16. 9 3
      src/Components/Components/src/RenderTree/RenderTreeFrame.cs
  17. 3 1
      src/Components/Components/src/RenderTree/RenderTreeFrameArrayBuilder.cs
  18. 3 1
      src/Components/Components/src/RenderTree/Renderer.cs
  19. 5 3
      src/Components/Components/src/Rendering/RenderTreeBuilder.cs
  20. 3 0
      src/Components/Components/src/Routing/RouteContext.cs
  21. 4 1
      src/Components/Components/src/Routing/RouteData.cs
  22. 4 3
      src/Components/Components/src/Routing/RouteEntry.cs
  23. 20 2
      src/Components/Components/src/Routing/RouteTableFactory.cs
  24. 3 3
      src/Components/Components/src/Routing/Router.cs
  25. 2 2
      src/Components/Components/test/Routing/RouteTableFactoryTests.cs
  26. 1 0
      src/Components/ComponentsNoDeps.slnf
  27. 11 0
      src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.WarningSuppressions.xml
  28. 1 1
      src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.csproj
  29. 1 0
      src/Components/Ignitor/src/Ignitor.csproj
  30. 1 0
      src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj
  31. 3 1
      src/Components/Shared/src/ComponentParametersTypeCache.cs
  32. 2 1
      src/Components/Shared/src/RootComponentTypeCache.cs
  33. 2 1
      src/Components/Shared/src/WebEventData.cs
  34. 2 0
      src/Components/Web/src/Forms/InputFile/InputFileJsCallbacksRelay.cs
  35. 1 0
      src/Components/Web/src/Microsoft.AspNetCore.Components.Web.csproj
  36. 1 1
      src/Components/Web/src/Routing/NavLink.cs
  37. 1 1
      src/Components/Web/src/Virtualization/Virtualize.cs
  38. 3 0
      src/Components/Web/src/Virtualization/VirtualizeJsInterop.cs
  39. 86 17
      src/Components/WebAssembly/Authentication.Msal/src/Interop/yarn.lock
  40. 96 17
      src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/yarn.lock
  41. 3 2
      src/Components/WebAssembly/WebAssembly.Authentication/src/RemoteAuthenticationBuilderExtensions.cs
  42. 2 1
      src/Components/WebAssembly/WebAssembly/src/Hosting/EntrypointInvoker.cs
  43. 5 4
      src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMapping.cs
  44. 5 4
      src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMappingCollection.cs
  45. 2 0
      src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs
  46. 7 0
      src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs
  47. 47 0
      src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.WarningSuppressions.xml
  48. 2 1
      src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj
  49. 2 3
      src/Components/WebAssembly/WebAssembly/src/Prerendering/ClientComponentParameterDeserializer.cs
  50. 4 4
      src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs
  51. 8 5
      src/Components/WebAssembly/WebAssembly/src/Services/DefaultWebAssemblyJSRuntime.cs
  52. 12 19
      src/Components/WebAssembly/WebAssembly/src/Services/LazyAssemblyLoader.cs
  53. 20 0
      src/Components/WebAssembly/testassets/WasmLinkerTest/README.md
  54. 80 0
      src/Components/WebAssembly/testassets/WasmLinkerTest/WasmLinkerTest.csproj
  55. 3 1
      src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs
  56. 4 1
      src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs
  57. 4 2
      src/JSInterop/Microsoft.JSInterop/src/IJSObjectReference.cs
  58. 4 2
      src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs
  59. 4 1
      src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs
  60. 4 2
      src/JSInterop/Microsoft.JSInterop/src/Implementation/JSObjectReference.cs
  61. 6 4
      src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs
  62. 4 2
      src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs
  63. 5 3
      src/JSInterop/Microsoft.JSInterop/src/JSObjectReferenceExtensions.cs
  64. 7 4
      src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs
  65. 5 4
      src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs
  66. 29 0
      src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml
  67. 1 4
      src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj
  68. 20 0
      src/Shared/LinkerFlags.cs

+ 15 - 0
AspNetCore.sln

@@ -1574,6 +1574,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Browse
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{722E5A66-D84A-4689-AA87-7197FF5D7070}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WasmLinkerTest", "src\Components\WebAssembly\testassets\WasmLinkerTest\WasmLinkerTest.csproj", "{3B375FFC-1E38-453E-A26D-A510CCD3339E}"
+EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapActionSample", "src\Http\Routing\samples\MapActionSample\MapActionSample.csproj", "{8F510BAA-FA6B-4648-8F98-28DF5C69DBB2}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{71287382-95EF-490D-A285-87196E29E88A}"
@@ -7489,6 +7491,18 @@ Global
 		{8F6F73F7-0DDA-4AA3-9887-2FB0141786AC}.Release|x64.Build.0 = Release|Any CPU
 		{8F6F73F7-0DDA-4AA3-9887-2FB0141786AC}.Release|x86.ActiveCfg = Release|Any CPU
 		{8F6F73F7-0DDA-4AA3-9887-2FB0141786AC}.Release|x86.Build.0 = Release|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Debug|x64.Build.0 = Debug|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Debug|x86.Build.0 = Debug|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Release|x64.ActiveCfg = Release|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Release|x64.Build.0 = Release|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Release|x86.ActiveCfg = Release|Any CPU
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E}.Release|x86.Build.0 = Release|Any CPU
 		{1A5582DD-06F4-4427-BFDC-B021A84A01BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{1A5582DD-06F4-4427-BFDC-B021A84A01BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{1A5582DD-06F4-4427-BFDC-B021A84A01BC}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -8291,6 +8305,7 @@ Global
 		{B739074E-6652-4F5B-B37E-775DC2245FEC} = {8F33439F-5532-45D6-8A44-20EF9104AA9D}
 		{722E5A66-D84A-4689-AA87-7197FF5D7070} = {54C42F57-5447-4C21-9812-4AF665567566}
 		{8F510BAA-FA6B-4648-8F98-28DF5C69DBB2} = {722E5A66-D84A-4689-AA87-7197FF5D7070}
+		{3B375FFC-1E38-453E-A26D-A510CCD3339E} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
 		{71287382-95EF-490D-A285-87196E29E88A} = {562D5067-8CD8-4F19-BCBB-873204932C61}
 		{B4226BE2-DCB7-40C5-93F2-94C9BD6F4394} = {71287382-95EF-490D-A285-87196E29E88A}
 		{8F6F73F7-0DDA-4AA3-9887-2FB0141786AC} = {B4226BE2-DCB7-40C5-93F2-94C9BD6F4394}

+ 1 - 0
src/Components/Components.slnf

@@ -34,6 +34,7 @@
       "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Client\\Wasm.Authentication.Client.csproj",
       "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Shared\\Wasm.Authentication.Shared.csproj",
       "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Server\\Wasm.Authentication.Server.csproj",
+      "src\\Components\\WebAssembly\\testassets\\WasmLinkerTest.csproj",
       "src\\Components\\Web\\src\\Microsoft.AspNetCore.Components.Web.csproj",
       "src\\Components\\Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj",
       "src\\Components\\benchmarkapps\\Wasm.Performance\\Driver\\Wasm.Performance.Driver.csproj",

+ 8 - 20
src/Components/Components/src/BindConverter.cs

@@ -486,19 +486,14 @@ namespace Microsoft.AspNetCore.Components
             return value.Value.ToString(culture ?? CultureInfo.CurrentCulture);
         }
 
-        private static string FormatEnumValueCore<T>(T value, CultureInfo? culture) where T : struct, Enum
-        {
-            return value.ToString(); // The overload that accepts a culture is [Obsolete]
-        }
-
-        private static string? FormatNullableEnumValueCore<T>(T? value, CultureInfo? culture) where T : struct, Enum
+        private static string? FormatEnumValueCore<T>(T value, CultureInfo? culture)
         {
             if (value == null)
             {
                 return null;
             }
 
-            return value.Value.ToString(); // The overload that accepts a culture is [Obsolete]
+            return value.ToString();
         }
 
         /// <summary>
@@ -1285,9 +1280,6 @@ namespace Microsoft.AspNetCore.Components
         {
             private readonly static ConcurrentDictionary<Type, Delegate> _cache = new ConcurrentDictionary<Type, Delegate>();
 
-            private static MethodInfo? _formatEnumValue;
-            private static MethodInfo? _formatNullableEnumValue;
-
             public static BindFormatter<T> Get<T>()
             {
                 if (!_cache.TryGetValue(typeof(T), out var formatter))
@@ -1370,17 +1362,9 @@ namespace Microsoft.AspNetCore.Components
                     {
                         formatter = (BindFormatter<DateTimeOffset?>)FormatNullableDateTimeOffsetValueCore;
                     }
-                    else if (typeof(T).IsEnum)
-                    {
-                        // We have to deal invoke this dynamically to work around the type constraint on Enum.TryParse.
-                        var method = _formatEnumValue ??= typeof(BindConverter).GetMethod(nameof(FormatEnumValueCore), BindingFlags.NonPublic | BindingFlags.Static)!;
-                        formatter = method.MakeGenericMethod(typeof(T)).CreateDelegate(typeof(BindFormatter<T>), target: null);
-                    }
-                    else if (Nullable.GetUnderlyingType(typeof(T)) is Type innerType && innerType.IsEnum)
+                    else if (typeof(T).IsEnum || Nullable.GetUnderlyingType(typeof(T)) is Type { IsEnum: true } innerType)
                     {
-                        // We have to deal invoke this dynamically to work around the type constraint on Enum.TryParse.
-                        var method = _formatNullableEnumValue ??= typeof(BindConverter).GetMethod(nameof(FormatNullableEnumValueCore), BindingFlags.NonPublic | BindingFlags.Static)!;
-                        formatter = method.MakeGenericMethod(innerType).CreateDelegate(typeof(BindFormatter<T>), target: null);
+                        formatter = (BindFormatter<T>)FormatEnumValueCore<T>;
                     }
                     else
                     {
@@ -1421,6 +1405,10 @@ namespace Microsoft.AspNetCore.Components
             private static MethodInfo? _convertToEnum;
             private static MethodInfo? _convertToNullableEnum;
 
+            [UnconditionalSuppressMessage(
+                "ReflectionAnalysis",
+                "IL2060:MakeGenericMethod",
+                Justification = "The referenced methods don't have any DynamicallyAccessedMembers annotations. See https://github.com/mono/linker/issues/1727")]
             public static BindParser<T> Get<T>()
             {
                 if (!_cache.TryGetValue(typeof(T), out var parser))

+ 8 - 6
src/Components/Components/src/CascadingParameterState.cs

@@ -1,19 +1,19 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
-using Microsoft.AspNetCore.Components.Reflection;
-using Microsoft.AspNetCore.Components.Rendering;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
+using Microsoft.AspNetCore.Components.Reflection;
+using Microsoft.AspNetCore.Components.Rendering;
 
 namespace Microsoft.AspNetCore.Components
 {
     internal readonly struct CascadingParameterState
     {
-        private readonly static ConcurrentDictionary<Type, ReflectedCascadingParameterInfo[]> _cachedInfos
-            = new ConcurrentDictionary<Type, ReflectedCascadingParameterInfo[]>();
+        private readonly static ConcurrentDictionary<Type, ReflectedCascadingParameterInfo[]> _cachedInfos = new();
 
         public string LocalValueName { get; }
         public ICascadingValueComponent ValueSupplier { get; }
@@ -76,7 +76,8 @@ namespace Microsoft.AspNetCore.Components
             return null;
         }
 
-        private static ReflectedCascadingParameterInfo[] GetReflectedCascadingParameterInfos(Type componentType)
+        private static ReflectedCascadingParameterInfo[] GetReflectedCascadingParameterInfos(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type componentType)
         {
             if (!_cachedInfos.TryGetValue(componentType, out var infos))
             {
@@ -87,7 +88,8 @@ namespace Microsoft.AspNetCore.Components
             return infos;
         }
 
-        private static ReflectedCascadingParameterInfo[] CreateReflectedCascadingParameterInfos(Type componentType)
+        private static ReflectedCascadingParameterInfo[] CreateReflectedCascadingParameterInfos(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type componentType)
         {
             List<ReflectedCascadingParameterInfo>? result = null;
             var candidateProps = ComponentProperties.GetCandidateBindableProperties(componentType);

+ 5 - 4
src/Components/Components/src/ComponentFactory.cs

@@ -3,9 +3,11 @@
 
 using System;
 using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
 using Microsoft.AspNetCore.Components.Reflection;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components
 {
@@ -14,8 +16,7 @@ namespace Microsoft.AspNetCore.Components
         private static readonly BindingFlags _injectablePropertyBindingFlags
             = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
 
-        private readonly ConcurrentDictionary<Type, Action<IServiceProvider, IComponent>> _cachedInitializers
-            = new ConcurrentDictionary<Type, Action<IServiceProvider, IComponent>>();
+        private readonly ConcurrentDictionary<Type, Action<IServiceProvider, IComponent>> _cachedInitializers = new();
 
         private readonly IComponentActivator _componentActivator;
 
@@ -24,7 +25,7 @@ namespace Microsoft.AspNetCore.Components
             _componentActivator = componentActivator ?? throw new ArgumentNullException(nameof(componentActivator));
         }
 
-        public IComponent InstantiateComponent(IServiceProvider serviceProvider, Type componentType)
+        public IComponent InstantiateComponent(IServiceProvider serviceProvider, [DynamicallyAccessedMembers(Component)] Type componentType)
         {
             var component = _componentActivator.CreateInstance(componentType);
             if (component is null)
@@ -52,7 +53,7 @@ namespace Microsoft.AspNetCore.Components
             initializer(serviceProvider, instance);
         }
 
-        private Action<IServiceProvider, IComponent> CreateInitializer(Type type)
+        private Action<IServiceProvider, IComponent> CreateInitializer([DynamicallyAccessedMembers(Component)] Type type)
         {
             // Do all the reflection up front
             var injectableProperties =

+ 4 - 4
src/Components/Components/src/DefaultComponentActivator.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 
 namespace Microsoft.AspNetCore.Components
 {
@@ -10,15 +11,14 @@ namespace Microsoft.AspNetCore.Components
         public static IComponentActivator Instance { get; } = new DefaultComponentActivator();
 
         /// <inheritdoc />
-        public IComponent CreateInstance(Type componentType)
+        public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
         {
-            var instance = Activator.CreateInstance(componentType);
-            if (!(instance is IComponent component))
+            if (!typeof(IComponent).IsAssignableFrom(componentType))
             {
                 throw new ArgumentException($"The type {componentType.FullName} does not implement {nameof(IComponent)}.", nameof(componentType));
             }
 
-            return component;
+            return (IComponent)Activator.CreateInstance(componentType)!;
         }
     }
 }

+ 2 - 1
src/Components/Components/src/IComponentActivator.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 
 namespace Microsoft.AspNetCore.Components
 {
@@ -17,6 +18,6 @@ namespace Microsoft.AspNetCore.Components
         /// </summary>
         /// <param name="componentType">The type of component to create.</param>
         /// <returns>A reference to the newly created component.</returns>
-        IComponent CreateInstance(Type componentType);
+        IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType);
     }
 }

+ 9 - 3
src/Components/Components/src/LayoutView.cs

@@ -2,8 +2,11 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components.Rendering;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components
 {
@@ -28,6 +31,7 @@ namespace Microsoft.AspNetCore.Components
         /// The type must implement <see cref="IComponent"/> and accept a parameter named <see cref="LayoutComponentBase.Body"/>.
         /// </summary>
         [Parameter]
+        [DynamicallyAccessedMembers(Component)]
         public Type Layout { get; set; } = default!;
 
         /// <inheritdoc />
@@ -61,14 +65,16 @@ namespace Microsoft.AspNetCore.Components
             _renderHandle.Render(fragment);
         }
 
-        private static RenderFragment WrapInLayout(Type layoutType, RenderFragment bodyParam)
+        private static RenderFragment WrapInLayout([DynamicallyAccessedMembers(Component)] Type layoutType, RenderFragment bodyParam)
         {
-            return builder =>
-            {
+            void Render(RenderTreeBuilder builder)
+            { 
                 builder.OpenComponent(0, layoutType);
                 builder.AddAttribute(1, LayoutComponentBase.BodyPropertyName, bodyParam);
                 builder.CloseComponent();
             };
+
+            return Render;
         }
 
         private static Type? GetParentLayoutType(Type type)

+ 3 - 2
src/Components/Components/src/Lifetime/ComponentApplicationState.cs

@@ -6,6 +6,7 @@ using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Text.Json;
 using System.Threading.Tasks;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components
 {
@@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.Components
         /// <typeparam name="TValue">The <paramref name="instance"/> type.</typeparam>
         /// <param name="key">The key to use to persist the state.</param>
         /// <param name="instance">The instance to persist.</param>
-        public void PersistAsJson<TValue>(string key, TValue instance)
+        public void PersistAsJson<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string key, TValue instance)
         {
             if (key is null)
             {
@@ -152,7 +153,7 @@ namespace Microsoft.AspNetCore.Components
         /// <param name="key">The key used to persist the instance.</param>
         /// <param name="instance">The persisted instance.</param>
         /// <returns><c>true</c> if the state was found; <c>false</c> otherwise.</returns>
-        public bool TryRedeemFromJson<TValue>(string key, [MaybeNullWhen(false)] out TValue? instance)
+        public bool TryRedeemFromJson<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string key, [MaybeNullWhen(false)] out TValue? instance)
         {
             if (key is null)
             {

+ 71 - 0
src/Components/Components/src/Microsoft.AspNetCore.Components.WarningSuppressions.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<linker>
+  <assembly fullname="Microsoft.AspNetCore.Components, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2026</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.LegacyRouteMatching.LegacyRouteTableFactory.&lt;&gt;c.&lt;Create&gt;b__2_1(System.Reflection.Assembly)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2026</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.RouteTableFactory.GetRouteableComponents(System.Collections.Generic.IEnumerable{System.Reflection.Assembly})</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2067</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.CascadingParameterState.CreateReflectedCascadingParameterInfos(System.Type)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.CascadingParameterState.FindCascadingParameters(Microsoft.AspNetCore.Components.Rendering.ComponentState)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.ComponentFactory.PerformPropertyInjection(System.IServiceProvider,Microsoft.AspNetCore.Components.IComponent)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.DynamicComponent.SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.LegacyRouteMatching.LegacyRouteEntry.Match(Microsoft.AspNetCore.Components.Routing.RouteContext)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.Reflection.ComponentProperties.SetProperties(Microsoft.AspNetCore.Components.ParameterView@,System.Object)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.RouteTableFactory.Create(System.Collections.Generic.Dictionary{System.Type,System.String[]})</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2077</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.ComponentFactory.CreateInitializer(System.Type)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2077</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.LayoutView.&lt;&gt;c__DisplayClass13_0.&lt;WrapInLayout&gt;g__Render|0(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder)</property>
+    </attribute>
+  </assembly>
+</linker>

+ 2 - 1
src/Components/Components/src/Microsoft.AspNetCore.Components.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -11,6 +11,7 @@
   <ItemGroup>
     <Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="RenderTree" />
     <Compile Include="$(ComponentsSharedSourceRoot)src\JsonSerializerOptionsProvider.cs" />
+    <Compile Include="$(SharedSourceRoot)LinkerFlags.cs" LinkBase="Shared" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 1
src/Components/Components/src/ParameterView.cs

@@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Components
         /// <param name="defaultValue">The default value to return if no such parameter exists in the collection.</param>
         /// <returns>The parameter value if found; otherwise <paramref name="defaultValue"/>.</returns>
         public TValue GetValueOrDefault<TValue>(string parameterName, TValue defaultValue)
-            => TryGetValue<TValue>(parameterName, out TValue result) ? result : defaultValue;
+            => TryGetValue<TValue>(parameterName, out TValue? result) ? result : defaultValue;
 
         /// <summary>
         /// Returns a dictionary populated with the contents of the <see cref="ParameterView"/>.

+ 6 - 4
src/Components/Components/src/Reflection/ComponentProperties.cs

@@ -7,6 +7,7 @@ using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.Reflection
 {
@@ -159,11 +160,12 @@ namespace Microsoft.AspNetCore.Components.Reflection
             }
         }
 
-        internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties(Type targetType)
+        internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties([DynamicallyAccessedMembers(Component)] Type targetType)
             => MemberAssignment.GetPropertiesIncludingInherited(targetType, _bindablePropertyFlags);
 
         [DoesNotReturn]
-        private static void ThrowForUnknownIncomingParameterName(Type targetType, string parameterName)
+        private static void ThrowForUnknownIncomingParameterName([DynamicallyAccessedMembers(Component)] Type targetType,
+            string parameterName)
         {
             // We know we're going to throw by this stage, so it doesn't matter that the following
             // reflection code will be slow. We're just trying to help developers see what they did wrong.
@@ -217,7 +219,7 @@ namespace Microsoft.AspNetCore.Components.Reflection
         }
 
         [DoesNotReturn]
-        private static void ThrowForMultipleCaptureUnmatchedValuesParameters(Type targetType)
+        private static void ThrowForMultipleCaptureUnmatchedValuesParameters([DynamicallyAccessedMembers(Component)] Type targetType)
         {
             // We don't care about perf here, we want to report an accurate and useful error.
             var propertyNames = targetType
@@ -249,7 +251,7 @@ namespace Microsoft.AspNetCore.Components.Reflection
             private readonly Dictionary<string, PropertySetter> _underlyingWriters;
             private readonly ConcurrentDictionary<string, PropertySetter?> _referenceEqualityWritersCache;
 
-            public WritersForType(Type targetType)
+            public WritersForType([DynamicallyAccessedMembers(Component)] Type targetType)
             {
                 _underlyingWriters = new Dictionary<string, PropertySetter>(StringComparer.OrdinalIgnoreCase);
                 _referenceEqualityWritersCache = new ConcurrentDictionary<string, PropertySetter?>(ReferenceEqualityComparer.Instance);

+ 5 - 3
src/Components/Components/src/Reflection/MemberAssignment.cs

@@ -3,15 +3,18 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.Reflection
 {
     internal class MemberAssignment
     {
         public static IEnumerable<PropertyInfo> GetPropertiesIncludingInherited(
-            Type type, BindingFlags bindingFlags)
+            [DynamicallyAccessedMembers(Component)] Type type,
+            BindingFlags bindingFlags)
         {
             var dictionary = new Dictionary<string, List<PropertyInfo>>();
 
@@ -19,8 +22,7 @@ namespace Microsoft.AspNetCore.Components.Reflection
 
             while (currentType != null)
             {
-                var properties = currentType.GetProperties(bindingFlags)
-                    .Where(prop => prop.DeclaringType == currentType);
+                var properties = currentType.GetProperties(bindingFlags  | BindingFlags.DeclaredOnly);
                 foreach (var property in properties)
                 {
                     if (!dictionary.TryGetValue(property.Name, out var others))

+ 5 - 0
src/Components/Components/src/Reflection/IPropertySetter.cs → src/Components/Components/src/Reflection/PropertySetter.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 
 namespace Microsoft.AspNetCore.Components.Reflection
@@ -13,6 +14,10 @@ namespace Microsoft.AspNetCore.Components.Reflection
 
         private readonly Action<object, object> _setterDelegate;
 
+        [UnconditionalSuppressMessage(
+            "ReflectionAnalysis",
+            "IL2060:MakeGenericMethod",
+            Justification = "The referenced methods don't have any DynamicallyAccessedMembers annotations. See https://github.com/mono/linker/issues/1727")]
         public PropertySetter(Type targetType, PropertyInfo property)
         {
             if (property.SetMethod == null)

+ 9 - 3
src/Components/Components/src/RenderTree/RenderTreeFrame.cs

@@ -4,10 +4,12 @@
 #nullable disable
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Runtime.InteropServices;
 #if !IGNITOR
 using Microsoft.AspNetCore.Components.Rendering;
 #endif
+using Microsoft.AspNetCore.Internal;
 
 #if IGNITOR
 namespace Ignitor
@@ -143,7 +145,11 @@ namespace Microsoft.AspNetCore.Components.RenderTree
 
         [FieldOffset(8)] internal int ComponentSubtreeLengthField;
         [FieldOffset(12)] internal int ComponentIdField;
-        [FieldOffset(16)] internal Type ComponentTypeField;
+        [FieldOffset(16)]
+#if !IGNITOR
+        [DynamicallyAccessedMembers(Internal.LinkerFlags.Component)]
+#endif
+        internal Type ComponentTypeField;
         [FieldOffset(24)] internal ComponentState ComponentStateField;
         [FieldOffset(32)] internal object ComponentKeyField;
 
@@ -265,7 +271,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
         }
 
         // Component constructor
-        private RenderTreeFrame(int sequence, int componentSubtreeLength, Type componentType, ComponentState componentState, object componentKey)
+        private RenderTreeFrame(int sequence, int componentSubtreeLength, [DynamicallyAccessedMembers(LinkerFlags.Component)] Type componentType, ComponentState componentState, object componentKey)
             : this()
         {
             SequenceField = sequence;
@@ -351,7 +357,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
         internal static RenderTreeFrame Attribute(int sequence, string name, object value)
             => new RenderTreeFrame(sequence, attributeName: name, attributeValue: value, attributeEventHandlerId: 0, attributeEventUpdatesAttributeName: null);
 
-        internal static RenderTreeFrame ChildComponent(int sequence, Type componentType)
+        internal static RenderTreeFrame ChildComponent(int sequence, [DynamicallyAccessedMembers(LinkerFlags.Component)] Type componentType)
             => new RenderTreeFrame(sequence, componentSubtreeLength: 0, componentType, null, null);
 
         internal static RenderTreeFrame PlaceholderChildComponentWithSubtreeLength(int subtreeLength)

+ 3 - 1
src/Components/Components/src/RenderTree/RenderTreeFrameArrayBuilder.cs

@@ -2,6 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.RenderTree
 {
@@ -74,7 +76,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
             };
         }
 
-        public void AppendComponent(int sequence, Type componentType)
+        public void AppendComponent(int sequence, [DynamicallyAccessedMembers(Component)] Type componentType)
         {
             if (_itemsInUse == _items.Length)
             {

+ 3 - 1
src/Components/Components/src/RenderTree/Renderer.cs

@@ -6,11 +6,13 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Components.Rendering;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.RenderTree
 {
@@ -114,7 +116,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
         /// </summary>
         /// <param name="componentType">The type of the component to instantiate.</param>
         /// <returns>The component instance.</returns>
-        protected IComponent InstantiateComponent(Type componentType)
+        protected IComponent InstantiateComponent([DynamicallyAccessedMembers(Component)] Type componentType)
             => _componentFactory.InstantiateComponent(_serviceProvider, componentType);
 
         /// <summary>

+ 5 - 3
src/Components/Components/src/Rendering/RenderTreeBuilder.cs

@@ -4,7 +4,9 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using Microsoft.AspNetCore.Components.RenderTree;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.Rendering
 {
@@ -480,7 +482,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
         /// </summary>
         /// <typeparam name="TComponent">The type of the child component.</typeparam>
         /// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
-        public void OpenComponent<TComponent>(int sequence) where TComponent : notnull, IComponent
+        public void OpenComponent<[DynamicallyAccessedMembers(Component)] TComponent>(int sequence) where TComponent : notnull, IComponent
             => OpenComponentUnchecked(sequence, typeof(TComponent));
 
         /// <summary>
@@ -488,7 +490,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
         /// </summary>
         /// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
         /// <param name="componentType">The type of the child component.</param>
-        public void OpenComponent(int sequence, Type componentType)
+        public void OpenComponent(int sequence, [DynamicallyAccessedMembers(Component)] Type componentType)
         {
             if (!typeof(IComponent).IsAssignableFrom(componentType))
             {
@@ -532,7 +534,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
             }
         }
 
-        private void OpenComponentUnchecked(int sequence, Type componentType)
+        private void OpenComponentUnchecked(int sequence, [DynamicallyAccessedMembers(Component)] Type componentType)
         {
             // We are entering a new scope, since we track the "duplicate attributes" per
             // element/component we might need to clean them up now.

+ 3 - 0
src/Components/Components/src/Routing/RouteContext.cs

@@ -3,6 +3,8 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.Routing
 {
@@ -24,6 +26,7 @@ namespace Microsoft.AspNetCore.Components.Routing
 
         public string[] Segments { get; }
 
+        [DynamicallyAccessedMembers(Component)]
         public Type? Handler { get; set; }
 
         public IReadOnlyDictionary<string, object>? Parameters { get; set; }

+ 4 - 1
src/Components/Components/src/Routing/RouteData.cs

@@ -3,6 +3,8 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components
 {
@@ -17,7 +19,7 @@ namespace Microsoft.AspNetCore.Components
         /// </summary>
         /// <param name="pageType">The type of the page matching the route, which must implement <see cref="IComponent"/>.</param>
         /// <param name="routeValues">The route parameter values extracted from the matched route.</param>
-        public RouteData(Type pageType, IReadOnlyDictionary<string, object> routeValues)
+        public RouteData([DynamicallyAccessedMembers(Component)] Type pageType, IReadOnlyDictionary<string, object> routeValues)
         {
             if (pageType == null)
             {
@@ -36,6 +38,7 @@ namespace Microsoft.AspNetCore.Components
         /// <summary>
         /// Gets the type of the page matching the route.
         /// </summary>
+        [DynamicallyAccessedMembers(Component)]
         public Type PageType { get; }
 
         /// <summary>

+ 4 - 3
src/Components/Components/src/Routing/RouteEntry.cs

@@ -4,17 +4,17 @@
 #nullable disable warnings
 
 using System;
-using System.Linq;
 using System.Collections.Generic;
 using System.Diagnostics;
-using System.Text;
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.Routing
 {
     [DebuggerDisplay("Handler = {Handler}, Template = {Template}")]
     internal class RouteEntry
     {
-        public RouteEntry(RouteTemplate template, Type handler, string[] unusedRouteParameterNames)
+        public RouteEntry(RouteTemplate template, [DynamicallyAccessedMembers(Component)] Type handler, string[] unusedRouteParameterNames)
         {
             Template = template;
             UnusedRouteParameterNames = unusedRouteParameterNames;
@@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Components.Routing
 
         public string[] UnusedRouteParameterNames { get; }
 
+        [DynamicallyAccessedMembers(Component)]
         public Type Handler { get; }
 
         internal void Match(RouteContext context)

+ 20 - 2
src/Components/Components/src/Routing/RouteTableFactory.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
 using Microsoft.AspNetCore.Components.Routing;
@@ -27,13 +28,30 @@ namespace Microsoft.AspNetCore.Components
                 return resolvedComponents;
             }
 
-            var componentTypes = key.Assemblies.SelectMany(a => a.ExportedTypes.Where(t => typeof(IComponent).IsAssignableFrom(t)));
+            var componentTypes = GetRouteableComponents(key.Assemblies);
             var routeTable = Create(componentTypes);
             Cache.TryAdd(key, routeTable);
             return routeTable;
         }
 
-        internal static RouteTable Create(IEnumerable<Type> componentTypes)
+        private static List<Type> GetRouteableComponents(IEnumerable<Assembly> assemblies)
+        {
+            var routeableComponents = new List<Type>();
+            foreach (var assembly in assemblies)
+            {
+                foreach (var type in assembly.ExportedTypes)
+                {
+                    if (typeof(IComponent).IsAssignableFrom(type) && type.IsDefined(typeof(RouteAttribute)))
+                    {
+                        routeableComponents.Add(type);
+                    }
+                }
+            }
+
+            return routeableComponents;
+        }
+
+        internal static RouteTable Create(List<Type> componentTypes)
         {
             var templatesByHandler = new Dictionary<Type, string[]>();
             foreach (var componentType in componentTypes)

+ 3 - 3
src/Components/Components/src/Routing/Router.cs

@@ -4,15 +4,16 @@
 #nullable disable warnings
 
 using System;
-using System.Runtime.ExceptionServices;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
+using System.Runtime.ExceptionServices;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
 using Microsoft.AspNetCore.Components.LegacyRouteMatching;
+using Microsoft.Extensions.Logging;
 
 namespace Microsoft.AspNetCore.Components.Routing
 {
@@ -162,7 +163,6 @@ namespace Microsoft.AspNetCore.Components.Routing
                 _assemblies.Clear();
                 _assemblies.UnionWith(assembliesSet);
             }
-
         }
 
         internal virtual void Refresh(bool isNavigationIntercepted)

+ 2 - 2
src/Components/Components/test/Routing/RouteTableFactoryTests.cs

@@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Components.Test.Routing
         public void CanDiscoverRoute()
         {
             // Arrange & Act
-            var routes = RouteTableFactory.Create(new[] { typeof(MyComponent), });
+            var routes = RouteTableFactory.Create(new List<Type> { typeof(MyComponent), });
 
             // Assert
             Assert.Equal("Test1", Assert.Single(routes.Routes).Template.TemplateText);
@@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Components.Test.Routing
         public void CanDiscoverRoutes_WithInheritance()
         {
             // Arrange & Act
-            var routes = RouteTableFactory.Create(new[] { typeof(MyComponent), typeof(MyInheritedComponent), });
+            var routes = RouteTableFactory.Create(new List<Type> { typeof(MyComponent), typeof(MyInheritedComponent), });
 
             // Assert
             Assert.Collection(

+ 1 - 0
src/Components/ComponentsNoDeps.slnf

@@ -36,6 +36,7 @@
       "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Client\\Wasm.Authentication.Client.csproj",
       "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Server\\Wasm.Authentication.Server.csproj",
       "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Shared\\Wasm.Authentication.Shared.csproj",
+      "src\\Components\\WebAssembly\\testassets\\WasmLinkerTest\\WasmLinkerTest.csproj",
       "src\\Components\\Web\\src\\Microsoft.AspNetCore.Components.Web.csproj",
       "src\\Components\\Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj",
       "src\\Components\\benchmarkapps\\Wasm.Performance\\ConsoleHost\\Wasm.Performance.ConsoleHost.csproj",

+ 11 - 0
src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.WarningSuppressions.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<linker>
+  <assembly fullname="Microsoft.AspNetCore.Components.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2080</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.TryGetValidatableProperty(Microsoft.AspNetCore.Components.Forms.FieldIdentifier@,System.Reflection.PropertyInfo@)</property>
+    </attribute>
+  </assembly>
+</linker>

+ 1 - 1
src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>

+ 1 - 0
src/Components/Ignitor/src/Ignitor.csproj

@@ -25,6 +25,7 @@
     <Compile Include="..\..\Components\src\RenderTree\RenderTreeFrame.cs" LinkBase="RenderTree" />
     <Compile Include="..\..\Components\src\RenderTree\RenderTreeFrameType.cs" LinkBase="RenderTree" />
     <Compile Include="..\..\Web\src\WebEventDescriptor.cs" LinkBase="RenderTree" />
+    <Compile Include="$(SharedSourceRoot)LinkerFlags.cs" LinkBase="Shared" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 0
src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj

@@ -25,6 +25,7 @@
     <Reference Include="Microsoft.Extensions.Logging" />
 
     <Compile Include="$(SharedSourceRoot)ValueStopwatch\*.cs" />
+    <Compile Include="$(SharedSourceRoot)LinkerFlags.cs" LinkBase="Shared" />
 
     <!-- Add a project dependency without reference output assemblies to enforce build order -->
     <!-- Applying workaround for https://github.com/microsoft/msbuild/issues/2661 and https://github.com/dotnet/sdk/issues/952 -->

+ 3 - 1
src/Components/Shared/src/ComponentParametersTypeCache.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
 
@@ -10,7 +11,7 @@ namespace Microsoft.AspNetCore.Components
 {
     internal class ComponentParametersTypeCache
     {
-        private readonly ConcurrentDictionary<Key, Type?> _typeToKeyLookUp = new ConcurrentDictionary<Key, Type?>();
+        private readonly ConcurrentDictionary<Key, Type?> _typeToKeyLookUp = new();
 
         public Type? GetParameterType(string assembly, string type)
         {
@@ -25,6 +26,7 @@ namespace Microsoft.AspNetCore.Components
             }
         }
 
+        [RequiresUnreferencedCode("This type attempts to load component parameters that may be trimmed.")]
         private static Type? ResolveType(Key key, Assembly[] assemblies)
         {
             var assembly = assemblies

+ 2 - 1
src/Components/Shared/src/RootComponentTypeCache.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
 
@@ -11,7 +12,7 @@ namespace Microsoft.AspNetCore.Components
     // A cache for root component types
     internal class RootComponentTypeCache
     {
-        private readonly ConcurrentDictionary<Key, Type?> _typeToKeyLookUp = new ConcurrentDictionary<Key, Type?>();
+        private readonly ConcurrentDictionary<Key, Type?> _typeToKeyLookUp = new();
 
         public Type? GetRootComponent(string assembly, string type)
         {

+ 2 - 1
src/Components/Shared/src/WebEventData.cs

@@ -5,6 +5,7 @@ using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Text.Json;
 using Microsoft.AspNetCore.Components.RenderTree;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.Web
 {
@@ -218,7 +219,7 @@ namespace Microsoft.AspNetCore.Components.Web
             return null;
         }
 
-        static T Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json, JsonSerializerOptionsProvider.Options)!;
+        static T Deserialize<[DynamicallyAccessedMembers(JsonSerialized)] T>(string json) => JsonSerializer.Deserialize<T>(json, JsonSerializerOptionsProvider.Options)!;
 
         private static ChangeEventArgs DeserializeChangeEventArgs(string eventArgsJson)
         {

+ 2 - 0
src/Components/Web/src/Forms/InputFile/InputFileJsCallbacksRelay.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Threading.Tasks;
 using Microsoft.JSInterop;
 
@@ -13,6 +14,7 @@ namespace Microsoft.AspNetCore.Components.Forms
 
         public IDisposable DotNetReference { get; }
 
+        [DynamicDependency(nameof(NotifyChange))]
         public InputFileJsCallbacksRelay(IInputFileJsCallbacks callbacks)
         {
             _callbacks = callbacks;

+ 1 - 0
src/Components/Web/src/Microsoft.AspNetCore.Components.Web.csproj

@@ -15,6 +15,7 @@
     <Reference Include="Microsoft.Extensions.DependencyInjection" />
     <Reference Include="Microsoft.JSInterop" />
     <Reference Include="System.IO.Pipelines" />
+    <Compile Include="$(SharedSourceRoot)LinkerFlags.cs" LinkBase="Shared" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/Components/Web/src/Routing/NavLink.cs

@@ -3,8 +3,8 @@
 
 using System;
 using System.Collections.Generic;
-using System.Globalization;
 using System.Diagnostics;
+using System.Globalization;
 using Microsoft.AspNetCore.Components.Rendering;
 
 namespace Microsoft.AspNetCore.Components.Routing

+ 1 - 1
src/Components/Web/src/Virtualization/Virtualize.cs

@@ -1,5 +1,5 @@
 // Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
 using System.Collections.Generic;

+ 3 - 0
src/Components/Web/src/Virtualization/VirtualizeJsInterop.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Threading.Tasks;
 using Microsoft.JSInterop;
 
@@ -17,6 +18,8 @@ namespace Microsoft.AspNetCore.Components.Web.Virtualization
 
         private DotNetObjectReference<VirtualizeJsInterop>? _selfReference;
 
+        [DynamicDependency(nameof(OnSpacerBeforeVisible))]
+        [DynamicDependency(nameof(OnSpacerAfterVisible))]
         public VirtualizeJsInterop(IVirtualizeJsCallbacks owner, IJSRuntime jsRuntime)
         {
             _owner = owner;

+ 86 - 17
src/Components/WebAssembly/Authentication.Msal/src/Interop/yarn.lock

@@ -208,6 +208,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   dependencies:
     color-convert "^1.9.0"
 
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
 anymatch@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -504,7 +511,7 @@ camelcase@^5.0.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
   integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
 
-chalk@^2.3.0, chalk@^2.4.2:
+chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -513,6 +520,14 @@ chalk@^2.3.0, chalk@^2.4.2:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
+chalk@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
+  integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
 chokidar@^2.1.8:
   version "2.1.8"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@@ -601,11 +616,23 @@ color-convert@^1.9.0:
   dependencies:
     color-name "1.1.3"
 
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
 [email protected]:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
   integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
 
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
 commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -1155,6 +1182,11 @@ has-flag@^3.0.0:
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
   integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
 
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
 has-value@^0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
@@ -1454,6 +1486,13 @@ json5@^1.0.1:
   dependencies:
     minimist "^1.2.0"
 
+json5@^2.1.2:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
+  integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
+  dependencies:
+    minimist "^1.2.5"
+
 kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -1483,7 +1522,7 @@ loader-runner@^2.4.0:
   resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
   integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
 
-loader-utils@^1.0.2, loader-utils@^1.2.3, loader-utils@^1.4.0:
+loader-utils@^1.2.3, loader-utils@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
   integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
@@ -1492,6 +1531,15 @@ loader-utils@^1.0.2, loader-utils@^1.2.3, loader-utils@^1.4.0:
     emojis-list "^3.0.0"
     json5 "^1.0.1"
 
+loader-utils@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
+  integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
+  dependencies:
+    big.js "^5.2.2"
+    emojis-list "^3.0.0"
+    json5 "^2.1.2"
+
 locate-path@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@@ -1507,6 +1555,13 @@ lru-cache@^5.1.1:
   dependencies:
     yallist "^3.0.2"
 
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
 make-dir@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
@@ -2143,10 +2198,12 @@ semver@^5.5.0, semver@^5.6.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
 
-semver@^6.0.0:
-  version "6.3.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
-  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+semver@^7.3.4:
+  version "7.3.4"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
+  integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
+  dependencies:
+    lru-cache "^6.0.0"
 
 serialize-javascript@^4.0.0:
   version "4.0.0"
@@ -2362,6 +2419,13 @@ supports-color@^6.1.0:
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
 tapable@^1.0.0, tapable@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
@@ -2443,16 +2507,16 @@ to-regex@^3.0.1, to-regex@^3.0.2:
     regex-not "^1.0.2"
     safe-regex "^1.1.0"
 
-ts-loader@^6.2.1:
-  version "6.2.2"
-  resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.2.tgz#dffa3879b01a1a1e0a4b85e2b8421dc0dfff1c58"
-  integrity sha512-HDo5kXZCBml3EUPcc7RlZOV/JGlLHwppTLEHb3SHnr5V7NXD4klMEkrhJe5wgRbaWsSXi+Y1SIBN/K9B6zWGWQ==
+ts-loader@^8.0.14:
+  version "8.0.17"
+  resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.17.tgz#98f2ccff9130074f4079fd89b946b4c637b1f2fc"
+  integrity sha512-OeVfSshx6ot/TCxRwpBHQ/4lRzfgyTkvi7ghDVrLXOHzTbSK413ROgu/xNqM72i3AFeAIJgQy78FwSMKmOW68w==
   dependencies:
-    chalk "^2.3.0"
+    chalk "^4.1.0"
     enhanced-resolve "^4.0.0"
-    loader-utils "^1.0.2"
+    loader-utils "^2.0.0"
     micromatch "^4.0.0"
-    semver "^6.0.0"
+    semver "^7.3.4"
 
 tslib@^1.9.0:
   version "1.14.1"
@@ -2469,10 +2533,10 @@ typedarray@^0.0.6:
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
-typescript@^3.7.5:
-  version "3.9.7"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
-  integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
+typescript@^4.1.3:
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72"
+  integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==
 
 union-value@^1.0.0:
   version "1.0.1"
@@ -2685,6 +2749,11 @@ yallist@^3.0.2:
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
   integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
 
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
 yargs-parser@^13.1.2:
   version "13.1.2"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"

+ 96 - 17
src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/yarn.lock

@@ -195,6 +195,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   dependencies:
     color-convert "^1.9.0"
 
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
 anymatch@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -470,7 +477,7 @@ camelcase@^5.0.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
   integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
 
[email protected], chalk@^2.3.0:
[email protected]:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -479,6 +486,14 @@ [email protected], chalk@^2.3.0:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
+chalk@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
+  integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
 chokidar@^2.0.2:
   version "2.1.8"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@@ -552,11 +567,23 @@ color-convert@^1.9.0:
   dependencies:
     color-name "1.1.3"
 
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
 [email protected]:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
   integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
 
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
 commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -792,6 +819,11 @@ emojis-list@^2.0.0:
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
   integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
 
+emojis-list@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
+  integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
+
 end-of-stream@^1.0.0, end-of-stream@^1.1.0:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -1121,6 +1153,11 @@ has-flag@^3.0.0:
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
   integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
 
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
 has-value@^0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
@@ -1422,6 +1459,13 @@ json5@^1.0.1:
   dependencies:
     minimist "^1.2.0"
 
+json5@^2.1.2:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
+  integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
+  dependencies:
+    minimist "^1.2.5"
+
 kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -1458,7 +1502,7 @@ loader-runner@^2.4.0:
   resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
   integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
 
[email protected], loader-utils@^1.0.2, loader-utils@^1.2.3:
[email protected], loader-utils@^1.2.3:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
   integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
@@ -1467,6 +1511,15 @@ [email protected], loader-utils@^1.0.2, loader-utils@^1.2.3:
     emojis-list "^2.0.0"
     json5 "^1.0.1"
 
+loader-utils@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
+  integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
+  dependencies:
+    big.js "^5.2.2"
+    emojis-list "^3.0.0"
+    json5 "^2.1.2"
+
 locate-path@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@@ -1482,6 +1535,13 @@ lru-cache@^5.1.1:
   dependencies:
     yallist "^3.0.2"
 
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
 make-dir@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
@@ -1615,6 +1675,11 @@ minimist@^1.2.0:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
   integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
 
+minimist@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+  integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
 mississippi@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
@@ -2165,10 +2230,12 @@ semver@^5.5.0, semver@^5.6.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
 
-semver@^6.0.0:
-  version "6.3.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
-  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+semver@^7.3.4:
+  version "7.3.4"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
+  integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
+  dependencies:
+    lru-cache "^6.0.0"
 
 serialize-javascript@^2.1.2:
   version "2.1.2"
@@ -2392,6 +2459,13 @@ supports-color@^5.3.0:
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
 tapable@^1.0.0, tapable@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
@@ -2473,16 +2547,16 @@ to-regex@^3.0.1, to-regex@^3.0.2:
     regex-not "^1.0.2"
     safe-regex "^1.1.0"
 
-ts-loader@^6.2.1:
-  version "6.2.1"
-  resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.1.tgz#67939d5772e8a8c6bdaf6277ca023a4812da02ef"
-  integrity sha512-Dd9FekWuABGgjE1g0TlQJ+4dFUfYGbYcs52/HQObE0ZmUNjQlmLAS7xXsSzy23AMaMwipsx5sNHvoEpT2CZq1g==
+ts-loader@^8.0.14:
+  version "8.0.17"
+  resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.17.tgz#98f2ccff9130074f4079fd89b946b4c637b1f2fc"
+  integrity sha512-OeVfSshx6ot/TCxRwpBHQ/4lRzfgyTkvi7ghDVrLXOHzTbSK413ROgu/xNqM72i3AFeAIJgQy78FwSMKmOW68w==
   dependencies:
-    chalk "^2.3.0"
+    chalk "^4.1.0"
     enhanced-resolve "^4.0.0"
-    loader-utils "^1.0.2"
+    loader-utils "^2.0.0"
     micromatch "^4.0.0"
-    semver "^6.0.0"
+    semver "^7.3.4"
 
 tslib@^1.9.0:
   version "1.10.0"
@@ -2499,10 +2573,10 @@ typedarray@^0.0.6:
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
-typescript@^3.7.5:
-  version "3.7.5"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
-  integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
+typescript@^4.1.3:
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72"
+  integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==
 
 union-value@^1.0.0:
   version "1.0.1"
@@ -2711,6 +2785,11 @@ yallist@^3.0.2:
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
   integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
 
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
 yargs-parser@^13.1.0:
   version "13.1.1"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"

+ 3 - 2
src/Components/WebAssembly/WebAssembly.Authentication/src/RemoteAuthenticationBuilderExtensions.cs

@@ -1,6 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Diagnostics.CodeAnalysis;
 using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 
@@ -19,7 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparam name="TAccountClaimsPrincipalFactory">The new user factory type.</typeparam>
         /// <param name="builder">The <see cref="IRemoteAuthenticationBuilder{TRemoteAuthenticationState, TAccount}"/>.</param>
         /// <returns>The <see cref="IRemoteAuthenticationBuilder{TRemoteAuthenticationState, TAccount}"/>.</returns>
-        public static IRemoteAuthenticationBuilder<TRemoteAuthenticationState, TAccount> AddAccountClaimsPrincipalFactory<TRemoteAuthenticationState, TAccount, TAccountClaimsPrincipalFactory>(
+        public static IRemoteAuthenticationBuilder<TRemoteAuthenticationState, TAccount> AddAccountClaimsPrincipalFactory<TRemoteAuthenticationState, TAccount, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TAccountClaimsPrincipalFactory>(
             this IRemoteAuthenticationBuilder<TRemoteAuthenticationState, TAccount> builder)
             where TRemoteAuthenticationState : RemoteAuthenticationState, new()
             where TAccount : RemoteUserAccount
@@ -37,7 +38,7 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparam name="TAccountClaimsPrincipalFactory">The new user factory type.</typeparam>
         /// <param name="builder">The <see cref="IRemoteAuthenticationBuilder{TRemoteAuthenticationState, Account}"/>.</param>
         /// <returns>The <see cref="IRemoteAuthenticationBuilder{TRemoteAuthenticationState, Account}"/>.</returns>
-        public static IRemoteAuthenticationBuilder<TRemoteAuthenticationState, RemoteUserAccount> AddAccountClaimsPrincipalFactory<TRemoteAuthenticationState, TAccountClaimsPrincipalFactory>(
+        public static IRemoteAuthenticationBuilder<TRemoteAuthenticationState, RemoteUserAccount> AddAccountClaimsPrincipalFactory<TRemoteAuthenticationState, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TAccountClaimsPrincipalFactory>(
             this IRemoteAuthenticationBuilder<TRemoteAuthenticationState, RemoteUserAccount> builder)
             where TRemoteAuthenticationState : RemoteAuthenticationState, new()
             where TAccountClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount> => builder.AddAccountClaimsPrincipalFactory<TRemoteAuthenticationState, RemoteUserAccount, TAccountClaimsPrincipalFactory>();

+ 2 - 1
src/Components/WebAssembly/WebAssembly/src/Hosting/EntrypointInvoker.cs

@@ -2,7 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
-using System.Globalization;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
 using System.Threading.Tasks;
@@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
             }
         }
 
+        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075", Justification = "MainAsync does not get linked.")]
         private static MethodBase FindUnderlyingEntrypoint(Assembly assembly)
         {
             // This is the entrypoint declared in .NET metadata. In the case of async main, it's the

+ 5 - 4
src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMapping.cs

@@ -2,8 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
-using System.Collections;
-using Microsoft.AspNetCore.Components;
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
 {
@@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
         /// </summary>
         /// <param name="componentType">The component type. Must implement <see cref="IComponent"/>.</param>
         /// <param name="selector">The DOM element selector or component registration id for the component.</param>
-        public RootComponentMapping(Type componentType, string selector)
+        public RootComponentMapping([DynamicallyAccessedMembers(Component)] Type componentType, string selector)
         {
             if (componentType is null)
             {
@@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
         /// <param name="componentType">The component type. Must implement <see cref="IComponent"/>.</param>
         /// <param name="selector">The DOM element selector or registration id for the component.</param>
         /// <param name="parameters">The parameters to pass to the component.</param>
-        public RootComponentMapping(Type componentType, string selector, ParameterView parameters) : this(componentType, selector)
+        public RootComponentMapping([DynamicallyAccessedMembers(Component)] Type componentType, string selector, ParameterView parameters) : this(componentType, selector)
         {
             Parameters = parameters;
         }
@@ -57,6 +57,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
         /// <summary>
         /// Gets the component type.
         /// </summary>
+        [DynamicallyAccessedMembers(Component)]
         public Type ComponentType { get; }
 
         /// <summary>

+ 5 - 4
src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMappingCollection.cs

@@ -4,7 +4,8 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
-using Microsoft.AspNetCore.Components;
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
 {
@@ -18,7 +19,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
         /// </summary>
         /// <typeparam name="TComponent">The component type.</typeparam>
         /// <param name="selector">The DOM element selector.</param>
-        public void Add<TComponent>(string selector) where TComponent : IComponent
+        public void Add<[DynamicallyAccessedMembers(Component)] TComponent>(string selector) where TComponent : IComponent
         {
             if (selector is null)
             {
@@ -33,7 +34,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
         /// </summary>
         /// <param name="componentType">The component type. Must implement <see cref="IComponent"/>.</param>
         /// <param name="selector">The DOM element selector.</param>
-        public void Add(Type componentType, string selector)
+        public void Add([DynamicallyAccessedMembers(Component)] Type componentType, string selector)
         {
             Add(componentType, selector, ParameterView.Empty);
         }
@@ -44,7 +45,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
         /// <param name="componentType">The component type. Must implement <see cref="IComponent"/>.</param>
         /// <param name="selector">The DOM element selector.</param>
         /// <param name="parameters">The parameters to the root component.</param>
-        public void Add(Type componentType, string selector, ParameterView parameters)
+        public void Add([DynamicallyAccessedMembers(Component)] Type componentType, string selector, ParameterView parameters)
         {
             if (componentType is null)
             {

+ 2 - 0
src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.IO;
 using System.Runtime.Loader;
@@ -12,6 +13,7 @@ using Microsoft.JSInterop;
 
 namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
 {
+    [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "This type loads resx files. We don't expect it's dependencies to be trimmed in the ordinary case.")]
     internal class WebAssemblyCultureProvider
     {
         internal const string GetSatelliteAssemblies = "window.Blazor._internal.getSatelliteAssemblies";

+ 7 - 0
src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs

@@ -2,18 +2,22 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.IO;
 using System.Linq;
 using Microsoft.AspNetCore.Components.Lifetime;
+using Microsoft.AspNetCore.Components.RenderTree;
 using Microsoft.AspNetCore.Components.Routing;
 using Microsoft.AspNetCore.Components.Web;
+using Microsoft.AspNetCore.Components.WebAssembly.Infrastructure;
 using Microsoft.AspNetCore.Components.WebAssembly.Services;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration.Json;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.JSInterop;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
 {
@@ -32,6 +36,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
         /// </summary>
         /// <param name="args">The argument passed to the application's main method.</param>
         /// <returns>A <see cref="WebAssemblyHostBuilder"/>.</returns>
+        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(EntrypointInvoker))]
+        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(JSInteropMethods))]
+        [DynamicDependency(JsonSerialized, typeof(WebEventDescriptor))]
         public static WebAssemblyHostBuilder CreateDefault(string[]? args = default)
         {
             // We don't use the args for anything right now, but we want to accept them

+ 47 - 0
src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.WarningSuppressions.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<linker>
+  <assembly fullname="Microsoft.AspNetCore.Components.WebAssembly, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60">
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2026</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.ComponentParametersTypeCache.GetParameterType(System.String,System.String)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2026</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.RootComponentTypeCache.ResolveType(Microsoft.AspNetCore.Components.RootComponentTypeCache.Key,System.Reflection.Assembly[])</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2026</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.InitializeRegisteredRootComponents(Microsoft.JSInterop.IJSUnmarshalledRuntime)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2026</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader.&lt;LoadAssembliesInClientAsync&gt;d__8.MoveNext</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.Web.WebEventData.ParseEventArgsJson(Microsoft.AspNetCore.Components.RenderTree.Renderer,System.UInt64,System.String,System.String)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.InitializeRegisteredRootComponents(Microsoft.JSInterop.IJSUnmarshalledRuntime)</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2072</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.AspNetCore.Components.WebAssemblyComponentParameterDeserializer.DeserializeParameters(System.Collections.Generic.IList{Microsoft.AspNetCore.Components.ComponentParameter},System.Collections.Generic.IList{System.Object})</property>
+    </attribute>
+  </assembly>
+</linker>

+ 2 - 1
src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -36,6 +36,7 @@
     <Compile Include="$(SharedSourceRoot)Components\WebAssemblyComponentMarker.cs" Link="Prerendering/WebAssemblyComponentMarker.cs" />
     <Compile Include="$(SharedSourceRoot)Components\ComponentParameter.cs" Link="Prerendering/ComponentParameter.cs" />
     <Compile Include="$(SharedSourceRoot)Components\PrerenderComponentApplicationStore.cs" />
+    <Compile Include="$(SharedSourceRoot)LinkerFlags.cs" LinkBase="Shared" />
   </ItemGroup>
 
   <ItemGroup>

+ 2 - 3
src/Components/WebAssembly/WebAssembly/src/Prerendering/ClientComponentParameterDeserializer.cs

@@ -3,10 +3,8 @@
 
 using System;
 using System.Collections.Generic;
-using System.Text;
+using System.Diagnostics.CodeAnalysis;
 using System.Text.Json;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
 
 namespace Microsoft.AspNetCore.Components
 {
@@ -80,6 +78,7 @@ namespace Microsoft.AspNetCore.Components
             return JsonSerializer.Deserialize<ComponentParameter[]>(parametersDefinitions, WebAssemblyComponentSerializationSettings.JsonSerializationOptions)!;
         }
 
+        [RequiresUnreferencedCode("This API attempts to JSON deserialize types which might be trimmed.")]
         public IList<object> GetParameterValues(string parameterValues)
         {
             return JsonSerializer.Deserialize<IList<object>>(parameterValues, WebAssemblyComponentSerializationSettings.JsonSerializationOptions)!;

+ 4 - 4
src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs

@@ -3,12 +3,12 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Components.RenderTree;
 using Microsoft.AspNetCore.Components.WebAssembly.Services;
 using Microsoft.Extensions.Logging;
-using Microsoft.JSInterop;
-using Microsoft.JSInterop.WebAssembly;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
 {
@@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
         /// Callers of this method may choose to ignore the returned <see cref="Task"/> if they do not
         /// want to await the rendering of the added component.
         /// </remarks>
-        public Task AddComponentAsync<TComponent>(string domElementSelector, ParameterView parameters) where TComponent : IComponent
+        public Task AddComponentAsync<[DynamicallyAccessedMembers(Component)] TComponent>(string domElementSelector, ParameterView parameters) where TComponent : IComponent
             => AddComponentAsync(typeof(TComponent), domElementSelector, parameters);
 
         /// <summary>
@@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
         /// Callers of this method may choose to ignore the returned <see cref="Task"/> if they do not
         /// want to await the rendering of the added component.
         /// </remarks>
-        public Task AddComponentAsync(Type componentType, string domElementSelector, ParameterView parameters)
+        public Task AddComponentAsync([DynamicallyAccessedMembers(Component)] Type componentType, string domElementSelector, ParameterView parameters)
         {
             var component = InstantiateComponent(componentType);
             var componentId = AssignRootComponentId(component);

+ 8 - 5
src/Components/WebAssembly/WebAssembly/src/Services/DefaultWebAssemblyJSRuntime.cs

@@ -1,6 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using Microsoft.JSInterop.Infrastructure;
 using Microsoft.JSInterop.WebAssembly;
@@ -13,25 +14,28 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
 
         public ElementReferenceContext ElementReferenceContext { get; }
 
+        [DynamicDependency(nameof(InvokeDotNet))]
+        [DynamicDependency(nameof(EndInvokeJS))]
+        [DynamicDependency(nameof(BeginInvokeDotNet))]
         private DefaultWebAssemblyJSRuntime()
         {
             ElementReferenceContext = new WebElementReferenceContext(this);
             JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
         }
 
-        #pragma warning disable IDE0051 // Remove unused private members. Invoked via Mono's JS interop mechanism (invoke_method)
-        private static string? InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson)
+        // The following methods are invoke via Mono's JS interop mechanism (invoke_method)
+        public static string? InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson)
         {
             var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId, CultureInfo.InvariantCulture), callId: null);
             return DotNetDispatcher.Invoke(Instance, callInfo, argsJson);
         }
 
         // Invoked via Mono's JS interop mechanism (invoke_method)
-        private static void EndInvokeJS(string argsJson)
+        public static void EndInvokeJS(string argsJson)
             => DotNetDispatcher.EndInvokeJS(Instance, argsJson);
 
         // Invoked via Mono's JS interop mechanism (invoke_method)
-        private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson)
+        public static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson)
         {
             // Figure out whether 'assemblyNameOrDotNetObjectId' is the assembly name or the instance ID
             // We only need one for any given call. This helps to work around the limitation that we can
@@ -52,6 +56,5 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
             var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId);
             DotNetDispatcher.BeginInvokeDotNet(Instance, callInfo, argsJson);
         }
-        #pragma warning restore IDE0051
     }
 }

+ 12 - 19
src/Components/WebAssembly/WebAssembly/src/Services/LazyAssemblyLoader.cs

@@ -3,13 +3,13 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Runtime.Loader;
 using System.Threading.Tasks;
 using Microsoft.JSInterop;
-using Microsoft.JSInterop.WebAssembly;
 
 namespace Microsoft.AspNetCore.Components.WebAssembly.Services
 {
@@ -45,14 +45,15 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
         /// </summary>
         /// <param name="assembliesToLoad">The names of the assemblies to load (e.g. "MyAssembly.dll")</param>
         /// <returns>A list of the loaded <see cref="Assembly"/></returns>
-        public async Task<IEnumerable<Assembly>> LoadAssembliesAsync(IEnumerable<string> assembliesToLoad)
+        [RequiresUnreferencedCode("Types and members the loaded assemblies depend on might be removed")]
+        public Task<IEnumerable<Assembly>> LoadAssembliesAsync(IEnumerable<string> assembliesToLoad)
         {
             if (OperatingSystem.IsBrowser())
             {
-                return await LoadAssembliesInClientAsync(assembliesToLoad);
+                return LoadAssembliesInClientAsync(assembliesToLoad);
             }
 
-            return await LoadAssembliesInServerAsync(assembliesToLoad);
+            return LoadAssembliesInServerAsync(assembliesToLoad);
         }
 
         private Task<IEnumerable<Assembly>> LoadAssembliesInServerAsync(IEnumerable<string> assembliesToLoad)
@@ -74,6 +75,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
             return Task.FromResult<IEnumerable<Assembly>>(loadedAssemblies);
         }
 
+        [RequiresUnreferencedCode("Types and members the loaded assemblies depend on might be removed")]
         private async Task<IEnumerable<Assembly>> LoadAssembliesInClientAsync(IEnumerable<string> assembliesToLoad)
         {
             // Check to see if the assembly has already been loaded and avoids reloading it if so.
@@ -84,28 +86,19 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
             var newAssembliesToLoad = assembliesToLoad.Where(assembly => !_loadedAssemblyCache.Contains(assembly));
             var loadedAssemblies = new List<Assembly>();
 
-            var count = (int)await ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<string[], object?, object?, Task<object>>(
+            var jsRuntime = (IJSUnmarshalledRuntime)_jsRuntime;
+
+            var count = (int)await jsRuntime.InvokeUnmarshalled<string[], Task<object>>(
                GetLazyAssemblies,
-               newAssembliesToLoad.ToArray(),
-               null,
-               null);
+               newAssembliesToLoad.ToArray());
 
             if (count == 0)
             {
                 return loadedAssemblies;
             }
 
-            var assemblies = ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<object?, object?, object?, byte[][]>(
-                ReadLazyAssemblies,
-                null,
-                null,
-                null);
-
-            var pdbs = ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<object?, object?, object?, byte[][]>(
-                ReadLazyPDBs,
-                null,
-                null,
-                null);
+            var assemblies = jsRuntime.InvokeUnmarshalled<byte[][]>(ReadLazyAssemblies);
+            var pdbs = jsRuntime.InvokeUnmarshalled<byte[][]>(ReadLazyPDBs);
 
             for (int i = 0; i < assemblies.Length; i++)
             {

+ 20 - 0
src/Components/WebAssembly/testassets/WasmLinkerTest/README.md

@@ -0,0 +1,20 @@
+## Trimmer baseline verification
+
+This project is used to verify that Blazor WASM APIs do not result in additional trimmer warnings other than the ones that are already known. It works by running the trimmer in "library mode", rooting all of it's public APIs, using a set of baselined suppressions and ensuring no new trimmer warnings are produced.
+
+### Updating the baselines
+
+If a suppressed warning has been resolved, or if new trimmer warnings are to be baselined, run the following command:
+
+```
+dotnet build /p:GenerateLinkerWarningSuppressions=true
+```
+
+This should update the WarningSuppressions.xml files associated with projects.
+
+⚠️ Note that the generated file sometimes messing up formatting for some compiler generated nested types and you may need to manually touch up these files on regenerating. The generated file uses braces `{...}` instead of angle brackets `<...>`:
+
+```diff
+- LegacyRouteTableFactory.&lt;&gt;c.{Create}b__2_1(System.Reflection.Assembly)
++ LegacyRouteTableFactory.&lt;&gt;c.&lt;Create&gt;b__2_1(System.Reflection.Assembly)
+```

+ 80 - 0
src/Components/WebAssembly/testassets/WasmLinkerTest/WasmLinkerTest.csproj

@@ -0,0 +1,80 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+    <RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
+    <SelfContained>true</SelfContained>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.AspNetCore.Metadata" />
+    <Reference Include="Microsoft.AspNetCore.Authorization" />
+    <Reference Include="Microsoft.AspNetCore.Components.Authorization" />
+    <Reference Include="Microsoft.AspNetCore.Components" />
+    <Reference Include="Microsoft.AspNetCore.Components.Forms" />
+    <Reference Include="Microsoft.AspNetCore.Components.Web" />
+    <Reference Include="Microsoft.AspNetCore.Components.WebAssembly" />
+    <Reference Include="Microsoft.JSInterop" />
+    <Reference Include="Microsoft.JSInterop.WebAssembly" />
+  </ItemGroup>
+  <Target Name="ILLinkTrimWasmProjects" DependsOnTargets="ResolveReferences" AfterTargets="Build" Condition="'$(DotNetBuildFromSource)' != 'true' AND '$(SkipTestBuild)' != 'true'">
+    <PropertyGroup>
+      <LibrariesTrimmedArtifactsPath>$(TargetDir)wasm-linked\</LibrariesTrimmedArtifactsPath>
+      <!-- Link all assemblies -->
+      <ILLinkArgs>$(ILLinkArgs) -c link</ILLinkArgs>
+      <ILLinkArgs>$(ILLinkArgs) -u link</ILLinkArgs>
+      <ILLinkArgs>$(ILLinkArgs) -b true</ILLinkArgs>
+      <ILLinkArgs Condition="'$(GenerateLinkerWarningSuppressions)' == 'true'">$(ILLinkArgs) --generate-warning-suppressions xml</ILLinkArgs>
+    </PropertyGroup>
+    <ItemGroup>
+      <RootAssemblies Include="@(ReferencePath->HasMetadata('ProjectPath'))" RootMode="visible" />
+      <_ILLinkSuppressionFile Include="$([System.IO.Path]::GetDirectoryName($([System.String]::new('%(RootAssemblies.ProjectPath)'))))\%(FileName).WarningSuppressions.xml"
+        SuppressionFileName="%(FileName).WarningSuppressions" />
+      <ILLinkSuppressionFile Condition="Exists(%(_ILLinkSuppressionFile.Identity))" Include="%(_ILLinkSuppressionFile.Identity)" />
+    </ItemGroup>
+
+    <PropertyGroup>
+      <ILLinkArgs Condition="'$(GenerateLinkerWarningSuppressions)' != 'true' AND @(ILLinkSuppressionFile->Count()) != 0">$(ILLinkArgs) --link-attributes @(ILLinkSuppressionFile->'%(FullPath)', ' --link-attributes ')</ILLinkArgs>
+    </PropertyGroup>
+
+    <!--
+        When running from Desktop MSBuild, DOTNET_HOST_PATH is not set.
+        In this case, explicitly specify the path to the dotnet host.
+        -->
+    <PropertyGroup Condition=" '$(DOTNET_HOST_PATH)' == '' ">
+      <!-- This is defined when building in Visual Studio. -->
+      <_DotNetHostDirectory>$(NetCoreRoot)</_DotNetHostDirectory>
+      <_DotNetHostFileName>$([System.IO.Path]::GetFileName('$(DotNetTool)'))</_DotNetHostFileName>
+    </PropertyGroup>
+    <ILLink
+        AssemblyPaths=""
+        RootAssemblyNames="@(RootAssemblies)"
+        OutputDirectory="$(LibrariesTrimmedArtifactsPath)"
+        ReferenceAssemblyPaths="@(RuntimePackAsset);@(ReferencePath->WithMetadataValue('ExternallyResolved', 'true'))"
+        ExtraArgs="$(ILLinkArgs)"
+        ToolExe="$(_DotNetHostFileName)"
+        ToolPath="$(_DotNetHostDirectory)" />
+
+      <ItemGroup Condition="'$(GenerateLinkerWarningSuppressions)' == 'true'">
+        <_UpdatedILLinkSuppressionFile Include="$(LibrariesTrimmedArtifactsPath)\*.WarningSuppressions.xml" />
+        <_UpdatedILLinkSuppressionFile SourcePath="%(FullPath)" />
+      </ItemGroup>
+
+       <JoinItems
+         Left="@(_ILLinkSuppressionFile)"
+         Right="@(_UpdatedILLinkSuppressionFile)"
+         LeftMetadata="*"
+         RightMetadata="SourcePath"
+         LeftKey="SuppressionFileName"
+         RightKey="FileName"
+         ItemSpecToUse="Left">
+
+        <Output TaskParameter="JoinResult" ItemName="_ILLinkFileToUpdate" />
+       </JoinItems>
+
+       <Copy
+        SourceFiles="%(_ILLinkFileToUpdate.SourcePath)"
+        DestinationFiles="%(_ILLinkFileToUpdate.FullPath)"
+        OverwriteReadOnlyFiles="true"
+        Condition="'$(GenerateLinkerWarningSuppressions)' == 'true'" />
+
+  </Target>
+</Project>

+ 3 - 1
src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs

@@ -2,6 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.JSInterop
 {
@@ -17,6 +19,6 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        TValue Invoke<TValue>(string identifier, params object?[]? args);
+        TValue Invoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, params object?[]? args);
     }
 }

+ 4 - 1
src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs

@@ -1,6 +1,9 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
+
 namespace Microsoft.JSInterop
 {
     /// <summary>
@@ -15,6 +18,6 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>window.someScope.someFunction</c>.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TResult"/> obtained by JSON-deserializing the return value.</returns>
-        TResult Invoke<TResult>(string identifier, params object?[]? args);
+        TResult Invoke<[DynamicallyAccessedMembers(JsonSerialized)] TResult>(string identifier, params object?[]? args);
     }
 }

+ 4 - 2
src/JSInterop/Microsoft.JSInterop/src/IJSObjectReference.cs

@@ -2,8 +2,10 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Threading;
 using System.Threading.Tasks;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.JSInterop
 {
@@ -23,7 +25,7 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args);
+        ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, object?[]? args);
 
         /// <summary>
         /// Invokes the specified JavaScript function asynchronously.
@@ -36,6 +38,6 @@ namespace Microsoft.JSInterop
         /// </param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args);
+        ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, CancellationToken cancellationToken, object?[]? args);
     }
 }

+ 4 - 2
src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs

@@ -1,8 +1,10 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Diagnostics.CodeAnalysis;
 using System.Threading;
 using System.Threading.Tasks;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.JSInterop
 {
@@ -22,7 +24,7 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>window.someScope.someFunction</c>.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args);
+        ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, object?[]? args);
 
         /// <summary>
         /// Invokes the specified JavaScript function asynchronously.
@@ -35,6 +37,6 @@ namespace Microsoft.JSInterop
         /// </param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args);
+        ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, CancellationToken cancellationToken, object?[]? args);
     }
 }

+ 4 - 1
src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs

@@ -1,6 +1,9 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Diagnostics.CodeAnalysis;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
+
 namespace Microsoft.JSInterop.Implementation
 {
     /// <summary>
@@ -21,7 +24,7 @@ namespace Microsoft.JSInterop.Implementation
         }
 
         /// <inheritdoc />
-        public TValue Invoke<TValue>(string identifier, params object?[]? args)
+        public TValue Invoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, params object?[]? args)
         {
             ThrowIfDisposed();
 

+ 4 - 2
src/JSInterop/Microsoft.JSInterop/src/Implementation/JSObjectReference.cs

@@ -2,8 +2,10 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Threading;
 using System.Threading.Tasks;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.JSInterop.Implementation
 {
@@ -34,7 +36,7 @@ namespace Microsoft.JSInterop.Implementation
         }
 
         /// <inheritdoc />
-        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
+        public ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, object?[]? args)
         {
             ThrowIfDisposed();
 
@@ -42,7 +44,7 @@ namespace Microsoft.JSInterop.Implementation
         }
 
         /// <inheritdoc />
-        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
+        public ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
         {
             ThrowIfDisposed();
 

+ 6 - 4
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
 using System.Runtime.ExceptionServices;
@@ -16,16 +17,15 @@ namespace Microsoft.JSInterop.Infrastructure
     /// <summary>
     /// Provides methods that receive incoming calls from JS to .NET.
     /// </summary>
+    [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";
         internal static readonly JsonEncodedText DotNetObjectRefKey = JsonEncodedText.Encode("__dotNetObject");
 
-        private static readonly ConcurrentDictionary<AssemblyKey, IReadOnlyDictionary<string, (MethodInfo, Type[])>> _cachedMethodsByAssembly
-            = new ConcurrentDictionary<AssemblyKey, IReadOnlyDictionary<string, (MethodInfo, Type[])>>();
+        private static readonly ConcurrentDictionary<AssemblyKey, IReadOnlyDictionary<string, (MethodInfo, Type[])>> _cachedMethodsByAssembly = new();
 
-        private static readonly ConcurrentDictionary<Type, IReadOnlyDictionary<string, (MethodInfo, Type[])>> _cachedMethodsByType
-            = new ConcurrentDictionary<Type, IReadOnlyDictionary<string, (MethodInfo, Type[])>>();
+        private static readonly ConcurrentDictionary<Type, IReadOnlyDictionary<string, (MethodInfo, Type[])>> _cachedMethodsByType = new();
 
         /// <summary>
         /// Receives a call from JS to .NET, locating and invoking the specified method.
@@ -349,6 +349,8 @@ namespace Microsoft.JSInterop.Infrastructure
             }
         }
 
+        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "We expect application code is configured to ensure JSInvokable methods are retained. https://github.com/dotnet/aspnetcore/issues/29946")]
+        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072", Justification = "We expect application code is configured to ensure JSInvokable methods are retained. https://github.com/dotnet/aspnetcore/issues/29946")]
         private static Dictionary<string, (MethodInfo, Type[])> ScanAssemblyForCallableMethods(AssemblyKey assemblyKey)
         {
             // TODO: Consider looking first for assembly-level attributes (i.e., if there are any,

+ 4 - 2
src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs

@@ -1,7 +1,9 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Diagnostics.CodeAnalysis;
 using System.Text.Json;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.JSInterop
 {
@@ -10,7 +12,7 @@ namespace Microsoft.JSInterop
     /// </summary>
     public abstract class JSInProcessRuntime : JSRuntime, IJSInProcessRuntime
     {
-        internal TValue Invoke<TValue>(string identifier, long targetInstanceId, params object?[]? args)
+        internal TValue Invoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, long targetInstanceId, params object?[]? args)
         {
             var resultJson = InvokeJS(
                 identifier,
@@ -36,7 +38,7 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>window.someScope.someFunction</c>.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public TValue Invoke<TValue>(string identifier, params object?[]? args)
+        public TValue Invoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, params object?[]? args)
             => Invoke<TValue>(identifier, 0, args);
 
         /// <summary>

+ 5 - 3
src/JSInterop/Microsoft.JSInterop/src/JSObjectReferenceExtensions.cs

@@ -2,8 +2,10 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Threading;
 using System.Threading.Tasks;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.JSInterop
 {
@@ -41,7 +43,7 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public static ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, params object?[] args)
+        public static ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSObjectReference jsObjectReference, string identifier, params object?[] args)
         {
             if (jsObjectReference is null)
             {
@@ -63,7 +65,7 @@ namespace Microsoft.JSInterop
         /// </param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public static ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, CancellationToken cancellationToken, params object?[] args)
+        public static ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSObjectReference jsObjectReference, string identifier, CancellationToken cancellationToken, params object?[] args)
         {
             if (jsObjectReference is null)
             {
@@ -102,7 +104,7 @@ namespace Microsoft.JSInterop
         /// <param name="timeout">The duration after which to cancel the async operation. Overrides default timeouts (<see cref="JSRuntime.DefaultAsyncTimeout"/>).</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
-        public static async ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, TimeSpan timeout, params object?[] args)
+        public static async ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSObjectReference jsObjectReference, string identifier, TimeSpan timeout, params object?[] args)
         {
             if (jsObjectReference is null)
             {

+ 7 - 4
src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs

@@ -4,10 +4,12 @@
 using System;
 using System.Collections.Concurrent;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.JSInterop.Infrastructure;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.JSInterop
 {
@@ -62,7 +64,7 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>window.someScope.someFunction</c>.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
+        public ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, object?[]? args)
             => InvokeAsync<TValue>(0, identifier, args);
 
         /// <summary>
@@ -76,10 +78,10 @@ namespace Microsoft.JSInterop
         /// </param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
+        public ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
             => InvokeAsync<TValue>(0, identifier, cancellationToken, args);
 
-        internal async ValueTask<TValue> InvokeAsync<TValue>(long targetInstanceId, string identifier, object?[]? args)
+        internal async ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(long targetInstanceId, string identifier, object?[]? args)
         {
             if (DefaultAsyncTimeout.HasValue)
             {
@@ -91,7 +93,7 @@ namespace Microsoft.JSInterop
             return await InvokeAsync<TValue>(targetInstanceId, identifier, CancellationToken.None, args);
         }
 
-        internal ValueTask<TValue> InvokeAsync<TValue>(
+        internal ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(
             long targetInstanceId,
             string identifier,
             CancellationToken cancellationToken,
@@ -172,6 +174,7 @@ namespace Microsoft.JSInterop
             DotNetInvocationInfo invocationInfo,
             in DotNetInvocationResult invocationResult);
 
+        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:RequiresUnreferencedCode", Justification = "We enforce trimmer attributes for JSON deserialized types on InvokeAsync.")]
         internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonReader)
         {
             if (!_pendingTasks.TryRemove(taskId, out var tcs))

+ 5 - 4
src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs

@@ -2,8 +2,10 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Threading;
 using System.Threading.Tasks;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
 namespace Microsoft.JSInterop
 {
@@ -41,7 +43,7 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>window.someScope.someFunction</c>.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public static ValueTask<TValue> InvokeAsync<TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args)
+        public static ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args)
         {
             if (jsRuntime is null)
             {
@@ -63,7 +65,7 @@ namespace Microsoft.JSInterop
         /// </param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public static ValueTask<TValue> InvokeAsync<TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args)
+        public static ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args)
         {
             if (jsRuntime is null)
             {
@@ -102,14 +104,13 @@ namespace Microsoft.JSInterop
         /// <param name="timeout">The duration after which to cancel the async operation. Overrides default timeouts (<see cref="JSRuntime.DefaultAsyncTimeout"/>).</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
-        public static async ValueTask<TValue> InvokeAsync<TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args)
+        public static async ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args)
         {
             if (jsRuntime is null)
             {
                 throw new ArgumentNullException(nameof(jsRuntime));
             }
 
-
             using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout);
             var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None;
 

+ 29 - 0
src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<linker>
+  <assembly fullname="Microsoft.JSInterop, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2062</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.JSInterop.Infrastructure.DotNetDispatcher.ParseArguments(Microsoft.JSInterop.JSRuntime,System.String,System.String,System.Type[])</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2091</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.JSInterop.JSObjectReferenceExtensions.&lt;InvokeAsync&gt;d__4`1.MoveNext</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2091</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.JSInterop.JSRuntime.&lt;InvokeAsync&gt;d__15`1.MoveNext</property>
+    </attribute>
+    <attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
+      <argument>ILLink</argument>
+      <argument>IL2091</argument>
+      <property name="Scope">member</property>
+      <property name="Target">M:Microsoft.JSInterop.JSRuntimeExtensions.&lt;InvokeAsync&gt;d__4`1.MoveNext</property>
+    </attribute>
+  </assembly>
+</linker>

+ 1 - 4
src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj

@@ -9,12 +9,9 @@
     <Nullable>enable</Nullable>
   </PropertyGroup>
 
-  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
-    <Reference Include="System.Text.Json" />
-  </ItemGroup>
-
   <ItemGroup>
     <Compile Include="$(SharedSourceRoot)JSInterop\JSCallResultTypeHelper.cs" LinkBase="Shared" />
+    <Compile Include="$(SharedSourceRoot)LinkerFlags.cs" LinkBase="Shared" />
   </ItemGroup>
 
   <ItemGroup>

+ 20 - 0
src/Shared/LinkerFlags.cs

@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.AspNetCore.Internal
+{
+    internal static class LinkerFlags
+    {
+        /// <summary>
+        /// Flags for a member that is JSON (de)serialized.
+        /// </summary>
+        public const DynamicallyAccessedMemberTypes JsonSerialized = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties;
+
+        /// <summary>
+        /// Flags for a component
+        /// </summary>
+        public const DynamicallyAccessedMemberTypes Component = DynamicallyAccessedMemberTypes.All;
+    }
+}