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

[EE] Prettify compiler-generated field names with IDkmClrFullNameProvider.GetClrMemberName #50931

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Globalization;

namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal static partial class GeneratedNames
{
internal const char DotReplacementInTypeNames = '-';
internal const string AnonymousNamePrefix = "<>f__AnonymousType";

internal static bool TryParseAnonymousTypeTemplateName(string name, out int index)
{
// No callers require anonymous types from net modules,
// so names with module id are ignored.
if (name.StartsWith(AnonymousNamePrefix, StringComparison.Ordinal))
{
if (int.TryParse(name.Substring(AnonymousNamePrefix.Length), NumberStyles.None, CultureInfo.InvariantCulture, out index))
{
return true;
}
}

index = -1;
return false;
}
Copy link
Member

@cston cston Feb 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to move these methods to a separate file? Moving the methods makes it difficult to track changes.

It looks like it is necessary to move the methods to allow including this file in ResultProvider.csproj.

Comparing this file with the previous version, it looks like the only change is including IndexOfBalancedParenthesis().


// The type of generated name. See TryParseGeneratedName.
internal static GeneratedNameKind GetKind(string name)
{
GeneratedNameKind kind;
int openBracketOffset;
int closeBracketOffset;
return TryParseGeneratedName(name, out kind, out openBracketOffset, out closeBracketOffset) ? kind : GeneratedNameKind.None;
}

// Parse the generated name. Returns true for names of the form
// [CS$]<[middle]>c[__[suffix]] where [CS$] is included for certain
// generated names, where [middle] and [__[suffix]] are optional,
// and where c is a single character in [1-9a-z]
// (csharp\LanguageAnalysis\LIB\SpecialName.cpp).
internal static bool TryParseGeneratedName(
string name,
out GeneratedNameKind kind,
out int openBracketOffset,
out int closeBracketOffset)
{
openBracketOffset = -1;
if (name.StartsWith("CS$<", StringComparison.Ordinal))
{
openBracketOffset = 3;
}
else if (name.StartsWith("<", StringComparison.Ordinal))
{
openBracketOffset = 0;
}

if (openBracketOffset >= 0)
{
closeBracketOffset = name.IndexOfBalancedParenthesis(openBracketOffset, '>');
if (closeBracketOffset >= 0 && closeBracketOffset + 1 < name.Length)
{
int c = name[closeBracketOffset + 1];
if ((c >= '1' && c <= '9') || (c >= 'a' && c <= 'z')) // Note '0' is not special.
{
kind = (GeneratedNameKind)c;
return true;
}
}
}

kind = GeneratedNameKind.None;
openBracketOffset = -1;
closeBracketOffset = -1;
return false;
}

internal static int IndexOfBalancedParenthesis(this string str, int openingOffset, char closing)
{
char opening = str[openingOffset];

int depth = 1;
for (int i = openingOffset + 1; i < str.Length; i++)
{
var c = str[i];
if (c == opening)
{
depth++;
}
else if (c == closing)
{
depth--;
if (depth == 0)
{
return i;
}
}
}

return -1;
}

internal static bool TryParseSourceMethodNameFromGeneratedName(string generatedName, GeneratedNameKind requiredKind, out string methodName)
{
int openBracketOffset;
int closeBracketOffset;
GeneratedNameKind kind;
if (!TryParseGeneratedName(generatedName, out kind, out openBracketOffset, out closeBracketOffset))
{
methodName = null;
return false;
}

if (requiredKind != 0 && kind != requiredKind)
{
methodName = null;
return false;
}

methodName = generatedName.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);

if (kind.IsTypeName())
{
methodName = methodName.Replace(DotReplacementInTypeNames, '.');
}

return true;
}

