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 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(")");
        }
        protected virtual void WriteInstruction(ITextOutput output, MetadataReader metadata, MethodDefinitionHandle methodDefinition, ref BlobReader blob)
        {
            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);

            output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true);
            output.Write(": ");
            if (opCode.IsDefined())
            {
                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:
                    int[] targets = ILParser.DecodeSwitchTargets(ref blob);
                    output.Write(" (");
                    for (int i = 0; i < targets.Length; i++)
                    {
                        if (i > 0)
                        {
                            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, methodDefinition, index);
                    }
                    else
                    {
                        DisassemblerHelpers.WriteParameterReference(output, metadata, methodDefinition, 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, methodDefinition, index);
                    }
                    else
                    {
                        DisassemblerHelpers.WriteParameterReference(output, metadata, methodDefinition, index);
                    }
                    break;
                }
            }
            else
            {
                ushort opCodeValue = (ushort)opCode;
                if (opCodeValue > 0xFF)
                {
                    // split 16-bit value into two emitbyte directives
                    output.WriteLine($".emitbyte 0x{(byte)(opCodeValue >> 8):x}");
                    // add label
                    output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset + 1), offset + 1, isDefinition: true);
                    output.Write(": ");
                    output.Write($".emitbyte 0x{(byte)(opCodeValue & 0xFF):x}");
                }
                else
                {
                    output.Write($".emitbyte 0x{(byte)opCodeValue:x}");
                }
            }
            output.WriteLine();
        }