Esempio n. 1
0
        internal Byte[] PerformEmitting(UInt32 currentOffset, out Boolean isTiny)
        {
            if (this._methodIL._labelOffsets.Any(offset => offset == MethodILImpl.NO_OFFSET))
            {
                throw new InvalidOperationException("Not all labels have been marked.");
            }
            if (this._methodIL._currentExceptionBlocks.Any())
            {
                throw new InvalidOperationException("Not all exception blocks have been completed.");
            }

            // Remember that inner exception blocks must precede outer ones
            var allExceptionBlocksCorrectlyOrdered = this._methodIL._allExceptionBlocks.ToArray();

            Array.Sort(
                allExceptionBlocksCorrectlyOrdered,
                (item1, item2) =>
            {
                // Return -1 if item1 is inner block of item2, 0 if they are same, 1 if item1 is not inner block of item2
                return(Object.ReferenceEquals(item1, item2) ? 0 :
                       (item1._tryOffset >= item2._handlerOffset + item2._handlerLength || (item1._tryOffset <= item2._tryOffset && item1._handlerOffset + item1._handlerLength > item2._handlerOffset + item2._handlerLength) ? 1 : -1));
            });

            // Setup stack sizes based on exception blocks
            foreach (var block in allExceptionBlocksCorrectlyOrdered)
            {
                switch (block._blockType)
                {
                case ExceptionBlockType.Exception:
                    this._stackSizes[block._handlerOffset] = 1;
                    break;

                case ExceptionBlockType.Filter:
                    this._stackSizes[block._handlerOffset] = 1;
                    this._stackSizes[block._filterOffset]  = 1;
                    break;
                }
            }

            // Emit opcodes and arguments
            foreach (var info in this._methodIL._opCodes)
            {
                info.EmitOpCode(this);
                ++this._methodILOffset;
            }

            // Mark label targets
            for (var i = 0; i < this._labelInfoIndex; ++i)
            {
                var thisOffset       = this._labelInfos[i].byteOffset;
                var startCountOffset = this._labelInfos[i].startCountOffset;
                var amountToJump     = this._opCodeInfoOffsets[this._methodIL._labelOffsets[this._labelInfos[i].labelIdx]] - (thisOffset + startCountOffset);
                if (startCountOffset == 1)
                {
                    if (amountToJump >= SByte.MinValue && amountToJump <= SByte.MaxValue)
                    {
                        this._ilCode.WriteSByteToBytes(ref thisOffset, amountToJump);
                    }
                    else
                    {
                        throw new InvalidOperationException("Tried to use one-byte branch instruction for offset of amount " + amountToJump);
                    }
                }
                else
                {
                    this._ilCode.WriteInt32LEToBytes(ref thisOffset, amountToJump);
                }
            }

            // Create exception blocks with byte offsets
            byte[][]  exceptionBlocks  = new byte[allExceptionBlocksCorrectlyOrdered.Length][];
            Boolean[] exceptionFormats = new Boolean[exceptionBlocks.Length];

            // TODO PEVerify doesn't like mixed small and fat blocks at all (however, at least Cecil understands that kind of situation)
            // TODO Apparently, PEVerify doesn't like multiple small blocks either (Cecil still loads code fine)
            // Also, because of exception block ordering, it is easier to do this way.
            var allAreSmall = allExceptionBlocksCorrectlyOrdered.Length <= MAX_SMALL_EXC_HANDLERS_IN_ONE_SECTION &&
                              allExceptionBlocksCorrectlyOrdered.All(excBlock =>
            {
                var tryOffset     = this._opCodeInfoOffsets[excBlock._tryOffset];
                var tryLength     = this._opCodeInfoOffsets[excBlock._tryOffset + excBlock._tryLength] - tryOffset;
                var handlerOffset = this._opCodeInfoOffsets[excBlock._handlerOffset];
                var handlerLength = this._opCodeInfoOffsets[excBlock._handlerOffset + excBlock._handlerLength] - handlerOffset;
                return(tryLength <= Byte.MaxValue && handlerLength <= Byte.MaxValue && tryOffset <= UInt16.MaxValue && handlerOffset <= UInt16.MaxValue);
            });

            for (var i = 0; i < exceptionBlocks.Length; ++i)
            {
                // ECMA-335, pp. 286-287
                var    block = allExceptionBlocksCorrectlyOrdered[i];
                Int32  idx   = 0;
                Byte[] array;
                var    tryOffset      = this._opCodeInfoOffsets[block._tryOffset];
                var    tryLength      = this._opCodeInfoOffsets[block._tryOffset + block._tryLength] - tryOffset;
                var    handlerOffset  = this._opCodeInfoOffsets[block._handlerOffset];
                var    handlerLength  = this._opCodeInfoOffsets[block._handlerOffset + block._handlerLength] - handlerOffset;
                var    useSmallFormat = allAreSmall &&
                                        tryLength <= Byte.MaxValue && handlerLength <= Byte.MaxValue && tryOffset <= UInt16.MaxValue && handlerOffset <= UInt16.MaxValue;
                exceptionFormats[i] = useSmallFormat;
                if (useSmallFormat)
                {
                    array = new Byte[12];
                    array.WriteInt16LEToBytes(ref idx, (Int16)block._blockType)
                    .WriteUInt16LEToBytes(ref idx, (UInt16)tryOffset)
                    .WriteByteToBytes(ref idx, (Byte)tryLength)
                    .WriteUInt16LEToBytes(ref idx, (UInt16)handlerOffset)
                    .WriteByteToBytes(ref idx, (Byte)handlerLength);
                }
                else
                {
                    array = new Byte[24];
                    array.WriteInt32LEToBytes(ref idx, (Int32)block._blockType)
                    .WriteInt32LEToBytes(ref idx, tryOffset)
                    .WriteInt32LEToBytes(ref idx, tryLength)
                    .WriteInt32LEToBytes(ref idx, handlerOffset)
                    .WriteInt32LEToBytes(ref idx, handlerLength);
                }

                if (ExceptionBlockType.Exception == block._blockType)
                {
                    array.WriteInt32LEToBytes(ref idx, this._metaData.GetTokenFor(this._assemblyMapper == null ? block._exceptionType : this._assemblyMapper.MapTypeBase(block._exceptionType), false));
                }
                else if (ExceptionBlockType.Filter == block._blockType)
                {
                    array.WriteInt32LEToBytes(ref idx, block._filterOffset);
                }
                exceptionBlocks[i] = array;
            }

            // Write method header, extra data sections, and IL
            Byte[] result;
            isTiny = this._ilCodeCount < 64 &&
                     exceptionBlocks.Length == 0 &&
                     this._maxStack <= 8 &&
                     this._methodIL._locals.Count == 0;
            var resultIndex               = 0;
            var hasAnyExc                 = false;
            var hasSmallExc               = false;
            var hasLargExc                = false;
            var smallExcCount             = 0;
            var largeExcCount             = 0;
            var amountToNext4ByteBoundary = 0;

            if (isTiny)
            {
                // Can use tiny header
                result = new Byte[this._ilCodeCount + 1];
                result[resultIndex++] = (Byte)((Int32)MethodHeaderFlags.TinyFormat | (this._ilCodeCount << 2));
            }
            else
            {
                // Use fat header
                hasAnyExc     = exceptionBlocks.Length > 0;
                hasSmallExc   = hasAnyExc && exceptionFormats.Any(excFormat => excFormat);
                hasLargExc    = hasAnyExc && exceptionFormats.Any(excFormat => !excFormat);
                smallExcCount = hasSmallExc ? exceptionFormats.Count(excFormat => excFormat) : 0;
                largeExcCount = hasLargExc ? exceptionFormats.Count(excFormat => !excFormat) : 0;
                var offsetAfterIL = (Int32)(BitUtils.MultipleOf4(currentOffset) + 12 + (UInt32)this._ilCodeCount);
                amountToNext4ByteBoundary = BitUtils.MultipleOf4(offsetAfterIL) - offsetAfterIL;

                result = new Byte[12
                                  + this._ilCodeCount +
                                  (hasAnyExc ? amountToNext4ByteBoundary : 0) +
                                  (hasSmallExc ? METHOD_DATA_SECTION_SIZE : 0) +
                                  (hasLargExc ? METHOD_DATA_SECTION_SIZE : 0) +
                                  smallExcCount * 12 +
                                  (smallExcCount / MAX_SMALL_EXC_HANDLERS_IN_ONE_SECTION) * METHOD_DATA_SECTION_SIZE + // (Amount of extra section headers ) * section size
                                  largeExcCount * 24
                         ];
                var flags = MethodHeaderFlags.FatFormat;
                if (hasAnyExc)
                {
                    flags |= MethodHeaderFlags.MoreSections;
                }
                if (this._methodIL.InitLocals)
                {
                    flags |= MethodHeaderFlags.InitLocals;
                }

                result.WriteInt16LEToBytes(ref resultIndex, (Int16)(((Int32)flags) | (3 << 12)))
                .WriteInt16LEToBytes(ref resultIndex, (Int16)this._maxStack)
                .WriteInt32LEToBytes(ref resultIndex, this._ilCodeCount)
                .WriteInt32LEToBytes(ref resultIndex, this._metaData.GetSignatureTokenFor(this._method, this._methodIL._locals.ToArray()));
            }

            Array.Copy(this._ilCode, 0, result, resultIndex, this._ilCodeCount);
            resultIndex += this._ilCodeCount;

            if (hasAnyExc)
            {
                var processedIndices = new HashSet <Int32>();
                resultIndex += amountToNext4ByteBoundary;
                var flags = MethodDataFlags.ExceptionHandling;
                // First, write fat sections
                if (hasLargExc)
                {
                    // TODO like with small sections, what if too many exception clauses to be fit into DataSize?
                    flags |= MethodDataFlags.FatFormat;
                    if (hasSmallExc)
                    {
                        flags |= MethodDataFlags.MoreSections;
                    }
                    result.WriteByteToBytes(ref resultIndex, (Byte)flags)
                    .WriteInt32LEToBytes(ref resultIndex, largeExcCount * 24 + METHOD_DATA_SECTION_SIZE);
                    --resultIndex;
                    for (var i = 0; i < exceptionBlocks.Length; ++i)
                    {
                        if (!exceptionFormats[i] && processedIndices.Add(i))
                        {
                            var length = exceptionBlocks[i].Length;
                            Array.Copy(exceptionBlocks[i], 0, result, resultIndex, length);
                            resultIndex += length;
                        }
                    }
                }
                // Then, write small sections
                // If exception counts * 12 + 4 are > Byte.MaxValue, have to write several sections
                // (Max 20 handlers per section)
                flags = MethodDataFlags.ExceptionHandling;
                if (hasSmallExc)
                {
                    var curSmallIdx = 0;
                    while (smallExcCount > 0)
                    {
                        var amountToBeWritten = Math.Min(smallExcCount, MAX_SMALL_EXC_HANDLERS_IN_ONE_SECTION);
                        if (amountToBeWritten < smallExcCount)
                        {
                            flags |= MethodDataFlags.MoreSections;
                        }
                        else
                        {
                            flags = flags & ~(MethodDataFlags.MoreSections);
                        }

                        result.WriteByteToBytes(ref resultIndex, (Byte)flags)
                        .WriteByteToBytes(ref resultIndex, (Byte)(amountToBeWritten * 12 + METHOD_DATA_SECTION_SIZE))
                        .WriteInt16LEToBytes(ref resultIndex, 0);
                        var amountActuallyWritten = 0;
                        while (curSmallIdx < exceptionBlocks.Length && amountActuallyWritten < amountToBeWritten)
                        {
                            if (exceptionFormats[curSmallIdx])
                            {
                                var length = exceptionBlocks[curSmallIdx].Length;
                                Array.Copy(exceptionBlocks[curSmallIdx], 0, result, resultIndex, length);
                                resultIndex += length;
                                ++amountActuallyWritten;
                            }
                            ++curSmallIdx;
                        }
                        smallExcCount -= amountToBeWritten;
                    }
                }
            }
#if DEBUG
            if (resultIndex != result.Length)
            {
                throw new Exception("Something went wrong when emitting method headers and body. Emitted " + resultIndex + " bytes, but was supposed to emit " + result.Length + " bytes.");
            }
#endif
            return(result);
        }