// Extracts the slot index from a name of a field that stores hoisted variables or awaiters.
// Such a name ends with "__{slot index + 1}".
// Returned slot index is >= 0.
internal static bool TryParseSlotIndex(string fieldName, out int slotIndex)
{
int lastUnder = fieldName.LastIndexOf('_');
if (lastUnder - 1 < 0 || lastUnder == fieldName.Length || fieldName[lastUnder - 1] != '_')
{
slotIndex = -1;
return false;
}

if (int.TryParse(fieldName.Substring(lastUnder + 1), NumberStyles.None, CultureInfo.InvariantCulture, out slotIndex) && slotIndex >= 1)
{
slotIndex--;
return true;
}

slotIndex = -1;
return false;
}
}
}
118 changes: 0 additions & 118 deletions src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
internal static partial class GeneratedNames
{
internal const string SynthesizedLocalNamePrefix = "CS$";
internal const char DotReplacementInTypeNames = '-';
private const string SuffixSeparator = "__";
private const char IdSeparator = '_';
private const char GenerationSeparator = '#';
Expand Down Expand Up @@ -72,24 +71,6 @@ internal static string MakeAnonymousTypeTemplateName(int index, int submissionSl
return name;
}

internal const string AnonymousNamePrefix = "<>f__AnonymousType";

internal static bool TryParseAnonymousTypeTemplateName(string name, out int index)
{
// No callers require anonymous types from net modules,
// so names with module id are ignored.
if (name.StartsWith(AnonymousNamePrefix, StringComparison.Ordinal))
{
if (int.TryParse(name.Substring(AnonymousNamePrefix.Length), NumberStyles.None, CultureInfo.InvariantCulture, out index))
{
return true;
}
}

index = -1;
return false;
}

internal static string MakeAnonymousTypeBackingFieldName(string propertyName)
{
return "<" + propertyName + ">i__Field";
Expand Down Expand Up @@ -280,111 +261,12 @@ internal static string MakeHoistedLocalFieldName(SynthesizedLocalKind kind, int
return result.ToStringAndFree();
}

// The type of generated name. See TryParseGeneratedName.
internal static GeneratedNameKind GetKind(string name)
{
GeneratedNameKind kind;
int openBracketOffset;
int closeBracketOffset;
return TryParseGeneratedName(name, out kind, out openBracketOffset, out closeBracketOffset) ? kind : GeneratedNameKind.None;
}

// Parse the generated name. Returns true for names of the form
// [CS$]<[middle]>c[__[suffix]] where [CS$] is included for certain
// generated names, where [middle] and [__[suffix]] are optional,
// and where c is a single character in [1-9a-z]
// (csharp\LanguageAnalysis\LIB\SpecialName.cpp).
internal static bool TryParseGeneratedName(
string name,
out GeneratedNameKind kind,
out int openBracketOffset,
out int closeBracketOffset)
{
openBracketOffset = -1;
if (name.StartsWith("CS$<", StringComparison.Ordinal))
{
openBracketOffset = 3;
}
else if (name.StartsWith("<", StringComparison.Ordinal))
{
openBracketOffset = 0;
}

if (openBracketOffset >= 0)
{
closeBracketOffset = name.IndexOfBalancedParenthesis(openBracketOffset, '>');
if (closeBracketOffset >= 0 && closeBracketOffset + 1 < name.Length)
{
int c = name[closeBracketOffset + 1];
if ((c >= '1' && c <= '9') || (c >= 'a' && c <= 'z')) // Note '0' is not special.
{
kind = (GeneratedNameKind)c;
return true;
}
}
}

kind = GeneratedNameKind.None;
openBracketOffset = -1;
closeBracketOffset = -1;
return false;
}

internal static bool TryParseSourceMethodNameFromGeneratedName(string generatedName, GeneratedNameKind requiredKind, out string methodName)
{
int openBracketOffset;
int closeBracketOffset;
GeneratedNameKind kind;
if (!TryParseGeneratedName(generatedName, out kind, out openBracketOffset, out closeBracketOffset))
{
methodName = null;
return false;
}

if (requiredKind != 0 && kind != requiredKind)
{
methodName = null;
return false;
}

methodName = generatedName.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);

if (kind.IsTypeName())
{
methodName = methodName.Replace(DotReplacementInTypeNames, '.');
}

return true;
}

internal static string AsyncAwaiterFieldName(int slotIndex)
{
Debug.Assert((char)GeneratedNameKind.AwaiterField == 'u');
return "<>u__" + StringExtensions.GetNumeral(slotIndex + 1);
}

// Extracts the slot index from a name of a field that stores hoisted variables or awaiters.
// Such a name ends with "__{slot index + 1}".
// Returned slot index is >= 0.
internal static bool TryParseSlotIndex(string fieldName, out int slotIndex)
{
int lastUnder = fieldName.LastIndexOf('_');
if (lastUnder - 1 < 0 || lastUnder == fieldName.Length || fieldName[lastUnder - 1] != '_')
{
slotIndex = -1;
return false;
}

if (int.TryParse(fieldName.Substring(lastUnder + 1), NumberStyles.None, CultureInfo.InvariantCulture, out slotIndex) && slotIndex >= 1)
{
slotIndex--;
return true;
}

slotIndex = -1;
return false;
}

internal static string MakeCachedFrameInstanceFieldName()
{
Debug.Assert((char)GeneratedNameKind.LambdaCacheField == '9');
Expand Down
25 changes: 0 additions & 25 deletions src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,31 +226,6 @@ internal static string Unquote(this string arg, out bool quoted)
}
}

