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

TypeName parsing API #100094

Merged
merged 52 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
22d131e
TypeNameParser first ref and configurable input validation
adamsitnik Jan 23, 2024
5de9526
assembly name parsing
adamsitnik Jan 24, 2024
6dbcd59
initial generic type info parsing
adamsitnik Jan 25, 2024
6f1f161
decorator parsing
adamsitnik Jan 26, 2024
4ec4ea5
Handle single dimensional array indexing
adamsitnik Jan 29, 2024
f5a8716
implement TypeName.GetType
adamsitnik Jan 29, 2024
52cb351
nested types support
adamsitnik Jan 30, 2024
60ad6f0
support ignore case
adamsitnik Jan 31, 2024
b5349bd
integrate with System.Private.CoreLib:
adamsitnik Jan 31, 2024
2dbd091
integrate with System.Private.CoreLib:
adamsitnik Feb 1, 2024
bd78637
integrate with System.Private.CoreLib for Mono and clr tools, improve…
adamsitnik Feb 2, 2024
8fda94a
build fix 1/n
adamsitnik Feb 2, 2024
745e7bb
make TypeNameParser internal, extend TypeName with Parse and TryParse…
adamsitnik Feb 7, 2024
ee43e71
introduce TotalComplexity
adamsitnik Feb 7, 2024
a3a7f26
introduce FullName, so we have Name, FullName and AssemblyQualifiedNa…
adamsitnik Feb 7, 2024
a2296d0
back tick error handling
adamsitnik Feb 9, 2024
4c8a6f7
move helper methods to a standalone helper type, include it as a link…
adamsitnik Feb 9, 2024
eadf970
increase test coverage, improve edge case handling
adamsitnik Feb 12, 2024
abf7543
sample SerializationBinder that uses the new APIs
adamsitnik Feb 12, 2024
281c4f3
cover more serialization binder scenarios with the tests to ensure th…
adamsitnik Feb 13, 2024
dc58cce
strict mode parsing: type names
adamsitnik Feb 19, 2024
b947977
strict mode parsing: assembly names
adamsitnik Feb 20, 2024
4c9a7d5
Merge remote-tracking branch 'upstream/main' into typeNameParser
adamsitnik Feb 22, 2024
c63f3a8
fix the build and apply some design changes
adamsitnik Feb 22, 2024
83cdd1b
add escaping support
adamsitnik Feb 22, 2024
23ad44f
fix the last failing tests, increase test coverage, fix the perf, fix…
adamsitnik Feb 23, 2024
96b04f4
Merge remote-tracking branch 'upstream/main' into typeNameParser
adamsitnik Mar 19, 2024
c8014fd
apply changes based on the 1st API Design Review
adamsitnik Mar 19, 2024
1c76366
apply changes based on the final API Design Review
adamsitnik Mar 20, 2024
8cd205a
solve the TODOs
adamsitnik Mar 20, 2024
ae514ec
implement IEquatable, add missing exception messages, set default Max…
adamsitnik Mar 21, 2024
c7c67c8
address code review feedback, make some tests conditional, remove inv…
adamsitnik Mar 22, 2024
87804e9
Apply suggestions from code review
adamsitnik Mar 22, 2024
97aad27
supress IL3050:RequiresDynamicCode
adamsitnik Mar 22, 2024
f940723
remove everything related to strict parsing (it will come back in a s…
adamsitnik Mar 22, 2024
b1ed250
remove special handling for Array.MaxLength-many generic args
adamsitnik Mar 22, 2024
90a5582
remove the unused property, use the new names for non-public APIs as …
adamsitnik Mar 22, 2024
9a5d01b
make the allocations happen when they are actually needed for the fir…
adamsitnik Apr 2, 2024
cfb2216
don't pre-allocate full names for all declaring types, just store the…
adamsitnik Apr 3, 2024
d63365f
build fix
adamsitnik Apr 3, 2024
7d685c8
Merge remote-tracking branch 'upstream/main' into typeNameParser
adamsitnik Apr 9, 2024
04731fb
address part of the code review feedback
adamsitnik Apr 9, 2024
113a87f
fix the build
adamsitnik Apr 11, 2024
7005164
AssemblyNameInfo
adamsitnik Apr 15, 2024
cd8a9c2
don't enforce the back tick convention
adamsitnik Apr 16, 2024
1143a3e
Merge remote-tracking branch 'upstream/main' into typeNameParser
adamsitnik Apr 17, 2024
afdbc5f
address API and code review feedback:
adamsitnik Apr 17, 2024
413be9e
minor tweaks after reading the code again
adamsitnik Apr 17, 2024
b424d76
try to improve the escaping handling
adamsitnik Apr 18, 2024
da203c0
address code review feedback:
adamsitnik Apr 23, 2024
7979e27
address code review feedback:
adamsitnik Apr 24, 2024
22de761
Apply suggestions from code review
adamsitnik Apr 24, 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
Prev Previous commit
Next Next commit
integrate with System.Private.CoreLib for Mono and clr tools, improve…
… perf
  • Loading branch information
adamsitnik committed Feb 2, 2024
commit bd78637c438215cda700e2e0faef5b0cd7dc329d
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,18 @@ internal partial struct TypeNameParser
public static TypeDesc ResolveType(ModuleDesc module, string name, bool throwIfNotFound,
Func<ModuleDesc, string, MetadataType> canonResolver)
{
return new TypeNameParser(name.AsSpan())
var parsed = Metadata.TypeNameParser.Parse(name.AsSpan(), throwOnError: false);
if (parsed is null)
{
ThrowHelper.ThrowTypeLoadException(name, module);
}

return new TypeNameParser()
{
_module = module,
_throwIfNotFound = throwIfNotFound,
_canonResolver = canonResolver
}.Parse()?.Value;
}.Resolve(parsed)?.Value;
}

