1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-10 01:50:53 +09:00

Proper type name parser for native AOT compiler (#83657)

* Proper type name parser for native AOT compiler

* Track failure of CA search rules in the type name parser

* Fix tests

* Suppress new native AOT warning for libraries tests

Co-authored-by: vitek-karas <10670590+vitek-karas@users.noreply.github.com>

Fixes #72833
This commit is contained in:
Jan Kotas 2023-03-30 09:15:58 -07:00 committed by GitHub
parent 6b0a0f3dd4
commit c1049390d5
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 206 additions and 150 deletions

View file

@ -29,7 +29,7 @@
<IlcSdkPath>$(CoreCLRAotSdkDir)</IlcSdkPath>
<IlcFrameworkPath>$(NetCoreAppCurrentTestHostSharedFrameworkPath)</IlcFrameworkPath>
<IlcFrameworkNativePath>$(NetCoreAppCurrentTestHostSharedFrameworkPath)</IlcFrameworkNativePath>
<NoWarn>$(NoWarn);IL1005;IL3000;IL3001;IL3002;IL3003</NoWarn>
<NoWarn>$(NoWarn);IL1005;IL2105;IL3000;IL3001;IL3002;IL3003</NoWarn>
<TrimMode>partial</TrimMode>
<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
<SuppressAotAnalysisWarnings>true</SuppressAotAnalysisWarnings>

View file

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
@ -70,7 +71,7 @@ namespace ILCompiler.Dataflow
MarkEvent(origin, @event, reason);
break;
// case InterfaceImplementation
// Nothing to do currently as Native AOT will presere all interfaces on a preserved type
// Nothing to do currently as Native AOT will preserve all interfaces on a preserved type
}
}
@ -78,19 +79,26 @@ namespace ILCompiler.Dataflow
{
ModuleDesc? callingModule = ((diagnosticContext.Origin.MemberDefinition as MethodDesc)?.OwningType as MetadataType)?.Module;
// NativeAOT doesn't have a fully capable type name resolver yet
// Once this is implemented don't forget to wire up marking of type forwards which are used in generic parameters
if (!DependencyAnalysis.ReflectionMethodBodyScanner.ResolveType(typeName, callingModule, diagnosticContext.Origin.MemberDefinition!.Context, out TypeDesc foundType, out ModuleDesc referenceModule))
List<ModuleDesc> referencedModules = new();
TypeDesc foundType = System.Reflection.TypeNameParser.ResolveType(typeName, callingModule, diagnosticContext.Origin.MemberDefinition!.Context,
referencedModules, out bool typeWasNotFoundInAssemblyNorBaseLibrary);
if (foundType == null)
{
if (needsAssemblyName && typeWasNotFoundInAssemblyNorBaseLibrary)
diagnosticContext.AddDiagnostic(DiagnosticId.TypeWasNotFoundInAssemblyNorBaseLibrary, typeName);
type = default;
return false;
}
if (_enabled)
{
foreach (ModuleDesc referencedModule in referencedModules)
{
// Also add module metadata in case this reference was through a type forward
if (Factory.MetadataManager.CanGenerateMetadata(referenceModule.GetGlobalModuleType()))
_dependencies.Add(Factory.ModuleMetadata(referenceModule), reason);
if (Factory.MetadataManager.CanGenerateMetadata(referencedModule.GetGlobalModuleType()))
_dependencies.Add(Factory.ModuleMetadata(referencedModule), reason);
}
MarkType(diagnosticContext.Origin, foundType, reason);
}

View file

@ -1,99 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Internal.TypeSystem;
using AssemblyName = System.Reflection.AssemblyName;
using StringBuilder = System.Text.StringBuilder;
namespace ILCompiler.DependencyAnalysis
{
internal static class ReflectionMethodBodyScanner
{
public static bool ResolveType(string name, ModuleDesc callingModule, TypeSystemContext context, out TypeDesc type, out ModuleDesc referenceModule)
{
// This can do enough resolution to resolve "Foo" or "Foo, Assembly, PublicKeyToken=...".
// The reflection resolution rules are complicated. This is only needed for a heuristic,
// not for correctness, so this shortcut is okay.
type = null;
int i = 0;
// Consume type name part
StringBuilder typeName = new StringBuilder();
StringBuilder typeNamespace = new StringBuilder();
string containingTypeName = null;
while (i < name.Length && (char.IsLetterOrDigit(name[i]) || name[i] == '.' || name[i] == '_' || name[i] == '`' || name[i] == '+'))
{
if (name[i] == '.')
{
if (typeNamespace.Length > 0)
typeNamespace.Append('.');
typeNamespace.Append(typeName);
typeName.Clear();
}
else if (name[i] == '+')
{
containingTypeName = typeName.ToString();
typeName.Clear();
}
else
{
typeName.Append(name[i]);
}
i++;
}
string nestedTypeName = null;
if (containingTypeName != null)
{
nestedTypeName = typeName.ToString();
typeName = new StringBuilder(containingTypeName);
}
// Consume any comma or white space
while (i < name.Length && (name[i] == ' ' || name[i] == ','))
{
i++;
}
// Consume assembly name
StringBuilder assemblyName = new StringBuilder();
while (i < name.Length && (char.IsLetterOrDigit(name[i]) || name[i] == '.' || name[i] == '_'))
{
assemblyName.Append(name[i]);
i++;
}
// If the name was assembly-qualified, resolve the assembly
// If it wasn't qualified, we resolve in the calling assembly
referenceModule = callingModule;
if (assemblyName.Length > 0)
{
referenceModule = context.ResolveAssembly(new AssemblyName(assemblyName.ToString()), false);
}
if (referenceModule == null)
return false;
// Resolve type in the assembly
MetadataType mdType = referenceModule.GetType(typeNamespace.ToString(), typeName.ToString(), throwIfNotFound: false);
if (mdType != null && nestedTypeName != null)
mdType = mdType.GetNestedType(nestedTypeName);
// If it didn't resolve and wasn't assembly-qualified, we also try core library
if (mdType == null && assemblyName.Length == 0)
{
referenceModule = context.SystemModule;
mdType = referenceModule.GetType(typeNamespace.ToString(), typeName.ToString(), throwIfNotFound: false);
if (mdType != null && nestedTypeName != null)
mdType = mdType.GetNestedType(nestedTypeName);
}
type = mdType;
return type != null;
}
}
}

View file

@ -0,0 +1,151 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Internal.TypeSystem;
namespace System.Reflection
{
internal unsafe ref partial struct TypeNameParser
{
private TypeSystemContext _context;
private ModuleDesc _callingModule;
private List<ModuleDesc> _referencedModules;
private bool _typeWasNotFoundInAssemblyNorBaseLibrary;
public static TypeDesc ResolveType(string name, ModuleDesc callingModule,
TypeSystemContext context, List<ModuleDesc> referencedModules, out bool typeWasNotFoundInAssemblyNorBaseLibrary)
{
var parser = new System.Reflection.TypeNameParser(name)
{
_context = context,
_callingModule = callingModule,
_referencedModules = referencedModules
};
TypeDesc result = parser.Parse()?.Value;
typeWasNotFoundInAssemblyNorBaseLibrary = parser._typeWasNotFoundInAssemblyNorBaseLibrary;
return result;
}
private sealed class Type
{
public Type(TypeDesc type) => Value = type;
public TypeDesc Value { get; }
public Type MakeArrayType() => new Type(Value.MakeArrayType());
public Type MakeArrayType(int rank) => new Type(Value.MakeArrayType(rank));
public Type MakePointerType() => new Type(Value.MakePointerType());
public Type MakeByRefType() => new Type(Value.MakeByRefType());
public Type MakeGenericType(Type[] typeArguments)
{
TypeDesc[] instantiation = new TypeDesc[typeArguments.Length];
for (int i = 0; i < typeArguments.Length; i++)
instantiation[i] = typeArguments[i].Value;
return new Type(((MetadataType)Value).MakeInstantiatedType(instantiation));
}
}
private static bool CheckTopLevelAssemblyQualifiedName() => true;
private Type GetType(string typeName, ReadOnlySpan<string> nestedTypeNames, string assemblyNameIfAny)
{
ModuleDesc module;
if (assemblyNameIfAny != null)
{
module = (TryParseAssemblyName(assemblyNameIfAny) is AssemblyName an) ?
_context.ResolveAssembly(an, throwIfNotFound: false) : null;
}
else
{
module = _callingModule;
}
Type type;
if (module != null)
{
type = GetTypeCore(module, typeName, nestedTypeNames);
if (type != null)
{
_referencedModules?.Add(module);
return type;
}
}
// If it didn't resolve and wasn't assembly-qualified, we also try core library
if (assemblyNameIfAny == null)
{
type = GetTypeCore(_context.SystemModule, typeName, nestedTypeNames);
if (type != null)
{
_referencedModules?.Add(_context.SystemModule);
return type;
}
_typeWasNotFoundInAssemblyNorBaseLibrary = true;
}
return null;
}
private static AssemblyName TryParseAssemblyName(string assemblyName)
{
try
{
return new AssemblyName(assemblyName);
}
catch (FileLoadException)
{
return null;
}
catch (ArgumentException)
{
return null;
}
}
private static Type GetTypeCore(ModuleDesc module, string typeName, ReadOnlySpan<string> nestedTypeNames)
{
string typeNamespace, name;
int separator = typeName.LastIndexOf('.');
if (separator <= 0)
{
typeNamespace = "";
name = typeName;
}
else
{
if (typeName[separator - 1] == '.')
separator--;
typeNamespace = typeName.Substring(0, separator);
name = typeName.Substring(separator + 1);
}
MetadataType type = module.GetType(typeNamespace, name, throwIfNotFound: false);
if (type == null)
return null;
for (int i = 0; i < nestedTypeNames.Length; i++)
{
type = type.GetNestedType(nestedTypeNames[i]);
if (type == null)
return null;
}
return new Type(type);
}
private static void ParseError()
{
}
}
}

View file

@ -30,6 +30,12 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\libraries\Common\src\System\Reflection\TypeNameParser.cs">
<Link>TypeNameParser.cs</Link>
</Compile>
<Compile Include="..\..\..\..\libraries\Common\src\System\Text\ValueStringBuilder.cs">
<Link>ValueStringBuilder.cs</Link>
</Compile>
<Compile Include="..\..\Common\TypeSystem\IL\DelegateInfo.cs">
<Link>IL\DelegateInfo.cs</Link>
</Compile>
@ -406,7 +412,6 @@
<Compile Include="Compiler\DependencyAnalysis\ReflectedFieldNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReflectedTypeNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReflectionInvokeSupportDependencyAlgorithm.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReflectionMethodBodyScanner.cs" />
<Compile Include="Compiler\DependencyAnalysis\StructMarshallingDataNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_ARM64\ARM64TentativeMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_ARM\ARMTentativeMethodNode.cs" />
@ -416,6 +421,7 @@
<Compile Include="Compiler\DependencyAnalysis\TentativeInstanceMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\TentativeMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\TrimmingDescriptorNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\TypeNameParser.cs" />
<Compile Include="Compiler\DependencyAnalysis\VariantInterfaceMethodUseNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\CustomAttributeBasedDependencyAlgorithm.cs" />
<Compile Include="Compiler\DependencyAnalysis\FieldMetadataNode.cs" />

View file

@ -83,29 +83,22 @@ namespace System.Net.Http
// the latter can't in turn have an explicit dependency on the former.
// Get the relevant types needed.
Type? socketsHttpHandlerType = Type.GetType("System.Net.Http.SocketsHttpHandler, System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
Type? httpMessageHandlerType = Type.GetType("System.Net.Http.HttpMessageHandler, System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
Type? httpClientType = Type.GetType("System.Net.Http.HttpClient, System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
Type? httpRequestMessageType = Type.GetType("System.Net.Http.HttpRequestMessage, System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
Type? httpResponseMessageType = Type.GetType("System.Net.Http.HttpResponseMessage, System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
Type? httpResponseHeadersType = Type.GetType("System.Net.Http.Headers.HttpResponseHeaders, System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
Type? httpContentType = Type.GetType("System.Net.Http.HttpContent, System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
Type? socketsHttpHandlerType = Type.GetType("System.Net.Http.SocketsHttpHandler, System.Net.Http", throwOnError: false);
Type? httpMessageHandlerType = Type.GetType("System.Net.Http.HttpMessageHandler, System.Net.Http", throwOnError: false);
Type? httpClientType = Type.GetType("System.Net.Http.HttpClient, System.Net.Http", throwOnError: false);
Type? httpRequestMessageType = Type.GetType("System.Net.Http.HttpRequestMessage, System.Net.Http", throwOnError: false);
Type? httpResponseMessageType = Type.GetType("System.Net.Http.HttpResponseMessage, System.Net.Http", throwOnError: false);
Type? httpResponseHeadersType = Type.GetType("System.Net.Http.Headers.HttpResponseHeaders, System.Net.Http", throwOnError: false);
Type? httpContentType = Type.GetType("System.Net.Http.HttpContent, System.Net.Http", throwOnError: false);
Type? taskOfHttpResponseMessageType = Type.GetType("System.Threading.Tasks.Task`1[[System.Net.Http.HttpResponseMessage, System.Net.Http]], System.Runtime", throwOnError: false);
if (socketsHttpHandlerType == null || httpMessageHandlerType == null || httpClientType == null || httpRequestMessageType == null ||
httpResponseMessageType == null || httpResponseHeadersType == null || httpContentType == null)
httpResponseMessageType == null || httpResponseHeadersType == null || httpContentType == null || taskOfHttpResponseMessageType == null)
{
Debug.Fail("Unable to load required type.");
return null;
}
// Workaround until https://github.com/dotnet/runtime/issues/72833 is fixed
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
Justification = "The type HttpResponseMessage is a reference type")]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
static Type GetTaskOfHttpResponseMessageType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? httpResponseMessageType) => typeof(Task<>).MakeGenericType(httpResponseMessageType!);
Type taskOfHttpResponseMessageType = GetTaskOfHttpResponseMessageType(httpResponseMessageType);
// Get the methods on those types.
ConstructorInfo? socketsHttpHandlerCtor = socketsHttpHandlerType.GetConstructor(Type.EmptyTypes);
PropertyInfo? pooledConnectionIdleTimeoutProp = socketsHttpHandlerType.GetProperty("PooledConnectionIdleTimeout");

View file

@ -6,6 +6,8 @@ using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
#nullable enable
namespace System.Reflection
{
//
@ -622,10 +624,12 @@ namespace System.Reflection
return fullName;
}
#if SYSTEM_PRIVATE_CORELIB
private void ParseError()
{
if (_throwOnError)
throw new ArgumentException(SR.Arg_ArgumentException, $"typeName@{_errorIndex}");
}
#endif
}
}

View file

@ -6,6 +6,8 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#nullable enable
namespace System.Text
{
internal ref partial struct ValueStringBuilder

View file

@ -238,10 +238,10 @@ namespace System.Reflection.Tests
[ActiveIssue("https://github.com/mono/mono/issues/15318", TestRuntimes.Mono)]
public void Invoke_LargeDimensionalArrayConstructor()
{
Type type = Type.GetType("System.Type[,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]");
Type type = Type.GetType($"System.Type[{new string(',', 31)}]");
ConstructorInfo[] cia = TypeExtensions.GetConstructors(type);
Assert.Equal(2, cia.Length);
Assert.Throws<TypeLoadException>(() => Type.GetType("System.Type[,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]"));
Assert.Throws<TypeLoadException>(() => Type.GetType($"System.Type[{new string(',', 42)}]"));
}
[Fact]

View file

@ -82,6 +82,10 @@ namespace System.Reflection.Tests
[InlineData(typeof(string), new Type[] { typeof(char), typeof(int) })]
public void Properties(Type type, Type[] typeParameters)
{
// Trick trimming into keeping the string ctor
if (string.Empty.Length > 0)
typeof(string).GetConstructors();
ConstructorInfo constructor = TypeExtensions.GetConstructor(type, typeParameters);
Assert.Equal(type, constructor.DeclaringType);

View file

@ -128,9 +128,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
// https://github.com/dotnet/runtime/issues/72833
// NativeAOT doesn't implement full type name parser yet
[Kept (By = Tool.Trimmer)]
[Kept]
class FromStringConstantWithGenericInner
{
}
@ -143,9 +141,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
public T GetValue () { return default (T); }
}
// https://github.com/dotnet/runtime/issues/72833
// NativeAOT doesn't implement full type name parser yet
[Kept (By = Tool.Trimmer)]
[Kept]
class FromStringConstantWithGenericInnerInner
{
[Kept (By = Tool.Trimmer)]
@ -156,7 +152,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
int unusedField;
}
[Kept (By = Tool.Trimmer)]
[Kept]
class FromStringConstantWithGenericInnerOne<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
[KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute), By = Tool.Trimmer)]
@ -164,9 +160,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
// https://github.com/dotnet/runtime/issues/72833
// NativeAOT doesn't implement full type name parser yet
[Kept (By = Tool.Trimmer)]
[Kept]
class FromStringConstantWithGenericInnerTwo
{
void UnusedMethod ()
@ -174,22 +168,14 @@ namespace Mono.Linker.Tests.Cases.DataFlow
}
}
// https://github.com/dotnet/runtime/issues/72833
// NativeAOT doesn't implement full type name parser yet
[Kept (By = Tool.Trimmer)]
[Kept]
class FromStringConstantWitGenericInnerMultiDimArray
{
}
// https://github.com/dotnet/runtime/issues/72833
// NativeAOT actually preserves this, but for a slightly wrong reason - it completely ignores the array notations
[Kept]
[KeptMember (".ctor()", By = Tool.NativeAot)]
class FromStringConstantWithMultiDimArray
{
// https://github.com/dotnet/runtime/issues/72833
// NativeAOT actually preserves this, but for a slightly wrong reason - it completely ignores the array notations
[Kept (By = Tool.NativeAot)]
public void UnusedMethod () { }
}
@ -200,6 +186,8 @@ namespace Mono.Linker.Tests.Cases.DataFlow
}
[Kept]
[KeptAttributeAttribute(typeof(UnconditionalSuppressMessageAttribute))]
[UnconditionalSuppressMessage("test", "IL3050", Justification = "The test applies DAM on System.Array, which contains CreateInstance method which has RDC on it.")]
static void TestFromStringConstantWithGeneric ()
{
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGeneric`1[[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInner]]");

View file

@ -60,11 +60,10 @@ namespace Mono.Linker.Tests.Cases.DataFlow
RequireNothing (type);
}
// NativeAOT doesn't implement this yet: https://github.com/dotnet/runtime/issues/72833
[ExpectedWarning ("IL2105",
"Type 'System.Invalid.TypeName' was not found in the caller assembly nor in the base library. " +
"Type name strings used for dynamically accessing a type should be assembly qualified.",
ProducedBy = Tool.Trimmer)]
ProducedBy = Tool.Trimmer | Tool.NativeAot)]
static void TestUnqualifiedTypeNameWarns ()
{
RequirePublicConstructors ("System.Invalid.TypeName");

View file

@ -191,18 +191,18 @@ namespace Mono.Linker.Tests
}
[DisplayName ("Mono.Linker.Tests.GetDisplayNameTests.MethodWithGenericTypeArgument(IList<GetDisplayNameTests.GenericClassOneParameter<Byte*[]>>)")]
public static void MethodWithGenericTypeArgument (IList<GenericClassOneParameter<byte*[]>> p)
public static unsafe void MethodWithGenericTypeArgument (IList<GenericClassOneParameter<byte*[]>> p)
{
}
[DisplayName ("Mono.Linker.Tests.GetDisplayNameTests.MethodWithGenericTypeArguments(GetDisplayNameTests.GenericClassMultipleParameters<Char*[],Int32[,][]>)")]
public static void MethodWithGenericTypeArguments (GenericClassMultipleParameters<char*[], int[,][]> p)
public static unsafe void MethodWithGenericTypeArguments (GenericClassMultipleParameters<char*[], int[,][]> p)
{
}
[DisplayName ("Mono.Linker.Tests.GetDisplayNameTests.MethodWithNestedGenericTypeArguments" +
"(GetDisplayNameTests.GenericClassMultipleParameters<Char*[],Int32[,][]>.NestedGenericClassMultipleParameters<Char*[],Int32[,][]>)")]
public static void MethodWithNestedGenericTypeArguments (GenericClassMultipleParameters<char*[], int[,][]>.NestedGenericClassMultipleParameters<char*[], int[,][]> p)
public static unsafe void MethodWithNestedGenericTypeArguments (GenericClassMultipleParameters<char*[], int[,][]>.NestedGenericClassMultipleParameters<char*[], int[,][]> p)
{
}