Skip to content

TypeMap API (Dynamic implementation) #113954

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

Merged
merged 25 commits into from
Apr 2, 2025

Conversation

AaronRobinsonMSFT
Copy link
Member

See #113362

Definition of TypeMaps APIs in BCL
CoreCLR implementation of TypeMap APIs

Contributes to #110691

Definition of TypeMaps APIs in BCL
CoreCLR implementation of TypeMap APIs
@ghost
Copy link

ghost commented Mar 26, 2025

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

1 similar comment
@ghost
Copy link

ghost commented Mar 26, 2025

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

Copy link
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

@AaronRobinsonMSFT
Copy link
Member Author

@AaronRobinsonMSFT AaronRobinsonMSFT marked this pull request as ready for review March 27, 2025 19:02
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements the dynamic TypeMap API in the BCL with CoreCLR support and introduces new test cases to validate both external and proxy type mappings. Key changes include adding comprehensive tests in TypeMapApp.cs and related libraries, providing the API definitions and implementation in System.Runtime.InteropServices, and introducing lazy dictionaries for efficient type map creation.

Reviewed Changes

Copilot reviewed 11 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/tests/Interop/TypeMap/TypeMapApp.cs Adds tests for external and proxy type mapping functionality
src/tests/Interop/TypeMap/Lib3.cs, Lib4.cs, GroupTypes.cs Provides supporting types and assembly attributes for type mapping
src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs Updates API definitions for TypeMap and related attributes
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs Implements the core TypeMapping API using lazy initialization
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs Implements lazy dictionaries for external and proxy type mappings
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapAttribute*.cs and TypeMapAssemblyTargetAttribute.cs Adds attribute definitions for type mapping configuration
Files not reviewed (9)
  • src/coreclr/vm/assemblynative.cpp: Language not supported
  • src/coreclr/vm/assemblynative.hpp: Language not supported
  • src/coreclr/vm/qcallentrypoints.cpp: Language not supported
  • src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems: Language not supported
  • src/tests/Interop/TypeMap/TypeMapApp.csproj: Language not supported
  • src/tests/Interop/TypeMap/TypeMapLib1.csproj: Language not supported
  • src/tests/Interop/TypeMap/TypeMapLib2.csproj: Language not supported
  • src/tests/Interop/TypeMap/TypeMapLib3.csproj: Language not supported
  • src/tests/Interop/TypeMap/TypeMapLib4.csproj: Language not supported

Set the current assembly during assembly processing to avoid
parsing assembly names.
from R2R to System.Private.CoreLib so it can be
used in other scenarios. R2R specific functions were
kept in R2R.
Update tests for generic type scenarios.
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements the new TypeMap API in CoreLib while refactoring several ReadyToRun compiler nodes to use the new version-resilient hash code logic. Key changes include:

  • Adding generic TypeMap attributes (TypeMapAttribute, TypeMapAssociationAttribute, and TypeMapAssemblyTargetAttribute) in System.Runtime.InteropServices.
  • Updating various ReadyToRun compiler nodes to call VersionResilientHashCode methods instead of ReadyToRunHashCode.
  • Refactoring the VersionResilientHashCode implementation to support the new design.

Reviewed Changes

Copilot reviewed 26 out of 35 changed files in this pull request and generated no comments.

Show a summary per file
File Description
System/Runtime/InteropServices/TypeMapAttribute.cs Adds a new generic attribute for type mapping.
System/Runtime/InteropServices/TypeMapAssociationAttribute.cs Introduces a generic attribute for associating a type with its proxy.
System/Runtime/InteropServices/TypeMapAssemblyTargetAttribute.cs Declares an attribute to designate assemblies for type map inspection.
ILCompiler.ReadyToRun/Compiler/VersionResilientHashCode.cs Provides a managed implementation of version-resilient hash code logic.
Other ReadyToRun nodes (.cs files in DependencyAnalysis, InliningInfoNode.cs, etc.) Update calls from ReadyToRunHashCode to VersionResilientHashCode.
TypeNameResolver.CoreCLR.cs Delegates type resolution to a new helper method.
Files not reviewed (9)
  • src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj: Language not supported
  • src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj: Language not supported
  • src/coreclr/vm/assemblynative.cpp: Language not supported
  • src/coreclr/vm/assemblynative.hpp: Language not supported
  • src/coreclr/vm/qcallentrypoints.cpp: Language not supported
  • src/coreclr/vm/versionresilienthashcode.cpp: Language not supported
  • src/coreclr/vm/versionresilienthashcode.h: Language not supported
  • src/libraries/System.Private.CoreLib/src/Resources/Strings.resx: Language not supported
  • src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems: Language not supported