private sealed class Type
Expand All @@ -64,12 +70,10 @@ public Type MakeGenericType(Type[] typeArguments)
}
}

private static bool CheckTopLevelAssemblyQualifiedName() => true;

private Type GetType(string typeName, ReadOnlySpan<string> nestedTypeNames, string assemblyNameIfAny)
private Type GetType(string typeName, ReadOnlySpan<string> nestedTypeNames, AssemblyName assemblyNameIfAny)
{
ModuleDesc module = (assemblyNameIfAny == null) ? _module :
_module.Context.ResolveAssembly(new AssemblyName(assemblyNameIfAny), throwIfNotFound: _throwIfNotFound);
_module.Context.ResolveAssembly(assemblyNameIfAny, throwIfNotFound: _throwIfNotFound);

if (_canonResolver != null && nestedTypeNames.IsEmpty)
{
Expand Down Expand Up @@ -115,10 +119,5 @@ private static Type GetTypeCore(ModuleDesc module, string typeName, ReadOnlySpan

return new Type(type);
}

private void ParseError()
{
ThrowHelper.ThrowTypeLoadException(_input.ToString(), _module);
}
}
}
17 changes: 16 additions & 1 deletion src/coreclr/tools/ILVerification/ILVerification.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,24 @@
<Compile Include="$(ToolsCommonPath)TypeSystem\Common\Utilities\CustomAttributeTypeNameParser.cs">
<Link>Utilities\CustomAttributeTypeNameParser.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\TypeNameParser.cs">
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\HexConverter.cs">
<Link>Utilities\HexConverter.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\AssemblyNameParser.cs">
<Link>Utilities\AssemblyNameParser.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\Metadata\TypeName.cs">
<Link>Utilities\TypeName.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\Metadata\TypeNameParserOptions.cs">
<Link>Utilities\TypeNameParserOptions.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\Metadata\TypeNameParser.cs">
<Link>Utilities\TypeNameParser.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\TypeNameParser.Helpers.cs">
<Link>Utilities\CustomAttributeTypeNameParser.Helpers</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Text\ValueStringBuilder.cs">
<Link>Utilities\ValueStringBuilder.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
// 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
Expand All @@ -20,14 +16,21 @@ internal partial struct TypeNameParser
public static TypeDesc ResolveType(string name, ModuleDesc callingModule,
TypeSystemContext context, List<ModuleDesc> referencedModules, out bool typeWasNotFoundInAssemblyNorBaseLibrary)
{
var parser = new TypeNameParser(name)
var parsed = Metadata.TypeNameParser.Parse(name, throwOnError: false);
if (parsed is null) // TODO adsitnik: verify that this is desired
{
typeWasNotFoundInAssemblyNorBaseLibrary = true;
return null;
}

var parser = new TypeNameParser()
{
_context = context,
_callingModule = callingModule,
_referencedModules = referencedModules
};

TypeDesc result = parser.Parse()?.Value;
TypeDesc result = parser.Resolve(parsed)?.Value;

typeWasNotFoundInAssemblyNorBaseLibrary = parser._typeWasNotFoundInAssemblyNorBaseLibrary;
return result;
Expand All @@ -52,16 +55,13 @@ public Type MakeGenericType(Type[] typeArguments)
}
}

