File: System\Reflection\Metadata\IL\MethodBodyBlock.cs
Web Access
Project: src\src\libraries\System.Reflection.Metadata\src\System.Reflection.Metadata.csproj (System.Reflection.Metadata)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Internal;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
 
namespace System.Reflection.Metadata
{
    public sealed class MethodBodyBlock
    {
        private readonly MemoryBlock _il;
        private readonly int _size;
        private readonly ushort _maxStack;
        private readonly bool _localVariablesInitialized;
        private readonly StandaloneSignatureHandle _localSignature;
        private readonly ImmutableArray<ExceptionRegion> _exceptionRegions;
 
        private MethodBodyBlock(
            bool localVariablesInitialized,
            ushort maxStack,
            StandaloneSignatureHandle localSignatureHandle,
            MemoryBlock il,
            ImmutableArray<ExceptionRegion> exceptionRegions,
            int size)
        {
            Debug.Assert(!exceptionRegions.IsDefault);
 
            _localVariablesInitialized = localVariablesInitialized;
            _maxStack = maxStack;
            _localSignature = localSignatureHandle;
            _il = il;
            _exceptionRegions = exceptionRegions;
            _size = size;
        }
 
        /// <summary>
        /// Size of the method body - includes the header, IL and exception regions.
        /// </summary>
        public int Size
        {
            get { return _size; }
        }
 
        public int MaxStack
        {
            get { return _maxStack; }
        }
 
        public bool LocalVariablesInitialized
        {
            get { return _localVariablesInitialized; }
        }
 
        public StandaloneSignatureHandle LocalSignature
        {
            get { return _localSignature; }
        }
 
        public ImmutableArray<ExceptionRegion> ExceptionRegions
        {
            get { return _exceptionRegions; }
        }
 
        public byte[]? GetILBytes()
        {
            return _il.ToArray();
        }
 
        public ImmutableArray<byte> GetILContent()
        {
            byte[]? bytes = GetILBytes();
            return ImmutableCollectionsMarshal.AsImmutableArray(bytes);
        }
 
        public BlobReader GetILReader()
        {
            return new BlobReader(_il);
        }
 
        private const byte ILTinyFormat = 0x02;
        private const byte ILFatFormat = 0x03;
        private const byte ILFormatMask = 0x03;
        private const int ILTinyFormatSizeShift = 2;
        private const byte ILMoreSects = 0x08;
        private const byte ILInitLocals = 0x10;
        private const byte ILFatFormatHeaderSize = 0x03;
        private const int ILFatFormatHeaderSizeShift = 4;
        private const byte SectEHTable = 0x01;
        private const byte SectFatFormat = 0x40;
 
