Browse Source

Fix #2018: Improve tooltips in IL/IL with C#/R2R view to show full member signatures

pull/2030/head
Siegfried Pammer 5 years ago
parent
commit
3436ac3246
  1. 212
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  2. 2
      ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs
  3. 2
      ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs
  4. 8
      ILSpy.ReadyToRun/ReadyToRunLanguage.cs
  5. 9
      ILSpy/Languages/CSharpILMixedLanguage.cs
  6. 38
      ILSpy/Languages/ILLanguage.cs
  7. 2
      ILSpy/Properties/AssemblyInfo.template.cs
  8. 20
      ILSpy/TextView/AvalonEditTextOutput.cs

212
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -302,6 +302,8 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -302,6 +302,8 @@ namespace ICSharpCode.Decompiler.Disassembler
if (spaceAfter) {
output.Write(' ');
}
} else if (spaceBefore && spaceAfter) {
output.Write(' ');
}
}
@ -1112,42 +1114,11 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1112,42 +1114,11 @@ namespace ICSharpCode.Decompiler.Disassembler
{ FieldAttributes.NotSerialized, "notserialized" },
};
public void DisassembleField(PEFile module, FieldDefinitionHandle field)
public void DisassembleField(PEFile module, FieldDefinitionHandle handle)
{
var metadata = module.Metadata;
var fieldDefinition = metadata.GetFieldDefinition(field);
output.WriteReference(module, field, ".field ", isDefinition: true);
int offset = fieldDefinition.GetOffset();
if (offset > -1) {
output.Write("[" + offset + "] ");
}
WriteEnum(fieldDefinition.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility);
const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA;
WriteFlags(fieldDefinition.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes);
var signature = fieldDefinition.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), new GenericContext(fieldDefinition.GetDeclaringType(), module));
var marshallingDescriptor = fieldDefinition.GetMarshallingDescriptor();
if (!marshallingDescriptor.IsNil) {
WriteMarshalInfo(metadata.GetBlobReader(marshallingDescriptor));
}
signature(ILNameSyntax.Signature);
output.Write(' ');
var fieldName = metadata.GetString(fieldDefinition.Name);
output.Write(DisassemblerHelpers.Escape(fieldName));
char sectionPrefix = 'D';
if (fieldDefinition.HasFlag(FieldAttributes.HasFieldRVA)) {
int rva = fieldDefinition.GetRelativeVirtualAddress();
sectionPrefix = GetRVASectionPrefix(module.Reader.PEHeaders, rva);
output.Write(" at {1}_{0:X8}", rva, sectionPrefix);
}
var defaultValue = fieldDefinition.GetDefaultValue();
if (!defaultValue.IsNil) {
output.Write(" = ");
WriteConstant(metadata, metadata.GetConstant(defaultValue));
}
var fieldDefinition = metadata.GetFieldDefinition(handle);
char sectionPrefix = DisassembleFieldHeaderInternal(module, handle, metadata, fieldDefinition);
output.WriteLine();
var attributes = fieldDefinition.GetCustomAttributes();
if (attributes.Count > 0) {
@ -1187,6 +1158,53 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1187,6 +1158,53 @@ namespace ICSharpCode.Decompiler.Disassembler
}
}
public void DisassembleFieldHeader(PEFile module, FieldDefinitionHandle handle)
{
var metadata = module.Metadata;
var fieldDefinition = metadata.GetFieldDefinition(handle);
DisassembleFieldHeaderInternal(module, handle, metadata, fieldDefinition);
}
private char DisassembleFieldHeaderInternal(PEFile module, FieldDefinitionHandle handle, MetadataReader metadata, FieldDefinition fieldDefinition)
{
output.WriteReference(module, handle, ".field", isDefinition: true);
WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle),
spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10);
int offset = fieldDefinition.GetOffset();
if (offset > -1) {
output.Write("[" + offset + "] ");
}
WriteEnum(fieldDefinition.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility);
const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA;
WriteFlags(fieldDefinition.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes);
var signature = fieldDefinition.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), new GenericContext(fieldDefinition.GetDeclaringType(), module));
var marshallingDescriptor = fieldDefinition.GetMarshallingDescriptor();
if (!marshallingDescriptor.IsNil) {
WriteMarshalInfo(metadata.GetBlobReader(marshallingDescriptor));
}
signature(ILNameSyntax.Signature);
output.Write(' ');
var fieldName = metadata.GetString(fieldDefinition.Name);
output.Write(DisassemblerHelpers.Escape(fieldName));
char sectionPrefix = 'D';
if (fieldDefinition.HasFlag(FieldAttributes.HasFieldRVA)) {
int rva = fieldDefinition.GetRelativeVirtualAddress();
sectionPrefix = GetRVASectionPrefix(module.Reader.PEHeaders, rva);
output.Write(" at {1}_{0:X8}", rva, sectionPrefix);
}
var defaultValue = fieldDefinition.GetDefaultValue();
if (!defaultValue.IsNil) {
output.Write(" = ");
WriteConstant(metadata, metadata.GetConstant(defaultValue));
}
return sectionPrefix;
}
char GetRVASectionPrefix(System.Reflection.PortableExecutable.PEHeaders headers, int rva)
{
int sectionIndex = headers.GetContainingSectionIndex(rva);
@ -1215,8 +1233,30 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1215,8 +1233,30 @@ namespace ICSharpCode.Decompiler.Disassembler
{
var metadata = module.Metadata;
var propertyDefinition = metadata.GetPropertyDefinition(property);
output.WriteReference(module, property, ".property", isDefinition: true);
output.Write(" ");
PropertyAccessors accessors = DisassemblePropertyHeaderInternal(module, property, metadata, propertyDefinition);
OpenBlock(false);
WriteAttributes(module, propertyDefinition.GetCustomAttributes());
WriteNestedMethod(".get", module, accessors.Getter);
WriteNestedMethod(".set", module, accessors.Setter);
foreach (var method in accessors.Others) {
WriteNestedMethod(".other", module, method);
}
CloseBlock();
}
public void DisassemblePropertyHeader(PEFile module, PropertyDefinitionHandle property)
{
var metadata = module.Metadata;
var propertyDefinition = metadata.GetPropertyDefinition(property);
DisassemblePropertyHeaderInternal(module, property, metadata, propertyDefinition);
}
private PropertyAccessors DisassemblePropertyHeaderInternal(PEFile module, PropertyDefinitionHandle handle, MetadataReader metadata, PropertyDefinition propertyDefinition)
{
output.WriteReference(module, handle, ".property", isDefinition: true);
WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle),
spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10);
WriteFlags(propertyDefinition.Attributes, propertyAttributes);
var accessors = propertyDefinition.GetAccessors();
var declaringType = metadata.GetMethodDefinition(accessors.GetAny()).GetDeclaringType();
@ -1239,15 +1279,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1239,15 +1279,7 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Unindent();
}
output.Write(')');
OpenBlock(false);
WriteAttributes(module, propertyDefinition.GetCustomAttributes());
WriteNestedMethod(".get", module, accessors.Getter);
WriteNestedMethod(".set", module, accessors.Setter);
foreach (var method in accessors.Others) {
WriteNestedMethod(".other", module, method);
}
CloseBlock();
return accessors;
}
void WriteNestedMethod(string keyword, PEFile module, MethodDefinitionHandle method)
@ -1272,6 +1304,27 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1272,6 +1304,27 @@ namespace ICSharpCode.Decompiler.Disassembler
{
var eventDefinition = module.Metadata.GetEventDefinition(handle);
var accessors = eventDefinition.GetAccessors();
DisassembleEventHeaderInternal(module, handle, eventDefinition, accessors);
OpenBlock(false);
WriteAttributes(module, eventDefinition.GetCustomAttributes());
WriteNestedMethod(".addon", module, accessors.Adder);
WriteNestedMethod(".removeon", module, accessors.Remover);
WriteNestedMethod(".fire", module, accessors.Raiser);
foreach (var method in accessors.Others) {
WriteNestedMethod(".other", module, method);
}
CloseBlock();
}
public void DisassembleEventHeader(PEFile module, EventDefinitionHandle handle)
{
var eventDefinition = module.Metadata.GetEventDefinition(handle);
var accessors = eventDefinition.GetAccessors();
DisassembleEventHeaderInternal(module, handle, eventDefinition, accessors);
}
private void DisassembleEventHeaderInternal(PEFile module, EventDefinitionHandle handle, EventDefinition eventDefinition, EventAccessors accessors)
{
TypeDefinitionHandle declaringType;
if (!accessors.Adder.IsNil) {
declaringType = module.Metadata.GetMethodDefinition(accessors.Adder).GetDeclaringType();
@ -1281,7 +1334,8 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1281,7 +1334,8 @@ namespace ICSharpCode.Decompiler.Disassembler
declaringType = module.Metadata.GetMethodDefinition(accessors.Raiser).GetDeclaringType();
}
output.WriteReference(module, handle, ".event", isDefinition: true);
output.Write(" ");
WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle),
spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10);
WriteFlags(eventDefinition.Attributes, eventAttributes);
var provider = new DisassemblerSignatureTypeProvider(module, output);
Action<ILNameSyntax> signature;
@ -1302,15 +1356,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1302,15 +1356,6 @@ namespace ICSharpCode.Decompiler.Disassembler
signature(ILNameSyntax.TypeName);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(module.Metadata.GetString(eventDefinition.Name)));
OpenBlock(false);
WriteAttributes(module, eventDefinition.GetCustomAttributes());
WriteNestedMethod(".addon", module, accessors.Adder);
WriteNestedMethod(".removeon", module, accessors.Remover);
WriteNestedMethod(".fire", module, accessors.Raiser);
foreach (var method in accessors.Others) {
WriteNestedMethod(".other", module, method);
}
CloseBlock();
}
#endregion
@ -1352,30 +1397,9 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1352,30 +1397,9 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleType(PEFile module, TypeDefinitionHandle type)
{
var typeDefinition = module.Metadata.GetTypeDefinition(type);
output.WriteReference(module, type, ".class", isDefinition: true);
output.Write(" ");
if ((typeDefinition.Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface)
output.Write("interface ");
WriteEnum(typeDefinition.Attributes & TypeAttributes.VisibilityMask, typeVisibility);
WriteEnum(typeDefinition.Attributes & TypeAttributes.LayoutMask, typeLayout);
WriteEnum(typeDefinition.Attributes & TypeAttributes.StringFormatMask, typeStringFormat);
const TypeAttributes masks = TypeAttributes.ClassSemanticsMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask;
WriteFlags(typeDefinition.Attributes & ~masks, typeAttributes);
output.Write(typeDefinition.GetDeclaringType().IsNil ? typeDefinition.GetFullTypeName(module.Metadata).ToILNameString() : DisassemblerHelpers.Escape(module.Metadata.GetString(typeDefinition.Name)));
GenericContext genericContext = new GenericContext(type, module);
WriteTypeParameters(output, module, genericContext, typeDefinition.GetGenericParameters());
output.MarkFoldStart(defaultCollapsed: !ExpandMemberDefinitions && isInType);
output.WriteLine();
EntityHandle baseType = typeDefinition.GetBaseTypeOrNil();
if (!baseType.IsNil) {
output.Indent();
output.Write("extends ");
baseType.WriteTo(module, output, genericContext, ILNameSyntax.TypeName);
output.WriteLine();
output.Unindent();
}
DisassembleTypeHeaderInternal(module, type, typeDefinition, genericContext);
var interfaces = typeDefinition.GetInterfaceImplementations();
if (interfaces.Count > 0) {
@ -1463,6 +1487,40 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1463,6 +1487,40 @@ namespace ICSharpCode.Decompiler.Disassembler
isInType = oldIsInType;
}
public void DisassembleTypeHeader(PEFile module, TypeDefinitionHandle type)
{
var typeDefinition = module.Metadata.GetTypeDefinition(type);
GenericContext genericContext = new GenericContext(type, module);
DisassembleTypeHeaderInternal(module, type, typeDefinition, genericContext);
}
private void DisassembleTypeHeaderInternal(PEFile module, TypeDefinitionHandle handle, TypeDefinition typeDefinition, GenericContext genericContext)
{
output.WriteReference(module, handle, ".class", isDefinition: true);
WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle),
spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10); if ((typeDefinition.Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface)
output.Write("interface ");
WriteEnum(typeDefinition.Attributes & TypeAttributes.VisibilityMask, typeVisibility);
WriteEnum(typeDefinition.Attributes & TypeAttributes.LayoutMask, typeLayout);
WriteEnum(typeDefinition.Attributes & TypeAttributes.StringFormatMask, typeStringFormat);
const TypeAttributes masks = TypeAttributes.ClassSemanticsMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask;
WriteFlags(typeDefinition.Attributes & ~masks, typeAttributes);
output.Write(typeDefinition.GetDeclaringType().IsNil ? typeDefinition.GetFullTypeName(module.Metadata).ToILNameString() : DisassemblerHelpers.Escape(module.Metadata.GetString(typeDefinition.Name)));
WriteTypeParameters(output, module, genericContext, typeDefinition.GetGenericParameters());
output.MarkFoldStart(defaultCollapsed: !ExpandMemberDefinitions && isInType);
output.WriteLine();
EntityHandle baseType = typeDefinition.GetBaseTypeOrNil();
if (!baseType.IsNil) {
output.Indent();
output.Write("extends ");
baseType.WriteTo(module, output, genericContext, ILNameSyntax.TypeName);
output.WriteLine();
output.Unindent();
}
}
void WriteTypeParameters(ITextOutput output, PEFile module, GenericContext context, GenericParameterHandleCollection p)
{
if (p.Count > 0) {

2
ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs

@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler.IL @@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler.IL
output.Write(primitiveType.ToString().ToLowerInvariant());
}
public static void WriteTo(this IType type, ITextOutput output, ILNameSyntax nameSyntax = ILNameSyntax.ShortTypeName)
public static void WriteTo(this IType type, ITextOutput output)
{
output.WriteReference(type, type.ReflectionName);
}

2
ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs

@ -135,7 +135,7 @@ namespace ICSharpCode.Decompiler.IL @@ -135,7 +135,7 @@ namespace ICSharpCode.Decompiler.IL
WriteILRange(output, options);
if (ConstrainedTo != null) {
output.Write("constrained[");
ConstrainedTo.WriteTo(output, ILNameSyntax.ShortTypeName);
ConstrainedTo.WriteTo(output);
output.Write("].");
}
if (IsTail)

8
ILSpy.ReadyToRun/ReadyToRunLanguage.cs

@ -26,11 +26,14 @@ using System.Reflection.Metadata.Ecma335; @@ -26,11 +26,14 @@ using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using Iced.Intel;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TextView;
using ILCompiler.Reflection.ReadyToRun;
namespace ICSharpCode.ILSpy.ReadyToRun
@ -195,6 +198,11 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -195,6 +198,11 @@ namespace ICSharpCode.ILSpy.ReadyToRun
output.WriteLine();
}
public override RichText GetRichTextTooltip(IEntity entity)
{
return Languages.ILLanguage.GetRichTextTooltip(entity);
}
private ReadyToRunReaderCacheEntry GetReader(LoadedAssembly assembly, PEFile module)
{
ReadyToRunReaderCacheEntry result;

9
ILSpy/Languages/CSharpILMixedLanguage.cs

@ -47,12 +47,17 @@ namespace ICSharpCode.ILSpy @@ -47,12 +47,17 @@ namespace ICSharpCode.ILSpy
protected override ReflectionDisassembler CreateDisassembler(ITextOutput output, DecompilationOptions options)
{
return new ReflectionDisassembler(output,
return new ReflectionDisassembler(output,
new MixedMethodBodyDisassembler(output, options) {
DetectControlStructure = detectControlStructure,
ShowSequencePoints = options.DecompilerSettings.ShowDebugInfo
},
options.CancellationToken);
options.CancellationToken)
{
ShowMetadataTokens = Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokens,
ShowMetadataTokensInBase10 = Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokensInBase10,
ExpandMemberDefinitions = options.DecompilerSettings.ExpandMemberDefinitions
};
}
static CSharpDecompiler CreateDecompiler(PEFile module, DecompilationOptions options)

38
ILSpy/Languages/ILLanguage.cs

@ -20,15 +20,14 @@ using System.Collections.Generic; @@ -20,15 +20,14 @@ using System.Collections.Generic;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using System.ComponentModel.Composition;
using System.Reflection.PortableExecutable;
using System.Reflection.Metadata;
using System.IO;
using System.Reflection.Metadata.Ecma335;
using System.Linq;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.ILSpy.TextView;
namespace ICSharpCode.ILSpy
{
@ -178,5 +177,38 @@ namespace ICSharpCode.ILSpy @@ -178,5 +177,38 @@ namespace ICSharpCode.ILSpy
}
return null;
}
public override RichText GetRichTextTooltip(IEntity entity)
{
var output = new AvalonEditTextOutput() { IgnoreNewLineAndIndent = true };
var disasm = CreateDisassembler(output, new DecompilationOptions());
switch (entity.SymbolKind) {
case SymbolKind.TypeDefinition:
disasm.DisassembleTypeHeader(entity.ParentModule.PEFile, (TypeDefinitionHandle)entity.MetadataToken);
break;
case SymbolKind.Field:
disasm.DisassembleFieldHeader(entity.ParentModule.PEFile, (FieldDefinitionHandle)entity.MetadataToken);
break;
case SymbolKind.Property:
case SymbolKind.Indexer:
disasm.DisassemblePropertyHeader(entity.ParentModule.PEFile, (PropertyDefinitionHandle)entity.MetadataToken);
break;
case SymbolKind.Event:
disasm.DisassembleEventHeader(entity.ParentModule.PEFile, (EventDefinitionHandle)entity.MetadataToken);
break;
case SymbolKind.Method:
case SymbolKind.Operator:
case SymbolKind.Constructor:
case SymbolKind.Destructor:
case SymbolKind.Accessor:
disasm.DisassembleMethodHeader(entity.ParentModule.PEFile, (MethodDefinitionHandle)entity.MetadataToken);
break;
default:
output.Write(GetDisplayName(entity, true, true, true));
break;
}
return new DocumentHighlighter(output.GetDocument(), base.SyntaxHighlighting).HighlightLine(1).ToRichText();
}
}
}

