From e43f6abcf01ee980402f91cb0f228105a6be4481 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Tue, 3 May 2011 02:34:33 +1000 Subject: [PATCH 01/59] Analyzer - Limit scope of overrides search --- .../AnalyzedEventOverridesTreeNode.cs | 33 ++++------ .../AnalyzedMethodOverridesTreeNode.cs | 51 ++++++--------- .../AnalyzedPropertyOverridesTreeNode.cs | 40 ++++-------- .../Analyzer/ScopedWhereUsedAnalyzer.cs | 65 +++++++++++++------ 4 files changed, 85 insertions(+), 104 deletions(-) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs index 113ffd029..fef82644d 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs @@ -68,36 +68,25 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct); - } - - private IEnumerable FindReferences(IEnumerable assemblies, CancellationToken ct) - { - assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null); + ScopedWhereUsedScopeAnalyzer analyzer; - // use parallelism only on the assembly level (avoid locks within Cecil) - return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct)); + analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedEvent, FindReferencesInType); + return analyzer.PerformAnalysis(ct); } - private IEnumerable FindReferences(LoadedAssembly asm, CancellationToken ct) + private IEnumerable FindReferencesInType(TypeDefinition type) { - string asmName = asm.AssemblyDefinition.Name.Name; string name = analyzedEvent.Name; string declTypeName = analyzedEvent.DeclaringType.FullName; - foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) { - ct.ThrowIfCancellationRequested(); - - if (!TypesHierarchyHelpers.IsBaseType(analyzedEvent.DeclaringType, type, resolveTypeArguments: false)) - continue; - foreach (EventDefinition eventDef in type.Events) { - ct.ThrowIfCancellationRequested(); + if (!TypesHierarchyHelpers.IsBaseType(analyzedEvent.DeclaringType, type, resolveTypeArguments: false)) + yield break; - if (TypesHierarchyHelpers.IsBaseEvent(analyzedEvent, eventDef)) { - MethodDefinition anyAccessor = eventDef.AddMethod ?? eventDef.RemoveMethod; - bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot; - yield return new AnalyzedEventTreeNode(eventDef, hidesParent ? "(hides) " : ""); - } + foreach (EventDefinition eventDef in type.Events) { + if (TypesHierarchyHelpers.IsBaseEvent(analyzedEvent, eventDef)) { + MethodDefinition anyAccessor = eventDef.AddMethod ?? eventDef.RemoveMethod; + bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot; + yield return new AnalyzedEventTreeNode(eventDef, hidesParent ? "(hides) " : ""); } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs index f8f891d2d..7ddb0178e 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs @@ -73,45 +73,32 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct); - } - - private IEnumerable FindReferences(IEnumerable assemblies, CancellationToken ct) - { - assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null); + ScopedWhereUsedScopeAnalyzer analyzer; - // use parallelism only on the assembly level (avoid locks within Cecil) - return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct)); + analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); + return analyzer.PerformAnalysis(ct); } - private IEnumerable FindReferences(LoadedAssembly asm, CancellationToken ct) + private IEnumerable FindReferencesInType(TypeDefinition type) { - string asmName = asm.AssemblyDefinition.Name.Name; - string name = analyzedMethod.Name; - string declTypeName = analyzedMethod.DeclaringType.FullName; - foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) { - ct.ThrowIfCancellationRequested(); - SharpTreeNode newNode = null; - try { - if (!TypesHierarchyHelpers.IsBaseType(analyzedMethod.DeclaringType, type, resolveTypeArguments: false)) - continue; - - foreach (MethodDefinition method in type.Methods) { - ct.ThrowIfCancellationRequested(); - - if (TypesHierarchyHelpers.IsBaseMethod(analyzedMethod, method)) { - bool hidesParent = !method.IsVirtual ^ method.IsNewSlot; - newNode = new AnalyzedMethodTreeNode(method, hidesParent ? "(hides) " : ""); - } + SharpTreeNode newNode = null; + try { + if (!TypesHierarchyHelpers.IsBaseType(analyzedMethod.DeclaringType, type, resolveTypeArguments: false)) + yield break; + + foreach (MethodDefinition method in type.Methods) { + if (TypesHierarchyHelpers.IsBaseMethod(analyzedMethod, method)) { + bool hidesParent = !method.IsVirtual ^ method.IsNewSlot; + newNode = new AnalyzedMethodTreeNode(method, hidesParent ? "(hides) " : ""); } } - catch (ReferenceResolvingException) { - // ignore this type definition. maybe add a notification about such cases. - } - - if (newNode != null) - yield return newNode; } + catch (ReferenceResolvingException) { + // ignore this type definition. maybe add a notification about such cases. + } + + if (newNode != null) + yield return newNode; } public static bool CanShow(MethodDefinition method) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs index 440a54f6e..81efb1676 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs @@ -69,45 +69,27 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct); - } - - private IEnumerable FindReferences(IEnumerable assemblies, CancellationToken ct) - { - assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null); + ScopedWhereUsedScopeAnalyzer analyzer; - // use parallelism only on the assembly level (avoid locks within Cecil) - return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct)); + analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedProperty, FindReferencesInType); + return analyzer.PerformAnalysis(ct); } - private IEnumerable FindReferences(LoadedAssembly asm, CancellationToken ct) + private IEnumerable FindReferencesInType(TypeDefinition type) { - string asmName = asm.AssemblyDefinition.Name.Name; string name = analyzedProperty.Name; string declTypeName = analyzedProperty.DeclaringType.FullName; - foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) { - ct.ThrowIfCancellationRequested(); - SharpTreeNode newNode = null; - try { - if (!TypesHierarchyHelpers.IsBaseType(analyzedProperty.DeclaringType, type, resolveTypeArguments: false)) - continue; + if (!TypesHierarchyHelpers.IsBaseType(analyzedProperty.DeclaringType, type, resolveTypeArguments: false)) + yield break; - foreach (PropertyDefinition property in type.Properties) { - ct.ThrowIfCancellationRequested(); + foreach (PropertyDefinition property in type.Properties) { - if (TypesHierarchyHelpers.IsBaseProperty(analyzedProperty, property)) { - MethodDefinition anyAccessor = property.GetMethod ?? property.SetMethod; - bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot; - newNode = new AnalyzedPropertyTreeNode(property, hidesParent ? "(hides) " : ""); - } - } - } - catch (ReferenceResolvingException) { - // ignore this type definition. + if (TypesHierarchyHelpers.IsBaseProperty(analyzedProperty, property)) { + MethodDefinition anyAccessor = property.GetMethod ?? property.SetMethod; + bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot; + yield return new AnalyzedPropertyTreeNode(property, hidesParent ? "(hides) " : ""); } - if (newNode != null) - yield return newNode; } } diff --git a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs index 48eb21bc5..98942939f 100644 --- a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs +++ b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs @@ -47,27 +47,23 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer public ScopedWhereUsedScopeAnalyzer(MethodDefinition method, Func> typeAnalysisFunction) : this(method.DeclaringType, typeAnalysisFunction) { - switch (method.Attributes & MethodAttributes.MemberAccessMask) { - case MethodAttributes.Private: - default: - memberAccessibility = Accessibility.Private; - break; - case MethodAttributes.FamANDAssem: - memberAccessibility = Accessibility.FamilyAndInternal; - break; - case MethodAttributes.Family: - memberAccessibility = Accessibility.Family; - break; - case MethodAttributes.Assembly: - memberAccessibility = Accessibility.Internal; - break; - case MethodAttributes.FamORAssem: - memberAccessibility = Accessibility.FamilyOrInternal; - break; - case MethodAttributes.Public: - memberAccessibility = Accessibility.Public; - break; - } + this.memberAccessibility = GetMethodAccessibility(method); + } + + public ScopedWhereUsedScopeAnalyzer(PropertyDefinition property, Func> typeAnalysisFunction) + : this(property.DeclaringType, typeAnalysisFunction) + { + Accessibility getterAccessibility = (property.GetMethod == null) ? Accessibility.Private : GetMethodAccessibility(property.GetMethod); + Accessibility setterAccessibility = (property.SetMethod == null) ? Accessibility.Private : GetMethodAccessibility(property.SetMethod); + this.memberAccessibility = (Accessibility)Math.Max((int)getterAccessibility, (int)setterAccessibility); + } + + public ScopedWhereUsedScopeAnalyzer(EventDefinition eventDef, Func> typeAnalysisFunction) + : this(eventDef.DeclaringType, typeAnalysisFunction) + { + // we only have to check the accessibility of the the get method + // [CLS Rule 30: The accessibility of an event and of its accessors shall be identical.] + this.memberAccessibility = GetMethodAccessibility(eventDef.AddMethod); } public ScopedWhereUsedScopeAnalyzer(FieldDefinition field, Func> typeAnalysisFunction) @@ -96,6 +92,33 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer } } + private Accessibility GetMethodAccessibility(MethodDefinition method) + { + Accessibility accessibility; + switch (method.Attributes & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Private: + default: + accessibility = Accessibility.Private; + break; + case MethodAttributes.FamANDAssem: + accessibility = Accessibility.FamilyAndInternal; + break; + case MethodAttributes.Family: + accessibility = Accessibility.Family; + break; + case MethodAttributes.Assembly: + accessibility = Accessibility.Internal; + break; + case MethodAttributes.FamORAssem: + accessibility = Accessibility.FamilyOrInternal; + break; + case MethodAttributes.Public: + accessibility = Accessibility.Public; + break; + } + return accessibility; + } + public IEnumerable PerformAnalysis(CancellationToken ct) { if (memberAccessibility == Accessibility.Private) { From b408accceac0a912de9ee08638dc942054e62bce Mon Sep 17 00:00:00 2001 From: Ivan Hamilton Date: Thu, 5 May 2011 17:42:43 +1000 Subject: [PATCH 02/59] Fix local variable post-increment recognition for split local variables. --- .../ILAst/PeepholeTransform.cs | 61 +++++++++++++++---- .../Tests/IncrementDecrement.cs | 6 ++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index 270b9f7d4..e4771eb48 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -440,6 +440,7 @@ namespace ICSharpCode.Decompiler.ILAst #endregion #region IntroducePostIncrement + bool IntroducePostIncrement(List body, ILExpression expr, int pos) { bool modified = IntroducePostIncrementForVariables(body, expr, pos); @@ -452,6 +453,13 @@ namespace ICSharpCode.Decompiler.ILAst } return modified; } + + private static IDictionary _postIncrementPairs = new Dictionary + { + {ILCode.Ldloc, ILCode.Stloc}, + {ILCode.Ldsfld, ILCode.Stsfld}, + {ILCode.CallGetter, ILCode.CallSetter} + }; bool IntroducePostIncrementForVariables(List body, ILExpression expr, int pos) { @@ -465,19 +473,40 @@ namespace ICSharpCode.Decompiler.ILAst ILExpression exprInit; if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated)) return false; - if (!(exprInit.Code == ILCode.Ldloc || exprInit.Code == ILCode.Ldsfld || (exprInit.Code == ILCode.CallGetter && exprInit.Arguments.Count == 0))) + + ILCode loadInstruction = exprInit.Code; + //We only recognise local variables, fields & getters with no arguments + if (!_postIncrementPairs.ContainsKey(loadInstruction)) + return false; + if (loadInstruction == ILCode.CallGetter && exprInit.Arguments.Count != 0) return false; + //The next expression ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; if (nextExpr == null) return false; - if (exprInit.Code == ILCode.CallGetter) { - if (!(nextExpr.Code == ILCode.CallSetter && IsGetterSetterPair(exprInit.Operand, nextExpr.Operand))) - return false; - } else { - if (!(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.Operand)) + ILCode storeInstruction = nextExpr.Code; + //Must be a matching store type + if (_postIncrementPairs[loadInstruction] != storeInstruction) + return false; + + object nextExprOperand = nextExpr.Operand; + object exprInitOperand = exprInit.Operand; + if (loadInstruction == ILCode.CallGetter) + if (!IsGetterSetterPair(exprInitOperand, nextExprOperand)) return false; - } + + if (loadInstruction == ILCode.Ldloc) + if ((nextExprOperand != exprInitOperand)) + if (((ILVariable) nextExprOperand).OriginalVariable == ((ILVariable) exprInitOperand).OriginalVariable) + //Spit local variable, unsplit these two instances + foreach (var ilExpression in body.SelectMany( + node => node.GetSelfAndChildrenRecursive( + expression => expression.Operand == nextExprOperand))) + ilExpression.Operand = exprInitOperand; + else + return false; + ILExpression addExpr = nextExpr.Arguments[0]; int incrementAmount; @@ -485,12 +514,18 @@ namespace ICSharpCode.Decompiler.ILAst if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar))) return false; - if (exprInit.Code == ILCode.Ldloc) - exprInit.Code = ILCode.Ldloca; - else if (exprInit.Code == ILCode.CallGetter) - exprInit = new ILExpression(ILCode.AddressOf, null, exprInit); - else - exprInit.Code = ILCode.Ldsflda; + switch (loadInstruction) + { + case ILCode.Ldloc: + exprInit.Code = ILCode.Ldloca; + break; + case ILCode.Ldsfld: + exprInit.Code = ILCode.Ldsflda; + break; + case ILCode.CallGetter: + exprInit = new ILExpression(ILCode.AddressOf, null, exprInit); + break; + } expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit); body.RemoveAt(pos + 1); // TODO ILRanges return true; diff --git a/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs b/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs index 02a72190f..12c47186d 100644 --- a/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs +++ b/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs @@ -176,6 +176,12 @@ public class IncrementDecrement return i++ + j; } + public void PostIncrementInlineLocalVariable(Func f) + { + int num = 0; + f(num++); + } + public int PostIncrementArrayElement(int[] array, int pos) { return array[pos]--; From e1ccd3586a7893f828166655fed21eb32f98ad64 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Sat, 7 May 2011 18:42:24 +1000 Subject: [PATCH 03/59] Analyzer: Limit search to assemblies with TypeRef to required type. --- .../Analyzer/ScopedWhereUsedAnalyzer.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs index 98942939f..6b741d6da 100644 --- a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs +++ b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs @@ -254,7 +254,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer break; } } - if (found) + if (found && AssemblyReferencesScopeType(assembly.AssemblyDefinition)) yield return assembly.AssemblyDefinition; } } @@ -278,12 +278,24 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foreach (var assembly in assemblies) { ct.ThrowIfCancellationRequested(); - if (friendAssemblies.Contains(assembly.ShortName)) { + if (friendAssemblies.Contains(assembly.ShortName) && AssemblyReferencesScopeType(assembly.AssemblyDefinition)) { yield return assembly.AssemblyDefinition; } } } } } + + private bool AssemblyReferencesScopeType(AssemblyDefinition asm) + { + bool hasRef = false; + foreach (var typeref in asm.MainModule.GetTypeReferences()) { + if (typeref.Name == typeScope.Name && typeref.Namespace == typeScope.Namespace) { + hasRef = true; + break; + } + } + return hasRef; + } } } From 4e95f435a43d0e2e332f86280d7fbcfce47f931f Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Sat, 7 May 2011 18:45:56 +1000 Subject: [PATCH 04/59] Analyzer: Redirect result from compiler-generated type/method to original code location. --- ILSpy/CSharpLanguage.cs | 110 ++++++++++-------- ILSpy/Language.cs | 69 ++++++----- .../Analyzer/AnalyzedFieldAccessTreeNode.cs | 32 ++++- .../Analyzer/AnalyzedMethodUsedByTreeNode.cs | 37 ++++-- ILSpy/TreeNodes/Analyzer/Helpers.cs | 64 ++++++++++ 5 files changed, 226 insertions(+), 86 deletions(-) diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 8e6948e14..9f2c05c3a 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -46,12 +46,12 @@ namespace ICSharpCode.ILSpy string name = "C#"; bool showAllMembers = false; Predicate transformAbortCondition = null; - + public CSharpLanguage() { } - - #if DEBUG + +#if DEBUG internal static IEnumerable GetDebugLanguages() { DecompilerContext context = new DecompilerContext(ModuleDefinition.CreateModule("dummy", ModuleKind.Dll)); @@ -70,20 +70,23 @@ namespace ICSharpCode.ILSpy showAllMembers = true }; } - #endif - - public override string Name { +#endif + + public override string Name + { get { return name; } } - - public override string FileExtension { + + public override string FileExtension + { get { return ".cs"; } } - - public override string ProjectFileExtension { + + public override string ProjectFileExtension + { get { return ".csproj"; } } - + public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true)); @@ -92,7 +95,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); } - + public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(property.DeclaringType, includeNamespace: true)); @@ -101,7 +104,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); } - + public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true)); @@ -110,7 +113,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); } - + public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(ev.DeclaringType, includeNamespace: true)); @@ -119,7 +122,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); } - + public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type); @@ -127,7 +130,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); } - + public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { @@ -146,7 +149,7 @@ namespace ICSharpCode.ILSpy } } } - + #region WriteProjectFile void WriteProjectFile(TextWriter writer, IEnumerable> files, ModuleDefinition module) { @@ -174,20 +177,20 @@ namespace ICSharpCode.ILSpy w.WriteStartElement("Project", ns); w.WriteAttributeString("ToolsVersion", "4.0"); w.WriteAttributeString("DefaultTargets", "Build"); - + w.WriteStartElement("PropertyGroup"); w.WriteElementString("ProjectGuid", Guid.NewGuid().ToString().ToUpperInvariant()); - + w.WriteStartElement("Configuration"); w.WriteAttributeString("Condition", " '$(Configuration)' == '' "); w.WriteValue("Debug"); w.WriteEndElement(); // - + w.WriteStartElement("Platform"); w.WriteAttributeString("Condition", " '$(Platform)' == '' "); w.WriteValue(platformName); w.WriteEndElement(); // - + switch (module.Kind) { case ModuleKind.Windows: w.WriteElementString("OutputType", "WinExe"); @@ -199,7 +202,7 @@ namespace ICSharpCode.ILSpy w.WriteElementString("OutputType", "Library"); break; } - + w.WriteElementString("AssemblyName", module.Assembly.Name.Name); switch (module.Runtime) { case TargetRuntime.Net_1_0: @@ -218,14 +221,14 @@ namespace ICSharpCode.ILSpy break; } w.WriteElementString("WarningLevel", "4"); - + w.WriteEndElement(); // - + w.WriteStartElement("PropertyGroup"); // platform-specific w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' "); w.WriteElementString("PlatformTarget", platformName); w.WriteEndElement(); // (platform-specific) - + w.WriteStartElement("PropertyGroup"); // Debug w.WriteAttributeString("Condition", " '$(Configuration)' == 'Debug' "); w.WriteElementString("OutputPath", "bin\\Debug\\"); @@ -233,7 +236,7 @@ namespace ICSharpCode.ILSpy w.WriteElementString("DebugType", "full"); w.WriteElementString("Optimize", "false"); w.WriteEndElement(); // (Debug) - + w.WriteStartElement("PropertyGroup"); // Release w.WriteAttributeString("Condition", " '$(Configuration)' == 'Release' "); w.WriteElementString("OutputPath", "bin\\Release\\"); @@ -241,8 +244,8 @@ namespace ICSharpCode.ILSpy w.WriteElementString("DebugType", "pdbonly"); w.WriteElementString("Optimize", "true"); w.WriteEndElement(); // (Release) - - + + w.WriteStartElement("ItemGroup"); // References foreach (AssemblyNameReference r in module.AssemblyReferences) { if (r.Name != "mscorlib") { @@ -253,7 +256,7 @@ namespace ICSharpCode.ILSpy } } w.WriteEndElement(); // (References) - + foreach (IGrouping gr in (from f in files group f.Item2 by f.Item1 into g orderby g.Key select g)) { w.WriteStartElement("ItemGroup"); foreach (string file in gr.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)) { @@ -263,16 +266,16 @@ namespace ICSharpCode.ILSpy } w.WriteEndElement(); } - + w.WriteStartElement("Import"); w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); w.WriteEndElement(); - + w.WriteEndDocument(); } } #endregion - + #region WriteCodeFilesInProject bool IncludeTypeWhenDecompilingProject(TypeDefinition type, DecompilationOptions options) { @@ -282,11 +285,11 @@ namespace ICSharpCode.ILSpy return false; return true; } - + IEnumerable> WriteCodeFilesInProject(AssemblyDefinition assembly, DecompilationOptions options, HashSet directories) { var files = assembly.MainModule.Types.Where(t => IncludeTypeWhenDecompilingProject(t, options)).GroupBy( - delegate (TypeDefinition type) { + delegate(TypeDefinition type) { string file = TextView.DecompilerTextView.CleanUpName(type.Name) + this.FileExtension; if (string.IsNullOrEmpty(type.Namespace)) { return file; @@ -301,7 +304,7 @@ namespace ICSharpCode.ILSpy Parallel.ForEach( files, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, - delegate (IGrouping file) { + delegate(IGrouping file) { using (StreamWriter w = new StreamWriter(Path.Combine(options.SaveAsProjectDirectory, file.Key))) { AstBuilder codeDomBuilder = CreateAstBuilder(options, currentModule: assembly.MainModule); foreach (TypeDefinition type in file) { @@ -315,7 +318,7 @@ namespace ICSharpCode.ILSpy return files.Select(f => Tuple.Create("Compile", f.Key)); } #endregion - + #region WriteResourceFilesInProject IEnumerable> WriteResourceFilesInProject(LoadedAssembly assembly, DecompilationOptions options, HashSet directories) { @@ -329,7 +332,8 @@ namespace ICSharpCode.ILSpy IEnumerable rs = null; try { rs = new ResourceSet(s).Cast(); - } catch (ArgumentException) { + } + catch (ArgumentException) { } if (rs != null && rs.All(e => e.Value is Stream)) { foreach (var pair in rs) { @@ -347,7 +351,8 @@ namespace ICSharpCode.ILSpy string xaml = null; try { xaml = decompiler.DecompileBaml(ms, assembly.FileName, new ConnectMethodDecompiler(assembly), new AssemblyResolver(assembly)); - } catch (XamlXmlWriterException) {} // ignore XAML writer exceptions + } + catch (XamlXmlWriterException) { } // ignore XAML writer exceptions if (xaml != null) { File.WriteAllText(Path.Combine(options.SaveAsProjectDirectory, Path.ChangeExtension(fileName, ".xaml")), xaml); yield return Tuple.Create("Page", Path.ChangeExtension(fileName, ".xaml")); @@ -368,12 +373,13 @@ namespace ICSharpCode.ILSpy } yield return Tuple.Create("EmbeddedResource", fileName); } - } finally { + } + finally { if (bamlDecompilerAppDomain != null) AppDomain.Unload(bamlDecompilerAppDomain); } } - + string GetFileNameForResource(string fullName, HashSet directories) { string[] splitName = fullName.Split('.'); @@ -389,7 +395,7 @@ namespace ICSharpCode.ILSpy return fileName; } #endregion - + AstBuilder CreateAstBuilder(DecompilationOptions options, ModuleDefinition currentModule = null, TypeDefinition currentType = null, bool isSingleMember = false) { if (currentModule == null) @@ -413,7 +419,7 @@ namespace ICSharpCode.ILSpy if (includeNamespace) options |= ConvertTypeOptions.IncludeNamespace; AstType astType = AstBuilder.ConvertType(type, typeAttributes, options); - + StringWriter w = new StringWriter(); if (type.IsByReference) { ParameterDefinition pd = typeAttributes as ParameterDefinition; @@ -421,11 +427,11 @@ namespace ICSharpCode.ILSpy w.Write("out "); else w.Write("ref "); - + if (astType is ComposedType && ((ComposedType)astType).PointerRank > 0) ((ComposedType)astType).PointerRank--; } - + astType.AcceptVisitor(new OutputVisitor(w, new CSharpFormattingOptions()), null); return w.ToString(); } @@ -460,12 +466,20 @@ namespace ICSharpCode.ILSpy } else return property.Name; } - + public override bool ShowMember(MemberReference member) { return showAllMembers || !AstBuilder.MemberIsHidden(member, new DecompilationOptions().DecompilerSettings); } - + + public override MemberReference GetOriginalCodeLocation(MemberReference member) + { + if (showAllMembers || !DecompilerSettingsPanel.CurrentDecompilerSettings.AnonymousMethods) + return member; + else + return ICSharpCode.ILSpy.TreeNodes.Analyzer.Helpers.GetOriginalCodeLocation(member); + } + public override string GetTooltip(MemberReference member) { MethodDefinition md = member as MethodDefinition; @@ -486,12 +500,12 @@ namespace ICSharpCode.ILSpy b.RunTransformations(); foreach (var attribute in b.CompilationUnit.Descendants.OfType()) attribute.Remove(); - + StringWriter w = new StringWriter(); b.GenerateCode(new PlainTextOutput(w)); return Regex.Replace(w.ToString(), @"\s+", " ").TrimEnd(); } - + return base.GetTooltip(member); } } diff --git a/ILSpy/Language.cs b/ILSpy/Language.cs index 539de51c8..c29818621 100644 --- a/ILSpy/Language.cs +++ b/ILSpy/Language.cs @@ -35,66 +35,69 @@ namespace ICSharpCode.ILSpy /// Gets the name of the language (as shown in the UI) /// public abstract string Name { get; } - + /// /// Gets the file extension used by source code files in this language. /// public abstract string FileExtension { get; } - - public virtual string ProjectFileExtension { + + public virtual string ProjectFileExtension + { get { return null; } } - + /// /// Gets the syntax highlighting used for this language. /// - public virtual ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting { - get { + public virtual ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting + { + get + { return ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinitionByExtension(this.FileExtension); } } - + public virtual void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(method.DeclaringType, true) + "." + method.Name); } - + public virtual void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(property.DeclaringType, true) + "." + property.Name); } - + public virtual void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(field.DeclaringType, true) + "." + field.Name); } - + public virtual void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(ev.DeclaringType, true) + "." + ev.Name); } - + public virtual void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(type, true)); } - + public virtual void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, nameSpace); } - + public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, assembly.FileName); WriteCommentLine(output, assembly.AssemblyDefinition.FullName); } - + public virtual void WriteCommentLine(ITextOutput output, string comment) { output.WriteLine("// " + comment); } - + /// /// Converts a type reference into a string. This method is used by the member tree node for parameter and return types. /// @@ -105,7 +108,7 @@ namespace ICSharpCode.ILSpy else return type.Name; } - + /// /// Converts a member signature to a string. /// This is used for displaying the tooltip on a member reference. @@ -117,14 +120,14 @@ namespace ICSharpCode.ILSpy else return member.ToString(); } - + public virtual string FormatPropertyName(PropertyDefinition property, bool? isIndexer = null) { if (property == null) throw new ArgumentNullException("property"); return property.Name; } - + /// /// Used for WPF keyboard navigation. /// @@ -132,39 +135,49 @@ namespace ICSharpCode.ILSpy { return Name; } - + public virtual bool ShowMember(MemberReference member) { return true; } + + /// + /// Used by the analyzer to map compiler generated code back to the original code's location + /// + public virtual MemberReference GetOriginalCodeLocation(MemberReference member) + { + return member; + } } - + public static class Languages { static ReadOnlyCollection allLanguages; - + /// /// A list of all languages. /// - public static ReadOnlyCollection AllLanguages { - get { + public static ReadOnlyCollection AllLanguages + { + get + { return allLanguages; } } - - + + internal static void Initialize(CompositionContainer composition) { List languages = new List(); languages.AddRange(composition.GetExportedValues()); languages.Add(new ILLanguage(true)); - #if DEBUG +#if DEBUG languages.AddRange(ILAstLanguage.GetDebugLanguages()); languages.AddRange(CSharpLanguage.GetDebugLanguages()); - #endif +#endif allLanguages = languages.AsReadOnly(); } - + /// /// Gets a language using its name. /// If the language is not found, C# is returned instead. diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs index c18b62d0f..d95232ee6 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs @@ -22,6 +22,7 @@ using System.Threading; using ICSharpCode.TreeView; using Mono.Cecil; using Mono.Cecil.Cil; +using System.Collections; namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { @@ -30,6 +31,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private readonly bool showWrites; // true: show writes; false: show read access private readonly FieldDefinition analyzedField; private readonly ThreadingSupport threading; + private Lazy foundMethods; + private object hashLock = new object(); public AnalyzedFieldAccessTreeNode(FieldDefinition analyzedField, bool showWrites) { @@ -68,8 +71,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { + foundMethods = new Lazy(LazyThreadSafetyMode.ExecutionAndPublication); + var analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedField, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct)) { + yield return child; + } + + foundMethods = null; } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -95,8 +104,12 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer method.Body = null; - if (found) - yield return new AnalyzedMethodTreeNode(method); + if (found) { + MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; + if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { + yield return new AnalyzedMethodTreeNode(codeLocation); + } + } } } @@ -116,5 +129,18 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return false; } } + + private bool HasAlreadyBeenFound(MethodDefinition method) + { + Hashtable hashtable = foundMethods.Value; + lock (hashLock) { + if (hashtable.Contains(method)) { + return true; + } else { + hashtable.Add(method, null); + return false; + } + } + } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs index 2a7cf784f..dff573890 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections; using System.Collections.Generic; using System.Threading; using ICSharpCode.TreeView; @@ -29,6 +30,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { private readonly MethodDefinition analyzedMethod; private readonly ThreadingSupport threading; + private Lazy foundMethods; + private object hashLock = new object(); public AnalyzedMethodUsedByTreeNode(MethodDefinition analyzedMethod) { @@ -66,10 +69,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; + foundMethods = new Lazy(LazyThreadSafetyMode.ExecutionAndPublication); - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + var analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); + foreach (var child in analyzer.PerformAnalysis(ct)) { + yield return child; + } + + foundMethods = null; } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -81,8 +88,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer continue; foreach (Instruction instr in method.Body.Instructions) { MethodReference mr = instr.Operand as MethodReference; - if (mr != null && - mr.Name == name && + if (mr != null && mr.Name == name && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) { found = true; @@ -92,8 +98,25 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer method.Body = null; - if (found) - yield return new AnalyzedMethodTreeNode(method); + if (found) { + MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; + if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { + yield return new AnalyzedMethodTreeNode(codeLocation); + } + } + } + } + + private bool HasAlreadyBeenFound(MethodDefinition method) + { + Hashtable hashtable = foundMethods.Value; + lock (hashLock) { + if (hashtable.Contains(method)) { + return true; + } else { + hashtable.Add(method, null); + return false; + } } } } diff --git a/ILSpy/TreeNodes/Analyzer/Helpers.cs b/ILSpy/TreeNodes/Analyzer/Helpers.cs index 778996a72..961880197 100644 --- a/ILSpy/TreeNodes/Analyzer/Helpers.cs +++ b/ILSpy/TreeNodes/Analyzer/Helpers.cs @@ -20,7 +20,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using ICSharpCode.Decompiler; using Mono.Cecil; +using ICSharpCode.Decompiler.ILAst; +using Mono.Cecil.Cil; namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { @@ -50,5 +53,66 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return true; } + + public static MemberReference GetOriginalCodeLocation(MemberReference member) + { + if (member is MethodDefinition) + return GetOriginalCodeLocation((MethodDefinition)member); + return member; + } + + public static MethodDefinition GetOriginalCodeLocation(MethodDefinition method) + { + if (method.Name.StartsWith("<", StringComparison.Ordinal) && method.IsCompilerGenerated()) { + return FindMethodUsageInType(method.DeclaringType, method); + } + + var typeUsage = GetOriginalCodeLocation(method.DeclaringType, method); + + return typeUsage ?? method; + } + public static MethodDefinition GetOriginalCodeLocation(TypeDefinition type, MethodDefinition method) + { + if (type != null && type.DeclaringType != null && + type.Name.StartsWith("<", StringComparison.Ordinal) && type.IsCompilerGenerated()) { + MethodDefinition constructor = GetTypeConstructor(type); + return FindMethodUsageInType(type.DeclaringType, constructor); + } + return null; + } + + private static MethodDefinition GetTypeConstructor(TypeDefinition type) + { + foreach (MethodDefinition method in type.Methods) { + if (method.Name == ".ctor") + return method; + } + return null; + } + + private static MethodDefinition FindMethodUsageInType(TypeDefinition type, MethodDefinition analyzedMethod) + { + string name = analyzedMethod.Name; + foreach (MethodDefinition method in type.Methods) { + bool found = false; + if (!method.HasBody) + continue; + foreach (Instruction instr in method.Body.Instructions) { + MethodReference mr = instr.Operand as MethodReference; + if (mr != null && mr.Name == name && + Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && + mr.Resolve() == analyzedMethod) { + found = true; + break; + } + } + + method.Body = null; + + if (found) + return method; + } + return null; + } } } From 91376b44f613c96a55b71227471251b2f501ade1 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Sat, 7 May 2011 19:38:12 +1000 Subject: [PATCH 05/59] Analyzer: remove property & event 'accessors' node and promote its children up one level. --- ILSpy/ILSpy.csproj | 4 +- ...de.cs => AnalyzedEventAccessorTreeNode.cs} | 43 +++---------------- .../Analyzer/AnalyzedEventTreeNode.cs | 17 +++++--- ...cs => AnalyzedPropertyAccessorTreeNode.cs} | 43 +++---------------- .../Analyzer/AnalyzedPropertyTreeNode.cs | 13 ++++-- 5 files changed, 34 insertions(+), 86 deletions(-) rename ILSpy/TreeNodes/Analyzer/{AnalyzedEventAccessorsTreeNode.cs => AnalyzedEventAccessorTreeNode.cs} (50%) rename ILSpy/TreeNodes/Analyzer/{AnalyzedPropertyAccessorsTreeNode.cs => AnalyzedPropertyAccessorTreeNode.cs} (50%) diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 836318799..526be03db 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -139,21 +139,21 @@ Code - + + - diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorTreeNode.cs similarity index 50% rename from ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs rename to ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorTreeNode.cs index d350147f1..041e4ef86 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorTreeNode.cs @@ -21,50 +21,19 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { - internal sealed class AnalyzedEventAccessorsTreeNode : AnalyzerTreeNode + internal class AnalyzedEventAccessorTreeNode : AnalyzedMethodTreeNode { - public AnalyzedEventAccessorsTreeNode(EventDefinition analyzedEvent) - { - if (analyzedEvent == null) - throw new ArgumentNullException("analyzedEvent"); - - if (analyzedEvent.AddMethod != null) - this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.AddMethod, "add")); - if (analyzedEvent.RemoveMethod != null) - this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.RemoveMethod, "remove")); - foreach (var accessor in analyzedEvent.OtherMethods) - this.Children.Add(new AnalyzedEventAccessorTreeNode(accessor, null)); - } + private string name; - public override object Icon + public AnalyzedEventAccessorTreeNode(MethodDefinition analyzedMethod, string name) + : base(analyzedMethod) { - get { return Images.Search; } + this.name = name; } public override object Text { - get { return "Accessors"; } - } - - public static bool CanShow(EventDefinition property) - { - return !MainWindow.Instance.CurrentLanguage.ShowMember(property.AddMethod ?? property.RemoveMethod); - } - - internal class AnalyzedEventAccessorTreeNode : AnalyzedMethodTreeNode - { - private string name; - - public AnalyzedEventAccessorTreeNode(MethodDefinition analyzedMethod, string name) - : base(analyzedMethod) - { - this.name = name; - } - - public override object Text - { - get { return name ?? base.Text; } - } + get { return name ?? base.Text; } } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs index 3ebf2d23b..89903abd4 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs @@ -57,8 +57,13 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer protected override void LoadChildren() { - if (AnalyzedEventAccessorsTreeNode.CanShow(analyzedEvent)) - this.Children.Add(new AnalyzedEventAccessorsTreeNode(analyzedEvent)); + if (analyzedEvent.AddMethod != null) + this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.AddMethod, "add")); + if (analyzedEvent.RemoveMethod != null) + this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.RemoveMethod, "remove")); + foreach (var accessor in analyzedEvent.OtherMethods) + this.Children.Add(new AnalyzedEventAccessorTreeNode(accessor, null)); + if (AnalyzedEventOverridesTreeNode.CanShow(analyzedEvent)) this.Children.Add(new AnalyzedEventOverridesTreeNode(analyzedEvent)); if (AnalyzedInterfaceEventImplementedByTreeNode.CanShow(analyzedEvent)) @@ -75,12 +80,12 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer public static bool CanShow(MemberReference member) { - var property = member as EventDefinition; - if (property == null) + var eventDef = member as EventDefinition; + if (eventDef == null) return false; - return AnalyzedEventAccessorsTreeNode.CanShow(property) - || AnalyzedEventOverridesTreeNode.CanShow(property); + return !MainWindow.Instance.CurrentLanguage.ShowMember(eventDef.AddMethod ?? eventDef.RemoveMethod) + || AnalyzedEventOverridesTreeNode.CanShow(eventDef); } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorsTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorTreeNode.cs similarity index 50% rename from ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorsTreeNode.cs rename to ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorTreeNode.cs index 621417d2b..e9b898cba 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorsTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorTreeNode.cs @@ -21,50 +21,19 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { - internal sealed class AnalyzedPropertyAccessorsTreeNode : AnalyzerTreeNode + internal class AnalyzedPropertyAccessorTreeNode : AnalyzedMethodTreeNode { - public AnalyzedPropertyAccessorsTreeNode(PropertyDefinition analyzedProperty) - { - if (analyzedProperty == null) - throw new ArgumentNullException("analyzedProperty"); - - if (analyzedProperty.GetMethod != null) - this.Children.Add(new AnalyzedPropertyAccessorTreeNode(analyzedProperty.GetMethod, "get")); - if (analyzedProperty.SetMethod != null) - this.Children.Add(new AnalyzedPropertyAccessorTreeNode(analyzedProperty.SetMethod, "set")); - foreach (var accessor in analyzedProperty.OtherMethods) - this.Children.Add(new AnalyzedPropertyAccessorTreeNode(accessor, null)); - } + private readonly string name; - public override object Icon + public AnalyzedPropertyAccessorTreeNode(MethodDefinition analyzedMethod, string name) + : base(analyzedMethod) { - get { return Images.Search; } + this.name = name; } public override object Text { - get { return "Accessors"; } - } - - public static bool CanShow(PropertyDefinition property) - { - return !MainWindow.Instance.CurrentLanguage.ShowMember(property.GetMethod ?? property.SetMethod); - } - - private class AnalyzedPropertyAccessorTreeNode : AnalyzedMethodTreeNode - { - private readonly string name; - - public AnalyzedPropertyAccessorTreeNode(MethodDefinition analyzedMethod, string name) - : base(analyzedMethod) - { - this.name = name; - } - - public override object Text - { - get { return name ?? base.Text; } - } + get { return name ?? base.Text; } } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs index bba0b3362..b08127b52 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs @@ -60,8 +60,13 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer protected override void LoadChildren() { - if (AnalyzedPropertyAccessorsTreeNode.CanShow(analyzedProperty)) - this.Children.Add(new AnalyzedPropertyAccessorsTreeNode(analyzedProperty)); + if (analyzedProperty.GetMethod != null) + this.Children.Add(new AnalyzedPropertyAccessorTreeNode(analyzedProperty.GetMethod, "get")); + if (analyzedProperty.SetMethod != null) + this.Children.Add(new AnalyzedPropertyAccessorTreeNode(analyzedProperty.SetMethod, "set")); + foreach (var accessor in analyzedProperty.OtherMethods) + this.Children.Add(new AnalyzedPropertyAccessorTreeNode(accessor, null)); + if (AnalyzedPropertyOverridesTreeNode.CanShow(analyzedProperty)) this.Children.Add(new AnalyzedPropertyOverridesTreeNode(analyzedProperty)); if (AnalyzedInterfacePropertyImplementedByTreeNode.CanShow(analyzedProperty)) @@ -82,8 +87,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer if (property == null) return false; - return AnalyzedPropertyAccessorsTreeNode.CanShow(property) - || AnalyzedPropertyOverridesTreeNode.CanShow(property); + return !MainWindow.Instance.CurrentLanguage.ShowMember(property.GetMethod ?? property.SetMethod) + || AnalyzedPropertyOverridesTreeNode.CanShow(property); } } } From 637a91236ce4a79ef0d0cc3227f77ae170eb98aa Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Sat, 7 May 2011 22:05:24 +1000 Subject: [PATCH 06/59] Add tree node icon for Static Class. --- ILSpy/ILSpy.csproj | 2 +- ILSpy/Images/Images.cs | 5 +++++ ILSpy/Images/StaticClass.png | Bin 0 -> 569 bytes ILSpy/Images/TypeIcon.cs | 3 ++- ILSpy/TreeNodes/TypeTreeNode.cs | 18 +++++++++++++++++- 5 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 ILSpy/Images/StaticClass.png diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 526be03db..fb0484079 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -236,6 +236,7 @@ + @@ -303,6 +304,5 @@ ICSharpCode.TreeView - \ No newline at end of file diff --git a/ILSpy/Images/Images.cs b/ILSpy/Images/Images.cs index 361f6a5a8..8755e772e 100644 --- a/ILSpy/Images/Images.cs +++ b/ILSpy/Images/Images.cs @@ -65,6 +65,7 @@ namespace ICSharpCode.ILSpy public static readonly BitmapImage Interface = LoadBitmap("Interface"); public static readonly BitmapImage Delegate = LoadBitmap("Delegate"); public static readonly BitmapImage Enum = LoadBitmap("Enum"); + public static readonly BitmapImage StaticClass = LoadBitmap("StaticClass"); public static readonly BitmapImage Field = LoadBitmap("Field"); @@ -133,6 +134,7 @@ namespace ICSharpCode.ILSpy PreloadPublicIconToCache(TypeIcon.Struct, Images.Struct); PreloadPublicIconToCache(TypeIcon.Interface, Images.Interface); PreloadPublicIconToCache(TypeIcon.Delegate, Images.Delegate); + PreloadPublicIconToCache(TypeIcon.StaticClass, Images.StaticClass); } protected override ImageSource GetBaseImage(TypeIcon icon) @@ -154,6 +156,9 @@ namespace ICSharpCode.ILSpy case TypeIcon.Delegate: baseImage = Images.Delegate; break; + case TypeIcon.StaticClass: + baseImage = Images.StaticClass; + break; default: throw new NotSupportedException(); } diff --git a/ILSpy/Images/StaticClass.png b/ILSpy/Images/StaticClass.png new file mode 100644 index 0000000000000000000000000000000000000000..af9ebd06a631b7049e1858a2a3e986de4031887a GIT binary patch literal 569 zcmV-90>=G`P)004R=004l4008;_004mL004C`008P>0026e000+nl3&F}00009 za7bBm000tl000tl0bXTW;{X5v7IZ~ebW?9;ba!ELWdLwtX>N2bZe?^JG%heZ==Es; z00E6jL_t(IPh()92B_Y3hqYnYYVjNLE)+bp>BPpo|2 zf0zL<&A-1i834jCn@Q4)>?M#3Op~_~Z!@;=00$f~&c+u-*g_uwZM-T~VI@L(00000NkvXX Hu0mjfVxS9o literal 0 HcmV?d00001 diff --git a/ILSpy/Images/TypeIcon.cs b/ILSpy/Images/TypeIcon.cs index 2e5f3f498..7bbb0d08a 100644 --- a/ILSpy/Images/TypeIcon.cs +++ b/ILSpy/Images/TypeIcon.cs @@ -26,6 +26,7 @@ namespace ICSharpCode.ILSpy Enum, Struct, Interface, - Delegate + Delegate, + StaticClass } } diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 23bce5df7..c62e53e9f 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -146,8 +146,10 @@ namespace ICSharpCode.ILSpy.TreeNodes } else { if (type.IsInterface) return TypeIcon.Interface; - else if (type.BaseType != null && type.BaseType.FullName == typeof(MulticastDelegate).FullName) + else if (IsDelegate(type)) return TypeIcon.Delegate; + else if (IsStaticClass(type)) + return TypeIcon.StaticClass; else return TypeIcon.Class; } @@ -178,6 +180,20 @@ namespace ICSharpCode.ILSpy.TreeNodes } return overlay; } + + private static bool IsDelegate(TypeDefinition type) + { + return type.BaseType != null && type.BaseType.FullName == typeof(MulticastDelegate).FullName; + } + + private static bool IsStaticClass(TypeDefinition type) + { + if(type.IsSealed) + return !type.Methods.Where(m => m.Name == ".ctor").Any(m => !m.IsPrivate); + + return false; + } + #endregion MemberReference IMemberTreeNode.Member { From 5bf46e3d19367ce3ca7fdb01119b43e6e17b323c Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Sat, 7 May 2011 22:37:44 +1000 Subject: [PATCH 07/59] Analyzer: result redirection - don't require that name starts with '<'. Always attempt redirect if member is compiler generated. --- ILSpy/TreeNodes/Analyzer/Helpers.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ILSpy/TreeNodes/Analyzer/Helpers.cs b/ILSpy/TreeNodes/Analyzer/Helpers.cs index 961880197..bef4e486a 100644 --- a/ILSpy/TreeNodes/Analyzer/Helpers.cs +++ b/ILSpy/TreeNodes/Analyzer/Helpers.cs @@ -63,8 +63,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer public static MethodDefinition GetOriginalCodeLocation(MethodDefinition method) { - if (method.Name.StartsWith("<", StringComparison.Ordinal) && method.IsCompilerGenerated()) { - return FindMethodUsageInType(method.DeclaringType, method); + if (method.IsCompilerGenerated()) { + return FindMethodUsageInType(method.DeclaringType, method) ?? method; } var typeUsage = GetOriginalCodeLocation(method.DeclaringType, method); @@ -73,10 +73,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer } public static MethodDefinition GetOriginalCodeLocation(TypeDefinition type, MethodDefinition method) { - if (type != null && type.DeclaringType != null && - type.Name.StartsWith("<", StringComparison.Ordinal) && type.IsCompilerGenerated()) { - MethodDefinition constructor = GetTypeConstructor(type); - return FindMethodUsageInType(type.DeclaringType, constructor); + if (type != null && type.DeclaringType != null && type.IsCompilerGenerated()) { + MethodDefinition constructor = GetTypeConstructor(type); + return FindMethodUsageInType(type.DeclaringType, constructor); } return null; } From 54d777f8332333f550e30551dc6f3b39ecdddbbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Sun, 8 May 2011 09:08:08 +0200 Subject: [PATCH 08/59] Fixed navigation to preserve state of decompiler's text view. --- ILSpy/AboutPage.cs | 4 ++-- ILSpy/App.xaml.cs | 2 +- ILSpy/BamlDecompiler.cs | 2 +- ILSpy/Commands.cs | 2 +- ILSpy/MainWindow.xaml.cs | 15 ++++++++++----- ILSpy/NavigationHistory.cs | 12 ++++++++---- ILSpy/NavigationState.cs | 10 ++++++++-- ILSpy/TextView/DecompilerTextView.cs | 21 ++++++++++++++++++++- ILSpy/TreeNodes/ResourceEntryNode.cs | 2 +- ILSpy/TreeNodes/ResourceTreeNode.cs | 2 +- ILSpy/TreeNodes/XamlResourceNode.cs | 2 +- 11 files changed, 54 insertions(+), 20 deletions(-) diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index c88f832dc..fd4fb4891 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -93,7 +93,7 @@ namespace ICSharpCode.ILSpy output.AddVisualLineElementGenerator(new MyLinkElementGenerator("SharpDevelop", "http://www.icsharpcode.net/opensource/sd/")); output.AddVisualLineElementGenerator(new MyLinkElementGenerator("MIT License", "resource:license.txt")); output.AddVisualLineElementGenerator(new MyLinkElementGenerator("LGPL", "resource:LGPL.txt")); - textView.Show(output); + textView.ShowText(output); } sealed class MyLinkElementGenerator : LinkElementGenerator @@ -130,7 +130,7 @@ namespace ICSharpCode.ILSpy } catch (Exception ex) { AvalonEditTextOutput exceptionOutput = new AvalonEditTextOutput(); exceptionOutput.WriteLine(ex.ToString()); - textView.Show(exceptionOutput); + textView.ShowText(exceptionOutput); } }, TaskScheduler.FromCurrentSynchronizationContext()); }; diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index f0644d70a..0eab2937f 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -165,7 +165,7 @@ namespace ICSharpCode.ILSpy } } } - ILSpy.MainWindow.Instance.TextView.Show(output); + ILSpy.MainWindow.Instance.TextView.ShowText(output); } else { Process.Start(e.Uri.ToString()); } diff --git a/ILSpy/BamlDecompiler.cs b/ILSpy/BamlDecompiler.cs index c223dcb82..07c60b13e 100644 --- a/ILSpy/BamlDecompiler.cs +++ b/ILSpy/BamlDecompiler.cs @@ -401,7 +401,7 @@ namespace ICSharpCode.ILSpy.Baml } return output; }), - t => textView.Show(t.Result, highlighting) + t => textView.ShowNode(t.Result, this, highlighting) ); return true; } diff --git a/ILSpy/Commands.cs b/ILSpy/Commands.cs index 67775f43d..8d89e9afc 100644 --- a/ILSpy/Commands.cs +++ b/ILSpy/Commands.cs @@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy return output; } ), - task => MainWindow.Instance.TextView.Show(task.Result)); + task => MainWindow.Instance.TextView.ShowText(task.Result)); } } #endif diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 262365ed0..4425555af 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -242,7 +242,7 @@ namespace ICSharpCode.ILSpy if (!found) { AvalonEditTextOutput output = new AvalonEditTextOutput(); output.Write("Cannot find " + args.NavigateTo); - decompilerTextView.Show(output); + decompilerTextView.ShowText(output); } } commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore @@ -511,8 +511,12 @@ namespace ICSharpCode.ILSpy if (ignoreDecompilationRequests) return; - if (recordHistory) - history.Record(new NavigationState(treeView.SelectedItems.OfType(), decompilerTextView.GetState())); + if (recordHistory) { + var dtState = decompilerTextView.GetState(); + if(dtState != null) + history.UpdateCurrent(new NavigationState(dtState)); + history.Record(new NavigationState(treeView.SelectedItems.OfType())); + } if (treeView.SelectedItems.Count == 1) { ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; @@ -586,8 +590,9 @@ namespace ICSharpCode.ILSpy void NavigateHistory(bool forward) { - var combinedState = new NavigationState(treeView.SelectedItems.OfType(), decompilerTextView.GetState()); - history.Record(combinedState, replace: true, clearForward: false); + var dtState = decompilerTextView.GetState(); + if(dtState != null) + history.UpdateCurrent(new NavigationState(dtState)); var newState = forward ? history.GoForward() : history.GoBack(); ignoreDecompilationRequests = true; diff --git a/ILSpy/NavigationHistory.cs b/ILSpy/NavigationHistory.cs index 0fb1bed05..c5983a96d 100644 --- a/ILSpy/NavigationHistory.cs +++ b/ILSpy/NavigationHistory.cs @@ -71,13 +71,18 @@ namespace ICSharpCode.ILSpy back.Clear(); forward.Clear(); } + + public void UpdateCurrent(T node) + { + current = node; + } - public void Record(T node, bool replace = false, bool clearForward = true) + public void Record(T node) { var navigationTime = DateTime.Now; var period = navigationTime - lastNavigationTime; - if (period.TotalSeconds < NavigationSecondsBeforeNewEntry || replace) { + if (period.TotalSeconds < NavigationSecondsBeforeNewEntry) { current = node; } else { if (current != null) @@ -88,8 +93,7 @@ namespace ICSharpCode.ILSpy current = node; } - if (clearForward) - forward.Clear(); + forward.Clear(); lastNavigationTime = navigationTime; } diff --git a/ILSpy/NavigationState.cs b/ILSpy/NavigationState.cs index 7a86ebec5..123c0b642 100644 --- a/ILSpy/NavigationState.cs +++ b/ILSpy/NavigationState.cs @@ -32,12 +32,18 @@ namespace ICSharpCode.ILSpy public IEnumerable TreeNodes { get { return treeNodes; } } public DecompilerTextViewState ViewState { get; private set; } - public NavigationState(IEnumerable treeNodes, DecompilerTextViewState viewState) + public NavigationState(DecompilerTextViewState viewState) { - this.treeNodes = new HashSet(treeNodes); + this.treeNodes = new HashSet(viewState.DecompiledNodes); ViewState = viewState; } + public NavigationState(IEnumerable treeNodes) + { + this.treeNodes = new HashSet(treeNodes); + } + + public bool Equals(NavigationState other) { // TODO: should this care about the view state as well? diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 1241037b1..75f837620 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -60,6 +60,7 @@ namespace ICSharpCode.ILSpy.TextView readonly UIElementGenerator uiElementGenerator; List activeCustomElementGenerators = new List(); FoldingManager foldingManager; + ILSpyTreeNode[] decompiledNodes; DefinitionLookup definitionLookup; CancellationTokenSource currentCancellationTokenSource; @@ -207,11 +208,21 @@ namespace ICSharpCode.ILSpy.TextView #endregion #region ShowOutput + public void ShowText(AvalonEditTextOutput textOutput) + { + ShowNodes(textOutput, null); + } + + public void ShowNode(AvalonEditTextOutput textOutput, ILSpyTreeNode node, IHighlightingDefinition highlighting = null) + { + ShowNodes(textOutput, new[] { node }, highlighting); + } + /// /// Shows the given output in the text view. /// Cancels any currently running decompilation tasks. /// - public void Show(AvalonEditTextOutput textOutput, IHighlightingDefinition highlighting = null) + public void ShowNodes(AvalonEditTextOutput textOutput, ILSpyTreeNode[] nodes, IHighlightingDefinition highlighting = null) { // Cancel the decompilation task: if (currentCancellationTokenSource != null) { @@ -220,6 +231,7 @@ namespace ICSharpCode.ILSpy.TextView } this.nextDecompilationRun = null; // remove scheduled decompilation run ShowOutput(textOutput, highlighting); + decompiledNodes = nodes; } /// @@ -340,6 +352,7 @@ namespace ICSharpCode.ILSpy.TextView } ShowOutput(output); } + decompiledNodes = context.TreeNodes; }); } @@ -513,6 +526,7 @@ namespace ICSharpCode.ILSpy.TextView output.WriteLine(ex.ToString()); ShowOutput(output); } + decompiledNodes = context.TreeNodes; }); } @@ -575,11 +589,15 @@ namespace ICSharpCode.ILSpy.TextView public DecompilerTextViewState GetState() { + if (decompiledNodes == null) + return null; + var state = new DecompilerTextViewState(); if (foldingManager != null) state.SaveFoldingsState(foldingManager.AllFoldings); state.VerticalOffset = textEditor.VerticalOffset; state.HorizontalOffset = textEditor.HorizontalOffset; + state.DecompiledNodes = decompiledNodes; return state; } } @@ -590,6 +608,7 @@ namespace ICSharpCode.ILSpy.TextView private int FoldingsChecksum; public double VerticalOffset; public double HorizontalOffset; + public ILSpyTreeNode[] DecompiledNodes; public void SaveFoldingsState(IEnumerable foldings) { diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceEntryNode.cs index 787c82dc3..43638064d 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceEntryNode.cs @@ -144,7 +144,7 @@ namespace ICSharpCode.ILSpy.TreeNodes output.AddUIElement(() => new Image { Source = image }); output.WriteLine(); output.AddButton(Images.Save, "Save", delegate { Save(null); }); - textView.Show(output, null); + textView.ShowNode(output, this, null); return true; } catch (Exception) { diff --git a/ILSpy/TreeNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceTreeNode.cs index 3b277a983..5e79845ba 100644 --- a/ILSpy/TreeNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceTreeNode.cs @@ -95,7 +95,7 @@ namespace ICSharpCode.ILSpy.TreeNodes ext = ".xml"; else ext = Path.GetExtension(DecompilerTextView.CleanUpName(er.Name)); - textView.Show(output, HighlightingManager.Instance.GetDefinitionByExtension(ext)); + textView.ShowNode(output, this, HighlightingManager.Instance.GetDefinitionByExtension(ext)); return true; } } diff --git a/ILSpy/TreeNodes/XamlResourceNode.cs b/ILSpy/TreeNodes/XamlResourceNode.cs index 9e42fb431..0ed226a1d 100644 --- a/ILSpy/TreeNodes/XamlResourceNode.cs +++ b/ILSpy/TreeNodes/XamlResourceNode.cs @@ -74,7 +74,7 @@ namespace ICSharpCode.ILSpy.Xaml } return output; }), - t => textView.Show(t.Result, highlighting) + t => textView.ShowNode(t.Result, this, highlighting) ); return true; } From e54bade36a198b9fccae8b9a7235ff577af5f743 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Mon, 9 May 2011 11:05:59 +1000 Subject: [PATCH 09/59] Analyzer: simplify concurrency handling --- ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs index dff573890..6c4f12025 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { private readonly MethodDefinition analyzedMethod; private readonly ThreadingSupport threading; - private Lazy foundMethods; + private Hashtable foundMethods; private object hashLock = new object(); public AnalyzedMethodUsedByTreeNode(MethodDefinition analyzedMethod) @@ -69,7 +69,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - foundMethods = new Lazy(LazyThreadSafetyMode.ExecutionAndPublication); + foundMethods = new Hashtable(); var analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); foreach (var child in analyzer.PerformAnalysis(ct)) { @@ -109,12 +109,11 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private bool HasAlreadyBeenFound(MethodDefinition method) { - Hashtable hashtable = foundMethods.Value; lock (hashLock) { - if (hashtable.Contains(method)) { + if (foundMethods.Contains(method)) { return true; } else { - hashtable.Add(method, null); + foundMethods.Add(method, null); return false; } } From e7caa3f43fa03c6480a76ba492eb7c53f858f617 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Tue, 10 May 2011 17:47:02 +1000 Subject: [PATCH 10/59] More efficient/correct test for static class icon --- ILSpy/TreeNodes/TypeTreeNode.cs | 56 ++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index c62e53e9f..2291bb074 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.ILSpy.TreeNodes { readonly TypeDefinition type; readonly AssemblyTreeNode parentAssemblyNode; - + public TypeTreeNode(TypeDefinition type, AssemblyTreeNode parentAssemblyNode) { if (parentAssemblyNode == null) @@ -41,29 +41,36 @@ namespace ICSharpCode.ILSpy.TreeNodes this.parentAssemblyNode = parentAssemblyNode; this.LazyLoading = true; } - - public TypeDefinition TypeDefinition { + + public TypeDefinition TypeDefinition + { get { return type; } } - - public AssemblyTreeNode ParentAssemblyNode { + + public AssemblyTreeNode ParentAssemblyNode + { get { return parentAssemblyNode; } } - - public string Name { + + public string Name + { get { return type.Name; } } - - public string Namespace { + + public string Namespace + { get { return type.Namespace; } } - - public override object Text { + + public override object Text + { get { return HighlightSearchMatch(this.Language.TypeToString(type, includeNamespace: false)); } } - - public bool IsPublicAPI { - get { + + public bool IsPublicAPI + { + get + { switch (type.Attributes & TypeAttributes.VisibilityMask) { case TypeAttributes.Public: case TypeAttributes.NestedPublic: @@ -75,7 +82,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } } - + public override FilterResult Filter(FilterSettings settings) { if (!settings.ShowInternalApi && !IsPublicAPI) @@ -89,7 +96,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } } - + protected override void LoadChildren() { if (type.BaseType != null || type.HasInterfaces) @@ -102,7 +109,7 @@ namespace ICSharpCode.ILSpy.TreeNodes foreach (FieldDefinition field in type.Fields.OrderBy(m => m.Name)) { this.Children.Add(new FieldTreeNode(field)); } - + foreach (PropertyDefinition property in type.Properties.OrderBy(m => m.Name)) { this.Children.Add(new PropertyTreeNode(property)); } @@ -116,7 +123,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } } - + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileType(type, output, options); @@ -188,15 +195,20 @@ namespace ICSharpCode.ILSpy.TreeNodes private static bool IsStaticClass(TypeDefinition type) { - if(type.IsSealed) - return !type.Methods.Where(m => m.Name == ".ctor").Any(m => !m.IsPrivate); + if (type.IsSealed) { + if (type.IsAbstract) + return true; + else + return !type.Methods.Where(m => m.Name == ".ctor").Any(m => !m.IsPrivate); + } return false; } #endregion - - MemberReference IMemberTreeNode.Member { + + MemberReference IMemberTreeNode.Member + { get { return type; } } } From 5efacf4860efc601e365c392b7a16df5a74d5db1 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Wed, 11 May 2011 12:22:05 +1000 Subject: [PATCH 11/59] Analyzer: renamed class. Previous name was nonsensical and did not match file name. --- .../Analyzer/AnalyzedEventOverridesTreeNode.cs | 4 ++-- .../Analyzer/AnalyzedFieldAccessTreeNode.cs | 2 +- .../AnalyzedInterfaceEventImplementedByTreeNode.cs | 4 ++-- .../AnalyzedInterfaceMethodImplementedByTreeNode.cs | 4 ++-- ...AnalyzedInterfacePropertyImplementedByTreeNode.cs | 4 ++-- .../Analyzer/AnalyzedMethodOverridesTreeNode.cs | 4 ++-- .../Analyzer/AnalyzedMethodUsedByTreeNode.cs | 2 +- .../Analyzer/AnalyzedPropertyOverridesTreeNode.cs | 4 ++-- .../Analyzer/AnalyzedTypeExposedByTreeNode.cs | 4 ++-- .../Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs | 4 ++-- .../Analyzer/AnalyzedTypeInstantiationsTreeNode.cs | 4 ++-- ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs | 12 ++++++------ 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs index fef82644d..549152bb7 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs @@ -68,9 +68,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; + ScopedWhereUsedAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedEvent, FindReferencesInType); + analyzer = new ScopedWhereUsedAnalyzer(analyzedEvent, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs index d95232ee6..850ab3141 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs @@ -73,7 +73,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { foundMethods = new Lazy(LazyThreadSafetyMode.ExecutionAndPublication); - var analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedField, FindReferencesInType); + var analyzer = new ScopedWhereUsedAnalyzer(analyzedField, FindReferencesInType); foreach (var child in analyzer.PerformAnalysis(ct)) { yield return child; } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs index c17f3ec36..09cab0bf3 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs @@ -69,8 +69,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); + ScopedWhereUsedAnalyzer analyzer; + analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs index 6ce86fc8b..960f48f89 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs @@ -67,8 +67,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); + ScopedWhereUsedAnalyzer analyzer; + analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs index 06a4fb961..35d9bc3d0 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs @@ -69,8 +69,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); + ScopedWhereUsedAnalyzer analyzer; + analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs index 7ddb0178e..05a3f5d6a 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs @@ -73,9 +73,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; + ScopedWhereUsedAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); + analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs index 6c4f12025..aef82d346 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs @@ -71,7 +71,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { foundMethods = new Hashtable(); - var analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedMethod, FindReferencesInType); + var analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); foreach (var child in analyzer.PerformAnalysis(ct)) { yield return child; } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs index 81efb1676..6a6ea0f32 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs @@ -69,9 +69,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; + ScopedWhereUsedAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedProperty, FindReferencesInType); + analyzer = new ScopedWhereUsedAnalyzer(analyzedProperty, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs index 00e09e37d..cd14c0575 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs @@ -65,9 +65,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; + ScopedWhereUsedAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedType, FindReferencesInType); + analyzer = new ScopedWhereUsedAnalyzer(analyzedType, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs index c88ece234..3bd42506c 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs @@ -66,9 +66,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; + ScopedWhereUsedAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedType, FindReferencesInType); + analyzer = new ScopedWhereUsedAnalyzer(analyzedType, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs index f11fca495..0c2d9465a 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs @@ -71,9 +71,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - ScopedWhereUsedScopeAnalyzer analyzer; + ScopedWhereUsedAnalyzer analyzer; - analyzer = new ScopedWhereUsedScopeAnalyzer(analyzedType, FindReferencesInType); + analyzer = new ScopedWhereUsedAnalyzer(analyzedType, FindReferencesInType); return analyzer.PerformAnalysis(ct); } diff --git a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs index 6b741d6da..2840901a9 100644 --- a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs +++ b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer /// /// Determines the accessibility domain of a member for where-used analysis. /// - internal class ScopedWhereUsedScopeAnalyzer + internal class ScopedWhereUsedAnalyzer { private AssemblyDefinition assemblyScope; private TypeDefinition typeScope; @@ -37,20 +37,20 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private Accessibility typeAccessibility = Accessibility.Public; private Func> typeAnalysisFunction; - public ScopedWhereUsedScopeAnalyzer(TypeDefinition type, Func> typeAnalysisFunction) + public ScopedWhereUsedAnalyzer(TypeDefinition type, Func> typeAnalysisFunction) { this.typeScope = type; this.assemblyScope = type.Module.Assembly; this.typeAnalysisFunction = typeAnalysisFunction; } - public ScopedWhereUsedScopeAnalyzer(MethodDefinition method, Func> typeAnalysisFunction) + public ScopedWhereUsedAnalyzer(MethodDefinition method, Func> typeAnalysisFunction) : this(method.DeclaringType, typeAnalysisFunction) { this.memberAccessibility = GetMethodAccessibility(method); } - public ScopedWhereUsedScopeAnalyzer(PropertyDefinition property, Func> typeAnalysisFunction) + public ScopedWhereUsedAnalyzer(PropertyDefinition property, Func> typeAnalysisFunction) : this(property.DeclaringType, typeAnalysisFunction) { Accessibility getterAccessibility = (property.GetMethod == null) ? Accessibility.Private : GetMethodAccessibility(property.GetMethod); @@ -58,7 +58,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer this.memberAccessibility = (Accessibility)Math.Max((int)getterAccessibility, (int)setterAccessibility); } - public ScopedWhereUsedScopeAnalyzer(EventDefinition eventDef, Func> typeAnalysisFunction) + public ScopedWhereUsedAnalyzer(EventDefinition eventDef, Func> typeAnalysisFunction) : this(eventDef.DeclaringType, typeAnalysisFunction) { // we only have to check the accessibility of the the get method @@ -66,7 +66,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer this.memberAccessibility = GetMethodAccessibility(eventDef.AddMethod); } - public ScopedWhereUsedScopeAnalyzer(FieldDefinition field, Func> typeAnalysisFunction) + public ScopedWhereUsedAnalyzer(FieldDefinition field, Func> typeAnalysisFunction) : this(field.DeclaringType, typeAnalysisFunction) { switch (field.Attributes & FieldAttributes.FieldAccessMask) { From db114400ba8896fbec99ad9be592ed91b2c72cbf Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Fri, 13 May 2011 10:35:04 +1000 Subject: [PATCH 12/59] Reverse Static Class icon changes to TypeTreeNode.cs Commit:637a91236ce4a79ef0d0cc3227f77ae170eb98aa Commit:e7caa3f43fa03c6480a76ba492eb7c53f858f617 --- ILSpy/TreeNodes/TypeTreeNode.cs | 70 ++++++++++----------------------- 1 file changed, 21 insertions(+), 49 deletions(-) diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 2291bb074..23bce5df7 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.ILSpy.TreeNodes { readonly TypeDefinition type; readonly AssemblyTreeNode parentAssemblyNode; - + public TypeTreeNode(TypeDefinition type, AssemblyTreeNode parentAssemblyNode) { if (parentAssemblyNode == null) @@ -41,36 +41,29 @@ namespace ICSharpCode.ILSpy.TreeNodes this.parentAssemblyNode = parentAssemblyNode; this.LazyLoading = true; } - - public TypeDefinition TypeDefinition - { + + public TypeDefinition TypeDefinition { get { return type; } } - - public AssemblyTreeNode ParentAssemblyNode - { + + public AssemblyTreeNode ParentAssemblyNode { get { return parentAssemblyNode; } } - - public string Name - { + + public string Name { get { return type.Name; } } - - public string Namespace - { + + public string Namespace { get { return type.Namespace; } } - - public override object Text - { + + public override object Text { get { return HighlightSearchMatch(this.Language.TypeToString(type, includeNamespace: false)); } } - - public bool IsPublicAPI - { - get - { + + public bool IsPublicAPI { + get { switch (type.Attributes & TypeAttributes.VisibilityMask) { case TypeAttributes.Public: case TypeAttributes.NestedPublic: @@ -82,7 +75,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } } - + public override FilterResult Filter(FilterSettings settings) { if (!settings.ShowInternalApi && !IsPublicAPI) @@ -96,7 +89,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } } - + protected override void LoadChildren() { if (type.BaseType != null || type.HasInterfaces) @@ -109,7 +102,7 @@ namespace ICSharpCode.ILSpy.TreeNodes foreach (FieldDefinition field in type.Fields.OrderBy(m => m.Name)) { this.Children.Add(new FieldTreeNode(field)); } - + foreach (PropertyDefinition property in type.Properties.OrderBy(m => m.Name)) { this.Children.Add(new PropertyTreeNode(property)); } @@ -123,7 +116,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } } - + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileType(type, output, options); @@ -153,10 +146,8 @@ namespace ICSharpCode.ILSpy.TreeNodes } else { if (type.IsInterface) return TypeIcon.Interface; - else if (IsDelegate(type)) + else if (type.BaseType != null && type.BaseType.FullName == typeof(MulticastDelegate).FullName) return TypeIcon.Delegate; - else if (IsStaticClass(type)) - return TypeIcon.StaticClass; else return TypeIcon.Class; } @@ -187,28 +178,9 @@ namespace ICSharpCode.ILSpy.TreeNodes } return overlay; } - - private static bool IsDelegate(TypeDefinition type) - { - return type.BaseType != null && type.BaseType.FullName == typeof(MulticastDelegate).FullName; - } - - private static bool IsStaticClass(TypeDefinition type) - { - if (type.IsSealed) { - if (type.IsAbstract) - return true; - else - return !type.Methods.Where(m => m.Name == ".ctor").Any(m => !m.IsPrivate); - } - - return false; - } - #endregion - - MemberReference IMemberTreeNode.Member - { + + MemberReference IMemberTreeNode.Member { get { return type; } } } From 87d700d4d9462f3a5be631cef8e9234f067dddb3 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Fri, 13 May 2011 10:45:44 +1000 Subject: [PATCH 13/59] Add tree node icon for Static Class. --- ILSpy/TreeNodes/TypeTreeNode.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 23bce5df7..e2e5c70dd 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -146,8 +146,10 @@ namespace ICSharpCode.ILSpy.TreeNodes } else { if (type.IsInterface) return TypeIcon.Interface; - else if (type.BaseType != null && type.BaseType.FullName == typeof(MulticastDelegate).FullName) + else if (IsDelegate(type)) return TypeIcon.Delegate; + else if (IsStaticClass(type)) + return TypeIcon.StaticClass; else return TypeIcon.Class; } @@ -178,6 +180,17 @@ namespace ICSharpCode.ILSpy.TreeNodes } return overlay; } + + private static bool IsDelegate(TypeDefinition type) + { + return type.BaseType != null && type.BaseType.FullName == typeof(MulticastDelegate).FullName; + } + + private static bool IsStaticClass(TypeDefinition type) + { + return type.IsSealed && type.IsAbstract; + } + #endregion MemberReference IMemberTreeNode.Member { From 0ccbbb2e1e32fd9712869021a5e146d60fd5a41f Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Fri, 13 May 2011 11:08:40 +1000 Subject: [PATCH 14/59] Analyzer: simplify concurrency handling --- .../Analyzer/AnalyzedMethodUsedByTreeNode.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs index aef82d346..7a820f517 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs @@ -17,7 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using ICSharpCode.TreeView; @@ -30,7 +30,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { private readonly MethodDefinition analyzedMethod; private readonly ThreadingSupport threading; - private Hashtable foundMethods; + private ConcurrentDictionary foundMethods; private object hashLock = new object(); public AnalyzedMethodUsedByTreeNode(MethodDefinition analyzedMethod) @@ -69,7 +69,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private IEnumerable FetchChildren(CancellationToken ct) { - foundMethods = new Hashtable(); + foundMethods = new ConcurrentDictionary(); var analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); foreach (var child in analyzer.PerformAnalysis(ct)) { @@ -109,14 +109,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private bool HasAlreadyBeenFound(MethodDefinition method) { - lock (hashLock) { - if (foundMethods.Contains(method)) { - return true; - } else { - foundMethods.Add(method, null); - return false; - } - } + return !foundMethods.TryAdd(method, 0); } } } From 323ea1a35f7b25adde917708636438c2c2f4a5c6 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Fri, 13 May 2011 11:43:44 +1000 Subject: [PATCH 15/59] Analyzer: removed redundant field from AnalyzedMethodUsedByTreeNode --- ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs index 7a820f517..f3f9504e6 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs @@ -31,7 +31,6 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer private readonly MethodDefinition analyzedMethod; private readonly ThreadingSupport threading; private ConcurrentDictionary foundMethods; - private object hashLock = new object(); public AnalyzedMethodUsedByTreeNode(MethodDefinition analyzedMethod) { From 4c2e64e92b364bf2fc510ffb10fe6772737195f8 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Mon, 16 May 2011 14:06:28 +1000 Subject: [PATCH 16/59] Analyzer: Improved where-used analysis for virtual methods --- ILSpy/ILSpy.csproj | 1 + .../Analyzer/AnalyzedMethodTreeNode.cs | 8 +- .../AnalyzedVirtualMethodUsedByTreeNode.cs | 151 ++++++++++++++++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index fb0484079..8c90134d6 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -150,6 +150,7 @@ + diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs index 2ecc053f8..ad7654eae 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs @@ -58,9 +58,15 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { if (analyzedMethod.HasBody) this.Children.Add(new AnalyzedMethodUsesTreeNode(analyzedMethod)); - this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod)); + + if (analyzedMethod.IsVirtual && !(analyzedMethod.IsNewSlot && analyzedMethod.IsFinal)) + this.Children.Add(new AnalyzedVirtualMethodUsedByTreeNode(analyzedMethod)); + else + this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod)); + if (AnalyzedMethodOverridesTreeNode.CanShow(analyzedMethod)) this.Children.Add(new AnalyzedMethodOverridesTreeNode(analyzedMethod)); + if (AnalyzedInterfaceMethodImplementedByTreeNode.CanShow(analyzedMethod)) this.Children.Add(new AnalyzedInterfaceMethodImplementedByTreeNode(analyzedMethod)); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs new file mode 100644 index 000000000..993ef36d7 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs @@ -0,0 +1,151 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using ICSharpCode.TreeView; +using Mono.Cecil; +using Mono.Cecil.Cil; +using ICSharpCode.Decompiler.Ast; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + internal sealed class AnalyzedVirtualMethodUsedByTreeNode : AnalyzerTreeNode + { + private readonly MethodDefinition analyzedMethod; + private readonly ThreadingSupport threading; + private ConcurrentDictionary foundMethods; + private MethodDefinition baseMethod; + private List possibleTypes; + + public AnalyzedVirtualMethodUsedByTreeNode(MethodDefinition analyzedMethod) + { + if (analyzedMethod == null) + throw new ArgumentNullException("analyzedMethod"); + + this.analyzedMethod = analyzedMethod; + this.threading = new ThreadingSupport(); + this.LazyLoading = true; + } + + public override object Text + { + get { return "Used By"; } + } + + public override object Icon + { + get { return Images.Search; } + } + + protected override void LoadChildren() + { + threading.LoadChildren(this, FetchChildren); + } + + protected override void OnCollapsing() + { + if (threading.IsRunning) { + this.LazyLoading = true; + threading.Cancel(); + this.Children.Clear(); + } + } + + private IEnumerable FetchChildren(CancellationToken ct) + { + InitializeAnalyzer(); + + var analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); + foreach (var child in analyzer.PerformAnalysis(ct)) { + yield return child; + } + + ReleaseAnalyzer(); + } + + private void InitializeAnalyzer() + { + foundMethods = new ConcurrentDictionary(); + + var BaseMethods = TypesHierarchyHelpers.FindBaseMethods(analyzedMethod).ToArray(); + if (BaseMethods.Length > 0) { + baseMethod = BaseMethods[BaseMethods.Length - 1]; + } + + possibleTypes = new List(); + + TypeReference type = analyzedMethod.DeclaringType.BaseType; + while (type !=null) + { + possibleTypes.Add(type); + type = type.Resolve().BaseType; + } + } + + private void ReleaseAnalyzer() + { + foundMethods = null; + baseMethod = null; + } + + private IEnumerable FindReferencesInType(TypeDefinition type) + { + string name = analyzedMethod.Name; + foreach (MethodDefinition method in type.Methods) { + bool found = false; + string prefix = string.Empty; + if (!method.HasBody) + continue; + foreach (Instruction instr in method.Body.Instructions) { + MethodReference mr = instr.Operand as MethodReference; + if (mr != null && mr.Name == name) { + // explicit call to the requested method + if (Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) { + found = true; + prefix = "(as base) "; + break; + } + // virtual call to base method + if (instr.OpCode.Code == Code.Callvirt && Helpers.IsReferencedBy(baseMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == baseMethod) { + found = true; + break; + } + } + } + + method.Body = null; + + if (found) { + MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; + if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { + yield return new AnalyzedMethodTreeNode(codeLocation, prefix); + } + } + } + } + + private bool HasAlreadyBeenFound(MethodDefinition method) + { + return !foundMethods.TryAdd(method, 0); + } + } +} From 01493897c7285ccfc93b59e68375ac030c99ff0e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 17 May 2011 16:23:46 +0200 Subject: [PATCH 17/59] fix https://github.com/icsharpcode/ILSpy/issues/177 --- .../CSharp/OutputVisitor/OutputVisitor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index 1c770b634..eb5e9d2ba 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -963,7 +963,9 @@ namespace ICSharpCode.NRefactory.CSharp case '\v': return "\\v"; default: - if (char.IsControl(ch) || char.IsSurrogate(ch)) { + if (char.IsControl(ch) || char.IsSurrogate(ch) || + // print all uncommon white spaces as numbers + (char.IsWhiteSpace(ch) && ch != ' ')) { return "\\u" + ((int)ch).ToString("x4"); } else { return ch.ToString(); From e654e336137e426974e3b27154acfb7ecc65cf5a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 18 May 2011 22:25:47 +0200 Subject: [PATCH 18/59] Add display settings to the options dialog: allows to configure font family and size used in the DecompilerTextView. fixes #176 --- ILSpy/DisplaySettings.cs | 65 +++++++++++++ ILSpy/DisplaySettingsPanel.xaml | 57 +++++++++++ ILSpy/DisplaySettingsPanel.xaml.cs | 137 +++++++++++++++++++++++++++ ILSpy/ILSpy.csproj | 6 ++ ILSpy/TextView/DecompilerTextView.cs | 3 + 5 files changed, 268 insertions(+) create mode 100644 ILSpy/DisplaySettings.cs create mode 100644 ILSpy/DisplaySettingsPanel.xaml create mode 100644 ILSpy/DisplaySettingsPanel.xaml.cs diff --git a/ILSpy/DisplaySettings.cs b/ILSpy/DisplaySettings.cs new file mode 100644 index 000000000..fe8cbe9e4 --- /dev/null +++ b/ILSpy/DisplaySettings.cs @@ -0,0 +1,65 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.ComponentModel; +using System.Windows.Media; + +namespace ICSharpCode.ILSpy +{ + /// + /// Description of DisplaySettings. + /// + public class DisplaySettings : INotifyPropertyChanged + { + public DisplaySettings() + { + } + + #region INotifyPropertyChanged implementation + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) + { + if (PropertyChanged != null) { + PropertyChanged(this, e); + } + } + + protected void OnPropertyChanged(string propertyName) + { + OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); + } + #endregion + + FontFamily selectedFont; + + public FontFamily SelectedFont { + get { return selectedFont; } + set { + if (selectedFont != value) { + selectedFont = value; + OnPropertyChanged("SelectedFont"); + } + } + } + + double selectedFontSize; + + public double SelectedFontSize { + get { return selectedFontSize; } + set { + if (selectedFontSize != value) { + selectedFontSize = value; + OnPropertyChanged("SelectedFontSize"); + } + } + } + + public void CopyValues(DisplaySettings s) + { + this.SelectedFont = s.selectedFont; + this.SelectedFontSize = s.selectedFontSize; + } + } +} diff --git a/ILSpy/DisplaySettingsPanel.xaml b/ILSpy/DisplaySettingsPanel.xaml new file mode 100644 index 000000000..b7282b268 --- /dev/null +++ b/ILSpy/DisplaySettingsPanel.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + + + + + + + + \ No newline at end of file diff --git a/ILSpy/DisplaySettingsPanel.xaml.cs b/ILSpy/DisplaySettingsPanel.xaml.cs new file mode 100644 index 000000000..21943746b --- /dev/null +++ b/ILSpy/DisplaySettingsPanel.xaml.cs @@ -0,0 +1,137 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Threading; +using System.Xml.Linq; +using ICSharpCode.Decompiler; + +namespace ICSharpCode.ILSpy +{ + /// + /// Interaction logic for DisplaySettingsPanel.xaml + /// + [ExportOptionPage("Display")] + public partial class DisplaySettingsPanel : UserControl, IOptionPage + { + public DisplaySettingsPanel() + { + InitializeComponent(); + + Task task = new Task(FontLoader); + task.Start(); + task.ContinueWith( + delegate(Task continuation) { + App.Current.Dispatcher.Invoke( + DispatcherPriority.Normal, + (Action)( + () => { + fontSelector.ItemsSource = task.Result; + if (continuation.Exception != null) { + foreach (var ex in continuation.Exception.InnerExceptions) { + MessageBox.Show(ex.ToString()); + } + } + }) + ); + } + ); + } + + public void Load(ILSpySettings settings) + { + this.DataContext = LoadDisplaySettings(settings); + } + + static DisplaySettings currentDisplaySettings; + + public static DisplaySettings CurrentDisplaySettings { + get { + return currentDisplaySettings ?? (currentDisplaySettings = LoadDisplaySettings(ILSpySettings.Load())); + } + } + + static bool IsSymbolFont(FontFamily fontFamily) + { + foreach (var tf in fontFamily.GetTypefaces()) { + GlyphTypeface glyph; + try { + if (tf.TryGetGlyphTypeface(out glyph)) + return glyph.Symbol; + } catch (Exception) { + return true; + } + } + return false; + } + + static FontFamily[] FontLoader() + { + return Fonts.SystemFontFamilies + .Where(ff => !IsSymbolFont(ff)) + .OrderBy(ff => ff.Source) + .ToArray(); + } + + public static DisplaySettings LoadDisplaySettings(ILSpySettings settings) + { + XElement e = settings["DisplaySettings"]; + DisplaySettings s = new DisplaySettings(); + s.SelectedFont = new FontFamily((string)e.Attribute("Font") ?? "Consolas"); + s.SelectedFontSize = (double?)e.Attribute("FontSize") ?? 10.0 * 4 / 3; + + return s; + } + + public void Save(XElement root) + { + DisplaySettings s = (DisplaySettings)this.DataContext; + + currentDisplaySettings.CopyValues(s); + + XElement section = new XElement("DisplaySettings"); + section.SetAttributeValue("Font", s.SelectedFont.Source); + section.SetAttributeValue("FontSize", s.SelectedFontSize); + + XElement existingElement = root.Element("DisplaySettings"); + if (existingElement != null) + existingElement.ReplaceWith(section); + else + root.Add(section); + } + } + + public class FontSizeConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is double) { + return Math.Round((double)value / 4 * 3); + } + + throw new NotImplementedException(); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is string) { + double d; + if (double.TryParse((string)value, out d)) + return d * 4 / 3; + return 11 * 4 / 3; + } + + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index e14465833..3eeb462a2 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -102,6 +102,7 @@ DecompilerSettingsPanel.xaml Code + @@ -138,6 +139,10 @@ Code + + DisplaySettingsPanel.xaml + Code + @@ -230,6 +235,7 @@ SearchPane.cs + DecompilerTextView.cs diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 1241037b1..6b6a26f84 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -29,6 +29,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; @@ -85,6 +86,8 @@ namespace ICSharpCode.ILSpy.TextView textEditor.Options.RequireControlModifierForHyperlinkClick = false; textEditor.TextArea.TextView.MouseHover += TextViewMouseHover; textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped; + textEditor.SetBinding(TextEditor.FontFamilyProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFont") }); + textEditor.SetBinding(TextEditor.FontSizeProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFontSize") }); } #endregion From 975be57b62f65ea253bce84d61538a76228bd76d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 19 May 2011 18:54:56 +0200 Subject: [PATCH 19/59] implement DecimalConstantTransform closes #184 --- .../Transforms/DecimalConstantTransform.cs | 146 ++++++++++++++++++ .../Ast/Transforms/TransformationPipeline.cs | 1 + .../ICSharpCode.Decompiler.csproj | 1 + 3 files changed, 148 insertions(+) create mode 100644 ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs new file mode 100644 index 000000000..2d75b8bc8 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs @@ -0,0 +1,146 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.Decompiler.Ast.Transforms +{ + /// + /// Description of DecimalConstantTransform. + /// + public class DecimalConstantTransform : DepthFirstAstVisitor, IAstTransform + { + static readonly ICSharpCode.NRefactory.CSharp.Attribute decimalConstantAttribute = new ICSharpCode.NRefactory.CSharp.Attribute { + Type = new SimpleType("DecimalConstant"), + Arguments = { new Repeat(new AnyNode()) } + }; + + static readonly FieldDeclaration decimalConstantPattern = new FieldDeclaration { + Attributes = { + new AttributeSection { + Attributes = { + decimalConstantAttribute + } + } + }, + Modifiers = Modifiers.Any, + Variables = { new Repeat(new AnyNode()) }, + ReturnType = new PrimitiveType("decimal") + }; + + Expression ConstructExpression(string typeName, string member) + { + return new AssignmentExpression( + new MemberReferenceExpression( + new TypeReferenceExpression(new SimpleType(typeName)), + member + ), + AssignmentOperatorType.Assign, + new AnyNode() + ); + } + + class ClassInfo + { + public ClassInfo() + { + Fields = new List(); + } + + public List Fields { get; private set; } + public TypeDeclaration Declaration { get; set; } + } + + Stack replaceableFields; + + public DecimalConstantTransform() + { + } + + void IAstTransform.Run(AstNode compilationUnit) + { + this.replaceableFields = new Stack(); + compilationUnit.AcceptVisitor(this, null); + } + + public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) + { + base.VisitFieldDeclaration(fieldDeclaration, data); + + var match = decimalConstantPattern.Match(fieldDeclaration); + + if (match.Success) { + Modifiers pattern = Modifiers.Static | Modifiers.Readonly; + if ((fieldDeclaration.Modifiers & pattern) == pattern) { + replaceableFields.Peek().Fields.AddRange(fieldDeclaration.Variables.Select(v => v.Name)); + fieldDeclaration.ReplaceWith(ReplaceFieldWithConstant); + } + } + + return null; + } + + AstNode ReplaceFieldWithConstant(AstNode node) + { + var old = node as FieldDeclaration; + var fd = new FieldDeclaration { + Modifiers = old.Modifiers & ~(Modifiers.Readonly | Modifiers.Static) | Modifiers.Const, + ReturnType = new PrimitiveType("decimal") + }; + + var foundAttr = old.Attributes.SelectMany(section => section.Attributes) + .First(a => decimalConstantAttribute.IsMatch(a)); + foundAttr.Remove(); + foreach (var attr in old.Attributes.Where(section => section.Attributes.Count == 0)) + attr.Remove(); + + old.Attributes.MoveTo(fd.Attributes); + old.Variables.MoveTo(fd.Variables); + + fd.Variables.Single().Initializer = new PrimitiveExpression(CreateDecimalValue(foundAttr)); + + return fd; + } + + object CreateDecimalValue(ICSharpCode.NRefactory.CSharp.Attribute foundAttr) + { + byte scale = (byte)((PrimitiveExpression)foundAttr.Arguments.ElementAt(0)).Value; + byte sign = (byte)((PrimitiveExpression)foundAttr.Arguments.ElementAt(1)).Value; + int high = (int)(uint)((PrimitiveExpression)foundAttr.Arguments.ElementAt(2)).Value; + int mid = (int)(uint)((PrimitiveExpression)foundAttr.Arguments.ElementAt(3)).Value; + int low = (int)(uint)((PrimitiveExpression)foundAttr.Arguments.ElementAt(4)).Value; + + return new Decimal(low, mid, high, sign == 1, scale); + } + + public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) + { + if ((constructorDeclaration.Modifiers & Modifiers.Static) == Modifiers.Static && replaceableFields.Count > 0) { + var current = replaceableFields.Peek(); + + foreach (var fieldName in current.Fields) { + var pattern = ConstructExpression(current.Declaration.Name, fieldName); + foreach (var expr in constructorDeclaration.Body + .OfType()) { + if (pattern.IsMatch(expr.Expression)) + expr.Remove(); + } + } + } + + return null; + } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + replaceableFields.Push(new ClassInfo() { Declaration = typeDeclaration }); + base.VisitTypeDeclaration(typeDeclaration, data); + replaceableFields.Pop(); + return null; + } + } +} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index 686a73a61..1433f0ef5 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public static IAstTransform[] CreatePipeline(DecompilerContext context) { return new IAstTransform[] { + new DecimalConstantTransform(), new PushNegation(), new DelegateConstruction(context), new PatternStatementTransform(context), diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index d003c00cd..603543820 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -61,6 +61,7 @@ + From 66c0e844dfd448ac02dae5c87d81c65e784595b7 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 19 May 2011 19:19:21 +0200 Subject: [PATCH 20/59] fix ugly layout of font combobox and preview in DisplaySettingsPanel --- ILSpy/DisplaySettingsPanel.xaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ILSpy/DisplaySettingsPanel.xaml b/ILSpy/DisplaySettingsPanel.xaml index b7282b268..0eb6f9294 100644 --- a/ILSpy/DisplaySettingsPanel.xaml +++ b/ILSpy/DisplaySettingsPanel.xaml @@ -16,18 +16,18 @@ - + - - + + - + - - + + 6 7 8 @@ -48,7 +48,7 @@ 23 24 - + From 184a6fc4f03e94b38523a4bcbec48bfe5ead5be1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 19 May 2011 19:43:07 +0200 Subject: [PATCH 21/59] replace file header in some files --- .../Transforms/DecimalConstantTransform.cs | 19 +++++++++++++++++-- ILSpy/ConnectMethodDecompiler.cs | 19 +++++++++++++++++-- ILSpy/DisplaySettings.cs | 19 +++++++++++++++++-- ILSpy/DisplaySettingsPanel.xaml.cs | 19 +++++++++++++++++-- 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs index 2d75b8bc8..66df123d2 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs @@ -1,5 +1,20 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; diff --git a/ILSpy/ConnectMethodDecompiler.cs b/ILSpy/ConnectMethodDecompiler.cs index b5487f06a..7cb2c1760 100644 --- a/ILSpy/ConnectMethodDecompiler.cs +++ b/ILSpy/ConnectMethodDecompiler.cs @@ -1,5 +1,20 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; diff --git a/ILSpy/DisplaySettings.cs b/ILSpy/DisplaySettings.cs index fe8cbe9e4..4135ec7a5 100644 --- a/ILSpy/DisplaySettings.cs +++ b/ILSpy/DisplaySettings.cs @@ -1,5 +1,20 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. using System; using System.ComponentModel; diff --git a/ILSpy/DisplaySettingsPanel.xaml.cs b/ILSpy/DisplaySettingsPanel.xaml.cs index 21943746b..d7e262c7e 100644 --- a/ILSpy/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/DisplaySettingsPanel.xaml.cs @@ -1,5 +1,20 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; From 51fc2f10cc1cd224ea2c9d6f5b692e8ebaf2c956 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 20 May 2011 09:19:41 +0200 Subject: [PATCH 22/59] Look for generic type parameters defined in TypeDefinition, allows us to display angle brackets in unbound type names, e.g., typeof(List<>) fixes #179 --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index b4860eb7d..45013b6aa 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -436,6 +436,15 @@ namespace ICSharpCode.Decompiler.Ast astType = new MemberType { Target = nsType, MemberName = name }; } else { astType = new SimpleType(name); + + // Look for generic type parameters defined in TypeDefinition + // allows us to display angle brackets in unbound type names + // e.g. typeof(List<>) + TypeDefinition resolvedType = type.Resolve(); + + for (int i = 0; i < resolvedType.GenericParameters.Count; i++) { + ((SimpleType)astType).TypeArguments.Add(new SimpleType("")); + } } astType.AddAnnotation(type); @@ -1433,7 +1442,7 @@ namespace ICSharpCode.Decompiler.Ast if (baseType.HasFields && AnyIsHiddenBy(baseType.Fields, member)) return true; if (includeBaseMethods && baseType.HasMethods - && AnyIsHiddenBy(baseType.Methods, member, m => !m.IsSpecialName)) + && AnyIsHiddenBy(baseType.Methods, member, m => !m.IsSpecialName)) return true; if (baseType.HasNestedTypes && AnyIsHiddenBy(baseType.NestedTypes, member)) return true; @@ -1447,8 +1456,8 @@ namespace ICSharpCode.Decompiler.Ast where T : IMemberDefinition { return members.Any(m => m.Name == derived.Name - && (condition == null || condition(m)) - && TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType)); + && (condition == null || condition(m)) + && TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType)); } /// From ba0b80856cb49813a383a135dfb03b79271ff581 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 20 May 2011 11:17:39 +0200 Subject: [PATCH 23/59] add foldings to documentation comments; closes #165 --- ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs | 13 +++++++++++-- .../CSharp/OutputVisitor/IOutputFormatter.cs | 2 +- .../CSharp/OutputVisitor/OutputVisitor.cs | 4 +++- .../OutputVisitor/TextWriterOutputFormatter.cs | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index 1e6915872..f380c7790 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.Ast readonly ITextOutput output; readonly Stack nodeStack = new Stack(); int braceLevelWithinType = -1; + bool inDocumentationComment = false; public TextOutputFormatter(ITextOutput output) { @@ -102,7 +103,7 @@ namespace ICSharpCode.Decompiler.Ast output.WriteLine(); } - public void WriteComment(CommentType commentType, string content) + public void WriteComment(CommentType commentType, string content, bool isLastLine = false) { switch (commentType) { case CommentType.SingleLine: @@ -115,8 +116,16 @@ namespace ICSharpCode.Decompiler.Ast output.Write("*/"); break; case CommentType.Documentation: + if (!inDocumentationComment) + output.MarkFoldStart("///" + content, true); output.Write("///"); - output.WriteLine(content); + output.Write(content); + inDocumentationComment = true; + if (isLastLine) { + inDocumentationComment = false; + output.MarkFoldEnd(); + } + output.WriteLine(); break; } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/IOutputFormatter.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/IOutputFormatter.cs index ac1966296..58f8c23ed 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/IOutputFormatter.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/IOutputFormatter.cs @@ -39,6 +39,6 @@ namespace ICSharpCode.NRefactory.CSharp void NewLine(); - void WriteComment(CommentType commentType, string content); + void WriteComment(CommentType commentType, string content, bool isLastLine = false); } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index eb5e9d2ba..e715324ef 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -2147,7 +2147,9 @@ namespace ICSharpCode.NRefactory.CSharp // "1.0 / /*comment*/a", then we need to insert a space in front of the comment. formatter.Space(); } - formatter.WriteComment(comment.CommentType, comment.Content); + bool isContinuing = (comment.NextSibling is Comment && ((Comment)comment.NextSibling).CommentType == comment.CommentType); + + formatter.WriteComment(comment.CommentType, comment.Content, !isContinuing); lastWritten = LastWritten.Whitespace; return null; } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/TextWriterOutputFormatter.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/TextWriterOutputFormatter.cs index f8c1340e5..c6081d4f5 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/TextWriterOutputFormatter.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/TextWriterOutputFormatter.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.NRefactory.CSharp indentation--; } - public void WriteComment(CommentType commentType, string content) + public void WriteComment(CommentType commentType, string content, bool isLastLine = false) { WriteIndentation(); switch (commentType) { From 8c228a4df161e1ed4ce3e3f81e3c7592eb6c41f9 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 20 May 2011 11:26:42 +0200 Subject: [PATCH 24/59] fix bug when printing type names introduced by commit 51fc2f10cc1cd224ea2c9d6f5b692e8ebaf2c956 --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 45013b6aa..97d3d55e8 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -437,13 +437,15 @@ namespace ICSharpCode.Decompiler.Ast } else { astType = new SimpleType(name); - // Look for generic type parameters defined in TypeDefinition - // allows us to display angle brackets in unbound type names - // e.g. typeof(List<>) - TypeDefinition resolvedType = type.Resolve(); - - for (int i = 0; i < resolvedType.GenericParameters.Count; i++) { - ((SimpleType)astType).TypeArguments.Add(new SimpleType("")); + if (!type.HasGenericParameters) { + // Look for generic type parameters defined in TypeDefinition + // allows us to display angle brackets in unbound type names + // e.g. typeof(List<>) + TypeDefinition resolvedType = type.Resolve(); + + for (int i = 0; i < resolvedType.GenericParameters.Count; i++) { + ((SimpleType)astType).TypeArguments.Add(new SimpleType("")); + } } } astType.AddAnnotation(type); From 7513c8c64bdb3182e2ee996249f50397dd684308 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 17 May 2011 00:21:12 +0200 Subject: [PATCH 25/59] Show attributes on parameters in IL view. Closes #187. --- .../Disassembler/ReflectionDisassembler.cs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 5231b78c1..bff343db7 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -18,8 +18,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; - using Mono.Cecil; using Mono.Collections.Generic; @@ -143,9 +143,12 @@ namespace ICSharpCode.Decompiler.Disassembler WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl); output.Unindent(); - if (method.HasBody || method.HasCustomAttributes) { + if (method.HasBody || method.HasCustomAttributes || method.Parameters.Any(HasParameterAttributes)) { OpenBlock(defaultCollapsed: isInType); WriteAttributes(method.CustomAttributes); + foreach (var p in method.Parameters) { + WriteParameterAttributes(p); + } if (method.HasBody) { // create IL code mappings - used in debugger @@ -163,6 +166,12 @@ namespace ICSharpCode.Decompiler.Disassembler { for (int i = 0; i < parameters.Count; i++) { var p = parameters[i]; + if (p.IsIn) + output.Write("[in] "); + if (p.IsOut) + output.Write("[out] "); + if (p.IsOptional) + output.Write("[opt] "); p.ParameterType.WriteTo(output); output.Write(' '); output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p); @@ -171,6 +180,27 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } } + + bool HasParameterAttributes(ParameterDefinition p) + { + return p.HasConstant || p.HasCustomAttributes; + } + + void WriteParameterAttributes(ParameterDefinition p) + { + if (!HasParameterAttributes(p)) + return; + output.Write(".param [{0}]", p.Index); + if (p.HasConstant) { + output.Write(" = "); + if (p.Constant != null) + DisassemblerHelpers.WriteOperand(output, p.Constant); + else + output.Write("nullref"); + } + output.WriteLine(); + WriteAttributes(p.CustomAttributes); + } #endregion #region Disassemble Field From 0e648ef19b564517d32c8796a74518a2ec2c27ae Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 11:58:10 +0200 Subject: [PATCH 26/59] Add support for recursively expanding tree nodes to SharpTreeView. --- ILSpy/TreeNodes/AssemblyTreeNode.cs | 4 ++++ ILSpy/TreeNodes/TypeTreeNode.cs | 4 ++++ SharpTreeView/SharpTreeNode.cs | 14 ++++++++++++++ SharpTreeView/SharpTreeView.cs | 17 +++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 73dbb38da..864759152 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -134,6 +134,10 @@ namespace ICSharpCode.ILSpy.TreeNodes this.Children.Add(ns); } } + + public override bool CanExpandRecursively { + get { return true; } + } public TypeTreeNode FindTypeNode(TypeDefinition def) { diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 23bce5df7..efefb2928 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -117,6 +117,10 @@ namespace ICSharpCode.ILSpy.TreeNodes } } + public override bool CanExpandRecursively { + get { return true; } + } + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileType(type, output, options); diff --git a/SharpTreeView/SharpTreeNode.cs b/SharpTreeView/SharpTreeNode.cs index 3de3c9f9f..ae0a0e646 100644 --- a/SharpTreeView/SharpTreeNode.cs +++ b/SharpTreeView/SharpTreeNode.cs @@ -267,12 +267,26 @@ namespace ICSharpCode.TreeView lazyLoading = value; if (lazyLoading) { IsExpanded = false; + if (canExpandRecursively) { + canExpandRecursively = false; + RaisePropertyChanged("CanExpandRecursively"); + } } RaisePropertyChanged("LazyLoading"); RaisePropertyChanged("ShowExpander"); } } + bool canExpandRecursively = true; + + /// + /// Gets whether this node can be expanded recursively. + /// If not overridden, this property returns false if the node is using lazy-loading, and true otherwise. + /// + public virtual bool CanExpandRecursively { + get { return canExpandRecursively; } + } + public virtual bool ShowIcon { get { return Icon != null; } diff --git a/SharpTreeView/SharpTreeView.cs b/SharpTreeView/SharpTreeView.cs index d9409b79b..f2a66c797 100644 --- a/SharpTreeView/SharpTreeView.cs +++ b/SharpTreeView/SharpTreeView.cs @@ -227,11 +227,28 @@ namespace ICSharpCode.TreeView e.Handled = true; } break; + case Key.Multiply: + if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) { + container.Node.IsExpanded = true; + ExpandRecursively(container.Node); + e.Handled = true; + } + break; } if (!e.Handled) base.OnKeyDown(e); } + void ExpandRecursively(SharpTreeNode node) + { + if (node.CanExpandRecursively) { + node.IsExpanded = true; + foreach (SharpTreeNode child in node.Children) { + ExpandRecursively(child); + } + } + } + /// /// Scrolls the specified node in view and sets keyboard focus on it. /// From 508073d6ac4659ddc3962bb698b5a770ae82ff1d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 12:46:16 +0200 Subject: [PATCH 27/59] Applied some of the optimizations suggested by Kris Vandermotten. #150 --- ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs | 2 +- .../Ast/Transforms/DelegateConstruction.cs | 2 +- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 10 +++++----- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 4 ++-- ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 2 +- ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs | 4 ++-- .../ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs | 4 ++-- .../ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs | 2 +- .../CSharp/Parser/TypeSystemConvertVisitor.cs | 4 ++-- .../CSharp/Resolver/ResolveVisitor.cs | 4 ++-- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index f380c7790..b97deb091 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -136,7 +136,7 @@ namespace ICSharpCode.Decompiler.Ast if (ranges != null && ranges.Count > 0) { // find the ancestor that has method mapping as annotation - if (node.Ancestors != null && node.Ancestors.Count() > 0) + if (node.Parent != null) { var n = node.Ancestors.FirstOrDefault(a => a.Annotation() != null); if (n != null) { diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 6a5256b44..1d58a021f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) { - if (objectCreateExpression.Arguments.Count() == 2) { + if (objectCreateExpression.Arguments.Count == 2) { Expression obj = objectCreateExpression.Arguments.First(); Expression func = objectCreateExpression.Arguments.Last(); Annotation annotation = func.Annotation(); diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index b1d883019..351c28428 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -91,7 +91,7 @@ namespace ICSharpCode.Decompiler.ILAst } } - var defaultCase = ilSwitch.CaseBlocks.Where(cb => cb.Values == null).SingleOrDefault(); + var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null); // If there is no default block, remove empty case blocks if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) { ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak)); @@ -140,14 +140,14 @@ namespace ICSharpCode.Decompiler.ILAst return true; } - ILNode breakBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop || n is ILSwitch).FirstOrDefault(); + ILNode breakBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop || n is ILSwitch); if (breakBlock != null && target == Exit(breakBlock, new HashSet() { gotoExpr })) { gotoExpr.Code = ILCode.LoopOrSwitchBreak; gotoExpr.Operand = null; return true; } - ILNode continueBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop).FirstOrDefault(); + ILNode continueBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop); if (continueBlock != null && target == Enter(continueBlock, new HashSet() { gotoExpr })) { gotoExpr.Code = ILCode.LoopContinue; gotoExpr.Operand = null; @@ -209,10 +209,10 @@ namespace ICSharpCode.Decompiler.ILAst } else if (expr.Code == ILCode.Nop) { return Exit(expr, visitedNodes); } else if (expr.Code == ILCode.LoopOrSwitchBreak) { - ILNode breakBlock = GetParents(expr).Where(n => n is ILWhileLoop || n is ILSwitch).First(); + ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch); return Exit(breakBlock, new HashSet() { expr }); } else if (expr.Code == ILCode.LoopContinue) { - ILNode continueBlock = GetParents(expr).Where(n => n is ILWhileLoop).First(); + ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop); return Enter(continueBlock, new HashSet() { expr }); } else { return expr; diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 9c845293b..aed211f56 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -455,7 +455,7 @@ namespace ICSharpCode.Decompiler.ILAst if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { var locVars = byteCode.StoreTo; // For each of the variables, find the location where it is loaded - there should be preciesly one - var loadedBy = locVars.Select(locVar => reachableBody.SelectMany(bc => bc.StackBefore).Where(s => s.LoadFrom == locVar).Single()).ToList(); + var loadedBy = locVars.Select(locVar => reachableBody.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); // We now know that all the variables have a single load, // Let's make sure that they have also a single store - us if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) { @@ -572,7 +572,7 @@ namespace ICSharpCode.Decompiler.ILAst Loads = new List() { load } }); } else if (storedBy.Length == 1) { - VariableInfo newVar = newVars.Where(v => v.Stores.Contains(storedBy[0])).Single(); + VariableInfo newVar = newVars.Single(v => v.Stores.Contains(storedBy[0])); newVar.Loads.Add(load); } else { List mergeVars = newVars.Where(v => v.Stores.Union(storedBy).Any()).ToList(); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index be7e16e2d..1e4ebeddd 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -398,7 +398,7 @@ namespace ICSharpCode.Decompiler.ILAst lastNode.IsUnconditionalControlFlow()) { // Try to reuse the label - ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; + ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() }; // Terminate the last block if (!lastNode.IsUnconditionalControlFlow()) { diff --git a/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs b/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs index cd5656c9d..e5409a2e5 100644 --- a/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs +++ b/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.ILAst { Dictionary labelToCfNode = new Dictionary(); - DecompilerContext context; + readonly DecompilerContext context; uint nextLabelIndex = 0; @@ -286,7 +286,7 @@ namespace ICSharpCode.Decompiler.ILAst ILLabel condLabel = caseLabels[i]; // Find or create new case block - ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault(); + ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel); if (caseBlock == null) { caseBlock = new ILSwitch.CaseBlock() { Values = new List(), diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs index b888cdea2..813f47213 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs @@ -52,7 +52,7 @@ namespace ICSharpCode.NRefactory.CSharp public int PointerRank { get { - return GetChildrenByRole(PointerRole).Count(); + return GetChildrenByRole(PointerRole).Count; } set { if (value < 0) @@ -141,7 +141,7 @@ namespace ICSharpCode.NRefactory.CSharp } public int Dimensions { - get { return 1 + GetChildrenByRole(Roles.Comma).Count(); } + get { return 1 + GetChildrenByRole(Roles.Comma).Count; } set { int d = this.Dimensions; while (d > value) { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs index 70f1ddca4..6db26e523 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs @@ -96,7 +96,7 @@ namespace ICSharpCode.NRefactory.CSharp { if (name == null) throw new ArgumentNullException("name"); - IsVerbatim = name.StartsWith ("@"); + IsVerbatim = name.Length > 0 && name[0] == '@'; this.Name = IsVerbatim ? name.Substring (1) : name; this.startLocation = location; } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs index 66865d53c..756adce40 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs @@ -263,7 +263,7 @@ namespace ICSharpCode.NRefactory.CSharp #region Fields public override IEntity VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) { - bool isSingleField = fieldDeclaration.Variables.Count() == 1; + bool isSingleField = fieldDeclaration.Variables.Count == 1; Modifiers modifiers = fieldDeclaration.Modifiers; DefaultField field = null; foreach (VariableInitializer vi in fieldDeclaration.Variables) { @@ -476,7 +476,7 @@ namespace ICSharpCode.NRefactory.CSharp #region Events public override IEntity VisitEventDeclaration(EventDeclaration eventDeclaration, object data) { - bool isSingleEvent = eventDeclaration.Variables.Count() == 1; + bool isSingleEvent = eventDeclaration.Variables.Count == 1; Modifiers modifiers = eventDeclaration.Modifiers; DefaultEvent ev = null; foreach (VariableInitializer vi in eventDeclaration.Variables) { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index 3bcea943b..162f5c2bf 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -241,7 +241,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ResolveResult VisitFieldOrEventDeclaration(AttributedNode fieldOrEventDeclaration) { - int initializerCount = fieldOrEventDeclaration.GetChildrenByRole(FieldDeclaration.Roles.Variable).Count(); + int initializerCount = fieldOrEventDeclaration.GetChildrenByRole(FieldDeclaration.Roles.Variable).Count; ResolveResult result = null; for (AstNode node = fieldOrEventDeclaration.FirstChild; node != null; node = node.NextSibling) { if (node.Role == FieldDeclaration.Roles.Variable) { @@ -939,7 +939,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver firstInitializer != null ? firstInitializer.Initializer : null, false); - int initializerCount = variableDeclarationStatement.Variables.Count(); + int initializerCount = variableDeclarationStatement.Variables.Count; ResolveResult result = null; for (AstNode node = variableDeclarationStatement.FirstChild; node != null; node = node.NextSibling) { if (node.Role == FieldDeclaration.Roles.Variable) { From 30fe30c2361880314d00c68906feeb90981efe01 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 14:19:09 +0200 Subject: [PATCH 28/59] Fix DecimalConstantTransform. --- .../Transforms/DecimalConstantTransform.cs | 137 +++--------------- .../Ast/Transforms/TransformationPipeline.cs | 2 +- 2 files changed, 18 insertions(+), 121 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs index 66df123d2..298682afb 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs @@ -17,145 +17,42 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; -using System.Linq; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.PatternMatching; +using Mono.Cecil; namespace ICSharpCode.Decompiler.Ast.Transforms { /// - /// Description of DecimalConstantTransform. + /// Transforms decimal constant fields. /// public class DecimalConstantTransform : DepthFirstAstVisitor, IAstTransform { - static readonly ICSharpCode.NRefactory.CSharp.Attribute decimalConstantAttribute = new ICSharpCode.NRefactory.CSharp.Attribute { - Type = new SimpleType("DecimalConstant"), - Arguments = { new Repeat(new AnyNode()) } - }; - - static readonly FieldDeclaration decimalConstantPattern = new FieldDeclaration { - Attributes = { - new AttributeSection { - Attributes = { - decimalConstantAttribute - } - } - }, - Modifiers = Modifiers.Any, - Variables = { new Repeat(new AnyNode()) }, - ReturnType = new PrimitiveType("decimal") - }; - - Expression ConstructExpression(string typeName, string member) - { - return new AssignmentExpression( - new MemberReferenceExpression( - new TypeReferenceExpression(new SimpleType(typeName)), - member - ), - AssignmentOperatorType.Assign, - new AnyNode() - ); - } - - class ClassInfo - { - public ClassInfo() - { - Fields = new List(); - } - - public List Fields { get; private set; } - public TypeDeclaration Declaration { get; set; } - } - - Stack replaceableFields; - - public DecimalConstantTransform() - { - } - - void IAstTransform.Run(AstNode compilationUnit) - { - this.replaceableFields = new Stack(); - compilationUnit.AcceptVisitor(this, null); - } + static readonly PrimitiveType decimalType = new PrimitiveType("decimal"); public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) { - base.VisitFieldDeclaration(fieldDeclaration, data); - - var match = decimalConstantPattern.Match(fieldDeclaration); - - if (match.Success) { - Modifiers pattern = Modifiers.Static | Modifiers.Readonly; - if ((fieldDeclaration.Modifiers & pattern) == pattern) { - replaceableFields.Peek().Fields.AddRange(fieldDeclaration.Variables.Select(v => v.Name)); - fieldDeclaration.ReplaceWith(ReplaceFieldWithConstant); - } - } - - return null; - } - - AstNode ReplaceFieldWithConstant(AstNode node) - { - var old = node as FieldDeclaration; - var fd = new FieldDeclaration { - Modifiers = old.Modifiers & ~(Modifiers.Readonly | Modifiers.Static) | Modifiers.Const, - ReturnType = new PrimitiveType("decimal") - }; - - var foundAttr = old.Attributes.SelectMany(section => section.Attributes) - .First(a => decimalConstantAttribute.IsMatch(a)); - foundAttr.Remove(); - foreach (var attr in old.Attributes.Where(section => section.Attributes.Count == 0)) - attr.Remove(); - - old.Attributes.MoveTo(fd.Attributes); - old.Variables.MoveTo(fd.Variables); - - fd.Variables.Single().Initializer = new PrimitiveExpression(CreateDecimalValue(foundAttr)); - - return fd; - } - - object CreateDecimalValue(ICSharpCode.NRefactory.CSharp.Attribute foundAttr) - { - byte scale = (byte)((PrimitiveExpression)foundAttr.Arguments.ElementAt(0)).Value; - byte sign = (byte)((PrimitiveExpression)foundAttr.Arguments.ElementAt(1)).Value; - int high = (int)(uint)((PrimitiveExpression)foundAttr.Arguments.ElementAt(2)).Value; - int mid = (int)(uint)((PrimitiveExpression)foundAttr.Arguments.ElementAt(3)).Value; - int low = (int)(uint)((PrimitiveExpression)foundAttr.Arguments.ElementAt(4)).Value; - - return new Decimal(low, mid, high, sign == 1, scale); - } - - public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) - { - if ((constructorDeclaration.Modifiers & Modifiers.Static) == Modifiers.Static && replaceableFields.Count > 0) { - var current = replaceableFields.Peek(); - - foreach (var fieldName in current.Fields) { - var pattern = ConstructExpression(current.Declaration.Name, fieldName); - foreach (var expr in constructorDeclaration.Body - .OfType()) { - if (pattern.IsMatch(expr.Expression)) - expr.Remove(); + const Modifiers staticReadOnly = Modifiers.Static | Modifiers.Readonly; + if ((fieldDeclaration.Modifiers & staticReadOnly) == staticReadOnly && decimalType.IsMatch(fieldDeclaration.ReturnType)) { + foreach (var attributeSection in fieldDeclaration.Attributes) { + foreach (var attribute in attributeSection.Attributes) { + TypeReference tr = attribute.Type.Annotation(); + if (tr != null && tr.Name == "DecimalConstantAttribute" && tr.Namespace == "System.Runtime.CompilerServices") { + attribute.Remove(); + if (attributeSection.Attributes.Count == 0) + attributeSection.Remove(); + fieldDeclaration.Modifiers = (fieldDeclaration.Modifiers & ~staticReadOnly) | Modifiers.Const; + return null; + } } } } - return null; } - public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + public void Run(AstNode compilationUnit) { - replaceableFields.Push(new ClassInfo() { Declaration = typeDeclaration }); - base.VisitTypeDeclaration(typeDeclaration, data); - replaceableFields.Pop(); - return null; + compilationUnit.AcceptVisitor(this, null); } } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index 1433f0ef5..13730ea1d 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -32,7 +32,6 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public static IAstTransform[] CreatePipeline(DecompilerContext context) { return new IAstTransform[] { - new DecimalConstantTransform(), new PushNegation(), new DelegateConstruction(context), new PatternStatementTransform(context), @@ -41,6 +40,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms new AddCheckedBlocks(), new DeclareVariables(context), // should run after most transforms that modify statements new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables + new DecimalConstantTransform(), new IntroduceUsingDeclarations(context), new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods From 478035dfd9608f1c1ef2524dbf61a75331e69fa0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 15:00:43 +0200 Subject: [PATCH 29/59] Fix issues introduced when merging #166. --- .../ILAst/PeepholeTransform.cs | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index e4771eb48..93c8aa5d0 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -454,13 +454,6 @@ namespace ICSharpCode.Decompiler.ILAst return modified; } - private static IDictionary _postIncrementPairs = new Dictionary - { - {ILCode.Ldloc, ILCode.Stloc}, - {ILCode.Ldsfld, ILCode.Stsfld}, - {ILCode.CallGetter, ILCode.CallSetter} - }; - bool IntroducePostIncrementForVariables(List body, ILExpression expr, int pos) { // Works for variables and static fields/properties @@ -474,38 +467,48 @@ namespace ICSharpCode.Decompiler.ILAst if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated)) return false; - ILCode loadInstruction = exprInit.Code; - //We only recognise local variables, fields & getters with no arguments - if (!_postIncrementPairs.ContainsKey(loadInstruction)) - return false; - if (loadInstruction == ILCode.CallGetter && exprInit.Arguments.Count != 0) - return false; - //The next expression ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; if (nextExpr == null) return false; + + ILCode loadInstruction = exprInit.Code; ILCode storeInstruction = nextExpr.Code; - //Must be a matching store type - if (_postIncrementPairs[loadInstruction] != storeInstruction) - return false; + bool recombineVariable = false; - object nextExprOperand = nextExpr.Operand; - object exprInitOperand = exprInit.Operand; - if (loadInstruction == ILCode.CallGetter) - if (!IsGetterSetterPair(exprInitOperand, nextExprOperand)) - return false; - - if (loadInstruction == ILCode.Ldloc) - if ((nextExprOperand != exprInitOperand)) - if (((ILVariable) nextExprOperand).OriginalVariable == ((ILVariable) exprInitOperand).OriginalVariable) - //Spit local variable, unsplit these two instances - foreach (var ilExpression in body.SelectMany( - node => node.GetSelfAndChildrenRecursive( - expression => expression.Operand == nextExprOperand))) - ilExpression.Operand = exprInitOperand; - else + // We only recognise local variables, static fields, and static getters with no arguments + switch (loadInstruction) { + case ILCode.Ldloc: + //Must be a matching store type + if (storeInstruction != ILCode.Stloc) return false; + ILVariable loadVar = (ILVariable)exprInit.Operand; + ILVariable storeVar = (ILVariable)nextExpr.Operand; + if (loadVar != storeVar) { + if (loadVar.OriginalVariable != null && loadVar.OriginalVariable == storeVar.OriginalVariable) + recombineVariable = true; + else + return false; + } + break; + case ILCode.Ldsfld: + if (storeInstruction != ILCode.Stsfld) + return false; + if (exprInit.Operand != nextExpr.Operand) + return false; + break; + case ILCode.CallGetter: + // non-static getters would have the 'this' argument + if (exprInit.Arguments.Count != 0) + return false; + if (storeInstruction != ILCode.CallSetter) + return false; + if (!IsGetterSetterPair(exprInit.Operand, nextExpr.Operand)) + return false; + break; + default: + return false; + } ILExpression addExpr = nextExpr.Arguments[0]; @@ -514,8 +517,13 @@ namespace ICSharpCode.Decompiler.ILAst if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar))) return false; - switch (loadInstruction) - { + if (recombineVariable) { + // Split local variable, unsplit these two instances + foreach (var ilExpression in method.GetSelfAndChildrenRecursive(expression => expression.Operand == nextExpr.Operand)) + ilExpression.Operand = exprInit.Operand; + } + + switch (loadInstruction) { case ILCode.Ldloc: exprInit.Code = ILCode.Ldloca; break; From ee2cae3d1ab76967953188ebed12e123e9241d2d Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Fri, 20 May 2011 17:14:34 +0300 Subject: [PATCH 30/59] Add line numbers to text editor and in display settings. --- ILSpy/DisplaySettings.cs | 13 +++++++++++++ ILSpy/DisplaySettingsPanel.xaml | 7 +++++++ ILSpy/DisplaySettingsPanel.xaml.cs | 2 ++ ILSpy/TextView/DecompilerTextView.cs | 1 + 4 files changed, 23 insertions(+) diff --git a/ILSpy/DisplaySettings.cs b/ILSpy/DisplaySettings.cs index 4135ec7a5..569bc0509 100644 --- a/ILSpy/DisplaySettings.cs +++ b/ILSpy/DisplaySettings.cs @@ -71,10 +71,23 @@ namespace ICSharpCode.ILSpy } } + bool showLineNumbers; + + public bool ShowLineNumbers { + get { return showLineNumbers; } + set { + if (showLineNumbers != value) { + showLineNumbers = value; + OnPropertyChanged("ShowLineNumbers"); + } + } + } + public void CopyValues(DisplaySettings s) { this.SelectedFont = s.selectedFont; this.SelectedFontSize = s.selectedFontSize; + this.ShowLineNumbers = s.showLineNumbers; } } } diff --git a/ILSpy/DisplaySettingsPanel.xaml b/ILSpy/DisplaySettingsPanel.xaml index 0eb6f9294..97131eca3 100644 --- a/ILSpy/DisplaySettingsPanel.xaml +++ b/ILSpy/DisplaySettingsPanel.xaml @@ -6,6 +6,10 @@ + + + + @@ -53,5 +57,8 @@ + + Show line numbers + \ No newline at end of file diff --git a/ILSpy/DisplaySettingsPanel.xaml.cs b/ILSpy/DisplaySettingsPanel.xaml.cs index d7e262c7e..b66bb316d 100644 --- a/ILSpy/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/DisplaySettingsPanel.xaml.cs @@ -104,6 +104,7 @@ namespace ICSharpCode.ILSpy DisplaySettings s = new DisplaySettings(); s.SelectedFont = new FontFamily((string)e.Attribute("Font") ?? "Consolas"); s.SelectedFontSize = (double?)e.Attribute("FontSize") ?? 10.0 * 4 / 3; + s.ShowLineNumbers = (bool?)e.Attribute("ShowLineNumbers") ?? false; return s; } @@ -117,6 +118,7 @@ namespace ICSharpCode.ILSpy XElement section = new XElement("DisplaySettings"); section.SetAttributeValue("Font", s.SelectedFont.Source); section.SetAttributeValue("FontSize", s.SelectedFontSize); + section.SetAttributeValue("ShowLineNumbers", s.ShowLineNumbers); XElement existingElement = root.Element("DisplaySettings"); if (existingElement != null) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 6b6a26f84..748210427 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -88,6 +88,7 @@ namespace ICSharpCode.ILSpy.TextView textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped; textEditor.SetBinding(TextEditor.FontFamilyProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFont") }); textEditor.SetBinding(TextEditor.FontSizeProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFontSize") }); + textEditor.SetBinding(TextEditor.ShowLineNumbersProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("ShowLineNumbers") }); } #endregion From 4ec92d9a558faad5afe1b75f485e682ffcc3e81f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 15:23:01 +0200 Subject: [PATCH 31/59] Handle cpobj instruction as stobj(ldobj). --- .../Ast/AstMethodBodyBuilder.cs | 15 +++++++-------- ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs | 4 ++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 4d184ad02..babc9cd79 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -485,9 +485,12 @@ namespace ICSharpCode.Decompiler.Ast return arg1; else return arg1.CastTo(operandAsTypeRef); - case ILCode.Isinst: return arg1.CastAs(operandAsTypeRef); - case ILCode.Box: return arg1; - case ILCode.Unbox: return InlineAssembly(byteCode, args); + case ILCode.Isinst: + return arg1.CastAs(operandAsTypeRef); + case ILCode.Box: + return arg1; + case ILCode.Unbox: + return MakeRef(arg1.CastTo(operandAsTypeRef)); #endregion #region Indirect case ILCode.Ldind_Ref: @@ -539,11 +542,7 @@ namespace ICSharpCode.Decompiler.Ast case ILCode.Endfilter: return InlineAssembly(byteCode, args); case ILCode.Endfinally: return null; case ILCode.Initblk: return InlineAssembly(byteCode, args); - case ILCode.Initobj: - if (args[0] is DirectionExpression) - return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), MakeDefaultValue((TypeReference)operand)); - else - return InlineAssembly(byteCode, args); + case ILCode.Initobj: return InlineAssembly(byteCode, args); case ILCode.DefaultValue: return MakeDefaultValue((TypeReference)operand); case ILCode.Jmp: return InlineAssembly(byteCode, args); diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index 93c8aa5d0..f9e596514 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -90,6 +90,10 @@ namespace ICSharpCode.Decompiler.ILAst expr.Code = ILCode.Stobj; expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand)); modified = true; + } else if (expr.Code == ILCode.Cpobj) { + expr.Code = ILCode.Stobj; + expr.Arguments[1] = new ILExpression(ILCode.Ldobj, expr.Operand, expr.Arguments[1]); + modified = true; } ILExpression arg, arg2; TypeReference type; From faf3a29a08c0a2a871f5efcd797f365abb88af19 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 18:40:22 +0200 Subject: [PATCH 32/59] Fixed position of XML comments on nested types. --- .../ICSharpCode.AvalonEdit.csproj | 1 + ICSharpCode.Decompiler/Ast/AstBuilder.cs | 18 +++++++++--------- .../Documentation/XmlDocumentationProvider.cs | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index 0b12dff5b..ee5e00268 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -215,6 +215,7 @@ + IBackgroundRenderer.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 97d3d55e8..4da825fb4 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -228,15 +228,6 @@ namespace ICSharpCode.Decompiler.Ast astType.TypeParameters.AddRange(MakeTypeParameters(genericParameters)); astType.Constraints.AddRange(MakeConstraints(genericParameters)); - // Nested types - foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) { - if (MemberIsHidden(nestedTypeDef, context.Settings)) - continue; - var nestedType = CreateType(nestedTypeDef); - SetNewModifier(nestedType); - astType.AddChild(nestedType, TypeDeclaration.MemberRole); - } - AttributedNode result = astType; if (typeDef.IsEnum) { long expectedEnumMemberValue = 0; @@ -608,6 +599,15 @@ namespace ICSharpCode.Decompiler.Ast void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef) { + // Nested types + foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) { + if (MemberIsHidden(nestedTypeDef, context.Settings)) + continue; + var nestedType = CreateType(nestedTypeDef); + SetNewModifier(nestedType); + astType.AddChild(nestedType, TypeDeclaration.MemberRole); + } + // Add fields foreach(FieldDefinition fieldDef in typeDef.Fields) { if (MemberIsHidden(fieldDef, context.Settings)) continue; diff --git a/NRefactory/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs b/NRefactory/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs index 315f212eb..70d88aa16 100644 --- a/NRefactory/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs +++ b/NRefactory/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs @@ -120,7 +120,7 @@ namespace ICSharpCode.NRefactory.Documentation static string GetRedirectionTarget(string target) { - string programFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + string programFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); programFilesDir = AppendDirectorySeparator(programFilesDir); string corSysDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(); From e9eca059d5c1e19492d3b6b969620b2cde6ab58b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 20 May 2011 13:23:44 +0200 Subject: [PATCH 33/59] remove isLastLine from interface - use stack instead; fix null reference if resolve of TypeDefinition fails --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 12 ++++++------ ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs | 3 ++- .../CSharp/OutputVisitor/IOutputFormatter.cs | 2 +- .../CSharp/OutputVisitor/OutputVisitor.cs | 4 +--- .../OutputVisitor/TextWriterOutputFormatter.cs | 2 +- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 4da825fb4..c7845c61d 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -427,13 +427,13 @@ namespace ICSharpCode.Decompiler.Ast astType = new MemberType { Target = nsType, MemberName = name }; } else { astType = new SimpleType(name); + + // Look for generic type parameters defined in TypeDefinition + // allows us to display angle brackets in unbound type names + // e.g. typeof(List<>) + TypeDefinition resolvedType = type.Resolve(); - if (!type.HasGenericParameters) { - // Look for generic type parameters defined in TypeDefinition - // allows us to display angle brackets in unbound type names - // e.g. typeof(List<>) - TypeDefinition resolvedType = type.Resolve(); - + if (!type.HasGenericParameters && resolvedType != null) { for (int i = 0; i < resolvedType.GenericParameters.Count; i++) { ((SimpleType)astType).TypeArguments.Add(new SimpleType("")); } diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index b97deb091..d0edf5bcc 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -103,7 +103,7 @@ namespace ICSharpCode.Decompiler.Ast output.WriteLine(); } - public void WriteComment(CommentType commentType, string content, bool isLastLine = false) + public void WriteComment(CommentType commentType, string content) { switch (commentType) { case CommentType.SingleLine: @@ -121,6 +121,7 @@ namespace ICSharpCode.Decompiler.Ast output.Write("///"); output.Write(content); inDocumentationComment = true; + bool isLastLine = !(nodeStack.Peek().NextSibling is Comment); if (isLastLine) { inDocumentationComment = false; output.MarkFoldEnd(); diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/IOutputFormatter.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/IOutputFormatter.cs index 58f8c23ed..ac1966296 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/IOutputFormatter.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/IOutputFormatter.cs @@ -39,6 +39,6 @@ namespace ICSharpCode.NRefactory.CSharp void NewLine(); - void WriteComment(CommentType commentType, string content, bool isLastLine = false); + void WriteComment(CommentType commentType, string content); } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index e715324ef..eb5e9d2ba 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -2147,9 +2147,7 @@ namespace ICSharpCode.NRefactory.CSharp // "1.0 / /*comment*/a", then we need to insert a space in front of the comment. formatter.Space(); } - bool isContinuing = (comment.NextSibling is Comment && ((Comment)comment.NextSibling).CommentType == comment.CommentType); - - formatter.WriteComment(comment.CommentType, comment.Content, !isContinuing); + formatter.WriteComment(comment.CommentType, comment.Content); lastWritten = LastWritten.Whitespace; return null; } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/TextWriterOutputFormatter.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/TextWriterOutputFormatter.cs index c6081d4f5..f8c1340e5 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/TextWriterOutputFormatter.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/TextWriterOutputFormatter.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.NRefactory.CSharp indentation--; } - public void WriteComment(CommentType commentType, string content, bool isLastLine = false) + public void WriteComment(CommentType commentType, string content) { WriteIndentation(); switch (commentType) { From 439de223bfa43ecfdb431e22f7ff618a02e35955 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 20 May 2011 19:45:40 +0200 Subject: [PATCH 34/59] implement proper fix for #179 + add unit test --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 11 -------- .../Ast/AstMethodBodyBuilder.cs | 28 ++++++++++++++++++- ICSharpCode.Decompiler/Tests/Generics.cs | 4 +++ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index c7845c61d..be793f881 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -427,17 +427,6 @@ namespace ICSharpCode.Decompiler.Ast astType = new MemberType { Target = nsType, MemberName = name }; } else { astType = new SimpleType(name); - - // Look for generic type parameters defined in TypeDefinition - // allows us to display angle brackets in unbound type names - // e.g. typeof(List<>) - TypeDefinition resolvedType = type.Resolve(); - - if (!type.HasGenericParameters && resolvedType != null) { - for (int i = 0; i < resolvedType.GenericParameters.Count; i++) { - ((SimpleType)astType).TypeArguments.Add(new SimpleType("")); - } - } } astType.AddAnnotation(type); diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index babc9cd79..b54cd21ae 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -601,7 +601,7 @@ namespace ICSharpCode.Decompiler.Ast case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); case ILCode.Ldtoken: if (operand is Cecil.TypeReference) { - return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle"); + return new Ast.TypeOfExpression { Type = AddEmptyTypeArgumentsForUnboundGenerics(operandAsTypeRef) }.Member("TypeHandle"); } else { return InlineAssembly(byteCode, args); } @@ -749,6 +749,32 @@ namespace ICSharpCode.Decompiler.Ast } } + AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type) + { + TypeDefinition typeDef = type.Annotation().Resolve(); + if (typeDef == null || !typeDef.HasGenericParameters) + return type; + SimpleType sType = type as SimpleType; + MemberType mType = type as MemberType; + if (sType != null) { + while (typeDef.GenericParameters.Count > sType.TypeArguments.Count) { + sType.TypeArguments.Add(new SimpleType("")); + } + } + + if (mType != null) { + AddEmptyTypeArgumentsForUnboundGenerics(mType.Target); + + int outerTypeParamCount = typeDef.DeclaringType == null ? 0 : typeDef.DeclaringType.GenericParameters.Count; + + while (typeDef.GenericParameters.Count - outerTypeParamCount > mType.TypeArguments.Count) { + mType.TypeArguments.Add(new SimpleType("")); + } + } + + return type; + } + static readonly AstNode objectInitializerPattern = new AssignmentExpression( new MemberReferenceExpression { Target = new InitializedObjectExpression() diff --git a/ICSharpCode.Decompiler/Tests/Generics.cs b/ICSharpCode.Decompiler/Tests/Generics.cs index 53cd5f623..4a1da99b5 100644 --- a/ICSharpCode.Decompiler/Tests/Generics.cs +++ b/ICSharpCode.Decompiler/Tests/Generics.cs @@ -60,6 +60,10 @@ public static class Generics } } + private static Type type1 = typeof(List<>); + private static Type type2 = typeof(Generics.MyArray<>); + private static Type type3 = typeof(List<>.Enumerator); + public static void MethodWithConstraint() where T : class, S where S : ICloneable, new() { } From b01fe1b4273b10bf34d332a2d0fe16e3535846a0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 19:55:06 +0200 Subject: [PATCH 35/59] Fixed NullReferenceException when decompiling "typeof(List[])". --- ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs | 5 ++++- ICSharpCode.Decompiler/Tests/Generics.cs | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index b54cd21ae..c8666b73a 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -751,7 +751,10 @@ namespace ICSharpCode.Decompiler.Ast AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type) { - TypeDefinition typeDef = type.Annotation().Resolve(); + TypeReference typeRef = type.Annotation(); + if (typeRef == null) + return type; + TypeDefinition typeDef = typeRef.Resolve(); // need to resolve to figure out the number of type parameters if (typeDef == null || !typeDef.HasGenericParameters) return type; SimpleType sType = type as SimpleType; diff --git a/ICSharpCode.Decompiler/Tests/Generics.cs b/ICSharpCode.Decompiler/Tests/Generics.cs index 4a1da99b5..e5b6e2eb7 100644 --- a/ICSharpCode.Decompiler/Tests/Generics.cs +++ b/ICSharpCode.Decompiler/Tests/Generics.cs @@ -63,6 +63,8 @@ public static class Generics private static Type type1 = typeof(List<>); private static Type type2 = typeof(Generics.MyArray<>); private static Type type3 = typeof(List<>.Enumerator); + private static Type type4 = typeof(Generics.MyArray<>.NestedClass<>); + private static Type type5 = typeof(List[]); public static void MethodWithConstraint() where T : class, S where S : ICloneable, new() { From e2794252fb1a5cde291a276aec9408d4bcff0b33 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 21:33:38 +0200 Subject: [PATCH 36/59] When decompiling a field, also decompile constructors to check whether there's an initializer on the field. When decompiling a constructor, display field initializers outside of the constructor. Closes #3. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 3 + .../ConvertConstructorCallIntoInitializer.cs | 56 +++++++--- ILSpy/CSharpLanguage.cs | 102 ++++++++++++++++-- .../CSharp/OutputVisitor/OutputVisitor.cs | 2 + 4 files changed, 141 insertions(+), 22 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index be793f881..f941af8b3 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -729,6 +729,9 @@ namespace ICSharpCode.Decompiler.Ast astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters); ConvertAttributes(astMethod, methodDef); astMethod.WithAnnotation(methodMapping); + if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) { + astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), AstNode.Roles.Comment); + } return astMethod; } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs index 716754595..054d3066e 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using System.Linq; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.PatternMatching; @@ -69,9 +70,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) { - var instanceCtors = typeDeclaration.Members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); + // Handle initializers on instance fields + HandleInstanceFieldInitializers(typeDeclaration.Members); + + // Now convert base constructor calls to initializers: + base.VisitTypeDeclaration(typeDeclaration, data); + + // Remove single empty constructor: + RemoveSingleEmptyConstructor(typeDeclaration); + + // Handle initializers on static fields: + HandleStaticFieldInitializers(typeDeclaration.Members); + return null; + } + + void HandleInstanceFieldInitializers(IEnumerable members) + { + var instanceCtors = members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); - if (instanceCtorsNotChainingWithThis.Length > 0 && typeDeclaration.ClassType == NRefactory.TypeSystem.ClassType.Class) { + if (instanceCtorsNotChainingWithThis.Length > 0) { + MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation(); + if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType) + return; + // Recognize field initializers: // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer. bool allSame; @@ -83,7 +104,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms FieldDefinition fieldDef = m.Get("fieldAccess").Single().Annotation().ResolveWithinSameModule(); if (fieldDef == null) break; - AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation() == fieldDef); + AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation() == fieldDef); if (fieldOrEventDecl == null) break; @@ -99,11 +120,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } while (allSame); } - - // Now convert base constructor calls to initializers: - base.VisitTypeDeclaration(typeDeclaration, data); - - // Remove single empty constructor: + } + + void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration) + { + var instanceCtors = typeDeclaration.Members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); if (instanceCtors.Length == 1) { ConstructorDeclaration emptyCtor = new ConstructorDeclaration(); emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public); @@ -111,12 +132,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (emptyCtor.IsMatch(instanceCtors[0])) instanceCtors[0].Remove(); } - + } + + void HandleStaticFieldInitializers(IEnumerable members) + { // Convert static constructor into field initializers if the class is BeforeFieldInit - var staticCtor = typeDeclaration.Members.OfType().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static); + var staticCtor = members.OfType().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static); if (staticCtor != null) { - TypeDefinition typeDef = typeDeclaration.Annotation(); - if (typeDef != null && typeDef.IsBeforeFieldInit) { + MethodDefinition ctorMethodDef = staticCtor.Annotation(); + if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) { while (true) { ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement; if (es == null) @@ -127,7 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms FieldDefinition fieldDef = assignment.Left.Annotation().ResolveWithinSameModule(); if (fieldDef == null || !fieldDef.IsStatic) break; - FieldDeclaration fieldDecl = typeDeclaration.Members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef); + FieldDeclaration fieldDecl = members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef); if (fieldDecl == null) break; fieldDecl.Variables.Single().Initializer = assignment.Right.Detach(); @@ -137,11 +161,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms staticCtor.Remove(); } } - return null; } void IAstTransform.Run(AstNode node) { + // If we're viewing some set of members (fields are direct children of CompilationUnit), + // we also need to handle those: + HandleInstanceFieldInitializers(node.Children); + HandleStaticFieldInitializers(node.Children); + node.AcceptVisitor(this, null); } } diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 3cdcb966a..928e53a37 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -52,7 +52,7 @@ namespace ICSharpCode.ILSpy { } -#if DEBUG + #if DEBUG internal static IEnumerable GetDebugLanguages() { DecompilerContext context = new DecompilerContext(ModuleDefinition.CreateModule("dummy", ModuleKind.Dll)); @@ -71,7 +71,7 @@ namespace ICSharpCode.ILSpy showAllMembers = true }; } -#endif + #endif public override string Name { @@ -92,8 +92,50 @@ namespace ICSharpCode.ILSpy { WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true)); AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: method.DeclaringType, isSingleMember: true); - codeDomBuilder.AddMethod(method); - RunTransformsAndGenerateCode(codeDomBuilder, output, options); + if (method.IsConstructor && !method.IsStatic && !method.DeclaringType.IsValueType) { + // also fields and other ctors so that the field initializers can be shown as such + AddFieldsAndCtors(codeDomBuilder, method.DeclaringType, method.IsStatic); + RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectCtorTransform(method)); + } else { + codeDomBuilder.AddMethod(method); + RunTransformsAndGenerateCode(codeDomBuilder, output, options); + } + } + + class SelectCtorTransform : IAstTransform + { + readonly MethodDefinition ctorDef; + + public SelectCtorTransform(MethodDefinition ctorDef) + { + this.ctorDef = ctorDef; + } + + public void Run(AstNode compilationUnit) + { + ConstructorDeclaration ctorDecl = null; + foreach (var node in compilationUnit.Children) { + ConstructorDeclaration ctor = node as ConstructorDeclaration; + if (ctor != null) { + if (ctor.Annotation() == ctorDef) { + ctorDecl = ctor; + } else { + // remove other ctors + ctor.Remove(); + } + } + // Remove any fields without initializers + FieldDeclaration fd = node as FieldDeclaration; + if (fd != null && fd.Variables.All(v => v.Initializer.IsNull)) + fd.Remove(); + } + if (ctorDecl.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) { + // remove all fields + foreach (var node in compilationUnit.Children) + if (node is FieldDeclaration) + node.Remove(); + } + } } public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) @@ -108,8 +150,48 @@ namespace ICSharpCode.ILSpy { WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true)); AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: field.DeclaringType, isSingleMember: true); - codeDomBuilder.AddField(field); - RunTransformsAndGenerateCode(codeDomBuilder, output, options); + if (field.IsLiteral) { + codeDomBuilder.AddField(field); + } else { + // also decompile ctors so that the field initializer can be shown + AddFieldsAndCtors(codeDomBuilder, field.DeclaringType, field.IsStatic); + } + RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectFieldTransform(field)); + } + + /// + /// Removes all top-level members except for the specified fields. + /// + sealed class SelectFieldTransform : IAstTransform + { + readonly FieldDefinition field; + + public SelectFieldTransform(FieldDefinition field) + { + this.field = field; + } + + public void Run(AstNode compilationUnit) + { + foreach (var child in compilationUnit.Children) { + if (child is AttributedNode) { + if (child.Annotation() != field) + child.Remove(); + } + } + } + } + + void AddFieldsAndCtors(AstBuilder codeDomBuilder, TypeDefinition declaringType, bool isStatic) + { + foreach (var field in declaringType.Fields) { + if (field.IsStatic == isStatic) + codeDomBuilder.AddField(field); + } + foreach (var ctor in declaringType.Methods) { + if (ctor.IsConstructor && ctor.IsStatic == isStatic) + codeDomBuilder.AddMethod(ctor); + } } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) @@ -127,11 +209,15 @@ namespace ICSharpCode.ILSpy RunTransformsAndGenerateCode(codeDomBuilder, output, options); } - void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options) + void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options, IAstTransform additionalTransform = null) { astBuilder.RunTransformations(transformAbortCondition); - if (options.DecompilerSettings.ShowXmlDocumentation) + if (additionalTransform != null) { + additionalTransform.Run(astBuilder.CompilationUnit); + } + if (options.DecompilerSettings.ShowXmlDocumentation) { AddXmlDocTransform.Run(astBuilder.CompilationUnit); + } astBuilder.GenerateCode(output); } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index eb5e9d2ba..ee76d82ee 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -2147,7 +2147,9 @@ namespace ICSharpCode.NRefactory.CSharp // "1.0 / /*comment*/a", then we need to insert a space in front of the comment. formatter.Space(); } + formatter.StartNode(comment); formatter.WriteComment(comment.CommentType, comment.Content); + formatter.EndNode(comment); lastWritten = LastWritten.Whitespace; return null; } From 4abc650ecd14bd31e2aa390b9d9d8193c31c2ab5 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 21:44:37 +0200 Subject: [PATCH 37/59] Fix NullReferenceException in TrransformSwitchOnString. Closes #95. --- .../Ast/Transforms/PatternStatementTransform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index bc17d13cc..6c34b1b4f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -648,7 +648,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment if (!m.Get("switchVar").Single().IsMatch(m.Get("switchExpr").Single())) { AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression; - if (!m.Get("switchVar").Single().IsMatch(assign.Left)) + if (!(assign != null && m.Get("switchVar").Single().IsMatch(assign.Left))) return null; } FieldReference cachedDictField = m.Get("cachedDict").Single().Annotation(); From 5c08e10a07f5a2083386ca050d33b97d14573d1d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 May 2011 22:37:38 +0200 Subject: [PATCH 38/59] Fixed incorrect detection of the 'using' statement pattern when the code was assigning to the using variable. Closes #121. --- .../Transforms/PatternStatementTransform.cs | 107 +++++++++++++----- .../Tests/ExceptionHandling.cs | 26 +++++ 2 files changed, 102 insertions(+), 31 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 6c34b1b4f..46ff353c8 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -184,40 +184,67 @@ namespace ICSharpCode.Decompiler.Ast.Transforms Match m2 = usingTryCatchPattern.Match(tryCatch); if (!m2.Success) return null; string variableName = m1.Get("variable").Single().Identifier; - if (variableName == m2.Get("ident").Single().Identifier) { - if (m2.Has("valueType")) { - // if there's no if(x!=null), then it must be a value type - ILVariable v = m1.Get("variable").Single().Annotation(); - if (v == null || v.Type == null || !v.Type.IsValueType) - return null; - } - node.Remove(); - BlockStatement body = m2.Get("body").Single(); - UsingStatement usingStatement = new UsingStatement(); - usingStatement.ResourceAcquisition = node.Expression.Detach(); - usingStatement.EmbeddedStatement = body.Detach(); - tryCatch.ReplaceWith(usingStatement); - // Move the variable declaration into the resource acquisition, if possible - // This is necessary for the foreach-pattern to work on the result of the using-pattern - VariableDeclarationStatement varDecl = FindVariableDeclaration(usingStatement, variableName); - if (varDecl != null && varDecl.Parent is BlockStatement) { - Statement declarationPoint; - if (CanMoveVariableDeclarationIntoStatement(varDecl, usingStatement, out declarationPoint)) { - // Moving the variable into the UsingStatement is allowed: - usingStatement.ResourceAcquisition = new VariableDeclarationStatement { - Type = (AstType)varDecl.Type.Clone(), - Variables = { - new VariableInitializer { - Name = variableName, - Initializer = m1.Get("initializer").Single().Detach() - }.CopyAnnotationsFrom(usingStatement.ResourceAcquisition) - } - }.CopyAnnotationsFrom(node); - } + if (variableName != m2.Get("ident").Single().Identifier) + return null; + if (m2.Has("valueType")) { + // if there's no if(x!=null), then it must be a value type + ILVariable v = m1.Get("variable").Single().Annotation(); + if (v == null || v.Type == null || !v.Type.IsValueType) + return null; + } + + // There are two variants of the using statement: + // "using (var a = init)" and "using (expr)". + // The former declares a read-only variable 'a', and the latter declares an unnamed read-only variable + // to store the original value of 'expr'. + // This means that in order to introduce a using statement, in both cases we need to detect a read-only + // variable that is used only within that block. + + if (HasAssignment(tryCatch, variableName)) + return null; + + VariableDeclarationStatement varDecl = FindVariableDeclaration(node, variableName); + if (varDecl == null || !(varDecl.Parent is BlockStatement)) + return null; + + // Create the using statement so that we can use definite assignment analysis to check + // whether the variable is declared correctly there. + node.Remove(); + BlockStatement body = m2.Get("body").Single(); + UsingStatement usingStatement = new UsingStatement(); + usingStatement.ResourceAcquisition = node.Expression.Detach(); + usingStatement.EmbeddedStatement = body.Detach(); + tryCatch.ReplaceWith(usingStatement); + + // Check whether moving the variable declaration into the resource acquisition is possible + Statement declarationPoint; + if (CanMoveVariableDeclarationIntoStatement(varDecl, usingStatement, out declarationPoint)) { + // Moving the variable into the UsingStatement is allowed. + // But if possible, we'll eliminate the variable completely: + if (body.Descendants.OfType().Any(ident => ident.Identifier == variableName)) { + usingStatement.ResourceAcquisition = new VariableDeclarationStatement { + Type = (AstType)varDecl.Type.Clone(), + Variables = { + new VariableInitializer { + Name = variableName, + Initializer = m1.Get("initializer").Single().Detach() + }.CopyAnnotationsFrom(usingStatement.ResourceAcquisition) + } + }.CopyAnnotationsFrom(node); + } else { + // the variable is never used; eliminate it: + usingStatement.ResourceAcquisition = m1.Get("initializer").Single().Detach(); } return usingStatement; + } else { + // oops, we can't use a using statement after all; so revert back to try-finally + usingStatement.ReplaceWith(tryCatch); + ((TryCatchStatement)tryCatch).TryBlock = body.Detach(); + node.Expression = (Expression)usingStatement.ResourceAcquisition.Detach(); + + tryCatch.Parent.InsertChildBefore(tryCatch, node, BlockStatement.StatementRole); + return null; } - return null; } internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier) @@ -251,6 +278,24 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } return true; } + + /// + /// Gets whether there is an assignment to 'variableName' anywhere within the given node. + /// + bool HasAssignment(AstNode root, string variableName) + { + foreach (AstNode node in root.DescendantsAndSelf) { + IdentifierExpression ident = node as IdentifierExpression; + if (ident != null && ident.Identifier == variableName) { + if (ident.Parent is AssignmentExpression && ident.Role == AssignmentExpression.LeftRole + || ident.Parent is DirectionExpression) + { + return true; + } + } + } + return false; + } #endregion #region foreach (generic) diff --git a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs index 5fdc27dc5..9d81936a5 100644 --- a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs +++ b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs @@ -2,6 +2,7 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Threading; public class ExceptionHandling { @@ -64,4 +65,29 @@ public class ExceptionHandling Console.WriteLine("other"); } } + + public void NoUsingStatementBecauseTheVariableIsAssignedTo() + { + CancellationTokenSource cancellationTokenSource = null; + try + { + cancellationTokenSource = new CancellationTokenSource(); + } + finally + { + if (cancellationTokenSource != null) + { + cancellationTokenSource.Dispose(); + } + } + } + + public void UsingStatementThatChangesTheVariable() + { + CancellationTokenSource cancellationTokenSource = null; + using (cancellationTokenSource) + { + cancellationTokenSource = new CancellationTokenSource(); + } + } } From 3833643aaf8a1df26ab68108b1051eca92aab502 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 00:45:59 +0200 Subject: [PATCH 39/59] Fixed issues with detection of using statements. --- .../Transforms/PatternStatementTransform.cs | 80 +++++++++++-------- .../Analysis/DefiniteAssignmentAnalysis.cs | 12 +-- 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 46ff353c8..3d5c0e596 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -154,7 +154,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } static readonly AstNode usingTryCatchPattern = new TryCatchStatement { - TryBlock = new AnyNode("body"), + TryBlock = new AnyNode(), FinallyBlock = new BlockStatement { new Choice { { "valueType", @@ -180,7 +180,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms { Match m1 = variableAssignPattern.Match(node); if (!m1.Success) return null; - AstNode tryCatch = node.NextSibling; + TryCatchStatement tryCatch = node.NextSibling as TryCatchStatement; Match m2 = usingTryCatchPattern.Match(tryCatch); if (!m2.Success) return null; string variableName = m1.Get("variable").Single().Identifier; @@ -207,44 +207,33 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (varDecl == null || !(varDecl.Parent is BlockStatement)) return null; - // Create the using statement so that we can use definite assignment analysis to check - // whether the variable is declared correctly there. + // Validate that the variable is not used after the using statement: + if (!IsVariableValueUnused(varDecl, tryCatch)) + return null; + node.Remove(); - BlockStatement body = m2.Get("body").Single(); + UsingStatement usingStatement = new UsingStatement(); - usingStatement.ResourceAcquisition = node.Expression.Detach(); - usingStatement.EmbeddedStatement = body.Detach(); + usingStatement.EmbeddedStatement = tryCatch.TryBlock.Detach(); tryCatch.ReplaceWith(usingStatement); - // Check whether moving the variable declaration into the resource acquisition is possible - Statement declarationPoint; - if (CanMoveVariableDeclarationIntoStatement(varDecl, usingStatement, out declarationPoint)) { - // Moving the variable into the UsingStatement is allowed. - // But if possible, we'll eliminate the variable completely: - if (body.Descendants.OfType().Any(ident => ident.Identifier == variableName)) { - usingStatement.ResourceAcquisition = new VariableDeclarationStatement { - Type = (AstType)varDecl.Type.Clone(), - Variables = { - new VariableInitializer { - Name = variableName, - Initializer = m1.Get("initializer").Single().Detach() - }.CopyAnnotationsFrom(usingStatement.ResourceAcquisition) - } - }.CopyAnnotationsFrom(node); - } else { - // the variable is never used; eliminate it: - usingStatement.ResourceAcquisition = m1.Get("initializer").Single().Detach(); - } - return usingStatement; + // If possible, we'll eliminate the variable completely: + if (usingStatement.EmbeddedStatement.Descendants.OfType().Any(ident => ident.Identifier == variableName)) { + // variable is used, so we'll create a variable declaration + usingStatement.ResourceAcquisition = new VariableDeclarationStatement { + Type = (AstType)varDecl.Type.Clone(), + Variables = { + new VariableInitializer { + Name = variableName, + Initializer = m1.Get("initializer").Single().Detach() + }.CopyAnnotationsFrom(node.Expression) + } + }.CopyAnnotationsFrom(node); } else { - // oops, we can't use a using statement after all; so revert back to try-finally - usingStatement.ReplaceWith(tryCatch); - ((TryCatchStatement)tryCatch).TryBlock = body.Detach(); - node.Expression = (Expression)usingStatement.ResourceAcquisition.Detach(); - - tryCatch.Parent.InsertChildBefore(tryCatch, node, BlockStatement.StatementRole); - return null; + // the variable is never used; eliminate it: + usingStatement.ResourceAcquisition = m1.Get("initializer").Single().Detach(); } + return usingStatement; } internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier) @@ -262,6 +251,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms return null; } + /// + /// Gets whether the old variable value (assigned inside 'targetStatement' or earlier) + /// is read anywhere in the remaining scope of the variable declaration. + /// + bool IsVariableValueUnused(VariableDeclarationStatement varDecl, Statement targetStatement) + { + Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent)); + BlockStatement block = (BlockStatement)varDecl.Parent; + DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(block, context.CancellationToken); + daa.SetAnalyzedRange(targetStatement, block, startInclusive: false); + daa.Analyze(varDecl.Variables.Single().Name); + return daa.UnassignedVariableUses.Count == 0; + } + + // I used this in the first implementation of the using-statement transform, but now no longer + // because there were problems when multiple using statements were using the same variable + // - no single using statement could be transformed without making the C# code invalid, + // but transforming both would work. + // We now use 'IsVariableValueUnused' which will perform the transform + // even if it results in two variables with the same name and overlapping scopes. + // (this issue could be fixed later by renaming one of the variables) + + // I'm not sure whether the other consumers of 'CanMoveVariableDeclarationIntoStatement' should be changed the same way. bool CanMoveVariableDeclarationIntoStatement(VariableDeclarationStatement varDecl, Statement targetStatement, out Statement declarationPoint) { Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent)); diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs index d4cc6e1c5..452ca686a 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs @@ -167,12 +167,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis /// This method can be used to restrict the analysis to only a part of the method. /// Only the control flow paths that are fully contained within the selected part will be analyzed. /// - /// Both 'start' and 'end' are inclusive. - public void SetAnalyzedRange(Statement start, Statement end) + /// By default, both 'start' and 'end' are inclusive. + public void SetAnalyzedRange(Statement start, Statement end, bool startInclusive = true, bool endInclusive = true) { - Debug.Assert(beginNodeDict.ContainsKey(start) && endNodeDict.ContainsKey(end)); - int startIndex = beginNodeDict[start].Index; - int endIndex = endNodeDict[end].Index; + var dictForStart = startInclusive ? beginNodeDict : endNodeDict; + var dictForEnd = endInclusive ? endNodeDict : beginNodeDict; + Debug.Assert(dictForStart.ContainsKey(start) && dictForEnd.ContainsKey(end)); + int startIndex = dictForStart[start].Index; + int endIndex = dictForEnd[end].Index; if (startIndex > endIndex) throw new ArgumentException("The start statement must be lexically preceding the end statement"); this.analyzedRangeStart = startIndex; From ae873ec18f1daee58548335649e86332a6029085 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 11:53:00 +0200 Subject: [PATCH 40/59] Fixed decompilation of anonymous methods created by the VB compiler. Closes #127. --- .../Ast/Transforms/DelegateConstruction.cs | 13 ++++++++----- ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 1d58a021f..85ac9da1e 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method) { - if (method == null || !method.Name.StartsWith("<", StringComparison.Ordinal)) + if (method == null || !(method.Name.StartsWith("<", StringComparison.Ordinal) || method.Name.Contains("$"))) return false; if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType))) return false; @@ -388,10 +388,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms continue; // skip static fields if (dict.ContainsKey(field)) // skip field if it already was handled as parameter continue; - EnsureVariableNameIsAvailable(blockStatement, field.Name); - currentlyUsedVariableNames.Add(field.Name); - variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), field.Name)); - dict[field] = new IdentifierExpression(field.Name); + string capturedVariableName = field.Name; + if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10) + capturedVariableName = capturedVariableName.Substring(10); + EnsureVariableNameIsAvailable(blockStatement, capturedVariableName); + currentlyUsedVariableNames.Add(capturedVariableName); + variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), capturedVariableName)); + dict[field] = new IdentifierExpression(capturedVariableName); } // Now figure out where the closure was accessed and use the simpler replacement expression there: diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 1e4ebeddd..8a3cc466f 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -308,6 +308,8 @@ namespace ICSharpCode.Decompiler.ILAst /// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions. /// /// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)". + /// + /// Also simplifies 'newobj(SomeDelegate, target, ldvirtftn(F, target))' to 'newobj(SomeDelegate, target, ldvirtftn(F))' /// void IntroducePropertyAccessInstructions(ILNode node) { @@ -365,6 +367,20 @@ namespace ICSharpCode.Decompiler.ILAst expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter; } } + } else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) { + // Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'. + ILVariable target; + ILExpression ldvirtftnArg; + if (expr.Arguments[0].Match(ILCode.Ldloc, out target) + && expr.Arguments[1].Code == ILCode.Ldvirtftn + && expr.Arguments[1].Arguments.Count == 1 + && expr.Arguments[1].Arguments[0].MatchLdloc(target)) + { + // Remove the 'target' argument from the ldvirtftn instruction. + // It's not needed in the translation to C#, and needs to be eliminated so that the target expression + // can be inlined. + expr.Arguments[1].Arguments.Clear(); + } } } From a15ecd533b37d130f1213fae96339504333f40fe Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 12:15:12 +0200 Subject: [PATCH 41/59] Fix NullReferenceException in XmlDocKeyProvider when decompiling C++/CLI code. Closes #173. --- ILSpy/XmlDoc/XmlDocKeyProvider.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ILSpy/XmlDoc/XmlDocKeyProvider.cs b/ILSpy/XmlDoc/XmlDocKeyProvider.cs index 7e9960067..fc503c115 100644 --- a/ILSpy/XmlDoc/XmlDocKeyProvider.cs +++ b/ILSpy/XmlDoc/XmlDocKeyProvider.cs @@ -79,6 +79,10 @@ namespace ICSharpCode.ILSpy.XmlDoc static void AppendTypeName(StringBuilder b, TypeReference type) { + if (type == null) { + // could happen when a TypeSpecification has no ElementType; e.g. function pointers in C++/CLI assemblies + return; + } if (type is GenericInstanceType) { GenericInstanceType giType = (GenericInstanceType)type; if (type.DeclaringType != null) { From d362f8d0f606398342cd10b549f0b0f52b5cd348 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 14:43:12 +0200 Subject: [PATCH 42/59] Fixed several bugs in the IL disassembler. --- .../Disassembler/DisassemblerHelpers.cs | 155 +++++++++++++++--- .../Disassembler/MethodBodyDisassembler.cs | 59 +++++-- .../Disassembler/ReflectionDisassembler.cs | 130 +++++++++------ ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 14 +- ILSpy/ILAstLanguage.cs | 4 +- ILSpy/ILLanguage.cs | 44 ++++- 6 files changed, 301 insertions(+), 105 deletions(-) diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index 0060b71d8..672cf252c 100644 --- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -22,6 +22,26 @@ using Mono.Cecil.Cil; namespace ICSharpCode.Decompiler.Disassembler { + public enum ILNameSyntax + { + /// + /// class/valuetype + TypeName (built-in types use keyword syntax) + /// + Signature, + /// + /// Like signature, but always refers to type parameters using their position + /// + SignatureNoNamedTypeParameters, + /// + /// [assembly]Full.Type.Name (even for built-in types) + /// + TypeName, + /// + /// Name (even for built-in types) + /// + ShortTypeName + } + public static class DisassemblerHelpers { public static void WriteOffsetReference(ITextOutput writer, Instruction instruction) @@ -35,6 +55,7 @@ namespace ICSharpCode.Decompiler.Disassembler WriteOffsetReference(writer, exceptionHandler.TryStart); writer.Write('-'); WriteOffsetReference(writer, exceptionHandler.TryEnd); + writer.Write(' '); writer.Write(exceptionHandler.HandlerType.ToString()); if (exceptionHandler.FilterStart != null) { writer.Write(' '); @@ -84,44 +105,70 @@ namespace ICSharpCode.Decompiler.Disassembler { if (method.HasThis) writer.Write("instance "); - method.ReturnType.WriteTo(writer); + method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); writer.Write(' '); if (method.DeclaringType != null) { - method.DeclaringType.WriteTo(writer, true); + method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write("::"); } - writer.WriteReference(method.Name, method); + writer.WriteReference(Escape(method.Name), method); + GenericInstanceMethod gim = method as GenericInstanceMethod; + if (gim != null) { + writer.Write('<'); + for (int i = 0; i < gim.GenericArguments.Count; i++) { + if (i > 0) + writer.Write(", "); + gim.GenericArguments[i].WriteTo(writer); + } + writer.Write('>'); + } writer.Write("("); var parameters = method.Parameters; for(int i = 0; i < parameters.Count; ++i) { if (i > 0) writer.Write(", "); - parameters[i].ParameterType.WriteTo(writer); + parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); } writer.Write(")"); } static void WriteTo(this FieldReference field, ITextOutput writer) { - field.FieldType.WriteTo(writer); + field.FieldType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); writer.Write(' '); - field.DeclaringType.WriteTo(writer); + field.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write("::"); - writer.WriteReference(field.Name, field); + writer.WriteReference(Escape(field.Name), field); + } + + static bool IsValidIdentifier(string identifier) + { + if (string.IsNullOrEmpty(identifier)) + return false; + if (!(char.IsLetter(identifier[0]) || identifier[0] == '_' || identifier[0] == '.')) + return false; + for (int i = 1; i < identifier.Length; i++) { + if (!(char.IsLetterOrDigit(identifier[i]) || identifier[i] == '_' || identifier[i] == '.' || identifier[i] == '`')) + return false; + } + return true; } public static string Escape(string identifier) { - return identifier; + if (IsValidIdentifier(identifier) && identifier != "value") + return identifier; + else + return "'" + identifier + "'"; } - public static void WriteTo(this TypeReference type, ITextOutput writer, bool onlyName = false, bool shortName = false) + public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature) { if (type is PinnedType) { writer.Write("pinned "); - ((PinnedType)type).ElementType.WriteTo(writer, onlyName, shortName); + ((PinnedType)type).ElementType.WriteTo(writer, syntax); } else if (type is ArrayType) { ArrayType at = (ArrayType)type; - at.ElementType.WriteTo(writer, onlyName, shortName); + at.ElementType.WriteTo(writer, syntax); writer.Write('['); writer.Write(string.Join(", ", at.Dimensions)); writer.Write(']'); @@ -129,49 +176,54 @@ namespace ICSharpCode.Decompiler.Disassembler writer.Write('!'); if (((GenericParameter)type).Owner.GenericParameterType == GenericParameterType.Method) writer.Write('!'); - writer.Write(type.Name); + if (string.IsNullOrEmpty(type.Name) || type.Name[0] == '!' || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) + writer.Write(((GenericParameter)type).Position.ToString()); + else + writer.Write(Escape(type.Name)); } else if (type is ByReferenceType) { - ((ByReferenceType)type).ElementType.WriteTo(writer, onlyName, shortName); + ((ByReferenceType)type).ElementType.WriteTo(writer, syntax); writer.Write('&'); } else if (type is PointerType) { - ((PointerType)type).ElementType.WriteTo(writer, onlyName, shortName); + ((PointerType)type).ElementType.WriteTo(writer, syntax); writer.Write('*'); } else if (type is GenericInstanceType) { - type.GetElementType().WriteTo(writer, onlyName, shortName); + type.GetElementType().WriteTo(writer, syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature); writer.Write('<'); var arguments = ((GenericInstanceType)type).GenericArguments; for (int i = 0; i < arguments.Count; i++) { if (i > 0) writer.Write(", "); - arguments[i].WriteTo(writer, onlyName, shortName); + arguments[i].WriteTo(writer, syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature); } writer.Write('>'); } else if (type is OptionalModifierType) { writer.Write("modopt("); - ((OptionalModifierType)type).ModifierType.WriteTo(writer, true, shortName); + ((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write(") "); - ((OptionalModifierType)type).ElementType.WriteTo(writer, onlyName, shortName); + ((OptionalModifierType)type).ElementType.WriteTo(writer, syntax); } else if (type is RequiredModifierType) { writer.Write("modreq("); - ((RequiredModifierType)type).ModifierType.WriteTo(writer, true, shortName); + ((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write(") "); - ((RequiredModifierType)type).ElementType.WriteTo(writer, onlyName, shortName); + ((RequiredModifierType)type).ElementType.WriteTo(writer, syntax); } else { string name = PrimitiveTypeName(type); - if (name != null) { + if (syntax == ILNameSyntax.ShortTypeName) { + writer.WriteReference(Escape(type.Name), type); + } else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) { writer.Write(name); } else { - if (!onlyName) + if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) writer.Write(type.IsValueType ? "valuetype " : "class "); if (type.DeclaringType != null) { - type.DeclaringType.WriteTo(writer, true, shortName); + type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write('/'); writer.WriteReference(Escape(type.Name), type); } else { - if (!type.IsDefinition && type.Scope != null && !shortName && !(type is TypeSpecification)) + if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification)) writer.Write("[{0}]", Escape(type.Scope.Name)); - writer.WriteReference(shortName ? type.Name : type.FullName, type); + writer.WriteReference(Escape(type.FullName), type); } } } @@ -196,7 +248,19 @@ namespace ICSharpCode.Decompiler.Disassembler VariableReference variableRef = operand as VariableReference; if (variableRef != null) { - writer.WriteReference(variableRef.Index.ToString(), variableRef); + if (string.IsNullOrEmpty(variableRef.Name)) + writer.WriteReference(variableRef.Index.ToString(), variableRef); + else + writer.WriteReference(Escape(variableRef.Name), variableRef); + return; + } + + ParameterReference paramRef = operand as ParameterReference; + if (paramRef != null) { + if (string.IsNullOrEmpty(paramRef.Name)) + writer.WriteReference(paramRef.Index.ToString(), paramRef); + else + writer.WriteReference(Escape(paramRef.Name), paramRef); return; } @@ -208,7 +272,7 @@ namespace ICSharpCode.Decompiler.Disassembler TypeReference typeRef = operand as TypeReference; if (typeRef != null) { - typeRef.WriteTo(writer); + typeRef.WriteTo(writer, ILNameSyntax.TypeName); return; } @@ -224,6 +288,41 @@ namespace ICSharpCode.Decompiler.Disassembler return; } + if (operand is float) { + float val = (float)operand; + if (val == 0) { + writer.Write("0.0"); + } else if (float.IsInfinity(val) || float.IsNaN(val)) { + byte[] data = BitConverter.GetBytes(val); + writer.Write('('); + for (int i = 0; i < data.Length; i++) { + if (i > 0) + writer.Write(' '); + writer.Write(data[i].ToString("X2")); + } + writer.Write(')'); + } else { + writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); + } + return; + } else if (operand is double) { + double val = (double)operand; + if (val == 0) { + writer.Write("0.0"); + } else if (double.IsInfinity(val) || double.IsNaN(val)) { + byte[] data = BitConverter.GetBytes(val); + writer.Write('('); + for (int i = 0; i < data.Length; i++) { + if (i > 0) + writer.Write(' '); + writer.Write(data[i].ToString("X2")); + } + writer.Write(')'); + } else { + writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); + } + return; + } s = ToInvariantCultureString(operand); writer.Write(s); } @@ -261,6 +360,8 @@ namespace ICSharpCode.Decompiler.Disassembler return "char"; case "System.Object": return "object"; + case "System.IntPtr": + return "native int"; default: return null; } diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index ef2f8a980..c446516b1 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -69,8 +69,12 @@ namespace ICSharpCode.Decompiler.Disassembler foreach (var v in method.Body.Variables) { output.WriteDefinition("[" + v.Index + "] ", v); v.VariableType.WriteTo(output); - output.Write(' '); - output.Write(DisassemblerHelpers.Escape(v.Name)); + if (!string.IsNullOrEmpty(v.Name)) { + output.Write(' '); + output.Write(DisassemblerHelpers.Escape(v.Name)); + } + if (v.Index + 1 < method.Body.Variables.Count) + output.Write(','); output.WriteLine(); } output.Unindent(); @@ -85,20 +89,24 @@ namespace ICSharpCode.Decompiler.Disassembler foreach (var inst in method.Body.Instructions) { inst.WriteTo(output); - // add IL code mappings - used in debugger - methodMapping.MemberCodeMappings.Add( - new SourceCodeMapping() { - SourceCodeLine = output.CurrentLine, - ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset }, - MemberMapping = methodMapping - }); + if (methodMapping != null) { + // add IL code mappings - used in debugger + methodMapping.MemberCodeMappings.Add( + new SourceCodeMapping() { + SourceCodeLine = output.CurrentLine, + ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset }, + MemberMapping = methodMapping + }); + } output.WriteLine(); } - output.WriteLine(); - foreach (var eh in method.Body.ExceptionHandlers) { - eh.WriteTo(output); + if (method.Body.HasExceptionHandlers) { output.WriteLine(); + foreach (var eh in method.Body.ExceptionHandlers) { + eh.WriteTo(output); + output.WriteLine(); + } } } } @@ -116,7 +124,8 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); break; case ILStructureType.Try: - output.WriteLine(".try {"); + output.WriteLine(".try"); + output.WriteLine("{"); break; case ILStructureType.Handler: switch (s.ExceptionHandler.HandlerType) { @@ -125,22 +134,24 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write("catch"); if (s.ExceptionHandler.CatchType != null) { output.Write(' '); - s.ExceptionHandler.CatchType.WriteTo(output); + s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName); } - output.WriteLine(" {"); + output.WriteLine(); break; case Mono.Cecil.Cil.ExceptionHandlerType.Finally: - output.WriteLine("finally {"); + output.WriteLine("finally"); break; case Mono.Cecil.Cil.ExceptionHandlerType.Fault: - output.WriteLine("fault {"); + output.WriteLine("fault"); break; default: throw new NotSupportedException(); } + output.WriteLine("{"); break; case ILStructureType.Filter: - output.WriteLine("filter {"); + output.WriteLine("filter"); + output.WriteLine("{"); break; default: throw new NotSupportedException(); @@ -150,6 +161,7 @@ namespace ICSharpCode.Decompiler.Disassembler void WriteStructureBody(ILStructure s, ref Instruction inst, MemberMapping currentMethodMapping, int codeSize) { + bool prevInstructionWasBranch = false; int childIndex = 0; while (inst != null && inst.Offset < s.EndOffset) { int offset = inst.Offset; @@ -158,7 +170,12 @@ namespace ICSharpCode.Decompiler.Disassembler WriteStructureHeader(child); WriteStructureBody(child, ref inst, currentMethodMapping, codeSize); WriteStructureFooter(child); + prevInstructionWasBranch = false; } else { + if (prevInstructionWasBranch) { + output.WriteLine(); // put empty line after branch instructions + } + inst.WriteTo(output); // add IL code mappings - used in debugger @@ -172,6 +189,12 @@ namespace ICSharpCode.Decompiler.Disassembler } output.WriteLine(); + + prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch + || inst.OpCode.FlowControl == FlowControl.Cond_Branch + || inst.OpCode.FlowControl == FlowControl.Return + || inst.OpCode.FlowControl == FlowControl.Throw; + inst = inst.Next; } } diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index bff343db7..3a1b48f6a 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -32,7 +32,6 @@ namespace ICSharpCode.Decompiler.Disassembler { ITextOutput output; CancellationToken cancellationToken; - bool detectControlStructure; bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) MethodBodyDisassembler methodBodyDisassembler; @@ -42,23 +41,22 @@ namespace ICSharpCode.Decompiler.Disassembler throw new ArgumentNullException("output"); this.output = output; this.cancellationToken = cancellationToken; - this.detectControlStructure = detectControlStructure; this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); } #region Disassemble Method EnumNameCollection methodAttributeFlags = new EnumNameCollection() { - { MethodAttributes.Static, "static" }, { MethodAttributes.Final, "final" }, - { MethodAttributes.Virtual, "virtual" }, { MethodAttributes.HideBySig, "hidebysig" }, - { MethodAttributes.Abstract, "abstract" }, { MethodAttributes.SpecialName, "specialname" }, { MethodAttributes.PInvokeImpl, "pinvokeimpl" }, { MethodAttributes.UnmanagedExport, "export" }, { MethodAttributes.RTSpecialName, "rtspecialname" }, { MethodAttributes.RequireSecObject, "requiresecobj" }, - { MethodAttributes.NewSlot, "newslot" } + { MethodAttributes.NewSlot, "newslot" }, + { MethodAttributes.Virtual, "virtual" }, + { MethodAttributes.Abstract, "abstract" }, + { MethodAttributes.Static, "static" } }; EnumNameCollection methodVisibility = new EnumNameCollection() { @@ -156,7 +154,7 @@ namespace ICSharpCode.Decompiler.Disassembler methodBodyDisassembler.Disassemble(method.Body, methodMapping); } - CloseBlock("End of method " + method.DeclaringType.Name + "." + method.Name); + CloseBlock("end of method " + method.DeclaringType.Name + "::" + method.Name); } else { output.WriteLine(); } @@ -234,12 +232,11 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(" = "); DisassemblerHelpers.WriteOperand(output, field.Constant); } + output.WriteLine(); if (field.HasCustomAttributes) { - OpenBlock(false); + output.MarkFoldStart(); WriteAttributes(field.CustomAttributes); - CloseBlock(); - } else { - output.WriteLine(); + output.MarkFoldEnd(); } } #endregion @@ -255,9 +252,21 @@ namespace ICSharpCode.Decompiler.Disassembler { output.WriteDefinition(".property ", property); WriteFlags(property.Attributes, propertyAttributes); + if (property.HasThis) + output.Write("instance "); property.PropertyType.WriteTo(output); output.Write(' '); output.Write(DisassemblerHelpers.Escape(property.Name)); + + output.Write("("); + if (property.HasParameters) { + output.WriteLine(); + output.Indent(); + WriteParameters(property.Parameters); + output.Unindent(); + } + output.Write(")"); + OpenBlock(false); WriteAttributes(property.CustomAttributes); WriteNestedMethod(".get", property.GetMethod); @@ -272,16 +281,10 @@ namespace ICSharpCode.Decompiler.Disassembler { if (method == null) return; - if (detectControlStructure) { - output.WriteDefinition(keyword, method); - output.Write(' '); - DisassembleMethodInternal(method); - } else { - output.Write(keyword); - output.Write(' '); - method.WriteTo(output); - output.WriteLine(); - } + output.Write(keyword); + output.Write(' '); + method.WriteTo(output); + output.WriteLine(); } #endregion @@ -361,7 +364,7 @@ namespace ICSharpCode.Decompiler.Disassembler const TypeAttributes masks = TypeAttributes.ClassSemanticMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask; WriteFlags(type.Attributes & ~masks, typeAttributes); - output.Write(DisassemblerHelpers.Escape(type.Name)); + output.Write(DisassemblerHelpers.Escape(type.DeclaringType != null ? type.Name : type.FullName)); WriteTypeParameters(output, type); output.MarkFoldStart(defaultCollapsed: isInType); output.WriteLine(); @@ -369,7 +372,7 @@ namespace ICSharpCode.Decompiler.Disassembler if (type.BaseType != null) { output.Indent(); output.Write("extends "); - type.BaseType.WriteTo(output, true); + type.BaseType.WriteTo(output, ILNameSyntax.TypeName); output.WriteLine(); output.Unindent(); } @@ -382,9 +385,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write("implements "); else output.Write(" "); - if (type.Interfaces[index].Namespace != null) - output.Write("{0}.", type.Interfaces[index].Namespace); - output.Write(type.Interfaces[index].Name); + type.Interfaces[index].WriteTo(output, ILNameSyntax.TypeName); } output.WriteLine(); output.Unindent(); @@ -417,6 +418,14 @@ namespace ICSharpCode.Decompiler.Disassembler } output.WriteLine(); } + if (type.HasMethods) { + output.WriteLine("// Methods"); + foreach (var m in type.Methods) { + cancellationToken.ThrowIfCancellationRequested(); + DisassembleMethod(m); + output.WriteLine(); + } + } if (type.HasProperties) { output.WriteLine("// Properties"); foreach (var prop in type.Properties) { @@ -434,18 +443,7 @@ namespace ICSharpCode.Decompiler.Disassembler } output.WriteLine(); } - if (type.HasMethods) { - output.WriteLine("// Methods"); - var accessorMethods = type.GetAccessorMethods(); - foreach (var m in type.Methods) { - cancellationToken.ThrowIfCancellationRequested(); - if (!(detectControlStructure && accessorMethods.Contains(m))) { - DisassembleMethod(m); - output.WriteLine(); - } - } - } - CloseBlock("End of class " + type.FullName); + CloseBlock("end of class " + (type.DeclaringType != null ? type.Name : type.FullName)); isInType = oldIsInType; } @@ -467,7 +465,7 @@ namespace ICSharpCode.Decompiler.Disassembler for (int j = 0; j < gp.Constraints.Count; j++) { if (j > 0) output.Write(", "); - gp.Constraints[j].WriteTo(output, true); + gp.Constraints[j].WriteTo(output, ILNameSyntax.TypeName); } output.Write(") "); } @@ -616,9 +614,11 @@ namespace ICSharpCode.Decompiler.Disassembler { output.Write(".assembly " + DisassemblerHelpers.Escape(asm.Name.Name)); OpenBlock(false); - Version v = asm.Name.Version; - if (v != null) { - output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision); + WriteAttributes(asm.CustomAttributes); + if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) { + output.Write(".publickey = "); + WriteBlob(asm.Name.PublicKey); + output.WriteLine(); } if (asm.Name.HashAlgorithm != AssemblyHashAlgorithm.None) { output.Write(".hash algorithm 0x{0:x8}", (int)asm.Name.HashAlgorithm); @@ -626,15 +626,51 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(" // SHA1"); output.WriteLine(); } - if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) { - output.Write(".publickey = "); - WriteBlob(asm.Name.PublicKey); - output.WriteLine(); + Version v = asm.Name.Version; + if (v != null) { + output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision); } - WriteAttributes(asm.CustomAttributes); CloseBlock(); } + public void WriteAssemblyReferences(ModuleDefinition module) + { + foreach (var mref in module.ModuleReferences) { + output.WriteLine(".module extern {0}", DisassemblerHelpers.Escape(mref.Name)); + } + foreach (var aref in module.AssemblyReferences) { + output.Write(".assembly extern {0}", DisassemblerHelpers.Escape(aref.Name)); + OpenBlock(false); + if (aref.PublicKeyToken != null) { + output.Write(".publickeytoken = "); + WriteBlob(aref.PublicKeyToken); + output.WriteLine(); + } + if (aref.Version != null) { + output.WriteLine(".ver {0}:{1}:{2}:{3}", aref.Version.Major, aref.Version.Minor, aref.Version.Build, aref.Version.Revision); + } + CloseBlock(); + } + } + + public void WriteModuleHeader(ModuleDefinition module) + { + output.WriteLine(".module {0}", module.Name); + output.WriteLine("// MVID: {0}", module.Mvid.ToString("B").ToUpperInvariant()); + // TODO: imagebase, file alignment, stackreserve, subsystem + output.WriteLine(".corflags 0x{0:x} // {1}", module.Attributes, module.Attributes.ToString()); + + WriteAttributes(module.CustomAttributes); + } + + public void WriteModuleContents(ModuleDefinition module) + { + foreach (TypeDefinition td in module.Types) { + DisassembleType(td); + output.WriteLine(); + } + } + /// public Tuple> CodeMappings { get; diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 1d366c06c..48312b080 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -378,10 +378,10 @@ namespace ICSharpCode.Decompiler.ILAst output.Write(((ILVariable)Operand).Name); if (this.InferredType != null) { output.Write(':'); - this.InferredType.WriteTo(output, true, true); + this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName); if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { output.Write("[exp:"); - this.ExpectedType.WriteTo(output, true, true); + this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write(']'); } } @@ -399,15 +399,15 @@ namespace ICSharpCode.Decompiler.ILAst output.Write(Code.GetName()); if (this.InferredType != null) { output.Write(':'); - this.InferredType.WriteTo(output, true, true); + this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName); if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { output.Write("[exp:"); - this.ExpectedType.WriteTo(output, true, true); + this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write(']'); } } else if (this.ExpectedType != null) { output.Write("[exp:"); - this.ExpectedType.WriteTo(output, true, true); + this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write(']'); } output.Write('('); @@ -425,13 +425,13 @@ namespace ICSharpCode.Decompiler.ILAst } else if (Operand is MethodReference) { MethodReference method = (MethodReference)Operand; if (method.DeclaringType != null) { - method.DeclaringType.WriteTo(output, true, true); + method.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write("::"); } output.WriteReference(method.Name, method); } else if (Operand is FieldReference) { FieldReference field = (FieldReference)Operand; - field.DeclaringType.WriteTo(output, true, true); + field.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write("::"); output.WriteReference(field.Name, field); } else { diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/ILAstLanguage.cs index 84348a4de..a462c2f21 100644 --- a/ILSpy/ILAstLanguage.cs +++ b/ILSpy/ILAstLanguage.cs @@ -68,7 +68,7 @@ namespace ICSharpCode.ILSpy output.Write(" : "); if (v.IsPinned) output.Write("pinned "); - v.Type.WriteTo(output, true, true); + v.Type.WriteTo(output, ILNameSyntax.ShortTypeName); } output.WriteLine(); } @@ -100,7 +100,7 @@ namespace ICSharpCode.ILSpy public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider) { PlainTextOutput output = new PlainTextOutput(); - t.WriteTo(output, true, shortName: !includeNamespace); + t.WriteTo(output, includeNamespace ? ILNameSyntax.TypeName : ILNameSyntax.ShortTypeName); return output.ToString(); } } diff --git a/ILSpy/ILLanguage.cs b/ILSpy/ILLanguage.cs index e77497fa5..7578f107f 100644 --- a/ILSpy/ILLanguage.cs +++ b/ILSpy/ILLanguage.cs @@ -62,12 +62,38 @@ namespace ICSharpCode.ILSpy public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { - new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleProperty(property); + ReflectionDisassembler rd = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); + rd.DisassembleProperty(property); + if (property.GetMethod != null) { + output.WriteLine(); + rd.DisassembleMethod(property.GetMethod); + } + if (property.SetMethod != null) { + output.WriteLine(); + rd.DisassembleMethod(property.SetMethod); + } + foreach (var m in property.OtherMethods) { + output.WriteLine(); + rd.DisassembleMethod(m); + } } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { - new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleEvent(ev); + ReflectionDisassembler rd = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); + rd.DisassembleEvent(ev); + if (ev.AddMethod != null) { + output.WriteLine(); + rd.DisassembleMethod(ev.AddMethod); + } + if (ev.RemoveMethod != null) { + output.WriteLine(); + rd.DisassembleMethod(ev.RemoveMethod); + } + foreach (var m in ev.OtherMethods) { + output.WriteLine(); + rd.DisassembleMethod(m); + } } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) @@ -85,13 +111,23 @@ namespace ICSharpCode.ILSpy output.WriteLine("// " + assembly.FileName); output.WriteLine(); - new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).WriteAssemblyHeader(assembly.AssemblyDefinition); + ReflectionDisassembler rd = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); + if (options.FullDecompilation) + rd.WriteAssemblyReferences(assembly.AssemblyDefinition.MainModule); + rd.WriteAssemblyHeader(assembly.AssemblyDefinition); + output.WriteLine(); + rd.WriteModuleHeader(assembly.AssemblyDefinition.MainModule); + if (options.FullDecompilation) { + output.WriteLine(); + output.WriteLine(); + rd.WriteModuleContents(assembly.AssemblyDefinition.MainModule); + } } public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider) { PlainTextOutput output = new PlainTextOutput(); - t.WriteTo(output, true, shortName: !includeNamespace); + t.WriteTo(output, includeNamespace ? ILNameSyntax.TypeName : ILNameSyntax.ShortTypeName); return output.ToString(); } } From ed08c83289ed63ed0a3322a6a8ae143ad1b1f749 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 17:21:59 +0200 Subject: [PATCH 43/59] Make ConvertChar and ConvertString public. --- .../CSharp/OutputVisitor/OutputVisitor.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index ee76d82ee..da524257d 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -941,7 +941,11 @@ namespace ICSharpCode.NRefactory.CSharp return ConvertChar(ch); } - static string ConvertChar(char ch) + /// + /// Gets the escape sequence for the specified character. + /// + /// This method does not convert ' or ". + public static string ConvertChar(char ch) { switch (ch) { case '\\': @@ -973,7 +977,10 @@ namespace ICSharpCode.NRefactory.CSharp } } - static string ConvertString(string str) + /// + /// Converts special characters to escape sequences within the given string. + /// + public static string ConvertString(string str) { StringBuilder sb = new StringBuilder(); foreach (char ch in str) { From dd4d090468edf4e2e1c0df27534fc12a9aa9db58 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 17:41:51 +0200 Subject: [PATCH 44/59] Lots of improvements/fixes for the disassembler. --- .../Disassembler/DisassemblerHelpers.cs | 99 +++-- .../Disassembler/ILStructure.cs | 6 +- .../Disassembler/MethodBodyDisassembler.cs | 33 +- .../Disassembler/ReflectionDisassembler.cs | 365 +++++++++++++++--- ILSpy/TextView/DecompilerTextView.cs | 4 +- 5 files changed, 423 insertions(+), 84 deletions(-) diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index 672cf252c..555eae487 100644 --- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; @@ -77,8 +78,14 @@ namespace ICSharpCode.Decompiler.Disassembler writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction); writer.Write(": "); writer.WriteReference(instruction.OpCode.Name, instruction.OpCode); - if(null != instruction.Operand) { + if (instruction.Operand != null) { writer.Write(' '); + if (instruction.OpCode == OpCodes.Ldtoken) { + if (instruction.Operand is MethodReference) + writer.Write("method "); + else if (instruction.Operand is FieldReference) + writer.Write("field "); + } WriteOperand(writer, instruction.Operand); } } @@ -153,22 +160,62 @@ namespace ICSharpCode.Decompiler.Disassembler return true; } + static readonly HashSet ilKeywords = BuildKeywordList( + "abstract", "algorithm", "alignment", "ansi", "any", "arglist", + "array", "as", "assembly", "assert", "at", "auto", "autochar", "beforefieldinit", + "blob", "blob_object", "bool", "brnull", "brnull.s", "brzero", "brzero.s", "bstr", + "bytearray", "byvalstr", "callmostderived", "carray", "catch", "cdecl", "cf", + "char", "cil", "class", "clsid", "const", "currency", "custom", "date", "decimal", + "default", "demand", "deny", "endmac", "enum", "error", "explicit", "extends", "extern", + "false", "famandassem", "family", "famorassem", "fastcall", "fault", "field", "filetime", + "filter", "final", "finally", "fixed", "float", "float32", "float64", "forwardref", + "fromunmanaged", "handler", "hidebysig", "hresult", "idispatch", "il", "illegal", + "implements", "implicitcom", "implicitres", "import", "in", "inheritcheck", "init", + "initonly", "instance", "int", "int16", "int32", "int64", "int8", "interface", "internalcall", + "iunknown", "lasterr", "lcid", "linkcheck", "literal", "localloc", "lpstr", "lpstruct", "lptstr", + "lpvoid", "lpwstr", "managed", "marshal", "method", "modopt", "modreq", "native", "nested", + "newslot", "noappdomain", "noinlining", "nomachine", "nomangle", "nometadata", "noncasdemand", + "noncasinheritance", "noncaslinkdemand", "noprocess", "not", "not_in_gc_heap", "notremotable", + "notserialized", "null", "nullref", "object", "objectref", "opt", "optil", "out", + "permitonly", "pinned", "pinvokeimpl", "prefix1", "prefix2", "prefix3", "prefix4", "prefix5", "prefix6", + "prefix7", "prefixref", "prejitdeny", "prejitgrant", "preservesig", "private", "privatescope", "protected", + "public", "record", "refany", "reqmin", "reqopt", "reqrefuse", "reqsecobj", "request", "retval", + "rtspecialname", "runtime", "safearray", "sealed", "sequential", "serializable", "special", "specialname", + "static", "stdcall", "storage", "stored_object", "stream", "streamed_object", "string", "struct", + "synchronized", "syschar", "sysstring", "tbstr", "thiscall", "tls", "to", "true", "typedref", + "unicode", "unmanaged", "unmanagedexp", "unsigned", "unused", "userdefined", "value", "valuetype", + "vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper", + + // These are not listed as keywords in spec, but ILAsm treats them as such + "property", "type", "flags", "callconv" + ); + + static HashSet BuildKeywordList(params string[] keywords) + { + HashSet s = new HashSet(keywords); + foreach (var field in typeof(OpCodes).GetFields()) { + s.Add(((OpCode)field.GetValue(null)).Name); + } + return s; + } + public static string Escape(string identifier) { - if (IsValidIdentifier(identifier) && identifier != "value") + if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) return identifier; else - return "'" + identifier + "'"; + return "'" + NRefactory.CSharp.OutputVisitor.ConvertString(identifier).Replace("'", "\\'") + "'"; } public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature) { + ILNameSyntax syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; if (type is PinnedType) { - writer.Write("pinned "); - ((PinnedType)type).ElementType.WriteTo(writer, syntax); + ((PinnedType)type).ElementType.WriteTo(writer, syntaxForElementTypes); + writer.Write(" pinned"); } else if (type is ArrayType) { ArrayType at = (ArrayType)type; - at.ElementType.WriteTo(writer, syntax); + at.ElementType.WriteTo(writer, syntaxForElementTypes); writer.Write('['); writer.Write(string.Join(", ", at.Dimensions)); writer.Write(']'); @@ -181,33 +228,33 @@ namespace ICSharpCode.Decompiler.Disassembler else writer.Write(Escape(type.Name)); } else if (type is ByReferenceType) { - ((ByReferenceType)type).ElementType.WriteTo(writer, syntax); + ((ByReferenceType)type).ElementType.WriteTo(writer, syntaxForElementTypes); writer.Write('&'); } else if (type is PointerType) { - ((PointerType)type).ElementType.WriteTo(writer, syntax); + ((PointerType)type).ElementType.WriteTo(writer, syntaxForElementTypes); writer.Write('*'); } else if (type is GenericInstanceType) { - type.GetElementType().WriteTo(writer, syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature); + type.GetElementType().WriteTo(writer, syntaxForElementTypes); writer.Write('<'); var arguments = ((GenericInstanceType)type).GenericArguments; for (int i = 0; i < arguments.Count; i++) { if (i > 0) writer.Write(", "); - arguments[i].WriteTo(writer, syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature); + arguments[i].WriteTo(writer, syntaxForElementTypes); } writer.Write('>'); } else if (type is OptionalModifierType) { - writer.Write("modopt("); + ((OptionalModifierType)type).ElementType.WriteTo(writer, syntax); + writer.Write(" modopt("); ((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write(") "); - ((OptionalModifierType)type).ElementType.WriteTo(writer, syntax); } else if (type is RequiredModifierType) { - writer.Write("modreq("); + ((RequiredModifierType)type).ElementType.WriteTo(writer, syntax); + writer.Write(" modreq("); ((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write(") "); - ((RequiredModifierType)type).ElementType.WriteTo(writer, syntax); } else { - string name = PrimitiveTypeName(type); + string name = PrimitiveTypeName(type.FullName); if (syntax == ILNameSyntax.ShortTypeName) { writer.WriteReference(Escape(type.Name), type); } else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) { @@ -284,11 +331,10 @@ namespace ICSharpCode.Decompiler.Disassembler string s = operand as string; if (s != null) { - writer.Write("\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""); - return; - } - - if (operand is float) { + writer.Write("\"" + NRefactory.CSharp.OutputVisitor.ConvertString(s) + "\""); + } else if (operand is char) { + writer.Write(((int)(char)operand).ToString()); + } else if (operand is float) { float val = (float)operand; if (val == 0) { writer.Write("0.0"); @@ -304,7 +350,6 @@ namespace ICSharpCode.Decompiler.Disassembler } else { writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); } - return; } else if (operand is double) { double val = (double)operand; if (val == 0) { @@ -321,15 +366,17 @@ namespace ICSharpCode.Decompiler.Disassembler } else { writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); } - return; + } else if (operand is bool) { + writer.Write((bool)operand ? "true" : "false"); + } else { + s = ToInvariantCultureString(operand); + writer.Write(s); } - s = ToInvariantCultureString(operand); - writer.Write(s); } - public static string PrimitiveTypeName(this TypeReference type) + public static string PrimitiveTypeName(string fullName) { - switch (type.FullName) { + switch (fullName) { case "System.SByte": return "int8"; case "System.Int16": diff --git a/ICSharpCode.Decompiler/Disassembler/ILStructure.cs b/ICSharpCode.Decompiler/Disassembler/ILStructure.cs index 5fc29a9bd..4c681e3c1 100644 --- a/ICSharpCode.Decompiler/Disassembler/ILStructure.cs +++ b/ICSharpCode.Decompiler/Disassembler/ILStructure.cs @@ -88,8 +88,10 @@ namespace ICSharpCode.Decompiler.Disassembler : this(ILStructureType.Root, 0, body.CodeSize) { // Build the tree of exception structures: - foreach (ExceptionHandler eh in body.ExceptionHandlers) { - AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh)); + for (int i = 0; i < body.ExceptionHandlers.Count; i++) { + ExceptionHandler eh = body.ExceptionHandlers[i]; + if (!body.ExceptionHandlers.Take(i).Any(oldEh => oldEh.TryStart == eh.TryStart && oldEh.TryEnd == eh.TryEnd)) + AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh)); if (eh.HandlerType == ExceptionHandlerType.Filter) AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh)); AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd == null ? body.CodeSize : eh.HandlerEnd.Offset, eh)); diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index c446516b1..2fcde2335 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -52,9 +52,6 @@ namespace ICSharpCode.Decompiler.Disassembler // start writing IL code MethodDefinition method = body.Method; output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA); - if (method.HasOverrides) - foreach (var methodOverride in method.Overrides) - output.WriteLine(".override {0}::{1}", methodOverride.DeclaringType.FullName, methodOverride.Name); output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize); output.WriteLine(".maxstack {0}", body.MaxStackSize); if (method.DeclaringType.Module.Assembly.EntryPoint == method) @@ -84,7 +81,8 @@ namespace ICSharpCode.Decompiler.Disassembler if (detectControlStructure && body.Instructions.Count > 0) { Instruction inst = body.Instructions[0]; - WriteStructureBody(new ILStructure(body), ref inst, methodMapping, method.Body.CodeSize); + HashSet branchTargets = GetBranchTargets(body.Instructions); + WriteStructureBody(new ILStructure(body), branchTargets, ref inst, methodMapping, method.Body.CodeSize); } else { foreach (var inst in method.Body.Instructions) { inst.WriteTo(output); @@ -111,6 +109,21 @@ namespace ICSharpCode.Decompiler.Disassembler } } + HashSet GetBranchTargets(IEnumerable instructions) + { + HashSet branchTargets = new HashSet(); + foreach (var inst in instructions) { + Instruction target = inst.Operand as Instruction; + if (target != null) + branchTargets.Add(target.Offset); + Instruction[] targets = inst.Operand as Instruction[]; + if (targets != null) + foreach (Instruction t in targets) + branchTargets.Add(t.Offset); + } + return branchTargets; + } + void WriteStructureHeader(ILStructure s) { switch (s.Type) { @@ -159,8 +172,9 @@ namespace ICSharpCode.Decompiler.Disassembler output.Indent(); } - void WriteStructureBody(ILStructure s, ref Instruction inst, MemberMapping currentMethodMapping, int codeSize) + void WriteStructureBody(ILStructure s, HashSet branchTargets, ref Instruction inst, MemberMapping currentMethodMapping, int codeSize) { + bool isFirstInstructionInStructure = true; bool prevInstructionWasBranch = false; int childIndex = 0; while (inst != null && inst.Offset < s.EndOffset) { @@ -168,14 +182,12 @@ namespace ICSharpCode.Decompiler.Disassembler if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { ILStructure child = s.Children[childIndex++]; WriteStructureHeader(child); - WriteStructureBody(child, ref inst, currentMethodMapping, codeSize); + WriteStructureBody(child, branchTargets, ref inst, currentMethodMapping, codeSize); WriteStructureFooter(child); - prevInstructionWasBranch = false; } else { - if (prevInstructionWasBranch) { - output.WriteLine(); // put empty line after branch instructions + if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) { + output.WriteLine(); // put an empty line after branches, and in front of branch targets } - inst.WriteTo(output); // add IL code mappings - used in debugger @@ -197,6 +209,7 @@ namespace ICSharpCode.Decompiler.Disassembler inst = inst.Next; } + isFirstInstructionInStructure = false; } } diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 3a1b48f6a..956848a3e 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -49,14 +49,16 @@ namespace ICSharpCode.Decompiler.Disassembler { MethodAttributes.Final, "final" }, { MethodAttributes.HideBySig, "hidebysig" }, { MethodAttributes.SpecialName, "specialname" }, - { MethodAttributes.PInvokeImpl, "pinvokeimpl" }, + { MethodAttributes.PInvokeImpl, null }, // handled separately { MethodAttributes.UnmanagedExport, "export" }, { MethodAttributes.RTSpecialName, "rtspecialname" }, - { MethodAttributes.RequireSecObject, "requiresecobj" }, + { MethodAttributes.RequireSecObject, "reqsecobj" }, { MethodAttributes.NewSlot, "newslot" }, - { MethodAttributes.Virtual, "virtual" }, + { MethodAttributes.CheckAccessOnOverride, "strict" }, { MethodAttributes.Abstract, "abstract" }, - { MethodAttributes.Static, "static" } + { MethodAttributes.Virtual, "virtual" }, + { MethodAttributes.Static, "static" }, + { MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm }; EnumNameCollection methodVisibility = new EnumNameCollection() { @@ -74,7 +76,7 @@ namespace ICSharpCode.Decompiler.Disassembler { MethodCallingConvention.ThisCall, "unmanaged thiscall" }, { MethodCallingConvention.FastCall, "unmanaged fastcall" }, { MethodCallingConvention.VarArg, "vararg" }, - { MethodCallingConvention.Generic, "generic" }, + { MethodCallingConvention.Generic, null }, }; EnumNameCollection methodCodeType = new EnumNameCollection() { @@ -88,6 +90,8 @@ namespace ICSharpCode.Decompiler.Disassembler { MethodImplAttributes.Synchronized, "synchronized" }, { MethodImplAttributes.NoInlining, "noinlining" }, { MethodImplAttributes.NoOptimization, "nooptimization" }, + { MethodImplAttributes.PreserveSig, "preservesig" }, + { MethodImplAttributes.InternalCall, "internalcall" }, }; public void DisassembleMethod(MethodDefinition method) @@ -107,6 +111,44 @@ namespace ICSharpCode.Decompiler.Disassembler WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility); WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags); + if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) { + output.Write("pinvokeimpl"); + if (method.HasPInvokeInfo) { + PInvokeInfo info = method.PInvokeInfo; + output.Write("(\"" + NRefactory.CSharp.OutputVisitor.ConvertString(info.Module.Name) + "\""); + + if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name) + output.Write(" as \"" + NRefactory.CSharp.OutputVisitor.ConvertString(info.EntryPoint) + "\""); + + if (info.IsNoMangle) + output.Write(" nomangle"); + + if (info.IsCharSetAnsi) + output.Write(" ansi"); + else if (info.IsCharSetAuto) + output.Write(" autochar"); + else if (info.IsCharSetUnicode) + output.Write(" unicode"); + + if (info.SupportsLastError) + output.Write(" lasterr"); + + if (info.IsCallConvCdecl) + output.Write(" cdecl"); + else if (info.IsCallConvFastcall) + output.Write(" fastcall"); + else if (info.IsCallConvStdCall) + output.Write(" stdcall"); + else if (info.IsCallConvThiscall) + output.Write(" thiscall"); + else if (info.IsCallConvWinapi) + output.Write(" winapi"); + + output.Write(')'); + } + output.Write(' '); + } + output.WriteLine(); output.Indent(); @@ -120,6 +162,9 @@ namespace ICSharpCode.Decompiler.Disassembler //return type method.ReturnType.WriteTo(output); output.Write(' '); + if (method.MethodReturnType.HasMarshalInfo) { + WriteMarshalInfo(method.MethodReturnType.MarshalInfo); + } output.Write(DisassemblerHelpers.Escape(method.Name)); WriteTypeParameters(output, method); @@ -141,25 +186,228 @@ namespace ICSharpCode.Decompiler.Disassembler WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl); output.Unindent(); - if (method.HasBody || method.HasCustomAttributes || method.Parameters.Any(HasParameterAttributes)) { - OpenBlock(defaultCollapsed: isInType); - WriteAttributes(method.CustomAttributes); - foreach (var p in method.Parameters) { - WriteParameterAttributes(p); - } - - if (method.HasBody) { - // create IL code mappings - used in debugger - MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings); - methodBodyDisassembler.Disassemble(method.Body, methodMapping); + OpenBlock(defaultCollapsed: isInType); + WriteAttributes(method.CustomAttributes); + if (method.HasOverrides) { + foreach (var methodOverride in method.Overrides) { + output.Write(".override method "); + methodOverride.WriteTo(output); + output.WriteLine(); } - - CloseBlock("end of method " + method.DeclaringType.Name + "::" + method.Name); - } else { - output.WriteLine(); } + foreach (var p in method.Parameters) { + WriteParameterAttributes(p); + } + + if (method.HasBody) { + // create IL code mappings - used in debugger + MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings); + methodBodyDisassembler.Disassemble(method.Body, methodMapping); + } + + CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name)); } + #region WriteMarshalInfo + void WriteMarshalInfo(MarshalInfo marshalInfo) + { + output.Write("marshal("); + WriteNativeType(marshalInfo.NativeType, marshalInfo); + output.Write(") "); + } + + void WriteNativeType(NativeType nativeType, MarshalInfo marshalInfo = null) + { + switch (nativeType) { + case NativeType.None: + break; + case NativeType.Boolean: + output.Write("bool"); + break; + case NativeType.I1: + output.Write("int8"); + break; + case NativeType.U1: + output.Write("unsigned int8"); + break; + case NativeType.I2: + output.Write("int16"); + break; + case NativeType.U2: + output.Write("unsigned int16"); + break; + case NativeType.I4: + output.Write("int32"); + break; + case NativeType.U4: + output.Write("unsigned int32"); + break; + case NativeType.I8: + output.Write("int64"); + break; + case NativeType.U8: + output.Write("unsigned int64"); + break; + case NativeType.R4: + output.Write("float32"); + break; + case NativeType.R8: + output.Write("float64"); + break; + case NativeType.LPStr: + output.Write("lpstr"); + break; + case NativeType.Int: + output.Write("int"); + break; + case NativeType.UInt: + output.Write("unsigned int"); + break; + case NativeType.Func: + goto default; // ?? + case NativeType.Array: + ArrayMarshalInfo ami = (ArrayMarshalInfo)marshalInfo; + if (ami == null) + goto default; + WriteNativeType(ami.ElementType); + output.Write("[size={0}, sizeParameterIndex={1}, sizeParameterMultiplier={2}]", ami.Size, ami.SizeParameterIndex, ami.SizeParameterMultiplier); + break; + case NativeType.Currency: + output.Write("currency"); + break; + case NativeType.BStr: + output.Write("bstr"); + break; + case NativeType.LPWStr: + output.Write("lpwstr"); + break; + case NativeType.LPTStr: + output.Write("lptstr"); + break; + case NativeType.FixedSysString: + output.Write("fixed sysstring[{0}]", ((FixedSysStringMarshalInfo)marshalInfo).Size); + break; + case NativeType.IUnknown: + output.Write("iunknown"); + break; + case NativeType.IDispatch: + output.Write("idispatch"); + break; + case NativeType.Struct: + output.Write("struct"); + break; + case NativeType.IntF: + output.Write("interface"); + break; + case NativeType.SafeArray: + output.Write("safearray "); + SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo; + if (sami != null) { + switch (sami.ElementType) { + case VariantType.None: + break; + case VariantType.I2: + output.Write("int16"); + break; + case VariantType.I4: + output.Write("int32"); + break; + case VariantType.R4: + output.Write("float32"); + break; + case VariantType.R8: + output.Write("float64"); + break; + case VariantType.CY: + output.Write("currency"); + break; + case VariantType.Date: + output.Write("date"); + break; + case VariantType.BStr: + output.Write("bstr"); + break; + case VariantType.Dispatch: + output.Write("idispatch"); + break; + case VariantType.Error: + output.Write("error"); + break; + case VariantType.Bool: + output.Write("bool"); + break; + case VariantType.Variant: + output.Write("variant"); + break; + case VariantType.Unknown: + output.Write("iunknown"); + break; + case VariantType.Decimal: + output.Write("decimal"); + break; + case VariantType.I1: + output.Write("int8"); + break; + case VariantType.UI1: + output.Write("unsigned int8"); + break; + case VariantType.UI2: + output.Write("unsigned int16"); + break; + case VariantType.UI4: + output.Write("unsigned int32"); + break; + case VariantType.Int: + output.Write("int"); + break; + case VariantType.UInt: + output.Write("unsigned int"); + break; + default: + output.Write(sami.ElementType.ToString()); + break; + } + } + break; + case NativeType.FixedArray: + output.Write("fixed sysstring"); + FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; + if (fami != null) + output.Write("[{0}]", ((FixedArrayMarshalInfo)marshalInfo).Size); + break; + case NativeType.ByValStr: + output.Write("byvalstr"); + break; + case NativeType.ANSIBStr: + output.Write("ansi bstr"); + break; + case NativeType.TBStr: + output.Write("tbstr"); + break; + case NativeType.VariantBool: + output.Write("variant bool"); + break; + case NativeType.ASAny: + output.Write("as any"); + break; + case NativeType.LPStruct: + output.Write("lpstruct"); + break; + case NativeType.CustomMarshaler: + CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; + goto default; // ??? + case NativeType.Error: + output.Write("error"); + break; + case NativeType.Max: + // ??? + default: + output.Write(nativeType.ToString()); + break; + } + } + #endregion + void WriteParameters(Collection parameters) { for (int i = 0; i < parameters.Count; i++) { @@ -188,17 +436,39 @@ namespace ICSharpCode.Decompiler.Disassembler { if (!HasParameterAttributes(p)) return; - output.Write(".param [{0}]", p.Index); + output.Write(".param [{0}]", p.Index + 1); if (p.HasConstant) { output.Write(" = "); - if (p.Constant != null) - DisassemblerHelpers.WriteOperand(output, p.Constant); - else - output.Write("nullref"); + WriteConstant(p.Constant); } output.WriteLine(); WriteAttributes(p.CustomAttributes); } + + void WriteConstant(object constant) + { + if (constant == null) { + output.Write("nullref"); + } else { + string typeName = DisassemblerHelpers.PrimitiveTypeName(constant.GetType().FullName); + if (typeName != null && typeName != "string") { + output.Write(typeName); + output.Write('('); + float? cf = constant as float?; + double? cd = constant as double?; + if (cf.HasValue && (float.IsNaN(cf.Value) || float.IsInfinity(cf.Value))) { + output.Write("0x{0:x8}", BitConverter.ToInt32(BitConverter.GetBytes(cf.Value), 0)); + } else if (cd.HasValue && (double.IsNaN(cd.Value) || double.IsInfinity(cd.Value))) { + output.Write("0x{0:x16}", BitConverter.DoubleToInt64Bits(cd.Value)); + } else { + DisassemblerHelpers.WriteOperand(output, constant); + } + output.Write(')'); + } else { + DisassemblerHelpers.WriteOperand(output, constant); + } + } + } #endregion #region Disassemble Field @@ -224,13 +494,20 @@ namespace ICSharpCode.Decompiler.Disassembler { output.WriteDefinition(".field ", field); WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); - WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | FieldAttributes.HasDefault), fieldAttributes); + const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; + WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes); + if (field.HasMarshalInfo) { + WriteMarshalInfo(field.MarshalInfo); + } field.FieldType.WriteTo(output); output.Write(' '); output.Write(DisassemblerHelpers.Escape(field.Name)); + if ((field.Attributes & FieldAttributes.HasFieldRVA) == FieldAttributes.HasFieldRVA) { + output.Write(" at I_{0:x8}", field.RVA); + } if (field.HasConstant) { output.Write(" = "); - DisassemblerHelpers.WriteOperand(output, field.Constant); + WriteConstant(field.Constant); } output.WriteLine(); if (field.HasCustomAttributes) { @@ -272,7 +549,7 @@ namespace ICSharpCode.Decompiler.Disassembler WriteNestedMethod(".get", property.GetMethod); WriteNestedMethod(".set", property.SetMethod); foreach (var method in property.OtherMethods) { - WriteNestedMethod(".method", method); + WriteNestedMethod(".other", method); } CloseBlock(); } @@ -298,16 +575,16 @@ namespace ICSharpCode.Decompiler.Disassembler { output.WriteDefinition(".event ", ev); WriteFlags(ev.Attributes, eventAttributes); - ev.EventType.WriteTo(output); + ev.EventType.WriteTo(output, ILNameSyntax.TypeName); output.Write(' '); output.Write(DisassemblerHelpers.Escape(ev.Name)); OpenBlock(false); WriteAttributes(ev.CustomAttributes); - WriteNestedMethod(".add", ev.AddMethod); - WriteNestedMethod(".remove", ev.RemoveMethod); - WriteNestedMethod(".invoke", ev.InvokeMethod); + WriteNestedMethod(".addon", ev.AddMethod); + WriteNestedMethod(".removeon", ev.RemoveMethod); + WriteNestedMethod(".fire", ev.InvokeMethod); foreach (var method in ev.OtherMethods) { - WriteNestedMethod(".method", method); + WriteNestedMethod(".other", method); } CloseBlock(); } @@ -426,14 +703,6 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } } - if (type.HasProperties) { - output.WriteLine("// Properties"); - foreach (var prop in type.Properties) { - cancellationToken.ThrowIfCancellationRequested(); - DisassembleProperty(prop); - } - output.WriteLine(); - } if (type.HasEvents) { output.WriteLine("// Events"); foreach (var ev in type.Events) { @@ -443,6 +712,14 @@ namespace ICSharpCode.Decompiler.Disassembler } output.WriteLine(); } + if (type.HasProperties) { + output.WriteLine("// Properties"); + foreach (var prop in type.Properties) { + cancellationToken.ThrowIfCancellationRequested(); + DisassembleProperty(prop); + } + output.WriteLine(); + } CloseBlock("end of class " + (type.DeclaringType != null ? type.Name : type.FullName)); isInType = oldIsInType; } @@ -460,6 +737,9 @@ namespace ICSharpCode.Decompiler.Disassembler } else if (gp.HasNotNullableValueTypeConstraint) { output.Write("valuetype "); } + if (gp.HasDefaultConstructorConstraint) { + output.Write(".ctor "); + } if (gp.HasConstraints) { output.Write('('); for (int j = 0; j < gp.Constraints.Count; j++) { @@ -469,9 +749,6 @@ namespace ICSharpCode.Decompiler.Disassembler } output.Write(") "); } - if (gp.HasDefaultConstructorConstraint) { - output.Write(".ctor "); - } if (gp.IsContravariant) { output.Write('-'); } else if (gp.IsCovariant) { diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index b312abf8a..024994462 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -617,12 +617,12 @@ namespace ICSharpCode.ILSpy.TextView public void SaveFoldingsState(IEnumerable foldings) { ExpandedFoldings = foldings.Where(f => !f.IsFolded).Select(f => Tuple.Create(f.StartOffset, f.EndOffset)).ToList(); - FoldingsChecksum = foldings.Select(f => f.StartOffset * 3 - f.EndOffset).Aggregate((a, b) => a + b); + FoldingsChecksum = unchecked(foldings.Select(f => f.StartOffset * 3 - f.EndOffset).Aggregate((a, b) => a + b)); } internal void RestoreFoldings(List list) { - var checksum = list.Select(f => f.StartOffset * 3 - f.EndOffset).Aggregate((a, b) => a + b); + var checksum = unchecked(list.Select(f => f.StartOffset * 3 - f.EndOffset).Aggregate((a, b) => a + b)); if (FoldingsChecksum == checksum) foreach (var folding in list) folding.DefaultClosed = !ExpandedFoldings.Any(f => f.Item1 == folding.StartOffset && f.Item2 == folding.EndOffset); From 2d42dd5c5740c9fcae7d33a4b8e6cdf0f148b543 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 19:01:32 +0200 Subject: [PATCH 45/59] Fixed several issues related to [MarshalAs] attributes. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 39 ++++++++- .../Disassembler/ReflectionDisassembler.cs | 41 +++++++-- .../ILAst/ILAstOptimizer.cs | 1 - .../Tests/ICSharpCode.Decompiler.Tests.csproj | 1 + ICSharpCode.Decompiler/Tests/PInvoke.cs | 87 +++++++++++++++++++ ICSharpCode.Decompiler/Tests/TestRunner.cs | 6 ++ 6 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 ICSharpCode.Decompiler/Tests/PInvoke.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index f941af8b3..e24ecb8e1 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -801,7 +801,13 @@ namespace ICSharpCode.Decompiler.Ast astProp.Setter.Body = CreateMethodBody(propDef.SetMethod); astProp.Setter.AddAnnotation(propDef.SetMethod); ConvertAttributes(astProp.Setter, propDef.SetMethod); - ConvertCustomAttributes(astProp.Setter, propDef.SetMethod.Parameters.Last(), "param"); + ParameterDefinition lastParam = propDef.SetMethod.Parameters.LastOrDefault(); + if (lastParam != null) { + ConvertCustomAttributes(astProp.Setter, lastParam, "param"); + if (lastParam.HasMarshalInfo) { + astProp.Setter.Attributes.Add(new AttributeSection(ConvertMarshalInfo(lastParam, propDef.Module)) { AttributeTarget = "param" }); + } + } if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask; @@ -1170,6 +1176,37 @@ namespace ICSharpCode.Decompiler.Ast Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module); var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib); attr.Arguments.Add(MakePrimitive((int)marshalInfo.NativeType, unmanagedType)); + + FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; + if (fami != null) { + attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fami.Size)); + if (fami.ElementType != NativeType.None) + attr.AddNamedArgument("ArraySubType", MakePrimitive((int)fami.ElementType, unmanagedType)); + } + SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo; + if (sami != null && sami.ElementType != VariantType.None) { + var varEnum = new TypeReference("System.Runtime.InteropServices", "VarEnum", module, module.TypeSystem.Corlib); + attr.AddNamedArgument("SafeArraySubType", MakePrimitive((int)sami.ElementType, varEnum)); + } + ArrayMarshalInfo ami = marshalInfo as ArrayMarshalInfo; + if (ami != null) { + if (ami.ElementType != NativeType.Max) + attr.AddNamedArgument("ArraySubType", MakePrimitive((int)ami.ElementType, unmanagedType)); + if (ami.Size >= 0) + attr.AddNamedArgument("SizeConst", new PrimitiveExpression(ami.Size)); + if (ami.SizeParameterMultiplier != 0) + attr.AddNamedArgument("SizeParamIndex", new PrimitiveExpression(ami.SizeParameterIndex)); + } + CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; + if (cmi != null) { + attr.AddNamedArgument("MarshalType", new PrimitiveExpression(cmi.ManagedType.FullName)); + if (!string.IsNullOrEmpty(cmi.Cookie)) + attr.AddNamedArgument("MarshalCookie", new PrimitiveExpression(cmi.Cookie)); + } + FixedSysStringMarshalInfo fssmi = marshalInfo as FixedSysStringMarshalInfo; + if (fssmi != null) { + attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fssmi.Size)); + } return attr; } #endregion diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 956848a3e..618910fe3 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -269,8 +269,18 @@ namespace ICSharpCode.Decompiler.Disassembler ArrayMarshalInfo ami = (ArrayMarshalInfo)marshalInfo; if (ami == null) goto default; - WriteNativeType(ami.ElementType); - output.Write("[size={0}, sizeParameterIndex={1}, sizeParameterMultiplier={2}]", ami.Size, ami.SizeParameterIndex, ami.SizeParameterMultiplier); + if (ami.ElementType != NativeType.Max) + WriteNativeType(ami.ElementType); + output.Write('['); + if (ami.SizeParameterMultiplier == 0) { + output.Write(ami.Size.ToString()); + } else { + if (ami.Size >= 0) + output.Write(ami.Size.ToString()); + output.Write(" + "); + output.Write(ami.SizeParameterIndex.ToString()); + } + output.Write(']'); break; case NativeType.Currency: output.Write("currency"); @@ -370,10 +380,15 @@ namespace ICSharpCode.Decompiler.Disassembler } break; case NativeType.FixedArray: - output.Write("fixed sysstring"); + output.Write("fixed array"); FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; - if (fami != null) - output.Write("[{0}]", ((FixedArrayMarshalInfo)marshalInfo).Size); + if (fami != null) { + output.Write("[{0}]", fami.Size); + if (fami.ElementType != NativeType.None) { + output.Write(' '); + WriteNativeType(fami.ElementType); + } + } break; case NativeType.ByValStr: output.Write("byvalstr"); @@ -395,12 +410,19 @@ namespace ICSharpCode.Decompiler.Disassembler break; case NativeType.CustomMarshaler: CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; - goto default; // ??? + if (cmi == null) + goto default; + output.Write("custom(\"{0}\", \"{1}\"", + NRefactory.CSharp.OutputVisitor.ConvertString(cmi.ManagedType.FullName), + NRefactory.CSharp.OutputVisitor.ConvertString(cmi.Cookie)); + if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) { + output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.OutputVisitor.ConvertString(cmi.UnmanagedType)); + } + output.Write(')'); + break; case NativeType.Error: output.Write("error"); break; - case NativeType.Max: - // ??? default: output.Write(nativeType.ToString()); break; @@ -420,6 +442,9 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write("[opt] "); p.ParameterType.WriteTo(output); output.Write(' '); + if (p.HasMarshalInfo) { + WriteMarshalInfo(p.MarshalInfo); + } output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p); if (i < parameters.Count - 1) output.Write(','); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 8a3cc466f..54db1aea7 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -370,7 +370,6 @@ namespace ICSharpCode.Decompiler.ILAst } else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) { // Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'. ILVariable target; - ILExpression ldvirtftnArg; if (expr.Arguments[0].Match(ILCode.Ldloc, out target) && expr.Arguments[1].Code == ILCode.Ldvirtftn && expr.Arguments[1].Arguments.Count == 1 diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index b4906c60f..6023f2aff 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -59,6 +59,7 @@ + diff --git a/ICSharpCode.Decompiler/Tests/PInvoke.cs b/ICSharpCode.Decompiler/Tests/PInvoke.cs new file mode 100644 index 000000000..fe9b9b5bc --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/PInvoke.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Runtime.InteropServices; + +// P/Invoke and marshalling attribute tests +public class PInvoke +{ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 2)] + public struct MarshalAsTest + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public uint[] FixedArray; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.Bool)] + public int[] FixedBoolArray; + + [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] + public string[] SafeBStrArray; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] + public string FixedString; + } + + [StructLayout(LayoutKind.Explicit)] + public struct Rect + { + [FieldOffset(0)] + public int left; + [FieldOffset(4)] + public int top; + [FieldOffset(8)] + public int right; + [FieldOffset(12)] + public int bottom; + } + + public static decimal MarshalAttributesOnPropertyAccessors + { + [return: MarshalAs(UnmanagedType.Currency)] + get + { + return 0m; + } + [param: MarshalAs(UnmanagedType.Currency)] + set + { + } + } + + [DllImport("xyz.dll", CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool Method([MarshalAs(UnmanagedType.LPStr)] string input); + + [DllImport("xyz.dll")] + private static extern void New1(int ElemCnt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] ar); + + [DllImport("xyz.dll")] + private static extern void New2([MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] int[] ar); + + [DllImport("xyz.dll")] + private static extern void New3([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Bool, SizeConst = 64, SizeParamIndex = 1)] int[] ar); + + public void CustomMarshal1([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler")] object o) + { + } + + public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler", MarshalCookie = "Cookie")] object o) + { + } +} diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 998c17370..de06f7d68 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -73,6 +73,12 @@ namespace ICSharpCode.Decompiler.Tests TestFile(@"..\..\Tests\MultidimensionalArray.cs"); } + [Test] + public void PInvoke() + { + TestFile(@"..\..\Tests\PInvoke.cs"); + } + [Test] public void PropertiesAndEvents() { From 32081feb1b8d34148242de4921cae5b47eb740a1 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 20:58:55 +0200 Subject: [PATCH 46/59] Decompile security declarations. Closes #189. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 64 +++++++- .../Disassembler/ReflectionDisassembler.cs | 139 ++++++++++++++++++ 2 files changed, 202 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index e24ecb8e1..7a489d07e 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -126,6 +126,7 @@ namespace ICSharpCode.Decompiler.Ast public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false) { ConvertCustomAttributes(astCompileUnit, assemblyDefinition, "assembly"); + ConvertSecurityAttributes(astCompileUnit, assemblyDefinition, "assembly"); ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, "module"); if (!onlyAssemblyLevel) { @@ -990,6 +991,7 @@ namespace ICSharpCode.Decompiler.Ast void ConvertAttributes(AttributedNode attributedNode, TypeDefinition typeDefinition) { ConvertCustomAttributes(attributedNode, typeDefinition); + ConvertSecurityAttributes(attributedNode, typeDefinition); // Handle the non-custom attributes: #region SerializableAttribute @@ -1040,6 +1042,7 @@ namespace ICSharpCode.Decompiler.Ast void ConvertAttributes(AttributedNode attributedNode, MethodDefinition methodDefinition) { ConvertCustomAttributes(attributedNode, methodDefinition); + ConvertSecurityAttributes(attributedNode, methodDefinition); MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; @@ -1194,7 +1197,7 @@ namespace ICSharpCode.Decompiler.Ast attr.AddNamedArgument("ArraySubType", MakePrimitive((int)ami.ElementType, unmanagedType)); if (ami.Size >= 0) attr.AddNamedArgument("SizeConst", new PrimitiveExpression(ami.Size)); - if (ami.SizeParameterMultiplier != 0) + if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0) attr.AddNamedArgument("SizeParamIndex", new PrimitiveExpression(ami.SizeParameterIndex)); } CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; @@ -1296,6 +1299,65 @@ namespace ICSharpCode.Decompiler.Ast } } + static void ConvertSecurityAttributes(AstNode attributedNode, ISecurityDeclarationProvider secDeclProvider, string attributeTarget = null) + { + if (!secDeclProvider.HasSecurityDeclarations) + return; + var attributes = new List(); + foreach (var secDecl in secDeclProvider.SecurityDeclarations) { + foreach (var secAttribute in secDecl.SecurityAttributes) { + var attribute = new ICSharpCode.NRefactory.CSharp.Attribute(); + attribute.AddAnnotation(secAttribute); + attribute.Type = ConvertType(secAttribute.AttributeType); + attributes.Add(attribute); + + SimpleType st = attribute.Type as SimpleType; + if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) { + st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length); + } + + var module = secAttribute.AttributeType.Module; + var securityActionType = new TypeReference("System.Security.Permissions", "SecurityAction", module, module.TypeSystem.Corlib); + attribute.Arguments.Add(MakePrimitive((int)secDecl.Action, securityActionType)); + + if (secAttribute.HasProperties) { + TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve(); + foreach (var propertyNamedArg in secAttribute.Properties) { + var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null; + var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference); + var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument); + attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue)); + } + } + + if (secAttribute.HasFields) { + TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve(); + foreach (var fieldNamedArg in secAttribute.Fields) { + var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null; + var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference); + var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument); + attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue)); + } + } + } + } + if (attributeTarget == "module" || attributeTarget == "assembly") { + // use separate section for each attribute + foreach (var attribute in attributes) { + var section = new AttributeSection(); + section.AttributeTarget = attributeTarget; + section.Attributes.Add(attribute); + attributedNode.AddChild(section, AttributedNode.AttributeRole); + } + } else if (attributes.Count > 0) { + // use single section for all attributes + var section = new AttributeSection(); + section.AttributeTarget = attributeTarget; + section.Attributes.AddRange(attributes); + attributedNode.AddChild(section, AttributedNode.AttributeRole); + } + } + private static Expression ConvertArgumentValue(CustomAttributeArgument argument) { if (argument.Value is CustomAttributeArgument[]) { diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 618910fe3..be00da59a 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -18,7 +18,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using System.Text; using System.Threading; using Mono.Cecil; using Mono.Collections.Generic; @@ -198,6 +200,7 @@ namespace ICSharpCode.Decompiler.Disassembler foreach (var p in method.Parameters) { WriteParameterAttributes(p); } + WriteSecurityDeclarations(method); if (method.HasBody) { // create IL code mappings - used in debugger @@ -208,6 +211,140 @@ namespace ICSharpCode.Decompiler.Disassembler CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name)); } + #region Write Security Declarations + void WriteSecurityDeclarations(ISecurityDeclarationProvider secDeclProvider) + { + if (!secDeclProvider.HasSecurityDeclarations) + return; + foreach (var secdecl in secDeclProvider.SecurityDeclarations) { + output.Write(".permissionset "); + switch (secdecl.Action) { + case SecurityAction.Request: + output.Write("request"); + break; + case SecurityAction.Demand: + output.Write("demand"); + break; + case SecurityAction.Assert: + output.Write("assert"); + break; + case SecurityAction.Deny: + output.Write("deny"); + break; + case SecurityAction.PermitOnly: + output.Write("permitonly"); + break; + case SecurityAction.LinkDemand: + output.Write("linkcheck"); + break; + case SecurityAction.InheritDemand: + output.Write("inheritcheck"); + break; + case SecurityAction.RequestMinimum: + output.Write("reqmin"); + break; + case SecurityAction.RequestOptional: + output.Write("reqopt"); + break; + case SecurityAction.RequestRefuse: + output.Write("reqrefuse"); + break; + case SecurityAction.PreJitGrant: + output.Write("prejitgrant"); + break; + case SecurityAction.PreJitDeny: + output.Write("prejitdeny"); + break; + case SecurityAction.NonCasDemand: + output.Write("noncasdemand"); + break; + case SecurityAction.NonCasLinkDemand: + output.Write("noncaslinkdemand"); + break; + case SecurityAction.NonCasInheritance: + output.Write("noncasinheritance"); + break; + default: + output.Write(secdecl.Action.ToString()); + break; + } + output.WriteLine(" = {"); + output.Indent(); + for (int i = 0; i < secdecl.SecurityAttributes.Count; i++) { + SecurityAttribute sa = secdecl.SecurityAttributes[i]; + if (sa.AttributeType.Scope == sa.AttributeType.Module) { + output.Write("class "); + output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(sa.AttributeType))); + } else { + sa.AttributeType.WriteTo(output, ILNameSyntax.TypeName); + } + output.Write(" = {"); + if (sa.HasFields || sa.HasProperties) { + output.WriteLine(); + output.Indent(); + + foreach (CustomAttributeNamedArgument na in sa.Fields) { + output.Write("field "); + WriteSecurityDeclarationArgument(na); + output.WriteLine(); + } + + foreach (CustomAttributeNamedArgument na in sa.Properties) { + output.Write("property "); + WriteSecurityDeclarationArgument(na); + output.WriteLine(); + } + + output.Unindent(); + } + output.Write('}'); + + if (i + 1< secdecl.SecurityAttributes.Count) + output.Write(','); + output.WriteLine(); + } + output.Unindent(); + output.WriteLine("}"); + } + } + + void WriteSecurityDeclarationArgument(CustomAttributeNamedArgument na) + { + TypeReference type = na.Argument.Type; + if (type.MetadataType == MetadataType.Class || type.MetadataType == MetadataType.ValueType) { + output.Write("enum "); + if (type.Scope != type.Module) { + output.Write("class "); + output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(type))); + } else { + type.WriteTo(output, ILNameSyntax.TypeName); + } + } else { + type.WriteTo(output); + } + output.Write(' '); + output.Write(DisassemblerHelpers.Escape(na.Name)); + output.Write(" = "); + WriteConstant(na.Argument.Value); + } + + string GetAssemblyQualifiedName(TypeReference type) + { + AssemblyNameReference anr = type.Scope as AssemblyNameReference; + if (anr == null) { + ModuleDefinition md = type.Scope as ModuleDefinition; + if (md != null) { + anr = md.Assembly.Name; + } + } + if (anr != null) { + return type.FullName + ", " + anr.FullName; + } else { + return type.FullName; + } + } + #endregion + #region WriteMarshalInfo void WriteMarshalInfo(MarshalInfo marshalInfo) { @@ -698,6 +835,7 @@ namespace ICSharpCode.Decompiler.Disassembler bool oldIsInType = isInType; isInType = true; WriteAttributes(type.CustomAttributes); + WriteSecurityDeclarations(type); if (type.HasLayoutInfo) { output.WriteLine(".pack {0}", type.PackingSize); output.WriteLine(".size {0}", type.ClassSize); @@ -917,6 +1055,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(".assembly " + DisassemblerHelpers.Escape(asm.Name.Name)); OpenBlock(false); WriteAttributes(asm.CustomAttributes); + WriteSecurityDeclarations(asm); if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) { output.Write(".publickey = "); WriteBlob(asm.Name.PublicKey); From 3779381796e327191b5702d8afc44cd1ca286153 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 21:57:53 +0200 Subject: [PATCH 47/59] Add support for type forwarding declarations ([TypeForwardedToAttribute]). Closes #190. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 68 ++++++++++++++++++- .../Ast/AstMethodBodyBuilder.cs | 31 +-------- .../Disassembler/ReflectionDisassembler.cs | 15 ++++ 3 files changed, 81 insertions(+), 33 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 7a489d07e..397b82030 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -128,6 +128,7 @@ namespace ICSharpCode.Decompiler.Ast ConvertCustomAttributes(astCompileUnit, assemblyDefinition, "assembly"); ConvertSecurityAttributes(astCompileUnit, assemblyDefinition, "assembly"); ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, "module"); + AddTypeForwarderAttributes(astCompileUnit, assemblyDefinition.MainModule, "assembly"); if (!onlyAssemblyLevel) { foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) { @@ -142,6 +143,30 @@ namespace ICSharpCode.Decompiler.Ast } } + void AddTypeForwarderAttributes(CompilationUnit astCompileUnit, ModuleDefinition module, string target) + { + if (!module.HasExportedTypes) + return; + foreach (ExportedType type in module.ExportedTypes) { + if (type.IsForwarder) { + var forwardedType = CreateTypeOfExpression(new TypeReference(type.Namespace, type.Name, module, type.Scope)); + astCompileUnit.AddChild( + new AttributeSection { + AttributeTarget = target, + Attributes = { + new NRefactory.CSharp.Attribute { + Type = new SimpleType("TypeForwardedTo") + .WithAnnotation(new TypeReference( + "System.Runtime.CompilerServices", "TypeForwardedToAttribute", + module, module.TypeSystem.Corlib)), + Arguments = { forwardedType } + } + } + }, AttributedNode.AttributeRole); + } + } + } + NamespaceDeclaration GetCodeNamespace(string name) { if (string.IsNullOrEmpty(name)) { @@ -306,6 +331,45 @@ namespace ICSharpCode.Decompiler.Ast return name; } + #region Create TypeOf Expression + /// + /// Creates a typeof-expression for the specified type. + /// + public static TypeOfExpression CreateTypeOfExpression(TypeReference type) + { + return new TypeOfExpression(AddEmptyTypeArgumentsForUnboundGenerics(ConvertType(type))); + } + + static AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type) + { + TypeReference typeRef = type.Annotation(); + if (typeRef == null) + return type; + TypeDefinition typeDef = typeRef.Resolve(); // need to resolve to figure out the number of type parameters + if (typeDef == null || !typeDef.HasGenericParameters) + return type; + SimpleType sType = type as SimpleType; + MemberType mType = type as MemberType; + if (sType != null) { + while (typeDef.GenericParameters.Count > sType.TypeArguments.Count) { + sType.TypeArguments.Add(new SimpleType("")); + } + } + + if (mType != null) { + AddEmptyTypeArgumentsForUnboundGenerics(mType.Target); + + int outerTypeParamCount = typeDef.DeclaringType == null ? 0 : typeDef.DeclaringType.GenericParameters.Count; + + while (typeDef.GenericParameters.Count - outerTypeParamCount > mType.TypeArguments.Count) { + mType.TypeArguments.Add(new SimpleType("")); + } + } + + return type; + } + #endregion + #region Convert Type Reference /// /// Converts a type reference. @@ -1378,9 +1442,7 @@ namespace ICSharpCode.Decompiler.Ast if (type != null && type.IsEnum) { return MakePrimitive(Convert.ToInt64(argument.Value), type); } else if (argument.Value is TypeReference) { - return new TypeOfExpression() { - Type = ConvertType((TypeReference)argument.Value), - }; + return CreateTypeOfExpression((TypeReference)argument.Value); } else { return new PrimitiveExpression(argument.Value); } diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index c8666b73a..b82101562 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -601,7 +601,7 @@ namespace ICSharpCode.Decompiler.Ast case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); case ILCode.Ldtoken: if (operand is Cecil.TypeReference) { - return new Ast.TypeOfExpression { Type = AddEmptyTypeArgumentsForUnboundGenerics(operandAsTypeRef) }.Member("TypeHandle"); + return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle"); } else { return InlineAssembly(byteCode, args); } @@ -749,35 +749,6 @@ namespace ICSharpCode.Decompiler.Ast } } - AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type) - { - TypeReference typeRef = type.Annotation(); - if (typeRef == null) - return type; - TypeDefinition typeDef = typeRef.Resolve(); // need to resolve to figure out the number of type parameters - if (typeDef == null || !typeDef.HasGenericParameters) - return type; - SimpleType sType = type as SimpleType; - MemberType mType = type as MemberType; - if (sType != null) { - while (typeDef.GenericParameters.Count > sType.TypeArguments.Count) { - sType.TypeArguments.Add(new SimpleType("")); - } - } - - if (mType != null) { - AddEmptyTypeArgumentsForUnboundGenerics(mType.Target); - - int outerTypeParamCount = typeDef.DeclaringType == null ? 0 : typeDef.DeclaringType.GenericParameters.Count; - - while (typeDef.GenericParameters.Count - outerTypeParamCount > mType.TypeArguments.Count) { - mType.TypeArguments.Add(new SimpleType("")); - } - } - - return type; - } - static readonly AstNode objectInitializerPattern = new AssignmentExpression( new MemberReferenceExpression { Target = new InitializedObjectExpression() diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index be00da59a..f4f8dac8a 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -1096,6 +1096,21 @@ namespace ICSharpCode.Decompiler.Disassembler public void WriteModuleHeader(ModuleDefinition module) { + if (module.HasExportedTypes) { + foreach (ExportedType exportedType in module.ExportedTypes) { + output.Write(".class extern "); + if (exportedType.IsForwarder) + output.Write("forwarder "); + output.Write(exportedType.DeclaringType != null ? exportedType.Name : exportedType.FullName); + OpenBlock(false); + if (exportedType.DeclaringType != null) + output.WriteLine(".class extern {0}", DisassemblerHelpers.Escape(exportedType.DeclaringType.FullName)); + else + output.WriteLine(".assembly extern {0}", DisassemblerHelpers.Escape(exportedType.Scope.Name)); + CloseBlock(); + } + } + output.WriteLine(".module {0}", module.Name); output.WriteLine("// MVID: {0}", module.Mvid.ToString("B").ToUpperInvariant()); // TODO: imagebase, file alignment, stackreserve, subsystem From 7d24f683d1ef1705aae3dcfcd6daee64c2d24c07 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 21 May 2011 22:03:36 +0200 Subject: [PATCH 48/59] Decompile AssemblyVersion attribute. Closes #188. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 18 ++++++++++++++++++ .../Tests/Helpers/RemoveCompilerAttribute.cs | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 397b82030..c0258db0b 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -125,6 +125,24 @@ namespace ICSharpCode.Decompiler.Ast public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false) { + if (assemblyDefinition.Name.Version != null) { + astCompileUnit.AddChild( + new AttributeSection { + AttributeTarget = "assembly", + Attributes = { + new NRefactory.CSharp.Attribute { + Type = new SimpleType("AssemblyVersion") + .WithAnnotation(new TypeReference( + "System.Reflection", "AssemblyVersionAttribute", + assemblyDefinition.MainModule, assemblyDefinition.MainModule.TypeSystem.Corlib)), + Arguments = { + new PrimitiveExpression(assemblyDefinition.Name.Version.ToString()) + } + } + } + }, AttributedNode.AttributeRole); + } + ConvertCustomAttributes(astCompileUnit, assemblyDefinition, "assembly"); ConvertSecurityAttributes(astCompileUnit, assemblyDefinition, "assembly"); ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, "module"); diff --git a/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs b/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs index 0076eb2ba..251916557 100644 --- a/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs +++ b/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs @@ -15,7 +15,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers var section = (AttributeSection)attribute.Parent; SimpleType type = attribute.Type as SimpleType; if (section.AttributeTarget == "assembly" && - (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility")) + (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "AssemblyVersion")) { attribute.Remove(); if (section.Attributes.Count == 0) From b74daf8c3259b70ca760ffa8efd952301469dd27 Mon Sep 17 00:00:00 2001 From: KevinCathcart Date: Sat, 21 May 2011 13:12:36 -0700 Subject: [PATCH 49/59] Add support for "instance explicit" calling convention for a MethodReference. --- ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index 555eae487..3c66ef83b 100644 --- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -110,8 +110,12 @@ namespace ICSharpCode.Decompiler.Disassembler public static void WriteTo(this MethodReference method, ITextOutput writer) { - if (method.HasThis) + if (method.ExplicitThis) { + writer.Write("instance explicit "); + } + else if (method.HasThis) { writer.Write("instance "); + } method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); writer.Write(' '); if (method.DeclaringType != null) { From 5274fe95c1b54f0aac5744b2c9b7eda9940079f8 Mon Sep 17 00:00:00 2001 From: KevinCathcart Date: Sat, 21 May 2011 13:44:43 -0700 Subject: [PATCH 50/59] Support "instance explicit" method definitions. (The informative metadata validation text indicates that it only applies to function pointers, but that is not enforced except as part of metadata validation.) --- .../Disassembler/ReflectionDisassembler.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index be00da59a..1a79908aa 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -153,9 +153,11 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); output.Indent(); - - if (method.HasThis) + if (method.ExplicitThis) { + output.Write("instance explicit "); + } else if (method.HasThis) { output.Write("instance "); + } //call convention WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention); From e198438a46553f1ae0d3e4504488a18c4d714ede Mon Sep 17 00:00:00 2001 From: KevinCathcart Date: Sat, 21 May 2011 13:48:35 -0700 Subject: [PATCH 51/59] Added support for IL "forwardref" method definitions. --- ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 1a79908aa..0cffebc18 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -94,6 +94,7 @@ namespace ICSharpCode.Decompiler.Disassembler { MethodImplAttributes.NoOptimization, "nooptimization" }, { MethodImplAttributes.PreserveSig, "preservesig" }, { MethodImplAttributes.InternalCall, "internalcall" }, + { MethodImplAttributes.ForwardRef, "forwardref" }, }; public void DisassembleMethod(MethodDefinition method) From 299bfbe4008a8d08ea4eb9012d3102f6bcfb09dd Mon Sep 17 00:00:00 2001 From: KevinCathcart Date: Sat, 21 May 2011 14:02:25 -0700 Subject: [PATCH 52/59] Added syntax highlighting support for IL's "strict" keyword. --- ILSpy/TextView/ILAsm-Mode.xshd | 1 + 1 file changed, 1 insertion(+) diff --git a/ILSpy/TextView/ILAsm-Mode.xshd b/ILSpy/TextView/ILAsm-Mode.xshd index 5f582d37c..76fbc1afa 100644 --- a/ILSpy/TextView/ILAsm-Mode.xshd +++ b/ILSpy/TextView/ILAsm-Mode.xshd @@ -348,6 +348,7 @@ tls true false + strict .class From 20f9a624f2958fdc649f6167538b041e8b139df2 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 21 May 2011 20:08:24 -0400 Subject: [PATCH 53/59] Add support for privatescope. Fixes #186 --- .../Disassembler/DisassemblerHelpers.cs | 7 ++++++- .../Disassembler/ReflectionDisassembler.cs | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index 3c66ef83b..97cff7d30 100644 --- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -122,7 +122,12 @@ namespace ICSharpCode.Decompiler.Disassembler method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write("::"); } - writer.WriteReference(Escape(method.Name), method); + MethodDefinition md = method as MethodDefinition; + if (md != null && md.IsCompilerControlled) { + writer.WriteReference(Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")), method); + } else { + writer.WriteReference(Escape(method.Name), method); + } GenericInstanceMethod gim = method as GenericInstanceMethod; if (gim != null) { writer.Write('<'); diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 0cffebc18..9100faab1 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -113,6 +113,7 @@ namespace ICSharpCode.Decompiler.Disassembler //emit flags WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility); WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags); + if(method.IsCompilerControlled) output.Write("privatescope "); if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) { output.Write("pinvokeimpl"); @@ -170,7 +171,13 @@ namespace ICSharpCode.Decompiler.Disassembler if (method.MethodReturnType.HasMarshalInfo) { WriteMarshalInfo(method.MethodReturnType.MarshalInfo); } - output.Write(DisassemblerHelpers.Escape(method.Name)); + + if (method.IsCompilerControlled) { + output.Write(DisassemblerHelpers.Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8"))); + } else { + output.Write(DisassemblerHelpers.Escape(method.Name)); + } + WriteTypeParameters(output, method); //( params ) From 399a87e5a1f59ef6ed7e3633c297884ae789fec8 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 22 May 2011 02:03:13 +0200 Subject: [PATCH 54/59] Fix #195 IL identfier escaping. --- .../Disassembler/DisassemblerHelpers.cs | 22 ++++++++++++++----- .../Disassembler/ReflectionDisassembler.cs | 7 +++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index 97cff7d30..a88ed7089 100644 --- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -156,14 +156,21 @@ namespace ICSharpCode.Decompiler.Disassembler writer.WriteReference(Escape(field.Name), field); } + static bool IsValidIdentifierCharacter(char c) + { + return c == '_' || c == '$' || c == '@' || c == '?' || c == '`'; + } + static bool IsValidIdentifier(string identifier) { if (string.IsNullOrEmpty(identifier)) return false; - if (!(char.IsLetter(identifier[0]) || identifier[0] == '_' || identifier[0] == '.')) - return false; + if (!(char.IsLetter(identifier[0]) || IsValidIdentifierCharacter(identifier[0]))) { + // As a special case, .ctor and .cctor are valid despite starting with a dot + return identifier == ".ctor" || identifier == ".cctor"; + } for (int i = 1; i < identifier.Length; i++) { - if (!(char.IsLetterOrDigit(identifier[i]) || identifier[i] == '_' || identifier[i] == '.' || identifier[i] == '`')) + if (!(char.IsLetterOrDigit(identifier[i]) || IsValidIdentifierCharacter(identifier[i]) || identifier[i] == '.')) return false; } return true; @@ -196,7 +203,7 @@ namespace ICSharpCode.Decompiler.Disassembler "vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper", // These are not listed as keywords in spec, but ILAsm treats them as such - "property", "type", "flags", "callconv" + "property", "type", "flags", "callconv", "strict" ); static HashSet BuildKeywordList(params string[] keywords) @@ -210,10 +217,13 @@ namespace ICSharpCode.Decompiler.Disassembler public static string Escape(string identifier) { - if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) + if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) { return identifier; - else + } else { + // The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence, + // but we follow Microsoft's ILDasm and use \'. return "'" + NRefactory.CSharp.OutputVisitor.ConvertString(identifier).Replace("'", "\\'") + "'"; + } } public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature) diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 4174f2b11..4e144924e 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -335,7 +335,12 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(' '); output.Write(DisassemblerHelpers.Escape(na.Name)); output.Write(" = "); - WriteConstant(na.Argument.Value); + if (na.Argument.Value is string) { + // secdecls use special syntax for strings + output.Write("string('{0}')", NRefactory.CSharp.OutputVisitor.ConvertString((string)na.Argument.Value).Replace("'", "\'")); + } else { + WriteConstant(na.Argument.Value); + } } string GetAssemblyQualifiedName(TypeReference type) From 229218174f2d53b5b7915565b5359da4d16c46a7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 22 May 2011 13:39:29 +0200 Subject: [PATCH 55/59] Inlining: remove the arg_-Variable in catch blocks. --- ICSharpCode.Decompiler/Ast/NameVariables.cs | 2 ++ .../Disassembler/DisassemblerHelpers.cs | 7 ++-- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 4 +++ ICSharpCode.Decompiler/ILAst/ILInlining.cs | 32 ++++++++++++++++++- .../Tests/ExceptionHandling.cs | 4 +-- ICSharpCode.Decompiler/Tests/TestRunner.cs | 2 +- 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs index 66f50ff13..c7ac8f385 100644 --- a/ICSharpCode.Decompiler/Ast/NameVariables.cs +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -279,6 +279,8 @@ namespace ICSharpCode.Decompiler.Ast name = "array"; } else if (type.IsPointer || type.IsByReference) { name = "ptr"; + } else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) { + name = "ex"; } else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) { name = type.Name; // remove the 'I' for interfaces diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index a88ed7089..df66c2650 100644 --- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.Disassembler /// TypeName, /// - /// Name (even for built-in types) + /// Name (but built-in types use keyword syntax) /// ShortTypeName } @@ -275,7 +275,10 @@ namespace ICSharpCode.Decompiler.Disassembler } else { string name = PrimitiveTypeName(type.FullName); if (syntax == ILNameSyntax.ShortTypeName) { - writer.WriteReference(Escape(type.Name), type); + if (name != null) + writer.Write(name); + else + writer.WriteReference(Escape(type.Name), type); } else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) { writer.Write(name); } else { diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 48312b080..f86225fb8 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -141,6 +141,10 @@ namespace ICSharpCode.Decompiler.ILAst { output.Write("catch "); output.WriteReference(ExceptionType.FullName, ExceptionType); + if (ExceptionVariable != null) { + output.Write(' '); + output.Write(ExceptionVariable.Name); + } output.WriteLine(" {"); output.Indent(); base.WriteTo(output); diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs index 76fde12bf..d839f41ef 100644 --- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/ICSharpCode.Decompiler/ILAst/ILInlining.cs @@ -47,7 +47,13 @@ namespace ICSharpCode.Decompiler.ILAst numLdloca.Clear(); // Analyse the whole method - foreach(ILExpression expr in method.GetSelfAndChildrenRecursive()) { + AnalyzeNode(method); + } + + void AnalyzeNode(ILNode node) + { + ILExpression expr = node as ILExpression; + if (expr != null) { ILVariable locVar = expr.Operand as ILVariable; if (locVar != null) { if (expr.Code == ILCode.Stloc) { @@ -60,6 +66,16 @@ namespace ICSharpCode.Decompiler.ILAst throw new NotSupportedException(expr.Code.ToString()); } } + foreach (ILExpression child in expr.Arguments) + AnalyzeNode(child); + } else { + var catchBlock = node as ILTryCatchBlock.CatchBlock; + if (catchBlock != null && catchBlock.ExceptionVariable != null) { + numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + 1; + } + + foreach (ILNode child in node.GetChildren()) + AnalyzeNode(child); } } @@ -76,6 +92,20 @@ namespace ICSharpCode.Decompiler.ILAst { bool modified = false; List body = block.Body; + if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) { + ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable; + if (v != null && v.IsGenerated) { + if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) { + ILVariable v2; + ILExpression ldException; + if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) { + body.RemoveAt(0); + ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2; + modified = true; + } + } + } + } for(int i = 0; i < body.Count - 1;) { ILVariable locVar; ILExpression expr; diff --git a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs index 9d81936a5..7f6e506ee 100644 --- a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs +++ b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs @@ -56,9 +56,9 @@ public class ExceptionHandling { Console.WriteLine(ex.Message); } - catch (Exception ex) + catch (Exception ex2) { - Console.WriteLine(ex.Message); + Console.WriteLine(ex2.Message); } catch { diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index de06f7d68..87bb31dd4 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.Tests TestFile(@"..\..\Tests\DelegateConstruction.cs"); } - [Test, Ignore("arg-Variables in catch clauses")] + [Test] public void ExceptionHandling() { TestFile(@"..\..\Tests\ExceptionHandling.cs"); From a1205d40e9d7d5f091a08b42c46a9a0dc96e7989 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 22 May 2011 13:56:44 +0200 Subject: [PATCH 56/59] Fix #168: ILSpy crashes on close when viewing an obfuscated assembly --- ILSpy/ILSpySettings.cs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/ILSpy/ILSpySettings.cs b/ILSpy/ILSpySettings.cs index 7d0fd19cb..053bee7ce 100644 --- a/ILSpy/ILSpySettings.cs +++ b/ILSpy/ILSpySettings.cs @@ -19,6 +19,7 @@ using System; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Xml; using System.Xml.Linq; @@ -58,7 +59,7 @@ namespace ICSharpCode.ILSpy { using (new MutexProtector(ConfigFileMutex)) { try { - XDocument doc = XDocument.Load(GetConfigFile()); + XDocument doc = LoadWithoutCheckingCharacters(GetConfigFile()); return new ILSpySettings(doc.Root); } catch (IOException) { return new ILSpySettings(); @@ -68,6 +69,15 @@ namespace ICSharpCode.ILSpy } } + static XDocument LoadWithoutCheckingCharacters(string fileName) + { + // XDocument.Load(fileName) validates that no invalid characters appear (not even in escaped form), + // but we need those characters for some obfuscated assemblies. + using (XmlTextReader r = new XmlTextReader(fileName)) { + return XDocument.Load(r); + } + } + /// /// Saves a setting section. /// @@ -94,7 +104,7 @@ namespace ICSharpCode.ILSpy string config = GetConfigFile(); XDocument doc; try { - doc = XDocument.Load(config); + doc = LoadWithoutCheckingCharacters(config); } catch (IOException) { // ensure the directory exists Directory.CreateDirectory(Path.GetDirectoryName(config)); @@ -104,7 +114,12 @@ namespace ICSharpCode.ILSpy } doc.Root.SetAttributeValue("version", RevisionClass.Major + "." + RevisionClass.Minor + "." + RevisionClass.Build + "." + RevisionClass.Revision); action(doc.Root); - doc.Save(config); + // We can't use XDocument.Save(filename) because that checks for invalid characters, but those can appear + // in obfuscated assemblies. + using (XmlTextWriter writer = new XmlTextWriter(config, Encoding.UTF8)) { + writer.Formatting = Formatting.Indented; + doc.Save(writer); + } } } From a929decdeb3289939e73fac9c03f8f88c5805f4d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 22 May 2011 14:35:41 +0200 Subject: [PATCH 57/59] implement basic support for custom short circuit operators + unit test; fixes #193 --- .../ILAst/ILAstOptimizer.cs | 4 + .../ILAst/SimpleControlFlow.cs | 84 ++++++++++++++++++- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 4 + .../Tests/CustomShortCircuitOperators.cs | 43 ++++++++++ .../Tests/ICSharpCode.Decompiler.Tests.csproj | 1 + ICSharpCode.Decompiler/Tests/TestRunner.cs | 6 ++ 6 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 54db1aea7..ef6ab4e54 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -44,6 +44,7 @@ namespace ICSharpCode.Decompiler.ILAst JoinBasicBlocks, TransformDecimalCtorToConstant, SimplifyLdObjAndStObj, + SimplifyCustomShortCircuit, TransformArrayInitializers, TransformObjectInitializers, MakeAssignmentExpression, @@ -136,6 +137,9 @@ namespace ICSharpCode.Decompiler.ILAst if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return; modified |= block.RunOptimization(SimplifyLdObjAndStObj); + if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) return; + modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit); + if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return; modified |= block.RunOptimization(TransformArrayInitializers); diff --git a/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs b/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs index e477dbff5..d821f9aae 100644 --- a/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs +++ b/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.ILAst newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); } } else { - // Ternary operator tends to create long complicated return statements + // Ternary operator tends to create long complicated return statements if (opCode == ILCode.Ret) return false; @@ -156,7 +156,7 @@ namespace ICSharpCode.Decompiler.ILAst // ... // v = NullCoalescing(ldloc(leftVar), rightExpr) // br(endBBLabel) - + ILVariable v, v2; ILExpression leftExpr, leftExpr2; ILVariable leftVar; @@ -165,7 +165,7 @@ namespace ICSharpCode.Decompiler.ILAst ILBasicBlock rightBB; ILExpression rightExpr; if (head.Body.Count >= 3 && - head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) && + head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) && leftExpr.Match(ILCode.Ldloc, out leftVar) && head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && leftExpr2.MatchLdloc(leftVar) && @@ -234,6 +234,84 @@ namespace ICSharpCode.Decompiler.ILAst return false; } + public bool SimplifyCustomShortCircuit(List body, ILBasicBlock head, int pos) + { + Debug.Assert(body.Contains(head)); + + // --- looking for the following pattern --- + // stloc(targetVar, leftVar) + // brtrue(exitLabel, call(op_False, leftVar) + // br(followingBlock) + // + // FollowingBlock: + // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) + // br(exitLabel) + // --- + + if (head.Body.Count < 3) + return false; + + // looking for: + // stloc(targetVar, leftVar) + ILVariable targetVar; + ILExpression targetVarInitExpr; + if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr)) + return false; + + // looking for: + // brtrue(exitLabel, call(op_False, leftVar) + // br(followingBlock) + ILExpression callExpr; + ILLabel exitLabel; + ILLabel followingBlock; + if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock)) + return false; + + MethodDefinition opFalse; + ILExpression leftVar; + if (!callExpr.Match(ILCode.Call, out opFalse, out leftVar)) + return false; + + if (!leftVar.MatchLdloc(targetVarInitExpr.Operand as ILVariable)) + return false; + + ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock]; + + // FollowingBlock: + // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) + // br(exitLabel) + ILVariable _targetVar; + ILExpression opBitwiseCallExpr; + ILLabel _exitLabel; + if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel)) + return false; + + if (_targetVar != targetVar) + return false; + + MethodDefinition opBitwise; + ILExpression leftVarExpression; + ILExpression rightExpression; + if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression)) + return false; + + if (!leftVar.MatchLdloc(leftVarExpression.Operand as ILVariable)) + return false; + + // insert: + // stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression) + // br(exitLabel) + ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr, leftVar, rightExpression); + shortCircuitExpr.Operand = opBitwise; + + head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); + head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr)); + head.Body.Add(new ILExpression(ILCode.Br, exitLabel)); + body.Remove(followingBasicBlock); + + return true; + } + ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) { // Assuming that the inputs are already left associative diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 1aa21a276..3c5c47bf6 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -267,6 +267,10 @@ namespace ICSharpCode.Decompiler.ILAst return typeSystem.Boolean; case ILCode.LogicAnd: case ILCode.LogicOr: + // if Operand is set the logic and/or expression is a custom operator + // we can deal with it the same as a normal invocation. + if (expr.Operand != null) + goto case ILCode.Call; if (forceInferChildren) { InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean); diff --git a/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs b/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs new file mode 100644 index 000000000..353e6aea0 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs @@ -0,0 +1,43 @@ +// 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; + +public static class CustomShortCircuitOperators +{ + private class B + { + public static bool operator true(CustomShortCircuitOperators.B x) + { + return true; + } + + public static bool operator false(CustomShortCircuitOperators.B x) + { + return false; + } + } + + private class C : CustomShortCircuitOperators.B + { + public static CustomShortCircuitOperators.C operator &(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y) + { + return null; + } + + public static CustomShortCircuitOperators.C operator |(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y) + { + return null; + } + + private static void Main() + { + CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); + CustomShortCircuitOperators.C c2 = new CustomShortCircuitOperators.C(); + CustomShortCircuitOperators.C c3 = c && c2; + CustomShortCircuitOperators.C c4 = c || c2; + Console.WriteLine(c3.ToString()); + Console.WriteLine(c4.ToString()); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 6023f2aff..33227a313 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -57,6 +57,7 @@ + diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 87bb31dd4..a3100391c 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -49,6 +49,12 @@ namespace ICSharpCode.Decompiler.Tests TestFile(@"..\..\Tests\Generics.cs"); } + [Test] + public void CustomShortCircuitOperators() + { + TestFile(@"..\..\Tests\CustomShortCircuitOperators.cs"); + } + [Test] public void IncrementDecrement() { From f86fa5d1dd448bd790cc5a8d5125207de93be1f6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 22 May 2011 15:28:35 +0200 Subject: [PATCH 58/59] implement support for op_True and extend unit test --- .../ReplaceMethodCallsWithOperators.cs | 7 ++-- .../Tests/CustomShortCircuitOperators.cs | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs index 7a9eaa365..ba641e77d 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs @@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms var arguments = invocationExpression.Arguments.ToArray(); // Reduce "String.Concat(a, b)" to "a + b" - if (methodRef != null && methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2) + if (methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2) { invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression Expression expr = arguments[0]; @@ -97,7 +97,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms return null; } if (methodRef.Name == "op_Implicit" && arguments.Length == 1) { - arguments[0].Remove(); // detach argument + invocationExpression.ReplaceWith(arguments[0]); + return null; + } + if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == AstNode.Roles.Condition) { invocationExpression.ReplaceWith(arguments[0]); return null; } diff --git a/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs b/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs index 353e6aea0..30a94a6e6 100644 --- a/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs +++ b/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs @@ -29,6 +29,11 @@ public static class CustomShortCircuitOperators { return null; } + + public static bool operator !(CustomShortCircuitOperators.C x) + { + return false; + } private static void Main() { @@ -39,5 +44,32 @@ public static class CustomShortCircuitOperators Console.WriteLine(c3.ToString()); Console.WriteLine(c4.ToString()); } + + private static void Test2() + { + CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); + if (c && c) + { + Console.WriteLine(c.ToString()); + } + + if (!(c && c)) + { + Console.WriteLine(c.ToString()); + } + } + + private static void Test3() + { + CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); + if (c) + { + Console.WriteLine(c.ToString()); + } + if (!c) + { + Console.WriteLine(c.ToString()); + } + } } } \ No newline at end of file From 2b366fbb97d40562c8223d96863f1a0cbfab9c0d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 22 May 2011 16:33:36 +0200 Subject: [PATCH 59/59] make pattern for custom short circuit operator invocations more restrictive --- .../ILAst/SimpleControlFlow.cs | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs b/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs index d821f9aae..319e18bbb 100644 --- a/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs +++ b/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs @@ -258,6 +258,10 @@ namespace ICSharpCode.Decompiler.ILAst if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr)) return false; + ILVariable leftVar; + if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar)) + return false; + // looking for: // brtrue(exitLabel, call(op_False, leftVar) // br(followingBlock) @@ -267,12 +271,19 @@ namespace ICSharpCode.Decompiler.ILAst if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock)) return false; - MethodDefinition opFalse; - ILExpression leftVar; - if (!callExpr.Match(ILCode.Call, out opFalse, out leftVar)) + if (labelGlobalRefCount[followingBlock] > 1) + return false; + + MethodReference opFalse; + ILExpression opFalseArg; + if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg)) + return false; + + // ignore operators other than op_False and op_True + if (opFalse.Name != "op_False" && opFalse.Name != "op_True") return false; - if (!leftVar.MatchLdloc(targetVarInitExpr.Operand as ILVariable)) + if (!opFalseArg.MatchLdloc(leftVar)) return false; ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock]; @@ -286,22 +297,34 @@ namespace ICSharpCode.Decompiler.ILAst if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel)) return false; - if (_targetVar != targetVar) + if (_targetVar != targetVar || exitLabel != _exitLabel) return false; - MethodDefinition opBitwise; + MethodReference opBitwise; ILExpression leftVarExpression; ILExpression rightExpression; if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression)) return false; - if (!leftVar.MatchLdloc(leftVarExpression.Operand as ILVariable)) + if (!opFalseArg.MatchLdloc(leftVarExpression.Operand as ILVariable)) + return false; + + // ignore operators other than op_BitwiseAnd and op_BitwiseOr + if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr") return false; // insert: // stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression) // br(exitLabel) - ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr, leftVar, rightExpression); + ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr; + + if (op == ILCode.LogicAnd && opFalse.Name != "op_False") + return false; + + if (op == ILCode.LogicOr && opFalse.Name != "op_True") + return false; + + ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression); shortCircuitExpr.Operand = opBitwise; head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br);