        public static MethodBodyBlock Create(BlobReader reader)
        {
            int startOffset = reader.Offset;
            int ilSize;
 
            // Error need to check if the Memory Block is empty. This is false for all the calls...
            byte headByte = reader.ReadByte();
            if ((headByte & ILFormatMask) == ILTinyFormat)
            {
                // tiny IL can't have locals so technically this shouldn't matter,
                // but false is consistent with other metadata readers and helps
                // for use cases involving comparing our output with theirs.
                const bool initLocalsForTinyIL = false;
 
                ilSize = headByte >> ILTinyFormatSizeShift;
                return new MethodBodyBlock(
                    initLocalsForTinyIL,
                    8,
                    default(StandaloneSignatureHandle),
                    reader.GetMemoryBlockAt(0, ilSize),
                    ImmutableArray<ExceptionRegion>.Empty,
                    1 + ilSize // header + IL
                );
            }
 
            if ((headByte & ILFormatMask) != ILFatFormat)
            {
                throw new BadImageFormatException(SR.Format(SR.InvalidMethodHeader1, headByte));
            }
 
            // FatILFormat
            byte headByte2 = reader.ReadByte();
            if ((headByte2 >> ILFatFormatHeaderSizeShift) != ILFatFormatHeaderSize)
            {
                throw new BadImageFormatException(SR.Format(SR.InvalidMethodHeader2, headByte, headByte2));
            }
 
            bool localsInitialized = (headByte & ILInitLocals) == ILInitLocals;
            bool hasExceptionHandlers = (headByte & ILMoreSects) == ILMoreSects;
 
            ushort maxStack = reader.ReadUInt16();
            ilSize = reader.ReadInt32();
 
            int localSignatureToken = reader.ReadInt32();
            StandaloneSignatureHandle localSignatureHandle;
            if (localSignatureToken == 0)
            {
                localSignatureHandle = default(StandaloneSignatureHandle);
            }
            else if ((localSignatureToken & TokenTypeIds.TypeMask) == TokenTypeIds.Signature)
            {
                localSignatureHandle = StandaloneSignatureHandle.FromRowId((int)((uint)localSignatureToken & TokenTypeIds.RIDMask));
            }
            else
            {
                throw new BadImageFormatException(SR.Format(SR.InvalidLocalSignatureToken, unchecked((uint)localSignatureToken)));
            }
 
            var ilBlock = reader.GetMemoryBlockAt(0, ilSize);
            reader.Offset += ilSize;
 
            ImmutableArray<ExceptionRegion> exceptionHandlers;
            if (hasExceptionHandlers)
            {
                reader.Align(4);
                byte sehHeader = reader.ReadByte();
                if ((sehHeader & SectEHTable) != SectEHTable)
                {
                    throw new BadImageFormatException(SR.Format(SR.InvalidSehHeader, sehHeader));
                }
 
                bool sehFatFormat = (sehHeader & SectFatFormat) == SectFatFormat;
                int dataSize = reader.ReadByte();
                if (sehFatFormat)
                {
                    dataSize += reader.ReadUInt16() << 8;
                    exceptionHandlers = ReadFatExceptionHandlers(ref reader, dataSize / 24);
                }
                else
                {
                    reader.Offset += 2; // skip over reserved field
                    exceptionHandlers = ReadSmallExceptionHandlers(ref reader, dataSize / 12);
                }
            }
            else
            {
                exceptionHandlers = ImmutableArray<ExceptionRegion>.Empty;
            }
 
            return new MethodBodyBlock(
                localsInitialized,
                maxStack,
                localSignatureHandle,
                ilBlock,
                exceptionHandlers,
                reader.Offset - startOffset);
        }
 
        private static ImmutableArray<ExceptionRegion> ReadSmallExceptionHandlers(ref BlobReader memReader, int count)
        {
            var result = new ExceptionRegion[count];
            for (int i = 0; i < result.Length; i++)
            {
                var kind = (ExceptionRegionKind)memReader.ReadUInt16();
                var tryOffset = memReader.ReadUInt16();
                var tryLength = memReader.ReadByte();
                var handlerOffset = memReader.ReadUInt16();
                var handlerLength = memReader.ReadByte();
                var classTokenOrFilterOffset = memReader.ReadInt32();
                result[i] = new ExceptionRegion(kind, tryOffset, tryLength, handlerOffset, handlerLength, classTokenOrFilterOffset);
            }
 
            return ImmutableArray.Create(result);
        }
 
        private static ImmutableArray<ExceptionRegion> ReadFatExceptionHandlers(ref BlobReader memReader, int count)
        {
            var result = new ExceptionRegion[count];
            for (int i = 0; i < result.Length; i++)
            {
                var sehFlags = (ExceptionRegionKind)memReader.ReadUInt32();
                int tryOffset = memReader.ReadInt32();
                int tryLength = memReader.ReadInt32();
                int handlerOffset = memReader.ReadInt32();
                int handlerLength = memReader.ReadInt32();
                int classTokenOrFilterOffset = memReader.ReadInt32();
                result[i] = new ExceptionRegion(sehFlags, tryOffset, tryLength, handlerOffset, handlerLength, classTokenOrFilterOffset);
            }
 
            return ImmutableArray.Create(result);
        }
    }
}