Comments suppressed due to low confidence (1)

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs:339

  • VersionResilientHashCode.CombineTwoValuesIntoHash is invoked here, but its implementation is not visible in the provided changes. Please verify that this helper method is defined and returns a compatible hash value.
uint hash = unchecked((uint)VersionResilientHashCode.CombineTwoValuesIntoHash((uint)hashOfAttribute, (uint)customAttributeEntry.Parent));

@AaronRobinsonMSFT
Copy link
Member Author

Did you have a chance to check the perf of the type map building at runtime? What's the typical time and memory cost of a single entry?

I wanted to wait until I got through a majority of the perf and architecture feedback. I have a small sample app that I've run a few scenarios on. I've only run the TypeMap scenario (that is, string -> Type) mapping. I was able to create the IReadOnlyDictionary<string, Type> instance with 50,000 TypeMap attributes in 25 ms. That is likely the cheapest scenario. Tomorrow I will get numbers for the Type -> Type mapping.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces new TypeMap API attributes into the BCL and updates the CoreCLR implementation of version-resilient hash code calculations, replacing usages of ReadyToRunHashCode with VersionResilientHashCode.

  • Adds new attribute types for type mapping (TypeMapAssociationAttribute and TypeMapAssemblyTargetAttribute).
  • Refactors hash code computations across CoreCLR and AOT components to use the new VersionResilientHashCode implementation.
  • Introduces a helper method in TypeNameResolver.CoreCLR.cs for type resolution.

Reviewed Changes

Copilot reviewed 26 out of 35 changed files in this pull request and generated no comments.

Show a summary per file
File Description
System/Runtime/InteropServices/TypeMapAssociationAttribute.cs New attribute type for type mapping associations.
System/Runtime/InteropServices/TypeMapAssemblyTargetAttribute.cs New attribute type for declaring assembly targets for type maps.
Common/src/Internal/VersionResilientHashCode.cs Refactored hash code implementation with removal of unused methods.
ILCompiler.ReadyToRun/Compiler/VersionResilientHashCode.ReadyToRun.cs Implements hash code functions for ReadyToRun scenarios.
Various ILCompiler.ReadyToRun dependency analysis files Replaced ReadyToRunHashCode calls with VersionResilientHashCode calls for consistency.
System/Reflection/TypeNameResolver.CoreCLR.cs Added helper method for type resolution.
System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.cs New partial class implementation for version-resilient hash code computation.
Files not reviewed (9)
  • src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj: Language not supported
  • src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj: Language not supported
  • src/coreclr/vm/assemblynative.cpp: Language not supported
  • src/coreclr/vm/assemblynative.hpp: Language not supported
  • src/coreclr/vm/qcallentrypoints.cpp: Language not supported
  • src/coreclr/vm/versionresilienthashcode.cpp: Language not supported
  • src/coreclr/vm/versionresilienthashcode.h: Language not supported
  • src/libraries/System.Private.CoreLib/src/Resources/Strings.resx: Language not supported
  • src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems: Language not supported
Comments suppressed due to low confidence (1)

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs:339

  • The method 'CombineTwoValuesIntoHash' is referenced from VersionResilientHashCode, yet there is no visible implementation in the provided diffs. Please ensure that an appropriate implementation exists to avoid runtime errors.
uint hash = unchecked((uint)VersionResilientHashCode.CombineTwoValuesIntoHash((uint)hashOfAttribute, (uint)customAttributeEntry.Parent));

@AaronRobinsonMSFT
Copy link
Member Author

AaronRobinsonMSFT commented Apr 1, 2025

EDIT I missed some changes from last night and so reran them. Slight improvement.

@jkotas Here are the numbers I'm seeing:

50,000 attributes of each type.

string -> Type: 36 ms 38 ms - Without the other attributes, around 23 ms 25 ms.
Type -> Type: 48 ms 56 ms - This is when there is 50,000 TypeMap attributes too.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;

[assembly: TypeMapAssemblyTarget<object>("AALL")]

{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    var typeMap = TypeMapping.GetOrCreateExternalTypeMapping<object>();
    stopwatch.Stop();
    Console.WriteLine($"Time taken: {stopwatch.ElapsedMilliseconds} ms");
}
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    var typeMap = TypeMapping.GetOrCreateProxyTypeMapping<object>();
    stopwatch.Stop();
    Console.WriteLine($"Time taken: {stopwatch.ElapsedMilliseconds} ms");
}
Assembly generator
using System;
using System.IO;

