From 4be02c001ff84f3f08be417c4ffcf331b8022df9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 19 Feb 2011 15:12:16 +0100 Subject: [PATCH] Improve naming of local variables. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 7 + .../Ast/AstMethodBodyBuilder.cs | 57 +------ ICSharpCode.Decompiler/Ast/NameVariables.cs | 158 ++++++++++++++++++ .../ICSharpCode.Decompiler.csproj | 1 + 4 files changed, 167 insertions(+), 56 deletions(-) create mode 100644 ICSharpCode.Decompiler/Ast/NameVariables.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index e4978d823..2d775d770 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -198,6 +198,13 @@ namespace Decompiler .MakeArrayType((type as Mono.Cecil.ArrayType).Rank); } else if (type is GenericInstanceType) { GenericInstanceType gType = (GenericInstanceType)type; + if (gType.ElementType.Namespace == "System" && gType.ElementType.Name == "Nullable`1" && gType.GenericArguments.Count == 1) { + typeIndex++; + return new ComposedType { + BaseType = ConvertType(gType.GenericArguments[0], typeAttributes, ref typeIndex), + HasNullableSpecifier = true + }; + } AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex); foreach (var typeArgument in gType.GenericArguments) { typeIndex++; diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 00f05951a..33dcbcef9 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -45,23 +45,6 @@ namespace Decompiler } } - static readonly Dictionary typeNameToVariableNameDict = new Dictionary { - { "System.Boolean", "flag" }, - { "System.Byte", "b" }, - { "System.SByte", "b" }, - { "System.Int16", "num" }, - { "System.Int32", "num" }, - { "System.Int64", "num" }, - { "System.UInt16", "num" }, - { "System.UInt32", "num" }, - { "System.UInt64", "num" }, - { "System.Single", "num" }, - { "System.Double", "num" }, - { "System.Decimal", "num" }, - { "System.String", "text" }, - { "System.Object", "obj" }, - }; - public BlockStatement CreateMethodBody() { if (methodDef.Body == null) return null; @@ -76,45 +59,7 @@ namespace Decompiler bodyGraph.Optimize(context, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); - List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); - Dictionary typeNames = new Dictionary(); - foreach(ILVariable varDef in astBuilder.Variables) { - if (varDef.Type.FullName == "System.Int32" && intNames.Count > 0) { - varDef.Name = intNames[0]; - intNames.RemoveAt(0); - } else { - string name; - if (varDef.Type.IsArray) { - name = "array"; - } else if (!typeNameToVariableNameDict.TryGetValue(varDef.Type.FullName, out name)) { - name = varDef.Type.Name; - // remove the 'I' for interfaces - if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2])) - name = name.Substring(1); - // remove the backtick (generics) - int pos = name.IndexOf('`'); - if (pos >= 0) - name = name.Substring(0, pos); - if (name.Length == 0) - name = "obj"; - else - name = char.ToLower(name[0]) + name.Substring(1); - } - if (!typeNames.ContainsKey(name)) { - typeNames.Add(name, 0); - } - int count = ++(typeNames[name]); - if (count > 1) { - name += count.ToString(); - } - varDef.Name = name; - } - -// Ast.VariableDeclaration astVar = new Ast.VariableDeclaration(varDef.Name); -// Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(astVar); -// astLocalVar.TypeReference = new Ast.TypeReference(varDef.VariableType.FullName); -// astBlock.Children.Add(astLocalVar); - } + NameVariables.AssignNamesToVariables(methodDef.Parameters.Select(p => p.Name), astBuilder.Variables, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs new file mode 100644 index 000000000..8797b43f1 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -0,0 +1,158 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; + +namespace Decompiler +{ + public class NameVariables + { + static readonly Dictionary typeNameToVariableNameDict = new Dictionary { + { "System.Boolean", "flag" }, + { "System.Byte", "b" }, + { "System.SByte", "b" }, + { "System.Int16", "num" }, + { "System.Int32", "num" }, + { "System.Int64", "num" }, + { "System.UInt16", "num" }, + { "System.UInt32", "num" }, + { "System.UInt64", "num" }, + { "System.Single", "num" }, + { "System.Double", "num" }, + { "System.Decimal", "num" }, + { "System.String", "text" }, + { "System.Object", "obj" }, + }; + + + public static void AssignNamesToVariables(IEnumerable existingNames, IEnumerable variables, ILBlock methodBody) + { + NameVariables nv = new NameVariables(); + nv.AddExistingNames(existingNames); + foreach (ILVariable varDef in variables) { + nv.AssignNameToVariable(varDef, methodBody.GetSelfAndChildrenRecursive()); + } + } + + Dictionary typeNames = new Dictionary(); + + void AddExistingNames(IEnumerable existingNames) + { + foreach (string name in existingNames) { + if (string.IsNullOrEmpty(name)) + continue; + // First, identify whether the name already ends with a number: + int pos = name.Length; + while (pos > 0 && name[pos-1] >= '0' && name[pos-1] <= '9') + pos--; + if (pos < name.Length) { + int number; + if (int.TryParse(name.Substring(pos), out number)) { + string nameWithoutDigits = name.Substring(0, pos); + int existingNumber; + if (typeNames.TryGetValue(nameWithoutDigits, out existingNumber)) { + typeNames[nameWithoutDigits] = Math.Max(number, existingNumber); + } else { + typeNames.Add(nameWithoutDigits, number); + } + continue; + } + } + if (!typeNames.ContainsKey(name)) + typeNames.Add(name, 1); + } + } + + void AssignNameToVariable(ILVariable varDef, IEnumerable allExpressions) + { + string proposedName = null; + foreach (ILExpression expr in allExpressions) { + if (expr.Operand != varDef) + continue; + if (expr.Code == ILCode.Stloc) { + proposedName = GetNameFromExpression(expr.Arguments.Single()); + } + if (proposedName != null) + break; + } + if (proposedName == null) + proposedName = GetNameByType(varDef.Type); + + if (!typeNames.ContainsKey(proposedName)) { + typeNames.Add(proposedName, 0); + } + int count = ++typeNames[proposedName]; + if (count > 1) { + varDef.Name = proposedName + count.ToString(); + } else { + varDef.Name = proposedName; + } + } + + static string GetNameFromExpression(ILExpression expr) + { + switch (expr.Code) { + case ILCode.Ldfld: + // Use the field name only if it's not a field on this (avoid confusion between local variables and fields) + if (!(expr.Arguments[0].Code == ILCode.Ldloc && ((ParameterDefinition)expr.Arguments[0].Operand).Index < 0)) + return ((FieldReference)expr.Operand).Name; + break; + case ILCode.Ldsfld: + return ((FieldReference)expr.Operand).Name; + case ILCode.Call: + case ILCode.Callvirt: + MethodReference mr = (MethodReference)expr.Operand; + if (mr.Name.StartsWith("get_", StringComparison.Ordinal)) + return CleanUpVariableName(mr.Name.Substring(4)); + else if (mr.Name.StartsWith("Get", StringComparison.Ordinal) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) + return CleanUpVariableName(mr.Name.Substring(3)); + break; + } + return null; + } + + string GetNameByType(TypeReference type) + { + GenericInstanceType git = type as GenericInstanceType; + if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) { + type = ((GenericInstanceType)type).GenericArguments[0]; + } + + if (type.FullName == "System.Int32") { + // try i,j,k, etc. + for (char c = 'i'; c <= 'n'; c++) { + if (!typeNames.ContainsKey(c.ToString())) + return c.ToString(); + } + } + string name; + if (type.IsArray) { + name = "array"; + } else if (type.IsPointer) { + name = "ptr"; + } else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) { + name = type.Name; + // remove the 'I' for interfaces + if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2])) + name = name.Substring(1); + name = CleanUpVariableName(name); + } + return name; + } + + static string CleanUpVariableName(string name) + { + // remove the backtick (generics) + int pos = name.IndexOf('`'); + if (pos >= 0) + name = name.Substring(0, pos); + if (name.Length == 0) + return "obj"; + else + return char.ToLower(name[0]) + name.Substring(1); + } + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 590030c50..13a0825f0 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -53,6 +53,7 @@ +