Skip to content

Do not trim private methods used by the designer #102244

@Tanya-Solyanik

Description

@Tanya-Solyanik

Description

Copied from dotnet/winforms#11314

Private methods whose names follow the pattern ShouldSerialize<PropertyName> or Reset<ProppertyName> should not be trimmed by the linker because they are used by the TypeDescriptor via reflection. Additional conditions: Winforms designer is interested in types derived from IComponent. Property should not be hidden from serializers via SerializerVisibility attribute. Property should be public.

TypeDescriptor accesses these methods here

Suggested fix from @ericstj

The fix is to add a file here runtime/src/libraries/System.Data.Common/src/ILLink at bad00cf23ec49a2607776ffd6e1810c4dbf540b3 · dotnet/runtime (github.com)

That looks similar to runtime/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Descriptors.LibraryBuild.xml at bad00cf23ec49a2607776ffd6e1810c4dbf540b3 · dotnet/runtime (github.com)

Search pattern for ShouldSerialize

Search pattern for Reset

Searches yeld some false positives if the class is not designable, not an IComponent, or class does not have the public, serializable property, or method is actually called directly. For each property we need to examine metadata in the trimmed assembly to see if the methods were removed.

Reproduction Steps

This works on 4.8.1 but doesn't on net6

using System;
using System.ComponentModel;
using System.Data;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            DataColumn dataColumn1 = new DataColumn
            {
                ColumnName = "dataColumn1",
                DataType = typeof(DateTime)
            };

            var properties = TypeDescriptor.GetProperties(dataColumn1);
            var property = properties[nameof(DataColumn.DefaultValue)];
            if (property != null)
            {
                bool shouldSerialize = property.ShouldSerializeValue(dataColumn1);
                Console.WriteLine($"ShouldSerialize default? expected {false} actual {shouldSerialize}");

                dataColumn1.DefaultValue = DateTime.MinValue;
                shouldSerialize = property.ShouldSerializeValue(dataColumn1);
                Console.WriteLine($"ShouldSerialize changed? expected {true} actual {shouldSerialize}");

                // Reset method is not available
                property.ResetValue(dataColumn1);
                Console.WriteLine($"Reset? expected {DateTime.MinValue} actual {dataColumn1.DefaultValue}");
            }

            property = properties[nameof(DataColumn.Caption)];
            if (property != null)
            {
                bool shouldSerialize = property.ShouldSerializeValue(dataColumn1);
                Console.WriteLine($"ShouldSerialize default? expected {false} actual {shouldSerialize}");

                dataColumn1.Caption = "Caption";
                shouldSerialize = property.ShouldSerializeValue(dataColumn1);
                Console.WriteLine($"ShouldSerialize changed? expected {true} actual {shouldSerialize}");

                // Reset method is available
                property.ResetValue(dataColumn1);
                Console.WriteLine($"Reset? expected {nameof(dataColumn1)} actual {dataColumn1.Caption}");
            }

            property = properties[nameof(DataColumn.Namespace)];
            if (property != null)
            {
                bool shouldSerialize = property.ShouldSerializeValue(dataColumn1);
                Console.WriteLine($"ShouldSerialize default? expected {false} actual {shouldSerialize}");

                dataColumn1.Namespace = "Namespace";
                shouldSerialize = property.ShouldSerializeValue(dataColumn1);
                Console.WriteLine($"ShouldSerialize changed? expected {true} actual {shouldSerialize}");

                // Reset method is available
                property.ResetValue(dataColumn1);
                Console.WriteLine($"Reset? expected '' actual `{dataColumn1.Namespace}`");
            }

            _ = Console.ReadLine();
        }
    }
}

Expected behavior

ShouldSerialize, Reset methods are present in the assembly

Actual behavior

ShouldSerialize, Reset methods are trimmed

Regression?

The end scenario regressed between the InProc and OOP designers.

Known Workarounds

none

Configuration

WIndows

Other information

Trimmed ShouldSerialize causes serialization using BinaryFormatter. The fix should be serviced.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions