/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Test/Core/Metadata/DynamicAnalysisDataReader.cs
385 строк
13 KB
Giridhar Trivedi
Fix endianness issues to fix failures in Emit testsuite (#75450)
09 окт 2024, 19:59
Не верифицирован
09 окт 2024, 19:59
f2f4c87
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable // TODO (https://github.com/dotnet/testimpact/issues/84): delete this using System; using System.Collections.Immutable; using System.Diagnostics; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis { internal readonly struct DynamicAnalysisDocument { public readonly BlobHandle Name; public readonly GuidHandle HashAlgorithm; public readonly BlobHandle Hash; public DynamicAnalysisDocument(BlobHandle name, GuidHandle hashAlgorithm, BlobHandle hash) { Name = name; HashAlgorithm = hashAlgorithm; Hash = hash; } } internal readonly struct DynamicAnalysisMethod { public readonly BlobHandle Blob; public DynamicAnalysisMethod(BlobHandle blob) { Blob = blob; } } internal readonly struct DynamicAnalysisSpan { public readonly int DocumentRowId; public readonly int StartLine; public readonly int StartColumn; public readonly int EndLine; public readonly int EndColumn; public DynamicAnalysisSpan(int documentRowId, int startLine, int startColumn, int endLine, int endColumn) { DocumentRowId = documentRowId; StartLine = startLine; StartColumn = startColumn; EndLine = endLine; EndColumn = endColumn; } } internal sealed unsafe class DynamicAnalysisDataReader { public ImmutableArray<DynamicAnalysisDocument> Documents { get; } public ImmutableArray<DynamicAnalysisMethod> Methods { get; } private readonly Blob _guidHeapBlob; private readonly Blob _blobHeapBlob; private const int GuidSize = 16; public DynamicAnalysisDataReader(byte* buffer, int size) { var reader = new BlobReader(buffer, size); // header: if (reader.ReadByte() != 'D' || reader.ReadByte() != 'A' || reader.ReadByte() != 'M' || reader.ReadByte() != 'D') { throw new BadImageFormatException(); } // version byte major = reader.ReadByte(); byte minor = reader.ReadByte(); if (major != 0 || minor < 1 || minor > 2) { throw new NotSupportedException(); } // table sizes: int documentRowCount = reader.ReadInt32(); int methodSpanRowCount = reader.ReadInt32(); // blob heap sizes: int stringHeapSize = (minor == 1) ? reader.ReadInt32() : 0; int userStringHeapSize = (minor == 1) ? reader.ReadInt32() : 0; int guidHeapSize = reader.ReadInt32(); int blobHeapSize = reader.ReadInt32(); // TODO: check size ranges bool isBlobHeapSmall = blobHeapSize <= ushort.MaxValue; bool isGuidHeapSmall = guidHeapSize / GuidSize <= ushort.MaxValue; var documentsBuilder = ArrayBuilder<DynamicAnalysisDocument>.GetInstance(documentRowCount); for (int i = 0; i < documentRowCount; i++) { var name = MetadataTokens.BlobHandle(ReadReference(ref reader, isBlobHeapSmall)); var hashAlgorithm = MetadataTokens.GuidHandle(ReadReference(ref reader, isGuidHeapSmall)); var hash = MetadataTokens.BlobHandle(ReadReference(ref reader, isBlobHeapSmall)); documentsBuilder.Add(new DynamicAnalysisDocument(name, hashAlgorithm, hash)); } Documents = documentsBuilder.ToImmutableAndFree(); var methodsBuilder = ArrayBuilder<DynamicAnalysisMethod>.GetInstance(methodSpanRowCount); for (int i = 0; i < methodSpanRowCount; i++) { methodsBuilder.Add(new DynamicAnalysisMethod(MetadataTokens.BlobHandle(ReadReference(ref reader, isBlobHeapSmall)))); } Methods = methodsBuilder.ToImmutableAndFree(); int stringHeapOffset = reader.Offset; int userStringHeapOffset = stringHeapOffset + stringHeapSize; int guidHeapOffset = userStringHeapOffset + userStringHeapSize; int blobHeapOffset = guidHeapOffset + guidHeapSize; if (reader.Length != blobHeapOffset + blobHeapSize) { throw new BadImageFormatException(); } _guidHeapBlob = new Blob(buffer + guidHeapOffset, guidHeapSize); _blobHeapBlob = new Blob(buffer + blobHeapOffset, blobHeapSize); } public static DynamicAnalysisDataReader TryCreateFromPE(PEReader peReader, string resourceName) { // TODO: review all range checks, better error messages var mdReader = peReader.GetMetadataReader(); long offset = -1; foreach (var resourceHandle in mdReader.ManifestResources) { var resource = mdReader.GetManifestResource(resourceHandle); if (resource.Implementation.IsNil && resource.Attributes == ManifestResourceAttributes.Private && mdReader.StringComparer.Equals(resource.Name, resourceName)) { offset = resource.Offset; } } if (offset < 0) { return null; } var resourcesDir = peReader.PEHeaders.CorHeader.ResourcesDirectory; if (resourcesDir.Size < 0) { throw new BadImageFormatException(); } if (!peReader.PEHeaders.TryGetDirectoryOffset(resourcesDir, out var start)) { return null; } var peImage = peReader.GetEntireImage(); if (start >= peImage.Length - resourcesDir.Size) { throw new BadImageFormatException(); } byte* resourceStart = peImage.Pointer + start; BlobReader tmpresource = peImage.GetReader(start, peImage.Length - start); int resourceSize = tmpresource.ReadInt32(); if (resourceSize > resourcesDir.Size - sizeof(int)) { throw new BadImageFormatException(); } return new DynamicAnalysisDataReader(resourceStart + sizeof(int), resourceSize); } private static int ReadReference(ref BlobReader reader, bool smallRefSize) { return smallRefSize ? reader.ReadUInt16() : reader.ReadInt32(); } public ImmutableArray<DynamicAnalysisSpan> GetSpans(BlobHandle handle) { if (handle.IsNil) { return ImmutableArray<DynamicAnalysisSpan>.Empty; } var builder = ArrayBuilder<DynamicAnalysisSpan>.GetInstance(); var reader = GetBlobReader(handle); // header: int documentRowId = ReadDocumentRowId(ref reader); int previousStartLine = -1; ushort previousStartColumn = 0; // records: while (reader.RemainingBytes > 0) { ReadDeltaLinesAndColumns(ref reader, out var deltaLines, out var deltaColumns); // document: if (deltaLines == 0 && deltaColumns == 0) { documentRowId = ReadDocumentRowId(ref reader); continue; } int startLine; ushort startColumn; // delta Start Line & Column: if (previousStartLine < 0) { Debug.Assert(previousStartColumn == 0); startLine = ReadLine(ref reader); startColumn = ReadColumn(ref reader); } else { startLine = AddLines(previousStartLine, reader.ReadCompressedSignedInteger()); startColumn = AddColumns(previousStartColumn, reader.ReadCompressedSignedInteger()); } previousStartLine = startLine; previousStartColumn = startColumn; int endLine = AddLines(startLine, deltaLines); int endColumn = AddColumns(startColumn, deltaColumns); var linePositionSpan = new DynamicAnalysisSpan(documentRowId, startLine, startColumn, endLine, endColumn); builder.Add(linePositionSpan); } return builder.ToImmutableAndFree(); } //TODO: some of the helpers below should be provided by System.Reflection.Metadata private readonly unsafe struct Blob { public readonly byte* Pointer; public readonly int Length; public Blob(byte* pointer, int length) { Pointer = pointer; Length = length; } } public Guid GetGuid(GuidHandle handle) { if (handle.IsNil) { return default(Guid); } int offset = (MetadataTokens.GetHeapOffset(handle) - 1) * GuidSize; if (offset + GuidSize > _guidHeapBlob.Length) { throw new BadImageFormatException(); } return *(Guid*)(_guidHeapBlob.Pointer + offset); } internal byte[] GetBytes(BlobHandle handle) { var reader = GetBlobReader(handle); return reader.ReadBytes(reader.Length); } private BlobReader GetBlobReader(BlobHandle handle) { int offset = MetadataTokens.GetHeapOffset(handle); byte* start = _blobHeapBlob.Pointer + offset; var reader = new BlobReader(start, _blobHeapBlob.Length - offset); int size = reader.ReadCompressedInteger(); return new BlobReader(start + reader.Offset, size); } public string GetDocumentName(BlobHandle handle) { var blobReader = GetBlobReader(handle); // Spec: separator is an ASCII encoded character in range [0x01, 0x7F], or byte 0 to represent an empty separator. int separator = blobReader.ReadByte(); if (separator > 0x7f) { throw new BadImageFormatException(string.Format("Invalid document name", separator)); } var pooledBuilder = PooledStringBuilder.GetInstance(); var builder = pooledBuilder.Builder; bool isFirstPart = true; while (blobReader.RemainingBytes > 0) { if (separator != 0 && !isFirstPart) { builder.Append((char)separator); } var partReader = GetBlobReader(blobReader.ReadBlobHandle()); // TODO: avoid allocating temp string (https://github.com/dotnet/corefx/issues/2102) builder.Append(partReader.ReadUTF8(partReader.Length)); isFirstPart = false; } return pooledBuilder.ToStringAndFree(); } private void ReadDeltaLinesAndColumns(ref BlobReader reader, out int deltaLines, out int deltaColumns) { deltaLines = reader.ReadCompressedInteger(); deltaColumns = (deltaLines == 0) ? reader.ReadCompressedInteger() : reader.ReadCompressedSignedInteger(); } private int ReadLine(ref BlobReader reader) { return reader.ReadCompressedInteger(); } private ushort ReadColumn(ref BlobReader reader) { int column = reader.ReadCompressedInteger(); if (column > ushort.MaxValue) { throw new BadImageFormatException("SequencePointValueOutOfRange"); } return (ushort)column; } private int AddLines(int value, int delta) { int result = unchecked(value + delta); if (result < 0) { throw new BadImageFormatException("SequencePointValueOutOfRange"); } return result; } private ushort AddColumns(ushort value, int delta) { int result = unchecked(value + delta); if (result < 0 || result >= ushort.MaxValue) { throw new BadImageFormatException("SequencePointValueOutOfRange"); } return (ushort)result; } private int ReadDocumentRowId(ref BlobReader reader) { int rowId = reader.ReadCompressedInteger(); if (rowId == 0) { throw new BadImageFormatException("Invalid handle"); } return rowId; } } }