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

TypeDescriptor-related trimming support #102094

Merged
merged 31 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5c7a83e
Add trimming support to TypeDescriptor
steveharter Apr 10, 2024
918f033
Add ComponentResourceManager.ApplyResources<T>
steveharter Apr 11, 2024
7e6bc89
Remove CreateInstanceFromKnownType
steveharter Apr 15, 2024
503aa8a
Add PropertyDescriptor.ConverterFromKnownType()
steveharter Apr 15, 2024
c09d5b6
Add PropertyDescriptor.ConverterFromKnownType tests
steveharter Apr 15, 2024
df490a4
Move to new feature switch model
steveharter Apr 16, 2024
509d29b
Revert some RUC attributes
steveharter Apr 16, 2024
f23af7e
Add more provider extensibility
steveharter Apr 17, 2024
0930d4e
Add some instance-based members
steveharter Apr 18, 2024
d79db32
Add TD.GetConverter(object)
steveharter Apr 19, 2024
da89f95
Fix linker warning
steveharter Apr 19, 2024
35fa7db
Add ApplyResources(object,
steveharter Apr 19, 2024
623fcfd
Rename KnownType to RegisteredType
steveharter Apr 24, 2024
38db301
Sync to main
LakshanF Apr 25, 2024
b0504dd
Build break fixes
LakshanF Apr 25, 2024
96ba348
Exploring a reduced DAM annotations for type registering
LakshanF Apr 25, 2024
4495eed
Update based on approved API
steveharter May 8, 2024
8ff8979
Add impl and tests to validate
steveharter May 9, 2024
65707cc
Remove GetAttributesFromRegisteredType
steveharter May 9, 2024
838b9b1
Update doc, annotations
steveharter May 10, 2024
5aa1e98
Update doc
steveharter May 10, 2024
94cf860
Address some linker warnings
steveharter May 10, 2024
a5e44cb
Fix some Moq tests
steveharter May 12, 2024
3fabac1
Add ConditionalFact for RemoteExecutor
steveharter May 13, 2024
348d9b8
Move nullable tests out of trim tests
steveharter May 13, 2024
905bb1c
Reflection provider changes
steveharter May 13, 2024
2178298
Add back internal GetConverterTrimUnsafe()
steveharter May 14, 2024
a6477ad
Remove a test; NativeAot semantics
steveharter May 14, 2024
0dbb512
Merge branch 'main' of https://github.com/steveharter/runtime into Ty…
steveharter May 15, 2024
72bc953
Add tests for base classes
steveharter May 15, 2024
4b21255
Address non-functional feedback
steveharter May 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
<_IsPublishing>true</_IsPublishing>
</PropertyGroup>

<PropertyGroup>
<_ComObjectDescriptorSupport>false</_ComObjectDescriptorSupport>
</PropertyGroup>

<ItemGroup>
<RuntimeHostConfigurationOption Include="System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported"
Condition="'$(_ComObjectDescriptorSupport)' != ''"
Value="$(_ComObjectDescriptorSupport)"
Trim="true" />
</ItemGroup>

<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
Copy link
Contributor

Choose a reason for hiding this comment

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

@sbomer, do we still need this? PublishTrimmed apps should have this property set to false automatically.


<PropertyGroup>
Expand Down

Large diffs are not rendered by default.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema

Version 2.0

The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.

Example:

... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>

There are any number of "resheader" rows that contain simple
name/value pairs.

Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.

The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:

Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.

mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
Expand Down Expand Up @@ -262,4 +321,10 @@
<data name="BinaryFormatterMessage" xml:space="preserve">
<value>BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.</value>
</data>
</root>
<data name="TypeIsNotRegistered" xml:space="preserve">
<value>The type {0} is not registered. Call 'TypeDescriptor.RegisterType()' to register the type.</value>
</data>
<data name="CustomTypeProviderNotImplemented" xml:space="preserve">
<value>Custom type providers must implement member {0}.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,14 @@
<Compile Include="System\ComponentModel\Design\IDictionaryService.cs" />
<Compile Include="System\ComponentModel\Design\IExtenderListService.cs" />
<Compile Include="System\ComponentModel\Design\ITypeDescriptorFilterService.cs" />
<Compile Include="$(CommonPath)System\Drawing\ColorConverterCommon.cs"
Link="System\Drawing\ColorConverterCommon.cs" />
<Compile Include="$(CommonPath)System\Drawing\ColorTable.cs"
Link="System\Drawing\ColorTable.cs" />
<Compile Include="$(CommonPath)System\Drawing\ColorConverterCommon.cs" Link="System\Drawing\ColorConverterCommon.cs" />
<Compile Include="$(CommonPath)System\Drawing\ColorTable.cs" Link="System\Drawing\ColorTable.cs" />
<Compile Include="System\Drawing\ColorConverter.cs" />
<Compile Include="System\Drawing\PointConverter.cs" />
<Compile Include="System\Drawing\RectangleConverter.cs" />
<Compile Include="System\Drawing\SizeConverter.cs" />
<Compile Include="System\Drawing\SizeFConverter.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs"
Link="Common\System\Obsoletions.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs" Link="Common\System\Obsoletions.cs" />
<!-- New code introduced for netcoreapp 2.0 -->
<Compile Include="System\ComponentModel\AddingNewEventArgs.cs" />
<Compile Include="System\ComponentModel\AddingNewEventHandler.cs" />
Expand Down Expand Up @@ -184,6 +181,7 @@
<Compile Include="System\ComponentModel\ToolboxItemAttribute.cs" />
<Compile Include="System\ComponentModel\ToolboxItemFilterAttribute.cs" />
<Compile Include="System\ComponentModel\ToolboxItemFilterType.cs" />
<Compile Include="System\ComponentModel\TrimSafeReflectionHelper.cs" />
<Compile Include="System\ComponentModel\WarningException.cs" />
<Compile Include="System\ComponentModel\Design\ActiveDesignerEventArgs.cs" />
<Compile Include="System\ComponentModel\Design\ActiveDocumentEventHandler.cs" />
Expand Down Expand Up @@ -250,10 +248,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="$(LibrariesProjectRoot)System.Text.RegularExpressions\gen\System.Text.RegularExpressions.Generator.csproj"
ReferenceOutputAssembly="false"
SetTargetFramework="TargetFramework=netstandard2.0"
OutputItemType="Analyzer" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Text.RegularExpressions\gen\System.Text.RegularExpressions.Generator.csproj" ReferenceOutputAssembly="false" SetTargetFramework="TargetFramework=netstandard2.0" OutputItemType="Analyzer" />
<Reference Include="System.Collections" />
<Reference Include="System.Collections.NonGeneric" />
<Reference Include="System.Collections.Specialized" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,31 @@ private CultureInfo? NeutralResourcesCulture
/// </summary>
[RequiresUnreferencedCode("The Type of value cannot be statically discovered.")]
public virtual void ApplyResources(object value, string objectName, CultureInfo? culture)
{
ArgumentNullException.ThrowIfNull(value);
ArgumentNullException.ThrowIfNull(objectName);
ApplyResources(value, value.GetType(), objectName, culture);
}

/// <summary>
/// This method examines all the resources for the provided culture.
/// When it finds a resource with a key in the format of
/// &quot;[objectName].[property name]&quot; or &quot;[objectName]-[property name]&quot; it will apply that resource's value
/// to the corresponding property on the object. If there is no matching
/// property the resource will be ignored.
/// </summary>
public virtual void ApplyResourcesToRegisteredType(object value, string objectName, CultureInfo? culture)
{
ArgumentNullException.ThrowIfNull(value);
ArgumentNullException.ThrowIfNull(objectName);

Type typeFromValue = value.GetType();
TypeDescriptor.ValidateRegisteredType(typeFromValue);
ApplyResources(value, typeFromValue, objectName, culture);
}

private void ApplyResources(object value, Type typeFromValue, string objectName, CultureInfo? culture)
{
culture ??= CultureInfo.CurrentUICulture;

// The general case here will be to always use the same culture, so optimize for
Expand Down Expand Up @@ -148,7 +169,7 @@ public virtual void ApplyResources(object value, string objectName, CultureInfo?

if (componentReflect)
{
PropertyDescriptor? prop = TypeDescriptor.GetProperties(value).Find(propName, IgnoreCase);
PropertyDescriptor? prop = TypeDescriptorGetProperties(value).Find(propName, IgnoreCase);

if (prop != null && !prop.IsReadOnly && (kvp.Value == null || prop.PropertyType.IsInstanceOfType(kvp.Value)))
{
Expand All @@ -161,16 +182,16 @@ public virtual void ApplyResources(object value, string objectName, CultureInfo?

try
{
prop = value.GetType().GetProperty(propName, flags);
prop = prop = TrimSafeReflectionHelper.GetProperty(typeFromValue, propName, flags);
}
catch (AmbiguousMatchException)
{
// Looks like we ran into a conflict between a declared property and an inherited one.
// In such cases, we choose the most declared one.
Type? t = value.GetType();
Type? t = typeFromValue;
do
{
prop = t.GetProperty(propName, flags | BindingFlags.DeclaredOnly);
prop = TrimSafeReflectionHelper.GetProperty(t, propName, flags | BindingFlags.DeclaredOnly);
t = t.BaseType;
} while (prop == null && t != null && t != typeof(object));
}
Expand All @@ -181,6 +202,10 @@ public virtual void ApplyResources(object value, string objectName, CultureInfo?
}
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Calling method either has RequiresUnreferencedCode or has registered types.")]
static PropertyDescriptorCollection TypeDescriptorGetProperties(object value) => TypeDescriptor.GetProperties(value);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,36 @@ public virtual AttributeCollection GetAttributes()
return new TypeConverter();
}

/// <summary>
/// The GetConverterFromRegisteredType method returns a type converter for the type this type
/// descriptor is representing.
/// </summary>
public virtual TypeConverter? GetConverterFromRegisteredType()
{
if (_parent != null)
{
return _parent.GetConverterFromRegisteredType();
}

if (RequireRegisteredTypes is null)
{
if (TypeDescriptor.RequireRegisteredTypes)
{
TypeDescriptor.ThrowHelper.ThrowNotImplementedException_CustomTypeProviderMustImplememtMember(nameof(GetConverterFromRegisteredType));
}
}
else if (RequireRegisteredTypes == true)
{
TypeDescriptor.ThrowHelper.ThrowNotImplementedException_CustomTypeProviderMustImplememtMember(nameof(GetConverterFromRegisteredType));
}

return Forward();

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = TypeDescriptionProvider.ForwardFromRegisteredMessage)]
TypeConverter? Forward() => GetConverter();
}

/// <summary>
/// The GetDefaultEvent method returns the event descriptor for the default
/// event on the object this type descriptor is representing.
Expand All @@ -103,10 +133,7 @@ public virtual AttributeCollection GetAttributes()

/// <summary>
/// The GetEvents method returns a collection of event descriptors
/// for the object this type descriptor is representing. An optional
/// attribute array may be provided to filter the collection that is
/// returned. If no parent is provided,this will return an empty
/// event collection.
/// for the object this type descriptor is representing.
/// </summary>
public virtual EventDescriptorCollection GetEvents()
{
Expand Down Expand Up @@ -136,12 +163,35 @@ public virtual EventDescriptorCollection GetEvents(Attribute[]? attributes)
return EventDescriptorCollection.Empty;
}

/// <summary>
/// Returns a collection of event descriptors
/// for the object this type descriptor is representing.
/// </summary>
public virtual EventDescriptorCollection GetEventsFromRegisteredType()
{
if (_parent != null)
{
return _parent.GetEventsFromRegisteredType();
}

if (RequireRegisteredTypes is null)
{
if (TypeDescriptor.RequireRegisteredTypes)
{
TypeDescriptor.ThrowHelper.ThrowNotImplementedException_CustomTypeProviderMustImplememtMember(nameof(GetEventsFromRegisteredType));
}
}
else if (RequireRegisteredTypes == true)
{
TypeDescriptor.ThrowHelper.ThrowNotImplementedException_CustomTypeProviderMustImplememtMember(nameof(GetEventsFromRegisteredType));
}

return GetEvents();
}

/// <summary>
/// The GetProperties method returns a collection of property descriptors
/// for the object this type descriptor is representing. An optional
/// attribute array may be provided to filter the collection that is returned.
/// If no parent is provided,this will return an empty
/// property collection.
/// for the object this type descriptor is representing.
/// </summary>
[RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)]
public virtual PropertyDescriptorCollection GetProperties()
Expand All @@ -154,6 +204,36 @@ public virtual PropertyDescriptorCollection GetProperties()
return PropertyDescriptorCollection.Empty;
}

/// <summary>
/// The GetProperties method returns a collection of property descriptors
/// for the object this type descriptor is representing.
/// </summary>
public virtual PropertyDescriptorCollection GetPropertiesFromRegisteredType()
{
if (_parent != null)
{
return _parent.GetPropertiesFromRegisteredType();
}

if (RequireRegisteredTypes is null)
{
if (TypeDescriptor.RequireRegisteredTypes)
{
TypeDescriptor.ThrowHelper.ThrowNotImplementedException_CustomTypeProviderMustImplememtMember(nameof(GetPropertiesFromRegisteredType));
}
}
else if (RequireRegisteredTypes == true)
{
TypeDescriptor.ThrowHelper.ThrowNotImplementedException_CustomTypeProviderMustImplememtMember(nameof(GetPropertiesFromRegisteredType));
}

return Forward();

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = TypeDescriptionProvider.ForwardFromRegisteredMessage)]
PropertyDescriptorCollection Forward() => GetProperties();
}

