mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			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.
		
		
		
		
		
			
		
			
				
					
					
						
							656 lines
						
					
					
						
							20 KiB
						
					
					
				
			
		
		
	
	
							656 lines
						
					
					
						
							20 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.Collections.Immutable; | 
						|
using System.Reflection.Metadata; | 
						|
using System.Reflection.Metadata.Ecma335; | 
						|
using System.Threading; | 
						|
 | 
						|
using ICSharpCode.Decompiler.DebugInfo; | 
						|
using ICSharpCode.Decompiler.IL; | 
						|
using ICSharpCode.Decompiler.Metadata; | 
						|
using ICSharpCode.Decompiler.Util; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.Disassembler | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Disassembles a method body. | 
						|
	/// </summary> | 
						|
	public class MethodBodyDisassembler | 
						|
	{ | 
						|
		readonly ITextOutput output; | 
						|
		readonly CancellationToken cancellationToken; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Show .try/finally as blocks in IL code; indent loops. | 
						|
		/// </summary> | 
						|
		public bool DetectControlStructure { get; set; } = true; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Show sequence points if debug information is loaded in Cecil. | 
						|
		/// </summary> | 
						|
		public bool ShowSequencePoints { get; set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Show metadata tokens for instructions with token operands. | 
						|
		/// </summary> | 
						|
		public bool ShowMetadataTokens { get; set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Show metadata tokens for instructions with token operands in base 10. | 
						|
		/// </summary> | 
						|
		public bool ShowMetadataTokensInBase10 { get; set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Show raw RVA offset and bytes before each instruction. | 
						|
		/// </summary> | 
						|
		public bool ShowRawRVAOffsetAndBytes { get; set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Optional provider for sequence points. | 
						|
		/// </summary> | 
						|
		public IDebugInfoProvider DebugInfo { get; set; } | 
						|
 | 
						|
		IList<DebugInfo.SequencePoint> sequencePoints; | 
						|
		int nextSequencePointIndex; | 
						|
 | 
						|
		// cache info | 
						|
		PEFile module; | 
						|
		MetadataReader metadata; | 
						|
		MetadataGenericContext genericContext; | 
						|
		DisassemblerSignatureTypeProvider signatureDecoder; | 
						|
 | 
						|
		public MethodBodyDisassembler(ITextOutput output, CancellationToken cancellationToken) | 
						|
		{ | 
						|
			this.output = output ?? throw new ArgumentNullException(nameof(output)); | 
						|
			this.cancellationToken = cancellationToken; | 
						|
		} | 
						|
 | 
						|
		public virtual void Disassemble(PEFile module, MethodDefinitionHandle handle) | 
						|
		{ | 
						|
			this.module = module ?? throw new ArgumentNullException(nameof(module)); | 
						|
			metadata = module.Metadata; | 
						|
			genericContext = new MetadataGenericContext(handle, module); | 
						|
			signatureDecoder = new DisassemblerSignatureTypeProvider(module, output); | 
						|
			var methodDefinition = metadata.GetMethodDefinition(handle); | 
						|
 | 
						|
			// start writing IL code | 
						|
			output.WriteLine("// Method begins at RVA 0x{0:x4}", methodDefinition.RelativeVirtualAddress); | 
						|
			if (methodDefinition.RelativeVirtualAddress == 0) | 
						|
			{ | 
						|
				output.WriteLine("// Header size: {0}", 0); | 
						|
				output.WriteLine("// Code size: {0} (0x{0:x})", 0); | 
						|
				output.WriteLine(".maxstack {0}", 0); | 
						|
				output.WriteLine(); | 
						|
				return; | 
						|
			} | 
						|
			MethodBodyBlock body; | 
						|
			BlobReader bodyBlockReader; | 
						|
			try | 
						|
			{ | 
						|
				body = module.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); | 
						|
				bodyBlockReader = module.Reader.GetSectionData(methodDefinition.RelativeVirtualAddress).GetReader(); | 
						|
			} | 
						|
			catch (BadImageFormatException ex) | 
						|
			{ | 
						|
				output.WriteLine("// {0}", ex.Message); | 
						|
				return; | 
						|
			} | 
						|
			var blob = body.GetILReader(); | 
						|
			int headerSize = ILParser.GetHeaderSize(bodyBlockReader); | 
						|
			output.WriteLine("// Header size: {0}", headerSize); | 
						|
			output.WriteLine("// Code size: {0} (0x{0:x})", blob.Length); | 
						|
			output.WriteLine(".maxstack {0}", body.MaxStack); | 
						|
 | 
						|
			var entrypointHandle = MetadataTokens.MethodDefinitionHandle(module.Reader.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); | 
						|
			if (handle == entrypointHandle) | 
						|
				output.WriteLine(".entrypoint"); | 
						|
 | 
						|
			DisassembleLocalsBlock(handle, body); | 
						|
			output.WriteLine(); | 
						|
 | 
						|
			sequencePoints = DebugInfo?.GetSequencePoints(handle) ?? EmptyList<DebugInfo.SequencePoint>.Instance; | 
						|
			nextSequencePointIndex = 0; | 
						|
			if (DetectControlStructure && blob.Length > 0) | 
						|
			{ | 
						|
				blob.Reset(); | 
						|
				HashSet<int> branchTargets = GetBranchTargets(blob); | 
						|
				blob.Reset(); | 
						|
				WriteStructureBody(new ILStructure(module, handle, genericContext, body), branchTargets, ref blob, methodDefinition.RelativeVirtualAddress + headerSize); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				while (blob.RemainingBytes > 0) | 
						|
				{ | 
						|
					cancellationToken.ThrowIfCancellationRequested(); | 
						|
					WriteInstruction(output, metadata, handle, ref blob, methodDefinition.RelativeVirtualAddress); | 
						|
				} | 
						|
				WriteExceptionHandlers(module, handle, body); | 
						|
			} | 
						|
			sequencePoints = null; | 
						|
		} | 
						|
 | 
						|
		void DisassembleLocalsBlock(MethodDefinitionHandle method, MethodBodyBlock body) | 
						|
		{ | 
						|
			if (body.LocalSignature.IsNil) | 
						|
				return; | 
						|
			output.Write(".locals"); | 
						|
			WriteMetadataToken(body.LocalSignature, spaceBefore: true); | 
						|
			if (body.LocalVariablesInitialized) | 
						|
				output.Write(" init"); | 
						|
			var blob = metadata.GetStandaloneSignature(body.LocalSignature); | 
						|
			var signature = ImmutableArray<Action<ILNameSyntax>>.Empty; | 
						|
			try | 
						|
			{ | 
						|
				if (blob.GetKind() == StandaloneSignatureKind.LocalVariables) | 
						|
				{ | 
						|
					signature = blob.DecodeLocalSignature(signatureDecoder, genericContext); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					output.Write(" /* wrong signature kind */"); | 
						|
				} | 
						|
			} | 
						|
			catch (BadImageFormatException ex) | 
						|
			{ | 
						|
				output.Write($" /* {ex.Message} */"); | 
						|
			} | 
						|
			output.Write(' '); | 
						|
			output.WriteLine("("); | 
						|
			output.Indent(); | 
						|
			int index = 0; | 
						|
			foreach (var v in signature) | 
						|
			{ | 
						|
				output.WriteLocalReference("[" + index + "]", "loc_" + index, isDefinition: true); | 
						|
				output.Write(' '); | 
						|
				v(ILNameSyntax.TypeName); | 
						|
				if (DebugInfo != null && DebugInfo.TryGetName(method, index, out var name)) | 
						|
				{ | 
						|
					output.Write(" " + DisassemblerHelpers.Escape(name)); | 
						|
				} | 
						|
				if (index + 1 < signature.Length) | 
						|
					output.Write(','); | 
						|
				output.WriteLine(); | 
						|
				index++; | 
						|
			} | 
						|
			output.Unindent(); | 
						|
			output.WriteLine(")"); | 
						|
		} | 
						|
 | 
						|
		internal void WriteExceptionHandlers(PEFile module, MethodDefinitionHandle handle, MethodBodyBlock body) | 
						|
		{ | 
						|
			this.module = module; | 
						|
			metadata = module.Metadata; | 
						|
			genericContext = new MetadataGenericContext(handle, module); | 
						|
			signatureDecoder = new DisassemblerSignatureTypeProvider(module, output); | 
						|
			var handlers = body.ExceptionRegions; | 
						|
			if (!handlers.IsEmpty) | 
						|
			{ | 
						|
				output.WriteLine(); | 
						|
				foreach (var eh in handlers) | 
						|
				{ | 
						|
					eh.WriteTo(module, genericContext, output); | 
						|
					output.WriteLine(); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		HashSet<int> GetBranchTargets(BlobReader blob) | 
						|
		{ | 
						|
			HashSet<int> branchTargets = new HashSet<int>(); | 
						|
			while (blob.RemainingBytes > 0) | 
						|
			{ | 
						|
				var opCode = ILParser.DecodeOpCode(ref blob); | 
						|
				if (opCode == ILOpCode.Switch) | 
						|
				{ | 
						|
					branchTargets.UnionWith(ILParser.DecodeSwitchTargets(ref blob)); | 
						|
				} | 
						|
				else if (opCode.IsBranch()) | 
						|
				{ | 
						|
					branchTargets.Add(ILParser.DecodeBranchTarget(ref blob, opCode)); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					ILParser.SkipOperand(ref blob, opCode); | 
						|
				} | 
						|
			} | 
						|
			return branchTargets; | 
						|
		} | 
						|
 | 
						|
		void WriteStructureHeader(ILStructure s) | 
						|
		{ | 
						|
			switch (s.Type) | 
						|
			{ | 
						|
				case ILStructureType.Loop: | 
						|
					output.Write("// loop start"); | 
						|
					if (s.LoopEntryPointOffset >= 0) | 
						|
					{ | 
						|
						output.Write(" (head: "); | 
						|
						DisassemblerHelpers.WriteOffsetReference(output, s.LoopEntryPointOffset); | 
						|
						output.Write(')'); | 
						|
					} | 
						|
					output.WriteLine(); | 
						|
					break; | 
						|
				case ILStructureType.Try: | 
						|
					output.WriteLine(".try"); | 
						|
					output.WriteLine("{"); | 
						|
					break; | 
						|
				case ILStructureType.Handler: | 
						|
					switch (s.ExceptionHandler.Kind) | 
						|
					{ | 
						|
						case ExceptionRegionKind.Catch: | 
						|
						case ExceptionRegionKind.Filter: | 
						|
							output.Write("catch"); | 
						|
							if (!s.ExceptionHandler.CatchType.IsNil) | 
						|
							{ | 
						|
								output.Write(' '); | 
						|
								s.ExceptionHandler.CatchType.WriteTo(s.Module, output, s.GenericContext, ILNameSyntax.TypeName); | 
						|
							} | 
						|
							output.WriteLine(); | 
						|
							break; | 
						|
						case ExceptionRegionKind.Finally: | 
						|
							output.WriteLine("finally"); | 
						|
							break; | 
						|
						case ExceptionRegionKind.Fault: | 
						|
							output.WriteLine("fault"); | 
						|
							break; | 
						|
						default: | 
						|
							throw new ArgumentOutOfRangeException(); | 
						|
					} | 
						|
					output.WriteLine("{"); | 
						|
					break; | 
						|
				case ILStructureType.Filter: | 
						|
					output.WriteLine("filter"); | 
						|
					output.WriteLine("{"); | 
						|
					break; | 
						|
				default: | 
						|
					throw new ArgumentOutOfRangeException(); | 
						|
			} | 
						|
			output.Indent(); | 
						|
		} | 
						|
 | 
						|
		void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref BlobReader body, int methodRva) | 
						|
		{ | 
						|
			bool isFirstInstructionInStructure = true; | 
						|
			bool prevInstructionWasBranch = false; | 
						|
			int childIndex = 0; | 
						|
			while (body.RemainingBytes > 0 && body.Offset < s.EndOffset) | 
						|
			{ | 
						|
				cancellationToken.ThrowIfCancellationRequested(); | 
						|
				int offset = body.Offset; | 
						|
				if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) | 
						|
				{ | 
						|
					ILStructure child = s.Children[childIndex++]; | 
						|
					WriteStructureHeader(child); | 
						|
					WriteStructureBody(child, branchTargets, ref body, methodRva); | 
						|
					WriteStructureFooter(child); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) | 
						|
					{ | 
						|
						output.WriteLine(); // put an empty line after branches, and in front of branch targets | 
						|
					} | 
						|
					var currentOpCode = ILParser.DecodeOpCode(ref body); | 
						|
					body.Offset = offset; // reset IL stream | 
						|
					WriteInstruction(output, metadata, s.MethodHandle, ref body, methodRva); | 
						|
					prevInstructionWasBranch = currentOpCode.IsBranch() | 
						|
						|| currentOpCode.IsReturn() | 
						|
						|| currentOpCode == ILOpCode.Throw | 
						|
						|| currentOpCode == ILOpCode.Rethrow | 
						|
						|| currentOpCode == ILOpCode.Switch; | 
						|
				} | 
						|
				isFirstInstructionInStructure = false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		void WriteStructureFooter(ILStructure s) | 
						|
		{ | 
						|
			output.Unindent(); | 
						|
			switch (s.Type) | 
						|
			{ | 
						|
				case ILStructureType.Loop: | 
						|
					output.WriteLine("// end loop"); | 
						|
					break; | 
						|
				case ILStructureType.Try: | 
						|
					output.WriteLine("} // end .try"); | 
						|
					break; | 
						|
				case ILStructureType.Handler: | 
						|
					output.WriteLine("} // end handler"); | 
						|
					break; | 
						|
				case ILStructureType.Filter: | 
						|
					output.WriteLine("} // end filter"); | 
						|
					break; | 
						|
				default: | 
						|
					throw new ArgumentOutOfRangeException(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected virtual void WriteInstruction(ITextOutput output, MetadataReader metadata, MethodDefinitionHandle methodHandle, ref BlobReader blob, int methodRva) | 
						|
		{ | 
						|
			int offset = blob.Offset; | 
						|
			if (ShowSequencePoints && nextSequencePointIndex < sequencePoints?.Count) | 
						|
			{ | 
						|
				var sp = sequencePoints[nextSequencePointIndex]; | 
						|
				if (sp.Offset <= offset) | 
						|
				{ | 
						|
					output.Write("// sequence point: "); | 
						|
					if (sp.Offset != offset) | 
						|
					{ | 
						|
						output.Write("!! at " + DisassemblerHelpers.OffsetToString(sp.Offset) + " !!"); | 
						|
					} | 
						|
					if (sp.IsHidden) | 
						|
					{ | 
						|
						output.WriteLine("hidden"); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						output.WriteLine($"(line {sp.StartLine}, col {sp.StartColumn}) to (line {sp.EndLine}, col {sp.EndColumn}) in {sp.DocumentUrl}"); | 
						|
					} | 
						|
					nextSequencePointIndex++; | 
						|
				} | 
						|
			} | 
						|
			ILOpCode opCode = ILParser.DecodeOpCode(ref blob); | 
						|
			if (opCode.IsDefined()) | 
						|
			{ | 
						|
				WriteRVA(blob, offset + methodRva, opCode); | 
						|
				output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); | 
						|
				output.Write(": "); | 
						|
				WriteOpCode(opCode); | 
						|
				switch (opCode.GetOperandType()) | 
						|
				{ | 
						|
					case OperandType.BrTarget: | 
						|
					case OperandType.ShortBrTarget: | 
						|
						output.Write(' '); | 
						|
						int targetOffset = ILParser.DecodeBranchTarget(ref blob, opCode); | 
						|
						output.WriteLocalReference($"IL_{targetOffset:x4}", targetOffset); | 
						|
						break; | 
						|
					case OperandType.Field: | 
						|
					case OperandType.Method: | 
						|
					case OperandType.Sig: | 
						|
					case OperandType.Type: | 
						|
						output.Write(' '); | 
						|
						int metadataToken = blob.ReadInt32(); | 
						|
						EntityHandle? handle = MetadataTokenHelpers.TryAsEntityHandle(metadataToken); | 
						|
						try | 
						|
						{ | 
						|
							handle?.WriteTo(module, output, genericContext); | 
						|
						} | 
						|
						catch (BadImageFormatException) | 
						|
						{ | 
						|
							handle = null; | 
						|
						} | 
						|
						WriteMetadataToken(handle, metadataToken, spaceBefore: true); | 
						|
						break; | 
						|
					case OperandType.Tok: | 
						|
						output.Write(' '); | 
						|
						metadataToken = blob.ReadInt32(); | 
						|
						handle = MetadataTokenHelpers.TryAsEntityHandle(metadataToken); | 
						|
						switch (handle?.Kind) | 
						|
						{ | 
						|
							case HandleKind.MemberReference: | 
						|
								switch (metadata.GetMemberReference((MemberReferenceHandle)handle).GetKind()) | 
						|
								{ | 
						|
									case MemberReferenceKind.Method: | 
						|
										output.Write("method "); | 
						|
										break; | 
						|
									case MemberReferenceKind.Field: | 
						|
										output.Write("field "); | 
						|
										break; | 
						|
								} | 
						|
								break; | 
						|
							case HandleKind.FieldDefinition: | 
						|
								output.Write("field "); | 
						|
								break; | 
						|
							case HandleKind.MethodDefinition: | 
						|
								output.Write("method "); | 
						|
								break; | 
						|
						} | 
						|
						try | 
						|
						{ | 
						|
							handle?.WriteTo(module, output, genericContext); | 
						|
						} | 
						|
						catch (BadImageFormatException) | 
						|
						{ | 
						|
							handle = null; | 
						|
						} | 
						|
						WriteMetadataToken(handle, metadataToken, spaceBefore: true); | 
						|
						break; | 
						|
					case OperandType.ShortI: | 
						|
						output.Write(' '); | 
						|
						DisassemblerHelpers.WriteOperand(output, blob.ReadSByte()); | 
						|
						break; | 
						|
					case OperandType.I: | 
						|
						output.Write(' '); | 
						|
						DisassemblerHelpers.WriteOperand(output, blob.ReadInt32()); | 
						|
						break; | 
						|
					case OperandType.I8: | 
						|
						output.Write(' '); | 
						|
						DisassemblerHelpers.WriteOperand(output, blob.ReadInt64()); | 
						|
						break; | 
						|
					case OperandType.ShortR: | 
						|
						output.Write(' '); | 
						|
						DisassemblerHelpers.WriteOperand(output, blob.ReadSingle()); | 
						|
						break; | 
						|
					case OperandType.R: | 
						|
						output.Write(' '); | 
						|
						DisassemblerHelpers.WriteOperand(output, blob.ReadDouble()); | 
						|
						break; | 
						|
					case OperandType.String: | 
						|
						metadataToken = blob.ReadInt32(); | 
						|
						output.Write(' '); | 
						|
						UserStringHandle? userString; | 
						|
						string text; | 
						|
						try | 
						|
						{ | 
						|
							userString = MetadataTokens.UserStringHandle(metadataToken); | 
						|
							text = metadata.GetUserString(userString.Value); | 
						|
						} | 
						|
						catch (BadImageFormatException) | 
						|
						{ | 
						|
							userString = null; | 
						|
							text = null; | 
						|
						} | 
						|
						if (userString != null) | 
						|
						{ | 
						|
							DisassemblerHelpers.WriteOperand(output, text); | 
						|
						} | 
						|
						WriteMetadataToken(userString, metadataToken, spaceBefore: true); | 
						|
						break; | 
						|
					case OperandType.Switch: | 
						|
						var tmp = blob; | 
						|
						int[] targets = ILParser.DecodeSwitchTargets(ref blob); | 
						|
						if (ShowRawRVAOffsetAndBytes) | 
						|
						{ | 
						|
							output.WriteLine(" ("); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							output.Write(" ("); | 
						|
						} | 
						|
						tmp.ReadInt32(); | 
						|
						for (int i = 0; i < targets.Length; i++) | 
						|
						{ | 
						|
							if (i > 0) | 
						|
							{ | 
						|
								if (ShowRawRVAOffsetAndBytes) | 
						|
								{ | 
						|
									output.WriteLine(","); | 
						|
								} | 
						|
								else | 
						|
								{ | 
						|
									output.Write(", "); | 
						|
								} | 
						|
							} | 
						|
							if (ShowRawRVAOffsetAndBytes) | 
						|
							{ | 
						|
								output.Write("/*              "); | 
						|
								output.Write($"{tmp.ReadByte():X2}{tmp.ReadByte():X2}{tmp.ReadByte():X2}{tmp.ReadByte():X2}"); | 
						|
								output.Write("         */ "); | 
						|
							} | 
						|
							if (ShowRawRVAOffsetAndBytes) | 
						|
							{ | 
						|
								output.Write("                 "); | 
						|
							} | 
						|
							output.WriteLocalReference($"IL_{targets[i]:x4}", targets[i]); | 
						|
						} | 
						|
						output.Write(")"); | 
						|
						break; | 
						|
					case OperandType.Variable: | 
						|
						output.Write(' '); | 
						|
						int index = blob.ReadUInt16(); | 
						|
						if (opCode == ILOpCode.Ldloc || opCode == ILOpCode.Ldloca || opCode == ILOpCode.Stloc) | 
						|
						{ | 
						|
							DisassemblerHelpers.WriteVariableReference(output, metadata, methodHandle, index); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							DisassemblerHelpers.WriteParameterReference(output, metadata, methodHandle, index); | 
						|
						} | 
						|
						break; | 
						|
					case OperandType.ShortVariable: | 
						|
						output.Write(' '); | 
						|
						index = blob.ReadByte(); | 
						|
						if (opCode == ILOpCode.Ldloc_s || opCode == ILOpCode.Ldloca_s || opCode == ILOpCode.Stloc_s) | 
						|
						{ | 
						|
							DisassemblerHelpers.WriteVariableReference(output, metadata, methodHandle, index); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							DisassemblerHelpers.WriteParameterReference(output, metadata, methodHandle, index); | 
						|
						} | 
						|
						break; | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				ushort opCodeValue = (ushort)opCode; | 
						|
				if (opCodeValue > 0xFF) | 
						|
				{ | 
						|
					if (ShowRawRVAOffsetAndBytes) | 
						|
					{ | 
						|
						output.Write("/* "); | 
						|
						output.Write($"0x{offset + methodRva:X8} {(ushort)opCode >> 8:X2}"); | 
						|
						output.Write("                 */ "); | 
						|
					} | 
						|
					output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); | 
						|
					output.Write(": "); | 
						|
					// split 16-bit value into two emitbyte directives | 
						|
					output.WriteLine($".emitbyte 0x{(byte)(opCodeValue >> 8):x}"); | 
						|
					if (ShowRawRVAOffsetAndBytes) | 
						|
					{ | 
						|
						output.Write("/* "); | 
						|
						output.Write($"0x{offset + methodRva + 1:X8} {(ushort)opCode & 0xFF:X2}"); | 
						|
						output.Write("                 */ "); | 
						|
					} | 
						|
					// add label | 
						|
					output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset + 1), offset + 1, isDefinition: true); | 
						|
					output.Write(": "); | 
						|
					output.Write($".emitbyte 0x{(byte)(opCodeValue & 0xFF):x}"); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					if (ShowRawRVAOffsetAndBytes) | 
						|
					{ | 
						|
						output.Write("/* "); | 
						|
						output.Write($"0x{offset + methodRva:X8} {(ushort)opCode & 0xFF:X2}"); | 
						|
						output.Write("                 */ "); | 
						|
					} | 
						|
					output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); | 
						|
					output.Write(": "); | 
						|
					output.Write($".emitbyte 0x{(byte)opCodeValue:x}"); | 
						|
				} | 
						|
			} | 
						|
			output.WriteLine(); | 
						|
		} | 
						|
 | 
						|
		void WriteRVA(BlobReader blob, int offset, ILOpCode opCode) | 
						|
		{ | 
						|
			if (ShowRawRVAOffsetAndBytes) | 
						|
			{ | 
						|
				output.Write("/* "); | 
						|
				var tmp = blob; | 
						|
				if (opCode == ILOpCode.Switch) | 
						|
				{ | 
						|
					tmp.ReadInt32(); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					ILParser.SkipOperand(ref tmp, opCode); | 
						|
				} | 
						|
				output.Write($"0x{offset:X8} {(ushort)opCode:X2}"); | 
						|
				int appendSpaces = (ushort)opCode > 0xFF ? 14 : 16; | 
						|
				while (blob.Offset < tmp.Offset) | 
						|
				{ | 
						|
					output.Write($"{blob.ReadByte():X2}"); | 
						|
					appendSpaces -= 2; | 
						|
				} | 
						|
				if (appendSpaces > 0) | 
						|
				{ | 
						|
					output.Write(new string(' ', appendSpaces)); | 
						|
				} | 
						|
				output.Write(" */ "); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private void WriteOpCode(ILOpCode opCode) | 
						|
		{ | 
						|
			var opCodeInfo = new OpCodeInfo(opCode, opCode.GetDisplayName()); | 
						|
			string index; | 
						|
			switch (opCode) | 
						|
			{ | 
						|
				case ILOpCode.Ldarg_0: | 
						|
				case ILOpCode.Ldarg_1: | 
						|
				case ILOpCode.Ldarg_2: | 
						|
				case ILOpCode.Ldarg_3: | 
						|
					output.WriteReference(opCodeInfo, omitSuffix: true); | 
						|
					index = opCodeInfo.Name.Substring(6); | 
						|
					output.WriteLocalReference(index, "param_" + index); | 
						|
					break; | 
						|
				case ILOpCode.Ldloc_0: | 
						|
				case ILOpCode.Ldloc_1: | 
						|
				case ILOpCode.Ldloc_2: | 
						|
				case ILOpCode.Ldloc_3: | 
						|
				case ILOpCode.Stloc_0: | 
						|
				case ILOpCode.Stloc_1: | 
						|
				case ILOpCode.Stloc_2: | 
						|
				case ILOpCode.Stloc_3: | 
						|
					output.WriteReference(opCodeInfo, omitSuffix: true); | 
						|
					index = opCodeInfo.Name.Substring(6); | 
						|
					output.WriteLocalReference(index, "loc_" + index); | 
						|
					break; | 
						|
				default: | 
						|
					output.WriteReference(opCodeInfo); | 
						|
					break; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private void WriteMetadataToken(EntityHandle handle, bool spaceBefore) | 
						|
		{ | 
						|
			WriteMetadataToken(handle, MetadataTokens.GetToken(handle), spaceBefore); | 
						|
		} | 
						|
 | 
						|
		private void WriteMetadataToken(Handle? handle, int metadataToken, bool spaceBefore) | 
						|
		{ | 
						|
			ReflectionDisassembler.WriteMetadataToken(output, module, handle, metadataToken, | 
						|
				spaceAfter: false, spaceBefore, ShowMetadataTokens, ShowMetadataTokensInBase10); | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |