Skip to content

Commit bf51aa5

Browse files
authored
Prevent LayoutComponentBase properties from being trimmed. (#30606)
The usage pattern for LayoutComponentBase does not really play well with static analysis and results in the property setter from being removed.
1 parent 201a0fa commit bf51aa5

File tree

3 files changed

+35
-27
lines changed

3 files changed

+35
-27
lines changed

src/Components/Components/src/LayoutComponentBase.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Threading.Tasks;
6+
using static Microsoft.AspNetCore.Internal.LinkerFlags;
7+
48
namespace Microsoft.AspNetCore.Components
59
{
610
/// <summary>
@@ -17,5 +21,12 @@ public abstract class LayoutComponentBase : ComponentBase
1721
/// </summary>
1822
[Parameter]
1923
public RenderFragment? Body { get; set; }
24+
25+
/// <inheritdoc />
26+
// Derived instances of LayoutComponentBase do not appear in any statically analyzable
27+
// calls of OpenComponent<T> where T is well-known. Consequently we have to explicitly provide a hint to the trimmer to preserve
28+
// properties.
29+
[DynamicDependency(Component, typeof(LayoutComponentBase))]
30+
public override Task SetParametersAsync(ParameterView parameters) => base.SetParametersAsync(parameters);
2031
}
2132
}

src/Components/Components/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute
3131
Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute.CascadingTypeParameterAttribute(string! name) -> void
3232
Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute.Name.get -> string!
3333
Microsoft.AspNetCore.Components.RenderTree.Renderer.GetEventArgsType(ulong eventHandlerId) -> System.Type!
34+
override Microsoft.AspNetCore.Components.LayoutComponentBase.SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) -> System.Threading.Tasks.Task!
3435
static Microsoft.AspNetCore.Components.ParameterView.FromDictionary(System.Collections.Generic.IDictionary<string!, object?>! parameters) -> Microsoft.AspNetCore.Components.ParameterView
3536
virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo? fieldInfo, System.EventArgs! eventArgs) -> System.Threading.Tasks.Task!
3637
*REMOVED*readonly Microsoft.AspNetCore.Components.RenderTree.RenderTreeEdit.RemovedAttributeName -> string