private static bool CheckTopLevelAssemblyQualifiedName() => true;

private Type GetType(string typeName, ReadOnlySpan<string> nestedTypeNames, string assemblyNameIfAny)
private Type GetType(string typeName, ReadOnlySpan<string> nestedTypeNames, AssemblyName assemblyNameIfAny)
{
ModuleDesc module;

if (assemblyNameIfAny != null)
{
module = (TryParseAssemblyName(assemblyNameIfAny) is AssemblyName an) ?
_context.ResolveAssembly(an, throwIfNotFound: false) : null;
module = _context.ResolveAssembly(assemblyNameIfAny, throwIfNotFound: false);
}
else
{
Expand Down Expand Up @@ -94,22 +94,6 @@ private Type GetType(string typeName, ReadOnlySpan<string> nestedTypeNames, stri
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, string name) = SplitFullTypeName(typeName);
Expand All @@ -127,9 +111,5 @@ private static Type GetTypeCore(ModuleDesc module, string typeName, ReadOnlySpan

return new Type(type);
}

private static void ParseError()
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,8 @@
<Compile Include="Compiler\Dataflow\TrimAnalysisPatternStore.cs" />
<Compile Include="Compiler\Dataflow\TrimAnalysisTokenAccessPattern.cs" />
<Compile Include="Compiler\Dataflow\TypeNameParser.Dataflow.cs" />
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\TypeNameParser.cs">
<Link>Compiler\Dataflow\TypeNameParser.cs</Link>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\TypeNameParser.Helpers.cs">
<Link>Compiler\Dataflow\TypeNameParser.Helpers.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Text\ValueStringBuilder.cs">
<Link>Utilities\ValueStringBuilder.cs</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,24 @@
<Compile Include="..\..\Common\TypeSystem\Common\Utilities\CustomAttributeTypeNameParser.cs">
<Link>Utilities\CustomAttributeTypeNameParser.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\TypeNameParser.cs">
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\HexConverter.cs">
<Link>Utilities\HexConverter.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\AssemblyNameParser.cs">
<Link>Utilities\AssemblyNameParser.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\Metadata\TypeName.cs">
<Link>Utilities\TypeName.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\Metadata\TypeNameParserOptions.cs">
<Link>Utilities\TypeNameParserOptions.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\Metadata\TypeNameParser.cs">
<Link>Utilities\TypeNameParser.cs</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Reflection\TypeNameParser.Helpers.cs">
<Link>Utilities\CustomAttributeTypeNameParser.Helpers</Link>
</Compile>
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Text\ValueStringBuilder.cs">
<Link>Utilities\ValueStringBuilder.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Runtime.CompilerServices;
using System.Text;

#nullable enable

namespace System.Reflection
{
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using System.Diagnostics.CodeAnalysis;

#nullable enable

namespace System.Reflection.Metadata
{
#if SYSTEM_PRIVATE_CORELIB
Expand All @@ -20,6 +22,7 @@ sealed class TypeName
// Negative value is modifier encoded using constants above.
private readonly int _rankOrModifier;
private readonly TypeName[]? _genericArguments;
private string? _assemblyQualifiedName;

internal TypeName(string name, AssemblyName? assemblyName, int rankOrModifier,
TypeName? underlyingType = default,
Expand All @@ -32,7 +35,6 @@ internal TypeName(string name, AssemblyName? assemblyName, int rankOrModifier,
UnderlyingType = underlyingType;
ContainingType = containingType;
_genericArguments = genericTypeArguments;
AssemblyQualifiedName = assemblyName is null ? name : $"{name}, {assemblyName.FullName}";
}

/// <summary>
Expand All @@ -41,7 +43,8 @@ internal TypeName(string name, AssemblyName? assemblyName, int rankOrModifier,
/// <remarks>
/// If <see cref="AssemblyName"/> is null, simply returns <see cref="Name"/>.
/// </remarks>
public string AssemblyQualifiedName { get; }
public string AssemblyQualifiedName
=> _assemblyQualifiedName ??= AssemblyName is null ? Name : $"{Name}, {AssemblyName.FullName}";

/// <summary>
/// The assembly which contains this type, or null if this <see cref="TypeName"/> was not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.Diagnostics;
using System.Text;

#nullable enable

namespace System.Reflection.Metadata
{
#if SYSTEM_PRIVATE_CORELIB
Expand All @@ -22,6 +24,7 @@ ref struct TypeNameParser
#if NET8_0_OR_GREATER
private static readonly SearchValues<char> _endOfTypeNameDelimitersSearchValues = SearchValues.Create(EndOfTypeNameDelimiters);
#endif
private static readonly TypeNameParserOptions _defaults = new();
private readonly bool _throwOnError;
private readonly TypeNameParserOptions _parseOptions;
private ReadOnlySpan<char> _inputString;
Expand All @@ -30,7 +33,7 @@ private TypeNameParser(ReadOnlySpan<char> name, bool throwOnError, TypeNameParse
{
_inputString = name;
_throwOnError = throwOnError;
_parseOptions = options ?? new();
_parseOptions = options ?? _defaults;
}

public static TypeName? Parse(ReadOnlySpan<char> typeName, bool allowFullyQualifiedName = true, bool throwOnError = true, TypeNameParserOptions? options = default)
Expand Down Expand Up @@ -314,8 +317,13 @@ private bool TryParseAssemblyName(ref AssemblyName? assemblyName)
return false;
}

#if SYSTEM_PRIVATE_CORELIB
assemblyName = new();
assemblyName.Init(parts);
#else
// TODO: fix the perf and avoid doing it twice (missing public ctors for System.Reflection.Metadata)
assemblyName = new(candidate.ToString());
#endif
_inputString = _inputString.Slice(assemblyNameLength);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ private static string EscapeTypeName(string typeName, ReadOnlySpan<string> neste
return fullName;
}

private static (string typeNamespace, string name) SplitFullTypeName(string typeName)
{
string typeNamespace, name;

// Matches algorithm from ns::FindSep in src\coreclr\utilcode\namespaceutil.cpp
adamsitnik marked this conversation as resolved.
Show resolved Hide resolved
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);
}

return (typeNamespace, name);
}

private Type? Resolve(Metadata.TypeName typeName)
{
if (typeName.IsNestedType)
Expand Down Expand Up @@ -88,8 +110,10 @@ private static string EscapeTypeName(string typeName, ReadOnlySpan<string> neste
return Make(Resolve(typeName.UnderlyingType), typeName);
}

#if !NETSTANDARD2_0 // needed for ILVerification project
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:UnrecognizedReflectionPattern",
Justification = "Used to implement resolving types from strings.")]
#endif
private Type? Make(Type? type, Metadata.TypeName typeName)
{
if (type is null || typeName.IsElementalType)
Expand Down
Loading
Loading