/ILSpy/Languages/Language.cs

http://github.com/icsharpcode/ILSpy · C# · 554 lines · 443 code · 54 blank · 57 comment · 55 complexity · f4cd0ce1562563930b6d0f2348d7f519 MD5 · raw file

  1. // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Reflection.Metadata;
  21. using System.Reflection.PortableExecutable;
  22. using System.Text;
  23. using ICSharpCode.AvalonEdit.Highlighting;
  24. using ICSharpCode.Decompiler;
  25. using ICSharpCode.Decompiler.Metadata;
  26. using ICSharpCode.Decompiler.Solution;
  27. using ICSharpCode.Decompiler.TypeSystem;
  28. using ICSharpCode.Decompiler.TypeSystem.Implementation;
  29. using ICSharpCode.Decompiler.Util;
  30. using SRM = System.Reflection.Metadata;
  31. namespace ICSharpCode.ILSpy
  32. {
  33. public class LanguageVersion
  34. {
  35. public string Version { get; }
  36. public string DisplayName { get; }
  37. public LanguageVersion(string version, string name = null)
  38. {
  39. this.Version = version ?? "";
  40. this.DisplayName = name ?? version.ToString();
  41. }
  42. public override string ToString()
  43. {
  44. return $"[LanguageVersion DisplayName={DisplayName}, Version={Version}]";
  45. }
  46. }
  47. /// <summary>
  48. /// Base class for language-specific decompiler implementations.
  49. /// </summary>
  50. /// <remarks>
  51. /// Implementations of this class must be thread-safe.
  52. /// </remarks>
  53. public abstract class Language
  54. {
  55. /// <summary>
  56. /// Gets the name of the language (as shown in the UI)
  57. /// </summary>
  58. public abstract string Name { get; }
  59. /// <summary>
  60. /// Gets the file extension used by source code files in this language.
  61. /// </summary>
  62. public abstract string FileExtension { get; }
  63. public virtual string ProjectFileExtension
  64. {
  65. get { return null; }
  66. }
  67. public virtual IReadOnlyList<LanguageVersion> LanguageVersions {
  68. get { return EmptyList<LanguageVersion>.Instance; }
  69. }
  70. public bool HasLanguageVersions => LanguageVersions.Count > 0;
  71. /// <summary>
  72. /// Gets the syntax highlighting used for this language.
  73. /// </summary>
  74. public virtual ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting
  75. {
  76. get
  77. {
  78. return ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinitionByExtension(this.FileExtension);
  79. }
  80. }
  81. public virtual TextView.IBracketSearcher BracketSearcher {
  82. get {
  83. return TextView.DefaultBracketSearcher.DefaultInstance;
  84. }
  85. }
  86. public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options)
  87. {
  88. WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition, includeNamespace: true) + "." + method.Name);
  89. }
  90. public virtual void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options)
  91. {
  92. WriteCommentLine(output, TypeToString(property.DeclaringTypeDefinition, includeNamespace: true) + "." + property.Name);
  93. }
  94. public virtual void DecompileField(IField field, ITextOutput output, DecompilationOptions options)
  95. {
  96. WriteCommentLine(output, TypeToString(field.DeclaringTypeDefinition, includeNamespace: true) + "." + field.Name);
  97. }
  98. public virtual void DecompileEvent(IEvent @event, ITextOutput output, DecompilationOptions options)
  99. {
  100. WriteCommentLine(output, TypeToString(@event.DeclaringTypeDefinition, includeNamespace: true) + "." + @event.Name);
  101. }
  102. public virtual void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options)
  103. {
  104. WriteCommentLine(output, TypeToString(type, includeNamespace: true));
  105. }
  106. public virtual void DecompileNamespace(string nameSpace, IEnumerable<ITypeDefinition> types, ITextOutput output, DecompilationOptions options)
  107. {
  108. WriteCommentLine(output, nameSpace);
  109. }
  110. public virtual ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
  111. {
  112. WriteCommentLine(output, assembly.FileName);
  113. var asm = assembly.GetPEFileOrNull();
  114. if (asm == null) return null;
  115. var metadata = asm.Metadata;
  116. if (metadata.IsAssembly) {
  117. var name = metadata.GetAssemblyDefinition();
  118. if ((name.Flags & System.Reflection.AssemblyFlags.WindowsRuntime) != 0) {
  119. WriteCommentLine(output, metadata.GetString(name.Name) + " [WinRT]");
  120. } else {
  121. WriteCommentLine(output, metadata.GetFullAssemblyName());
  122. }
  123. } else {
  124. WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name));
  125. }
  126. return null;
  127. }
  128. public virtual void WriteCommentLine(ITextOutput output, string comment)
  129. {
  130. output.WriteLine("// " + comment);
  131. }
  132. #region TypeToString
  133. /// <summary>
  134. /// Converts a type definition, reference or specification into a string. This method is used by tree nodes and search results.
  135. /// </summary>
  136. public virtual string TypeToString(IType type, bool includeNamespace)
  137. {
  138. var visitor = new TypeToStringVisitor(includeNamespace);
  139. type.AcceptVisitor(visitor);
  140. return visitor.ToString();
  141. }
  142. class TypeToStringVisitor : TypeVisitor
  143. {
  144. readonly bool includeNamespace;
  145. readonly StringBuilder builder;
  146. public override string ToString()
  147. {
  148. return builder.ToString();
  149. }
  150. public TypeToStringVisitor(bool includeNamespace)
  151. {
  152. this.includeNamespace = includeNamespace;
  153. this.builder = new StringBuilder();
  154. }
  155. public override IType VisitArrayType(ArrayType type)
  156. {
  157. base.VisitArrayType(type);
  158. builder.Append('[');
  159. builder.Append(',', type.Dimensions - 1);
  160. builder.Append(']');
  161. return type;
  162. }
  163. public override IType VisitByReferenceType(ByReferenceType type)
  164. {
  165. base.VisitByReferenceType(type);
  166. builder.Append('&');
  167. return type;
  168. }
  169. public override IType VisitModOpt(ModifiedType type)
  170. {
  171. type.ElementType.AcceptVisitor(this);
  172. builder.Append(" modopt(");
  173. type.Modifier.AcceptVisitor(this);
  174. builder.Append(")");
  175. return type;
  176. }
  177. public override IType VisitModReq(ModifiedType type)
  178. {
  179. type.ElementType.AcceptVisitor(this);
  180. builder.Append(" modreq(");
  181. type.Modifier.AcceptVisitor(this);
  182. builder.Append(")");
  183. return type;
  184. }
  185. public override IType VisitPointerType(PointerType type)
  186. {
  187. base.VisitPointerType(type);
  188. builder.Append('*');
  189. return type;
  190. }
  191. public override IType VisitTypeParameter(ITypeParameter type)
  192. {
  193. base.VisitTypeParameter(type);
  194. EscapeName(builder, type.Name);
  195. return type;
  196. }
  197. public override IType VisitParameterizedType(ParameterizedType type)
  198. {
  199. type.GenericType.AcceptVisitor(this);
  200. builder.Append('<');
  201. for (int i = 0; i < type.TypeArguments.Count; i++) {
  202. if (i > 0)
  203. builder.Append(',');
  204. type.TypeArguments[i].AcceptVisitor(this);
  205. }
  206. builder.Append('>');
  207. return type;
  208. }
  209. public override IType VisitTupleType(TupleType type)
  210. {
  211. type.UnderlyingType.AcceptVisitor(this);
  212. return type;
  213. }
  214. public override IType VisitOtherType(IType type)
  215. {
  216. WriteType(type);
  217. return type;
  218. }
  219. private void WriteType(IType type)
  220. {
  221. if (includeNamespace)
  222. EscapeName(builder, type.FullName);
  223. else
  224. EscapeName(builder, type.Name);
  225. if (type.TypeParameterCount > 0) {
  226. builder.Append('`');
  227. builder.Append(type.TypeParameterCount);
  228. }
  229. }
  230. public override IType VisitTypeDefinition(ITypeDefinition type)
  231. {
  232. switch (type.KnownTypeCode) {
  233. case KnownTypeCode.Object:
  234. builder.Append("object");
  235. break;
  236. case KnownTypeCode.Boolean:
  237. builder.Append("bool");
  238. break;
  239. case KnownTypeCode.Char:
  240. builder.Append("char");
  241. break;
  242. case KnownTypeCode.SByte:
  243. builder.Append("int8");
  244. break;
  245. case KnownTypeCode.Byte:
  246. builder.Append("uint8");
  247. break;
  248. case KnownTypeCode.Int16:
  249. builder.Append("int16");
  250. break;
  251. case KnownTypeCode.UInt16:
  252. builder.Append("uint16");
  253. break;
  254. case KnownTypeCode.Int32:
  255. builder.Append("int32");
  256. break;
  257. case KnownTypeCode.UInt32:
  258. builder.Append("uint32");
  259. break;
  260. case KnownTypeCode.Int64:
  261. builder.Append("int64");
  262. break;
  263. case KnownTypeCode.UInt64:
  264. builder.Append("uint64");
  265. break;
  266. case KnownTypeCode.Single:
  267. builder.Append("float32");
  268. break;
  269. case KnownTypeCode.Double:
  270. builder.Append("float64");
  271. break;
  272. case KnownTypeCode.String:
  273. builder.Append("string");
  274. break;
  275. case KnownTypeCode.Void:
  276. builder.Append("void");
  277. break;
  278. case KnownTypeCode.IntPtr:
  279. builder.Append("native int");
  280. break;
  281. case KnownTypeCode.UIntPtr:
  282. builder.Append("native uint");
  283. break;
  284. case KnownTypeCode.TypedReference:
  285. builder.Append("typedref");
  286. break;
  287. default:
  288. WriteType(type);
  289. break;
  290. }
  291. return type;
  292. }
  293. }
  294. #endregion
  295. /// <summary>
  296. /// Converts a member signature to a string.
  297. /// This is used for displaying the tooltip on a member reference.
  298. /// </summary>
  299. public virtual string GetTooltip(IEntity entity)
  300. {
  301. return GetDisplayName(entity, true, true, true);
  302. }
  303. /// <summary>
  304. /// Converts a member signature to a string.
  305. /// This is used for displaying the tooltip on a member reference.
  306. /// </summary>
  307. public virtual RichText GetRichTextTooltip(IEntity entity)
  308. {
  309. return GetTooltip(entity);
  310. }
  311. public virtual string FieldToString(IField field, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
  312. {
  313. if (field == null)
  314. throw new ArgumentNullException(nameof(field));
  315. return GetDisplayName(field, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName) + " : " + TypeToString(field.ReturnType, includeNamespace);
  316. }
  317. public virtual string PropertyToString(IProperty property, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
  318. {
  319. if (property == null)
  320. throw new ArgumentNullException(nameof(property));
  321. return GetDisplayName(property, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName) + " : " + TypeToString(property.ReturnType, includeNamespace);
  322. }
  323. public virtual string MethodToString(IMethod method, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
  324. {
  325. if (method == null)
  326. throw new ArgumentNullException(nameof(method));
  327. int i = 0;
  328. var buffer = new StringBuilder();
  329. buffer.Append(GetDisplayName(method, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName));
  330. var typeParameters = method.TypeParameters;
  331. if (typeParameters.Count > 0) {
  332. buffer.Append("``");
  333. buffer.Append(typeParameters.Count);
  334. buffer.Append('<');
  335. foreach (var tp in typeParameters) {
  336. if (i > 0)
  337. buffer.Append(", ");
  338. buffer.Append(tp.Name);
  339. i++;
  340. }
  341. buffer.Append('>');
  342. }
  343. buffer.Append('(');
  344. i = 0;
  345. var parameters = method.Parameters;
  346. foreach (var param in parameters) {
  347. if (i > 0)
  348. buffer.Append(", ");
  349. buffer.Append(TypeToString(param.Type, includeNamespace));
  350. i++;
  351. }
  352. buffer.Append(')');
  353. if (!method.IsConstructor) {
  354. buffer.Append(" : ");
  355. buffer.Append(TypeToString(method.ReturnType, includeNamespace));
  356. }
  357. return buffer.ToString();
  358. }
  359. public virtual string EventToString(IEvent @event, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
  360. {
  361. if (@event == null)
  362. throw new ArgumentNullException(nameof(@event));
  363. var buffer = new StringBuilder();
  364. buffer.Append(GetDisplayName(@event, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName));
  365. buffer.Append(" : ");
  366. buffer.Append(TypeToString(@event.ReturnType, includeNamespace));
  367. return buffer.ToString();
  368. }
  369. protected string GetDisplayName(IEntity entity, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
  370. {
  371. string entityName;
  372. if (entity is ITypeDefinition t && !t.MetadataToken.IsNil) {
  373. MetadataReader metadata = t.ParentModule.PEFile.Metadata;
  374. var typeDef = metadata.GetTypeDefinition((TypeDefinitionHandle)t.MetadataToken);
  375. entityName = EscapeName(metadata.GetString(typeDef.Name));
  376. } else {
  377. entityName = EscapeName(entity.Name);
  378. }
  379. if (includeNamespace || includeDeclaringTypeName) {
  380. if (entity.DeclaringTypeDefinition != null)
  381. return TypeToString(entity.DeclaringTypeDefinition, includeNamespaceOfDeclaringTypeName) + "." + entityName;
  382. return EscapeName(entity.Namespace) + "." + entityName;
  383. } else {
  384. return entityName;
  385. }
  386. }
  387. /// <summary>
  388. /// Used for WPF keyboard navigation.
  389. /// </summary>
  390. public override string ToString()
  391. {
  392. return Name;
  393. }
  394. public virtual bool ShowMember(IEntity member)
  395. {
  396. return true;
  397. }
  398. /// <summary>
  399. /// This should produce a string representation of the entity for search to match search strings against.
  400. /// </summary>
  401. public virtual string GetEntityName(PEFile module, EntityHandle handle, bool fullName, bool omitGenerics)
  402. {
  403. MetadataReader metadata = module.Metadata;
  404. switch (handle.Kind) {
  405. case HandleKind.TypeDefinition:
  406. if (fullName)
  407. return EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString(omitGenerics));
  408. var td = metadata.GetTypeDefinition((TypeDefinitionHandle)handle);
  409. return EscapeName(metadata.GetString(td.Name));
  410. case HandleKind.FieldDefinition:
  411. var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle);
  412. if (fullName)
  413. return EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(fd.Name));
  414. return EscapeName(metadata.GetString(fd.Name));
  415. case HandleKind.MethodDefinition:
  416. var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle);
  417. string methodName = metadata.GetString(md.Name);
  418. if (!omitGenerics) {
  419. int genericParamCount = md.GetGenericParameters().Count;
  420. if (genericParamCount > 0)
  421. methodName += "``" + genericParamCount;
  422. }
  423. if (fullName)
  424. return EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + methodName);
  425. return EscapeName(methodName);
  426. case HandleKind.EventDefinition:
  427. var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle);
  428. var declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType();
  429. if (fullName)
  430. return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(ed.Name));
  431. return EscapeName(metadata.GetString(ed.Name));
  432. case HandleKind.PropertyDefinition:
  433. var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle);
  434. declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType();
  435. if (fullName)
  436. return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(pd.Name));
  437. return EscapeName(metadata.GetString(pd.Name));
  438. default:
  439. return null;
  440. }
  441. }
  442. public virtual CodeMappingInfo GetCodeMappingInfo(PEFile module, SRM.EntityHandle member)
  443. {
  444. var declaringType = member.GetDeclaringType(module.Metadata);
  445. if (declaringType.IsNil && member.Kind == SRM.HandleKind.TypeDefinition) {
  446. declaringType = (SRM.TypeDefinitionHandle)member;
  447. }
  448. return new CodeMappingInfo(module, declaringType);
  449. }
  450. public static string GetPlatformDisplayName(PEFile module)
  451. {
  452. var headers = module.Reader.PEHeaders;
  453. var architecture = headers.CoffHeader.Machine;
  454. var characteristics = headers.CoffHeader.Characteristics;
  455. var corflags = headers.CorHeader.Flags;
  456. switch (architecture) {
  457. case Machine.I386:
  458. if ((corflags & CorFlags.Prefers32Bit) != 0)
  459. return "AnyCPU (32-bit preferred)";
  460. if ((corflags & CorFlags.Requires32Bit) != 0)
  461. return "x86";
  462. // According to ECMA-335, II.25.3.3.1 CorFlags.Requires32Bit and Characteristics.Bit32Machine must be in sync
  463. // for assemblies containing managed code. However, this is not true for C++/CLI assemblies.
  464. if ((corflags & CorFlags.ILOnly) == 0 && (characteristics & Characteristics.Bit32Machine) != 0)
  465. return "x86";
  466. return "AnyCPU (64-bit preferred)";
  467. case Machine.Amd64:
  468. return "x64";
  469. case Machine.IA64:
  470. return "Itanium";
  471. default:
  472. return architecture.ToString();
  473. }
  474. }
  475. public static string GetRuntimeDisplayName(PEFile module)
  476. {
  477. return module.Metadata.MetadataVersion;
  478. }
  479. /// <summary>
  480. /// Escape characters that cannot be displayed in the UI.
  481. /// </summary>
  482. public static StringBuilder EscapeName(StringBuilder sb, string name)
  483. {
  484. foreach (char ch in name) {
  485. if (char.IsWhiteSpace(ch) || char.IsControl(ch) || char.IsSurrogate(ch))
  486. sb.AppendFormat("\\u{0:x4}", (int)ch);
  487. else
  488. sb.Append(ch);
  489. }
  490. return sb;
  491. }
  492. /// <summary>
  493. /// Escape characters that cannot be displayed in the UI.
  494. /// </summary>
  495. public static string EscapeName(string name)
  496. {
  497. return EscapeName(new StringBuilder(name.Length), name).ToString();
  498. }
  499. }
  500. }