public class Program
{
    static void Main()
    {
        using (StreamWriter writer = new StreamWriter("bin\\GeneratedAttributes.cs"))
        {
            writer.WriteLine("using System;");
            writer.WriteLine("using System.Runtime.InteropServices;");
            writer.WriteLine("");

            // Generate 10 attributes
            writer.WriteLine("#if ATTR_10 || ATTR_100 || ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 0; i < 10; i++)
            {
                writer.WriteLine($"[assembly: TypeMap<object>(\"{i}\", typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 90 more attributes (total 100)
            writer.WriteLine("#if ATTR_100 || ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 10; i < 100; i++)
            {
                writer.WriteLine($"[assembly: TypeMap<object>(\"{i}\", typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 900 more attributes (total 1000)
            writer.WriteLine("#if ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 100; i < 1000; i++)
            {
                writer.WriteLine($"[assembly: TypeMap<object>(\"{i}\", typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 9000 more attributes (total 10000)
            writer.WriteLine("#if ATTR_10000 || ATTR_ALL");
            for (int i = 1000; i < 10000; i++)
            {
                writer.WriteLine($"[assembly: TypeMap<object>(\"{i}\", typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 40000 more attributes (total 50000)
            writer.WriteLine("#if ATTR_ALL");
            for (int i = 10000; i < 50000; i++)
            {
                writer.WriteLine($"[assembly: TypeMap<object>(\"{i}\", typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Now generate the TypeMapAssociation attributes
            writer.WriteLine("// New TypeMapAssociation attributes");

            // Generate 10 TypeMapAssociation attributes
            writer.WriteLine("#if ATTR_10 || ATTR_100 || ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 0; i < 10; i++)
            {
                writer.WriteLine($"[assembly: TypeMapAssociation<object>(typeof(SourceType{i}), typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 90 more TypeMapAssociation attributes (total 100)
            writer.WriteLine("#if ATTR_100 || ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 10; i < 100; i++)
            {
                writer.WriteLine($"[assembly: TypeMapAssociation<object>(typeof(SourceType{i}), typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 900 more TypeMapAssociation attributes (total 1000)
            writer.WriteLine("#if ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 100; i < 1000; i++)
            {
                writer.WriteLine($"[assembly: TypeMapAssociation<object>(typeof(SourceType{i}), typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 9000 more TypeMapAssociation attributes (total 10000)
            writer.WriteLine("#if ATTR_10000 || ATTR_ALL");
            for (int i = 1000; i < 10000; i++)
            {
                writer.WriteLine($"[assembly: TypeMapAssociation<object>(typeof(SourceType{i}), typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 40000 more TypeMapAssociation attributes (total 50000)
            writer.WriteLine("#if ATTR_ALL");
            for (int i = 10000; i < 50000; i++)
            {
                writer.WriteLine($"[assembly: TypeMapAssociation<object>(typeof(SourceType{i}), typeof(object))]");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Now generate the corresponding class declarations
            writer.WriteLine("// Class declarations for TypeMapAssociation attributes");

            // Generate 10 class declarations
            writer.WriteLine("#if ATTR_10 || ATTR_100 || ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 0; i < 10; i++)
            {
                writer.WriteLine($"public class SourceType{i} {{ }}");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 90 more class declarations (total 100)
            writer.WriteLine("#if ATTR_100 || ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 10; i < 100; i++)
            {
                writer.WriteLine($"public class SourceType{i} {{ }}");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 900 more class declarations (total 1000)
            writer.WriteLine("#if ATTR_1000 || ATTR_10000 || ATTR_ALL");
            for (int i = 100; i < 1000; i++)
            {
                writer.WriteLine($"public class SourceType{i} {{ }}");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 9000 more class declarations (total 10000)
            writer.WriteLine("#if ATTR_10000 || ATTR_ALL");
            for (int i = 1000; i < 10000; i++)
            {
                writer.WriteLine($"public class SourceType{i} {{ }}");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");

            // Generate 40000 more class declarations (total 50000)
            writer.WriteLine("#if ATTR_ALL");
            for (int i = 10000; i < 50000; i++)
            {
                writer.WriteLine($"public class SourceType{i} {{ }}");
            }
            writer.WriteLine("#endif");
            writer.WriteLine("");
        }

        Console.WriteLine("Generated file with TypeMap and TypeMapAssociation attributes (50,000 of each) successfully!");
        Console.WriteLine("Also generated 50,000 unique source types for TypeMapAssociation attributes.");
    }
}

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

LGTM. Thank you!

@AaronRobinsonMSFT
Copy link
Member Author

/ba-g Timeouts

@AaronRobinsonMSFT AaronRobinsonMSFT merged commit 8512b2a into dotnet:main Apr 2, 2025
146 of 154 checks passed
@AaronRobinsonMSFT AaronRobinsonMSFT deleted the type_map_impl branch April 2, 2025 01:03
@github-actions github-actions bot locked and limited conversation to collaborators May 2, 2025
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.

4 participants