You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

600 lines
19 KiB

// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Abstractions;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Base class for language-specific decompiler implementations.
/// </summary>
/// <remarks>
/// Implementations of this class must be thread-safe.
/// </remarks>
public abstract class Language : ILanguage
{
protected static SettingsService SettingsService { get; } = App.ExportProvider.GetExportedValue<SettingsService>();
protected static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue<AssemblyTreeModel>();
protected static ICollection<IResourceFileHandler> ResourceFileHandlers { get; } = App.ExportProvider.GetExportedValues<IResourceFileHandler>().ToArray();
/// <summary>
/// Gets the name of the language (as shown in the UI)
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Gets the file extension used by source code files in this language.
/// </summary>
public abstract string FileExtension { get; }
public virtual string ProjectFileExtension {
get { return null; }
}
public virtual IReadOnlyList<LanguageVersion> LanguageVersions {
get { return EmptyList<LanguageVersion>.Instance; }
}
public bool HasLanguageVersions => LanguageVersions.Count > 0;
/// <summary>
/// Gets the syntax highlighting used for this language.
/// </summary>
public virtual IHighlightingDefinition SyntaxHighlighting {
get {
return HighlightingManager.Instance.GetDefinitionByExtension(FileExtension);
}
}
public virtual TextView.IBracketSearcher BracketSearcher {
get {
return TextView.DefaultBracketSearcher.DefaultInstance;
}
}
public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition, includeNamespace: true) + "." + method.Name);
}
public virtual void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(property.DeclaringTypeDefinition, includeNamespace: true) + "." + property.Name);
}
public virtual void DecompileField(IField field, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(field.DeclaringTypeDefinition, includeNamespace: true) + "." + field.Name);
}
public virtual void DecompileEvent(IEvent @event, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(@event.DeclaringTypeDefinition, includeNamespace: true) + "." + @event.Name);
}
public virtual void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(type, includeNamespace: true));
}
public virtual void DecompileNamespace(string nameSpace, IEnumerable<ITypeDefinition> types, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, nameSpace);
}
public virtual ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, assembly.FileName);
var asm = assembly.GetMetadataFileOrNull();
if (asm == null)
return null;
if (options.FullDecompilation && options.SaveAsProjectDirectory != null)
{
throw new NotSupportedException($"Language '{Name}' does not support exporting assemblies as projects!");
}
var metadata = asm.Metadata;
if (metadata.IsAssembly)
{
var name = metadata.GetAssemblyDefinition();
if ((name.Flags & System.Reflection.AssemblyFlags.WindowsRuntime) != 0)
{
WriteCommentLine(output, metadata.GetString(name.Name) + " [WinRT]");
}
else if (metadata.TryGetFullAssemblyName(out string assemblyName))
{
WriteCommentLine(output, assemblyName);
}
else
{
WriteCommentLine(output, "ERR: Could not read assembly name");
}
}
else
{
WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name));
}
return null;
}
public virtual void WriteCommentLine(ITextOutput output, string comment)
{
output.WriteLine("// " + comment);
}
#region TypeToString
/// <summary>
/// Converts a type definition, reference or specification into a string. This method is used by tree nodes and search results.
/// </summary>
public virtual string TypeToString(IType type, bool includeNamespace)
{
var visitor = new TypeToStringVisitor(includeNamespace);
type.AcceptVisitor(visitor);
return visitor.ToString();
}
class TypeToStringVisitor : TypeVisitor
{
readonly bool includeNamespace;
readonly StringBuilder builder;
public override string ToString()
{
return builder.ToString();
}
public TypeToStringVisitor(bool includeNamespace)
{
this.includeNamespace = includeNamespace;
this.builder = new StringBuilder();
}
public override IType VisitArrayType(ArrayType type)
{
base.VisitArrayType(type);
builder.Append('[');
builder.Append(',', type.Dimensions - 1);
builder.Append(']');
return type;
}
public override IType VisitByReferenceType(ByReferenceType type)
{
base.VisitByReferenceType(type);
builder.Append('&');
return type;
}
public override IType VisitModOpt(ModifiedType type)
{
type.ElementType.AcceptVisitor(this);
builder.Append(" modopt(");
type.Modifier.AcceptVisitor(this);
builder.Append(")");
return type;
}
public override IType VisitModReq(ModifiedType type)
{
type.ElementType.AcceptVisitor(this);
builder.Append(" modreq(");
type.Modifier.AcceptVisitor(this);
builder.Append(")");
return type;
}
public override IType VisitPointerType(PointerType type)
{
base.VisitPointerType(type);
builder.Append('*');
return type;
}
public override IType VisitTypeParameter(ITypeParameter type)
{
base.VisitTypeParameter(type);
EscapeName(builder, type.Name);
return type;
}
public override IType VisitParameterizedType(ParameterizedType type)
{
type.GenericType.AcceptVisitor(this);
builder.Append('<');
for (int i = 0; i < type.TypeArguments.Count; i++)
{
if (i > 0)
builder.Append(',');
type.TypeArguments[i].AcceptVisitor(this);
}
builder.Append('>');
return type;
}
public override IType VisitTupleType(TupleType type)
{
type.UnderlyingType.AcceptVisitor(this);
return type;
}
public override IType VisitFunctionPointerType(FunctionPointerType type)
{
builder.Append("method ");
if (type.CallingConvention != SignatureCallingConvention.Default)
{
builder.Append(type.CallingConvention.ToILSyntax());
builder.Append(' ');
}
type.ReturnType.AcceptVisitor(this);
builder.Append(" *(");
bool first = true;
foreach (var p in type.ParameterTypes)
{
if (first)
first = false;
else
builder.Append(", ");
p.AcceptVisitor(this);
}
builder.Append(')');
return type;
}
public override IType VisitOtherType(IType type)
{
WriteType(type);
return type;
}
private void WriteType(IType type)
{
if (includeNamespace)
EscapeName(builder, type.FullName);
else
EscapeName(builder, type.Name);
if (type.TypeParameterCount > 0)
{
builder.Append('`');
builder.Append(type.TypeParameterCount);
}
}
public override IType VisitTypeDefinition(ITypeDefinition type)
{
switch (type.KnownTypeCode)
{
case KnownTypeCode.Object:
builder.Append("object");
break;
case KnownTypeCode.Boolean:
builder.Append("bool");
break;
case KnownTypeCode.Char:
builder.Append("char");
break;
case KnownTypeCode.SByte:
builder.Append("int8");
break;
case KnownTypeCode.Byte:
builder.Append("uint8");
break;
case KnownTypeCode.Int16:
builder.Append("int16");
break;
case KnownTypeCode.UInt16:
builder.Append("uint16");
break;
case KnownTypeCode.Int32:
builder.Append("int32");
break;
case KnownTypeCode.UInt32:
builder.Append("uint32");
break;
case KnownTypeCode.Int64:
builder.Append("int64");
break;
case KnownTypeCode.UInt64:
builder.Append("uint64");
break;
case KnownTypeCode.Single:
builder.Append("float32");
break;
case KnownTypeCode.Double:
builder.Append("float64");
break;
case KnownTypeCode.String:
builder.Append("string");
break;
case KnownTypeCode.Void:
builder.Append("void");
break;
case KnownTypeCode.IntPtr:
builder.Append("native int");
break;
case KnownTypeCode.UIntPtr:
builder.Append("native uint");
break;
case KnownTypeCode.TypedReference:
builder.Append("typedref");
break;
default:
WriteType(type);
break;
}
return type;
}
}
#endregion
/// <summary>
/// Converts a member signature to a string.
/// This is used for displaying the tooltip on a member reference.
/// </summary>
public virtual string GetTooltip(IEntity entity)
{
return GetDisplayName(entity, true, true, true);
}
/// <summary>
/// Converts a member signature to a string.
/// This is used for displaying the tooltip on a member reference.
/// </summary>
public virtual RichText GetRichTextTooltip(IEntity entity)
{
return GetTooltip(entity);
}
public virtual string FieldToString(IField field, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
{
if (field == null)
throw new ArgumentNullException(nameof(field));
return GetDisplayName(field, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName) + " : " + TypeToString(field.ReturnType, includeNamespace);
}
public virtual string PropertyToString(IProperty property, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
{
if (property == null)
throw new ArgumentNullException(nameof(property));
return GetDisplayName(property, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName) + " : " + TypeToString(property.ReturnType, includeNamespace);
}
public virtual string MethodToString(IMethod method, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
{
if (method == null)
throw new ArgumentNullException(nameof(method));
int i = 0;
var buffer = new StringBuilder();
buffer.Append(GetDisplayName(method, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName));
var typeParameters = method.TypeParameters;
if (typeParameters.Count > 0)
{
buffer.Append("``");
buffer.Append(typeParameters.Count);
buffer.Append('<');
foreach (var tp in typeParameters)
{
if (i > 0)
buffer.Append(", ");
buffer.Append(tp.Name);
i++;
}
buffer.Append('>');
}
buffer.Append('(');
i = 0;
var parameters = method.Parameters;
foreach (var param in parameters)
{
if (i > 0)
buffer.Append(", ");
buffer.Append(TypeToString(param.Type, includeNamespace));
i++;
}
buffer.Append(')');
if (!method.IsConstructor)
{
buffer.Append(" : ");
buffer.Append(TypeToString(method.ReturnType, includeNamespace));
}
return buffer.ToString();
}
public virtual string EventToString(IEvent @event, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
{
if (@event == null)
throw new ArgumentNullException(nameof(@event));
var buffer = new StringBuilder();
buffer.Append(GetDisplayName(@event, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName));
buffer.Append(" : ");
buffer.Append(TypeToString(@event.ReturnType, includeNamespace));
return buffer.ToString();
}
protected string GetDisplayName(IEntity entity, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
{
string entityName;
if (entity is ITypeDefinition t && !t.MetadataToken.IsNil)
{
MetadataReader metadata = t.ParentModule.MetadataFile.Metadata;
var typeDef = metadata.GetTypeDefinition((TypeDefinitionHandle)t.MetadataToken);
entityName = EscapeName(metadata.GetString(typeDef.Name));
}
else
{
entityName = EscapeName(entity.Name);
}
if (includeNamespace || includeDeclaringTypeName)
{
if (entity.DeclaringTypeDefinition != null)
return TypeToString(entity.DeclaringTypeDefinition, includeNamespaceOfDeclaringTypeName) + "." + entityName;
return EscapeName(entity.Namespace) + "." + entityName;
}
else
{
return entityName;
}
}
/// <summary>
/// Used for WPF keyboard navigation.
/// </summary>
public override string ToString()
{
return Name;
}
public virtual bool ShowMember(IEntity member)
{
return true;
}
/// <summary>
/// This should produce a string representation of the entity for search to match search strings against.
/// </summary>
public virtual string GetEntityName(MetadataFile module, EntityHandle handle, bool fullName, bool omitGenerics)
{
MetadataReader metadata = module.Metadata;
switch (handle.Kind)
{
case HandleKind.TypeDefinition:
if (fullName)
return EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString(omitGenerics));
var td = metadata.GetTypeDefinition((TypeDefinitionHandle)handle);
return EscapeName(metadata.GetString(td.Name));
case HandleKind.FieldDefinition:
var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle);
if (fullName)
return EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(fd.Name));
return EscapeName(metadata.GetString(fd.Name));
case HandleKind.MethodDefinition:
var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle);
string methodName = metadata.GetString(md.Name);
if (!omitGenerics)
{
int genericParamCount = md.GetGenericParameters().Count;
if (genericParamCount > 0)
methodName += "``" + genericParamCount;
}
if (fullName)
return EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + methodName);
return EscapeName(methodName);
case HandleKind.EventDefinition:
var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle);
var declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType();
if (fullName)
return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(ed.Name));
return EscapeName(metadata.GetString(ed.Name));
case HandleKind.PropertyDefinition:
var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle);
declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType();
if (fullName)
return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(pd.Name));
return EscapeName(metadata.GetString(pd.Name));
default:
return null;
}
}
public virtual CodeMappingInfo GetCodeMappingInfo(MetadataFile module, EntityHandle member)
{
var declaringType = (TypeDefinitionHandle)member.GetDeclaringType(module.Metadata);
if (declaringType.IsNil && member.Kind == HandleKind.TypeDefinition)
{
declaringType = (TypeDefinitionHandle)member;
}
return new CodeMappingInfo(module, declaringType);
}
public static string GetPlatformDisplayName(PEFile module)
{
var headers = module.Reader.PEHeaders;
var architecture = headers.CoffHeader.Machine;
var characteristics = headers.CoffHeader.Characteristics;
var corflags = headers.CorHeader.Flags;
switch (architecture)
{
case Machine.I386:
if ((corflags & CorFlags.Prefers32Bit) != 0)
return "AnyCPU (32-bit preferred)";
if ((corflags & CorFlags.Requires32Bit) != 0)
return "x86";
// According to ECMA-335, II.25.3.3.1 CorFlags.Requires32Bit and Characteristics.Bit32Machine must be in sync
// for assemblies containing managed code. However, this is not true for C++/CLI assemblies.
if ((corflags & CorFlags.ILOnly) == 0 && (characteristics & Characteristics.Bit32Machine) != 0)
return "x86";
return "AnyCPU (64-bit preferred)";
case Machine.Amd64:
return "x64";
case Machine.IA64:
return "Itanium";
default:
return architecture.ToString();
}
}
public static string GetRuntimeDisplayName(MetadataFile module)
{
return module.Metadata.MetadataVersion;
}
/// <summary>
/// Escape characters that cannot be displayed in the UI.
/// </summary>
public static StringBuilder EscapeName(StringBuilder sb, string name)
{
foreach (char ch in name)
{
if (char.IsWhiteSpace(ch) || char.IsControl(ch) || char.IsSurrogate(ch))
sb.AppendFormat("\\u{0:x4}", (int)ch);
else
sb.Append(ch);
}
return sb;
}
/// <summary>
/// Escape characters that cannot be displayed in the UI.
/// </summary>
public static string EscapeName(string name)
{
return EscapeName(new StringBuilder(name.Length), name).ToString();
}
}
}