internal static int IndexOfBalancedParenthesis(this string str, int openingOffset, char closing)
{
char opening = str[openingOffset];

int depth = 1;
for (int i = openingOffset + 1; i < str.Length; i++)
{
var c = str[i];
if (c == opening)
{
depth++;
}
else if (c == closing)
{
depth--;
if (depth == 0)
{
return i;
}
}
}

return -1;
}

// String isn't IEnumerable<char> in the current Portable profile.
internal static char First(this string arg)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#nullable disable

using System.Collections.ObjectModel;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.PooledObjects;
using Type = Microsoft.VisualStudio.Debugger.Metadata.Type;
Expand Down Expand Up @@ -47,6 +48,21 @@ internal override string TrimAndGetFormatSpecifiers(string expression, out ReadO
return RemoveLeadingAndTrailingContent(expression, 0, expression.Length, IsWhitespace, ch => ch == ';' || IsWhitespace(ch));
}

/// <summary>
/// Parses a compiler-generated name and returns the simpler user-visible name.
/// </summary>
internal override string PrettifyCompilerGeneratedName(string name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps SimplifyCompilerGeneratedName().

{
if (!GeneratedNames.TryParseGeneratedName(name, out GeneratedNameKind kind, out int openBracketOffset, out int closeBracketOffset) ||
kind == GeneratedNameKind.None)
{
return name;
}

string result = name.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
return result;
}

private static string RemoveComments(string expression)
{
var pooledBuilder = PooledStringBuilder.GetInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
<Compile Include="..\..\..\..\..\Compilers\CSharp\Portable\SymbolDisplay\ObjectDisplay.cs">
<Link>Compiler\SymbolDisplay\ObjectDisplay.cs</Link>
</Compile>
<Compile Include="..\..\..\..\..\Compilers\CSharp\Portable\Symbols\Synthesized\GeneratedNameKind.cs">
<Link>Compiler\Symbols\Synthesized\GeneratedNameKind.cs</Link>
</Compile>
<Compile Include="..\..\..\..\..\Compilers\CSharp\Portable\Symbols\Synthesized\GeneratedNames.Parser.cs">
<Link>Compiler\Symbols\Synthesized\GeneratedNames.Parser.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Core\Source\ResultProvider\NetFX20\ResultProvider.NetFX20.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
<Compile Include="..\..\..\..\..\Compilers\CSharp\Portable\SymbolDisplay\ObjectDisplay.cs">
<Link>Compiler\SymbolDisplay\ObjectDisplay.cs</Link>
</Compile>
<Compile Include="..\..\..\..\..\Compilers\CSharp\Portable\Symbols\Synthesized\GeneratedNameKind.cs">
<Link>Compiler\Symbols\Synthesized\GeneratedNameKind.cs</Link>
</Compile>
<Compile Include="..\..\..\..\..\Compilers\CSharp\Portable\Symbols\Synthesized\GeneratedNames.Parser.cs">
<Link>Compiler\Symbols\Synthesized\GeneratedNames.Parser.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\..\..\..\Core\Source\ResultProvider\Portable\Microsoft.CodeAnalysis.ResultProvider.csproj" />
Expand Down
Loading