-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Implement Interop Type Map support in NativeAOT #116355
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
Changes from 42 commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
4c1143b
Track type cast targets
jkoritzinsky ce10c21
Scan for casts as part of the MethodBodyScanner instead
jkoritzinsky 5495324
After feedback, first draft implementing external type map (compiler …
jkoritzinsky bb84f34
Add first pass of external type map implementation for NAOT runtime
jkoritzinsky 650c033
Force the target type in the external type map entry to be constructa…
jkoritzinsky 1e9b608
First pass implementing the proxy type map
jkoritzinsky a619e5d
Fix compiler asserts
jkoritzinsky 2a5531e
Don't root type map custom attributes
jkoritzinsky c7f3638
Provide a mechanism to specify NativeAOT-only test apps
jkoritzinsky 847760b
Fix format writing so it actually works
jkoritzinsky 6c825d8
Cache type map attribute type lookup
jkoritzinsky 64af9d3
Add additional node in the IL Scanner when we see a cast that may be …
jkoritzinsky c3bf1ed
Enable the TypeMapApp test for NativeAOT and adjust as necessary to m…
jkoritzinsky 2ed2d97
Split type map manager type so we don't need to pass null
jkoritzinsky 4334a63
Match attributes based on name. Don't care about the assembly
jkoritzinsky 654731b
Hook up the type map manager to the trimming test driver
jkoritzinsky 89a776f
Add TrimmingTest and add one more test case in the NativeAOT test app
jkoritzinsky 99cb5c9
Add warning validation
jkoritzinsky b73848d
Fix node name
jkoritzinsky 0f083a5
Don't materialize a string for every entry as we search the hashtable
jkoritzinsky a48496a
Remove the "type map entry" nodes and rename the "associated" type ma…
jkoritzinsky bcf98ef
Collapse MetadataBasedTypeMapManager and TypeMapManager
jkoritzinsky 462334c
We know all the type map nodes up front in the AnalysisBasedTypeMapMa…
jkoritzinsky e4ff1d9
Add an interface for each set of nodes to collapse the number of list…
jkoritzinsky 1881042
Refactor the concept of "optimized out observations we must not forge…
jkoritzinsky 3a9dbc9
Move attribute check outside of UsageBasedMetadataManager
jkoritzinsky 098f57f
Put TypeMapStates into its own file as a top-level type
jkoritzinsky b3026cb
Fix comparison to match pattern
jkoritzinsky de0ed0b
Fix cast typo
jkoritzinsky 2f40f9c
Make diagnostic names for invalid type map nodes unique
jkoritzinsky 0445c4c
Just use reference equality and the default hash code for the hashtab…
jkoritzinsky 3eae3d1
Add some more test cases around interfaces and use nameof instead of …
jkoritzinsky 46cd22b
Add support in the illink analyzer test host for no-body classes and …
jkoritzinsky 1659b1e
Don't try to remember optimizations. Just root whatever we scanned du…
jkoritzinsky 0b037e8
Add comment on TrimTarget
jkoritzinsky aa1ada7
Fix failures in TypeMapApp test
jkoritzinsky da59332
Seal types
jkoritzinsky c37d585
Put stubs on the generated asssembly
jkoritzinsky 44c0635
Don't instantiate Dictionary<TKey, TValue> on the "no map" path.
jkoritzinsky 680e8bc
Persist maps for all requests and throw an exception when no map is a…
jkoritzinsky a31d9dc
Fix indentation and revert change in NativeFormatWriter
jkoritzinsky 74c8a5a
Add more interesting test cases.
jkoritzinsky 364edc7
Merge branch 'main' of https://github.com/dotnet/runtime into typemap…
jkoritzinsky 1571bf5
Add a more in-depth spec at the IL level describing the type map feature
jkoritzinsky 41a894b
PR feedback
jkoritzinsky e45a026
Cleanup based on primary constructor guidance
jkoritzinsky File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
209 changes: 209 additions & 0 deletions
209
...tem.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Collections; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Text; | ||
|
|
||
| using Internal.NativeFormat; | ||
| using Internal.Reflection.Core.Execution; | ||
| using Internal.Runtime; | ||
| using Internal.Runtime.Augments; | ||
| using Internal.Runtime.TypeLoader; | ||
|
|
||
| namespace System.Runtime.InteropServices | ||
| { | ||
| internal static class TypeMapLazyDictionary | ||
| { | ||
| public static IReadOnlyDictionary<string, Type> CreateExternalTypeMap(RuntimeType typeMapGroup) | ||
| { | ||
| RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle; | ||
| foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules()) | ||
| { | ||
| if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.ExternalTypeMap, out NativeReader externalTypeMapReader)) | ||
| { | ||
| continue; | ||
| } | ||
| NativeParser externalTypeMapParser = new NativeParser(externalTypeMapReader, 0); | ||
| NativeHashtable externalTypeMapTable = new NativeHashtable(externalTypeMapParser); | ||
|
|
||
| ExternalReferencesTable externalReferences = default; | ||
| externalReferences.InitializeCommonFixupsTable(module); | ||
|
|
||
| var lookup = externalTypeMapTable.Lookup(typeMapGroupHandle.GetHashCode()); | ||
| NativeParser entryParser; | ||
| while (!(entryParser = lookup.GetNext()).IsNull) | ||
| { | ||
| RuntimeTypeHandle foundTypeMapGroup = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); | ||
| if (!foundTypeMapGroup.Equals(typeMapGroupHandle)) | ||
| { | ||
| continue; | ||
| } | ||
| bool isValid = entryParser.GetUnsigned() == 1; | ||
| if (!isValid) | ||
| { | ||
| unsafe | ||
| { | ||
| delegate*<void> exceptionStub = (delegate*<void>)externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); | ||
| exceptionStub(); | ||
| Debug.Fail("Expected exception stub to throw an exception."); | ||
| return null; // Should never reach here, as the exception stub should throw an exception. | ||
| } | ||
| } | ||
|
|
||
| return new ExternalTypeMapDictionary(new NativeHashtable(entryParser), externalReferences); | ||
| } | ||
| } | ||
|
|
||
| throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(typeMapGroup); | ||
| } | ||
|
|
||
| public static IReadOnlyDictionary<Type, Type> CreateProxyTypeMap(RuntimeType typeMapGroup) | ||
| { | ||
| RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle; | ||
| foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules()) | ||
| { | ||
| if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.ProxyTypeMap, out NativeReader externalTypeMapReader)) | ||
| { | ||
| continue; | ||
| } | ||
| NativeParser externalTypeMapParser = new NativeParser(externalTypeMapReader, 0); | ||
| NativeHashtable externalTypeMapTable = new NativeHashtable(externalTypeMapParser); | ||
|
|
||
| ExternalReferencesTable externalReferences = default; | ||
| externalReferences.InitializeCommonFixupsTable(module); | ||
|
|
||
| var lookup = externalTypeMapTable.Lookup(typeMapGroupHandle.GetHashCode()); | ||
| NativeParser entryParser; | ||
| while (!(entryParser = lookup.GetNext()).IsNull) | ||
| { | ||
| RuntimeTypeHandle foundTypeMapGroup = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); | ||
| if (!foundTypeMapGroup.Equals(typeMapGroupHandle)) | ||
| { | ||
| continue; | ||
| } | ||
| bool isValid = entryParser.GetUnsigned() == 1; | ||
| if (!isValid) | ||
| { | ||
| unsafe | ||
| { | ||
| delegate*<void> exceptionStub = (delegate*<void>)externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); | ||
| exceptionStub(); | ||
| Debug.Fail("Expected exception stub to throw an exception."); | ||
| return null; // Should never reach here, as the exception stub should throw an exception. | ||
| } | ||
| } | ||
|
|
||
| return new AssociatedTypeMapDictionary(new NativeHashtable(entryParser), externalReferences); | ||
| } | ||
| } | ||
|
|
||
| throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(typeMapGroup); | ||
| } | ||
|
|
||
| private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReflectionMapBlob blob, out NativeReader reader) | ||
| { | ||
| byte* pBlob; | ||
| uint cbBlob; | ||
|
|
||
| if (RuntimeImports.RhFindBlob(module, (uint)blob, &pBlob, &cbBlob)) | ||
| { | ||
| reader = new NativeReader(pBlob, cbBlob); | ||
| return true; | ||
| } | ||
|
|
||
| reader = default; | ||
| return false; | ||
| } | ||
|
|
||
| private abstract class TypeMapDictionaryBase<TKey> : IReadOnlyDictionary<TKey, Type> | ||
| { | ||
| public abstract Type this[TKey key] { get; } | ||
| public abstract bool TryGetValue(TKey key, [MaybeNullWhen(false)] out Type value); | ||
| // Not supported to avoid exposing TypeMap entries in a manner that | ||
| // would violate invariants the Trimmer is attempting to enforce. | ||
| public IEnumerable<TKey> Keys => throw new NotSupportedException(); | ||
| public IEnumerable<Type> Values => throw new NotSupportedException(); | ||
| public int Count => throw new NotSupportedException(); | ||
| public bool ContainsKey(TKey key) => throw new NotSupportedException(); | ||
| public IEnumerator<KeyValuePair<TKey, Type>> GetEnumerator() => throw new NotSupportedException(); | ||
| IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
| } | ||
|
|
||
| private sealed class ExternalTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : TypeMapDictionaryBase<string> | ||
| { | ||
| public override Type this[string key] | ||
| { | ||
| get | ||
| { | ||
| if (!TryGetValue(key, out Type? value)) | ||
| { | ||
| ThrowHelper.ThrowKeyNotFoundException(key); | ||
| } | ||
| return value; | ||
| } | ||
| } | ||
|
|
||
| public override bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) | ||
| { | ||
| var lookup = table.Lookup(TypeHashingAlgorithms.ComputeNameHashCode(key)); | ||
| NativeParser entryParser; | ||
| while (!(entryParser = lookup.GetNext()).IsNull) | ||
| { | ||
| if (entryParser.StringEquals(key)) | ||
| { | ||
| entryParser.SkipString(); | ||
| RuntimeTypeHandle typeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); | ||
| value = Type.GetTypeFromHandle(typeHandle)!; | ||
| return true; | ||
| } | ||
| } | ||
| value = null; | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| private sealed class AssociatedTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : TypeMapDictionaryBase<Type> | ||
| { | ||
| public override Type this[Type key] | ||
| { | ||
| get | ||
| { | ||
| if (!TryGetValue(key, out Type? value)) | ||
| { | ||
| ThrowHelper.ThrowKeyNotFoundException(key); | ||
| } | ||
| return value; | ||
| } | ||
| } | ||
|
|
||
| public override bool TryGetValue(Type key, [MaybeNullWhen(false)] out Type value) | ||
| { | ||
| RuntimeTypeHandle handle = key.TypeHandle; | ||
| if (handle.IsNull) | ||
| { | ||
| value = null; | ||
| return false; | ||
| } | ||
|
|
||
| var lookup = table.Lookup(handle.GetHashCode()); | ||
| NativeParser entryParser; | ||
| while (!(entryParser = lookup.GetNext()).IsNull) | ||
| { | ||
| RuntimeTypeHandle foundHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); | ||
| if (foundHandle.Equals(handle)) | ||
| { | ||
| RuntimeTypeHandle targetHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); | ||
| value = Type.GetTypeFromHandle(targetHandle)!; | ||
| return true; | ||
| } | ||
| } | ||
| value = null; | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.