2
ILSpy/Properties/AssemblyInfo.template.cs

@ -40,7 +40,7 @@ internal static class RevisionClass @@ -40,7 +40,7 @@ internal static class RevisionClass
public const string Minor = "0";
public const string Build = "0";
public const string Revision = "$INSERTREVISION$";
public const string VersionName = "preview3";
public const string VersionName = "preview4";
public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$";
}

20
ILSpy/TextView/AvalonEditTextOutput.cs

@ -82,6 +82,8 @@ namespace ICSharpCode.ILSpy.TextView @@ -82,6 +82,8 @@ namespace ICSharpCode.ILSpy.TextView
public string IndentationString { get; set; } = "\t";
internal bool IgnoreNewLineAndIndent { get; set; }
public string Title { get; set; } = Properties.Resources.NewTab;
/// <summary>
@ -177,16 +179,22 @@ namespace ICSharpCode.ILSpy.TextView @@ -177,16 +179,22 @@ namespace ICSharpCode.ILSpy.TextView
public void Indent()
{
if (IgnoreNewLineAndIndent)
return;
indent++;
}
public void Unindent()
{
if (IgnoreNewLineAndIndent)
return;
indent--;
}
void WriteIndent()
{
if (IgnoreNewLineAndIndent)
return;
Debug.Assert(textDocument == null);
if (needsIndent) {
needsIndent = false;
@ -211,10 +219,14 @@ namespace ICSharpCode.ILSpy.TextView @@ -211,10 +219,14 @@ namespace ICSharpCode.ILSpy.TextView
public void WriteLine()
{
Debug.Assert(textDocument == null);
b.AppendLine();
needsIndent = true;
lastLineStart = b.Length;
lineNumber++;
if (IgnoreNewLineAndIndent) {
b.Append(' ');
} else {
b.AppendLine();
needsIndent = true;
lastLineStart = b.Length;
lineNumber++;
}
if (this.TextLength > LengthLimit) {
throw new OutputLengthExceededException();
}

Loading…
Cancel
Save