Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add trimming annotations #7256

Merged
10 commits merged into from
Jun 17, 2022
Merged

Add trimming annotations #7256

10 commits merged into from
Jun 17, 2022

Conversation

kant2002
Copy link
Contributor

@kant2002 kant2002 commented Jun 3, 2022

Mostly make Clone methods works by applying PublicParameterlessConstructor and annotate EditType

Microsoft Reviewers: Open in CodeFlow

@kant2002 kant2002 requested a review from a team as a code owner June 3, 2022 16:25
@ghost ghost assigned kant2002 Jun 3, 2022
@kant2002
Copy link
Contributor Author

kant2002 commented Jun 3, 2022

First jab at #4649

Copy link
Member

@RussKie RussKie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please describe the developer flow (that is, what steps are to surface the issue) and the testing steps (i.e., the results before and after)?
Thanks

@RussKie
Copy link
Member

RussKie commented Jun 6, 2022

@agocke does it look like we're moving in the right direction?

@RussKie RussKie added area-Trimming 📭 waiting-author-feedback The team requires more information from the author labels Jun 6, 2022
@ghost ghost removed the 📭 waiting-author-feedback The team requires more information from the author label Jun 6, 2022
@kant2002
Copy link
Contributor Author

kant2002 commented Jun 6, 2022

I think I should start with dotnet/runtime#61874 (comment) suggestions and create baseline first, and then chip-off from that issues in subsequence PR like this one.

@RussKie
Copy link
Member

RussKie commented Jun 7, 2022

I think I should start with dotnet/runtime#61874 (comment) suggestions and create baseline first, and then chip-off from that issues in subsequence PR like this one.

Thank you for the link. Sounds good👍

Copy link
Member

@RussKie RussKie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fails to compile

Comment on lines 9 to 23
internal const string FilterRequiresUnreferencedCodeMessage = "The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.";

internal const string AttributesRequiresUnreferencedCodeMessage = "Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.";

internal const string PropertyDescriptorPropertyTypeMessage = "PropertyDescriptor's PropertyType cannot be statically discovered.";

internal const string EditorRequiresUnreferencedCode = "Editors registered in TypeDescriptor.AddEditorTable may be trimmed.";

internal const string EventDescriptorRequiresUnreferencedCodeMessage = "The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.";

internal const string TypeConverterGetPropertiesMessage = "The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.";

internal const string BindingListViewFilterMessage = "Members of types used in the filter expression might be trimmed.";

internal const string SiteNameMessage = "The Type of components in the container cannot be statically discovered to validate the name.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep these in A-Z order, because once we have dozens of those searching for these outside an IDE may become a royal pain.

@RussKie
Copy link
Member

RussKie commented Jun 10, 2022

Could you please describe the developer flow what commands you ran, what were the results before this change and with the change?
I understand you're following the guide provided by @vitek-karas in dotnet/runtime#61874 (comment), however, we can't expect everyone looking at this change to go an reverse engineer the suggested.

@RussKie RussKie added the 📭 waiting-author-feedback The team requires more information from the author label Jun 10, 2022
@kant2002
Copy link
Contributor Author

No, actually I do not follow that guide, I just trim my sample application using regular trimming options. And fix all errors. What comment by @vitek-karas gives, is one time setup for infra structure and then warnings would be part of build pipeline. also suppressions would be documented and PRs on improving trimmability would be just chipping from long list of issues.

I need to prepare new PR based on Vitek's comments so we have baseline for trimming and then I comeback to this PR.

@ghost ghost removed the 📭 waiting-author-feedback The team requires more information from the author label Jun 10, 2022
@@ -516,6 +518,7 @@ string ICustomTypeDescriptor.GetComponentName()
/// <summary>
/// ICustomTypeDescriptor implementation.
/// </summary>
[RequiresUnreferencedCode(AttributesRequiresUnreferencedCodeMessage)]
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return GetConverter();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't the GetConverter annotated with RUC?
Typically when we added annotations in runtime we went bottom to top - started with the places which reported warnings and added annotations as necessary. In this case the problematic calls is TypeDescriptor.GetConverter - which is in GetConverter, so I would expect that to be annotated first - and from there all of these should be annotated as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see - the interface definition also annotates these with RUC - got it.
You'll need to annotate GetConverter at some point, but I understand how you got here now - sorry.

