Skip to content

Commit

Permalink
Prevent LayoutComponentBase properties from being trimmed. (#30606)
Browse files Browse the repository at this point in the history
The usage pattern for LayoutComponentBase does not really play well with static analysis and results in the
property setter from being removed.
  • Loading branch information
pranavkm committed Mar 8, 2021
1 parent 201a0fa commit bf51aa5
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 27 deletions.
11 changes: 11 additions & 0 deletions src/Components/Components/src/LayoutComponentBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +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.Tasks;
using static Microsoft.AspNetCore.Internal.LinkerFlags;

namespace Microsoft.AspNetCore.Components
{
/// <summary>
Expand All @@ -17,5 +21,12 @@ public abstract class LayoutComponentBase : ComponentBase
/// </summary>
[Parameter]
public RenderFragment? Body { get; set; }

/// <inheritdoc />
// Derived instances of LayoutComponentBase do not appear in any statically analyzable
// calls of OpenComponent<T> where T is well-known. Consequently we have to explicitly provide a hint to the trimmer to preserve
// properties.
[DynamicDependency(Component, typeof(LayoutComponentBase))]
public override Task SetParametersAsync(ParameterView parameters) => base.SetParametersAsync(parameters);
}
}
1 change: 1 addition & 0 deletions src/Components/Components/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute
Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute.CascadingTypeParameterAttribute(string! name) -> void
Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute.Name.get -> string!
Microsoft.AspNetCore.Components.RenderTree.Renderer.GetEventArgsType(ulong eventHandlerId) -> System.Type!
override Microsoft.AspNetCore.Components.LayoutComponentBase.SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) -> System.Threading.Tasks.Task!
static Microsoft.AspNetCore.Components.ParameterView.FromDictionary(System.Collections.Generic.IDictionary<string!, object?>! parameters) -> Microsoft.AspNetCore.Components.ParameterView
virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo? fieldInfo, System.EventArgs! eventArgs) -> System.Threading.Tasks.Task!
*REMOVED*readonly Microsoft.AspNetCore.Components.RenderTree.RenderTreeEdit.RemovedAttributeName -> string
50 changes: 23 additions & 27 deletions src/Components/Shared/src/WebEventData.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// 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.

#nullable enable

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Microsoft.AspNetCore.Components.RenderTree;
using static Microsoft.AspNetCore.Internal.LinkerFlags;
#nullable enable

namespace Microsoft.AspNetCore.Components.Web
{
internal class WebEventData
Expand Down Expand Up @@ -61,21 +63,13 @@ private static EventArgs ParseEventArgsJson(Renderer renderer, ulong eventHandle
{
try
{
if (TryGetStandardWebEventArgsType(eventName, out var eventArgsType))
{
// Special case for ChangeEventArgs because its value type can be one of
// several types, and System.Text.Json doesn't pick types dynamically
if (eventArgsType == typeof(ChangeEventArgs))
{
return DeserializeChangeEventArgs(eventArgsJson);
}
}
else
if (TryDeserializeStandardWebEventArgs(eventName, eventArgsJson, out var eventArgs))
{
// For custom events, the args type is determined from the associated delegate
eventArgsType = renderer.GetEventArgsType(eventHandlerId);
return eventArgs;
}

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

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

case "copy":
case "cut":
case "paste":
type = typeof(ClipboardEventArgs);
eventArgs = Deserialize<ClipboardEventArgs>(eventArgsJson);
return true;

case "drag":
Expand All @@ -113,20 +109,20 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
case "dragover":
case "dragstart":
case "drop":
type = typeof(DragEventArgs);
eventArgs = Deserialize<DragEventArgs>(eventArgsJson);
return true;

case "focus":
case "blur":
case "focusin":
case "focusout":
type = typeof(FocusEventArgs);
eventArgs = Deserialize<FocusEventArgs>(eventArgsJson);
return true;

case "keydown":
case "keyup":
case "keypress":
type = typeof(KeyboardEventArgs);
eventArgs = Deserialize<KeyboardEventArgs>(eventArgsJson);
return true;

case "contextmenu":
Expand All @@ -137,11 +133,11 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
case "mousedown":
case "mouseup":
case "dblclick":
type = typeof(MouseEventArgs);
eventArgs = Deserialize<MouseEventArgs>(eventArgsJson);
return true;

case "error":
type = typeof(ErrorEventArgs);
eventArgs = Deserialize<ErrorEventArgs>(eventArgsJson);
return true;

case "loadstart":
Expand All @@ -150,7 +146,7 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
case "load":
case "loadend":
case "progress":
type = typeof(ProgressEventArgs);
eventArgs = Deserialize<ProgressEventArgs>(eventArgsJson);
return true;

case "touchcancel":
Expand All @@ -159,7 +155,7 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
case "touchenter":
case "touchleave":
case "touchstart":
type = typeof(TouchEventArgs);
eventArgs = Deserialize<TouchEventArgs>(eventArgsJson);
return true;

case "gotpointercapture":
Expand All @@ -172,22 +168,22 @@ private static bool TryGetStandardWebEventArgsType(string eventName, [MaybeNullW
case "pointerout":
case "pointerover":
case "pointerup":
type = typeof(PointerEventArgs);
eventArgs = Deserialize<PointerEventArgs>(eventArgsJson);
return true;

case "wheel":
case "mousewheel":
type = typeof(WheelEventArgs);
eventArgs = Deserialize<WheelEventArgs>(eventArgsJson);
return true;

case "toggle":
type = typeof(EventArgs);
eventArgs = Deserialize<EventArgs>(eventArgsJson);
return true;

default:
// For custom event types, there are no built-in rules, so the deserialization type is
// determined by the parameter declared on the delegate.
type = null;
eventArgs = null;
return false;
}
}
Expand Down

0 comments on commit bf51aa5

Please sign in to comment.