/// <summary>
/// The GetProperties method returns a collection of property descriptors
/// for the object this type descriptor is representing. An optional
Expand All @@ -180,5 +260,30 @@ public virtual PropertyDescriptorCollection GetProperties(Attribute[]? attribute
/// to use its default type description services.
/// </summary>
public virtual object? GetPropertyOwner(PropertyDescriptor? pd) => _parent?.GetPropertyOwner(pd);

/// <summary>
/// Whether types are required to be registered through <see cref="TypeDescriptionProvider.RegisterType{T}"/>.
/// </summary>
/// <remarks>
/// The default value is <see langword="null"/> which means that the type descriptor has not declared whether or not it is compatible registered types.
/// A type descriptor needs to implement this to return <see langword="true"/> or <see langword="false"/> if the feature switch
/// 'System.ComponentModel.TypeDescriptor.RequireRegisteredTypes' is enabled.
/// If <see langword="true"/> is returned, then the type descriptor must also implement
/// <see cref="ICustomTypeDescriptor.GetConverterFromRegisteredType()"/>,
/// <see cref="ICustomTypeDescriptor.GetEventsFromRegisteredType()"/>, and
/// <see cref="ICustomTypeDescriptor.GetPropertiesFromRegisteredType()"/>.
/// </remarks>
public virtual bool? RequireRegisteredTypes
{
get
{
if (_parent != null)
{
return _parent.RequireRegisteredTypes;
}

return null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,13 @@ public override Type GetReflectionType(
}

public override bool IsSupportedType(Type type) => Provider.IsSupportedType(type);

public override bool IsRegisteredType(Type type) => Provider.IsRegisteredType(type);

public override bool? RequireRegisteredTypes => Provider.RequireRegisteredTypes;

public override ICustomTypeDescriptor? GetTypeDescriptorFromRegisteredType(Type objectType, object? instance) => Provider.GetTypeDescriptorFromRegisteredType(objectType, instance);

public override void RegisterType<[DynamicallyAccessedMembers(TypeDescriptor.RegisteredTypesDynamicallyAccessedMembers)] T>() => Provider.RegisterType<T>();
}
}
Loading