@@ -61,6 +62,8 @@ internal AxSourcingSite(IComponent component, Ole32.IOleClientSite clientSite, s
public string? Name
{
get => _name;

[RequiresUnreferencedCode(TrimmingConstants.SiteNameMessage)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs some comment to explain what's dangerous about setting the property.
I assume that the name is later used in some reflection stuff, but in that case the .ctor should also be annotated as it can also set the name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I see from code, I can add supression saying that Name is used only for naming purposes. Even in the designer I think that names used for CodeDom mostly, and never accessed via reflections.

Okay I found places where reflection used

[Obsolete("This method has been deprecated. Use InitializeNewComponent instead. https://go.microsoft.com/fwlink/?linkid=14202")]
public virtual void OnSetComponentDefaults()
{
ISite site = Component?.Site;
if (site is not null)
{
IComponent component = Component;
PropertyDescriptor pd = TypeDescriptor.GetDefaultProperty(component);
if (pd is not null && pd.PropertyType.Equals(typeof(string)))
{
string current = (string)pd.GetValue(component);
if (current is null || current.Length == 0)
{
pd.SetValue(component, site.Name);

CodeDom

string compName = site.Name;
if (!(site.GetService(typeof(CodeTypeDeclaration)) is CodeTypeDeclaration typeDecl) || compName is null)
{
return;
}
foreach (CodeTypeMember member in typeDecl.Members)
{
if (member is CodeMemberField field && field.Name.Equals(compName))
{
typeDecl.Members.Remove(field);

and
string name = site.Name;
if (name is null)
{
return MemberAttributes.Private;
}
FieldInfo field = TypeDescriptor.GetReflectionType(baseType).GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And one more I miss to post due to slippery pressing Enter

defaultValues["Text"] = site.Name;
IComponent component = Component;
PropertyDescriptor pd = TypeDescriptor.GetProperties(ToolStripItem)["Text"];
if (pd != null && pd.PropertyType.Equals(typeof(string)))
{
string current = (string)pd.GetValue(component);
if (current is null || current.Length == 0)
{
pd.SetValue(component, site.Name);

I'm curious is designers only partially open sourced, or these locations can be rewritten without need for reflection.

@@ -57,51 +58,71 @@ Type IReflect.UnderlyingSystemType
}

// Methods
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr)
{
return typeIReflectImplementation.GetField(name, bindingAttr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need to annotate the fields with All probably as well for this to work correctly.
Honestly this class is weird - it will end up calling the GetFields on itself - basically typeof(HtmlToClrEventProxy).GetFields. I assume it's because it's really only used for the InvokeMember, but in that case I would expect everything else to throw, since the way it is right now it doesn't make much sense to me.

@@ -12,6 +12,7 @@ namespace System.Windows.Forms
/// <summary>
/// Identifies a band or column in the dataGridView.
/// </summary>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This is the only type in its hierarchy which technically needs the attribute. The attribute is effectively inherited and so you don't need to add it to a derived type again, even if that type "uses" it. But there's no harm in having it on the derived types either.

@vitek-karas
Copy link
Member

If the effort to make winforms trimmable is serious (as in the repo maintainers want this), then I would probably start with some infra similar to what runtime has:

  • Custom step in build which runs the trimmer on all of the assemblies "at once" - @agocke: ideally we would make it easier to set this up, since I assume other repos will want to use this as well - maybe we should make it part of arcade?
  • This step uses ILLink.suppression.xml files in each project to suppress trim warnings produced - these files effectively act like TODO lists. Once the effort to make winforms trimmable is done, these files should be empty.
  • Run it first, populate the suppression files and merge

An example of this in the runtime repo is this target: https://github.com/dotnet/runtime/blob/6c7d9359c271212416865c446ad0167cbdcd17e1/src/libraries/oob.proj#L75
The infra in runtime is probably overly complex (since runtime runs the linker twice, which winforms probably shouldn't do), but the basics can be seen there.

This provides infra which has couple of advantages:

  • Provides a repeatable way to get the trim warnings - per @RussKie request
  • Should be part of CI, so new code doesn't regress the trimming effort - or at least does it intentionally
  • As noted above, acts as a TODO list to track the remaining work (it's not very precise as some warnings are easy while others are hard to fix, but at least something)

@RussKie
Copy link
Member

RussKie commented Jun 16, 2022

Our build agents are currently undergoing some maintenance, so we'll need to re-run the build once the agents are back online.

@dotnet dotnet deleted a comment from azure-pipelines bot Jun 16, 2022
@RussKie
Copy link
Member

RussKie commented Jun 17, 2022

/azp run

@ghost
Copy link

ghost commented Jun 17, 2022

Hello @RussKie!

Because this pull request has the :octocat: automerge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to oblige

@ghost ghost merged commit b49db1e into dotnet:main Jun 17, 2022
@ghost ghost added this to the 7.0 Preview6 milestone Jun 17, 2022
@kant2002 kant2002 deleted the kant/trimming branch June 17, 2022 09:56
@ghost ghost locked as resolved and limited conversation to collaborators Jul 17, 2022
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants