From f9b9d91397ae9cfaa10df2678601d13eea4b9114 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 20 Jun 2024 01:01:23 +0000 Subject: [PATCH 1/4] Use SRM's TypeName parser in ILLink --- src/tools/illink/external/corert/README.md | 1 - .../Reflection/AssemblyNameFormatter.cs | 156 ------------ .../System/Reflection/AssemblyNameHelpers.cs | 42 ---- .../System/Reflection/AssemblyNameLexer.cs | 123 --------- .../System/Reflection/AssemblyNameParser.cs | 217 ---------------- .../System/Reflection/RuntimeAssemblyName.cs | 123 --------- .../Runtime/TypeParsing/TypeLexer.cs | 238 ------------------ .../Runtime/TypeParsing/TypeName.cs | 219 ---------------- .../Runtime/TypeParsing/TypeParser.cs | 226 ----------------- .../src/linker/Linker.Steps/MarkStep.cs | 11 +- .../illink/src/linker/Linker/LinkContext.cs | 9 +- .../src/linker/Linker/TypeNameResolver.cs | 118 ++++++--- .../illink/src/linker/Mono.Linker.csproj | 8 +- .../Dependencies/EscapedTypeNames.il | 23 ++ .../Reflection/TypeUsedViaReflection.cs | 14 +- .../TestCasesRunner/ResultChecker.cs | 28 ++- .../Tests/TypeNameResolverTests.cs | 88 ------- 17 files changed, 162 insertions(+), 1482 deletions(-) delete mode 100644 src/tools/illink/external/corert/README.md delete mode 100644 src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs delete mode 100644 src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameHelpers.cs delete mode 100644 src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameLexer.cs delete mode 100644 src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs delete mode 100644 src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/RuntimeAssemblyName.cs delete mode 100644 src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs delete mode 100644 src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs delete mode 100644 src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/EscapedTypeNames.il delete mode 100644 src/tools/illink/test/Mono.Linker.Tests/Tests/TypeNameResolverTests.cs diff --git a/src/tools/illink/external/corert/README.md b/src/tools/illink/external/corert/README.md deleted file mode 100644 index babf7d8e26fa08..00000000000000 --- a/src/tools/illink/external/corert/README.md +++ /dev/null @@ -1 +0,0 @@ -The code in this folder was adapted from dotnet/corert commit c8bfca5f4554badfb89b80d2319769f83512bf62 \ No newline at end of file diff --git a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs b/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs deleted file mode 100644 index b8fe76cf2fc468..00000000000000 --- a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs +++ /dev/null @@ -1,156 +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 System.IO; -using System.Text; -using System.Globalization; -using System.Collections.Generic; - -namespace System.Reflection -{ - internal static class AssemblyNameFormatter - { - public static string ComputeDisplayName(RuntimeAssemblyName a) - { - const int PUBLIC_KEY_TOKEN_LEN = 8; - - if (string.IsNullOrEmpty(a.Name)) - throw new FileLoadException(); - - StringBuilder sb = new StringBuilder(); - if (a.Name != null) - { - sb.AppendQuoted(a.Name); - } - - if (a.Version != null) - { - Version canonicalizedVersion = a.Version.CanonicalizeVersion(); - if (canonicalizedVersion.Major != ushort.MaxValue) - { - sb.Append(", Version="); - sb.Append(canonicalizedVersion.Major); - - if (canonicalizedVersion.Minor != ushort.MaxValue) - { - sb.Append('.'); - sb.Append(canonicalizedVersion.Minor); - - if (canonicalizedVersion.Build != ushort.MaxValue) - { - sb.Append('.'); - sb.Append(canonicalizedVersion.Build); - - if (canonicalizedVersion.Revision != ushort.MaxValue) - { - sb.Append('.'); - sb.Append(canonicalizedVersion.Revision); - } - } - } - } - } - - string cultureName = a.CultureName; - if (cultureName != null) - { - if (cultureName.Length == 0) - cultureName = "neutral"; - sb.Append(", Culture="); - sb.AppendQuoted(cultureName); - } - - byte[] pkt = a.PublicKeyOrToken; - if (pkt != null) - { - if (pkt.Length > PUBLIC_KEY_TOKEN_LEN) - throw new ArgumentException("Invalid token length", nameof(a)); - - sb.Append(", PublicKeyToken="); - if (pkt.Length == 0) - sb.Append("null"); - else - { - foreach (byte b in pkt) - { - sb.Append(b.ToString("x2", CultureInfo.InvariantCulture)); - } - } - } - - if (0 != (a.Flags & AssemblyNameFlags.Retargetable)) - sb.Append(", Retargetable=Yes"); - - AssemblyContentType contentType = a.Flags.ExtractAssemblyContentType(); - if (contentType == AssemblyContentType.WindowsRuntime) - sb.Append(", ContentType=WindowsRuntime"); - - // NOTE: By design (desktop compat) AssemblyName.FullName and ToString() do not include ProcessorArchitecture. - - return sb.ToString(); - } - - private static void AppendQuoted(this StringBuilder sb, string s) - { - bool needsQuoting = false; - const char quoteChar = '\"'; - - // App-compat: You can use double or single quotes to quote a name, and Fusion (or rather the IdentityAuthority) picks one - // by some algorithm. Rather than guess at it, we use double quotes consistently. - if (s != s.Trim() || s.Contains('"') || s.Contains('\'')) - needsQuoting = true; - - if (needsQuoting) - sb.Append(quoteChar); - - for (int i = 0; i < s.Length; i++) - { - bool addedEscape = false; - foreach (KeyValuePair kv in EscapeSequences) - { - string escapeReplacement = kv.Value; - if (!(s[i] == escapeReplacement[0])) - continue; - if ((s.Length - i) < escapeReplacement.Length) - continue; - if (s.Substring(i, escapeReplacement.Length).Equals(escapeReplacement)) - { - sb.Append('\\'); - sb.Append(kv.Key); - addedEscape = true; - } - } - - if (!addedEscape) - sb.Append(s[i]); - } - - if (needsQuoting) - sb.Append(quoteChar); - } - - private static Version CanonicalizeVersion(this Version version) - { - ushort major = (ushort)version.Major; - ushort minor = (ushort)version.Minor; - ushort build = (ushort)version.Build; - ushort revision = (ushort)version.Revision; - - if (major == version.Major && minor == version.Minor && build == version.Build && revision == version.Revision) - return version; - - return new Version(major, minor, build, revision); - } - - public static KeyValuePair[] EscapeSequences = - { - new KeyValuePair('\\', "\\"), - new KeyValuePair(',', ","), - new KeyValuePair('=', "="), - new KeyValuePair('\'', "'"), - new KeyValuePair('\"', "\""), - new KeyValuePair('n', Environment.NewLine), - new KeyValuePair('t', "\t"), - }; - } -} diff --git a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameHelpers.cs b/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameHelpers.cs deleted file mode 100644 index d6db056956eb8c..00000000000000 --- a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameHelpers.cs +++ /dev/null @@ -1,42 +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 System; -using System.Globalization; -using System.IO; -using System.Text; -using System.Collections.Generic; - -namespace System.Reflection -{ - public static partial class AssemblyNameHelpers - { - - - // - // These helpers convert between the combined flags+contentType+processorArchitecture value and the separated parts. - // - // Since these are only for trusted callers, they do NOT check for out of bound bits. - // - - internal static AssemblyContentType ExtractAssemblyContentType(this AssemblyNameFlags flags) - { - return (AssemblyContentType)((((int)flags) >> 9) & 0x7); - } - - internal static ProcessorArchitecture ExtractProcessorArchitecture(this AssemblyNameFlags flags) - { - return (ProcessorArchitecture)((((int)flags) >> 4) & 0x7); - } - - public static AssemblyNameFlags ExtractAssemblyNameFlags(this AssemblyNameFlags combinedFlags) - { - return combinedFlags & unchecked((AssemblyNameFlags)0xFFFFF10F); - } - - internal static AssemblyNameFlags CombineAssemblyNameFlags(AssemblyNameFlags flags, AssemblyContentType contentType, ProcessorArchitecture processorArchitecture) - { - return (AssemblyNameFlags)(((int)flags) | (((int)contentType) << 9) | ((int)processorArchitecture << 4)); - } - } -} diff --git a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameLexer.cs b/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameLexer.cs deleted file mode 100644 index 060f605e6a94d7..00000000000000 --- a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameLexer.cs +++ /dev/null @@ -1,123 +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 System.IO; -using System.Text; -using System.Collections.Generic; - -namespace System.Reflection -{ - // - // A simple lexer for assembly display names. - // - internal struct AssemblyNameLexer - { - internal AssemblyNameLexer(string s) - { - // Convert string to char[] with NUL terminator. (An actual NUL terminator in the input string will be treated - // as an actual end of string: this is compatible with desktop behavior.) - char[] chars = new char[s.Length + 1]; - s.CopyTo(0, chars, 0, s.Length); - _chars = chars; - _index = 0; - } - - // - // Return the next token in assembly name. If you expect the result to be DisplayNameToken.String, - // use GetNext(out String) instead. - // - internal Token GetNext() - { - return GetNext(out _); - } - - // - // Return the next token in assembly name. If the result is DisplayNameToken.String, - // sets "tokenString" to the tokenized string. - // - internal Token GetNext(out string? tokenString) - { - tokenString = null; - while (char.IsWhiteSpace(_chars[_index])) - _index++; - - char c = _chars[_index++]; - if (c == 0) - return Token.End; - if (c == ',') - return Token.Comma; - if (c == '=') - return Token.Equals; - - StringBuilder sb = new StringBuilder(); - - char quoteChar = (char)0; - if (c == '\'' || c == '\"') - { - quoteChar = c; - c = _chars[_index++]; - } - - for (; ; ) - { - if (c == 0) - { - _index--; - break; // Terminate: End of string (desktop compat: if string was quoted, permitted to terminate without end-quote.) - } - - if (quoteChar != 0 && c == quoteChar) - break; // Terminate: Found closing quote of quoted string. - - if (quoteChar == 0 && (c == ',' || c == '=')) - { - _index--; - break; // Terminate: Found start of a new ',' or '=' token. - } - - if (quoteChar == 0 && (c == '\'' || c == '\"')) - throw new FileLoadException(); // Desktop compat: Unescaped quote illegal unless entire string is quoted. - - if (c == '\\') - { - c = _chars[_index++]; - bool matched = false; - foreach (KeyValuePair kv in AssemblyNameFormatter.EscapeSequences) - { - if (c == kv.Key) - { - matched = true; - sb.Append(kv.Value); - break; - } - } - if (!matched) - throw new FileLoadException(); // Unrecognized escape - } - else - { - sb.Append(c); - } - - c = _chars[_index++]; - } - - tokenString = sb.ToString(); - if (quoteChar == 0) - tokenString = tokenString.Trim(); // Unless quoted, whitespace at beginning or end doesn't count. - return Token.String; - } - - // Token categories for display name lexer. - internal enum Token - { - Equals = 1, - Comma = 2, - String = 3, - End = 4, - } - - private readonly char[] _chars; - private int _index; - } -} diff --git a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs b/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs deleted file mode 100644 index b7463367819f96..00000000000000 --- a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs +++ /dev/null @@ -1,217 +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 System.IO; -using System.Diagnostics; -using System.Globalization; -using System.Collections.Generic; - -namespace System.Reflection -{ - // - // Parses an assembly name. - // - internal static class AssemblyNameParser - { - internal static RuntimeAssemblyName Parse(string s) - { - Debug.Assert(s != null); - - int indexOfNul = s.IndexOf((char)0); - if (indexOfNul != -1) - s = s.Substring(0, indexOfNul); - if (s.Length == 0) - throw new ArgumentException("Empty string", nameof(s)); - - AssemblyNameLexer lexer = new AssemblyNameLexer(s); - - // Name must come first. - string? name; - AssemblyNameLexer.Token token = lexer.GetNext(out name); - if (token != AssemblyNameLexer.Token.String) - throw new FileLoadException(); - - if (string.IsNullOrEmpty(name) || name.IndexOfAny(s_illegalCharactersInSimpleName) != -1) - throw new FileLoadException(); - - Version? version = null; - string? cultureName = null; - byte[]? pkt = null; - AssemblyNameFlags flags = 0; - - List alreadySeen = new List(); - token = lexer.GetNext(); - while (token != AssemblyNameLexer.Token.End) - { - if (token != AssemblyNameLexer.Token.Comma) - throw new FileLoadException(); - string? attributeName; - token = lexer.GetNext(out attributeName); - if (token != AssemblyNameLexer.Token.String) - throw new FileLoadException(); - token = lexer.GetNext(); - - // Compat note: Inside AppX apps, the desktop CLR's AssemblyName parser skips past any elements that don't follow the "=" pattern. - // (when running classic Windows apps, such an illegal construction throws an exception as expected.) - // Naturally, at least one app unwittingly takes advantage of this. - if (token == AssemblyNameLexer.Token.Comma || token == AssemblyNameLexer.Token.End) - continue; - - if (token != AssemblyNameLexer.Token.Equals) - throw new FileLoadException(); - string? attributeValue; - token = lexer.GetNext(out attributeValue); - if (token != AssemblyNameLexer.Token.String) - throw new FileLoadException(); - - if (string.IsNullOrEmpty(attributeName)) - throw new FileLoadException(); - - for (int i = 0; i < alreadySeen.Count; i++) - { - if (alreadySeen[i].Equals(attributeName, StringComparison.OrdinalIgnoreCase)) - throw new FileLoadException(); // Cannot specify the same attribute twice. - } - Debug.Assert(attributeValue is not null); - alreadySeen.Add(attributeName); - if (attributeName.Equals("Version", StringComparison.OrdinalIgnoreCase)) - { - version = ParseVersion(attributeValue); - } - - if (attributeName.Equals("Culture", StringComparison.OrdinalIgnoreCase)) - { - cultureName = ParseCulture(attributeValue); - } - - if (attributeName.Equals("PublicKeyToken", StringComparison.OrdinalIgnoreCase)) - { - pkt = ParsePKT(attributeValue); - } - - if (attributeName.Equals("ProcessorArchitecture", StringComparison.OrdinalIgnoreCase)) - { - flags |= (AssemblyNameFlags)(((int)ParseProcessorArchitecture(attributeValue)) << 4); - } - - if (attributeName.Equals("Retargetable", StringComparison.OrdinalIgnoreCase)) - { - if (attributeValue.Equals("Yes", StringComparison.OrdinalIgnoreCase)) - flags |= AssemblyNameFlags.Retargetable; - else if (attributeValue.Equals("No", StringComparison.OrdinalIgnoreCase)) - { - // nothing to do - } - else - throw new FileLoadException(); - } - - if (attributeName.Equals("ContentType", StringComparison.OrdinalIgnoreCase)) - { - if (attributeValue.Equals("WindowsRuntime", StringComparison.OrdinalIgnoreCase)) - flags |= (AssemblyNameFlags)(((int)AssemblyContentType.WindowsRuntime) << 9); - else - throw new FileLoadException(); - } - - // Desktop compat: If we got here, the attribute name is unknown to us. Ignore it (as long it's not duplicated.) - token = lexer.GetNext(); - } - return new RuntimeAssemblyName(name!, version!, cultureName!, flags, pkt!); - } - - private static Version ParseVersion(string attributeValue) - { - string[] parts = attributeValue.Split('.'); - if (parts.Length > 4) - throw new FileLoadException(); - ushort[] versionNumbers = new ushort[4]; - for (int i = 0; i < versionNumbers.Length; i++) - { - if (i >= parts.Length) - versionNumbers[i] = ushort.MaxValue; - else - { - // Desktop compat: TryParse is a little more forgiving than Fusion. - for (int j = 0; j < parts[i].Length; j++) - { - if (!char.IsDigit(parts[i][j])) - throw new FileLoadException(); - } - if (!(ushort.TryParse(parts[i], out versionNumbers[i]))) - { - throw new FileLoadException(); - } - } - } - - if (versionNumbers[0] == ushort.MaxValue || versionNumbers[1] == ushort.MaxValue) - throw new FileLoadException(); - if (versionNumbers[2] == ushort.MaxValue) - return new Version(versionNumbers[0], versionNumbers[1]); - if (versionNumbers[3] == ushort.MaxValue) - return new Version(versionNumbers[0], versionNumbers[1], versionNumbers[2]); - return new Version(versionNumbers[0], versionNumbers[1], versionNumbers[2], versionNumbers[3]); - } - - private static string ParseCulture(string attributeValue) - { - if (attributeValue.Equals("Neutral", StringComparison.OrdinalIgnoreCase)) - { - return ""; - } - else - { - CultureInfo culture = CultureInfo.GetCultureInfo(attributeValue); // Force a CultureNotFoundException if not a valid culture. - return culture.Name; - } - } - - private static byte[] ParsePKT(string attributeValue) - { - if (attributeValue.Equals("null", StringComparison.OrdinalIgnoreCase) || attributeValue.Length == 0) - return Array.Empty(); - - if (attributeValue.Length != 8 * 2) - throw new FileLoadException(); - - byte[] pkt = new byte[8]; - int srcIndex = 0; - for (int i = 0; i < 8; i++) - { - char hi = attributeValue[srcIndex++]; - char lo = attributeValue[srcIndex++]; - pkt[i] = (byte)((ParseHexNybble(hi) << 4) | ParseHexNybble(lo)); - } - return pkt; - } - - private static ProcessorArchitecture ParseProcessorArchitecture(string attributeValue) - { - if (attributeValue.Equals("msil", StringComparison.OrdinalIgnoreCase)) - return ProcessorArchitecture.MSIL; - if (attributeValue.Equals("x86", StringComparison.OrdinalIgnoreCase)) - return ProcessorArchitecture.X86; - if (attributeValue.Equals("ia64", StringComparison.OrdinalIgnoreCase)) - return ProcessorArchitecture.IA64; - if (attributeValue.Equals("amd64", StringComparison.OrdinalIgnoreCase)) - return ProcessorArchitecture.Amd64; - if (attributeValue.Equals("arm", StringComparison.OrdinalIgnoreCase)) - return ProcessorArchitecture.Arm; - throw new FileLoadException(); - } - - private static byte ParseHexNybble(char c) - { - if (c >= '0' && c <= '9') - return (byte)(c - '0'); - if (c >= 'a' && c <= 'f') - return (byte)(c - 'a' + 10); - if (c >= 'A' && c <= 'F') - return (byte)(c - 'A' + 10); - throw new FileLoadException(); - } - - private static readonly char[] s_illegalCharactersInSimpleName = { '/', '\\', ':' }; - } -} diff --git a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/RuntimeAssemblyName.cs b/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/RuntimeAssemblyName.cs deleted file mode 100644 index 4c340ea07deb0b..00000000000000 --- a/src/tools/illink/external/corert/src/System.Private.CoreLib/shared/System/Reflection/RuntimeAssemblyName.cs +++ /dev/null @@ -1,123 +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 System.Diagnostics; - -namespace System.Reflection -{ - // - // This is a private assembly name abstraction that's more suitable for use as keys in our caches. - // - // - Immutable, unlike the public AssemblyName - // - Has a useful Equals() override, unlike the public AssemblyName. - // - // We use this as our internal interchange type and only convert to and from the public AssemblyName class at public boundaries. - // - public sealed class RuntimeAssemblyName : IEquatable - { - public RuntimeAssemblyName(string name, Version version, string cultureName, AssemblyNameFlags flags, byte[] publicKeyOrToken) - { - Debug.Assert(name != null); - this.Name = name; - - // Optional version. - this.Version = version; - - // Optional culture name. - this.CultureName = cultureName; - - // Optional flags (this is actually an OR of the classic flags and the ContentType.) - this.Flags = flags; - - // Optional public key (if Flags.PublicKey == true) or public key token. - this.PublicKeyOrToken = publicKeyOrToken; - } - - // Simple name. - public string Name { get; } - - // Optional version. - public Version Version { get; } - - // Optional culture name. - public string CultureName { get; } - - // Optional flags (this is actually an OR of the classic flags and the ContentType.) - public AssemblyNameFlags Flags { get; } - - // Optional public key (if Flags.PublicKey == true) or public key token. - public byte[] PublicKeyOrToken { get; } - - // Equality - this compares every bit of data in the RuntimeAssemblyName which is acceptable for use as keys in a cache - // where semantic duplication is permissible. This method is *not* meant to define ref->def binding rules or - // assembly binding unification rules. - public bool Equals(RuntimeAssemblyName? other) - { - if (other == null) - return false; - if (!this.Name.Equals(other.Name)) - return false; - if (this.Version == null) - { - if (other.Version != null) - return false; - } - else - { - if (!this.Version.Equals(other.Version)) - return false; - } - if (!string.Equals(this.CultureName, other.CultureName)) - return false; - if (this.Flags != other.Flags) - return false; - - byte[] thisPK = this.PublicKeyOrToken; - byte[] otherPK = other.PublicKeyOrToken; - if (thisPK == null) - { - if (otherPK != null) - return false; - } - else if (otherPK == null) - { - return false; - } - else if (thisPK.Length != otherPK.Length) - { - return false; - } - else - { - for (int i = 0; i < thisPK.Length; i++) - { - if (thisPK[i] != otherPK[i]) - return false; - } - } - - return true; - } - - public sealed override bool Equals(object? obj) - { - RuntimeAssemblyName? other = obj as RuntimeAssemblyName; - if (other == null) - return false; - return Equals(other); - } - - public sealed override int GetHashCode() - { - return this.Name.GetHashCode(); - } - - public string FullName - { - get - { - return AssemblyNameFormatter.ComputeDisplayName(this); - } - } - } -} diff --git a/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs b/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs deleted file mode 100644 index ba045487bc0737..00000000000000 --- a/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs +++ /dev/null @@ -1,238 +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 System.Diagnostics; - -namespace System.Reflection.Runtime.TypeParsing -{ - // - // String tokenizer for typenames passed to the GetType() api's. - // - internal sealed class TypeLexer - { - public TypeLexer(string s) - { - // Turn the string into a char array with a NUL terminator. - char[] chars = new char[s.Length + 1]; - s.CopyTo(0, chars, 0, s.Length); - _chars = chars; - _index = 0; - } - - public TokenType Peek - { - get - { - SkipWhiteSpace(); - char c = _chars[_index]; - return CharToToken(c); - } - } - - public TokenType PeekSecond - { - get - { - SkipWhiteSpace(); - int index = _index + 1; - while (char.IsWhiteSpace(_chars[index])) - index++; - char c = _chars[index]; - return CharToToken(c); - } - } - - - public void Skip() - { - Debug.Assert(_index != _chars.Length); - SkipWhiteSpace(); - _index++; - } - - // Return the next token and skip index past it unless already at end of string - // or the token is not a reserved token. - public TokenType GetNextToken() - { - TokenType tokenType = Peek; - if (tokenType == TokenType.End || tokenType == TokenType.Other) - return tokenType; - Skip(); - return tokenType; - } - - // - // Lex the next segment as part of a type name. (Do not use for assembly names.) - // - // Note that unescaped "."'s do NOT terminate the identifier, but unescaped "+"'s do. - // - // Terminated by the first non-escaped reserved character ('[', ']', '+', '&', '*' or ',') - // - public string GetNextIdentifier() - { - SkipWhiteSpace(); - - int src = _index; - char[] buffer = new char[_chars.Length]; - int dst = 0; - for (; ; ) - { - char c = _chars[src]; - TokenType token = CharToToken(c); - if (token != TokenType.Other) - break; - src++; - if (c == '\\') - { - c = _chars[src]; - if (c != NUL) - src++; - if (c == NUL || CharToToken(c) == TokenType.Other) - { - // If we got here, a backslash was used to escape a character that is not legal to escape inside a type name. - // - // Common sense would dictate throwing an ArgumentException but that's not what the desktop CLR does. - // The desktop CLR treats this case by returning FALSE from TypeName::TypeNameParser::GetIdentifier(). - // Unfortunately, no one checks this return result. Instead, the CLR keeps parsing (unfortunately, the lexer - // was left in some strange state by the previous failure but typically, this goes unnoticed) and eventually, tries to resolve - // a Type whose name is the empty string. When it can't resolve that type, the CLR throws a TypeLoadException() - // complaining about be unable to find a type with the empty name. - // - // To emulate this accidental behavior, we'll throw a special exception that's caught by the TypeParser. - // - throw new IllegalEscapeSequenceException(); - } - } - buffer[dst++] = c; - } - - _index = src; - return new string(buffer, 0, dst); - } - - // - // Lex the next segment as the assembly name at the end of an assembly-qualified type name. (Do not use for - // assembly names embedded inside generic type arguments.) - // - // Terminated by NUL. There are no escape characters defined by the typename lexer (however, AssemblyName - // does have its own escape rules.) - // - public RuntimeAssemblyName GetNextAssemblyName() - { - SkipWhiteSpace(); - - int src = _index; - char[] buffer = new char[_chars.Length]; - int dst = 0; - for (; ; ) - { - char c = _chars[src]; - if (c == NUL) - break; - src++; - buffer[dst++] = c; - } - _index = src; - string fullName = new string(buffer, 0, dst); - return AssemblyNameParser.Parse(fullName); - } - - // - // Lex the next segment as an assembly name embedded inside a generic argument type. - // - // Terminated by an unescaped ']'. - // - public RuntimeAssemblyName GetNextEmbeddedAssemblyName() - { - SkipWhiteSpace(); - - int src = _index; - char[] buffer = new char[_chars.Length]; - int dst = 0; - for (; ; ) - { - char c = _chars[src]; - if (c == NUL) - throw new ArgumentException(); - if (c == ']') - break; - src++; - - // Backslash can be used to escape a ']' - any other backslash character is left alone (along with the backslash) - // for the AssemblyName parser to handle. - if (c == '\\' && _chars[src] == ']') - { - c = _chars[src++]; - } - buffer[dst++] = c; - } - _index = src; - var fullName = new string(buffer, 0, dst); - return AssemblyNameParser.Parse(fullName); - } - - // - // Classify a character as a TokenType. (Fortunately, all tokens in typename strings other than identifiers are single-character tokens.) - // - private static TokenType CharToToken(char c) - { - switch (c) - { - case NUL: - return TokenType.End; - case '[': - return TokenType.OpenSqBracket; - case ']': - return TokenType.CloseSqBracket; - case ',': - return TokenType.Comma; - case '+': - return TokenType.Plus; - case '*': - return TokenType.Asterisk; - case '&': - return TokenType.Ampersand; - default: - return TokenType.Other; - } - } - - // - // The desktop typename parser has a strange attitude towards whitespace. It throws away whitespace between punctuation tokens and whitespace - // preceeding identifiers or assembly names (and this cannot be escaped away). But whitespace between the end of an identifier - // and the punctuation that ends it is *not* ignored. - // - // In other words, GetType(" Foo") searches for "Foo" but GetType("Foo ") searches for "Foo ". - // - // Whitespace between the end of an assembly name and the punction mark that ends it is also not ignored by this parser, - // but this is irrelevant since the assembly name is then turned over to AssemblyName for parsing, which *does* ignore trailing whitespace. - // - private void SkipWhiteSpace() - { - while (char.IsWhiteSpace(_chars[_index])) - _index++; - } - - - private int _index; - private readonly char[] _chars; - private const char NUL = (char)0; - - - public sealed class IllegalEscapeSequenceException : Exception - { - } - } - - internal enum TokenType - { - End = 0, //At end of string - OpenSqBracket = 1, //'[' - CloseSqBracket = 2, //']' - Comma = 3, //',' - Plus = 4, //'+' - Asterisk = 5, //'*' - Ampersand = 6, //'&' - Other = 7, //Type identifier, AssemblyName or embedded AssemblyName. - } -} diff --git a/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs b/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs deleted file mode 100644 index a8d20aedef086d..00000000000000 --- a/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs +++ /dev/null @@ -1,219 +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 System.Diagnostics; -using System.Collections.Generic; - -namespace System.Reflection.Runtime.TypeParsing -{ - // - // The TypeName class is the base class for a family of types that represent the nodes in a parse tree for - // assembly-qualified type names. - // - public abstract class TypeName - { - public abstract override string ToString(); - } - - // - // Represents a parse of a type name optionally qualified by an assembly name. If present, the assembly name follows - // a comma following the type name. - // - public sealed class AssemblyQualifiedTypeName : TypeName - { - public AssemblyQualifiedTypeName(NonQualifiedTypeName typeName, RuntimeAssemblyName assemblyName) - { - Debug.Assert(typeName != null); - TypeName = typeName; - AssemblyName = assemblyName; - } - - public sealed override string ToString() - { - return TypeName.ToString() + ((AssemblyName == null) ? "" : ", " + AssemblyName.FullName); - } - - public RuntimeAssemblyName AssemblyName { get; } - public NonQualifiedTypeName TypeName { get; } - } - - // - // Base class for all non-assembly-qualified type names. - // - public abstract class NonQualifiedTypeName : TypeName - { - } - - // - // Base class for namespace or nested type. - // - internal abstract class NamedTypeName : NonQualifiedTypeName - { - } - - // - // Non-nested named type. The full name is the namespace-qualified name. For example, the FullName for - // System.Collections.Generic.IList<> is "System.Collections.Generic.IList`1". - // - internal sealed partial class NamespaceTypeName : NamedTypeName - { - public NamespaceTypeName(string[] namespaceParts, string name) - { - Debug.Assert(namespaceParts != null); - Debug.Assert(name != null); - - _name = name; - _namespaceParts = namespaceParts; - } - - public sealed override string ToString() - { - string fullName = ""; - for (int i = 0; i < _namespaceParts.Length; i++) - { - fullName += _namespaceParts[_namespaceParts.Length - i - 1]; - fullName += "."; - } - fullName += _name; - return fullName; - } - - private string _name; - private string[] _namespaceParts; - } - - // - // A nested type. The Name is the simple name of the type (not including any portion of its declaring type name.) - // - internal sealed class NestedTypeName : NamedTypeName - { - public NestedTypeName(string name, NamedTypeName declaringType) - { - Name = name; - DeclaringType = declaringType; - } - - public string Name { get; private set; } - public NamedTypeName DeclaringType { get; private set; } - - public sealed override string ToString() - { - // Cecil's format uses '/' instead of '+' for nested types. - return DeclaringType + "/" + Name; - } - } - - // - // Abstract base for array, byref and pointer type names. - // - internal abstract class HasElementTypeName : NonQualifiedTypeName - { - public HasElementTypeName(TypeName elementTypeName) - { - ElementTypeName = elementTypeName; - } - - public TypeName ElementTypeName { get; } - } - - // - // A single-dimensional zero-lower-bound array type name. - // - internal sealed class ArrayTypeName : HasElementTypeName - { - public ArrayTypeName(TypeName elementTypeName) - : base(elementTypeName) - { - } - - public sealed override string ToString() - { - return ElementTypeName + "[]"; - } - } - - // - // A multidim array type name. - // - internal sealed class MultiDimArrayTypeName : HasElementTypeName - { - public MultiDimArrayTypeName(TypeName elementTypeName, int rank) - : base(elementTypeName) - { - Rank = rank; - } - - public sealed override string ToString() - { - return ElementTypeName + "[" + (Rank == 1 ? "*" : new string(',', Rank - 1)) + "]"; - } - - public int Rank { get; } - } - - // - // A byref type. - // - internal sealed class ByRefTypeName : HasElementTypeName - { - public ByRefTypeName(TypeName elementTypeName) - : base(elementTypeName) - { - } - - public sealed override string ToString() - { - return ElementTypeName + "&"; - } - } - - // - // A pointer type. - // - internal sealed class PointerTypeName : HasElementTypeName - { - public PointerTypeName(TypeName elementTypeName) - : base(elementTypeName) - { - } - - public sealed override string ToString() - { - return ElementTypeName + "*"; - } - } - - // - // A constructed generic type. - // - internal sealed class ConstructedGenericTypeName : NonQualifiedTypeName - { - public ConstructedGenericTypeName(NamedTypeName genericType, IEnumerable genericArguments) - { - GenericType = genericType; - GenericArguments = genericArguments; - } - - public NamedTypeName GenericType { get; } - public IEnumerable GenericArguments { get; } - - public sealed override string ToString() - { - string s = GenericType.ToString(); - s += "["; - string sep = ""; - foreach (TypeName genericTypeArgument in GenericArguments) - { - s += sep; - sep = ","; - AssemblyQualifiedTypeName? assemblyQualifiedTypeArgument = genericTypeArgument as AssemblyQualifiedTypeName; - if (assemblyQualifiedTypeArgument == null || assemblyQualifiedTypeArgument.AssemblyName == null) - s += genericTypeArgument.ToString(); - else - s += "[" + genericTypeArgument.ToString() + "]"; - } - s += "]"; - return s; - } - } -} diff --git a/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs b/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs deleted file mode 100644 index 712bfddf7793c8..00000000000000 --- a/src/tools/illink/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs +++ /dev/null @@ -1,226 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#pragma warning disable CA2208 -using System.Collections.Generic; - -namespace System.Reflection.Runtime.TypeParsing -{ - // - // Parser for type names passed to GetType() apis. - // - public sealed class TypeParser - { - // - // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name. - // - public static TypeName? ParseTypeName(string s) - { - try - { - return ParseAssemblyQualifiedTypeName(s); - } - catch (ArgumentException) - { - return null; - } - } - - // - // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name. - // - private static TypeName? ParseAssemblyQualifiedTypeName(string s) - { - if (string.IsNullOrEmpty(s)) - return null; - - // Desktop compat: a whitespace-only "typename" qualified by an assembly name throws an ArgumentException rather than - // a TypeLoadException. - int idx = 0; - while (idx < s.Length && char.IsWhiteSpace(s[idx])) - { - idx++; - } - if (idx < s.Length && s[idx] == ',') - throw new ArgumentException(); - - try - { - TypeParser parser = new TypeParser(s); - NonQualifiedTypeName typeName = parser.ParseNonQualifiedTypeName(); - TokenType token = parser._lexer.GetNextToken(); - if (token == TokenType.End) - return typeName; - if (token == TokenType.Comma) - { - RuntimeAssemblyName assemblyName = parser._lexer.GetNextAssemblyName(); - token = parser._lexer.Peek; - if (token != TokenType.End) - throw new ArgumentException(); - return new AssemblyQualifiedTypeName(typeName, assemblyName); - } - throw new ArgumentException(); - } - catch (TypeLexer.IllegalEscapeSequenceException) - { - // Emulates a CLR4.5 bug that causes any string that contains an illegal escape sequence to be parsed as the empty string. - return ParseAssemblyQualifiedTypeName(string.Empty); - } - } - - private TypeParser(string s) - { - _lexer = new TypeLexer(s); - } - - - // - // Parses a type name without any assembly name qualification. - // - private NonQualifiedTypeName ParseNonQualifiedTypeName() - { - // Parse the named type or constructed generic type part first. - NonQualifiedTypeName typeName = ParseNamedOrConstructedGenericTypeName(); - - // Iterate through any "has-element" qualifiers ([], &, *). - for (;;) - { - TokenType token = _lexer.Peek; - if (token == TokenType.End) - break; - if (token == TokenType.Asterisk) - { - _lexer.Skip(); - typeName = new PointerTypeName(typeName); - } - else if (token == TokenType.Ampersand) - { - _lexer.Skip(); - typeName = new ByRefTypeName(typeName); - } - else if (token == TokenType.OpenSqBracket) - { - _lexer.Skip(); - token = _lexer.GetNextToken(); - if (token == TokenType.Asterisk) - { - typeName = new MultiDimArrayTypeName(typeName, 1); - token = _lexer.GetNextToken(); - } - else - { - int rank = 1; - while (token == TokenType.Comma) - { - token = _lexer.GetNextToken(); - rank++; - } - if (rank == 1) - typeName = new ArrayTypeName(typeName); - else - typeName = new MultiDimArrayTypeName(typeName, rank); - } - if (token != TokenType.CloseSqBracket) - throw new ArgumentException(); - } - else - { - break; - } - } - return typeName; - } - - // - // Foo or Foo+Inner or Foo[String] or Foo+Inner[String] - // - private NonQualifiedTypeName ParseNamedOrConstructedGenericTypeName() - { - NamedTypeName namedType = ParseNamedTypeName(); - // Because "[" is used both for generic arguments and array indexes, we must peek two characters deep. - if (!(_lexer.Peek == TokenType.OpenSqBracket && (_lexer.PeekSecond == TokenType.Other || _lexer.PeekSecond == TokenType.OpenSqBracket))) - return namedType; - else - { - _lexer.Skip(); - List genericTypeArguments = new List(); - for (;;) - { - TypeName genericTypeArgument = ParseGenericTypeArgument(); - genericTypeArguments.Add(genericTypeArgument); - TokenType token = _lexer.GetNextToken(); - if (token == TokenType.CloseSqBracket) - break; - if (token != TokenType.Comma) - throw new ArgumentException(); - } - - return new ConstructedGenericTypeName(namedType, genericTypeArguments); - } - } - - // - // Foo or Foo+Inner - // - private NamedTypeName ParseNamedTypeName() - { - NamedTypeName namedType = ParseNamespaceTypeName(); - while (_lexer.Peek == TokenType.Plus) - { - _lexer.Skip(); - string nestedTypeName = _lexer.GetNextIdentifier(); - namedType = new NestedTypeName(nestedTypeName, namedType); - } - return namedType; - } - - // - // Non-nested named type. - // - private NamespaceTypeName ParseNamespaceTypeName() - { - string fullName = _lexer.GetNextIdentifier(); - string[] parts = fullName.Split('.'); - int numNamespaceParts = parts.Length - 1; - string[] namespaceParts = new string[numNamespaceParts]; - for (int i = 0; i < numNamespaceParts; i++) - namespaceParts[numNamespaceParts - i - 1] = parts[i]; - string name = parts[numNamespaceParts]; - return new NamespaceTypeName(namespaceParts, name); - } - - // - // Parse a generic argument. In particular, generic arguments can take the special form [,]. - // - private TypeName ParseGenericTypeArgument() - { - TokenType token = _lexer.GetNextToken(); - if (token == TokenType.Other) - { - NonQualifiedTypeName nonQualifiedTypeName = ParseNonQualifiedTypeName(); - return nonQualifiedTypeName; - } - else if (token == TokenType.OpenSqBracket) - { - RuntimeAssemblyName? assemblyName = null; - NonQualifiedTypeName typeName = ParseNonQualifiedTypeName(); - token = _lexer.GetNextToken(); - if (token == TokenType.Comma) - { - assemblyName = _lexer.GetNextEmbeddedAssemblyName(); - token = _lexer.GetNextToken(); - } - if (token != TokenType.CloseSqBracket) - throw new ArgumentException(); - if (assemblyName == null) - return typeName; - else - return new AssemblyQualifiedTypeName(typeName, assemblyName); - } - else - throw new ArgumentException(); - } - - private readonly TypeLexer _lexer; - } -} diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 0e95901f271aca..01d04b0ca6f82c 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -35,7 +35,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Reflection.Runtime.TypeParsing; using System.Text.RegularExpressions; using ILCompiler.DependencyAnalysisFramework; using ILLink.Shared; @@ -46,6 +45,10 @@ using Mono.Collections.Generic; using Mono.Linker.Dataflow; +using AssemblyNameInfo = System.Reflection.Metadata.AssemblyNameInfo; +using TypeName = System.Reflection.Metadata.TypeName; +using TypeNameParseOptions = System.Reflection.Metadata.TypeNameParseOptions; + namespace Mono.Linker.Steps { @@ -2151,12 +2154,6 @@ protected virtual void DoAdditionalInstantiatedTypeProcessing (TypeDefinition ty if (property.Name == "TargetTypeName") { string targetTypeName = (string) property.Argument.Value; - TypeName? typeName = TypeParser.ParseTypeName (targetTypeName); - if (typeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { - AssemblyDefinition? assembly = Context.TryResolve (assemblyQualifiedTypeName.AssemblyName.Name); - return assembly == null ? null : Context.TryResolve (assembly, targetTypeName); - } - return Context.TryResolve (asm, targetTypeName); } } diff --git a/src/tools/illink/src/linker/Linker/LinkContext.cs b/src/tools/illink/src/linker/Linker/LinkContext.cs index 4d825e73d91822..21d6ffa7c94d36 100644 --- a/src/tools/illink/src/linker/Linker/LinkContext.cs +++ b/src/tools/illink/src/linker/Linker/LinkContext.cs @@ -65,7 +65,12 @@ public interface ITryResolveMetadata TypeDefinition? TryResolve (TypeReference typeReference); } - public class LinkContext : IMetadataResolver, ITryResolveMetadata, IDisposable + internal interface ITryResolveAssemblyName + { + AssemblyDefinition? TryResolve (string assemblyName); + } + + public class LinkContext : IMetadataResolver, ITryResolveMetadata, ITryResolveAssemblyName, IDisposable { readonly Pipeline _pipeline; @@ -211,7 +216,7 @@ protected LinkContext (Pipeline pipeline, ILogger logger, string outputDirectory _logger = logger ?? throw new ArgumentNullException (nameof (logger)); _resolver = factory.CreateResolver (this); - _typeNameResolver = new TypeNameResolver (this); + _typeNameResolver = new TypeNameResolver (this, this); _actions = new Dictionary (); _parameters = new Dictionary (StringComparer.Ordinal); _customAttributes = new CustomAttributeSource (this); diff --git a/src/tools/illink/src/linker/Linker/TypeNameResolver.cs b/src/tools/illink/src/linker/Linker/TypeNameResolver.cs index cd429719b891a5..f700471c980e76 100644 --- a/src/tools/illink/src/linker/Linker/TypeNameResolver.cs +++ b/src/tools/illink/src/linker/Linker/TypeNameResolver.cs @@ -5,22 +5,33 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection.Runtime.TypeParsing; using ILLink.Shared; using ILLink.Shared.TrimAnalysis; using Mono.Cecil; +using Mono.Linker; + +using TypeName = System.Reflection.Metadata.TypeName; +using TypeNameParseOptions = System.Reflection.Metadata.TypeNameParseOptions; +using AssemblyNameInfo = System.Reflection.Metadata.AssemblyNameInfo; +using TypeNameHelpers = System.Reflection.Metadata.TypeNameHelpers; + +#nullable enable namespace Mono.Linker { internal sealed class TypeNameResolver { - readonly LinkContext _context; + readonly ITryResolveMetadata _metadataResolver; + readonly ITryResolveAssemblyName _assemblyResolver; + + private static readonly TypeNameParseOptions s_typeNameParseOptions = new () { MaxNodes = int.MaxValue }; public readonly record struct TypeResolutionRecord (AssemblyDefinition ReferringAssembly, TypeDefinition ResolvedType); - public TypeNameResolver (LinkContext context) + public TypeNameResolver (ITryResolveMetadata metadataResolver, ITryResolveAssemblyName assemblyNameResolver) { - _context = context; + _metadataResolver = metadataResolver; + _assemblyResolver = assemblyNameResolver; } public bool TryResolveTypeName ( @@ -35,25 +46,19 @@ public bool TryResolveTypeName ( if (string.IsNullOrEmpty (typeNameString)) return false; - TypeName? parsedTypeName; - try { - parsedTypeName = TypeParser.ParseTypeName (typeNameString); - } catch (ArgumentException) { - return false; - } catch (System.IO.FileLoadException) { + if (!TypeName.TryParse (typeNameString, out TypeName? parsedTypeName, s_typeNameParseOptions)) return false; - } typeResolutionRecords = new List (); AssemblyDefinition? typeAssembly; - if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { - typeAssembly = _context.TryResolve (assemblyQualifiedTypeName.AssemblyName.Name); - if (typeAssembly == null) { + if (parsedTypeName.AssemblyName is AssemblyNameInfo assemblyName) { + typeAssembly = _assemblyResolver.TryResolve (assemblyName.Name); + if (typeAssembly is null) { typeResolutionRecords = null; return false; } - typeReference = ResolveTypeName (typeAssembly, assemblyQualifiedTypeName.TypeName, typeResolutionRecords); + typeReference = ResolveTypeName (typeAssembly, parsedTypeName, typeResolutionRecords); if (typeReference == null) { typeResolutionRecords = null; } @@ -77,7 +82,7 @@ public bool TryResolveTypeName ( return true; // If type is not found in the caller's assembly, try in core assembly. - typeAssembly = _context.TryResolve (PlatformAssemblies.CoreLib); + typeAssembly = _assemblyResolver.TryResolve (PlatformAssemblies.CoreLib); if (typeAssembly != null && TryResolveTypeName (typeAssembly, parsedTypeName, out typeReference, typeResolutionRecords)) return true; @@ -107,7 +112,11 @@ public bool TryResolveTypeName ( [NotNullWhen (true)] out List? typeResolutionRecords) { typeResolutionRecords = new List (); - typeReference = ResolveTypeName (assembly, TypeParser.ParseTypeName (typeNameString), typeResolutionRecords); + if (!TypeName.TryParse (typeNameString, out TypeName? parsedTypeName, s_typeNameParseOptions)) { + typeReference = null; + return false; + } + typeReference = ResolveTypeName (assembly, parsedTypeName, typeResolutionRecords); if (typeReference == null) typeResolutionRecords = null; @@ -115,25 +124,27 @@ public bool TryResolveTypeName ( return typeReference != null; } - TypeReference? ResolveTypeName (AssemblyDefinition assembly, TypeName? typeName, List typeResolutionRecords) + TypeReference? ResolveTypeName (AssemblyDefinition originalAssembly, TypeName? typeName, List typeResolutionRecords) { - if (typeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { + if (typeName == null) + return null; + + AssemblyDefinition? assembly = originalAssembly; + if (typeName.AssemblyName is AssemblyNameInfo assemblyName) // In this case we ignore the assembly parameter since the type name has assembly in it - var assemblyFromName = _context.TryResolve (assemblyQualifiedTypeName.AssemblyName.Name); - return assemblyFromName == null ? null : ResolveTypeName (assemblyFromName, assemblyQualifiedTypeName.TypeName, typeResolutionRecords); - } + assembly = _assemblyResolver.TryResolve (assemblyName.Name); - if (assembly == null || typeName == null) + if (assembly == null) return null; - if (typeName is ConstructedGenericTypeName constructedGenericTypeName) { - var genericTypeRef = ResolveTypeName (assembly, constructedGenericTypeName.GenericType, typeResolutionRecords); + if (typeName.IsConstructedGenericType) { + var genericTypeRef = ResolveTypeName (assembly, typeName.GetGenericTypeDefinition (), typeResolutionRecords); if (genericTypeRef == null) return null; Debug.Assert (genericTypeRef is TypeDefinition); var genericInstanceType = new GenericInstanceType (genericTypeRef); - foreach (var arg in constructedGenericTypeName.GenericArguments) { + foreach (var arg in typeName.GetGenericArguments()) { var genericArgument = ResolveTypeName (assembly, arg, typeResolutionRecords); if (genericArgument == null) return null; @@ -142,29 +153,62 @@ public bool TryResolveTypeName ( } return genericInstanceType; - } else if (typeName is HasElementTypeName elementTypeName) { - var elementType = ResolveTypeName (assembly, elementTypeName.ElementTypeName, typeResolutionRecords); + } else if (typeName.IsArray || typeName.IsPointer || typeName.IsByRef) { + var elementType = ResolveTypeName (assembly, typeName.GetElementType (), typeResolutionRecords); if (elementType == null) return null; - return typeName switch { - ArrayTypeName => new ArrayType (elementType), - MultiDimArrayTypeName multiDimArrayTypeName => new ArrayType (elementType, multiDimArrayTypeName.Rank), - ByRefTypeName => new ByReferenceType (elementType), - PointerTypeName => new PointerType (elementType), - _ => elementType - }; + if (typeName.IsArray) + return typeName.IsSZArray ? new ArrayType (elementType) : new ArrayType (elementType, typeName.GetArrayRank ()); + if (typeName.IsByRef) + return new ByReferenceType (elementType); + if (typeName.IsPointer) + return new PointerType (elementType); + Debug.Fail("Unreachable"); + return null; } - TypeDefinition? resolvedType = assembly.MainModule.ResolveType (typeName.ToString (), _context); + Debug.Assert (typeName.IsSimple); + TypeName topLevelTypeName = typeName; + while (topLevelTypeName.IsNested) + topLevelTypeName = topLevelTypeName.DeclaringType!; + Debug.Assert (topLevelTypeName.AssemblyName == typeName.AssemblyName); + TypeDefinition? resolvedType = GetSimpleTypeFromModule (typeName, assembly.MainModule); // True type references (like generics and arrays) don't count as actually resolved types, they're just wrappers // so only record type resolutions for types which are actually resolved. if (resolvedType != null) { typeResolutionRecords.Add (new (assembly, resolvedType)); + return resolvedType; + } + + return null; + + TypeDefinition? GetSimpleTypeFromModule (TypeName typeName, ModuleDefinition module) + { + if (typeName.IsNested) + { + TypeDefinition? type = GetSimpleTypeFromModule (typeName.DeclaringType!, module); + if (type == null) + return null; + return GetNestedType (type, TypeNameHelpers.Unescape (typeName.Name)); + } + + return module.ResolveType (TypeNameHelpers.Unescape (typeName.FullName), _metadataResolver); } - return resolvedType; + TypeDefinition? GetNestedType (TypeDefinition type, string nestedTypeName) + { + if (!type.HasNestedTypes) + return null; + + foreach (var nestedType in type.NestedTypes) { + if (nestedType.Name == nestedTypeName) + return nestedType; + } + + return null; + } } } } diff --git a/src/tools/illink/src/linker/Mono.Linker.csproj b/src/tools/illink/src/linker/Mono.Linker.csproj index 8241ceb22e40ad..5c151fad0e9639 100644 --- a/src/tools/illink/src/linker/Mono.Linker.csproj +++ b/src/tools/illink/src/linker/Mono.Linker.csproj @@ -23,6 +23,7 @@ $(NoWarn);NU5131 $(TargetsForTfmSpecificContentInPackage);_AddReferenceAssemblyToPackage $(DefineConstants);ILLINK + true @@ -73,7 +74,12 @@ - + + + + + + diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/EscapedTypeNames.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/EscapedTypeNames.il new file mode 100644 index 00000000000000..e7913bd63df5e8 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/EscapedTypeNames.il @@ -0,0 +1,23 @@ +// Metadata version: v4.0.30319 +.assembly extern System.Runtime +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: +} +.assembly 'library' +{ + .hash algorithm 0x00008004 + .ver 1:0:0:0 +} +.module library.dll + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public auto ansi sealed beforefieldinit 'Library.Not+Nested' + extends [System.Runtime]System.Object +{ + + .class nested public auto ansi sealed beforefieldinit 'Nes\\ted' + extends [System.Runtime]System.Object + { + } +} // end of class Library.TypeInfoCalls diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs index 981ad6227ceb8d..24c2fae4ab2d4b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs @@ -3,11 +3,16 @@ using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.Reflection { [KeptMember (".cctor()")] [ExpectedNoWarnings ()] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/EscapedTypeNames.il" })] + [KeptTypeInAssembly ("library", "Library.Not\\+Nested")] + [KeptTypeInAssembly ("library", "Library.Not\\+Nested+Nes\\\\ted")] [KeptDelegateCacheField ("0", nameof (AssemblyResolver))] [KeptDelegateCacheField ("1", nameof (GetTypeFromAssembly))] public class TypeUsedViaReflection @@ -47,8 +52,8 @@ public static void Main () BaseTypeInterfaces.Test (); - TestInvalidTypeCombination (); + TestEscapedTypeName (); } [Kept] @@ -465,5 +470,12 @@ static void TestInvalidTypeCombination () Console.WriteLine (Type.GetType ("System.Span`1[[System.Byte, System.Runtime]][], System.Runtime")); } catch (Exception e) { } } + + [Kept] + static void TestEscapedTypeName () + { + var typeKept = Type.GetType ("Library.Not\\+Nested, library"); + typeKept = Type.GetType ("Library.Not\\+Nested+Nes\\\\ted, library"); + } } } diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index e9e9b94de191e4..6782eded99db39 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -12,11 +12,14 @@ using System.Text.RegularExpressions; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.Linker; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; using Mono.Linker.Tests.Extensions; using Mono.Linker.Tests.TestCasesRunner.ILVerification; +using ILLink.Shared.TrimAnalysis; using NUnit.Framework; +using System.Data.Common; namespace Mono.Linker.Tests.TestCasesRunner { @@ -24,6 +27,7 @@ public class ResultChecker { readonly BaseAssemblyResolver _originalsResolver; readonly BaseAssemblyResolver _linkedResolver; + readonly TypeNameResolver _linkedTypeNameResolver; readonly ReaderParameters _originalReaderParameters; readonly ReaderParameters _linkedReaderParameters; @@ -43,10 +47,31 @@ public ResultChecker (BaseAssemblyResolver originalsResolver, BaseAssemblyResolv { _originalsResolver = originalsResolver; _linkedResolver = linkedResolver; + var testResolver = new TestResolver (_linkedResolver); + _linkedTypeNameResolver = new TypeNameResolver (testResolver, testResolver); _originalReaderParameters = originalReaderParameters; _linkedReaderParameters = linkedReaderParameters; } + struct TestResolver : ITryResolveAssemblyName, ITryResolveMetadata + { + readonly BaseAssemblyResolver _assemblyResolver; + + public TestResolver (BaseAssemblyResolver resolver) + { + _assemblyResolver = resolver; + } + + public AssemblyDefinition TryResolve (string assemblyName) + => _assemblyResolver.Resolve (new AssemblyNameReference (assemblyName, null), new ReaderParameters ()); + + public MethodDefinition TryResolve (MethodReference methodReference) => methodReference.Resolve (); + + public FieldDefinition TryResolve (FieldReference fieldReference) => fieldReference.Resolve (); + + public TypeDefinition TryResolve (TypeReference typeReference) => typeReference.Resolve (); + } + protected static void ValidateTypeRefsHaveValidAssemblyRefs (AssemblyDefinition linked) { foreach (var typeRef in linked.MainModule.GetTypeReferences ()) { @@ -309,7 +334,8 @@ void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original) } var expectedTypeName = checkAttrInAssembly.ConstructorArguments[1].Value.ToString (); - TypeDefinition linkedType = linkedAssembly.MainModule.GetType (expectedTypeName); + _linkedTypeNameResolver.TryResolveTypeName (linkedAssembly, expectedTypeName, out TypeReference linkedTypeRef, out _); + TypeDefinition linkedType = linkedTypeRef?.Resolve (); if (linkedType == null && linkedAssembly.MainModule.HasExportedTypes) { ExportedType exportedType = linkedAssembly.MainModule.ExportedTypes diff --git a/src/tools/illink/test/Mono.Linker.Tests/Tests/TypeNameResolverTests.cs b/src/tools/illink/test/Mono.Linker.Tests/Tests/TypeNameResolverTests.cs deleted file mode 100644 index de3759d46b4822..00000000000000 --- a/src/tools/illink/test/Mono.Linker.Tests/Tests/TypeNameResolverTests.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Reflection.Runtime.TypeParsing; -using NUnit.Framework; - -namespace Mono.Linker.Tests -{ - [TestFixture] - public class TypeNameResolverTests - { - [Test] - public void TryParseAssemblyQualifiedTypeName_Null () - { - Assert.IsNull (TypeParser.ParseTypeName (null)); - } - - [Test] - public void TryParseAssemblyQualifiedTypeName_FullyQualified () - { - var value = typeof (TypeNameResolverTests).AssemblyQualifiedName; - var typeName = TypeParser.ParseTypeName (value); - Assert.IsTrue (typeName is AssemblyQualifiedTypeName); - AssemblyQualifiedTypeName assemblyQualifiedTypeName = (AssemblyQualifiedTypeName) typeName; - Assert.AreEqual (assemblyQualifiedTypeName.AssemblyName.FullName, typeof (TypeNameResolverTests).Assembly.FullName); - Assert.AreEqual (assemblyQualifiedTypeName.TypeName.ToString (), typeof (TypeNameResolverTests).FullName); - } - - [Test] - public void TryParseAssemblyQualifiedTypeName_NameAndAssemblyOnly () - { - var value = $"{typeof (TypeNameResolverTests).FullName}, {typeof (TypeNameResolverTests).Assembly.GetName ().Name}"; - var typeName = TypeParser.ParseTypeName (value); - Assert.IsTrue (typeName is AssemblyQualifiedTypeName); - AssemblyQualifiedTypeName assemblyQualifiedTypeName = (AssemblyQualifiedTypeName) typeName; - Assert.AreEqual (assemblyQualifiedTypeName.AssemblyName.Name, typeof (TypeNameResolverTests).Assembly.GetName ().Name); - Assert.AreEqual (assemblyQualifiedTypeName.TypeName.ToString (), typeof (TypeNameResolverTests).FullName); - } - - [Test] - public void TryParseAssemblyQualifiedTypeName_NameOnly () - { - var value = typeof (TypeNameResolverTests).FullName; - var typeName = TypeParser.ParseTypeName (value); - Assert.IsFalse (typeName is AssemblyQualifiedTypeName); - Assert.AreEqual (typeName.ToString (), value); - } - - [Test] - public void TryParseAssemblyQualifiedTypeName_GenericType_FullyQualified () - { - var value = typeof (GenericType<,>).AssemblyQualifiedName; - var typeName = TypeParser.ParseTypeName (value); - Assert.IsTrue (typeName is AssemblyQualifiedTypeName); - AssemblyQualifiedTypeName assemblyQualifiedTypeName = (AssemblyQualifiedTypeName) typeName; - Assert.AreEqual (assemblyQualifiedTypeName.AssemblyName.Name, typeof (TypeNameResolverTests).Assembly.GetName ().Name); - Assert.AreEqual (assemblyQualifiedTypeName.TypeName.ToString (), $"{typeof (TypeNameResolverTests).FullName}/GenericType`2"); - } - - [Test] - public void TryParseAssemblyQualifiedTypeName_GenericType_NameAndAssemblyOnly () - { - var value = $"{typeof (GenericType<,>).FullName}, {typeof (TypeNameResolverTests).Assembly.GetName ().Name}"; - var typeName = TypeParser.ParseTypeName (value); - Assert.IsTrue (typeName is AssemblyQualifiedTypeName); - AssemblyQualifiedTypeName assemblyQualifiedTypeName = (AssemblyQualifiedTypeName) typeName; - Assert.AreEqual (assemblyQualifiedTypeName.AssemblyName.Name, typeof (TypeNameResolverTests).Assembly.GetName ().Name); - Assert.AreEqual (assemblyQualifiedTypeName.TypeName.ToString (), $"{typeof (TypeNameResolverTests).FullName}/GenericType`2"); - } - - [Test] - public void TryParseAssemblyQualifiedTypeName_GenericType_NameOnly () - { - var value = typeof (GenericType<,>).FullName; - var typeName = TypeParser.ParseTypeName (value); - Assert.IsFalse (typeName is AssemblyQualifiedTypeName); - Assert.AreEqual (typeName.ToString (), $"{typeof (TypeNameResolverTests).FullName}/GenericType`2"); - } - - [Test] - public void MissingTypeName () - { - Assert.IsNull (TypeParser.ParseTypeName (", System")); - } - - sealed class GenericType { } - } -} From fddb2de1ab4e229e04f2cf23ebe1a3cdffe7d69e Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 20 Jun 2024 19:59:45 +0000 Subject: [PATCH 2/4] Fix test resolution logic Some tests have exported types that are kept even though the types they point to are removed. The ResultCheckr resolution logic should not throw in that case. --- .../test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index 6782eded99db39..fd9eed7dec3386 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -334,7 +334,10 @@ void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original) } var expectedTypeName = checkAttrInAssembly.ConstructorArguments[1].Value.ToString (); - _linkedTypeNameResolver.TryResolveTypeName (linkedAssembly, expectedTypeName, out TypeReference linkedTypeRef, out _); + TypeReference linkedTypeRef = null; + try { + _linkedTypeNameResolver.TryResolveTypeName (linkedAssembly, expectedTypeName, out linkedTypeRef, out _); + } catch (AssemblyResolutionException) {} TypeDefinition linkedType = linkedTypeRef?.Resolve (); if (linkedType == null && linkedAssembly.MainModule.HasExportedTypes) { From 9e1c3d6df902b2f341978f51756bff8062451788 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 21 Jun 2024 18:09:20 +0000 Subject: [PATCH 3/4] Use TypeNameResolver in ILCompiler.Trimming.Tests --- ...mentationSignatureGenerator.PartVisitor.cs | 3 +- .../ILCompiler.Trimming.Tests.csproj | 8 ++ .../TestCasesRunner/AssemblyChecker.cs | 6 +- ...mentationSignatureGenerator.PartVisitor.cs | 3 +- .../Linker/DocumentationSignatureGenerator.cs | 2 +- .../Linker/DocumentationSignatureParser.cs | 2 +- .../illink/src/linker/Linker/ITryResolve.cs | 21 +++++ .../illink/src/linker/Linker/LinkContext.cs | 11 --- .../Linker/ModuleDefinitionExtensions.cs | 7 +- .../TypeNameResolver.WithDiagnostics.cs | 90 +++++++++++++++++++ .../src/linker/Linker/TypeNameResolver.cs | 77 +--------------- .../linker/Linker/TypeReferenceExtensions.cs | 2 +- .../TestCasesRunner/ResultChecker.cs | 22 +---- .../DocumentationSignatureParserTests.cs | 8 -- .../TestAssemblyNameResolver.cs | 20 +++++ .../Trimming.Tests.Shared/TestResolver.cs | 18 ++++ 16 files changed, 173 insertions(+), 127 deletions(-) create mode 100644 src/tools/illink/src/linker/Linker/ITryResolve.cs create mode 100644 src/tools/illink/src/linker/Linker/TypeNameResolver.WithDiagnostics.cs create mode 100644 src/tools/illink/test/Trimming.Tests.Shared/TestAssemblyNameResolver.cs create mode 100644 src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/DocumentationSignatureGenerator.PartVisitor.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/DocumentationSignatureGenerator.PartVisitor.cs index 6f9815991ea194..5caa8cc8675454 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/DocumentationSignatureGenerator.PartVisitor.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/DocumentationSignatureGenerator.PartVisitor.cs @@ -8,8 +8,7 @@ namespace ILCompiler.Logging { - - public sealed partial class DocumentationSignatureGenerator + internal sealed partial class DocumentationSignatureGenerator { /// /// A visitor that generates the part of the documentation comment after the initial type diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj index 935d178a29ef85..de47993ee6f56b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj @@ -24,6 +24,14 @@ + + + + + + + + $(RuntimeBinDir) diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs index c312bddba0afa2..b2a6ea4cee9601 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs @@ -37,6 +37,7 @@ class LinkedMethodEntity : LinkedEntity } private readonly BaseAssemblyResolver originalsResolver; + private readonly TypeNameResolver originalsTypeNameResolver; private readonly ReaderParameters originalReaderParameters; private readonly AssemblyDefinition originalAssembly; private readonly TrimmedTestCaseResult testResult; @@ -69,6 +70,7 @@ public AssemblyChecker ( TrimmedTestCaseResult testResult) { this.originalsResolver = originalsResolver; + this.originalsTypeNameResolver = new TypeNameResolver (new TestResolver (), new TestAssemblyNameResolver (originalsResolver)); this.originalReaderParameters = originalReaderParameters; this.originalAssembly = original; this.testResult = testResult; @@ -1468,7 +1470,9 @@ internal IEnumerable VerifyLinkingOfOtherAssemblies (AssemblyDefinition } var expectedTypeName = checkAttrInAssembly.ConstructorArguments[1].Value.ToString ()!; - var expectedType = originalTargetAssembly.MainModule.GetType(expectedTypeName); + if (!originalsTypeNameResolver.TryResolveTypeName (originalTargetAssembly, expectedTypeName, out TypeReference? expectedTypeRef, out _)) + Assert.Fail($"Could not resolve original type `{expectedTypeName}' in assembly {assemblyName}"); + TypeDefinition expectedType = expectedTypeRef.Resolve (); linkedMembersInAssembly.TryGetValue(new AssemblyQualifiedToken(expectedType), out LinkedEntity? linkedTypeEntity); MetadataType? linkedType = linkedTypeEntity?.Entity as MetadataType; diff --git a/src/tools/illink/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs b/src/tools/illink/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs index 60e87246c7d0d7..6a4c664074cdd6 100644 --- a/src/tools/illink/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs +++ b/src/tools/illink/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs @@ -9,8 +9,7 @@ namespace Mono.Linker { - - public sealed partial class DocumentationSignatureGenerator + internal sealed partial class DocumentationSignatureGenerator { /// /// A visitor that generates the part of the documentation comment after the initial type diff --git a/src/tools/illink/src/linker/Linker/DocumentationSignatureGenerator.cs b/src/tools/illink/src/linker/Linker/DocumentationSignatureGenerator.cs index b76f714d520c62..8a13a977cf5014 100644 --- a/src/tools/illink/src/linker/Linker/DocumentationSignatureGenerator.cs +++ b/src/tools/illink/src/linker/Linker/DocumentationSignatureGenerator.cs @@ -12,7 +12,7 @@ namespace Mono.Linker /// Adapted from Roslyn's DocumentationCommentIDVisitor: /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.cs /// - public sealed partial class DocumentationSignatureGenerator + internal sealed partial class DocumentationSignatureGenerator { internal const string MethodPrefix = "M:"; internal const string FieldPrefix = "F:"; diff --git a/src/tools/illink/src/linker/Linker/DocumentationSignatureParser.cs b/src/tools/illink/src/linker/Linker/DocumentationSignatureParser.cs index 6b7d96ad5b09ab..eb2a3f4629e242 100644 --- a/src/tools/illink/src/linker/Linker/DocumentationSignatureParser.cs +++ b/src/tools/illink/src/linker/Linker/DocumentationSignatureParser.cs @@ -24,7 +24,7 @@ namespace Mono.Linker /// This API instead works with the Cecil OM. It can be used to refer to IL definitions /// where the signature of a member can contain references to instantiated generics. /// - public static class DocumentationSignatureParser + internal static class DocumentationSignatureParser { [Flags] public enum MemberType diff --git a/src/tools/illink/src/linker/Linker/ITryResolve.cs b/src/tools/illink/src/linker/Linker/ITryResolve.cs new file mode 100644 index 00000000000000..aa760547a3fb9c --- /dev/null +++ b/src/tools/illink/src/linker/Linker/ITryResolve.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Cecil; + +#nullable enable + +namespace Mono.Linker +{ + internal interface ITryResolveMetadata + { + MethodDefinition? TryResolve (MethodReference methodReference); + TypeDefinition? TryResolve (TypeReference typeReference); + TypeDefinition? TryResolve (ExportedType exportedType); + } + + internal interface ITryResolveAssemblyName + { + AssemblyDefinition? TryResolve (string assemblyName); + } +} diff --git a/src/tools/illink/src/linker/Linker/LinkContext.cs b/src/tools/illink/src/linker/Linker/LinkContext.cs index 21d6ffa7c94d36..31733b0c6ad565 100644 --- a/src/tools/illink/src/linker/Linker/LinkContext.cs +++ b/src/tools/illink/src/linker/Linker/LinkContext.cs @@ -59,17 +59,6 @@ public static class TargetRuntimeVersion public const int NET6 = 6; } - public interface ITryResolveMetadata - { - MethodDefinition? TryResolve (MethodReference methodReference); - TypeDefinition? TryResolve (TypeReference typeReference); - } - - internal interface ITryResolveAssemblyName - { - AssemblyDefinition? TryResolve (string assemblyName); - } - public class LinkContext : IMetadataResolver, ITryResolveMetadata, ITryResolveAssemblyName, IDisposable { diff --git a/src/tools/illink/src/linker/Linker/ModuleDefinitionExtensions.cs b/src/tools/illink/src/linker/Linker/ModuleDefinitionExtensions.cs index 64ad1df836f6f0..b79ff38c7d7c85 100644 --- a/src/tools/illink/src/linker/Linker/ModuleDefinitionExtensions.cs +++ b/src/tools/illink/src/linker/Linker/ModuleDefinitionExtensions.cs @@ -4,18 +4,19 @@ using System.Diagnostics.CodeAnalysis; using Mono.Cecil; +#nullable enable + namespace Mono.Linker { - public static class ModuleDefinitionExtensions + internal static class ModuleDefinitionExtensions { - public static bool IsCrossgened (this ModuleDefinition module) { return (module.Attributes & ModuleAttributes.ILOnly) == 0 && (module.Attributes & ModuleAttributes.ILLibrary) != 0; } - public static bool GetMatchingExportedType (this ModuleDefinition module, TypeDefinition typeDefinition, LinkContext context, [NotNullWhen (true)] out ExportedType? exportedType) + public static bool GetMatchingExportedType (this ModuleDefinition module, TypeDefinition typeDefinition, ITryResolveMetadata context, [NotNullWhen (true)] out ExportedType? exportedType) { exportedType = null; if (!module.HasExportedTypes) diff --git a/src/tools/illink/src/linker/Linker/TypeNameResolver.WithDiagnostics.cs b/src/tools/illink/src/linker/Linker/TypeNameResolver.WithDiagnostics.cs new file mode 100644 index 00000000000000..24bd45aba1877f --- /dev/null +++ b/src/tools/illink/src/linker/Linker/TypeNameResolver.WithDiagnostics.cs @@ -0,0 +1,90 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ILLink.Shared; +using ILLink.Shared.TrimAnalysis; +using Mono.Cecil; +using Mono.Linker; + +using TypeName = System.Reflection.Metadata.TypeName; +using AssemblyNameInfo = System.Reflection.Metadata.AssemblyNameInfo; + +namespace Mono.Linker +{ + internal sealed partial class TypeNameResolver + { + public bool TryResolveTypeName ( + string typeNameString, + in DiagnosticContext diagnosticContext, + [NotNullWhen (true)] out TypeReference? typeReference, + [NotNullWhen (true)] out List? typeResolutionRecords, + bool needsAssemblyName = true) + { + typeReference = null; + typeResolutionRecords = null; + if (string.IsNullOrEmpty (typeNameString)) + return false; + + if (!TypeName.TryParse (typeNameString, out TypeName? parsedTypeName, s_typeNameParseOptions)) + return false; + + typeResolutionRecords = new List (); + AssemblyDefinition? typeAssembly; + if (parsedTypeName.AssemblyName is AssemblyNameInfo assemblyName) { + typeAssembly = _assemblyResolver.TryResolve (assemblyName.Name); + if (typeAssembly is null) { + typeResolutionRecords = null; + return false; + } + + typeReference = ResolveTypeName (typeAssembly, parsedTypeName, typeResolutionRecords); + if (typeReference == null) { + typeResolutionRecords = null; + } + + return typeReference != null; + } + + // If parsedTypeName doesn't have an assembly name in it but it does have a namespace, + // search for the type in the calling object's assembly. If not found, look in the core + // assembly. + ICustomAttributeProvider? provider = diagnosticContext.Origin.Provider; + typeAssembly = provider switch { + AssemblyDefinition asm => asm, + TypeDefinition type => type.Module?.Assembly, + IMemberDefinition member => member.DeclaringType.Module.Assembly, + null => null, + _ => throw new NotSupportedException () + }; + + if (typeAssembly != null && TryResolveTypeName (typeAssembly, parsedTypeName, out typeReference, typeResolutionRecords)) + return true; + + // If type is not found in the caller's assembly, try in core assembly. + typeAssembly = _assemblyResolver.TryResolve (PlatformAssemblies.CoreLib); + if (typeAssembly != null && TryResolveTypeName (typeAssembly, parsedTypeName, out typeReference, typeResolutionRecords)) + return true; + + // It is common to use Type.GetType for looking if a type is available. + // If no type was found only warn and return null. + if (needsAssemblyName && provider != null) + diagnosticContext.AddDiagnostic (DiagnosticId.TypeWasNotFoundInAssemblyNorBaseLibrary, typeNameString); + + typeResolutionRecords = null; + return false; + + bool TryResolveTypeName (AssemblyDefinition assemblyDefinition, TypeName? typeName, [NotNullWhen (true)] out TypeReference? typeReference, List typeResolutionRecords) + { + typeReference = null; + if (assemblyDefinition == null) + return false; + + typeReference = ResolveTypeName (assemblyDefinition, typeName, typeResolutionRecords); + return typeReference != null; + } + } + } +} diff --git a/src/tools/illink/src/linker/Linker/TypeNameResolver.cs b/src/tools/illink/src/linker/Linker/TypeNameResolver.cs index f700471c980e76..3bb0e4452cb2dd 100644 --- a/src/tools/illink/src/linker/Linker/TypeNameResolver.cs +++ b/src/tools/illink/src/linker/Linker/TypeNameResolver.cs @@ -1,14 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using ILLink.Shared; -using ILLink.Shared.TrimAnalysis; using Mono.Cecil; -using Mono.Linker; using TypeName = System.Reflection.Metadata.TypeName; using TypeNameParseOptions = System.Reflection.Metadata.TypeNameParseOptions; @@ -19,7 +15,7 @@ namespace Mono.Linker { - internal sealed class TypeNameResolver + internal sealed partial class TypeNameResolver { readonly ITryResolveMetadata _metadataResolver; readonly ITryResolveAssemblyName _assemblyResolver; @@ -34,77 +30,6 @@ public TypeNameResolver (ITryResolveMetadata metadataResolver, ITryResolveAssemb _assemblyResolver = assemblyNameResolver; } - public bool TryResolveTypeName ( - string typeNameString, - in DiagnosticContext diagnosticContext, - [NotNullWhen (true)] out TypeReference? typeReference, - [NotNullWhen (true)] out List? typeResolutionRecords, - bool needsAssemblyName = true) - { - typeReference = null; - typeResolutionRecords = null; - if (string.IsNullOrEmpty (typeNameString)) - return false; - - if (!TypeName.TryParse (typeNameString, out TypeName? parsedTypeName, s_typeNameParseOptions)) - return false; - - typeResolutionRecords = new List (); - AssemblyDefinition? typeAssembly; - if (parsedTypeName.AssemblyName is AssemblyNameInfo assemblyName) { - typeAssembly = _assemblyResolver.TryResolve (assemblyName.Name); - if (typeAssembly is null) { - typeResolutionRecords = null; - return false; - } - - typeReference = ResolveTypeName (typeAssembly, parsedTypeName, typeResolutionRecords); - if (typeReference == null) { - typeResolutionRecords = null; - } - - return typeReference != null; - } - - // If parsedTypeName doesn't have an assembly name in it but it does have a namespace, - // search for the type in the calling object's assembly. If not found, look in the core - // assembly. - ICustomAttributeProvider? provider = diagnosticContext.Origin.Provider; - typeAssembly = provider switch { - AssemblyDefinition asm => asm, - TypeDefinition type => type.Module?.Assembly, - IMemberDefinition member => member.DeclaringType.Module.Assembly, - null => null, - _ => throw new NotSupportedException () - }; - - if (typeAssembly != null && TryResolveTypeName (typeAssembly, parsedTypeName, out typeReference, typeResolutionRecords)) - return true; - - // If type is not found in the caller's assembly, try in core assembly. - typeAssembly = _assemblyResolver.TryResolve (PlatformAssemblies.CoreLib); - if (typeAssembly != null && TryResolveTypeName (typeAssembly, parsedTypeName, out typeReference, typeResolutionRecords)) - return true; - - // It is common to use Type.GetType for looking if a type is available. - // If no type was found only warn and return null. - if (needsAssemblyName && provider != null) - diagnosticContext.AddDiagnostic (DiagnosticId.TypeWasNotFoundInAssemblyNorBaseLibrary, typeNameString); - - typeResolutionRecords = null; - return false; - - bool TryResolveTypeName (AssemblyDefinition assemblyDefinition, TypeName? typeName, [NotNullWhen (true)] out TypeReference? typeReference, List typeResolutionRecords) - { - typeReference = null; - if (assemblyDefinition == null) - return false; - - typeReference = ResolveTypeName (assemblyDefinition, typeName, typeResolutionRecords); - return typeReference != null; - } - } - public bool TryResolveTypeName ( AssemblyDefinition assembly, string typeNameString, diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 6189eae6d794cb..35f6ca0bd04d4e 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -11,7 +11,7 @@ namespace Mono.Linker { - public static class TypeReferenceExtensions + internal static class TypeReferenceExtensions { public static string GetDisplayName (this TypeReference type) { diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index fd9eed7dec3386..f6480e554c3528 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -47,31 +47,11 @@ public ResultChecker (BaseAssemblyResolver originalsResolver, BaseAssemblyResolv { _originalsResolver = originalsResolver; _linkedResolver = linkedResolver; - var testResolver = new TestResolver (_linkedResolver); - _linkedTypeNameResolver = new TypeNameResolver (testResolver, testResolver); + _linkedTypeNameResolver = new TypeNameResolver (new TestResolver (), new TestAssemblyNameResolver (_linkedResolver)); _originalReaderParameters = originalReaderParameters; _linkedReaderParameters = linkedReaderParameters; } - struct TestResolver : ITryResolveAssemblyName, ITryResolveMetadata - { - readonly BaseAssemblyResolver _assemblyResolver; - - public TestResolver (BaseAssemblyResolver resolver) - { - _assemblyResolver = resolver; - } - - public AssemblyDefinition TryResolve (string assemblyName) - => _assemblyResolver.Resolve (new AssemblyNameReference (assemblyName, null), new ReaderParameters ()); - - public MethodDefinition TryResolve (MethodReference methodReference) => methodReference.Resolve (); - - public FieldDefinition TryResolve (FieldReference fieldReference) => fieldReference.Resolve (); - - public TypeDefinition TryResolve (TypeReference typeReference) => typeReference.Resolve (); - } - protected static void ValidateTypeRefsHaveValidAssemblyRefs (AssemblyDefinition linked) { foreach (var typeRef in linked.MainModule.GetTypeReferences ()) { diff --git a/src/tools/illink/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs b/src/tools/illink/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs index 7b442116c188f5..89e3e18d891e97 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs @@ -12,14 +12,6 @@ namespace Mono.Linker.Tests { - sealed class TestResolver : ITryResolveMetadata - { - public static TestResolver Instance => new TestResolver (); - - public MethodDefinition TryResolve (MethodReference methodReference) => methodReference.Resolve (); - public TypeDefinition TryResolve (TypeReference typeReference) => typeReference.Resolve (); - } - [TestFixture] public class DocumentationSignatureParserTests { diff --git a/src/tools/illink/test/Trimming.Tests.Shared/TestAssemblyNameResolver.cs b/src/tools/illink/test/Trimming.Tests.Shared/TestAssemblyNameResolver.cs new file mode 100644 index 00000000000000..f55dc4b9154a2a --- /dev/null +++ b/src/tools/illink/test/Trimming.Tests.Shared/TestAssemblyNameResolver.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Cecil; + +namespace Mono.Linker.Tests.TestCasesRunner +{ + struct TestAssemblyNameResolver : ITryResolveAssemblyName + { + readonly BaseAssemblyResolver _assemblyResolver; + + public TestAssemblyNameResolver (BaseAssemblyResolver resolver) + { + _assemblyResolver = resolver; + } + + public AssemblyDefinition TryResolve (string assemblyName) + => _assemblyResolver.Resolve (new AssemblyNameReference (assemblyName, null), new ReaderParameters ()); + } +} diff --git a/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs b/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs new file mode 100644 index 00000000000000..d99285f7b1f5bf --- /dev/null +++ b/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Cecil; + +namespace Mono.Linker.Tests.TestCasesRunner +{ + struct TestResolver : ITryResolveMetadata + { + public static TestResolver Instance => new TestResolver (); + + public MethodDefinition TryResolve (MethodReference methodReference) => methodReference.Resolve (); + + public TypeDefinition TryResolve (TypeReference typeReference) => typeReference.Resolve (); + + public TypeDefinition TryResolve (ExportedType exportedType) => exportedType.Resolve (); + } +} From 0ee862644934b2c437d1255abe71318be7971fc0 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Jun 2024 21:01:54 +0000 Subject: [PATCH 4/4] PR feedback - Remove TestResolver.Instance - Test type name with '/' --- .../Reflection/Dependencies/EscapedTypeNames.il | 5 +++++ .../Reflection/TypeUsedViaReflection.cs | 4 +++- .../Tests/DocumentationSignatureParserTests.cs | 8 ++++---- .../illink/test/Trimming.Tests.Shared/TestResolver.cs | 2 -- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/EscapedTypeNames.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/EscapedTypeNames.il index e7913bd63df5e8..392f5903be75eb 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/EscapedTypeNames.il +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/EscapedTypeNames.il @@ -20,4 +20,9 @@ extends [System.Runtime]System.Object { } + + .class nested public auto ansi sealed beforefieldinit 'Nes/ted' + extends [System.Runtime]System.Object + { + } } // end of class Library.TypeInfoCalls diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs index 24c2fae4ab2d4b..dcf6be970e8197 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; @@ -13,6 +13,7 @@ namespace Mono.Linker.Tests.Cases.Reflection [SetupCompileBefore ("library.dll", new[] { "Dependencies/EscapedTypeNames.il" })] [KeptTypeInAssembly ("library", "Library.Not\\+Nested")] [KeptTypeInAssembly ("library", "Library.Not\\+Nested+Nes\\\\ted")] + [KeptTypeInAssembly ("library", "Library.Not\\+Nested+Nes/ted")] [KeptDelegateCacheField ("0", nameof (AssemblyResolver))] [KeptDelegateCacheField ("1", nameof (GetTypeFromAssembly))] public class TypeUsedViaReflection @@ -476,6 +477,7 @@ static void TestEscapedTypeName () { var typeKept = Type.GetType ("Library.Not\\+Nested, library"); typeKept = Type.GetType ("Library.Not\\+Nested+Nes\\\\ted, library"); + typeKept = Type.GetType ("Library.Not\\+Nested+Nes/ted, library"); } } } diff --git a/src/tools/illink/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs b/src/tools/illink/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs index 89e3e18d891e97..4d50fa3d812161 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs @@ -43,7 +43,7 @@ public static void CheckUniqueParsedString (IMemberDefinition member, string inp { var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); - var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module, TestResolver.Instance); + var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module, new TestResolver ()); Assert.AreEqual (1, parseResults.Count ()); Assert.AreEqual (member, parseResults.First ()); } @@ -51,7 +51,7 @@ public static void CheckUniqueParsedString (IMemberDefinition member, string inp public static void CheckGeneratedString (IMemberDefinition member, string expected) { var builder = new StringBuilder (); - DocumentationSignatureGenerator.VisitMember (member, builder, TestResolver.Instance); + DocumentationSignatureGenerator.VisitMember (member, builder, new TestResolver ()); Assert.AreEqual (expected, builder.ToString ()); } @@ -59,7 +59,7 @@ public static void CheckParsedString (IMemberDefinition member, string input) { var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); - var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module, TestResolver.Instance); + var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module, new TestResolver ()); CollectionAssert.Contains (parseResults, member); } @@ -67,7 +67,7 @@ public static void CheckUnresolvedDocumentationSignature (IMemberDefinition memb { var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); - var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module, TestResolver.Instance); + var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module, new TestResolver ()); CollectionAssert.DoesNotContain (parseResults, member); } diff --git a/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs b/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs index d99285f7b1f5bf..af37fcfbe6487d 100644 --- a/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs +++ b/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs @@ -7,8 +7,6 @@ namespace Mono.Linker.Tests.TestCasesRunner { struct TestResolver : ITryResolveMetadata { - public static TestResolver Instance => new TestResolver (); - public MethodDefinition TryResolve (MethodReference methodReference) => methodReference.Resolve (); public TypeDefinition TryResolve (TypeReference typeReference) => typeReference.Resolve ();