From b9172d18b7eef4dfbd87fe091209a4f2ff27c1ba Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 12 Mar 2024 19:35:06 -0700 Subject: [PATCH 1/9] Initial PDB API shape and sequence point implementation --- .../src/System/Reflection/Emit/ILGenerator.cs | 4 + .../System/Reflection/Emit/LocalBuilder.cs | 2 + .../System/Reflection/Emit/ModuleBuilder.cs | 3 + .../System.Reflection.Emit.ILGeneration.cs | 2 + ...System.Reflection.Emit.ILGeneration.csproj | 1 + .../ref/System.Reflection.Emit.cs | 5 +- .../ref/System.Reflection.Emit.csproj | 1 + .../src/System.Reflection.Emit.csproj | 2 + .../System/Reflection/Emit/ILGeneratorImpl.cs | 23 +++ .../Reflection/Emit/LocalBuilderImpl.cs | 6 + .../Reflection/Emit/ModuleBuilderImpl.cs | 150 +++++++++++++++++- .../Reflection/Emit/Pdb/SequencePoint.cs | 27 ++++ .../Emit/Pdb/SymbolDocumentWriter.cs | 34 ++++ .../Emit/PersistedAssemblyBuilder.cs | 4 +- 14 files changed, 256 insertions(+), 8 deletions(-) create mode 100644 src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SequencePoint.cs create mode 100644 src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs index b56f69381450fc..7a80016e38ab04 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using System.Diagnostics.SymbolStore; namespace System.Reflection.Emit { @@ -200,6 +201,9 @@ public virtual LocalBuilder DeclareLocal(Type localType) [CLSCompliant(false)] public void Emit(OpCode opcode, sbyte arg) => Emit(opcode, (byte)arg); + public virtual void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) + => throw new NotSupportedException(); + #endregion #endregion diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs index 7c38c287bf22b5..c26273ebeabedd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs @@ -12,5 +12,7 @@ public abstract class LocalBuilder : LocalVariableInfo /// This constructor is invoked by derived classes. /// protected LocalBuilder() { } + + public virtual void SetLocalSymInfo(string name) => throw new NotSupportedException(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs index 21f68439ee3927..0de0c3a5ddab71 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.SymbolStore; using System.Runtime.InteropServices; namespace System.Reflection.Emit @@ -24,6 +25,8 @@ public EnumBuilder DefineEnum(string name, TypeAttributes visibility, Type under return DefineEnumCore(name, visibility, underlyingType); } + public virtual ISymbolDocumentWriter DefineDocument(string url, Guid language = default) => throw new NotSupportedException(); + protected abstract EnumBuilder DefineEnumCore(string name, TypeAttributes visibility, Type underlyingType); public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, Type? returnType, Type[]? parameterTypes) diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs index f79ee4288df6ce..b40ba4c28ce8e9 100644 --- a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs +++ b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs @@ -54,6 +54,7 @@ public virtual void EmitWriteLine(string value) { } public abstract void EndExceptionBlock(); public abstract void EndScope(); public abstract void MarkLabel(System.Reflection.Emit.Label loc); + public virtual void MarkSequencePoint(System.Diagnostics.SymbolStore.ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { } public virtual void ThrowException([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type excType) { } public abstract void UsingNamespace(string usingNamespace); } @@ -73,6 +74,7 @@ protected LocalBuilder() { } public override bool IsPinned { get { throw null; } } public override int LocalIndex { get { throw null; } } public override System.Type LocalType { get { throw null; } } + public virtual void SetLocalSymInfo(string name) { } } public abstract partial class ParameterBuilder { diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.csproj b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.csproj index 6894be6dab8f9f..4364c5c0b56ddf 100644 --- a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.csproj +++ b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.csproj @@ -9,5 +9,6 @@ + diff --git a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs index 3690bf72a4c876..88f315f2a9b46f 100644 --- a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs +++ b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs @@ -398,6 +398,7 @@ protected ModuleBuilder() { } public override string ScopeName { get { throw null; } } public void CreateGlobalFunctions() { } protected abstract void CreateGlobalFunctionsCore(); + public virtual System.Diagnostics.SymbolStore.ISymbolDocumentWriter DefineDocument(string url, System.Guid language = default) { throw null; } public System.Reflection.Emit.EnumBuilder DefineEnum(string name, System.Reflection.TypeAttributes visibility, System.Type underlyingType) { throw null; } protected abstract System.Reflection.Emit.EnumBuilder DefineEnumCore(string name, System.Reflection.TypeAttributes visibility, System.Type underlyingType); public System.Reflection.Emit.MethodBuilder DefineGlobalMethod(string name, System.Reflection.MethodAttributes attributes, System.Reflection.CallingConventions callingConvention, System.Type? returnType, System.Type[]? parameterTypes) { throw null; } @@ -479,9 +480,9 @@ public PersistedAssemblyBuilder(System.Reflection.AssemblyName name, System.Refl public override System.Reflection.Module ManifestModule { get { throw null; } } [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Defining a dynamic assembly requires dynamic code.")] protected override System.Reflection.Emit.ModuleBuilder DefineDynamicModuleCore(string name) { throw null; } - protected override System.Reflection.Emit.ModuleBuilder? GetDynamicModuleCore(string name) { throw null; } [System.CLSCompliantAttribute(false)] - public System.Reflection.Metadata.Ecma335.MetadataBuilder GenerateMetadata(out System.Reflection.Metadata.BlobBuilder ilStream, out System.Reflection.Metadata.BlobBuilder mappedFieldData) { throw null; } + public System.Reflection.Metadata.Ecma335.MetadataBuilder GenerateMetadata(out System.Reflection.Metadata.BlobBuilder ilStream, out System.Reflection.Metadata.BlobBuilder mappedFieldData, bool addPdb = false) { throw null; } + protected override System.Reflection.Emit.ModuleBuilder? GetDynamicModuleCore(string name) { throw null; } public override System.Reflection.AssemblyName GetName(bool copiedName) { throw null; } public void Save(string assemblyFileName) { throw null; } public void Save(System.IO.Stream stream) { throw null; } diff --git a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.csproj b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.csproj index 44f0feefe67db2..89a1505b49ceab 100644 --- a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.csproj +++ b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.csproj @@ -11,5 +11,6 @@ + \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj index 0c34ef45f44862..57b328a0bbde26 100644 --- a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj +++ b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj @@ -23,6 +23,8 @@ + + diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index dcb92654e124b7..083e81e80a0d99 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.SymbolStore; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Runtime.InteropServices; @@ -28,6 +29,7 @@ internal sealed class ILGeneratorImpl : ILGenerator private Dictionary _labelTable = new(2); private List> _memberReferences = new(); private List _exceptionStack = new(); + private Dictionary> _sequencePoints = new(); internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size) { @@ -44,6 +46,7 @@ internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size) internal InstructionEncoder Instructions => _il; internal bool HasDynamicStackAllocation => _hasDynamicStackAllocation; internal List Locals => _locals; + internal Dictionary> SequencePoints => _sequencePoints; public override int ILOffset => _il.Offset; @@ -777,6 +780,26 @@ public override void MarkLabel(Label loc) } } + public override void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) + { + if (document is SymbolDocumentWriter symbolDoc) + { + if (_sequencePoints.TryGetValue(symbolDoc, out List? sequencePoints)) + { + sequencePoints.Add(new SequencePoint(_il.Offset, startLine, startColumn, endLine, endColumn)); + } + else + { + sequencePoints = new List { new SequencePoint(_il.Offset, startLine, startColumn, endLine, endColumn) }; + _sequencePoints.Add(symbolDoc, sequencePoints); + } + } + else + { + throw new ArgumentException(nameof(document)); // TODO: SR + } + } + public override void UsingNamespace(string usingNamespace) { // TODO: No-op, will be implemented wit PDB support diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs index fa9bffbec4345c..e598e2706750cb 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs @@ -10,6 +10,7 @@ internal sealed class LocalBuilderImpl : LocalBuilder private readonly Type _localType; private readonly MethodInfo _method; private readonly bool _isPinned; + private string? _name; #endregion #region Constructor @@ -24,6 +25,11 @@ internal LocalBuilderImpl(int index, Type type, MethodInfo method, bool isPinned #region Internal Members internal MethodInfo GetMethodBuilder() => _method; + internal string? Name => _name; + #endregion + + #region LocalVariableInfo Override + public override void SetLocalSymInfo(string name) => _name = name; #endregion #region LocalVariableInfo Override diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 0349186b717fef..33b110b4534a54 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.SymbolStore; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; @@ -25,6 +26,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly Guid _moduleVersionId; private Dictionary? _moduleReferences; private List? _customAttributes; + private Dictionary _docHandles = new(); private int _nextTypeDefRowId = 1; private int _nextMethodDefRowId = 1; private int _nextFieldDefRowId = 1; @@ -108,7 +110,7 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) return null; } - internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder) + internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder, bool addPdb) { // Add module metadata ModuleDefinitionHandle moduleHandle = _metadataBuilder.AddModule( @@ -134,7 +136,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuil // Add global members WriteFields(_globalTypeBuilder, fieldDataBuilder); - WriteMethods(_globalTypeBuilder._methodDefinitions, genericParams, methodBodyEncoder); + WriteMethods(_globalTypeBuilder._methodDefinitions, genericParams, methodBodyEncoder, addPdb); // Add each type definition to metadata table. foreach (TypeBuilderImpl typeBuilder in _typeDefinitions) @@ -173,7 +175,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuil WriteCustomAttributes(typeBuilder._customAttributes, typeHandle); WriteProperties(typeBuilder); WriteFields(typeBuilder, fieldDataBuilder); - WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder); + WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder, addPdb); WriteEvents(typeBuilder); } @@ -339,7 +341,8 @@ private void PopulateTokensForTypesAndItsMembers() } } - private void WriteMethods(List methods, List genericParams, MethodBodyStreamEncoder methodBodyEncoder) + private void WriteMethods(List methods, List genericParams, + MethodBodyStreamEncoder methodBodyEncoder, bool addPdb) { foreach (MethodBuilderImpl method in methods) { @@ -351,6 +354,10 @@ private void WriteMethods(List methods, List methods, List 0) + { + Dictionary>.Enumerator enumerator = il.SequencePoints.GetEnumerator(); + if (il.SequencePoints.Count == 1) + { + enumerator.MoveNext(); + DocumentHandle docHandle = GetDocument(enumerator.Current.Key); + BlobBuilder spBlobBuilder = new BlobBuilder(); + spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignatureHandle)); + PopulateSequencePointsBlob(spBlobBuilder, enumerator.Current.Value); + _metadataBuilder.AddMethodDebugInformation(docHandle, _metadataBuilder.GetOrAddBlob(spBlobBuilder)); + } + else + { + // sequence points spans multiple docs + _metadataBuilder.AddMethodDebugInformation(default, PopulateMultiDocSequencePointsBlob(enumerator, localSignatureHandle)); + } + } + else + { + // add empty sequence point + _metadataBuilder.AddMethodDebugInformation(default, default); + } + } + + private BlobHandle PopulateMultiDocSequencePointsBlob(Dictionary>.Enumerator enumerator, StandaloneSignatureHandle localSignatureHandle) + { + BlobBuilder spBlobBuilder = new BlobBuilder(); + // header: + spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignatureHandle)); + + while (enumerator.MoveNext()) + { + KeyValuePair> pair = enumerator.Current; + DocumentHandle documentHandle = GetDocument(pair.Key); + spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(documentHandle)); + PopulateSequencePointsBlob(spBlobBuilder, pair.Value); + } + + return _metadataBuilder.GetOrAddBlob(spBlobBuilder); + } + + private static void PopulateSequencePointsBlob(BlobBuilder spBlobBuilder, List sequencePoints) + { + int previousNonHiddenStartLine = -1; + int previousNonHiddenStartColumn = -1; + + for (int i = 0; i < sequencePoints.Count; i++) + { + // IL offset delta: + if (i > 0) + { + spBlobBuilder.WriteCompressedInteger(sequencePoints[i].Offset - sequencePoints[i - 1].Offset); + } + else + { + spBlobBuilder.WriteCompressedInteger(sequencePoints[i].Offset); + } + + if (sequencePoints[i].IsHidden) + { + spBlobBuilder.WriteUInt16(0); + // spBuilder.WriteUInt16(0); 1 for column? + continue; + } + + // Delta Lines & Columns: + SerializeDeltaLinesAndColumns(spBlobBuilder, sequencePoints[i]); + + // delta Start Lines & Columns: + if (previousNonHiddenStartLine < 0) + { + Debug.Assert(previousNonHiddenStartColumn < 0); + spBlobBuilder.WriteCompressedInteger(sequencePoints[i].StartLine); + spBlobBuilder.WriteCompressedInteger(sequencePoints[i].StartColumn); + } + else + { + spBlobBuilder.WriteCompressedSignedInteger(sequencePoints[i].StartLine - previousNonHiddenStartLine); + spBlobBuilder.WriteCompressedSignedInteger(sequencePoints[i].StartColumn - previousNonHiddenStartColumn); + } + + previousNonHiddenStartLine = sequencePoints[i].StartLine; + previousNonHiddenStartColumn = sequencePoints[i].StartColumn; + } + } + + private static void SerializeDeltaLinesAndColumns(BlobBuilder spBuilder, SequencePoint sequencePoint) + { + int deltaLines = sequencePoint.EndLine - sequencePoint.StartLine; + int deltaColumns = sequencePoint.EndColumn - sequencePoint.StartColumn; + + // only hidden sequence points have zero width + Debug.Assert(deltaLines != 0 || deltaColumns != 0 || sequencePoint.IsHidden); + + spBuilder.WriteCompressedInteger(deltaLines); + + if (deltaLines == 0) + { + spBuilder.WriteCompressedInteger(deltaColumns); + } + else + { + spBuilder.WriteCompressedSignedInteger(deltaColumns); + } + } + + private DocumentHandle GetDocument(SymbolDocumentWriter docWriter) + { + if (!_docHandles.TryGetValue(docWriter, out DocumentHandle handle)) + { + handle = AddDocument(docWriter.URL, docWriter.Language, docWriter.HashAlgorithm, docWriter.Hash); + _docHandles.Add(docWriter, handle); + } + + return handle; + } + + private DocumentHandle AddDocument(string url, Guid language, Guid hashAlgorithm, byte[]? hash) => + _metadataBuilder.AddDocument( + name: _metadataBuilder.GetOrAddDocumentName(url), + hashAlgorithm: hashAlgorithm == default ? default : _metadataBuilder.GetOrAddGuid(hashAlgorithm), + hash: hash == null ? default : _metadataBuilder.GetOrAddBlob(hash), + language: language == default ? default : _metadataBuilder.GetOrAddGuid(language)); + private void FillMemberReferences(ILGeneratorImpl il) { foreach (KeyValuePair pair in il.GetMemberReferences()) @@ -1158,5 +1293,12 @@ private static SignatureCallingConvention GetSignatureConvention(CallingConventi CallingConvention.FastCall => SignatureCallingConvention.FastCall, _ => SignatureCallingConvention.Default, }; + + public override ISymbolDocumentWriter DefineDocument(string url, Guid language = default) + { + ArgumentNullException.ThrowIfNull(url); + + return new SymbolDocumentWriter(url, language); + } } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SequencePoint.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SequencePoint.cs new file mode 100644 index 00000000000000..f82fea8986ff2f --- /dev/null +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SequencePoint.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + internal sealed class SequencePoint + { + public const int HiddenLine = 0xfeefee; + + public int Offset { get; } + public int StartLine { get; } + public int EndLine { get; } + public int StartColumn { get; } + public int EndColumn { get; } + + public SequencePoint(int offset, int startLine, int startColumn, int endLine, int endColumn) + { + Offset = offset; + StartLine = startLine; + EndLine = endLine; + StartColumn = startColumn; + EndColumn = endColumn; + } + + public bool IsHidden => StartLine == HiddenLine; + } +} diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs new file mode 100644 index 00000000000000..19ce1872adb275 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.SymbolStore; + +namespace System.Reflection.Emit +{ + internal sealed class SymbolDocumentWriter : ISymbolDocumentWriter + { + internal readonly Guid _language; + internal readonly string _url; + private Guid _hashAlgorithm; + private byte[]? _hash; + + internal string URL => _url; + internal Guid Language => _language; + internal Guid HashAlgorithm => _hashAlgorithm; + internal byte[]? Hash => _hash; + + public SymbolDocumentWriter(string url, Guid language) + { + _language = language; + _url = url; + } + + public void SetCheckSum(Guid algorithmId, byte[] checkSum) + { + _hashAlgorithm = algorithmId; + _hash = checkSum; + } + + public void SetSource(byte[] source) => throw new NotImplementedException(); + } +} diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs index 12fbf4704b7bac..a4c4a1899173f7 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs @@ -102,17 +102,17 @@ private void SaveInternal(Stream stream) WritePEImage(stream, ilStream, fieldData); } - /// /// Generates the metadata for the . /// /// Outputs bytes that includes all method's IL (body) emitted. /// Outputs bytes that includes all field RVA data defined in the assembly. + /// A boolean value that indicates whether to include PDB information. /// A that includes all members defined in the Assembly. /// A module not defined for the assembly. /// The metadata already populated for the assembly before. [CLSCompliant(false)] - public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData) + public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData, bool addPdb = false) { PopulateAssemblyMetadata(out ilStream, out mappedFieldData); From a7396870459297194b123acfc962e199f0cac181 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 5 Apr 2024 13:20:11 -0700 Subject: [PATCH 2/9] Finish PDB implementation and testing --- .../src/System/Reflection/Emit/ILGenerator.cs | 56 +++- .../System/Reflection/Emit/LocalBuilder.cs | 16 +- .../System/Reflection/Emit/ModuleBuilder.cs | 40 ++- .../System.Reflection.Emit.ILGeneration.cs | 6 +- .../ref/System.Reflection.Emit.cs | 9 +- .../src/Resources/Strings.resx | 6 + .../System/Reflection/Emit/ILGeneratorImpl.cs | 79 ++++- .../Reflection/Emit/LocalBuilderImpl.cs | 17 +- .../Reflection/Emit/ModuleBuilderImpl.cs | 127 +++++--- .../Emit/Pdb/SymbolDocumentWriter.cs | 7 +- .../Emit/PersistedAssemblyBuilder.cs | 28 +- ...ILGeneratorScopesAndSequencePointsTests.cs | 286 ++++++++++++++++++ .../PortablePdbStandalonePdbTest.cs | 234 ++++++++++++++ .../tests/System.Reflection.Emit.Tests.csproj | 2 + 14 files changed, 848 insertions(+), 65 deletions(-) create mode 100644 src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs create mode 100644 src/libraries/System.Reflection.Emit/tests/PortablePdb/PortablePdbStandalonePdbTest.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs index 7a80016e38ab04..8d32862f447ff1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -201,8 +201,60 @@ public virtual LocalBuilder DeclareLocal(Type localType) [CLSCompliant(false)] public void Emit(OpCode opcode, sbyte arg) => Emit(opcode, (byte)arg); - public virtual void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) - => throw new NotSupportedException(); + /// + /// Marks a sequence point in the Microsoft intermediate language (MSIL) stream. + /// + /// The document for which the sequence point is being defined. + /// The line where the sequence point begins. + /// The column in the line where the sequence point begins. + /// The line where the sequence point ends. + /// The column in the line where the sequence point ends. + /// is . + /// is not valid. + /// + /// is not within range [0, 0x20000000). + /// is not within range [0, 0x20000000) or lower than . + /// is not within range [0, 0x10000). + /// is not within range [0, 0x10000) or + /// equal to and lower than or equal to . + /// + public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) + { + ArgumentNullException.ThrowIfNull(document); + + if (startLine < 0 || startLine >= 0x20000000) + { + throw new ArgumentOutOfRangeException(nameof(startLine)); + } + + if (endLine < 0 || endLine > 0x20000000 || startLine > endLine) + { + throw new ArgumentOutOfRangeException(nameof(endLine)); + } + + if (startColumn < 0 || startColumn > 0x10000) + { + throw new ArgumentOutOfRangeException(nameof(startColumn)); + } + + if (endColumn < 0 || endColumn > 0x10000 || + (startLine == endLine && startLine != 0xfeefee && startColumn >= endColumn)) + { + throw new ArgumentOutOfRangeException(nameof(endColumn)); + } + + MarkSequencePointCore(document, startLine, startColumn, endLine, endColumn); + } + + /// + /// When overridden in a derived class, marks a sequence point in the Microsoft intermediate language (MSIL) stream. + /// + /// The document for which the sequence point is being defined. + /// The line where the sequence point begins. + /// The column in the line where the sequence point begins. + /// The line where the sequence point ends. + /// The column in the line where the sequence point ends. + protected virtual void MarkSequencePointCore(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { } #endregion diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs index c26273ebeabedd..efb4366a27b002 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs @@ -13,6 +13,20 @@ public abstract class LocalBuilder : LocalVariableInfo /// protected LocalBuilder() { } - public virtual void SetLocalSymInfo(string name) => throw new NotSupportedException(); + /// + /// Sets the name of this local variable. + /// + /// The name of the local variable + /// The containing type has been created with CreateType() or + /// containing type doesn't support symbol writing." + public void SetLocalSymInfo(string name) => SetLocalSymInfoCore(name); + + /// + /// Sets the name of this local variable. + /// + /// The name of the local variable. + /// The containing type has been created with CreateType() or + /// containing type doesn't support symbol writing." + protected virtual void SetLocalSymInfoCore(string name) { } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs index 0de0c3a5ddab71..f97935b24d7bd9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.SymbolStore; using System.Runtime.InteropServices; @@ -25,7 +26,44 @@ public EnumBuilder DefineEnum(string name, TypeAttributes visibility, Type under return DefineEnumCore(name, visibility, underlyingType); } - public virtual ISymbolDocumentWriter DefineDocument(string url, Guid language = default) => throw new NotSupportedException(); + /// + /// Defines a document for source. + /// + /// The URL for the document. + /// The GUID that identifies the document language. This can be Empty + /// The GUID that identifies the document language vendor. This is not used. + /// The GUID that identifies the document language vendor. This is not used. + /// The defined document. + /// is . + /// This method is called on a dynamic module that is not a persisted module. + [EditorBrowsable(EditorBrowsableState.Never)] + public ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType) => + DefineDocument(url, language); + + /// + /// Defines a document for source. + /// + /// The URL for the document. + /// The GUID that identifies the document language. This is optional + /// The defined document. + /// is . + /// This method is called on a dynamic module that is not a persisted module. + public ISymbolDocumentWriter DefineDocument(string url, Guid language = default) + { + ArgumentException.ThrowIfNullOrEmpty(url); + + return DefineDocumentCore(url, language); + } + + /// + /// When override in a derived class, defines a document for source. + /// + /// The URL for the document. + /// The GUID that identifies the document language. This is optional + /// The defined document. + /// This method is called on a dynamic module that is not a debug module. + protected virtual ISymbolDocumentWriter DefineDocumentCore(string url, Guid language = default) => + throw new InvalidOperationException(SR.InvalidOperation_NotADebugModule); protected abstract EnumBuilder DefineEnumCore(string name, TypeAttributes visibility, Type underlyingType); diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs index b40ba4c28ce8e9..c5dcbd65fb1888 100644 --- a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs +++ b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs @@ -54,7 +54,8 @@ public virtual void EmitWriteLine(string value) { } public abstract void EndExceptionBlock(); public abstract void EndScope(); public abstract void MarkLabel(System.Reflection.Emit.Label loc); - public virtual void MarkSequencePoint(System.Diagnostics.SymbolStore.ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { } + public void MarkSequencePoint(System.Diagnostics.SymbolStore.ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { throw null; } + protected virtual void MarkSequencePointCore(System.Diagnostics.SymbolStore.ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { throw null; } public virtual void ThrowException([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type excType) { } public abstract void UsingNamespace(string usingNamespace); } @@ -74,7 +75,8 @@ protected LocalBuilder() { } public override bool IsPinned { get { throw null; } } public override int LocalIndex { get { throw null; } } public override System.Type LocalType { get { throw null; } } - public virtual void SetLocalSymInfo(string name) { } + public void SetLocalSymInfo(string name) { throw null; } + protected virtual void SetLocalSymInfoCore(string name) { throw null; } } public abstract partial class ParameterBuilder { diff --git a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs index 88f315f2a9b46f..995ab5679fc736 100644 --- a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs +++ b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs @@ -398,7 +398,10 @@ protected ModuleBuilder() { } public override string ScopeName { get { throw null; } } public void CreateGlobalFunctions() { } protected abstract void CreateGlobalFunctionsCore(); - public virtual System.Diagnostics.SymbolStore.ISymbolDocumentWriter DefineDocument(string url, System.Guid language = default) { throw null; } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public System.Diagnostics.SymbolStore.ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType) { throw null; } + public System.Diagnostics.SymbolStore.ISymbolDocumentWriter DefineDocument(string url, System.Guid language = default) { throw null; } + protected virtual System.Diagnostics.SymbolStore.ISymbolDocumentWriter DefineDocumentCore(string url, System.Guid language = default) { throw null; } public System.Reflection.Emit.EnumBuilder DefineEnum(string name, System.Reflection.TypeAttributes visibility, System.Type underlyingType) { throw null; } protected abstract System.Reflection.Emit.EnumBuilder DefineEnumCore(string name, System.Reflection.TypeAttributes visibility, System.Type underlyingType); public System.Reflection.Emit.MethodBuilder DefineGlobalMethod(string name, System.Reflection.MethodAttributes attributes, System.Reflection.CallingConventions callingConvention, System.Type? returnType, System.Type[]? parameterTypes) { throw null; } @@ -481,7 +484,9 @@ public PersistedAssemblyBuilder(System.Reflection.AssemblyName name, System.Refl [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Defining a dynamic assembly requires dynamic code.")] protected override System.Reflection.Emit.ModuleBuilder DefineDynamicModuleCore(string name) { throw null; } [System.CLSCompliantAttribute(false)] - public System.Reflection.Metadata.Ecma335.MetadataBuilder GenerateMetadata(out System.Reflection.Metadata.BlobBuilder ilStream, out System.Reflection.Metadata.BlobBuilder mappedFieldData, bool addPdb = false) { throw null; } + public System.Reflection.Metadata.Ecma335.MetadataBuilder GenerateMetadata(out System.Reflection.Metadata.BlobBuilder ilStream, out System.Reflection.Metadata.BlobBuilder mappedFieldData) { throw null; } + [System.CLSCompliantAttribute(false)] + public System.Reflection.Metadata.Ecma335.MetadataBuilder GenerateMetadata(out System.Reflection.Metadata.BlobBuilder ilStream, out System.Reflection.Metadata.BlobBuilder mappedFieldData, out System.Reflection.Metadata.Ecma335.MetadataBuilder pdbBuilder) { throw null; } protected override System.Reflection.Emit.ModuleBuilder? GetDynamicModuleCore(string name) { throw null; } public override System.Reflection.AssemblyName GetName(bool copiedName) { throw null; } public void Save(string assemblyFileName) { throw null; } diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index 4ab50a83684c01..e3cb6a426c71ba 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -297,4 +297,10 @@ Not supported in an array method of a type definition that is not complete. + + Invalid source document. + + + Emitting symbols is not supported for contaning type. + \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index 083e81e80a0d99..b5a7a82a32cf65 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -18,6 +18,8 @@ internal sealed class ILGeneratorImpl : ILGenerator private readonly BlobBuilder _builder; private readonly InstructionEncoder _il; private readonly ControlFlowBuilder _cfBuilder; + private readonly Scope _scope; // scope of the entire method body + private Scope _currentScope; private bool _hasDynamicStackAllocation; private int _maxStackDepth; private int _currentStackDepth; // Current stack labelStartDepth @@ -25,11 +27,11 @@ internal sealed class ILGeneratorImpl : ILGenerator // Adjustment to add to _maxStackDepth for incorrect/invalid IL. For example, when branch // instructions branches backward with non zero stack depths targeting the same label. private int _depthAdjustment; - private List _locals = new(); + private int _localCount; private Dictionary _labelTable = new(2); private List> _memberReferences = new(); private List _exceptionStack = new(); - private Dictionary> _sequencePoints = new(); + private Dictionary> _documentToSequencePoints = new(); internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size) { @@ -39,14 +41,18 @@ internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size) _builder = new BlobBuilder(Math.Max(size, DefaultSize)); _cfBuilder = new ControlFlowBuilder(); _il = new InstructionEncoder(_builder, _cfBuilder); + _scope = new Scope(_il.Offset, parent: null); + _currentScope = _scope; } internal int GetMaxStack() => Math.Min(ushort.MaxValue, _maxStackDepth + _depthAdjustment); internal List> GetMemberReferences() => _memberReferences; internal InstructionEncoder Instructions => _il; internal bool HasDynamicStackAllocation => _hasDynamicStackAllocation; - internal List Locals => _locals; - internal Dictionary> SequencePoints => _sequencePoints; + internal List Locals => _scope.GetAllLocals(); + internal int LocalCount => _localCount; + internal Scope Scope => _scope; + internal Dictionary> DocumentToSequencePoints => _documentToSequencePoints; public override int ILOffset => _il.Offset; @@ -201,16 +207,19 @@ public override void BeginFinallyBlock() public override void BeginScope() { - // TODO: No-op, will be implemented wit PDB support + _currentScope._children ??= new List(); + Scope newScope = new Scope(_il.Offset, _currentScope); + _currentScope._children.Add(newScope); + _currentScope = newScope; } public override LocalBuilder DeclareLocal(Type localType, bool pinned) { ArgumentNullException.ThrowIfNull(localType); - LocalBuilder local = new LocalBuilderImpl(_locals.Count, localType, _methodBuilder, pinned); - _locals.Add(local); - + _currentScope._locals ??= new List(); + LocalBuilder local = new LocalBuilderImpl(_localCount++, localType, _methodBuilder, pinned); + _currentScope._locals.Add(local); return local; } @@ -734,7 +743,10 @@ public override void EndExceptionBlock() public override void EndScope() { - // TODO: No-op, will be implemented wit PDB support + Debug.Assert(_currentScope._parent != null); + + _currentScope._endOffset = _il.Offset; + _currentScope = _currentScope._parent; } public override void MarkLabel(Label loc) @@ -780,29 +792,32 @@ public override void MarkLabel(Label loc) } } - public override void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) + protected override void MarkSequencePointCore(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { if (document is SymbolDocumentWriter symbolDoc) { - if (_sequencePoints.TryGetValue(symbolDoc, out List? sequencePoints)) + if (_documentToSequencePoints.TryGetValue(symbolDoc, out List? sequencePoints)) { sequencePoints.Add(new SequencePoint(_il.Offset, startLine, startColumn, endLine, endColumn)); } else { sequencePoints = new List { new SequencePoint(_il.Offset, startLine, startColumn, endLine, endColumn) }; - _sequencePoints.Add(symbolDoc, sequencePoints); + _documentToSequencePoints.Add(symbolDoc, sequencePoints); } } else { - throw new ArgumentException(nameof(document)); // TODO: SR + throw new ArgumentException(SR.InvalidOperation_InValidDocument, nameof(document)); } } public override void UsingNamespace(string usingNamespace) { - // TODO: No-op, will be implemented wit PDB support + ArgumentException.ThrowIfNullOrEmpty(usingNamespace); + + _currentScope._importNamespaces ??= new List(); + _currentScope._importNamespaces.Add(usingNamespace); } } @@ -840,4 +855,40 @@ internal LabelInfo(LabelHandle metaLabel) internal int _startDepth; // Stack labelStartDepth, with -1 meaning unknown. internal LabelHandle _metaLabel; } + + internal sealed class Scope + { + public Scope(int offset, Scope? parent) + { + _startOffset = offset; + _parent = parent; + } + + internal Scope? _parent; + internal List? _children; + internal List? _locals; + internal List? _importNamespaces; + internal int _startOffset; + internal int _endOffset; + + internal List GetAllLocals() + { + List locals = new List(); + + if (_locals != null) + { + locals.AddRange(_locals); + } + + if (_children != null) + { + foreach (Scope child in _children) + { + locals.AddRange(child.GetAllLocals()); + } + } + + return locals; + } + } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs index e598e2706750cb..562711965fac78 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs @@ -28,8 +28,21 @@ internal LocalBuilderImpl(int index, Type type, MethodInfo method, bool isPinned internal string? Name => _name; #endregion - #region LocalVariableInfo Override - public override void SetLocalSymInfo(string name) => _name = name; + #region LocalBuilder Override + protected override void SetLocalSymInfoCore(string name) + { + if (_method.DeclaringType is not TypeBuilderImpl typeBuilder) + { + throw new InvalidOperationException(SR.InvalidOperation_NotValidTypeBuilder); + } + + if (typeBuilder.IsCreated()) + { + throw new InvalidOperationException(SR.InvalidOperation_TypeHasBeenCreated); + } + + _name = name; + } #endregion #region LocalVariableInfo Override diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 33b110b4534a54..a2d3500fb4cc5a 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -36,6 +36,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private bool _coreTypesFullyPopulated; private bool _hasGlobalBeenCreated; private Type?[]? _coreTypes; + private MetadataBuilder _pdbMetadata = new(); private static readonly Type[] s_coreTypes = { typeof(void), typeof(object), typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(string), typeof(nint), typeof(nuint), typeof(TypedReference) }; @@ -110,7 +111,7 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) return null; } - internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder, bool addPdb) + internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder, out MetadataBuilder pdbMetadata) { // Add module metadata ModuleDefinitionHandle moduleHandle = _metadataBuilder.AddModule( @@ -136,7 +137,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuil // Add global members WriteFields(_globalTypeBuilder, fieldDataBuilder); - WriteMethods(_globalTypeBuilder._methodDefinitions, genericParams, methodBodyEncoder, addPdb); + WriteMethods(_globalTypeBuilder._methodDefinitions, genericParams, methodBodyEncoder); // Add each type definition to metadata table. foreach (TypeBuilderImpl typeBuilder in _typeDefinitions) @@ -175,7 +176,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuil WriteCustomAttributes(typeBuilder._customAttributes, typeHandle); WriteProperties(typeBuilder); WriteFields(typeBuilder, fieldDataBuilder); - WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder, addPdb); + WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder); WriteEvents(typeBuilder); } @@ -192,6 +193,8 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuil { AddGenericTypeParametersAndConstraintsCustomAttributes(param._parentHandle, param); } + + pdbMetadata = _pdbMetadata; } private void WriteInterfaceImplementations(TypeBuilderImpl typeBuilder, TypeDefinitionHandle typeHandle) @@ -342,7 +345,7 @@ private void PopulateTokensForTypesAndItsMembers() } private void WriteMethods(List methods, List genericParams, - MethodBodyStreamEncoder methodBodyEncoder, bool addPdb) + MethodBodyStreamEncoder methodBodyEncoder) { foreach (MethodBuilderImpl method in methods) { @@ -351,13 +354,10 @@ private void WriteMethods(List methods, List methods, List 0) + if (il.DocumentToSequencePoints.Count > 0) { - Dictionary>.Enumerator enumerator = il.SequencePoints.GetEnumerator(); - if (il.SequencePoints.Count == 1) + Dictionary>.Enumerator enumerator = il.DocumentToSequencePoints.GetEnumerator(); + if (il.DocumentToSequencePoints.Count == 1) // single document { enumerator.MoveNext(); DocumentHandle docHandle = GetDocument(enumerator.Current.Key); BlobBuilder spBlobBuilder = new BlobBuilder(); spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignatureHandle)); PopulateSequencePointsBlob(spBlobBuilder, enumerator.Current.Value); - _metadataBuilder.AddMethodDebugInformation(docHandle, _metadataBuilder.GetOrAddBlob(spBlobBuilder)); + _pdbMetadata.AddMethodDebugInformation(docHandle, _pdbMetadata.GetOrAddBlob(spBlobBuilder)); } else { // sequence points spans multiple docs - _metadataBuilder.AddMethodDebugInformation(default, PopulateMultiDocSequencePointsBlob(enumerator, localSignatureHandle)); + _pdbMetadata.AddMethodDebugInformation(default, PopulateMultiDocSequencePointsBlob(enumerator, localSignatureHandle)); } } else { - // add empty sequence point - _metadataBuilder.AddMethodDebugInformation(default, default); + // empty sequence point + _pdbMetadata.AddMethodDebugInformation(default, default); } + + Scope scope = il.Scope; + scope._endOffset = il.ILOffset; + + AddLocalScope(methodHandle, parentImport: default, MetadataTokens.LocalVariableHandle(_pdbMetadata.GetRowCount(TableIndex.LocalVariable) + 1), scope); } - private BlobHandle PopulateMultiDocSequencePointsBlob(Dictionary>.Enumerator enumerator, StandaloneSignatureHandle localSignatureHandle) + private BlobHandle PopulateMultiDocSequencePointsBlob(Dictionary>.Enumerator enumerator, StandaloneSignatureHandle localSignature) { BlobBuilder spBlobBuilder = new BlobBuilder(); // header: - spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignatureHandle)); + spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignature)); + enumerator.MoveNext(); + KeyValuePair> pair = enumerator.Current; + spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(GetDocument(pair.Key))); + // First sequence point record + PopulateSequencePointsBlob(spBlobBuilder, pair.Value); while (enumerator.MoveNext()) { - KeyValuePair> pair = enumerator.Current; - DocumentHandle documentHandle = GetDocument(pair.Key); - spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(documentHandle)); + pair = enumerator.Current; + spBlobBuilder.WriteCompressedInteger(0); + spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(GetDocument(pair.Key))); PopulateSequencePointsBlob(spBlobBuilder, pair.Value); } - return _metadataBuilder.GetOrAddBlob(spBlobBuilder); + return _pdbMetadata.GetOrAddBlob(spBlobBuilder); } private static void PopulateSequencePointsBlob(BlobBuilder spBlobBuilder, List sequencePoints) @@ -471,7 +480,6 @@ private static void PopulateSequencePointsBlob(BlobBuilder spBlobBuilder, List? locals, LocalVariableHandle firstLocalOfLastScope) + { + if (locals != null) + { + bool firstLocalSet = false; + foreach (LocalBuilderImpl local in locals) + { + LocalVariableHandle localHandle = _pdbMetadata.AddLocalVariable(LocalVariableAttributes.None, local.LocalIndex, + local.Name == null ? default : _pdbMetadata.GetOrAddString(local.Name)); + if (!firstLocalSet) + { + firstLocalOfLastScope = localHandle; + firstLocalSet = true; + } + } + } + + return firstLocalOfLastScope; + } + + private ImportScopeHandle GetImportScopeHandle(List? importNamespaces, ImportScopeHandle parent) + { + if (importNamespaces == null) + { + return default; + } + + BlobBuilder importBlob = new BlobBuilder(); + + foreach (string importNs in importNamespaces) + { + importBlob.WriteByte((byte)ImportDefinitionKind.ImportNamespace); + importBlob.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_pdbMetadata.GetOrAddBlobUTF8(importNs))); + } + + return _pdbMetadata.AddImportScope(parent, _pdbMetadata.GetOrAddBlob(importBlob)); + } + private static void SerializeDeltaLinesAndColumns(BlobBuilder spBuilder, SequencePoint sequencePoint) { int deltaLines = sequencePoint.EndLine - sequencePoint.StartLine; @@ -528,11 +590,11 @@ private DocumentHandle GetDocument(SymbolDocumentWriter docWriter) } private DocumentHandle AddDocument(string url, Guid language, Guid hashAlgorithm, byte[]? hash) => - _metadataBuilder.AddDocument( - name: _metadataBuilder.GetOrAddDocumentName(url), - hashAlgorithm: hashAlgorithm == default ? default : _metadataBuilder.GetOrAddGuid(hashAlgorithm), + _pdbMetadata.AddDocument( + name: _pdbMetadata.GetOrAddDocumentName(url), + hashAlgorithm: hashAlgorithm == default ? default : _pdbMetadata.GetOrAddGuid(hashAlgorithm), hash: hash == null ? default : _metadataBuilder.GetOrAddBlob(hash), - language: language == default ? default : _metadataBuilder.GetOrAddGuid(language)); + language: language == default ? default : _pdbMetadata.GetOrAddGuid(language)); private void FillMemberReferences(ILGeneratorImpl il) { @@ -1027,8 +1089,7 @@ private EntityHandle GetHandleForMember(MemberInfo member) } private static bool IsConstructedFromTypeBuilder(Type type) => type.IsConstructedGenericType && - (type.GetGenericTypeDefinition() is TypeBuilderImpl || - ContainsTypeBuilder(type.GetGenericArguments())); + (type.GetGenericTypeDefinition() is TypeBuilderImpl || ContainsTypeBuilder(type.GetGenericArguments())); internal static bool ContainsTypeBuilder(Type[] genericArguments) { @@ -1294,10 +1355,8 @@ private static SignatureCallingConvention GetSignatureConvention(CallingConventi _ => SignatureCallingConvention.Default, }; - public override ISymbolDocumentWriter DefineDocument(string url, Guid language = default) + protected override ISymbolDocumentWriter DefineDocumentCore(string url, Guid language = default) { - ArgumentNullException.ThrowIfNull(url); - return new SymbolDocumentWriter(url, language); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs index 19ce1872adb275..a07b44620fa85f 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs @@ -11,11 +11,13 @@ internal sealed class SymbolDocumentWriter : ISymbolDocumentWriter internal readonly string _url; private Guid _hashAlgorithm; private byte[]? _hash; + private byte[]? _source; internal string URL => _url; internal Guid Language => _language; internal Guid HashAlgorithm => _hashAlgorithm; internal byte[]? Hash => _hash; + internal byte[]? Source => _source; public SymbolDocumentWriter(string url, Guid language) { @@ -29,6 +31,9 @@ public void SetCheckSum(Guid algorithmId, byte[] checkSum) _hash = checkSum; } - public void SetSource(byte[] source) => throw new NotImplementedException(); + public void SetSource(byte[] source) + { + _source = source; + } } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs index a4c4a1899173f7..e75096efd7b6c1 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs @@ -98,7 +98,7 @@ private void SaveInternal(Stream stream) { ArgumentNullException.ThrowIfNull(stream); - PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData); + PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, out _); WritePEImage(stream, ilStream, fieldData); } @@ -107,19 +107,35 @@ private void SaveInternal(Stream stream) /// /// Outputs bytes that includes all method's IL (body) emitted. /// Outputs bytes that includes all field RVA data defined in the assembly. - /// A boolean value that indicates whether to include PDB information. /// A that includes all members defined in the Assembly. /// A module not defined for the assembly. /// The metadata already populated for the assembly before. [CLSCompliant(false)] - public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData, bool addPdb = false) + public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData) { - PopulateAssemblyMetadata(out ilStream, out mappedFieldData); + PopulateAssemblyMetadata(out ilStream, out mappedFieldData, out _); return _metadataBuilder; } - private void PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData) + /// + /// Generates the metadata for the . + /// + /// Outputs bytes that includes all method's IL (body) emitted. + /// Outputs bytes that includes all field RVA data defined in the assembly. + /// Outputs that includes PDB metadata. + /// A that includes all members defined in the Assembly. + /// A module not defined for the assembly. + /// The metadata already populated for the assembly before. + [CLSCompliant(false)] + public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData, out MetadataBuilder pdbBuilder) + { + PopulateAssemblyMetadata(out ilStream, out mappedFieldData, out pdbBuilder); + + return _metadataBuilder; + } + + private void PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, out MetadataBuilder pdbMetadata) { if (_module == null) { @@ -147,7 +163,7 @@ private void PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder ); _module.WriteCustomAttributes(_customAttributes, assemblyHandle); - _module.AppendMetadata(new MethodBodyStreamEncoder(ilStream), fieldData); + _module.AppendMetadata(new MethodBodyStreamEncoder(ilStream), fieldData, out pdbMetadata); _isMetadataPopulated = true; } diff --git a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs new file mode 100644 index 00000000000000..f79ef3ee71cbf4 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs @@ -0,0 +1,286 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.SymbolStore; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using Xunit; + +namespace System.Reflection.Emit.Tests +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public class ILGeneratorScopesAndSequencePointsTests + { + [Fact] + public void SetLocalSymInfo_UsingNamespace_Validations() + { + ModuleBuilder mb = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly2"), typeof(object).Assembly).DefineDynamicModule("MyModule2"); + TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + ILGenerator il1 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static).GetILGenerator(); + LocalBuilder local = il1.DeclareLocal(typeof(int)); + Assert.Throws("usingNamespace", () => il1.UsingNamespace(null)); + Assert.Throws("usingNamespace", () => il1.UsingNamespace(string.Empty)); + local.SetLocalSymInfo(null); // can be null + il1.Emit(OpCodes.Ret); + tb.CreateType(); + Assert.Throws(() => local.SetLocalSymInfo("myInt1")); // type created + } + + [Fact] + public void LocalsNamespacesWithinNestedScopes() + { + PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly); + ModuleBuilder mb = ab.DefineDynamicModule("MyModule"); + TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + ISymbolDocumentWriter srcDoc = mb.DefineDocument("MySourceFile.cs", SymLanguageType.CSharp); + MethodBuilder method = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); + ILGenerator il1 = method.GetILGenerator(); + LocalBuilder local = il1.DeclareLocal(typeof(int)); + il1.UsingNamespace("System"); + local.SetLocalSymInfo("myInt1"); + il1.Emit(OpCodes.Ldarg_0); + il1.Emit(OpCodes.Ldarg_1); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Stloc_0); + LocalBuilder local2 = il1.DeclareLocal(typeof(string)); + local2.SetLocalSymInfo("myString"); + il1.Emit(OpCodes.Ldstr, "MyAssembly"); + il1.Emit(OpCodes.Stloc, local2); + il1.BeginScope(); + il1.UsingNamespace("System.Reflection"); + LocalBuilder local3 = il1.DeclareLocal(typeof(AssemblyName)); + local3.SetLocalSymInfo("myAssembly"); + il1.Emit(OpCodes.Ldloc_1); + il1.Emit(OpCodes.Newobj, typeof(AssemblyName).GetConstructor([typeof(string)])); + il1.Emit(OpCodes.Stloc_2); + il1.BeginScope(); + LocalBuilder local4 = il1.DeclareLocal(typeof(int)); + local4.SetLocalSymInfo("myInt2"); + LocalBuilder local5 = il1.DeclareLocal(typeof(int)); + local5.SetLocalSymInfo("myInt3"); + il1.Emit(OpCodes.Ldc_I4_2); + il1.Emit(OpCodes.Stloc_3); + il1.Emit(OpCodes.Ldc_I4_5); + il1.Emit(OpCodes.Stloc_S, 4); + il1.Emit(OpCodes.Ldloc_S, 4); + il1.Emit(OpCodes.Ldloc_3); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Stloc_0); + il1.EndScope(); + il1.UsingNamespace("System.Reflection.Emit"); + il1.EndScope(); + il1.Emit(OpCodes.Ldloc_0); + il1.Emit(OpCodes.Ret); + tb.CreateType(); + + MetadataBuilder mdb = ab.GenerateMetadata(out BlobBuilder _, out BlobBuilder _, out MetadataBuilder pdbMetadata); + + BlobBuilder portablePdbBlob = new BlobBuilder(); + PortablePdbBuilder pdbBuilder = new PortablePdbBuilder(pdbMetadata, mdb.GetRowCounts(), default); + pdbBuilder.Serialize(portablePdbBlob); + using TempFile pdbFile = TempFile.Create(); + using var pdbFileStream = new FileStream(pdbFile.Path, FileMode.Create, FileAccess.Write); + portablePdbBlob.WriteContentTo(pdbFileStream); + pdbFileStream.Close(); + + using var fs = new FileStream(pdbFile.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using MetadataReaderProvider provider = MetadataReaderProvider.FromPortablePdbStream(fs); + MetadataReader reader = provider.GetMetadataReader(); + MethodDebugInformation mdi = reader.GetMethodDebugInformation(MetadataTokens.MethodDebugInformationHandle(method.MetadataToken)); + SequencePointCollection.Enumerator spcEnumerator = mdi.GetSequencePoints().GetEnumerator(); + Assert.False(spcEnumerator.MoveNext()); + + LocalScopeHandleCollection.Enumerator localScopes = reader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(method.MetadataToken)).GetEnumerator(); + Assert.True(localScopes.MoveNext()); + LocalScope localScope = reader.GetLocalScope(localScopes.Current); + Assert.Equal(0, localScope.StartOffset); + Assert.Equal(35, localScope.EndOffset); + LocalVariableHandleCollection.Enumerator localEnumerator = localScope.GetLocalVariables().GetEnumerator(); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("myInt1", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("myString", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.False(localEnumerator.MoveNext()); + + ImportScope importScope = reader.GetImportScope(localScope.ImportScope); + Assert.True(importScope.Parent.IsNil); + ImportDefinitionCollection.Enumerator importEnumerator = importScope.GetImports().GetEnumerator(); + Assert.True(importEnumerator.MoveNext()); + ImportDefinition importDef = importEnumerator.Current; + Assert.Equal(ImportDefinitionKind.ImportNamespace, importDef.Kind); + BlobReader blobReader = reader.GetBlobReader(importDef.TargetNamespace); + Assert.Equal("System", blobReader.ReadUTF8(blobReader.Length)); + Assert.False(importEnumerator.MoveNext()); + + Assert.True(localScopes.MoveNext()); + LocalScope innerScope = reader.GetLocalScope(localScopes.Current); + Assert.Equal(10, innerScope.StartOffset); + Assert.Equal(33, innerScope.EndOffset); + localEnumerator = innerScope.GetLocalVariables().GetEnumerator(); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("myAssembly", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.False(localEnumerator.MoveNext()); + + ImportScope innerImport = reader.GetImportScope(innerScope.ImportScope); + Assert.Equal(importScope, reader.GetImportScope(innerImport.Parent)); + importEnumerator = innerImport.GetImports().GetEnumerator(); + Assert.True(importEnumerator.MoveNext()); + importDef = importEnumerator.Current; + Assert.Equal(ImportDefinitionKind.ImportNamespace, importDef.Kind); + blobReader = reader.GetBlobReader(importDef.TargetNamespace); + Assert.Equal("System.Reflection", blobReader.ReadUTF8(blobReader.Length)); + Assert.True(importEnumerator.MoveNext()); + importDef = importEnumerator.Current; + Assert.Equal(ImportDefinitionKind.ImportNamespace, importDef.Kind); + blobReader = reader.GetBlobReader(importDef.TargetNamespace); + Assert.Equal("System.Reflection.Emit", blobReader.ReadUTF8(blobReader.Length)); + Assert.False(importEnumerator.MoveNext()); + + Assert.True(localScopes.MoveNext()); + LocalScope innerMost = reader.GetLocalScope(localScopes.Current); + Assert.Equal(17, innerMost.StartOffset); + Assert.Equal(33, innerMost.EndOffset); + localEnumerator = innerMost.GetLocalVariables().GetEnumerator(); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("myInt2", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("myInt3", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.False(localEnumerator.MoveNext()); + Assert.False(localScopes.MoveNext()); + + Assert.True(innerMost.ImportScope.IsNil); + } + + [Fact] + public void DefineDocument_MarkSequencePoint_Validations() + { + ModuleBuilder runtimeModule = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("T"), AssemblyBuilderAccess.Run).DefineDynamicModule("T"); + Assert.Throws(() => runtimeModule.DefineDocument("MySourceFile.cs", SymLanguageType.CSharp)); + + ModuleBuilder mb = new PersistedAssemblyBuilder(new AssemblyName("Assembly"), typeof(object).Assembly).DefineDynamicModule("MyModule"); + TypeBuilder tb = mb.DefineType("Type", TypeAttributes.Public | TypeAttributes.Class); + Assert.Throws("url", () => mb.DefineDocument(null)); + Assert.Throws("url", () => mb.DefineDocument(string.Empty)); + + ILGenerator il = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static).GetILGenerator(); + Assert.Throws("document", () => il.MarkSequencePoint(null, 0, 0, 0, 1)); + Assert.Throws("document", () => il.MarkSequencePoint(new TestDocument(), 0, 0, 0, 1)); + Assert.Throws("startLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), -1, 1, 1, 1)); + Assert.Throws("startColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, -1, 1, 1)); + Assert.Throws("endLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, -1, 1)); + Assert.Throws("endColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 1, -1)); + Assert.Throws("startLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 0x20000001, 1, 1, 1)); + Assert.Throws("startColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 0x10001, 1, 1)); + Assert.Throws("endLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 0x20000001, 1)); + Assert.Throws("endColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 1, 0x10001)); + Assert.Throws("endColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 1, 1)); + Assert.Throws("endLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 0, 1)); + } + + [Fact] + public void MultipleDocumentsAndSequencePoints() + { + PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly); + ModuleBuilder mb = ab.DefineDynamicModule("MyModule"); + TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + ISymbolDocumentWriter srcDoc1 = mb.DefineDocument("MySource1.cs", SymLanguageType.CSharp); + ISymbolDocumentWriter srcDoc2 = mb.DefineDocument("MySource2.cs", SymLanguageType.CSharp); + MethodBuilder method = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static); + ILGenerator il1 = method.GetILGenerator(); + LocalBuilder local = il1.DeclareLocal(typeof(int)); + local.SetLocalSymInfo("MyInt"); + il1.MarkSequencePoint(srcDoc2, 7, 0, 7, 20); + il1.Emit(OpCodes.Ldarg_0); + il1.Emit(OpCodes.Ldarg_1); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Stloc_0); + il1.MarkSequencePoint(srcDoc1, 8, 0, 9, 18); + il1.Emit(OpCodes.Ldc_I4_2); + il1.Emit(OpCodes.Stloc_1); + il1.Emit(OpCodes.Ldloc_0); + il1.Emit(OpCodes.Ldloc_1); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Stloc_0); + il1.Emit(OpCodes.Ldloc_0); + il1.MarkSequencePoint(srcDoc1, 0xfeefee, 0, 0xfeefee, 0); // hidden sequence point + il1.Emit(OpCodes.Ret); + + MethodBuilder entryPoint = tb.DefineMethod("Main", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static); + ILGenerator il2 = entryPoint.GetILGenerator(); + il2.Emit(OpCodes.Ldc_I4_S, 10); + il2.Emit(OpCodes.Ldc_I4_1); + il2.Emit(OpCodes.Call, method); + il2.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", [typeof(int)])); + il2.Emit(OpCodes.Ret); + tb.CreateType(); + + MetadataBuilder mdb = ab.GenerateMetadata(out BlobBuilder _, out BlobBuilder _, out MetadataBuilder pdbMetadata); + + BlobBuilder portablePdbBlob = new BlobBuilder(); + PortablePdbBuilder pdbBuilder = new PortablePdbBuilder(pdbMetadata, mdb.GetRowCounts(), default); + pdbBuilder.Serialize(portablePdbBlob); + using TempFile pdbFile = TempFile.Create(); + using var pdbFileStream = new FileStream(pdbFile.Path, FileMode.Create, FileAccess.Write); + portablePdbBlob.WriteContentTo(pdbFileStream); + pdbFileStream.Close(); + + using var fs = new FileStream(pdbFile.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using MetadataReaderProvider provider = MetadataReaderProvider.FromPortablePdbStream(fs); + MetadataReader reader = provider.GetMetadataReader(); + DocumentHandleCollection.Enumerator docEnumerator = reader.Documents.GetEnumerator(); + Assert.Equal(2, reader.Documents.Count); + Assert.True(docEnumerator.MoveNext()); + Document doc1 = reader.GetDocument(docEnumerator.Current); + Assert.Equal("MySource2.cs", reader.GetString(doc1.Name)); + Assert.Equal(SymLanguageType.CSharp, reader.GetGuid(doc1.Language)); + Assert.True(docEnumerator.MoveNext()); + Document doc2 = reader.GetDocument(docEnumerator.Current); + Assert.Equal("MySource1.cs", reader.GetString(doc2.Name)); + Assert.Equal(SymLanguageType.CSharp, reader.GetGuid(doc2.Language)); + Assert.False(docEnumerator.MoveNext()); + + MethodDebugInformation mdi1 = reader.GetMethodDebugInformation(MetadataTokens.MethodDebugInformationHandle(method.MetadataToken)); + Assert.True(mdi1.Document.IsNil); + SequencePointCollection.Enumerator spcEnumerator = mdi1.GetSequencePoints().GetEnumerator(); + Assert.True(spcEnumerator.MoveNext()); + SequencePoint sp = spcEnumerator.Current; + Assert.Equal(doc1, reader.GetDocument(sp.Document)); + Assert.Equal(7, sp.StartLine); + Assert.False(sp.IsHidden); + Assert.Equal(0, sp.Offset); + Assert.Equal(0, sp.StartColumn); + Assert.Equal(7, sp.EndLine); + Assert.Equal(20, sp.EndColumn); + Assert.True(spcEnumerator.MoveNext()); + sp = spcEnumerator.Current; + Assert.Equal(doc2, reader.GetDocument(sp.Document)); + //Assert.Equal(8, sp.StartLine); 11 + Assert.Equal(0, sp.StartColumn); + //Assert.Equal(9, sp.EndLine); 12 + Assert.Equal(18, sp.EndColumn); + Assert.True(spcEnumerator.MoveNext()); + sp = spcEnumerator.Current; + Assert.Equal(doc2, reader.GetDocument(sp.Document)); + Assert.True(sp.IsHidden); + Assert.False(spcEnumerator.MoveNext()); + + LocalScopeHandleCollection.Enumerator localScopes = reader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(method.MetadataToken)).GetEnumerator(); + Assert.True(localScopes.MoveNext()); + LocalScope locals = reader.GetLocalScope(localScopes.Current); + Assert.Equal(0, locals.StartOffset); + Assert.Equal(12, locals.EndOffset); + LocalVariableHandleCollection.Enumerator localEnumerator = locals.GetLocalVariables().GetEnumerator(); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("MyInt", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.False(localEnumerator.MoveNext()); + Assert.False(localScopes.MoveNext()); + } + + private class TestDocument : ISymbolDocumentWriter + { + public void SetCheckSum(Guid algorithmId, byte[] checkSum) => throw new NotImplementedException(); + public void SetSource(byte[] source) => throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/PortablePdb/PortablePdbStandalonePdbTest.cs b/src/libraries/System.Reflection.Emit/tests/PortablePdb/PortablePdbStandalonePdbTest.cs new file mode 100644 index 00000000000000..e1ce56f151d81c --- /dev/null +++ b/src/libraries/System.Reflection.Emit/tests/PortablePdb/PortablePdbStandalonePdbTest.cs @@ -0,0 +1,234 @@ +// 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.SymbolStore; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using Xunit; + +namespace System.Reflection.Emit.Tests +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public class PortablePdbStandalonePdbTest + { + [Fact] + public void CreateStandalonePDBAndVerifyTest() + { + using (TempFile pdbFile = TempFile.Create()) + using (TempFile file = TempFile.Create()) + { + MethodBuilder method1, entryPoint; + MetadataBuilder metadataBuilder = GenerateAssemblyAndMetadata(out method1, out entryPoint, out BlobBuilder ilStream, out MetadataBuilder pdbMetadata); + MethodDefinitionHandle entryPointHandle = MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken); + + BlobBuilder portablePdbBlob = new BlobBuilder(); + PortablePdbBuilder pdbBuilder = new PortablePdbBuilder(pdbMetadata, metadataBuilder.GetRowCounts(), entryPointHandle); + BlobContentId pdbContentId = pdbBuilder.Serialize(portablePdbBlob); + using var pdbFileStream = new FileStream(pdbFile.Path, FileMode.Create, FileAccess.Write); + portablePdbBlob.WriteContentTo(pdbFileStream); + pdbFileStream.Close(); + + DebugDirectoryBuilder debugDirectoryBuilder = new DebugDirectoryBuilder(); + debugDirectoryBuilder.AddCodeViewEntry(pdbFile.Path, pdbContentId, pdbBuilder.FormatVersion); + + ManagedPEBuilder peBuilder = new ManagedPEBuilder( + header: new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage), + metadataRootBuilder: new MetadataRootBuilder(metadataBuilder), + ilStream: ilStream, + debugDirectoryBuilder: debugDirectoryBuilder, + entryPoint: entryPointHandle); + + BlobBuilder peBlob = new BlobBuilder(); + peBuilder.Serialize(peBlob); + using var assemblyFileStream = new FileStream(file.Path, FileMode.Create, FileAccess.Write); + peBlob.WriteContentTo(assemblyFileStream); + assemblyFileStream.Close(); + + using var fs = new FileStream(pdbFile.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using MetadataReaderProvider provider = MetadataReaderProvider.FromPortablePdbStream(fs); + ValidatePDB(method1, entryPoint, provider.GetMetadataReader()); + } + } + + private static void ValidatePDB(MethodBuilder method, MethodBuilder entryPoint, MetadataReader reader) + { + DocumentHandleCollection.Enumerator docEnumerator = reader.Documents.GetEnumerator(); + Assert.Equal(1, reader.Documents.Count); + Assert.True(docEnumerator.MoveNext()); + Document doc = reader.GetDocument(docEnumerator.Current); + Assert.Equal("MySourceFile.cs", reader.GetString(doc.Name)); + Assert.Equal(SymLanguageType.CSharp, reader.GetGuid(doc.Language)); + Assert.Equal(default, reader.GetGuid(doc.HashAlgorithm)); + Assert.False(docEnumerator.MoveNext()); + + MethodDebugInformation mdi1 = reader.GetMethodDebugInformation(MetadataTokens.MethodDebugInformationHandle(method.MetadataToken)); + Assert.Equal(doc, reader.GetDocument(mdi1.Document)); + SequencePointCollection.Enumerator spce = mdi1.GetSequencePoints().GetEnumerator(); + Assert.True(spce.MoveNext()); + SequencePoint sp = spce.Current; + Assert.False(sp.IsHidden); + Assert.Equal(7, sp.StartLine); + Assert.Equal(0, sp.StartColumn); + Assert.Equal(7, sp.EndLine); + Assert.Equal(20, sp.EndColumn); + Assert.True(spce.MoveNext()); + sp = spce.Current; + Assert.False(sp.IsHidden); + Assert.Equal(8, sp.StartLine); + Assert.Equal(0, sp.StartColumn); + Assert.Equal(9, sp.EndLine); + Assert.Equal(18, sp.EndColumn); + Assert.False(spce.MoveNext()); + + LocalScopeHandleCollection.Enumerator localScopes = reader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(method.MetadataToken)).GetEnumerator(); + Assert.True(localScopes.MoveNext()); + LocalScope localScope = reader.GetLocalScope(localScopes.Current); + Assert.Equal(0, localScope.StartOffset); + Assert.Equal(12, localScope.EndOffset); + LocalVariableHandleCollection.Enumerator localEnumerator = localScope.GetLocalVariables().GetEnumerator(); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("MyInt", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.False(localEnumerator.MoveNext()); + + Assert.True(localScope.ImportScope.IsNil); + + Assert.True(localScopes.MoveNext()); + localScope = reader.GetLocalScope(localScopes.Current); + Assert.Equal(4, localScope.StartOffset); + Assert.Equal(10, localScope.EndOffset); + localEnumerator = localScope.GetLocalVariables().GetEnumerator(); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("MyInt2", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.False(localEnumerator.MoveNext()); + Assert.False(localScopes.MoveNext()); + + ImportScope importScope = reader.GetImportScope(localScope.ImportScope); + Assert.True(importScope.Parent.IsNil); + ImportDefinitionCollection.Enumerator importEnumerator = importScope.GetImports().GetEnumerator(); + Assert.True(importEnumerator.MoveNext()); + ImportDefinition importDef = importEnumerator.Current; + Assert.Equal(ImportDefinitionKind.ImportNamespace, importDef.Kind); + BlobReader blobReader = reader.GetBlobReader(importDef.TargetNamespace); + Assert.Equal("System.Reflection", blobReader.ReadUTF8(blobReader.Length)); + + mdi1 = reader.GetMethodDebugInformation(MetadataTokens.MethodDebugInformationHandle(entryPoint.MetadataToken)); + Assert.Equal(doc, reader.GetDocument(mdi1.Document)); + spce = mdi1.GetSequencePoints().GetEnumerator(); + Assert.True(spce.MoveNext()); + sp = spce.Current; + Assert.False(sp.IsHidden); + Assert.Equal(12, sp.StartLine); + Assert.Equal(0, sp.StartColumn); + Assert.Equal(12, sp.EndLine); + Assert.Equal(37, sp.EndColumn); + Assert.False(spce.MoveNext()); + + localScopes = reader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken)).GetEnumerator(); + Assert.True(localScopes.MoveNext()); + localScope = reader.GetLocalScope(localScopes.Current); + Assert.Equal(0, localScope.StartOffset); + Assert.Equal(21, localScope.EndOffset); + localEnumerator = localScope.GetLocalVariables().GetEnumerator(); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("MyLoc1", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("MyLoc2", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.False(localEnumerator.MoveNext()); + Assert.False(localScopes.MoveNext()); + } + + private static MetadataBuilder GenerateAssemblyAndMetadata(out MethodBuilder method, + out MethodBuilder entryPoint, out BlobBuilder ilStream, out MetadataBuilder pdbMetadata) + { + PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly2"), typeof(object).Assembly); + ModuleBuilder mb = ab.DefineDynamicModule("MyModule2"); + TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + ISymbolDocumentWriter srcdoc = mb.DefineDocument("MySourceFile.cs", SymLanguageType.CSharp); + method = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); + ILGenerator il1 = method.GetILGenerator(); + LocalBuilder local = il1.DeclareLocal(typeof(int)); + local.SetLocalSymInfo("MyInt"); + il1.MarkSequencePoint(srcdoc, 7, 0, 7, 20); + il1.Emit(OpCodes.Ldarg_0); + il1.Emit(OpCodes.Ldarg_1); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Stloc_0); + il1.MarkSequencePoint(srcdoc, 8, 0, 9, 18); + il1.BeginScope(); + il1.UsingNamespace("System.Reflection"); + LocalBuilder local2 = il1.DeclareLocal(typeof(int)); + local2.SetLocalSymInfo("MyInt2"); + il1.Emit(OpCodes.Ldc_I4_2); + il1.Emit(OpCodes.Stloc_1); + il1.Emit(OpCodes.Ldloc_0); + il1.Emit(OpCodes.Ldloc_1); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Stloc_0); + il1.EndScope(); + il1.Emit(OpCodes.Ldloc_0); + il1.Emit(OpCodes.Ret); + + entryPoint = tb.DefineMethod("Mm", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static); + ILGenerator il2 = entryPoint.GetILGenerator(); + local = il2.DeclareLocal(typeof(int)); + local.SetLocalSymInfo("MyLoc1"); + il2.MarkSequencePoint(srcdoc, 12, 0, 12, 37); + local2 = il2.DeclareLocal(typeof(int)); + local2.SetLocalSymInfo("MyLoc2"); + il2.Emit(OpCodes.Ldc_I4_S, 10); + il2.Emit(OpCodes.Stloc_0); + il2.Emit(OpCodes.Ldc_I4_1); + il2.Emit(OpCodes.Stloc_1); + il2.Emit(OpCodes.Ldloc_0); + il2.Emit(OpCodes.Ldloc_1); + il2.Emit(OpCodes.Call, method); + il2.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", [typeof(int)])); + il2.Emit(OpCodes.Ret); + tb.CreateType(); + return ab.GenerateMetadata(out ilStream, out BlobBuilder _, out pdbMetadata); + } + + [Fact] + public void CreateEmbeddedPDBAndVerifyTest() + { + using (TempFile file = TempFile.Create()) + { + MethodBuilder method1, entryPoint; + MetadataBuilder metadataBuilder = GenerateAssemblyAndMetadata(out method1, out entryPoint, out BlobBuilder ilStream, out MetadataBuilder pdbMetadata); + MethodDefinitionHandle entryPointHandle = MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken); + + BlobBuilder portablePdbBlob = new BlobBuilder(); + PortablePdbBuilder pdbBuilder = new PortablePdbBuilder(pdbMetadata, metadataBuilder.GetRowCounts(), entryPointHandle); + + BlobContentId pdbContentId = pdbBuilder.Serialize(portablePdbBlob); + DebugDirectoryBuilder debugDirectoryBuilder = new DebugDirectoryBuilder(); + debugDirectoryBuilder.AddEmbeddedPortablePdbEntry(portablePdbBlob, pdbBuilder.FormatVersion); + + ManagedPEBuilder peBuilder = new ManagedPEBuilder( + header: new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage), + metadataRootBuilder: new MetadataRootBuilder(metadataBuilder), + ilStream: ilStream, + debugDirectoryBuilder: debugDirectoryBuilder, + entryPoint: entryPointHandle); + + BlobBuilder peBlob = new BlobBuilder(); + peBuilder.Serialize(peBlob); + using var fileStream = new FileStream(file.Path, FileMode.Create, FileAccess.Write); + peBlob.WriteContentTo(fileStream); + fileStream.Close(); + + using var fs = new FileStream(file.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var peReader = new PEReader(fs); + ImmutableArray entries = peReader.ReadDebugDirectory(); + Assert.Equal(1, entries.Length); + Assert.Equal(DebugDirectoryEntryType.EmbeddedPortablePdb, entries[0].Type); + + using MetadataReaderProvider provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entries[0]); + ValidatePDB(method1, entryPoint, provider.GetMetadataReader()); + } + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj index 5317b25145a55d..56d7b3869a1665 100644 --- a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj +++ b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj @@ -73,6 +73,8 @@ + + From 8b5a2ee691188d84a96cab3f108d81deea113d1b Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 5 Apr 2024 15:39:32 -0700 Subject: [PATCH 3/9] Fix bug in multi doc sequence point --- .../System/Reflection/Emit/ModuleBuilderImpl.cs | 10 +++++----- .../ILGeneratorScopesAndSequencePointsTests.cs | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index a2d3500fb4cc5a..a98d96a2f91905 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -448,23 +448,23 @@ private BlobHandle PopulateMultiDocSequencePointsBlob(Dictionary sequencePoints) + private static void PopulateSequencePointsBlob(BlobBuilder spBlobBuilder, List sequencePoints, int previousNonHiddenStartLine = -1, int previousNonHiddenStartColumn = -1) { - int previousNonHiddenStartLine = -1; - int previousNonHiddenStartColumn = -1; - for (int i = 0; i < sequencePoints.Count; i++) { // IL offset delta: diff --git a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs index f79ef3ee71cbf4..36bdb9e0b8c9cc 100644 --- a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs @@ -198,12 +198,13 @@ public void MultipleDocumentsAndSequencePoints() il1.MarkSequencePoint(srcDoc1, 8, 0, 9, 18); il1.Emit(OpCodes.Ldc_I4_2); il1.Emit(OpCodes.Stloc_1); + il1.MarkSequencePoint(srcDoc1, 0xfeefee, 0, 0xfeefee, 0); // hidden sequence point il1.Emit(OpCodes.Ldloc_0); il1.Emit(OpCodes.Ldloc_1); il1.Emit(OpCodes.Add); il1.Emit(OpCodes.Stloc_0); + il1.MarkSequencePoint(srcDoc1, 11, 1, 11, 20); il1.Emit(OpCodes.Ldloc_0); - il1.MarkSequencePoint(srcDoc1, 0xfeefee, 0, 0xfeefee, 0); // hidden sequence point il1.Emit(OpCodes.Ret); MethodBuilder entryPoint = tb.DefineMethod("Main", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static); @@ -255,14 +256,24 @@ public void MultipleDocumentsAndSequencePoints() Assert.True(spcEnumerator.MoveNext()); sp = spcEnumerator.Current; Assert.Equal(doc2, reader.GetDocument(sp.Document)); - //Assert.Equal(8, sp.StartLine); 11 + Assert.Equal(4, sp.Offset); + Assert.Equal(8, sp.StartLine); Assert.Equal(0, sp.StartColumn); - //Assert.Equal(9, sp.EndLine); 12 + Assert.Equal(9, sp.EndLine); Assert.Equal(18, sp.EndColumn); Assert.True(spcEnumerator.MoveNext()); sp = spcEnumerator.Current; Assert.Equal(doc2, reader.GetDocument(sp.Document)); Assert.True(sp.IsHidden); + Assert.Equal(6, sp.Offset); + Assert.True(spcEnumerator.MoveNext()); + sp = spcEnumerator.Current; + Assert.Equal(doc2, reader.GetDocument(sp.Document)); + Assert.Equal(10, sp.Offset); + Assert.Equal(11, sp.StartLine); + Assert.Equal(1, sp.StartColumn); + Assert.Equal(11, sp.EndLine); + Assert.Equal(20, sp.EndColumn); Assert.False(spcEnumerator.MoveNext()); LocalScopeHandleCollection.Enumerator localScopes = reader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(method.MetadataToken)).GetEnumerator(); From 5e1e756b3c99bb6a430458ae86eb78c8ea7a2155 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 5 Apr 2024 16:37:08 -0700 Subject: [PATCH 4/9] Apply feedbacks --- .../src/System/Reflection/Emit/ILGenerator.cs | 10 ++++--- .../src/Resources/Strings.resx | 4 +-- .../System/Reflection/Emit/ILGeneratorImpl.cs | 4 +-- .../Reflection/Emit/ModuleBuilderImpl.cs | 29 +++++++++---------- .../Emit/PersistedAssemblyBuilder.cs | 4 +-- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs index 8d32862f447ff1..516c55dbf89682 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -212,11 +212,11 @@ public virtual LocalBuilder DeclareLocal(Type localType) /// is . /// is not valid. /// - /// is not within range [0, 0x20000000). - /// is not within range [0, 0x20000000) or lower than . - /// is not within range [0, 0x10000). + /// is not within range [0, 0x20000000) or + /// is not within range [0, 0x20000000) or lower than or + /// is not within range [0, 0x10000) or /// is not within range [0, 0x10000) or - /// equal to and lower than or equal to . + /// equal to and it is not hidden sequence point and lower than or equal to . /// public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { @@ -254,6 +254,8 @@ public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int /// The column in the line where the sequence point begins. /// The line where the sequence point ends. /// The column in the line where the sequence point ends. + /// is not valid. + /// The parameters validated in the caller: . protected virtual void MarkSequencePointCore(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { } #endregion diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index e3cb6a426c71ba..bdfbea9a506d57 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -297,10 +297,10 @@ Not supported in an array method of a type definition that is not complete. - + Invalid source document. - Emitting symbols is not supported for contaning type. + Emitting symbols is not supported for containing type. \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index b5a7a82a32cf65..b238e56eff3b1b 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -808,7 +808,7 @@ protected override void MarkSequencePointCore(ISymbolDocumentWriter document, in } else { - throw new ArgumentException(SR.InvalidOperation_InValidDocument, nameof(document)); + throw new ArgumentException(SR.InvalidOperation_InvalidDocument, nameof(document)); } } @@ -858,7 +858,7 @@ internal LabelInfo(LabelHandle metaLabel) internal sealed class Scope { - public Scope(int offset, Scope? parent) + internal Scope(int offset, Scope? parent) { _startOffset = offset; _parent = parent; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index a98d96a2f91905..b62c3d7c85dbed 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -408,32 +408,31 @@ private void WriteMethods(List methods, List 0) + if (il.DocumentToSequencePoints.Count == 0) + { + // empty sequence point + _pdbMetadata.AddMethodDebugInformation(default, default); + } + else { Dictionary>.Enumerator enumerator = il.DocumentToSequencePoints.GetEnumerator(); - if (il.DocumentToSequencePoints.Count == 1) // single document + if (il.DocumentToSequencePoints.Count > 1) + { + // sequence points spans multiple docs + _pdbMetadata.AddMethodDebugInformation(default, PopulateMultiDocSequencePointsBlob(enumerator, localSignatureHandle)); + } + else // single document sequence point { enumerator.MoveNext(); - DocumentHandle docHandle = GetDocument(enumerator.Current.Key); BlobBuilder spBlobBuilder = new BlobBuilder(); spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignatureHandle)); PopulateSequencePointsBlob(spBlobBuilder, enumerator.Current.Value); - _pdbMetadata.AddMethodDebugInformation(docHandle, _pdbMetadata.GetOrAddBlob(spBlobBuilder)); + _pdbMetadata.AddMethodDebugInformation(GetDocument(enumerator.Current.Key), _pdbMetadata.GetOrAddBlob(spBlobBuilder)); } - else - { - // sequence points spans multiple docs - _pdbMetadata.AddMethodDebugInformation(default, PopulateMultiDocSequencePointsBlob(enumerator, localSignatureHandle)); - } - } - else - { - // empty sequence point - _pdbMetadata.AddMethodDebugInformation(default, default); } Scope scope = il.Scope; - scope._endOffset = il.ILOffset; + scope._endOffset = il.ILOffset; // Outer most scope covers the entire method body, so haven't closed by the user. AddLocalScope(methodHandle, parentImport: default, MetadataTokens.LocalVariableHandle(_pdbMetadata.GetRowCount(TableIndex.LocalVariable) + 1), scope); } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs index e75096efd7b6c1..6815c5cb41aea5 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs @@ -135,7 +135,7 @@ public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilde return _metadataBuilder; } - private void PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, out MetadataBuilder pdbMetadata) + private void PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, out MetadataBuilder pdbBuilder) { if (_module == null) { @@ -163,7 +163,7 @@ private void PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder ); _module.WriteCustomAttributes(_customAttributes, assemblyHandle); - _module.AppendMetadata(new MethodBodyStreamEncoder(ilStream), fieldData, out pdbMetadata); + _module.AppendMetadata(new MethodBodyStreamEncoder(ilStream), fieldData, out pdbBuilder); _isMetadataPopulated = true; } From b62e716bbf2ec6428a5e4b57ae575abd92c17a99 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 8 Apr 2024 11:28:22 -0700 Subject: [PATCH 5/9] Validate null name for SetLocalSymInfo, do not add locals that has no name to the locals table --- .../System/Reflection/Emit/LocalBuilder.cs | 8 ++- .../Reflection/Emit/ModuleBuilderImpl.cs | 13 ++-- ...ILGeneratorScopesAndSequencePointsTests.cs | 59 ++++++++++++++++++- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs index efb4366a27b002..1a630fbb981209 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs @@ -17,9 +17,15 @@ protected LocalBuilder() { } /// Sets the name of this local variable. /// /// The name of the local variable + /// The is null. /// The containing type has been created with CreateType() or /// containing type doesn't support symbol writing." - public void SetLocalSymInfo(string name) => SetLocalSymInfoCore(name); + public void SetLocalSymInfo(string name) + { + ArgumentNullException.ThrowIfNull(name); + + SetLocalSymInfoCore(name); + } /// /// Sets the name of this local variable. diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index b62c3d7c85dbed..03a284e48b8280 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -526,12 +526,15 @@ private LocalVariableHandle GetLocalVariableHandle(List? locals, L bool firstLocalSet = false; foreach (LocalBuilderImpl local in locals) { - LocalVariableHandle localHandle = _pdbMetadata.AddLocalVariable(LocalVariableAttributes.None, local.LocalIndex, - local.Name == null ? default : _pdbMetadata.GetOrAddString(local.Name)); - if (!firstLocalSet) + if (!string.IsNullOrEmpty(local.Name)) { - firstLocalOfLastScope = localHandle; - firstLocalSet = true; + LocalVariableHandle localHandle = _pdbMetadata.AddLocalVariable(LocalVariableAttributes.None, local.LocalIndex, + local.Name == null ? _pdbMetadata.GetOrAddString(string.Empty) : _pdbMetadata.GetOrAddString(local.Name)); + if (!firstLocalSet) + { + firstLocalOfLastScope = localHandle; + firstLocalSet = true; + } } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs index 36bdb9e0b8c9cc..e0292dcd31fb17 100644 --- a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs @@ -21,12 +21,69 @@ public void SetLocalSymInfo_UsingNamespace_Validations() LocalBuilder local = il1.DeclareLocal(typeof(int)); Assert.Throws("usingNamespace", () => il1.UsingNamespace(null)); Assert.Throws("usingNamespace", () => il1.UsingNamespace(string.Empty)); - local.SetLocalSymInfo(null); // can be null + Assert.Throws("name", () => local.SetLocalSymInfo(null)); il1.Emit(OpCodes.Ret); tb.CreateType(); Assert.Throws(() => local.SetLocalSymInfo("myInt1")); // type created } + [Fact] + public void LocalWithoutSymInfoWillNotAddedToLocalVariablesTable() + { + PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly); + ModuleBuilder mb = ab.DefineDynamicModule("MyModule"); + TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + ISymbolDocumentWriter srcDoc = mb.DefineDocument("MySourceFile.cs", SymLanguageType.CSharp); + MethodBuilder method = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); + ILGenerator il1 = method.GetILGenerator(); + LocalBuilder local = il1.DeclareLocal(typeof(int)); + local.SetLocalSymInfo("myInt1"); + il1.Emit(OpCodes.Ldarg_0); + il1.Emit(OpCodes.Ldarg_1); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Stloc_0); + LocalBuilder local2 = il1.DeclareLocal(typeof(string)); + il1.Emit(OpCodes.Ldstr, "MyAssembly"); + il1.Emit(OpCodes.Stloc, local2); + LocalBuilder local3 = il1.DeclareLocal(typeof(int)); + local3.SetLocalSymInfo("myInt2"); + il1.Emit(OpCodes.Ldc_I4_2); + il1.Emit(OpCodes.Stloc_2); + il1.Emit(OpCodes.Ldloc_2); + il1.Emit(OpCodes.Ldloc_0); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Ret); + tb.CreateType(); + + MetadataBuilder mdb = ab.GenerateMetadata(out BlobBuilder _, out BlobBuilder _, out MetadataBuilder pdbMetadata); + + BlobBuilder portablePdbBlob = new BlobBuilder(); + PortablePdbBuilder pdbBuilder = new PortablePdbBuilder(pdbMetadata, mdb.GetRowCounts(), default); + pdbBuilder.Serialize(portablePdbBlob); + using TempFile pdbFile = TempFile.Create(); + using var pdbFileStream = new FileStream(pdbFile.Path, FileMode.Create, FileAccess.Write); + portablePdbBlob.WriteContentTo(pdbFileStream); + pdbFileStream.Close(); + + using var fs = new FileStream(pdbFile.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using MetadataReaderProvider provider = MetadataReaderProvider.FromPortablePdbStream(fs); + MetadataReader reader = provider.GetMetadataReader(); + MethodDebugInformation mdi = reader.GetMethodDebugInformation(MetadataTokens.MethodDebugInformationHandle(method.MetadataToken)); + SequencePointCollection.Enumerator spcEnumerator = mdi.GetSequencePoints().GetEnumerator(); + Assert.False(spcEnumerator.MoveNext()); + + LocalScopeHandleCollection.Enumerator localScopes = reader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(method.MetadataToken)).GetEnumerator(); + Assert.True(localScopes.MoveNext()); + LocalScope localScope = reader.GetLocalScope(localScopes.Current); + LocalVariableHandleCollection.Enumerator localEnumerator = localScope.GetLocalVariables().GetEnumerator(); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("myInt1", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.True(localEnumerator.MoveNext()); + Assert.Equal("myInt2", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name)); + Assert.False(localEnumerator.MoveNext()); + Assert.False(localScopes.MoveNext()); + } + [Fact] public void LocalsNamespacesWithinNestedScopes() { From 9bbf3346fa949b240603b027b1b6ff8fe43ea4cb Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 10 Apr 2024 16:38:22 -0700 Subject: [PATCH 6/9] Throw on parent virtual methods, remove unnecessary throw --- .../System.Private.CoreLib/src/Resources/Strings.resx | 3 +++ .../src/System/Reflection/Emit/ILGenerator.cs | 3 ++- .../src/System/Reflection/Emit/LocalBuilder.cs | 2 +- .../System.Reflection.Emit/src/Resources/Strings.resx | 3 --- .../src/System/Reflection/Emit/LocalBuilderImpl.cs | 7 +------ 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index bbc4b27c51ddeb..78576b697c9462 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4313,4 +4313,7 @@ Blocking wait is not supported on the JS interop threads. + + Emitting debug info is not supported for this member. + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs index 516c55dbf89682..8eed2f919764e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -256,7 +256,8 @@ public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int /// The column in the line where the sequence point ends. /// is not valid. /// The parameters validated in the caller: . - protected virtual void MarkSequencePointCore(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { } + protected virtual void MarkSequencePointCore(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) => + throw new NotSupportedException(SR.NotSupported_EmitDebugInfo); #endregion diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs index 1a630fbb981209..329aec4cbf4e99 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs @@ -33,6 +33,6 @@ public void SetLocalSymInfo(string name) /// The name of the local variable. /// The containing type has been created with CreateType() or /// containing type doesn't support symbol writing." - protected virtual void SetLocalSymInfoCore(string name) { } + protected virtual void SetLocalSymInfoCore(string name) => throw new NotSupportedException(SR.NotSupported_EmitDebugInfo); } } diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index bdfbea9a506d57..d01856de6b6f00 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -300,7 +300,4 @@ Invalid source document. - - Emitting symbols is not supported for containing type. - \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs index 562711965fac78..406d0812047633 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/LocalBuilderImpl.cs @@ -31,12 +31,7 @@ internal LocalBuilderImpl(int index, Type type, MethodInfo method, bool isPinned #region LocalBuilder Override protected override void SetLocalSymInfoCore(string name) { - if (_method.DeclaringType is not TypeBuilderImpl typeBuilder) - { - throw new InvalidOperationException(SR.InvalidOperation_NotValidTypeBuilder); - } - - if (typeBuilder.IsCreated()) + if (_method.DeclaringType is TypeBuilder typeBuilder && typeBuilder.IsCreated()) { throw new InvalidOperationException(SR.InvalidOperation_TypeHasBeenCreated); } From 59e19b0beea4aa4194c7cba7aa4d9903cc1bb176 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 11 Apr 2024 10:21:04 -0700 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Aaron Robinson --- .../src/System/Reflection/Emit/ILGenerator.cs | 6 +++--- .../src/System/Reflection/Emit/ModuleBuilderImpl.cs | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs index 8eed2f919764e6..bd905728c5eeeb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -227,17 +227,17 @@ public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int throw new ArgumentOutOfRangeException(nameof(startLine)); } - if (endLine < 0 || endLine > 0x20000000 || startLine > endLine) + if (endLine < 0 || endLine >= 0x20000000 || startLine > endLine) { throw new ArgumentOutOfRangeException(nameof(endLine)); } - if (startColumn < 0 || startColumn > 0x10000) + if (startColumn < 0 || startColumn >= 0x10000) { throw new ArgumentOutOfRangeException(nameof(startColumn)); } - if (endColumn < 0 || endColumn > 0x10000 || + if (endColumn < 0 || endColumn >= 0x10000 || (startLine == endLine && startLine != 0xfeefee && startColumn >= endColumn)) { throw new ArgumentOutOfRangeException(nameof(endColumn)); diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 03a284e48b8280..26deacf2dd1bbe 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -576,6 +576,7 @@ private static void SerializeDeltaLinesAndColumns(BlobBuilder spBuilder, Sequenc } else { + Debug.Assert(deltaLines > 0); spBuilder.WriteCompressedSignedInteger(deltaColumns); } } From 1256234661fc7a30add98f59610f3e92a9113000 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 15 Apr 2024 10:44:04 -0700 Subject: [PATCH 8/9] Small updates --- .../src/System/Reflection/Emit/ILGenerator.cs | 8 +- .../System/Reflection/Emit/LocalBuilder.cs | 6 +- .../src/Resources/Strings.resx | 3 + .../System/Reflection/Emit/ILGeneratorImpl.cs | 5 +- .../Reflection/Emit/ModuleBuilderImpl.cs | 42 +++---- .../Emit/PersistedAssemblyBuilder.cs | 4 +- ...ILGeneratorScopesAndSequencePointsTests.cs | 114 +++++++++--------- 7 files changed, 95 insertions(+), 87 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs index 8eed2f919764e6..826fda1e2c5a04 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -218,6 +218,7 @@ public virtual LocalBuilder DeclareLocal(Type localType) /// is not within range [0, 0x10000) or /// equal to and it is not hidden sequence point and lower than or equal to . /// + /// Emitting debug info is not supported." public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { ArgumentNullException.ThrowIfNull(document); @@ -227,17 +228,17 @@ public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int throw new ArgumentOutOfRangeException(nameof(startLine)); } - if (endLine < 0 || endLine > 0x20000000 || startLine > endLine) + if (endLine < 0 || endLine >= 0x20000000 || startLine > endLine) { throw new ArgumentOutOfRangeException(nameof(endLine)); } - if (startColumn < 0 || startColumn > 0x10000) + if (startColumn < 0 || startColumn >= 0x10000) { throw new ArgumentOutOfRangeException(nameof(startColumn)); } - if (endColumn < 0 || endColumn > 0x10000 || + if (endColumn < 0 || endColumn >= 0x10000 || (startLine == endLine && startLine != 0xfeefee && startColumn >= endColumn)) { throw new ArgumentOutOfRangeException(nameof(endColumn)); @@ -255,6 +256,7 @@ public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int /// The line where the sequence point ends. /// The column in the line where the sequence point ends. /// is not valid. + /// Emitting debug info is not supported." /// The parameters validated in the caller: . protected virtual void MarkSequencePointCore(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) => throw new NotSupportedException(SR.NotSupported_EmitDebugInfo); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs index 329aec4cbf4e99..e5e86ab883f615 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs @@ -28,11 +28,11 @@ public void SetLocalSymInfo(string name) } /// - /// Sets the name of this local variable. + /// When overridden in a derived class, sets the name of this local variable. /// /// The name of the local variable. - /// The containing type has been created with CreateType() or - /// containing type doesn't support symbol writing." + /// Emitting debug info is not supported." + /// The containing type has been created with CreateType()." protected virtual void SetLocalSymInfoCore(string name) => throw new NotSupportedException(SR.NotSupported_EmitDebugInfo); } } diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index d01856de6b6f00..4717cb70d2aece 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -300,4 +300,7 @@ Invalid source document. + + Non-matching symbol scope. + \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index b238e56eff3b1b..3979caa37f92e8 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -743,7 +743,10 @@ public override void EndExceptionBlock() public override void EndScope() { - Debug.Assert(_currentScope._parent != null); + if (_currentScope._parent == null) + { + throw new InvalidOperationException(SR.Argument_UnmatchingSymScope); + } _currentScope._endOffset = _il.Offset; _currentScope = _currentScope._parent; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 03a284e48b8280..b36daa8c16128f 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -36,7 +36,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private bool _coreTypesFullyPopulated; private bool _hasGlobalBeenCreated; private Type?[]? _coreTypes; - private MetadataBuilder _pdbMetadata = new(); + private MetadataBuilder _pdbBuilder = new(); private static readonly Type[] s_coreTypes = { typeof(void), typeof(object), typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(string), typeof(nint), typeof(nuint), typeof(TypedReference) }; @@ -111,7 +111,7 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) return null; } - internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder, out MetadataBuilder pdbMetadata) + internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder, out MetadataBuilder pdbBuilder) { // Add module metadata ModuleDefinitionHandle moduleHandle = _metadataBuilder.AddModule( @@ -194,7 +194,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuil AddGenericTypeParametersAndConstraintsCustomAttributes(param._parentHandle, param); } - pdbMetadata = _pdbMetadata; + pdbBuilder = _pdbBuilder; } private void WriteInterfaceImplementations(TypeBuilderImpl typeBuilder, TypeDefinitionHandle typeHandle) @@ -411,7 +411,7 @@ private void AddSymbolInfo(ILGeneratorImpl il, StandaloneSignatureHandle localSi if (il.DocumentToSequencePoints.Count == 0) { // empty sequence point - _pdbMetadata.AddMethodDebugInformation(default, default); + _pdbBuilder.AddMethodDebugInformation(default, default); } else { @@ -419,7 +419,7 @@ private void AddSymbolInfo(ILGeneratorImpl il, StandaloneSignatureHandle localSi if (il.DocumentToSequencePoints.Count > 1) { // sequence points spans multiple docs - _pdbMetadata.AddMethodDebugInformation(default, PopulateMultiDocSequencePointsBlob(enumerator, localSignatureHandle)); + _pdbBuilder.AddMethodDebugInformation(default, PopulateMultiDocSequencePointsBlob(enumerator, localSignatureHandle)); } else // single document sequence point { @@ -427,14 +427,14 @@ private void AddSymbolInfo(ILGeneratorImpl il, StandaloneSignatureHandle localSi BlobBuilder spBlobBuilder = new BlobBuilder(); spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignatureHandle)); PopulateSequencePointsBlob(spBlobBuilder, enumerator.Current.Value); - _pdbMetadata.AddMethodDebugInformation(GetDocument(enumerator.Current.Key), _pdbMetadata.GetOrAddBlob(spBlobBuilder)); + _pdbBuilder.AddMethodDebugInformation(GetDocument(enumerator.Current.Key), _pdbBuilder.GetOrAddBlob(spBlobBuilder)); } } Scope scope = il.Scope; scope._endOffset = il.ILOffset; // Outer most scope covers the entire method body, so haven't closed by the user. - AddLocalScope(methodHandle, parentImport: default, MetadataTokens.LocalVariableHandle(_pdbMetadata.GetRowCount(TableIndex.LocalVariable) + 1), scope); + AddLocalScope(methodHandle, parentImport: default, MetadataTokens.LocalVariableHandle(_pdbBuilder.GetRowCount(TableIndex.LocalVariable) + 1), scope); } private BlobHandle PopulateMultiDocSequencePointsBlob(Dictionary>.Enumerator enumerator, StandaloneSignatureHandle localSignature) @@ -459,7 +459,7 @@ private BlobHandle PopulateMultiDocSequencePointsBlob(Dictionary sequencePoints, int previousNonHiddenStartLine = -1, int previousNonHiddenStartColumn = -1) @@ -507,19 +507,19 @@ private void AddLocalScope(MethodDefinitionHandle methodHandle, ImportScopeHandl { parentImport = GetImportScopeHandle(scope._importNamespaces, parentImport); firstLocalVariableHandle = GetLocalVariableHandle(scope._locals, firstLocalVariableHandle); - _pdbMetadata.AddLocalScope(methodHandle, parentImport, firstLocalVariableHandle, + _pdbBuilder.AddLocalScope(methodHandle, parentImport, firstLocalVariableHandle, constantList: MetadataTokens.LocalConstantHandle(1), scope._startOffset, scope._endOffset - scope._startOffset); if (scope._children != null) { foreach (Scope childScope in scope._children) { - AddLocalScope(methodHandle, parentImport, MetadataTokens.LocalVariableHandle(_pdbMetadata.GetRowCount(TableIndex.LocalVariable) + 1), childScope); + AddLocalScope(methodHandle, parentImport, MetadataTokens.LocalVariableHandle(_pdbBuilder.GetRowCount(TableIndex.LocalVariable) + 1), childScope); } } } - private LocalVariableHandle GetLocalVariableHandle(List? locals, LocalVariableHandle firstLocalOfLastScope) + private LocalVariableHandle GetLocalVariableHandle(List? locals, LocalVariableHandle firstLocalHandleOfLastScope) { if (locals != null) { @@ -528,18 +528,18 @@ private LocalVariableHandle GetLocalVariableHandle(List? locals, L { if (!string.IsNullOrEmpty(local.Name)) { - LocalVariableHandle localHandle = _pdbMetadata.AddLocalVariable(LocalVariableAttributes.None, local.LocalIndex, - local.Name == null ? _pdbMetadata.GetOrAddString(string.Empty) : _pdbMetadata.GetOrAddString(local.Name)); + LocalVariableHandle localHandle = _pdbBuilder.AddLocalVariable(LocalVariableAttributes.None, local.LocalIndex, + local.Name == null ? _pdbBuilder.GetOrAddString(string.Empty) : _pdbBuilder.GetOrAddString(local.Name)); if (!firstLocalSet) { - firstLocalOfLastScope = localHandle; + firstLocalHandleOfLastScope = localHandle; firstLocalSet = true; } } } } - return firstLocalOfLastScope; + return firstLocalHandleOfLastScope; } private ImportScopeHandle GetImportScopeHandle(List? importNamespaces, ImportScopeHandle parent) @@ -554,10 +554,10 @@ private ImportScopeHandle GetImportScopeHandle(List? importNamespaces, I foreach (string importNs in importNamespaces) { importBlob.WriteByte((byte)ImportDefinitionKind.ImportNamespace); - importBlob.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_pdbMetadata.GetOrAddBlobUTF8(importNs))); + importBlob.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_pdbBuilder.GetOrAddBlobUTF8(importNs))); } - return _pdbMetadata.AddImportScope(parent, _pdbMetadata.GetOrAddBlob(importBlob)); + return _pdbBuilder.AddImportScope(parent, _pdbBuilder.GetOrAddBlob(importBlob)); } private static void SerializeDeltaLinesAndColumns(BlobBuilder spBuilder, SequencePoint sequencePoint) @@ -592,11 +592,11 @@ private DocumentHandle GetDocument(SymbolDocumentWriter docWriter) } private DocumentHandle AddDocument(string url, Guid language, Guid hashAlgorithm, byte[]? hash) => - _pdbMetadata.AddDocument( - name: _pdbMetadata.GetOrAddDocumentName(url), - hashAlgorithm: hashAlgorithm == default ? default : _pdbMetadata.GetOrAddGuid(hashAlgorithm), + _pdbBuilder.AddDocument( + name: _pdbBuilder.GetOrAddDocumentName(url), + hashAlgorithm: hashAlgorithm == default ? default : _pdbBuilder.GetOrAddGuid(hashAlgorithm), hash: hash == null ? default : _metadataBuilder.GetOrAddBlob(hash), - language: language == default ? default : _pdbMetadata.GetOrAddGuid(language)); + language: language == default ? default : _pdbBuilder.GetOrAddGuid(language)); private void FillMemberReferences(ILGeneratorImpl il) { diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs index 6815c5cb41aea5..ad127094936ee4 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PersistedAssemblyBuilder.cs @@ -109,7 +109,7 @@ private void SaveInternal(Stream stream) /// Outputs bytes that includes all field RVA data defined in the assembly. /// A that includes all members defined in the Assembly. /// A module not defined for the assembly. - /// The metadata already populated for the assembly before. + /// The metadata already populated for the assembly previously. [CLSCompliant(false)] public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData) { @@ -126,7 +126,7 @@ public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilde /// Outputs that includes PDB metadata. /// A that includes all members defined in the Assembly. /// A module not defined for the assembly. - /// The metadata already populated for the assembly before. + /// The metadata already populated for the assembly previously. [CLSCompliant(false)] public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData, out MetadataBuilder pdbBuilder) { diff --git a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs index e0292dcd31fb17..e0c74dc400d109 100644 --- a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs @@ -17,12 +17,12 @@ public void SetLocalSymInfo_UsingNamespace_Validations() { ModuleBuilder mb = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly2"), typeof(object).Assembly).DefineDynamicModule("MyModule2"); TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - ILGenerator il1 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static).GetILGenerator(); - LocalBuilder local = il1.DeclareLocal(typeof(int)); - Assert.Throws("usingNamespace", () => il1.UsingNamespace(null)); - Assert.Throws("usingNamespace", () => il1.UsingNamespace(string.Empty)); + ILGenerator il = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static).GetILGenerator(); + LocalBuilder local = il.DeclareLocal(typeof(int)); + Assert.Throws("usingNamespace", () => il.UsingNamespace(null)); + Assert.Throws("usingNamespace", () => il.UsingNamespace(string.Empty)); Assert.Throws("name", () => local.SetLocalSymInfo(null)); - il1.Emit(OpCodes.Ret); + il.Emit(OpCodes.Ret); tb.CreateType(); Assert.Throws(() => local.SetLocalSymInfo("myInt1")); // type created } @@ -35,24 +35,24 @@ public void LocalWithoutSymInfoWillNotAddedToLocalVariablesTable() TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); ISymbolDocumentWriter srcDoc = mb.DefineDocument("MySourceFile.cs", SymLanguageType.CSharp); MethodBuilder method = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); - ILGenerator il1 = method.GetILGenerator(); - LocalBuilder local = il1.DeclareLocal(typeof(int)); + ILGenerator il = method.GetILGenerator(); + LocalBuilder local = il.DeclareLocal(typeof(int)); local.SetLocalSymInfo("myInt1"); - il1.Emit(OpCodes.Ldarg_0); - il1.Emit(OpCodes.Ldarg_1); - il1.Emit(OpCodes.Add); - il1.Emit(OpCodes.Stloc_0); - LocalBuilder local2 = il1.DeclareLocal(typeof(string)); - il1.Emit(OpCodes.Ldstr, "MyAssembly"); - il1.Emit(OpCodes.Stloc, local2); - LocalBuilder local3 = il1.DeclareLocal(typeof(int)); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Stloc_0); + LocalBuilder local2 = il.DeclareLocal(typeof(string)); + il.Emit(OpCodes.Ldstr, "MyAssembly"); + il.Emit(OpCodes.Stloc, local2); + LocalBuilder local3 = il.DeclareLocal(typeof(int)); local3.SetLocalSymInfo("myInt2"); - il1.Emit(OpCodes.Ldc_I4_2); - il1.Emit(OpCodes.Stloc_2); - il1.Emit(OpCodes.Ldloc_2); - il1.Emit(OpCodes.Ldloc_0); - il1.Emit(OpCodes.Add); - il1.Emit(OpCodes.Ret); + il.Emit(OpCodes.Ldc_I4_2); + il.Emit(OpCodes.Stloc_2); + il.Emit(OpCodes.Ldloc_2); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Ret); tb.CreateType(); MetadataBuilder mdb = ab.GenerateMetadata(out BlobBuilder _, out BlobBuilder _, out MetadataBuilder pdbMetadata); @@ -92,43 +92,43 @@ public void LocalsNamespacesWithinNestedScopes() TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); ISymbolDocumentWriter srcDoc = mb.DefineDocument("MySourceFile.cs", SymLanguageType.CSharp); MethodBuilder method = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); - ILGenerator il1 = method.GetILGenerator(); - LocalBuilder local = il1.DeclareLocal(typeof(int)); - il1.UsingNamespace("System"); + ILGenerator il = method.GetILGenerator(); + LocalBuilder local = il.DeclareLocal(typeof(int)); + il.UsingNamespace("System"); local.SetLocalSymInfo("myInt1"); - il1.Emit(OpCodes.Ldarg_0); - il1.Emit(OpCodes.Ldarg_1); - il1.Emit(OpCodes.Add); - il1.Emit(OpCodes.Stloc_0); - LocalBuilder local2 = il1.DeclareLocal(typeof(string)); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Stloc_0); + LocalBuilder local2 = il.DeclareLocal(typeof(string)); local2.SetLocalSymInfo("myString"); - il1.Emit(OpCodes.Ldstr, "MyAssembly"); - il1.Emit(OpCodes.Stloc, local2); - il1.BeginScope(); - il1.UsingNamespace("System.Reflection"); - LocalBuilder local3 = il1.DeclareLocal(typeof(AssemblyName)); + il.Emit(OpCodes.Ldstr, "MyAssembly"); + il.Emit(OpCodes.Stloc, local2); + il.BeginScope(); + il.UsingNamespace("System.Reflection"); + LocalBuilder local3 = il.DeclareLocal(typeof(AssemblyName)); local3.SetLocalSymInfo("myAssembly"); - il1.Emit(OpCodes.Ldloc_1); - il1.Emit(OpCodes.Newobj, typeof(AssemblyName).GetConstructor([typeof(string)])); - il1.Emit(OpCodes.Stloc_2); - il1.BeginScope(); - LocalBuilder local4 = il1.DeclareLocal(typeof(int)); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Newobj, typeof(AssemblyName).GetConstructor([typeof(string)])); + il.Emit(OpCodes.Stloc_2); + il.BeginScope(); + LocalBuilder local4 = il.DeclareLocal(typeof(int)); local4.SetLocalSymInfo("myInt2"); - LocalBuilder local5 = il1.DeclareLocal(typeof(int)); + LocalBuilder local5 = il.DeclareLocal(typeof(int)); local5.SetLocalSymInfo("myInt3"); - il1.Emit(OpCodes.Ldc_I4_2); - il1.Emit(OpCodes.Stloc_3); - il1.Emit(OpCodes.Ldc_I4_5); - il1.Emit(OpCodes.Stloc_S, 4); - il1.Emit(OpCodes.Ldloc_S, 4); - il1.Emit(OpCodes.Ldloc_3); - il1.Emit(OpCodes.Add); - il1.Emit(OpCodes.Stloc_0); - il1.EndScope(); - il1.UsingNamespace("System.Reflection.Emit"); - il1.EndScope(); - il1.Emit(OpCodes.Ldloc_0); - il1.Emit(OpCodes.Ret); + il.Emit(OpCodes.Ldc_I4_2); + il.Emit(OpCodes.Stloc_3); + il.Emit(OpCodes.Ldc_I4_5); + il.Emit(OpCodes.Stloc_S, 4); + il.Emit(OpCodes.Ldloc_S, 4); + il.Emit(OpCodes.Ldloc_3); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Stloc_0); + il.EndScope(); + il.UsingNamespace("System.Reflection.Emit"); + il.EndScope(); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ret); tb.CreateType(); MetadataBuilder mdb = ab.GenerateMetadata(out BlobBuilder _, out BlobBuilder _, out MetadataBuilder pdbMetadata); @@ -227,10 +227,10 @@ public void DefineDocument_MarkSequencePoint_Validations() Assert.Throws("startColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, -1, 1, 1)); Assert.Throws("endLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, -1, 1)); Assert.Throws("endColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 1, -1)); - Assert.Throws("startLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 0x20000001, 1, 1, 1)); - Assert.Throws("startColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 0x10001, 1, 1)); - Assert.Throws("endLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 0x20000001, 1)); - Assert.Throws("endColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 1, 0x10001)); + Assert.Throws("startLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 0x20000000, 1, 1, 1)); + Assert.Throws("startColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 0x10000, 1, 1)); + Assert.Throws("endLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 0x20000000, 1)); + Assert.Throws("endColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 1, 0x10000)); Assert.Throws("endColumn", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 1, 1)); Assert.Throws("endLine", () => il.MarkSequencePoint(mb.DefineDocument("MySourceFile.cs"), 1, 1, 0, 1)); } From 0555573afeb6e3e95c48dc6012d320665c881f32 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 15 Apr 2024 16:18:48 -0700 Subject: [PATCH 9/9] Apply feedback, fix issue found in sequence point PreviousNonHidden.StatrtLine/StartColumn --- .../src/Resources/Strings.resx | 4 ++-- .../System/Reflection/Emit/ILGeneratorImpl.cs | 2 +- .../Reflection/Emit/ModuleBuilderImpl.cs | 21 ++++++++++-------- ...ILGeneratorScopesAndSequencePointsTests.cs | 22 +++++++++++++++++-- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index 4717cb70d2aece..6103dbef1f159d 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -300,7 +300,7 @@ Invalid source document. - - Non-matching symbol scope. + + Unmatching symbol scope. \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index 3979caa37f92e8..d4b40387e30e20 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -745,7 +745,7 @@ public override void EndScope() { if (_currentScope._parent == null) { - throw new InvalidOperationException(SR.Argument_UnmatchingSymScope); + throw new InvalidOperationException(SR.InvalidOperation_UnmatchingSymScope); } _currentScope._endOffset = _il.Offset; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 98405bc0f5598a..b78526adda13cd 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -423,10 +423,12 @@ private void AddSymbolInfo(ILGeneratorImpl il, StandaloneSignatureHandle localSi } else // single document sequence point { + int previousNonHiddenStartLine = -1; + int previousNonHiddenStartColumn = -1; enumerator.MoveNext(); BlobBuilder spBlobBuilder = new BlobBuilder(); spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignatureHandle)); - PopulateSequencePointsBlob(spBlobBuilder, enumerator.Current.Value); + PopulateSequencePointsBlob(spBlobBuilder, enumerator.Current.Value, ref previousNonHiddenStartLine, ref previousNonHiddenStartColumn); _pdbBuilder.AddMethodDebugInformation(GetDocument(enumerator.Current.Key), _pdbBuilder.GetOrAddBlob(spBlobBuilder)); } } @@ -440,29 +442,30 @@ private void AddSymbolInfo(ILGeneratorImpl il, StandaloneSignatureHandle localSi private BlobHandle PopulateMultiDocSequencePointsBlob(Dictionary>.Enumerator enumerator, StandaloneSignatureHandle localSignature) { BlobBuilder spBlobBuilder = new BlobBuilder(); - // header: - spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignature)); + int previousNonHiddenStartLine = -1; + int previousNonHiddenStartColumn = -1; enumerator.MoveNext(); KeyValuePair> pair = enumerator.Current; + + // header: + spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(localSignature)); spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(GetDocument(pair.Key))); + // First sequence point record - PopulateSequencePointsBlob(spBlobBuilder, pair.Value); - int i = 0; + PopulateSequencePointsBlob(spBlobBuilder, pair.Value, ref previousNonHiddenStartLine, ref previousNonHiddenStartColumn); while (enumerator.MoveNext()) { - int previousNonHiddenStartLine = pair.Value[i].StartLine; - int previousNonHiddenStartColumn = pair.Value[i].StartColumn; pair = enumerator.Current; spBlobBuilder.WriteCompressedInteger(0); spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(GetDocument(pair.Key))); - PopulateSequencePointsBlob(spBlobBuilder, pair.Value, previousNonHiddenStartLine, previousNonHiddenStartColumn); + PopulateSequencePointsBlob(spBlobBuilder, pair.Value, ref previousNonHiddenStartLine, ref previousNonHiddenStartColumn); } return _pdbBuilder.GetOrAddBlob(spBlobBuilder); } - private static void PopulateSequencePointsBlob(BlobBuilder spBlobBuilder, List sequencePoints, int previousNonHiddenStartLine = -1, int previousNonHiddenStartColumn = -1) + private static void PopulateSequencePointsBlob(BlobBuilder spBlobBuilder, List sequencePoints, ref int previousNonHiddenStartLine, ref int previousNonHiddenStartColumn) { for (int i = 0; i < sequencePoints.Count; i++) { diff --git a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs index e0c74dc400d109..abdbf5e4047b58 100644 --- a/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs @@ -243,6 +243,7 @@ public void MultipleDocumentsAndSequencePoints() TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); ISymbolDocumentWriter srcDoc1 = mb.DefineDocument("MySource1.cs", SymLanguageType.CSharp); ISymbolDocumentWriter srcDoc2 = mb.DefineDocument("MySource2.cs", SymLanguageType.CSharp); + ISymbolDocumentWriter srcDoc3 = mb.DefineDocument("MySource3.cs", SymLanguageType.CSharp); MethodBuilder method = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static); ILGenerator il1 = method.GetILGenerator(); LocalBuilder local = il1.DeclareLocal(typeof(int)); @@ -262,6 +263,11 @@ public void MultipleDocumentsAndSequencePoints() il1.Emit(OpCodes.Stloc_0); il1.MarkSequencePoint(srcDoc1, 11, 1, 11, 20); il1.Emit(OpCodes.Ldloc_0); + il1.Emit(OpCodes.Ldloc_1); + il1.Emit(OpCodes.Add); + il1.Emit(OpCodes.Stloc_0); + il1.MarkSequencePoint(srcDoc3, 5, 0, 5, 20); + il1.Emit(OpCodes.Ldloc_0); il1.Emit(OpCodes.Ret); MethodBuilder entryPoint = tb.DefineMethod("Main", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static); @@ -287,7 +293,7 @@ public void MultipleDocumentsAndSequencePoints() using MetadataReaderProvider provider = MetadataReaderProvider.FromPortablePdbStream(fs); MetadataReader reader = provider.GetMetadataReader(); DocumentHandleCollection.Enumerator docEnumerator = reader.Documents.GetEnumerator(); - Assert.Equal(2, reader.Documents.Count); + Assert.Equal(3, reader.Documents.Count); Assert.True(docEnumerator.MoveNext()); Document doc1 = reader.GetDocument(docEnumerator.Current); Assert.Equal("MySource2.cs", reader.GetString(doc1.Name)); @@ -296,6 +302,10 @@ public void MultipleDocumentsAndSequencePoints() Document doc2 = reader.GetDocument(docEnumerator.Current); Assert.Equal("MySource1.cs", reader.GetString(doc2.Name)); Assert.Equal(SymLanguageType.CSharp, reader.GetGuid(doc2.Language)); + Assert.True(docEnumerator.MoveNext()); + Document doc3 = reader.GetDocument(docEnumerator.Current); + Assert.Equal("MySource3.cs", reader.GetString(doc3.Name)); + Assert.Equal(SymLanguageType.CSharp, reader.GetGuid(doc3.Language)); Assert.False(docEnumerator.MoveNext()); MethodDebugInformation mdi1 = reader.GetMethodDebugInformation(MetadataTokens.MethodDebugInformationHandle(method.MetadataToken)); @@ -331,13 +341,21 @@ public void MultipleDocumentsAndSequencePoints() Assert.Equal(1, sp.StartColumn); Assert.Equal(11, sp.EndLine); Assert.Equal(20, sp.EndColumn); + Assert.True(spcEnumerator.MoveNext()); + sp = spcEnumerator.Current; + Assert.Equal(doc3, reader.GetDocument(sp.Document)); + Assert.Equal(24, sp.Offset); + Assert.Equal(5, sp.StartLine); + Assert.Equal(0, sp.StartColumn); + Assert.Equal(5, sp.EndLine); + Assert.Equal(20, sp.EndColumn); Assert.False(spcEnumerator.MoveNext()); LocalScopeHandleCollection.Enumerator localScopes = reader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(method.MetadataToken)).GetEnumerator(); Assert.True(localScopes.MoveNext()); LocalScope locals = reader.GetLocalScope(localScopes.Current); Assert.Equal(0, locals.StartOffset); - Assert.Equal(12, locals.EndOffset); + Assert.Equal(16, locals.EndOffset); LocalVariableHandleCollection.Enumerator localEnumerator = locals.GetLocalVariables().GetEnumerator(); Assert.True(localEnumerator.MoveNext()); Assert.Equal("MyInt", reader.GetString(reader.GetLocalVariable(localEnumerator.Current).Name));