diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 94ed97cc2..ec74795da 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -65,7 +65,8 @@ namespace ICSharpCode.Decompiler.Ast context.CancellationToken.ThrowIfCancellationRequested(); var allVariables = ilMethod.GetSelfAndChildrenRecursive(e => e.Operand is ILVariable).Select(e => (ILVariable)e.Operand).Distinct(); - NameVariables.AssignNamesToVariables(methodDef.Parameters, allVariables, ilMethod); + Debug.Assert(context.CurrentMethod == methodDef); + NameVariables.AssignNamesToVariables(context, allVariables, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs index 0e94dbb62..f869eecfc 100644 --- a/ICSharpCode.Decompiler/Ast/NameVariables.cs +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -31,12 +31,13 @@ namespace ICSharpCode.Decompiler.Ast }; - public static void AssignNamesToVariables(IEnumerable parameters, IEnumerable variables, ILBlock methodBody) + public static void AssignNamesToVariables(DecompilerContext context, IEnumerable variables, ILBlock methodBody) { NameVariables nv = new NameVariables(); - nv.AddExistingNames(parameters.Select(p => p.Name)); + nv.fieldNamesInCurrentType = context.CurrentType.Fields.Select(f => f.Name).ToList(); + nv.AddExistingNames(context.CurrentMethod.Parameters.Select(p => p.Name)); nv.AddExistingNames(variables.Where(v => v.IsGenerated).Select(v => v.Name)); - foreach (ParameterDefinition p in parameters) { + foreach (ParameterDefinition p in context.CurrentMethod.Parameters) { if (string.IsNullOrEmpty(p.Name)) p.Name = nv.GenerateNameForVariableOrParameter(p, p.ParameterType, methodBody); } @@ -47,6 +48,7 @@ namespace ICSharpCode.Decompiler.Ast } } + List fieldNamesInCurrentType; Dictionary typeNames = new Dictionary(); void AddExistingNames(IEnumerable existingNames) @@ -83,16 +85,28 @@ namespace ICSharpCode.Decompiler.Ast where expr.Code == ILCode.Stloc && expr.Operand == variableOrParameter select GetNameFromExpression(expr.Arguments.Single()) into name where !string.IsNullOrEmpty(name) - select name).Distinct().ToList(); + select name).Except(fieldNamesInCurrentType).ToList(); - string proposedName; + string proposedName = null; if (proposedNameForStores.Count == 1) { proposedName = proposedNameForStores[0]; } else { - // TODO: infer proposed names from loads - proposedName = GetNameByType(varType); + var proposedNameForLoads = + (from expr in methodBody.GetSelfAndChildrenRecursive() + from i in Enumerable.Range(0, expr.Arguments.Count) + let arg = expr.Arguments[i] + where (arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldarg) && arg.Operand == variableOrParameter + select GetNameForArgument(expr, i) into name + where name != null + select name).Except(fieldNamesInCurrentType).ToList(); + if (proposedNameForLoads.Count == 1) { + proposedName = proposedNameForLoads[0]; + } } + if (proposedName == null) + proposedName = GetNameByType(varType); + if (!typeNames.ContainsKey(proposedName)) { typeNames.Add(proposedName, 0); } @@ -108,10 +122,6 @@ namespace ICSharpCode.Decompiler.Ast { 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.Ldarg && ((ParameterDefinition)expr.Arguments[0].Operand).Index < 0)) - return CleanUpVariableName(((FieldReference)expr.Operand).Name); - break; case ILCode.Ldsfld: return CleanUpVariableName(((FieldReference)expr.Operand).Name); case ILCode.Call: @@ -129,6 +139,31 @@ namespace ICSharpCode.Decompiler.Ast return null; } + static string GetNameForArgument(ILExpression parent, int i) + { + switch (parent.Code) { + case ILCode.Stfld: + case ILCode.Stsfld: + if (i == parent.Arguments.Count - 1) // last argument is stored value + return CleanUpVariableName(((FieldReference)parent.Operand).Name); + else + break; + case ILCode.Call: + case ILCode.Callvirt: + case ILCode.Newobj: + MethodDefinition methodDef = ((MethodReference)parent.Operand).Resolve(); + if (methodDef != null) { + var p = methodDef.Parameters.ElementAtOrDefault((parent.Code != ILCode.Newobj && methodDef.HasThis) ? i - 1 : i); + if (p != null && !string.IsNullOrEmpty(p.Name)) + return CleanUpVariableName(p.Name); + } + break; + case ILCode.Ret: + return "result"; + } + return null; + } + string GetNameByType(TypeReference type) { GenericInstanceType git = type as GenericInstanceType; @@ -164,6 +199,13 @@ namespace ICSharpCode.Decompiler.Ast int pos = name.IndexOf('`'); if (pos >= 0) name = name.Substring(0, pos); + + // remove field prefix: + if (name.Length > 2 && name.StartsWith("m_", StringComparison.Ordinal)) + name = name.Substring(2); + else if (name.Length > 1 && name[0] == '_') + name = name.Substring(1); + if (name.Length == 0) return "obj"; else