@ -55,7 +55,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -55,7 +55,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILTransformContext context ;
List < string > currentLowerCaseTypeOrMemberNames ;
Dictionary < string , int > reservedVariableNames ;
Dictionary < MethodDefinitionHandle , string > localFunctionMapping ;
HashSet < ILVariable > loopCounters ;
const char maxLoopVariableName = 'n' ;
int numDisplayClassLocals ;
@ -75,7 +74,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -75,7 +74,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
currentLowerCaseTypeOrMemberNames . Add ( name ) ;
AddExistingName ( reservedVariableNames , name ) ;
}
localFunctionMapping = new Dictionary < MethodDefinitionHandle , string > ( ) ;
loopCounters = CollectLoopCounters ( function ) ;
foreach ( var f in function . Descendants . OfType < ILFunction > ( ) )
{
@ -153,10 +151,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -153,10 +151,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
numDisplayClassLocals = 0 ;
foreach ( ILFunction f in function . Descendants . OfType < ILFunction > ( ) . Reverse ( ) )
{
PerformAssignment ( f ) ;
}
PerformAssignment ( function ) ;
}
static IEnumerable < string > CollectAllLowerCaseMemberNames ( ITypeDefinition type )
@ -195,128 +190,131 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -195,128 +190,131 @@ namespace ICSharpCode.Decompiler.IL.Transforms
void PerformAssignment ( ILFunction function )
{
// remove unused variables before assigning names
function . Variables . RemoveDead ( ) ;
Dictionary < int , string > assignedLocalSignatureIndices = new Dictionary < int , string > ( ) ;
foreach ( var v in function . Variables . OrderBy ( v = > v . Name ) )
var localFunctionMapping = new Dictionary < MethodDefinitionHandle , string > ( ) ;
var variableMapping = new Dictionary < ILVariable , string > ( ILVariableEqualityComparer . Instance ) ;
var assignedLocalSignatureIndices = new Dictionary < ( ILFunction , int ) , string > ( ) ;
foreach ( var inst in function . Descendants )
{
switch ( v . Kind )
if ( inst is ILFunction { Kind : ILFunctionKind . LocalFunction } localFunction )
{
case VariableKind . Parameter :
// Parameter names are handled in ILReader.CreateILVariable
// and CSharpDecompiler.FixParameterNames
break ;
case VariableKind . InitializerTarget : // keep generated names
AddExistingName ( reservedVariableNames , v . Name ) ;
break ;
case VariableKind . DisplayClassLocal :
v . Name = "CS$<>8__locals" + ( numDisplayClassLocals + + ) ;
break ;
case VariableKind . Local when v . Index ! = null :
if ( assignedLocalSignatureIndices . TryGetValue ( v . Index . Value , out string name ) )
// assign names to local functions
if ( ! LocalFunctionDecompiler . ParseLocalFunctionName ( localFunction . Name , out _ , out var newName ) | | ! IsValidName ( newName ) )
newName = null ;
if ( newName = = null )
{
string nameWithoutNumber = "f" ;
if ( ! reservedVariableNames . TryGetValue ( nameWithoutNumber , out int currentIndex ) )
{
// make sure all local ILVariables that refer to the same slot in the locals signature
// are assigned the same name.
v . Name = name ;
currentIndex = 1 ;
}
int count = Math . Max ( 1 , currentIndex ) + 1 ;
reservedVariableNames [ nameWithoutNumber ] = count ;
if ( count > 1 )
{
newName = nameWithoutNumber + count . ToString ( ) ;
}
else
{
AssignName ( ) ;
// Remember the newly assigned name:
assignedLocalSignatureIndices . Add ( v . Index . Value , v . Name ) ;
newName = nameWithoutNumber ;
}
break ;
default :
AssignName ( ) ;
break ;
}
localFunction . Name = newName ;
localFunction . ReducedMethod . Name = newName ;
localFunctionMapping [ ( MethodDefinitionHandle ) localFunction . ReducedMethod . MetadataToken ] = newName ;
}
void AssignName ( )
else if ( inst is IInstructionWithVariableOperand i )
{
if ( v . HasGeneratedName | | ! IsValidName ( v . Name ) | | ConflictWithLocal ( v ) )
var v = i . Variable ;
// if there is already a valid name for the variable slot, just use it
if ( variableMapping . TryGetValue ( v , out string name ) )
{
// don't use the name from the debug symbols if it looks like a generated name
v . Name = null ;
v . Name = name ;
continue ;
}
else
switch ( v . Kind )
{
// use the name from the debug symbols and update index appended to duplicates
string nameWithoutNumber = SplitName ( v . Name , out int newIndex ) ;
if ( ! reservedVariableNames . TryGetValue ( nameWithoutNumber , out int currentIndex ) )
{
currentIndex = 1 ;
}
reservedVariableNames [ nameWithoutNumber ] = Math . Max ( newIndex , currentIndex ) ;
case VariableKind . Parameter :
// Parameter names are handled in ILReader.CreateILVariable
// and CSharpDecompiler.FixParameterNames
break ;
case VariableKind . InitializerTarget : // keep generated names
AddExistingName ( reservedVariableNames , v . Name ) ;
break ;
case VariableKind . DisplayClassLocal :
v . Name = "CS$<>8__locals" + ( numDisplayClassLocals + + ) ;
break ;
case VariableKind . Local when v . Index ! = null :
if ( assignedLocalSignatureIndices . TryGetValue ( ( v . Function , v . Index . Value ) , out name ) )
{
// make sure all local ILVariables that refer to the same slot in the locals signature
// are assigned the same name.
v . Name = name ;
}
else
{
v . Name = AssignName ( v , variableMapping ) ;
// Remember the newly assigned name:
assignedLocalSignatureIndices . Add ( ( v . Function , v . Index . Value ) , v . Name ) ;
}
break ;
default :
v . Name = AssignName ( v , variableMapping ) ;
break ;
}
}
else if ( inst is ( Call or LdFtn ) and IInstructionWithMethodOperand m )
{
// update references to local functions
if ( m . Method is LocalFunctionMethod lf
& & localFunctionMapping . TryGetValue ( ( MethodDefinitionHandle ) lf . MetadataToken , out var name ) )
{
lf . Name = name ;
}
}
}
foreach ( var localFunction in function . LocalFunctions )
}
string AssignName ( ILVariable v , Dictionary < ILVariable , string > variableMapping )
{
// variable has no valid name
string newName = v . Name ;
if ( v . HasGeneratedName | | ! IsValidName ( newName ) )
{
if ( ! LocalFunctionDecompiler . ParseLocalFunctionName ( localFunction . Name , out _ , out var newName ) | | ! IsValidName ( newName ) )
newName = null ;
localFunction . Name = newName ;
localFunction . ReducedMethod . Name = newName ;
// don't use the name from the debug symbols if it looks like a generated name
// generate a new one based on how the variable is used
newName = GenerateNameForVariable ( v ) ;
}
// Now generate names:
var mapping = new Dictionary < ILVariable , string > ( ILVariableEqualityComparer . Instance ) ;
foreach ( var inst in function . Descendants . OfType < IInstructionWithVariableOperand > ( ) )
// use the existing name and update index appended to future conflicts
string nameWithoutNumber = SplitName ( newName , out int newIndex ) ;
if ( reservedVariableNames . TryGetValue ( nameWithoutNumber , out int lastUsedIndex ) )
{
var v = inst . Variable ;
if ( ! mapping . TryGetValue ( v , out string name ) )
// name without number was already used
if ( v . Type . IsKnownType ( KnownTypeCode . Int32 ) & & loopCounters . Contains ( v ) )
{
if ( string . IsNullOrEmpty ( v . Name ) )
v . Name = GenerateNameForVariable ( v ) ;
mapping . Add ( v , v . Name ) ;
// special case for loop counters,
// we don't want them to be named i, i2, ..., but i, j, ...
newName = GenerateNameForVariable ( v ) ;
}
else
{
v . Name = name ;
}
}
foreach ( var localFunction in function . LocalFunctions )
{
var newName = localFunction . Name ;
if ( newName = = null )
{
string nameWithoutNumber = "f" ;
if ( ! reservedVariableNames . TryGetValue ( nameWithoutNumber , out int currentIndex ) )
{
currentIndex = 1 ;
}
int count = Math . Max ( 1 , currentIndex ) + 1 ;
reservedVariableNames [ nameWithoutNumber ] = count ;
if ( count > 1 )
if ( newIndex > lastUsedIndex )
{
newName = nameWithoutNumber + count . ToString ( ) ;
// new index is larger than last, so we can use it
}
else
{
newName = nameWithoutNumber ;
// new index is smaller or equal, so we use the next value
newIndex = lastUsedIndex + 1 ;
}
// resolve conflicts by appending the index to the new name:
newName = nameWithoutNumber + newIndex . ToString ( ) ;
}
localFunction . Name = newName ;
localFunction . ReducedMethod . Name = newName ;
localFunctionMapping [ ( MethodDefinitionHandle ) localFunction . ReducedMethod . MetadataToken ] = newName ;
}
foreach ( var inst in function . Descendants )
{
LocalFunctionMethod localFunction ;
switch ( inst )
{
case Call call :
localFunction = call . Method as LocalFunctionMethod ;
break ;
case LdFtn ldftn :
localFunction = ldftn . Method as LocalFunctionMethod ;
break ;
default :
localFunction = null ;
break ;
}
if ( localFunction = = null | | ! localFunctionMapping . TryGetValue ( ( MethodDefinitionHandle ) localFunction . MetadataToken , out var name ) )
continue ;
localFunction . Name = name ;
}
// update the last used index
reservedVariableNames [ nameWithoutNumber ] = newIndex ;
variableMapping . Add ( v , newName ) ;
return newName ;
}
/// <remarks>
@ -326,6 +324,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -326,6 +324,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
switch ( arg )
{
case GetPinnableReference _ :
case LdObj _ :
case LdFlda _ :
case LdsFlda _ :
@ -336,16 +335,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -336,16 +335,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
bool ConflictWithLocal ( ILVariable v )
{
if ( v . Kind = = VariableKind . UsingLocal | | v . Kind = = VariableKind . ForeachLocal )
{
if ( reservedVariableNames . ContainsKey ( v . Name ) )
return true ;
}
return false ;
}
internal static bool IsValidName ( string varName )
{
if ( string . IsNullOrWhiteSpace ( varName ) )
@ -431,6 +420,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -431,6 +420,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if ( ! currentLowerCaseTypeOrMemberNames . Contains ( name ) )
proposedNameForStores . Add ( name ) ;
}
else if ( store is PinnedRegion pinnedRegion )
{
var name = GetNameFromInstruction ( pinnedRegion . Init ) ;
if ( ! currentLowerCaseTypeOrMemberNames . Contains ( name ) )
proposedNameForStores . Add ( name ) ;
}
}
if ( proposedNameForStores . Count = = 1 )
{
@ -462,32 +457,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -462,32 +457,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
proposedName = GetNameByType ( variable . Type ) ;
}
// remove any numbers from the proposed name
proposedName = SplitName ( proposedName , out int number ) ;
if ( ! reservedVariableNames . ContainsKey ( proposedName ) )
{
reservedVariableNames . Add ( proposedName , 0 ) ;
}
int count = + + reservedVariableNames [ proposedName ] ;
Debug . Assert ( ! string . IsNullOrWhiteSpace ( proposedName ) ) ;
if ( count > 1 )
{
return proposedName + count . ToString ( ) ;
}
else
{
return proposedName ;
}
// for generated names remove number-suffixes
return SplitName ( proposedName , out _ ) ;
}
static string GetNameFromInstruction ( ILInstruction inst )
{
switch ( inst )
{
case GetPinnableReference getPinnableReference :
return GetNameFromInstruction ( getPinnableReference . Argument ) ;
case LdObj ldobj :
return GetNameFromInstruction ( ldobj . Target ) ;
case LdFlda ldflda :
if ( ldflda . Field . IsCompilerGeneratedOrIsInCompilerGeneratedClass ( ) )
return GetNameFromInstruction ( ldflda . Target ) ;
return CleanUpVariableName ( ldflda . Field . Name ) ;
case LdsFlda ldsflda :
return CleanUpVariableName ( ldsflda . Field . Name ) ;
@ -567,6 +551,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -567,6 +551,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true ;
if ( m . Name = = "Concat" & & m . DeclaringType . IsKnownType ( KnownTypeCode . String ) )
return true ;
if ( m . Name = = "GetPinnableReference" )
return true ;
return false ;
}