Skip to content

Commit

Permalink
Proper type name parser for native AOT compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
jkotas committed Mar 25, 2023
1 parent 91b93eb commit e33895c
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -78,19 +79,22 @@ internal bool TryResolveTypeNameAndMark(string typeName, in DiagnosticContext di
{
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);
if (foundType == null)
{
type = default;
return false;
}

if (_enabled)
{
// 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);
foreach (ModuleDesc referencedModule in referencedModules)
{
// Also add module metadata in case this reference was through a type forward
if (Factory.MetadataManager.CanGenerateMetadata(referencedModule.GetGlobalModuleType()))
_dependencies.Add(Factory.ModuleMetadata(referencedModule), reason);
}

MarkType(diagnosticContext.Origin, foundType, reason);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// 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;

public static TypeDesc ResolveType(string name, ModuleDesc callingModule, TypeSystemContext context, List<ModuleDesc> referencedModules)
{
return new System.Reflection.TypeNameParser(name)
{
_context = context,
_callingModule = callingModule,
_referencedModules = referencedModules
}.Parse()?.Value;
}

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 = null;

if (assemblyNameIfAny != null)
{
AssemblyName an = TryParseAssemblyName(assemblyNameIfAny);
if (an != null)
module = _context.ResolveAssembly(an, throwIfNotFound: false);
}
else
{
module = _callingModule;
}

if (module == null)
return null;

Type 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;
}
}

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()
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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" />
Expand All @@ -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" />
Expand Down
25 changes: 9 additions & 16 deletions src/libraries/Common/src/System/Net/Http/X509ResourceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,29 +83,22 @@ internal static partial class X509ResourceClient
// 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");
Expand Down
4 changes: 4 additions & 0 deletions src/libraries/Common/src/System/Reflection/TypeNameParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Runtime.InteropServices;
using System.Text;

#nullable enable

namespace System.Reflection
{
//
Expand Down Expand Up @@ -622,10 +624,12 @@ private static string EscapeTypeName(string typeName, ReadOnlySpan<string> neste
return fullName;
}

#if SYSTEM_PRIVATE_CORELIB
private void ParseError()
{
if (_throwOnError)
throw new ArgumentException(SR.Arg_ArgumentException, $"typeName@{_errorIndex}");
}
#endif
}
}
2 changes: 2 additions & 0 deletions src/libraries/Common/src/System/Text/ValueStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

#nullable enable

namespace System.Text
{
internal ref partial struct ValueStringBuilder
Expand Down
Loading

0 comments on commit e33895c

Please sign in to comment.