|
|
|
@ -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)) |
|
|
|
|
{ |
|
|
|
|
currentIndex = 1; |
|
|
|
|
} |
|
|
|
|
int count = Math.Max(1, currentIndex) + 1; |
|
|
|
|
reservedVariableNames[nameWithoutNumber] = count; |
|
|
|
|
if (count > 1) |
|
|
|
|
{ |
|
|
|
|
// make sure all local ILVariables that refer to the same slot in the locals signature
|
|
|
|
|
// are assigned the same name.
|
|
|
|
|
v.Name = name; |
|
|
|
|
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)) |
|
|
|
|
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)) |
|
|
|
|
if (newIndex > lastUsedIndex) |
|
|
|
|
{ |
|
|
|
|
currentIndex = 1; |
|
|
|
|
} |
|
|
|
|
int count = Math.Max(1, currentIndex) + 1; |
|
|
|
|
reservedVariableNames[nameWithoutNumber] = count; |
|
|
|
|
if (count > 1) |
|
|
|
|
{ |
|
|
|
|
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>
|
|
|
|
@ -459,23 +457,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -459,23 +457,8 @@ 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) |
|
|
|
|