зеркало из https://github.com/microsoft/DbgShell.git
CrackTemplate: recognize dotnet-generated names
This commit is contained in:
Родитель
006de74f54
Коммит
9b4149690a
|
@ -236,10 +236,100 @@ namespace MS.Dbg
|
|||
if( ')' == name[ name.Length - 1 ] )
|
||||
return false;
|
||||
|
||||
if( _LooksLikeDotNetGeneratedName( name ) )
|
||||
return false;
|
||||
|
||||
return _LooksLikeATemplateNameInner( name, startIdx, ref angleBracketIdx );
|
||||
}
|
||||
|
||||
|
||||
private static bool _LooksLikeDotNetGeneratedName( string name )
|
||||
{
|
||||
// See roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs
|
||||
// https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs
|
||||
//
|
||||
// This looks like something that could be done handily with a regex, but I
|
||||
// really don't want to spin up regex here; this stuff needs to be as quick as
|
||||
// possible. So below is a simple, hand-rolled state machine. We work
|
||||
// backwards from the end of the string. The states map to the parts of a
|
||||
// dotnet-generated name like so:
|
||||
//
|
||||
// "Blahblahblahblah<MaybeStuffHere>X__ABCD"
|
||||
// /// \__//
|
||||
// state_singleLetterOrDigit---/// / /
|
||||
// state_underscore2---// / /
|
||||
// state_underscore1---/ / /
|
||||
// state_identifier---/ /
|
||||
// state_start---/
|
||||
//
|
||||
|
||||
const int state_start = 0;
|
||||
const int state_identifier = 1;
|
||||
const int state_underscore1 = 2;
|
||||
const int state_underscore2 = 3;
|
||||
const int state_singleLetterOrDigit = 4;
|
||||
|
||||
// No "invalid" state: we just directly return false.
|
||||
|
||||
int state = state_start;
|
||||
|
||||
int idx = name.Length;
|
||||
|
||||
// The minimum length of such a name is 7: "A<>B__C"
|
||||
// So if we get to index 1 before finding the '>', we can stop.
|
||||
|
||||
if( idx < 7 )
|
||||
return false;
|
||||
|
||||
while( --idx > 1 )
|
||||
{
|
||||
char c = name[ idx ];
|
||||
|
||||
switch( state )
|
||||
{
|
||||
case state_start:
|
||||
if( Char.IsLetterOrDigit( c ) )
|
||||
state = state_identifier;
|
||||
else
|
||||
return false;
|
||||
|
||||
break;
|
||||
case state_identifier:
|
||||
if( c == '_' )
|
||||
state = state_underscore1;
|
||||
else if( !Char.IsLetterOrDigit( c ) )
|
||||
return false;
|
||||
|
||||
break;
|
||||
case state_underscore1:
|
||||
if( c == '_' )
|
||||
state = state_underscore2;
|
||||
else
|
||||
return false;
|
||||
|
||||
break;
|
||||
case state_underscore2:
|
||||
if( Char.IsLetterOrDigit( c ) )
|
||||
state = state_singleLetterOrDigit;
|
||||
else
|
||||
return false;
|
||||
|
||||
break;
|
||||
case state_singleLetterOrDigit:
|
||||
if( c == '>' )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
default:
|
||||
throw new Exception( "unexpected: invalid state" );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} // end _LooksLikeDotNetGeneratedName()
|
||||
|
||||
|
||||
// Takes care of checks that we need to do repeatedly, after the one-time checks
|
||||
// in the outer method.
|
||||
private static bool _LooksLikeATemplateNameInner( string name,
|
||||
|
@ -438,7 +528,7 @@ namespace MS.Dbg
|
|||
}
|
||||
else
|
||||
{
|
||||
problem = Util.Sprintf( "Unexpected characters at position {0}.", idx );
|
||||
problem = Util.Sprintf( "Unexpected characters at position {0}.", idx + 1 );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -195,6 +195,43 @@ Describe "TemplateMatching" {
|
|||
$ti1.Parameters[ 0 ].FullName | Should Be '<lambda_fcd0511faf603cca0b560671c4e52d53>'
|
||||
}
|
||||
|
||||
It "can deal with non-template dotnet names" {
|
||||
|
||||
# The CLR uses brackets to make types not usable in C#; it's not a template.
|
||||
$typeName1 = 'MS.Dbg.IMAGEHLP_MODULEW64+<LoadedImageName>e__FixedBuffer'
|
||||
$ti1 = [MS.Dbg.DbgTemplateNode]::CrackTemplate( $typeName1 )
|
||||
$ti1.IsTemplate | Should Be $false
|
||||
$ti1.FullName | Should Be $typeName1
|
||||
$ti1.TemplateName | Should Be $typeName1
|
||||
([MS.Dbg.DbgTemplateNode]::LooksLikeATemplateName( $typeName1 )) | Should Be $false
|
||||
|
||||
# This is a "fake" name (not observed in the wild; it's the same as the previous
|
||||
# one but with the content between the angle brackets removed), but according to
|
||||
# dotnet source, this sort of name should be possible.
|
||||
$typeName1 = 'MS.Dbg.IMAGEHLP_MODULEW64+<>e__FixedBuffer'
|
||||
$ti1 = [MS.Dbg.DbgTemplateNode]::CrackTemplate( $typeName1 )
|
||||
$ti1.IsTemplate | Should Be $false
|
||||
$ti1.FullName | Should Be $typeName1
|
||||
$ti1.TemplateName | Should Be $typeName1
|
||||
([MS.Dbg.DbgTemplateNode]::LooksLikeATemplateName( $typeName1 )) | Should Be $false
|
||||
|
||||
# Minimal archetypal dotnet generated name:
|
||||
$typeName1 = 'A<>9__X'
|
||||
$ti1 = [MS.Dbg.DbgTemplateNode]::CrackTemplate( $typeName1 )
|
||||
$ti1.IsTemplate | Should Be $false
|
||||
$ti1.FullName | Should Be $typeName1
|
||||
$ti1.TemplateName | Should Be $typeName1
|
||||
([MS.Dbg.DbgTemplateNode]::LooksLikeATemplateName( $typeName1 )) | Should Be $false
|
||||
|
||||
# now with something inside the brackets
|
||||
$typeName1 = 'A<B>9__X'
|
||||
$ti1 = [MS.Dbg.DbgTemplateNode]::CrackTemplate( $typeName1 )
|
||||
$ti1.IsTemplate | Should Be $false
|
||||
$ti1.FullName | Should Be $typeName1
|
||||
$ti1.TemplateName | Should Be $typeName1
|
||||
([MS.Dbg.DbgTemplateNode]::LooksLikeATemplateName( $typeName1 )) | Should Be $false
|
||||
}
|
||||
|
||||
It "throws if the multi-match wildcard doesn't come last" {
|
||||
|
||||
[bool] $itThrew = $false
|
||||
|
|
Загрузка…
Ссылка в новой задаче