PageRenderTime 72ms CodeModel.GetById 16ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 0ms

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