From 529b004ea9dd6a391b64aeb9ca0b66b35ece1a25 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 24 Feb 2020 12:21:36 -0800 Subject: [PATCH 01/14] [ILSpy.ReadyToRun] Avoid eager parsing --- ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj | 2 +- ILSpy.ReadyToRun/ReadyToRunLanguage.cs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj index 1f573d07a..864bc5f3a 100644 --- a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj +++ b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj @@ -57,7 +57,7 @@ - + diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs index cabf65180..089b59f6f 100644 --- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -175,6 +175,13 @@ namespace ICSharpCode.ILSpy.ReadyToRun LoadedAssembly loadedAssembly = this.loadedAssembly.LookupReferencedAssembly(new Decompiler.Metadata.AssemblyReference(metadataReader, assemblyReferenceHandle)); return loadedAssembly?.GetPEFileOrNull()?.Metadata; } + + public MetadataReader FindAssembly(string simpleName, string parentFile) + { + // This is called only for the composite R2R scenario, + // So it will never be called before the feature is released. + throw new NotSupportedException("Composite R2R format is not currently supported"); + } } private class ReadyToRunReaderCacheEntry From 73e0f7c3ac87fbb40f013dceeb85900730403085 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 27 Feb 2020 14:10:28 +0100 Subject: [PATCH 02/14] Fix #1936: TransformDisplayClassUsage should remove copies of display-class references. --- .../TestCases/Correctness/Capturing.cs | 20 +++++++++++++++++++ .../Transforms/TransformDisplayClassUsage.cs | 15 +++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Capturing.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Capturing.cs index 1c57ddb13..27f95114a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Capturing.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Capturing.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { @@ -18,6 +19,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness InsideLoopOverArray2(); NotWhileDueToVariableInsideLoop(); NotDoWhileDueToVariableInsideLoop(); + Issue1936(); } static void TestCase1() @@ -210,5 +212,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Console.WriteLine(f()); } } + + public static void Issue1936() + { + IEnumerable outerCapture = null; + for (int i = 0; i < 10; i++) { + int innerCapture = 0; + Action a = (delegate { + List list = new List(); + Console.WriteLine("before inc: " + innerCapture); + ++innerCapture; + Console.WriteLine("after inc: " + innerCapture); + Console.WriteLine("before assign: " + outerCapture); + outerCapture = outerCapture == null ? list : outerCapture.Concat(list); + Console.WriteLine("after assign: " + outerCapture); + }); + a.Invoke(); + } + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 49e1c89cf..e83a37ccd 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -309,9 +309,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms { base.VisitStLoc(inst); - if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { - context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); - inst.ReplaceWith(inst.Value); + if (inst.Parent is Block && inst.Variable.IsSingleDefinition) { + if (inst.Variable.Kind == VariableKind.Local && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { + context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); + inst.ReplaceWith(inst.Value); + return; + } + if (inst.Value.MatchLdLoc(out var displayClassVariable) && displayClasses.TryGetValue(displayClassVariable, out var displayClass)) { + context.Step($"Found copy-assignment of display-class variable {displayClassVariable.Name}", inst); + displayClasses.Add(inst.Variable, displayClass); + instructionsToRemove.Add(inst); + return; + } } } From df84ab8f6b167963e7560b270189d6d501248d5a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 27 Feb 2020 19:21:19 +0100 Subject: [PATCH 03/14] Fix #1882: Provide a setting to desugar X? into Nullable for value types --- .../CSharp/CSharpDecompiler.cs | 19 ++++--- .../CSharp/ExpressionBuilder.cs | 1 + .../CSharp/OutputVisitor/CSharpAmbience.cs | 1 + .../CSharp/Syntax/TypeSystemAstBuilder.cs | 55 +++++++++++-------- .../Transforms/IntroduceUsingDeclarations.cs | 7 ++- ICSharpCode.Decompiler/Output/IAmbience.cs | 5 ++ ILSpy/Languages/CSharpLanguage.cs | 11 +++- 7 files changed, 63 insertions(+), 36 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 740097fc3..174673e13 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -401,12 +401,13 @@ namespace ICSharpCode.Decompiler.CSharp return new DecompilerTypeSystem(file, resolver); } - static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext) + static TypeSystemAstBuilder CreateAstBuilder(DecompilerSettings settings) { var typeSystemAstBuilder = new TypeSystemAstBuilder(); typeSystemAstBuilder.ShowAttributes = true; typeSystemAstBuilder.AlwaysUseShortTypeNames = true; typeSystemAstBuilder.AddResolveResultAnnotations = true; + typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; return typeSystemAstBuilder; } @@ -421,7 +422,7 @@ namespace ICSharpCode.Decompiler.CSharp void RunTransforms(AstNode rootNode, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { - var typeSystemAstBuilder = CreateAstBuilder(decompilationContext); + var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); var context = new TransformContext(typeSystem, decompileRun, decompilationContext, typeSystemAstBuilder); foreach (var transform in astTransforms) { CancellationToken.ThrowIfCancellationRequested(); @@ -466,13 +467,13 @@ namespace ICSharpCode.Decompiler.CSharp { try { foreach (var a in typeSystem.MainModule.GetAssemblyAttributes()) { - var astBuilder = CreateAstBuilder(decompilationContext); + var astBuilder = CreateAstBuilder(decompileRun.Settings); var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a)); attrSection.AttributeTarget = "assembly"; syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole); } foreach (var a in typeSystem.MainModule.GetModuleAttributes()) { - var astBuilder = CreateAstBuilder(decompilationContext); + var astBuilder = CreateAstBuilder(decompileRun.Settings); var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a)); attrSection.AttributeTarget = "module"; syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole); @@ -1076,7 +1077,7 @@ namespace ICSharpCode.Decompiler.CSharp { Debug.Assert(decompilationContext.CurrentTypeDefinition == typeDef); try { - var typeSystemAstBuilder = CreateAstBuilder(decompilationContext); + var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); var entityDecl = typeSystemAstBuilder.ConvertEntity(typeDef); var typeDecl = entityDecl as TypeDeclaration; if (typeDecl == null) { @@ -1239,7 +1240,7 @@ namespace ICSharpCode.Decompiler.CSharp EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentMember == method); - var typeSystemAstBuilder = CreateAstBuilder(decompilationContext); + var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); var methodDecl = typeSystemAstBuilder.ConvertEntity(method); int lastDot = method.Name.LastIndexOf('.'); if (method.IsExplicitInterfaceImplementation && lastDot >= 0) { @@ -1412,7 +1413,7 @@ namespace ICSharpCode.Decompiler.CSharp { Debug.Assert(decompilationContext.CurrentMember == field); try { - var typeSystemAstBuilder = CreateAstBuilder(decompilationContext); + var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); if (decompilationContext.CurrentTypeDefinition.Kind == TypeKind.Enum && field.IsConst) { var enumDec = new EnumMemberDeclaration { Name = field.Name }; object constantValue = field.GetConstantValue(); @@ -1483,7 +1484,7 @@ namespace ICSharpCode.Decompiler.CSharp { Debug.Assert(decompilationContext.CurrentMember == property); try { - var typeSystemAstBuilder = CreateAstBuilder(decompilationContext); + var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); EntityDeclaration propertyDecl = typeSystemAstBuilder.ConvertEntity(property); if (property.IsExplicitInterfaceImplementation && !property.IsIndexer) { int lastDot = property.Name.LastIndexOf('.'); @@ -1518,7 +1519,7 @@ namespace ICSharpCode.Decompiler.CSharp { Debug.Assert(decompilationContext.CurrentMember == ev); try { - var typeSystemAstBuilder = CreateAstBuilder(decompilationContext); + var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); typeSystemAstBuilder.UseCustomEvents = ev.DeclaringTypeDefinition.Kind != TypeKind.Interface; var eventDecl = typeSystemAstBuilder.ConvertEntity(ev); int lastDot = ev.Name.LastIndexOf('.'); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 3e045f087..9856960d4 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp this.astBuilder = new TypeSystemAstBuilder(resolver); this.astBuilder.AlwaysUseShortTypeNames = true; this.astBuilder.AddResolveResultAnnotations = true; + this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved }; } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index 2e46c429b..c6b09f079 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -195,6 +195,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames; astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; + astBuilder.UseNullableSpecifierForValueTypes = (ConversionFlags & ConversionFlags.UseNullableSpecifierForValueTypes) != 0; return astBuilder; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index d43e616cb..6776082c5 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -64,7 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax #region Properties void InitProperties() { - this.AlwaysUseBuiltinTypeNames = true; + this.UseKeywordsForBuiltinTypes = true; + this.UseNullableSpecifierForValueTypes = true; this.ShowAccessibility = true; this.ShowModifiers = true; this.ShowBaseTypes = true; @@ -78,82 +79,88 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// Specifies whether the ast builder should add annotations to type references. - /// The default value is false. + /// The default value is . /// public bool AddTypeReferenceAnnotations { get; set; } /// /// Specifies whether the ast builder should add ResolveResult annotations to AST nodes. - /// The default value is false. + /// The default value is . /// public bool AddResolveResultAnnotations { get; set; } /// /// Controls the accessibility modifiers are shown. - /// The default value is true. + /// The default value is . /// public bool ShowAccessibility { get; set; } /// /// Controls the non-accessibility modifiers are shown. - /// The default value is true. + /// The default value is . /// public bool ShowModifiers { get; set; } /// /// Controls whether base type references are shown. - /// The default value is true. + /// The default value is . /// public bool ShowBaseTypes { get; set; } /// /// Controls whether type parameter declarations are shown. - /// The default value is true. + /// The default value is . /// public bool ShowTypeParameters { get; set; } /// /// Controls whether type parameter names are shown for unbound types. - /// The default value is false. + /// The default value is . /// public bool ShowTypeParametersForUnboundTypes { get; set; } /// /// Controls whether constraints on type parameter declarations are shown. /// Has no effect if ShowTypeParameters is false. - /// The default value is true. + /// The default value is . /// public bool ShowTypeParameterConstraints { get; set; } /// /// Controls whether the names of parameters are shown. - /// The default value is true. + /// The default value is . /// public bool ShowParameterNames { get; set; } /// /// Controls whether to show default values of optional parameters, and the values of constant fields. - /// The default value is true. + /// The default value is . /// public bool ShowConstantValues { get; set; } /// /// Controls whether to show attributes. - /// The default value is false. + /// The default value is . /// public bool ShowAttributes { get; set; } /// /// Controls whether to use fully-qualified type names or short type names. - /// The default value is false. + /// The default value is . /// public bool AlwaysUseShortTypeNames { get; set; } /// - /// Controls whether to use fully-qualified type names or short type names. - /// The default value is true. + /// Controls whether to use keywords for builtin types. + /// The default value is . + /// + public bool UseKeywordsForBuiltinTypes { get; set; } + + /// + /// Controls whether to use T? or Nullable<T> for nullable value types. + /// The default value is . /// - public bool AlwaysUseBuiltinTypeNames { get; set; } + public bool UseNullableSpecifierForValueTypes { get; set; } /// /// Determines the name lookup mode for converting a type name. @@ -165,37 +172,37 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// Controls whether to generate a body that throws a System.NotImplementedException. - /// The default value is false. + /// The default value is . /// public bool GenerateBody { get; set; } /// /// Controls whether to generate custom events. - /// The default value is false. + /// The default value is . /// public bool UseCustomEvents { get; set; } /// /// Controls whether unbound type argument names are inserted in the ast or not. - /// The default value is false. + /// The default value is . /// public bool ConvertUnboundTypeArguments { get; set;} /// /// Controls whether aliases should be used inside the type name or not. - /// The default value is true. + /// The default value is . /// public bool UseAliases { get; set; } /// /// Controls whether constants like int.MaxValue are converted to a or . - /// The default value is true. + /// The default value is . /// public bool UseSpecialConstants { get; set; } /// /// Controls whether integral constants should be printed in hexadecimal format. - /// The default value is false. + /// The default value is . /// public bool PrintIntegralValuesAsHex { get; set; } #endregion @@ -290,7 +297,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } break; case ParameterizedType pt: - if (AlwaysUseBuiltinTypeNames && pt.IsKnownType(KnownTypeCode.NullableOfT)) { + if (UseNullableSpecifierForValueTypes && pt.IsKnownType(KnownTypeCode.NullableOfT)) { return ConvertType(pt.TypeArguments[0]).MakeNullableType(); } astType = ConvertTypeHelper(pt.GenericType, pt.TypeArguments); @@ -313,7 +320,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax Debug.Assert(typeDef != null || genericType.Kind == TypeKind.Unknown); Debug.Assert(typeArguments.Count >= genericType.TypeParameterCount); - if (AlwaysUseBuiltinTypeNames && typeDef != null) { + if (UseKeywordsForBuiltinTypes && typeDef != null) { string keyword = KnownTypeReference.GetCSharpNameByTypeCode(typeDef.KnownTypeCode); if (keyword != null) { return new PrimitiveType(keyword); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs index b06686d0f..3d5180139 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs @@ -116,12 +116,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { readonly Stack context; readonly bool ignoreUsingScope; + readonly DecompilerSettings settings; TypeSystemAstBuilder astBuilder; public FullyQualifyAmbiguousTypeNamesVisitor(TransformContext context, UsingScope usingScope) { this.ignoreUsingScope = !context.Settings.UsingDeclarations; + this.settings = context.Settings; CSharpTypeResolveContext currentContext; if (ignoreUsingScope) { @@ -139,7 +141,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms this.astBuilder = CreateAstBuilder(currentContext); } - static TypeSystemAstBuilder CreateAstBuilder(CSharpTypeResolveContext context, IL.ILFunction function = null) + TypeSystemAstBuilder CreateAstBuilder(CSharpTypeResolveContext context, IL.ILFunction function = null) { CSharpResolver resolver = new CSharpResolver(context); if (function != null) { @@ -148,8 +150,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms resolver = resolver.AddVariable(new DefaultVariable(v.Type, v.Name)); } } - + return new TypeSystemAstBuilder(resolver) { + UseNullableSpecifierForValueTypes = settings.LiftNullables, AddResolveResultAnnotations = true, UseAliases = true }; diff --git a/ICSharpCode.Decompiler/Output/IAmbience.cs b/ICSharpCode.Decompiler/Output/IAmbience.cs index 7f75780e0..a8b547f3c 100644 --- a/ICSharpCode.Decompiler/Output/IAmbience.cs +++ b/ICSharpCode.Decompiler/Output/IAmbience.cs @@ -92,12 +92,17 @@ namespace ICSharpCode.Decompiler.Output /// Show default values of parameters. /// ShowParameterDefaultValues = 0x4000, + /// + /// Use T? instead of Nullable<T>. + /// + UseNullableSpecifierForValueTypes = 0x8000, StandardConversionFlags = ShowParameterNames | ShowAccessibility | ShowParameterList | ShowParameterModifiers | ShowParameterDefaultValues | + UseNullableSpecifierForValueTypes | ShowReturnType | ShowModifiers | ShowTypeParameterList | diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index d55b5a036..1defe0622 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -457,6 +457,9 @@ namespace ICSharpCode.ILSpy CSharpAmbience ambience = new CSharpAmbience(); // Do not forget to update CSharpAmbienceTests.ILSpyMainTreeViewTypeFlags, if this ever changes. ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList; + if (new DecompilationOptions().DecompilerSettings.LiftNullables) { + ambience.ConversionFlags |= ConversionFlags.UseNullableSpecifierForValueTypes; + } return ambience; } @@ -635,7 +638,13 @@ namespace ICSharpCode.ILSpy var output = new StringWriter(); var decoratedWriter = new TextWriterTokenWriter(output); var writer = new CSharpHighlightingTokenWriter(TokenWriter.InsertRequiredSpaces(decoratedWriter), locatable: decoratedWriter); - new CSharpAmbience() { ConversionFlags = flags }.ConvertSymbol(entity, writer, new DecompilerSettings().CSharpFormattingOptions); + var settings = new DecompilationOptions().DecompilerSettings; + if (!settings.LiftNullables) { + flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes; + } + new CSharpAmbience() { + ConversionFlags = flags, + }.ConvertSymbol(entity, writer, settings.CSharpFormattingOptions); return new RichText(output.ToString(), writer.HighlightingModel); } From 46bb042e6ea26463784611b34b51125b2f9c03c0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 27 Feb 2020 20:19:26 +0100 Subject: [PATCH 04/14] FindNodeByPath: Use metadata token as identification of member/type tree nodes. --- ILSpy/TextView/DecompilerTextView.cs | 4 ++-- ILSpy/TreeNodes/EventTreeNode.cs | 6 ++++++ ILSpy/TreeNodes/FieldTreeNode.cs | 6 ++++++ ILSpy/TreeNodes/MethodTreeNode.cs | 6 ++++++ ILSpy/TreeNodes/PropertyTreeNode.cs | 6 ++++++ ILSpy/TreeNodes/TypeTreeNode.cs | 6 ++++++ 6 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 2f831e000..8f1843c66 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -581,7 +581,7 @@ namespace ICSharpCode.ILSpy.TextView this.nextDecompilationRun = null; } if (nodes != null && string.IsNullOrEmpty(textOutput.Title)) - textOutput.Title = string.Join(", ", nodes.Select(n => n.ToString())); + textOutput.Title = string.Join(", ", nodes.Select(n => n.Text)); ShowOutput(textOutput, highlighting); decompiledNodes = nodes; } @@ -772,7 +772,7 @@ namespace ICSharpCode.ILSpy.TextView { var nodes = context.TreeNodes; if (textOutput is ISmartTextOutput smartTextOutput) { - smartTextOutput.Title = string.Join(", ", nodes.Select(n => n.ToString())); + smartTextOutput.Title = string.Join(", ", nodes.Select(n => n.Text)); } for (int i = 0; i < nodes.Length; i++) { if (i > 0) diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 1aadff896..5a4468fd5 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -86,5 +86,11 @@ namespace ICSharpCode.ILSpy.TreeNodes } IEntity IMemberTreeNode.Member => EventDefinition; + + public override string ToString() + { + int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(EventDefinition.MetadataToken); + return "@" + token.ToString("X8"); + } } } diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 5d4d2f7b0..75255c6a2 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -87,5 +87,11 @@ namespace ICSharpCode.ILSpy.TreeNodes } IEntity IMemberTreeNode.Member => FieldDefinition; + + public override string ToString() + { + int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(FieldDefinition.MetadataToken); + return "@" + token.ToString("X8"); + } } } diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 9e4d95e45..db7f72254 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -112,5 +112,11 @@ namespace ICSharpCode.ILSpy.TreeNodes } IEntity IMemberTreeNode.Member => MethodDefinition; + + public override string ToString() + { + int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(MethodDefinition.MetadataToken); + return "@" + token.ToString("X8"); + } } } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 14769ea66..ede31f5bd 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -93,5 +93,11 @@ namespace ICSharpCode.ILSpy.TreeNodes } IEntity IMemberTreeNode.Member => PropertyDefinition; + + public override string ToString() + { + int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(PropertyDefinition.MetadataToken); + return "@" + token.ToString("X8"); + } } } diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index be9bc9546..3a65fc385 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -154,5 +154,11 @@ namespace ICSharpCode.ILSpy.TreeNodes } IEntity IMemberTreeNode.Member => TypeDefinition; + + public override string ToString() + { + int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(TypeDefinition.MetadataToken); + return "@" + token.ToString("X8"); + } } } From da33002c6684da8e3380e5da8ae3c56374ade2e8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 27 Feb 2020 20:26:06 +0100 Subject: [PATCH 05/14] Fix #1504: Selected decompiler settings should affect tree view and tooltips --- ILSpy/LoadedAssembly.cs | 16 ++++++++++++++++ ILSpy/LoadedAssemblyExtensions.cs | 5 +++++ ILSpy/MainWindow.xaml.cs | 6 ++++++ ILSpy/Metadata/CorTables/EventTableTreeNode.cs | 2 +- ILSpy/Metadata/CorTables/FieldTableTreeNode.cs | 2 +- ILSpy/Metadata/CorTables/MethodTableTreeNode.cs | 2 +- .../Metadata/CorTables/PropertyTableTreeNode.cs | 2 +- ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs | 2 +- ILSpy/Search/LiteralSearchStrategy.cs | 2 +- ILSpy/Search/MemberSearchStrategy.cs | 2 +- ILSpy/Search/MetadataTokenSearchStrategy.cs | 2 +- ILSpy/Search/NamespaceSearchStrategy.cs | 2 +- ILSpy/TreeNodes/AssemblyTreeNode.cs | 2 +- ILSpy/TreeNodes/BaseTypesEntryNode.cs | 2 +- 14 files changed, 38 insertions(+), 11 deletions(-) diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 0e3051126..479e67e86 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -115,6 +115,22 @@ namespace ICSharpCode.ILSpy MinimalCorlib.Instance); } + ICompilation typeSystemWithOptions; + TypeSystemOptions currentTypeSystemOptions; + + public ICompilation GetTypeSystemOrNull(TypeSystemOptions options) + { + if (typeSystemWithOptions != null && options == currentTypeSystemOptions) + return typeSystemWithOptions; + var module = GetPEFileOrNull(); + if (module == null) + return null; + currentTypeSystemOptions = options; + return typeSystemWithOptions = new SimpleCompilation( + module.WithOptions(options | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers), + MinimalCorlib.Instance); + } + public AssemblyList AssemblyList => assemblyList; public string FileName => fileName; diff --git a/ILSpy/LoadedAssemblyExtensions.cs b/ILSpy/LoadedAssemblyExtensions.cs index c3bceb62b..f2b9c15ed 100644 --- a/ILSpy/LoadedAssemblyExtensions.cs +++ b/ILSpy/LoadedAssemblyExtensions.cs @@ -45,6 +45,11 @@ namespace ICSharpCode.ILSpy return GetLoadedAssembly(file).GetTypeSystemOrNull(); } + public static ICompilation GetTypeSystemWithCurrentOptionsOrNull(this PEFile file) + { + return GetLoadedAssembly(file).GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(new DecompilationOptions().DecompilerSettings)); + } + public static LoadedAssembly GetLoadedAssembly(this PEFile file) { if (file == null) diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 6a144c7c4..ec23b1740 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -662,6 +662,7 @@ namespace ICSharpCode.ILSpy void filterSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) { + RefreshTreeView(); RefreshTreeViewFilter(); if (e.PropertyName == "Language" || e.PropertyName == "LanguageVersion") { DecompileSelectedNodes(recordHistory: false); @@ -944,6 +945,11 @@ namespace ICSharpCode.ILSpy } void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + RefreshTreeView(); + } + + void RefreshTreeView() { try { refreshInProgress = true; diff --git a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs index b327f0aa0..40504dccd 100644 --- a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs @@ -98,7 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata public string Name => metadata.GetString(eventDef.Name); - IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); [StringFormat("X8")] public int Type => MetadataTokens.GetToken(eventDef.Type); diff --git a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs index 96a9bf4b7..7e2b89de6 100644 --- a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs @@ -104,7 +104,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(fieldDef.Name):X} \"{Name}\""; - IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(fieldDef.Signature); diff --git a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs index 013f67dd9..590662382 100644 --- a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs @@ -130,7 +130,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); public MethodDefEntry(PEFile module, MethodDefinitionHandle handle) { diff --git a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs index 4b7062f50..1877b7ae7 100644 --- a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs @@ -98,7 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(propertyDef.Name):X} \"{Name}\""; - IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); [StringFormat("X")] public int Signature => MetadataTokens.GetHeapOffset(propertyDef.Signature); diff --git a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs index cfd02edfc..4d5e605c4 100644 --- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs @@ -165,7 +165,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); public TypeDefEntry(PEFile module, TypeDefinitionHandle handle) { diff --git a/ILSpy/Search/LiteralSearchStrategy.cs b/ILSpy/Search/LiteralSearchStrategy.cs index c369d8f6f..a20c5909c 100644 --- a/ILSpy/Search/LiteralSearchStrategy.cs +++ b/ILSpy/Search/LiteralSearchStrategy.cs @@ -56,7 +56,7 @@ namespace ICSharpCode.ILSpy.Search { cancellationToken.ThrowIfCancellationRequested(); var metadata = module.Metadata; - var typeSystem = module.GetTypeSystemOrNull(); + var typeSystem = module.GetTypeSystemWithCurrentOptionsOrNull(); if (typeSystem == null) return; foreach (var handle in metadata.MethodDefinitions) { diff --git a/ILSpy/Search/MemberSearchStrategy.cs b/ILSpy/Search/MemberSearchStrategy.cs index fa1a11662..cbeae69e4 100644 --- a/ILSpy/Search/MemberSearchStrategy.cs +++ b/ILSpy/Search/MemberSearchStrategy.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.ILSpy.Search { cancellationToken.ThrowIfCancellationRequested(); var metadata = module.Metadata; - var typeSystem = module.GetTypeSystemOrNull(); + var typeSystem = module.GetTypeSystemWithCurrentOptionsOrNull(); if (typeSystem == null) return; if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Type) { diff --git a/ILSpy/Search/MetadataTokenSearchStrategy.cs b/ILSpy/Search/MetadataTokenSearchStrategy.cs index b2eec37df..47f6fa070 100644 --- a/ILSpy/Search/MetadataTokenSearchStrategy.cs +++ b/ILSpy/Search/MetadataTokenSearchStrategy.cs @@ -26,7 +26,7 @@ namespace ICSharpCode.ILSpy.Search { cancellationToken.ThrowIfCancellationRequested(); if (searchTermToken.IsNil) return; - var typeSystem = module.GetTypeSystemOrNull(); + var typeSystem = module.GetTypeSystemWithCurrentOptionsOrNull(); if (typeSystem == null) return; var metadataModule = (MetadataModule)typeSystem.MainModule; int row = module.Metadata.GetRowNumber(searchTermToken); diff --git a/ILSpy/Search/NamespaceSearchStrategy.cs b/ILSpy/Search/NamespaceSearchStrategy.cs index 6aca2973a..ffd255139 100644 --- a/ILSpy/Search/NamespaceSearchStrategy.cs +++ b/ILSpy/Search/NamespaceSearchStrategy.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy.Search public override void Search(PEFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var typeSystem = module.GetTypeSystemOrNull(); + var typeSystem = module.GetTypeSystemWithCurrentOptionsOrNull(); if (typeSystem == null) return; var root = ((MetadataModule)typeSystem.MainModule).RootNamespace; diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 33a5ad24a..5f25530f9 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -139,7 +139,7 @@ namespace ICSharpCode.ILSpy.TreeNodes // if we crashed on loading, then we don't have any children return; } - typeSystem = LoadedAssembly.GetTypeSystemOrNull(); + typeSystem = LoadedAssembly.GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(new DecompilationOptions().DecompilerSettings)); var assembly = (MetadataModule)typeSystem.MainModule; var metadata = module.Metadata; this.Children.Add(new Metadata.MetadataTreeNode(module, this)); diff --git a/ILSpy/TreeNodes/BaseTypesEntryNode.cs b/ILSpy/TreeNodes/BaseTypesEntryNode.cs index df8121e84..ba63adf26 100644 --- a/ILSpy/TreeNodes/BaseTypesEntryNode.cs +++ b/ILSpy/TreeNodes/BaseTypesEntryNode.cs @@ -54,7 +54,7 @@ namespace ICSharpCode.ILSpy.TreeNodes var t = typeSystem.MainModule.ResolveEntity(handle) as ITypeDefinition; if (t != null) { showExpander = t.DirectBaseTypes.Any(); - var other = t.ParentModule.PEFile.GetTypeSystemOrNull(); + var other = t.ParentModule.PEFile.GetTypeSystemWithCurrentOptionsOrNull(); Debug.Assert(other != null); t = other.FindType(t.FullTypeName).GetDefinition(); } else { From ecda21f7f978b13b067ac33bcd9f64332002f91c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 29 Feb 2020 12:27:35 +0100 Subject: [PATCH 06/14] Fix #1932: Add ExportToolPaneAttribute and reimplement ShowInBottomPane and ShowInTopPane. --- ILSpy/Analyzers/AnalyzerTreeView.cs | 9 +- ILSpy/Commands/DecompileInNewViewCommand.cs | 2 +- ILSpy/Commands/ExportCommandAttribute.cs | 20 +++- ILSpy/Commands/ShowDebugSteps.cs | 2 +- ILSpy/Commands/SortAssemblyListCommand.cs | 6 +- ILSpy/Docking/DockWorkspace.cs | 93 +++++++++++------ ILSpy/ILSpy.csproj | 2 +- ILSpy/IPane.cs | 26 ----- ILSpy/Languages/ILAstLanguage.cs | 2 +- ILSpy/MainWindow.xaml | 19 +--- ILSpy/MainWindow.xaml.cs | 110 ++++++++++++++------ ILSpy/Search/SearchPane.cs | 10 +- ILSpy/ViewModels/AnalyzerPaneModel.cs | 7 +- ILSpy/ViewModels/AssemblyListPaneModel.cs | 6 +- ILSpy/ViewModels/DebugStepsPaneModel.cs | 7 +- ILSpy/ViewModels/LegacyToolPaneModel.cs | 32 ++++++ ILSpy/ViewModels/SearchPaneModel.cs | 7 +- ILSpy/ViewModels/ToolPaneModel.cs | 4 + ILSpy/Views/DebugSteps.xaml.cs | 14 +-- 19 files changed, 225 insertions(+), 153 deletions(-) delete mode 100644 ILSpy/IPane.cs create mode 100644 ILSpy/ViewModels/LegacyToolPaneModel.cs diff --git a/ILSpy/Analyzers/AnalyzerTreeView.cs b/ILSpy/Analyzers/AnalyzerTreeView.cs index edbdf43cf..8bc21a665 100644 --- a/ILSpy/Analyzers/AnalyzerTreeView.cs +++ b/ILSpy/Analyzers/AnalyzerTreeView.cs @@ -32,7 +32,7 @@ namespace ICSharpCode.ILSpy.Analyzers /// /// Analyzer tree view. /// - public class AnalyzerTreeView : SharpTreeView, IPane + public class AnalyzerTreeView : SharpTreeView { public AnalyzerTreeView() { @@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy.Analyzers public void Show() { - AnalyzerPaneModel.Instance.Show(); + DockWorkspace.Instance.ShowToolPane(AnalyzerPaneModel.PaneContentId); } public void Show(AnalyzerTreeNode node) @@ -122,11 +122,6 @@ namespace ICSharpCode.ILSpy.Analyzers throw new ArgumentOutOfRangeException(nameof(entity), $"Entity {entity.GetType().FullName} is not supported."); } } - - void IPane.Closed() - { - this.Root.Children.Clear(); - } sealed class AnalyzerRootNode : AnalyzerTreeNode { diff --git a/ILSpy/Commands/DecompileInNewViewCommand.cs b/ILSpy/Commands/DecompileInNewViewCommand.cs index 35facb748..3069d7c6b 100644 --- a/ILSpy/Commands/DecompileInNewViewCommand.cs +++ b/ILSpy/Commands/DecompileInNewViewCommand.cs @@ -49,7 +49,7 @@ namespace ICSharpCode.ILSpy.Commands IEnumerable GetNodes(TextViewContext context) { if (context.SelectedTreeNodes != null) { - if (context.TreeView != MainWindow.Instance.treeView) { + if (context.TreeView != MainWindow.Instance.AssemblyTreeView) { return context.SelectedTreeNodes.OfType().Select(FindTreeNode).Where(n => n != null); } else { return context.SelectedTreeNodes.OfType().Where(n => n != null); diff --git a/ILSpy/Commands/ExportCommandAttribute.cs b/ILSpy/Commands/ExportCommandAttribute.cs index 2c8f3962f..eaa296682 100644 --- a/ILSpy/Commands/ExportCommandAttribute.cs +++ b/ILSpy/Commands/ExportCommandAttribute.cs @@ -30,7 +30,6 @@ namespace ICSharpCode.ILSpy string ToolbarCategory { get; } object Tag { get; } double ToolbarOrder { get; } - } [MetadataAttribute] @@ -85,4 +84,23 @@ namespace ICSharpCode.ILSpy public double MenuOrder { get; set; } } #endregion + + #region Tool Panes + public interface IToolPaneMetadata + { + string ContentId { get; } + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ExportToolPaneAttribute : ExportAttribute, IToolPaneMetadata + { + public ExportToolPaneAttribute() + : base("ToolPane", typeof(ViewModels.ToolPaneModel)) + { + } + + public string ContentId { get; set; } + } + #endregion } diff --git a/ILSpy/Commands/ShowDebugSteps.cs b/ILSpy/Commands/ShowDebugSteps.cs index a5c3d98fd..bb2ead201 100644 --- a/ILSpy/Commands/ShowDebugSteps.cs +++ b/ILSpy/Commands/ShowDebugSteps.cs @@ -11,7 +11,7 @@ namespace ICSharpCode.ILSpy.Commands { public override void Execute(object parameter) { - DebugStepsPaneModel.Instance.Show(); + DockWorkspace.Instance.ShowToolPane(DebugStepsPaneModel.PaneContentId); } } } diff --git a/ILSpy/Commands/SortAssemblyListCommand.cs b/ILSpy/Commands/SortAssemblyListCommand.cs index 5347f61cc..2222518a6 100644 --- a/ILSpy/Commands/SortAssemblyListCommand.cs +++ b/ILSpy/Commands/SortAssemblyListCommand.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy { public override void Execute(object parameter) { - using (MainWindow.Instance.treeView.LockUpdates()) + using (MainWindow.Instance.AssemblyTreeView.LockUpdates()) MainWindow.Instance.CurrentAssemblyList.Sort(this); } @@ -45,8 +45,8 @@ namespace ICSharpCode.ILSpy { public override void Execute(object parameter) { - using (MainWindow.Instance.treeView.LockUpdates()) - CollapseChildren(MainWindow.Instance.treeView.Root); + using (MainWindow.Instance.AssemblyTreeView.LockUpdates()) + CollapseChildren(MainWindow.Instance.AssemblyTreeView.Root); void CollapseChildren(SharpTreeNode node) { diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index 8bc68e07b..f94ad7eff 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -22,6 +22,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.Composition; using System.Linq; +using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -34,7 +35,7 @@ using Xceed.Wpf.AvalonDock.Layout.Serialization; namespace ICSharpCode.ILSpy.Docking { - public class DockWorkspace : INotifyPropertyChanged + public class DockWorkspace : INotifyPropertyChanged, ILayoutUpdateStrategy { private SessionSettings sessionSettings; @@ -58,21 +59,16 @@ namespace ICSharpCode.ILSpy.Docking public PaneCollection TabPages { get; } = new PaneCollection(); - private ToolPaneModel[] toolPanes; - public IEnumerable ToolPanes { - get { - if (toolPanes == null) { - toolPanes = new ToolPaneModel[] { - AssemblyListPaneModel.Instance, - SearchPaneModel.Instance, - AnalyzerPaneModel.Instance, -#if DEBUG - DebugStepsPaneModel.Instance, -#endif - }; - } - return toolPanes; + public ObservableCollection ToolPanes { get; } = new ObservableCollection(); + + public bool ShowToolPane(string contentId) + { + var pane = ToolPanes.FirstOrDefault(p => p.ContentId == contentId); + if (pane != null) { + pane.Show(); + return true; } + return false; } public void Remove(PaneModel model) @@ -103,6 +99,7 @@ namespace ICSharpCode.ILSpy.Docking public void InitializeLayout(Xceed.Wpf.AvalonDock.DockingManager manager) { + manager.LayoutUpdateStrategy = this; XmlLayoutSerializer serializer = new XmlLayoutSerializer(manager); serializer.LayoutSerializationCallback += LayoutSerializationCallback; try { @@ -116,25 +113,8 @@ namespace ICSharpCode.ILSpy.Docking { switch (e.Model) { case LayoutAnchorable la: - switch (la.ContentId) { - case AssemblyListPaneModel.PaneContentId: - e.Content = AssemblyListPaneModel.Instance; - break; - case SearchPaneModel.PaneContentId: - e.Content = SearchPaneModel.Instance; - break; - case AnalyzerPaneModel.PaneContentId: - e.Content = AnalyzerPaneModel.Instance; - break; -#if DEBUG - case DebugStepsPaneModel.PaneContentId: - e.Content = DebugStepsPaneModel.Instance; - break; -#endif - default: - e.Cancel = true; - break; - } + e.Content = ToolPanes.FirstOrDefault(p => p.ContentId == la.ContentId); + e.Cancel = e.Content == null; la.CanDockAsTabbedDocument = false; if (!e.Cancel) { e.Cancel = ((ToolPaneModel)e.Content).IsVisible; @@ -203,5 +183,50 @@ namespace ICSharpCode.ILSpy.Docking InitializeLayout(MainWindow.Instance.DockManager); MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.RefreshDecompiledView); } + + static readonly PropertyInfo previousContainerProperty = typeof(LayoutContent).GetProperty("PreviousContainer", BindingFlags.NonPublic | BindingFlags.Instance); + + public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer) + { + if (!(anchorableToShow.Content is LegacyToolPaneModel legacyContent)) + return false; + anchorableToShow.CanDockAsTabbedDocument = false; + + LayoutAnchorablePane previousContainer; + switch (legacyContent.Location) { + case LegacyToolPaneLocation.Top: + previousContainer = GetContainer(); + previousContainer.Children.Add(anchorableToShow); + return true; + case LegacyToolPaneLocation.Bottom: + previousContainer = GetContainer(); + previousContainer.Children.Add(anchorableToShow); + return true; + default: + return false; + } + + LayoutAnchorablePane GetContainer() + { + var anchorable = layout.Descendents().OfType().FirstOrDefault(x => x.Content is T) + ?? layout.Hidden.First(x => x.Content is T); + return (LayoutAnchorablePane)previousContainerProperty.GetValue(anchorable) ?? (LayoutAnchorablePane)anchorable.Parent; + } + } + + public void AfterInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableShown) + { + anchorableShown.IsActive = true; + anchorableShown.IsSelected = true; + } + + public bool BeforeInsertDocument(LayoutRoot layout, LayoutDocument anchorableToShow, ILayoutContainer destinationContainer) + { + return false; + } + + public void AfterInsertDocument(LayoutRoot layout, LayoutDocument anchorableShown) + { + } } } diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index fc1bfc95b..633c777ff 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -208,6 +208,7 @@ + @@ -248,7 +249,6 @@ - diff --git a/ILSpy/IPane.cs b/ILSpy/IPane.cs deleted file mode 100644 index d429b6cb2..000000000 --- a/ILSpy/IPane.cs +++ /dev/null @@ -1,26 +0,0 @@ -// 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. - - -namespace ICSharpCode.ILSpy -{ - public interface IPane - { - void Closed(); - } -} diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index d7c100990..5aca29d64 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy } } (output as ISmartTextOutput)?.AddButton(Images.ViewCode, "Show Steps", delegate { - DebugStepsPaneModel.Instance.Show(); + Docking.DockWorkspace.Instance.ShowToolPane(DebugStepsPaneModel.PaneContentId); }); output.WriteLine(); il.WriteTo(output, DebugSteps.Options); diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 3eb590066..479293aa4 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -16,7 +16,6 @@ MinHeight="200" UseLayoutRounding="True" TextOptions.TextFormattingMode="Display" - FocusManager.FocusedElement="{Binding ElementName=treeView}" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d:DesignHeight="500" d:DesignWidth="500" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" > @@ -24,7 +23,7 @@ - - + @@ -169,7 +168,7 @@ @@ -185,18 +184,6 @@ - - - - - - - - - - - - diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index ec23b1740..5ef3f010f 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -84,9 +84,9 @@ namespace ICSharpCode.ILSpy internal AssemblyListManager AssemblyListManager { get; } - public SharpTreeView treeView { + public SharpTreeView AssemblyTreeView { get { - return FindResource("TreeView") as SharpTreeView; + return FindResource("AssemblyTreeView") as SharpTreeView; } } @@ -120,13 +120,13 @@ namespace ICSharpCode.ILSpy DockWorkspace.Instance.LoadSettings(sessionSettings); InitializeComponent(); + InitToolPanes(); DockWorkspace.Instance.InitializeLayout(DockManager); sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged; sessionSettings.PropertyChanged += SessionSettings_PropertyChanged; - InitMainMenu(); InitToolbar(); - ContextMenuProvider.Add(treeView); + ContextMenuProvider.Add(AssemblyTreeView); this.Loaded += MainWindow_Loaded; } @@ -231,6 +231,48 @@ namespace ICSharpCode.ILSpy } #endregion + #region Tool Pane extensibility + private void InitToolPanes() + { + var toolPanes = App.ExportProvider.GetExports("ToolPane"); + var templateSelector = new PaneTemplateSelector(); + templateSelector.Mappings.Add(new TemplateMapping { + Type = typeof(TabPageModel), + Template = (DataTemplate)FindResource("DefaultContentTemplate") + }); + templateSelector.Mappings.Add(new TemplateMapping { + Type = typeof(LegacyToolPaneModel), + Template = (DataTemplate)FindResource("DefaultContentTemplate") + }); + foreach (var toolPane in toolPanes) { + ToolPaneModel model = toolPane.Value; + templateSelector.Mappings.Add(new TemplateMapping { Type = model.GetType(), Template = model.Template }); + DockWorkspace.Instance.ToolPanes.Add(model); + } + DockManager.LayoutItemTemplateSelector = templateSelector; + } + + public void ShowInTopPane(string title, object content) + { + var model = DockWorkspace.Instance.ToolPanes.OfType().FirstOrDefault(p => p.Content == content); + if (model == null) { + model = new LegacyToolPaneModel(title, content, LegacyToolPaneLocation.Top); + DockWorkspace.Instance.ToolPanes.Add(model); + } + model.Show(); + } + + public void ShowInBottomPane(string title, object content) + { + var model = DockWorkspace.Instance.ToolPanes.OfType().FirstOrDefault(p => p.Content == content); + if (model == null) { + model = new LegacyToolPaneModel(title, content, LegacyToolPaneLocation.Bottom); + DockWorkspace.Instance.ToolPanes.Add(model); + } + model.Show(); + } + #endregion + #region Message Hook protected override void OnSourceInitialized(EventArgs e) { @@ -337,7 +379,7 @@ namespace ICSharpCode.ILSpy async void NavigateOnLaunch(string navigateTo, string[] activeTreeViewPath, ILSpySettings spySettings, List relevantAssemblies) { - var initialSelection = treeView.SelectedItem; + var initialSelection = AssemblyTreeView.SelectedItem; if (navigateTo != null) { bool found = false; if (navigateTo.StartsWith("N:", StringComparison.Ordinal)) { @@ -351,7 +393,7 @@ namespace ICSharpCode.ILSpy NamespaceTreeNode nsNode = asmNode.FindNamespaceNode(namespaceName); if (nsNode != null) { found = true; - if (treeView.SelectedItem == initialSelection) { + if (AssemblyTreeView.SelectedItem == initialSelection) { SelectNode(nsNode); } break; @@ -366,12 +408,12 @@ namespace ICSharpCode.ILSpy IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies)); if (mr != null && mr.ParentModule.PEFile != null) { found = true; - if (treeView.SelectedItem == initialSelection) { + if (AssemblyTreeView.SelectedItem == initialSelection) { JumpToReference(mr); } } } - if (!found && treeView.SelectedItem == initialSelection) { + if (!found && AssemblyTreeView.SelectedItem == initialSelection) { AvalonEditTextOutput output = new AvalonEditTextOutput(); output.Write(string.Format("Cannot find '{0}' in command line specified assemblies.", navigateTo)); DockWorkspace.Instance.ShowText(output); @@ -380,7 +422,7 @@ namespace ICSharpCode.ILSpy // NavigateTo == null and an assembly was given on the command-line: // Select the newly loaded assembly AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(relevantAssemblies[0]); - if (asmNode != null && treeView.SelectedItem == initialSelection) { + if (asmNode != null && AssemblyTreeView.SelectedItem == initialSelection) { SelectNode(asmNode); } } else if (spySettings != null) { @@ -395,7 +437,7 @@ namespace ICSharpCode.ILSpy } node = FindNodeByPath(activeTreeViewPath, true); } - if (treeView.SelectedItem == initialSelection) { + if (AssemblyTreeView.SelectedItem == initialSelection) { if (node != null) { SelectNode(node); @@ -611,7 +653,7 @@ namespace ICSharpCode.ILSpy assemblyListTreeNode = new AssemblyListTreeNode(assemblyList); assemblyListTreeNode.FilterSettings = sessionSettings.FilterSettings.Clone(); assemblyListTreeNode.Select = x => SelectNode(x, inNewTabPage: false); - treeView.Root = assemblyListTreeNode; + AssemblyTreeView.Root = assemblyListTreeNode; if (assemblyList.ListName == AssemblyListManager.DefaultListName) #if DEBUG @@ -695,12 +737,12 @@ namespace ICSharpCode.ILSpy LanguageVersion = CurrentLanguageVersion }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); - treeView.SelectedItem = null; + AssemblyTreeView.SelectedItem = null; } // Set both the selection and focus to ensure that keyboard navigation works as expected. - treeView.FocusNode(obj); - treeView.SelectedItem = obj; + AssemblyTreeView.FocusNode(obj); + AssemblyTreeView.SelectedItem = obj; } else { MessageBox.Show("Navigation failed because the target is hidden or a compiler-generated class.\n" + "Please disable all filters that might hide the item (i.e. activate " + @@ -722,8 +764,8 @@ namespace ICSharpCode.ILSpy DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); } - treeView.FocusNode(nodes.First()); - treeView.SetSelectedNodes(nodes); + AssemblyTreeView.FocusNode(nodes.First()); + AssemblyTreeView.SetSelectedNodes(nodes); } } @@ -734,7 +776,7 @@ namespace ICSharpCode.ILSpy { if (path == null) return null; - SharpTreeNode node = treeView.Root; + SharpTreeNode node = AssemblyTreeView.Root; SharpTreeNode bestMatch = node; foreach (var element in path) { if (node == null) @@ -887,7 +929,7 @@ namespace ICSharpCode.ILSpy throw new ArgumentNullException(nameof(fileNames)); if (focusNode) - treeView.UnselectAll(); + AssemblyTreeView.UnselectAll(); LoadAssemblies(fileNames, focusNode: focusNode); } @@ -913,7 +955,7 @@ namespace ICSharpCode.ILSpy else { var node = assemblyListTreeNode.FindAssemblyNode(nugetAsm); if (node != null && focusNode) { - treeView.SelectedItems.Add(node); + AssemblyTreeView.SelectedItems.Add(node); lastNode = node; } } @@ -931,7 +973,7 @@ namespace ICSharpCode.ILSpy else { var node = assemblyListTreeNode.FindAssemblyNode(asm); if (node != null && focusNode) { - treeView.SelectedItems.Add(node); + AssemblyTreeView.SelectedItems.Add(node); lastNode = node; } } @@ -940,7 +982,7 @@ namespace ICSharpCode.ILSpy } if (lastNode != null && focusNode) - treeView.FocusNode(lastNode); + AssemblyTreeView.FocusNode(lastNode); } } @@ -953,7 +995,7 @@ namespace ICSharpCode.ILSpy { try { refreshInProgress = true; - var path = GetPathForNode(treeView.SelectedItem as SharpTreeNode); + var path = GetPathForNode(AssemblyTreeView.SelectedItem as SharpTreeNode); ShowAssemblyList(AssemblyListManager.LoadList(ILSpySettings.Load(), assemblyList.ListName)); SelectNode(FindNodeByPath(path, true)); } finally { @@ -963,7 +1005,7 @@ namespace ICSharpCode.ILSpy void SearchCommandExecuted(object sender, ExecutedRoutedEventArgs e) { - SearchPaneModel.Instance.Show(); + DockWorkspace.Instance.ShowToolPane(SearchPaneModel.PaneContentId); } #endregion @@ -983,20 +1025,20 @@ namespace ICSharpCode.ILSpy if (ignoreDecompilationRequests) return; - if (treeView.SelectedItems.Count == 0 && refreshInProgress) + if (AssemblyTreeView.SelectedItems.Count == 0 && refreshInProgress) return; if (recordHistory) { var currentState = DockWorkspace.Instance.ActiveTabPage.GetState(); if (currentState != null) history.UpdateCurrent(new NavigationState(currentState)); - history.Record(new NavigationState(treeView.SelectedItems.OfType())); + history.Record(new NavigationState(AssemblyTreeView.SelectedItems.OfType())); } DockWorkspace.Instance.ActiveTabPage.SupportsLanguageSwitching = true; - if (treeView.SelectedItems.Count == 1) { - ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; + if (AssemblyTreeView.SelectedItems.Count == 1) { + ILSpyTreeNode node = AssemblyTreeView.SelectedItem as ILSpyTreeNode; if (node != null && node.View(DockWorkspace.Instance.ActiveTabPage)) return; } @@ -1035,7 +1077,7 @@ namespace ICSharpCode.ILSpy public IEnumerable SelectedNodes { get { - return treeView.GetTopLevelSelection().OfType(); + return AssemblyTreeView.GetTopLevelSelection().OfType(); } } #endregion @@ -1077,12 +1119,12 @@ namespace ICSharpCode.ILSpy var newState = forward ? history.GoForward() : history.GoBack(); ignoreDecompilationRequests = true; - treeView.SelectedItems.Clear(); + AssemblyTreeView.SelectedItems.Clear(); foreach (var node in newState.TreeNodes) { - treeView.SelectedItems.Add(node); + AssemblyTreeView.SelectedItems.Add(node); } if (newState.TreeNodes.Any()) - treeView.FocusNode(newState.TreeNodes.First()); + AssemblyTreeView.FocusNode(newState.TreeNodes.First()); ignoreDecompilationRequests = false; DecompileSelectedNodes(newState.ViewState as DecompilerTextViewState, false); } @@ -1142,8 +1184,8 @@ namespace ICSharpCode.ILSpy { base.OnClosing(e); sessionSettings.ActiveAssemblyList = assemblyList.ListName; - sessionSettings.ActiveTreeViewPath = GetPathForNode(treeView.SelectedItem as SharpTreeNode); - sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(treeView.SelectedItem as SharpTreeNode); + sessionSettings.ActiveTreeViewPath = GetPathForNode(AssemblyTreeView.SelectedItem as SharpTreeNode); + sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(AssemblyTreeView.SelectedItem as SharpTreeNode); sessionSettings.WindowBounds = this.RestoreBounds; sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(DockManager)); sessionSettings.Save(); @@ -1167,7 +1209,7 @@ namespace ICSharpCode.ILSpy public void UnselectAll() { - treeView.UnselectAll(); + AssemblyTreeView.UnselectAll(); } public void SetStatus(string status, Brush foreground) diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 6fc3862c5..c74173c1c 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -23,6 +23,7 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -40,7 +41,7 @@ namespace ICSharpCode.ILSpy /// /// Search pane /// - public partial class SearchPane : UserControl, IPane + public partial class SearchPane : UserControl { const int MAX_RESULTS = 1000; const int MAX_REFRESH_TIME_MS = 10; // More means quicker forward of data, less means better responsibility @@ -105,7 +106,7 @@ namespace ICSharpCode.ILSpy public void Show() { if (!IsVisible) { - SearchPaneModel.Instance.IsVisible = true; + DockWorkspace.Instance.ToolPanes.Single(p => p.ContentId == SearchPaneModel.PaneContentId).IsVisible = true; if (runSearchOnNextShow) { runSearchOnNextShow = false; StartSearch(this.SearchTerm); @@ -139,11 +140,6 @@ namespace ICSharpCode.ILSpy MainWindow.Instance.SessionSettings.SelectedSearchMode = (SearchMode)searchModeComboBox.SelectedIndex; StartSearch(this.SearchTerm); } - - void IPane.Closed() - { - this.SearchTerm = string.Empty; - } void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e) { diff --git a/ILSpy/ViewModels/AnalyzerPaneModel.cs b/ILSpy/ViewModels/AnalyzerPaneModel.cs index 57c4513fd..28b07734c 100644 --- a/ILSpy/ViewModels/AnalyzerPaneModel.cs +++ b/ILSpy/ViewModels/AnalyzerPaneModel.cs @@ -16,18 +16,21 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Windows; + namespace ICSharpCode.ILSpy.ViewModels { + [ExportToolPane(ContentId = PaneContentId)] public class AnalyzerPaneModel : ToolPaneModel { public const string PaneContentId = "analyzerPane"; - public static AnalyzerPaneModel Instance { get; } = new AnalyzerPaneModel(); - private AnalyzerPaneModel() { ContentId = PaneContentId; Title = Properties.Resources.Analyze; } + + public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("AnalyzerPaneTemplate"); } } diff --git a/ILSpy/ViewModels/AssemblyListPaneModel.cs b/ILSpy/ViewModels/AssemblyListPaneModel.cs index 009958d41..386cc1c1d 100644 --- a/ILSpy/ViewModels/AssemblyListPaneModel.cs +++ b/ILSpy/ViewModels/AssemblyListPaneModel.cs @@ -16,21 +16,23 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Windows; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy.ViewModels { + [ExportToolPane(ContentId = PaneContentId)] public class AssemblyListPaneModel : ToolPaneModel { public const string PaneContentId = "assemblyListPane"; - public static AssemblyListPaneModel Instance { get; } = new AssemblyListPaneModel(); - private AssemblyListPaneModel() { Title = Resources.Assemblies; ContentId = PaneContentId; IsCloseable = false; } + + public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("AssemblyListPaneTemplate"); } } diff --git a/ILSpy/ViewModels/DebugStepsPaneModel.cs b/ILSpy/ViewModels/DebugStepsPaneModel.cs index f7e9f2faf..7ea45bb6a 100644 --- a/ILSpy/ViewModels/DebugStepsPaneModel.cs +++ b/ILSpy/ViewModels/DebugStepsPaneModel.cs @@ -16,18 +16,21 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Windows; + namespace ICSharpCode.ILSpy.ViewModels { + [ExportToolPane(ContentId = PaneContentId)] public class DebugStepsPaneModel : ToolPaneModel { public const string PaneContentId = "debugStepsPane"; - public static DebugStepsPaneModel Instance { get; } = new DebugStepsPaneModel(); - private DebugStepsPaneModel() { ContentId = PaneContentId; Title = Properties.Resources.DebugSteps; } + + public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("DebugStepsPaneTemplate"); } } diff --git a/ILSpy/ViewModels/LegacyToolPaneModel.cs b/ILSpy/ViewModels/LegacyToolPaneModel.cs new file mode 100644 index 000000000..40ecc4cb2 --- /dev/null +++ b/ILSpy/ViewModels/LegacyToolPaneModel.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace ICSharpCode.ILSpy.ViewModels +{ + internal enum LegacyToolPaneLocation + { + Top, + Bottom + } + + internal class LegacyToolPaneModel : ToolPaneModel + { + public LegacyToolPaneModel(string title, object content, LegacyToolPaneLocation location) + { + this.Title = title; + this.Content = content; + this.IsCloseable = true; + this.Location = location; + } + + public object Content { get; } + + public override DataTemplate Template => throw new NotSupportedException(); + + public LegacyToolPaneLocation Location { get; } + } +} diff --git a/ILSpy/ViewModels/SearchPaneModel.cs b/ILSpy/ViewModels/SearchPaneModel.cs index ac1208281..66a156825 100644 --- a/ILSpy/ViewModels/SearchPaneModel.cs +++ b/ILSpy/ViewModels/SearchPaneModel.cs @@ -16,14 +16,15 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Windows; + namespace ICSharpCode.ILSpy.ViewModels { + [ExportToolPane(ContentId = PaneContentId)] public class SearchPaneModel : ToolPaneModel { public const string PaneContentId = "searchPane"; - public static SearchPaneModel Instance { get; } = new SearchPaneModel(); - private SearchPaneModel() { ContentId = PaneContentId; @@ -36,5 +37,7 @@ namespace ICSharpCode.ILSpy.ViewModels base.Show(); MainWindow.Instance.SearchPane.Show(); } + + public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("SearchPaneTemplate"); } } diff --git a/ILSpy/ViewModels/ToolPaneModel.cs b/ILSpy/ViewModels/ToolPaneModel.cs index f28574d25..672273674 100644 --- a/ILSpy/ViewModels/ToolPaneModel.cs +++ b/ILSpy/ViewModels/ToolPaneModel.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Windows; + namespace ICSharpCode.ILSpy.ViewModels { public abstract class ToolPaneModel : PaneModel @@ -25,5 +27,7 @@ namespace ICSharpCode.ILSpy.ViewModels this.IsActive = true; this.IsVisible = true; } + + public abstract DataTemplate Template { get; } } } diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index 1c7d9e5fb..d0870f496 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -9,7 +9,7 @@ using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy { - public partial class DebugSteps : UserControl, IPane + public partial class DebugSteps : UserControl { static readonly ILAstWritingOptions writingOptions = new ILAstWritingOptions { UseFieldSugar = true, @@ -79,18 +79,6 @@ namespace ICSharpCode.ILSpy #endif } - void IPane.Closed() - { -#if DEBUG - MainWindow.Instance.SessionSettings.FilterSettings.PropertyChanged -= FilterSettings_PropertyChanged; - MainWindow.Instance.SelectionChanged -= SelectionChanged; - writingOptions.PropertyChanged -= WritingOptions_PropertyChanged; - if (language != null) { - language.StepperUpdated -= ILAstStepperUpdated; - } -#endif - } - private void ShowStateAfter_Click(object sender, RoutedEventArgs e) { Stepper.Node n = (Stepper.Node)tree.SelectedItem; From bef75321fb9cde52c587fcc30639d4346b38f7f9 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 29 Feb 2020 20:40:19 +0100 Subject: [PATCH 07/14] #1913: Do not destroy control-flow by removing return statements --- .../IL/Transforms/TransformDisplayClassUsage.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index e83a37ccd..795800433 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -64,7 +64,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(context, v, out ITypeDefinition closureType, out var inst)) { if (!CanRemoveAllReferencesTo(context, v)) continue; - instructionsToRemove.Add(inst); + if (inst is StObj || inst is StLoc) + instructionsToRemove.Add(inst); AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false); continue; } From 0a983ecb9f89933852b1316dcadce65514efb9ef Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 2 Mar 2020 20:44:27 +0100 Subject: [PATCH 08/14] Fix #1929: Name missing from start menu tile --- ILSpy.Package/Package-CI.appxmanifest | 8 +++++++- ILSpy.Package/Package-Local.appxmanifest | 8 +++++++- ILSpy.Package/Package.appxmanifest | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ILSpy.Package/Package-CI.appxmanifest b/ILSpy.Package/Package-CI.appxmanifest index 2f1f75eae..a1c30a9b7 100644 --- a/ILSpy.Package/Package-CI.appxmanifest +++ b/ILSpy.Package/Package-CI.appxmanifest @@ -37,7 +37,13 @@ BackgroundColor="transparent" Square150x150Logo="Images\Square150x150Logo.png" Square44x44Logo="Images\Square44x44Logo.png"> - + + + + + + + diff --git a/ILSpy.Package/Package-Local.appxmanifest b/ILSpy.Package/Package-Local.appxmanifest index 1922193ed..b1457a491 100644 --- a/ILSpy.Package/Package-Local.appxmanifest +++ b/ILSpy.Package/Package-Local.appxmanifest @@ -37,7 +37,13 @@ BackgroundColor="transparent" Square150x150Logo="Images\Square150x150Logo.png" Square44x44Logo="Images\Square44x44Logo.png"> - + + + + + + + diff --git a/ILSpy.Package/Package.appxmanifest b/ILSpy.Package/Package.appxmanifest index 4375f716b..ac20b4264 100644 --- a/ILSpy.Package/Package.appxmanifest +++ b/ILSpy.Package/Package.appxmanifest @@ -37,7 +37,13 @@ BackgroundColor="transparent" Square150x150Logo="Images\Square150x150Logo.png" Square44x44Logo="Images\Square44x44Logo.png"> - + + + + + + + From 70b087bf8d2b2f05f440d5d22af395d5fa83a9e4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 5 Mar 2020 00:21:27 +0100 Subject: [PATCH 09/14] #1942: WholeProjectDecompiler: Do not use filenames that collide with names of special devices. Note: this changes the PDB structure from multi-level folders per namespace to "dotted name" folders. --- .../CSharp/WholeProjectDecompiler.cs | 36 ++++++++++++++++++- .../DebugInfo/PortablePdbWriter.cs | 3 +- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index 23cce2661..74409be17 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -512,7 +512,41 @@ namespace ICSharpCode.Decompiler.CSharp } if (b.Length == 0) b.Append('-'); - return b.ToString(); + string name = b.ToString(); + if (IsReservedFileSystemName(name)) + return name + "_"; + return name; + } + + static bool IsReservedFileSystemName(string name) + { + switch (name.ToUpperInvariant()) { + case "AUX": + case "COM1": + case "COM2": + case "COM3": + case "COM4": + case "COM5": + case "COM6": + case "COM7": + case "COM8": + case "COM9": + case "CON": + case "LPT1": + case "LPT2": + case "LPT3": + case "LPT4": + case "LPT5": + case "LPT6": + case "LPT7": + case "LPT8": + case "LPT9": + case "NUL": + case "PRN": + return true; + default: + return false; + } } public static string GetPlatformName(Metadata.PEFile module) diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index a329bcfbf..7a2ae24cd 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -84,7 +84,8 @@ namespace ICSharpCode.Decompiler.DebugInfo lock (metadata) { var sourceBlob = WriteSourceToBlob(metadata, sourceText, out var sourceCheckSum); - var name = metadata.GetOrAddDocumentName(type.GetFullTypeName(reader).ReflectionName.Replace('.', Path.DirectorySeparatorChar) + ".cs"); + var typeName = type.GetFullTypeName(reader).TopLevelTypeName; + var name = metadata.GetOrAddDocumentName(Path.Combine(WholeProjectDecompiler.CleanUpFileName(typeName.Namespace), WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs")); // Create Document(Handle) var document = metadata.AddDocument(name, From ed76b3823263835732fc71709ecfbee7222c4e4c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 5 Mar 2020 11:33:48 +0100 Subject: [PATCH 10/14] Make sure a treenode is selected after changing assembly list to make commands work properly. --- ILSpy/MainWindow.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 5ef3f010f..c4d3ad19f 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -640,6 +640,7 @@ namespace ICSharpCode.ILSpy //Only load a new list when it is a different one if (list.ListName != CurrentAssemblyList.ListName) { ShowAssemblyList(list); + SelectNode(AssemblyTreeView.Root); } } From 22799ca6b17b62bdc8f8c0ee38cfdd8d7bfb7e3e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 5 Mar 2020 12:03:36 +0100 Subject: [PATCH 11/14] #1942: Make sure to group all types by filename in the generated PDB. --- .../DebugInfo/PortablePdbWriter.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index 7a2ae24cd..2c26d81cc 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -62,11 +62,15 @@ namespace ICSharpCode.Decompiler.DebugInfo var customMethodDebugInfo = new List<(MethodDefinitionHandle Parent, GuidHandle Guid, BlobHandle Blob)>(); var globalImportScope = metadata.AddImportScope(default, default); - foreach (var handle in reader.GetTopLevelTypeDefinitions()) { - var type = reader.GetTypeDefinition(handle); + string BuildFileNameFromTypeName(TypeDefinitionHandle handle) + { + var typeName = handle.GetFullTypeName(reader).TopLevelTypeName; + return Path.Combine(WholeProjectDecompiler.CleanUpFileName(typeName.Namespace), WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs"); + } + foreach (var sourceFile in reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName)) { // Generate syntax tree - var syntaxTree = decompiler.DecompileTypes(new[] { handle }); + var syntaxTree = decompiler.DecompileTypes(sourceFile); if (!syntaxTree.HasChildren) continue; @@ -84,8 +88,7 @@ namespace ICSharpCode.Decompiler.DebugInfo lock (metadata) { var sourceBlob = WriteSourceToBlob(metadata, sourceText, out var sourceCheckSum); - var typeName = type.GetFullTypeName(reader).TopLevelTypeName; - var name = metadata.GetOrAddDocumentName(Path.Combine(WholeProjectDecompiler.CleanUpFileName(typeName.Namespace), WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs")); + var name = metadata.GetOrAddDocumentName(sourceFile.Key); // Create Document(Handle) var document = metadata.AddDocument(name, From a49f55ad8a9b19f57e74bbc69fd8128b122a5f1c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 5 Mar 2020 12:43:42 +0100 Subject: [PATCH 12/14] Fix build --- ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml | 2 +- ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml | 2 +- .../TestCases/PdbGen/LambdaCapturing.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml index 1d41df003..aebd4ff9f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml @@ -1,7 +1,7 @@ - - - Date: Sat, 7 Mar 2020 17:50:08 +0100 Subject: [PATCH 13/14] Fix #1943: "ref readonly" returns from properties/indexers --- .../TestCases/Pretty/RefLocalsAndReturns.cs | 10 +++++++++ .../CSharp/Syntax/TypeSystemAstBuilder.cs | 9 ++++++++ .../Transforms/NormalizeBlockStatements.cs | 21 +++++++++++++++++-- .../CSharp/WholeProjectDecompiler.cs | 9 +++++--- .../TypeSystem/IProperty.cs | 5 +++++ ICSharpCode.Decompiler/TypeSystem/ISymbol.cs | 4 ++++ .../Implementation/AttributeListBuilder.cs | 4 ++++ .../Implementation/MetadataMethod.cs | 2 +- .../Implementation/MetadataProperty.cs | 7 +++++++ .../Implementation/SpecializedProperty.cs | 2 ++ 10 files changed, 67 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index b8be46721..895efc1f1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -62,6 +62,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public struct NormalStruct { private readonly int dummy; + private int[] arr; public int Property { get { @@ -96,8 +97,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + public ref int RefProperty => ref arr[0]; + public ref readonly int RefReadonlyProperty => ref arr[0]; + public readonly ref int ReadonlyRefProperty => ref arr[0]; + public readonly ref readonly int ReadonlyRefReadonlyProperty => ref arr[0]; +#endif + + public ref readonly int this[in int index] => ref arr[index]; + public event EventHandler NormalEvent; +#if CS80 public readonly event EventHandler ReadOnlyEvent { add { } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 6776082c5..c6b563bec 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1574,6 +1574,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax decl.AddAnnotation(new MemberResolveResult(null, property)); } decl.ReturnType = ConvertType(property.ReturnType); + if (property.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { + ct.HasReadOnlySpecifier = true; + } decl.Name = property.Name; decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false); decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true); @@ -1601,6 +1604,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax decl.AddAnnotation(new MemberResolveResult(null, indexer)); } decl.ReturnType = ConvertType(indexer.ReturnType); + if (indexer.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { + ct.HasReadOnlySpecifier = true; + } foreach (IParameter p in indexer.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } @@ -1695,6 +1701,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax decl.Modifiers = GetMemberModifiers(op); decl.OperatorType = opType.Value; decl.ReturnType = ConvertType(op.ReturnType); + if (op.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { + ct.HasReadOnlySpecifier = true; + } foreach (IParameter p in op.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs index 306892735..b4b88cbc1 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -144,7 +144,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Name = Pattern.AnyString, PrivateImplementationType = new AnyNodeOrNull(), ReturnType = new AnyNode(), - Getter = new Accessor() { Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } } + Getter = new Accessor() { + Modifiers = Modifiers.Any, + Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } + } }; static readonly IndexerDeclaration CalculatedGetterOnlyIndexerPattern = new IndexerDeclaration() { @@ -153,14 +156,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms PrivateImplementationType = new AnyNodeOrNull(), Parameters = { new Repeat(new AnyNode()) }, ReturnType = new AnyNode(), - Getter = new Accessor() { Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } } + Getter = new Accessor() { + Modifiers = Modifiers.Any, + Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } + } }; + /// + /// Modifiers that are emitted on accessors, but can be moved to the property declaration. + /// + const Modifiers movableModifiers = Modifiers.Readonly; + void SimplifyPropertyDeclaration(PropertyDeclaration propertyDeclaration) { var m = CalculatedGetterOnlyPropertyPattern.Match(propertyDeclaration); if (!m.Success) return; + if ((propertyDeclaration.Getter.Modifiers & ~movableModifiers) != 0) + return; + propertyDeclaration.Modifiers |= propertyDeclaration.Getter.Modifiers; propertyDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); propertyDeclaration.Getter.Remove(); } @@ -170,6 +184,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var m = CalculatedGetterOnlyIndexerPattern.Match(indexerDeclaration); if (!m.Success) return; + if ((indexerDeclaration.Getter.Modifiers & ~movableModifiers) != 0) + return; + indexerDeclaration.Modifiers |= indexerDeclaration.Getter.Modifiers; indexerDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); indexerDeclaration.Getter.Remove(); } diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index 74409be17..769b5d33c 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -228,7 +228,7 @@ namespace ICSharpCode.Decompiler.CSharp } w.WriteElementString("WarningLevel", "4"); w.WriteElementString("AllowUnsafeBlocks", "True"); - + if (StrongNameKeyFile != null) { w.WriteElementString("SignAssembly", "True"); w.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(StrongNameKeyFile)); @@ -463,7 +463,7 @@ namespace ICSharpCode.Decompiler.CSharp } catch (EndOfStreamException) { // if the .resources can't be decoded, just save them as-is } - } + } using (FileStream fs = new FileStream(Path.Combine(targetDirectory, fileName), FileMode.Create, FileAccess.Write)) { entryStream.CopyTo(fs); } @@ -515,7 +515,10 @@ namespace ICSharpCode.Decompiler.CSharp string name = b.ToString(); if (IsReservedFileSystemName(name)) return name + "_"; - return name; + else if (name == ".") + return "_"; + else + return name; } static bool IsReservedFileSystemName(string name) diff --git a/ICSharpCode.Decompiler/TypeSystem/IProperty.cs b/ICSharpCode.Decompiler/TypeSystem/IProperty.cs index 495f7c22c..388c770e0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IProperty.cs @@ -30,5 +30,10 @@ namespace ICSharpCode.Decompiler.TypeSystem IMethod Setter { get; } bool IsIndexer { get; } + + /// + /// Gets whether the return type is 'ref readonly'. + /// + bool ReturnTypeIsRefReadOnly { get; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs b/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs index b1b59c404..b269c785a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs @@ -73,6 +73,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Constraint on a type parameter. /// Constraint, + /// + /// Return type. Not actually an ISymbol implementation; but can appear as attribut target. + /// + ReturnType, } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index 920106c37..46cb1c99d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -208,6 +208,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation case SymbolKind.Method: case SymbolKind.Accessor: return (options & TypeSystemOptions.ReadOnlyMethods) != 0; + case SymbolKind.ReturnType: + case SymbolKind.Property: + case SymbolKind.Indexer: + return true; // "ref readonly" is currently always active default: return false; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index 881933acd..87f7eb4c9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -400,7 +400,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var retParam = metadata.GetParameter(parameters.First()); if (retParam.SequenceNumber == 0) { b.AddMarshalInfo(retParam.GetMarshallingDescriptor()); - b.Add(retParam.GetCustomAttributes(), symbolKind); + b.Add(retParam.GetCustomAttributes(), SymbolKind.ReturnType); } } return b.Build(); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs index cd98cf907..7ba165859 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs @@ -113,6 +113,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } + public bool ReturnTypeIsRefReadOnly { + get { + var propertyDef = module.metadata.GetPropertyDefinition(propertyHandle); + return propertyDef.GetCustomAttributes().HasKnownAttribute(module.metadata, KnownAttribute.IsReadOnly); + } + } + private void DecodeSignature() { var propertyDef = module.metadata.GetPropertyDefinition(propertyHandle); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs index 6f7ea7ed4..3cd66fe33 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs @@ -65,5 +65,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public bool IsIndexer { get { return propertyDefinition.IsIndexer; } } + + public bool ReturnTypeIsRefReadOnly => propertyDefinition.ReturnTypeIsRefReadOnly; } } From 0c55dff1fc53b845ed221692e97fb0397bf49f41 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 7 Mar 2020 22:50:50 +0100 Subject: [PATCH 14/14] Fix #1948: Toolbar loses focus when changing language/version --- ILSpy/Languages/Languages.cs | 10 ++++++++ ILSpy/MainWindow.xaml.cs | 38 +++++++++++++++++++++++++---- ILSpy/TreeNodes/EventTreeNode.cs | 3 +-- ILSpy/TreeNodes/FieldTreeNode.cs | 3 +-- ILSpy/TreeNodes/MethodTreeNode.cs | 3 +-- ILSpy/TreeNodes/PropertyTreeNode.cs | 3 +-- ILSpy/TreeNodes/TypeTreeNode.cs | 3 +-- 7 files changed, 48 insertions(+), 15 deletions(-) diff --git a/ILSpy/Languages/Languages.cs b/ILSpy/Languages/Languages.cs index d8ef5b8ff..f8c70f46a 100644 --- a/ILSpy/Languages/Languages.cs +++ b/ILSpy/Languages/Languages.cs @@ -60,5 +60,15 @@ namespace ICSharpCode.ILSpy { return AllLanguages.FirstOrDefault(l => l.Name == name) ?? AllLanguages.First(); } + + static ILLanguage ilLanguage; + public static ILLanguage ILLanguage { + get { + if (ilLanguage == null) { + ilLanguage = (ILLanguage)GetLanguage("IL"); + } + return ilLanguage; + } + } } } diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index c4d3ad19f..b72cb2454 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -727,7 +727,17 @@ namespace ICSharpCode.ILSpy #region Node Selection - public void SelectNode(SharpTreeNode obj, bool inNewTabPage = false) + public void SelectNode(SharpTreeNode obj) + { + SelectNode(obj, false); + } + + public void SelectNode(SharpTreeNode obj, bool inNewTabPage) + { + SelectNode(obj, inNewTabPage, true); + } + + public void SelectNode(SharpTreeNode obj, bool inNewTabPage, bool setFocus) { if (obj != null) { if (!obj.AncestorsAndSelf().Any(node => node.IsHidden)) { @@ -742,7 +752,11 @@ namespace ICSharpCode.ILSpy } // Set both the selection and focus to ensure that keyboard navigation works as expected. - AssemblyTreeView.FocusNode(obj); + if (setFocus) { + AssemblyTreeView.FocusNode(obj); + } else { + AssemblyTreeView.ScrollIntoView(obj); + } AssemblyTreeView.SelectedItem = obj; } else { MessageBox.Show("Navigation failed because the target is hidden or a compiler-generated class.\n" + @@ -753,7 +767,17 @@ namespace ICSharpCode.ILSpy } } - public void SelectNodes(IEnumerable nodes, bool inNewTabPage = false) + public void SelectNodes(IEnumerable nodes) + { + SelectNodes(nodes, false); + } + + public void SelectNodes(IEnumerable nodes, bool inNewTabPage) + { + SelectNodes(nodes, inNewTabPage, true); + } + + public void SelectNodes(IEnumerable nodes, bool inNewTabPage, bool setFocus) { if (nodes.Any() && nodes.All(n => !n.AncestorsAndSelf().Any(a => a.IsHidden))) { if (inNewTabPage) { @@ -765,7 +789,11 @@ namespace ICSharpCode.ILSpy DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); } - AssemblyTreeView.FocusNode(nodes.First()); + if (setFocus) { + AssemblyTreeView.FocusNode(nodes.First()); + } else { + AssemblyTreeView.ScrollIntoView(nodes.First()); + } AssemblyTreeView.SetSelectedNodes(nodes); } } @@ -998,7 +1026,7 @@ namespace ICSharpCode.ILSpy refreshInProgress = true; var path = GetPathForNode(AssemblyTreeView.SelectedItem as SharpTreeNode); ShowAssemblyList(AssemblyListManager.LoadList(ILSpySettings.Load(), assemblyList.ListName)); - SelectNode(FindNodeByPath(path, true)); + SelectNode(FindNodeByPath(path, true), false, false); } finally { refreshInProgress = false; } diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 5a4468fd5..e8f9b6e4f 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -89,8 +89,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override string ToString() { - int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(EventDefinition.MetadataToken); - return "@" + token.ToString("X8"); + return EventDefinition.Name; } } } diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 75255c6a2..ceb7c046e 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -90,8 +90,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override string ToString() { - int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(FieldDefinition.MetadataToken); - return "@" + token.ToString("X8"); + return FieldDefinition.Name; } } } diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index db7f72254..ed743dc56 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -115,8 +115,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override string ToString() { - int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(MethodDefinition.MetadataToken); - return "@" + token.ToString("X8"); + return Languages.ILLanguage.MethodToString(MethodDefinition, false, false, false); } } } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index ede31f5bd..a9df0df87 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -96,8 +96,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override string ToString() { - int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(PropertyDefinition.MetadataToken); - return "@" + token.ToString("X8"); + return Languages.ILLanguage.PropertyToString(PropertyDefinition, false, false, false); } } } diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 3a65fc385..1d6fef38c 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -157,8 +157,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override string ToString() { - int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(TypeDefinition.MetadataToken); - return "@" + token.ToString("X8"); + return TypeDefinition.ReflectionName; } } }