src/Components/Shared/src/WebEventData.cs

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
#nullable enable
5+
46
using System;
57
using System.Diagnostics.CodeAnalysis;
68
using System.Text.Json;
79
using Microsoft.AspNetCore.Components.RenderTree;
810
using static Microsoft.AspNetCore.Internal.LinkerFlags;
9-
#nullable enable
11+
1012
namespace Microsoft.AspNetCore.Components.Web
1113
{
1214
internal class WebEventData
@@ -61,21 +63,13 @@ private static EventArgs ParseEventArgsJson(Renderer renderer, ulong eventHandle
6163
{
6264
try
6365
{
64-
if (TryGetStandardWebEventArgsType(eventName, out var eventArgsType))
65-
{
66-
// Special case for ChangeEventArgs because its value type can be one of
67-
// several types, and System.Text.Json doesn't pick types dynamically
68-
if (eventArgsType == typeof(ChangeEventArgs))
69-
{
70-
return DeserializeChangeEventArgs(eventArgsJson);
71-
}
72-
}
73-
else
66+
if (TryDeserializeStandardWebEventArgs(eventName, eventArgsJson, out var eventArgs))
7467
{
75-
// For custom events, the args type is determined from the associated delegate
76-
eventArgsType = renderer.GetEventArgsType(eventHandlerId);
68+
return eventArgs;
7769
}
7870

71+
// For custom events, the args type is determined from the associated delegate
72+
var eventArgsType = renderer.GetEventArgsType(eventHandlerId);
7973
return (EventArgs)JsonSerializer.Deserialize(eventArgsJson, eventArgsType, JsonSerializerOptionsProvider.Options)!;
8074
}
8175
catch (Exception e)
@@ -84,7 +78,7 @@ private static EventArgs ParseEventArgsJson(Renderer renderer, ulong eventHandle
8478
}
8579
}
8680

87-
private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullWhen(false)] out Type type)
81+
private static bool TryDeserializeStandardWebEventArgs(string eventName, string eventArgsJson, [NotNullWhen(true)] out EventArgs? eventArgs)
8882
{
8983
// For back-compatibility, we recognize the built-in list of web event names and hard-code
9084
// rules about the deserialization type for their eventargs. This makes it possible to declare
@@ -97,13 +91,15 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
9791
{
9892
case "input":
9993
case "change":
100-
type = typeof(ChangeEventArgs);
94+
// Special case for ChangeEventArgs because its value type can be one of
95+
// several types, and System.Text.Json doesn't pick types dynamically
96+
eventArgs = DeserializeChangeEventArgs(eventArgsJson);
10197
return true;
10298

10399
case "copy":
104100
case "cut":
105101
case "paste":
106-
type = typeof(ClipboardEventArgs);
102+
eventArgs = Deserialize<ClipboardEventArgs>(eventArgsJson);
107103
return true;
108104

109105
case "drag":
@@ -113,20 +109,20 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
113109
case "dragover":
114110
case "dragstart":
115111
case "drop":
116-
type = typeof(DragEventArgs);
112+
eventArgs = Deserialize<DragEventArgs>(eventArgsJson);
117113
return true;
118114

119115
case "focus":
120116
case "blur":
121117
case "focusin":
122118
case "focusout":
123-
type = typeof(FocusEventArgs);
119+
eventArgs = Deserialize<FocusEventArgs>(eventArgsJson);
124120
return true;
125121

126122
case "keydown":
127123
case "keyup":
128124
case "keypress":
129-
type = typeof(KeyboardEventArgs);
125+
eventArgs = Deserialize<KeyboardEventArgs>(eventArgsJson);
130126
return true;
131127

132128
case "contextmenu":
@@ -137,11 +133,11 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
137133
case "mousedown":
138134
case "mouseup":
139135
case "dblclick":
140-
type = typeof(MouseEventArgs);
136+
eventArgs = Deserialize<MouseEventArgs>(eventArgsJson);
141137
return true;
142138

143139
case "error":
144-
type = typeof(ErrorEventArgs);
140+
eventArgs = Deserialize<ErrorEventArgs>(eventArgsJson);
145141
return true;
146142

147143
case "loadstart":
@@ -150,7 +146,7 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
150146
case "load":
151147
case "loadend":
152148
case "progress":
153-
type = typeof(ProgressEventArgs);
149+
eventArgs = Deserialize<ProgressEventArgs>(eventArgsJson);
154150
return true;
155151

156152
case "touchcancel":
@@ -159,7 +155,7 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
159155
case "touchenter":
160156
case "touchleave":
161157
case "touchstart":
162-
type = typeof(TouchEventArgs);
158+
eventArgs = Deserialize<TouchEventArgs>(eventArgsJson);
163159
return true;
164160

165161
case "gotpointercapture":
@@ -172,22 +168,22 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
172168
case "pointerout":
173169
case "pointerover":
174170
case "pointerup":
175-
type = typeof(PointerEventArgs);
171+
eventArgs = Deserialize<PointerEventArgs>(eventArgsJson);
176172
return true;
177173

178174
case "wheel":
179175
case "mousewheel":
180-
type = typeof(WheelEventArgs);
176+
eventArgs = Deserialize<WheelEventArgs>(eventArgsJson);
181177
return true;
182178

183179
case "toggle":
184-
type = typeof(EventArgs);
180+
eventArgs = Deserialize<EventArgs>(eventArgsJson);
185181
return true;
186182

187183
default:
188184
// For custom event types, there are no built-in rules, so the deserialization type is
189185
// determined by the parameter declared on the delegate.
190-
type = null;
186+
eventArgs = null;
191187
return false;
192188
}
193189
}

0 commit comments

Comments
 (0)