diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index 2bc7699526df53..c4504f140527a3 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -4316,4 +4316,7 @@
Blocking wait is not supported on the JS interop threads.
+
+ Emitting debug info is not supported for this member.
+
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..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
@@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
+using System.Diagnostics.SymbolStore;
namespace System.Reflection.Emit
{
@@ -200,6 +201,66 @@ public virtual LocalBuilder DeclareLocal(Type localType)
[CLSCompliant(false)]
public void Emit(OpCode opcode, sbyte arg) => Emit(opcode, (byte)arg);
+ ///
+ /// 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) 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 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);
+
+ 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.
+ /// 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);
+
#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..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
@@ -12,5 +12,27 @@ public abstract class LocalBuilder : LocalVariableInfo
/// This constructor is invoked by derived classes.
///
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)
+ {
+ ArgumentNullException.ThrowIfNull(name);
+
+ SetLocalSymInfoCore(name);
+ }
+
+ ///
+ /// When overridden in a derived class, sets the name of this local variable.
+ ///
+ /// The name of the local variable.
+ /// 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.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs
index 21f68439ee3927..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,7 +1,9 @@
// 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;
namespace System.Reflection.Emit
@@ -24,6 +26,45 @@ public EnumBuilder DefineEnum(string name, TypeAttributes visibility, Type under
return DefineEnumCore(name, visibility, underlyingType);
}
+ ///
+ /// 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);
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..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,6 +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 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);
}
@@ -73,6 +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 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.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..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,6 +398,10 @@ protected ModuleBuilder() { }
public override string ScopeName { get { throw null; } }
public void CreateGlobalFunctions() { }
protected abstract void CreateGlobalFunctionsCore();
+ [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; }
@@ -479,9 +483,11 @@ 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; }
+ [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; }
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/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx
index 4ab50a83684c01..6103dbef1f159d 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.
+
+
+ Unmatching symbol scope.
+
\ 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..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
@@ -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;
@@ -17,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
@@ -24,10 +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> _documentToSequencePoints = new();
internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size)
{
@@ -37,13 +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 List Locals => _scope.GetAllLocals();
+ internal int LocalCount => _localCount;
+ internal Scope Scope => _scope;
+ internal Dictionary> DocumentToSequencePoints => _documentToSequencePoints;
public override int ILOffset => _il.Offset;
@@ -198,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;
}
@@ -731,7 +743,13 @@ public override void EndExceptionBlock()
public override void EndScope()
{
- // TODO: No-op, will be implemented wit PDB support
+ if (_currentScope._parent == null)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_UnmatchingSymScope);
+ }
+
+ _currentScope._endOffset = _il.Offset;
+ _currentScope = _currentScope._parent;
}
public override void MarkLabel(Label loc)
@@ -777,9 +795,32 @@ public override void MarkLabel(Label loc)
}
}
+ protected override void MarkSequencePointCore(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn)
+ {
+ if (document is SymbolDocumentWriter symbolDoc)
+ {
+ 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) };
+ _documentToSequencePoints.Add(symbolDoc, sequencePoints);
+ }
+ }
+ else
+ {
+ 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);
}
}
@@ -817,4 +858,40 @@ internal LabelInfo(LabelHandle metaLabel)
internal int _startDepth; // Stack labelStartDepth, with -1 meaning unknown.
internal LabelHandle _metaLabel;
}
+
+ internal sealed class Scope
+ {
+ internal 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 fa9bffbec4345c..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
@@ -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,19 @@ internal LocalBuilderImpl(int index, Type type, MethodInfo method, bool isPinned
#region Internal Members
internal MethodInfo GetMethodBuilder() => _method;
+ internal string? Name => _name;
+ #endregion
+
+ #region LocalBuilder Override
+ protected override void SetLocalSymInfoCore(string name)
+ {
+ if (_method.DeclaringType is TypeBuilder typeBuilder && 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 0349186b717fef..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
@@ -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;
@@ -34,6 +36,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder
private bool _coreTypesFullyPopulated;
private bool _hasGlobalBeenCreated;
private Type?[]? _coreTypes;
+ 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) };
@@ -108,7 +111,7 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId)
return null;
}
- internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder)
+ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder, out MetadataBuilder pdbBuilder)
{
// Add module metadata
ModuleDefinitionHandle moduleHandle = _metadataBuilder.AddModule(
@@ -190,6 +193,8 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuil
{
AddGenericTypeParametersAndConstraintsCustomAttributes(param._parentHandle, param);
}
+
+ pdbBuilder = _pdbBuilder;
}
private void WriteInterfaceImplementations(TypeBuilderImpl typeBuilder, TypeDefinitionHandle typeHandle)
@@ -339,7 +344,8 @@ private void PopulateTokensForTypesAndItsMembers()
}
}
- private void WriteMethods(List methods, List genericParams, MethodBodyStreamEncoder methodBodyEncoder)
+ private void WriteMethods(List methods, List genericParams,
+ MethodBodyStreamEncoder methodBodyEncoder)
{
foreach (MethodBuilderImpl method in methods)
{
@@ -348,9 +354,10 @@ private void WriteMethods(List methods, List methods, List>.Enumerator enumerator = il.DocumentToSequencePoints.GetEnumerator();
+ if (il.DocumentToSequencePoints.Count > 1)
+ {
+ // sequence points spans multiple docs
+ _pdbBuilder.AddMethodDebugInformation(default, PopulateMultiDocSequencePointsBlob(enumerator, localSignatureHandle));
+ }
+ 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, ref previousNonHiddenStartLine, ref previousNonHiddenStartColumn);
+ _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(_pdbBuilder.GetRowCount(TableIndex.LocalVariable) + 1), scope);
+ }
+
+ private BlobHandle PopulateMultiDocSequencePointsBlob(Dictionary>.Enumerator enumerator, StandaloneSignatureHandle localSignature)
+ {
+ BlobBuilder spBlobBuilder = new BlobBuilder();
+ 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, ref previousNonHiddenStartLine, ref previousNonHiddenStartColumn);
+
+ while (enumerator.MoveNext())
+ {
+ pair = enumerator.Current;
+ spBlobBuilder.WriteCompressedInteger(0);
+ spBlobBuilder.WriteCompressedInteger(MetadataTokens.GetRowNumber(GetDocument(pair.Key)));
+ PopulateSequencePointsBlob(spBlobBuilder, pair.Value, ref previousNonHiddenStartLine, ref previousNonHiddenStartColumn);
+ }
+
+ return _pdbBuilder.GetOrAddBlob(spBlobBuilder);
+ }
+
+ private static void PopulateSequencePointsBlob(BlobBuilder spBlobBuilder, List sequencePoints, ref int previousNonHiddenStartLine, ref int previousNonHiddenStartColumn)
+ {
+ 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);
+ 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 void AddLocalScope(MethodDefinitionHandle methodHandle, ImportScopeHandle parentImport, LocalVariableHandle firstLocalVariableHandle, Scope scope)
+ {
+ parentImport = GetImportScopeHandle(scope._importNamespaces, parentImport);
+ firstLocalVariableHandle = GetLocalVariableHandle(scope._locals, 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(_pdbBuilder.GetRowCount(TableIndex.LocalVariable) + 1), childScope);
+ }
+ }
+ }
+
+ private LocalVariableHandle GetLocalVariableHandle(List? locals, LocalVariableHandle firstLocalHandleOfLastScope)
+ {
+ if (locals != null)
+ {
+ bool firstLocalSet = false;
+ foreach (LocalBuilderImpl local in locals)
+ {
+ if (!string.IsNullOrEmpty(local.Name))
+ {
+ LocalVariableHandle localHandle = _pdbBuilder.AddLocalVariable(LocalVariableAttributes.None, local.LocalIndex,
+ local.Name == null ? _pdbBuilder.GetOrAddString(string.Empty) : _pdbBuilder.GetOrAddString(local.Name));
+ if (!firstLocalSet)
+ {
+ firstLocalHandleOfLastScope = localHandle;
+ firstLocalSet = true;
+ }
+ }
+ }
+ }
+
+ return firstLocalHandleOfLastScope;
+ }
+
+ 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(_pdbBuilder.GetOrAddBlobUTF8(importNs)));
+ }
+
+ return _pdbBuilder.AddImportScope(parent, _pdbBuilder.GetOrAddBlob(importBlob));
+ }
+
+ 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
+ {
+ Debug.Assert(deltaLines > 0);
+ 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) =>
+ _pdbBuilder.AddDocument(
+ name: _pdbBuilder.GetOrAddDocumentName(url),
+ hashAlgorithm: hashAlgorithm == default ? default : _pdbBuilder.GetOrAddGuid(hashAlgorithm),
+ hash: hash == null ? default : _metadataBuilder.GetOrAddBlob(hash),
+ language: language == default ? default : _pdbBuilder.GetOrAddGuid(language));
+
private void FillMemberReferences(ILGeneratorImpl il)
{
foreach (KeyValuePair pair in il.GetMemberReferences())
@@ -892,8 +1095,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)
{
@@ -1158,5 +1360,10 @@ private static SignatureCallingConvention GetSignatureConvention(CallingConventi
CallingConvention.FastCall => SignatureCallingConvention.FastCall,
_ => SignatureCallingConvention.Default,
};
+
+ protected override ISymbolDocumentWriter DefineDocumentCore(string url, Guid language = default)
+ {
+ 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..a07b44620fa85f
--- /dev/null
+++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/Pdb/SymbolDocumentWriter.cs
@@ -0,0 +1,39 @@
+// 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;
+ 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)
+ {
+ _language = language;
+ _url = url;
+ }
+
+ public void SetCheckSum(Guid algorithmId, byte[] checkSum)
+ {
+ _hashAlgorithm = algorithmId;
+ _hash = checkSum;
+ }
+
+ 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 12fbf4704b7bac..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
@@ -98,11 +98,10 @@ 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);
}
-
///
/// Generates the metadata for the .
///
@@ -110,16 +109,33 @@ 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)
{
- PopulateAssemblyMetadata(out ilStream, out mappedFieldData);
+ PopulateAssemblyMetadata(out ilStream, out mappedFieldData, out _);
+
+ return _metadataBuilder;
+ }
+
+ ///
+ /// 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 previously.
+ [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)
+ private void PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, out MetadataBuilder pdbBuilder)
{
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 pdbBuilder);
_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..abdbf5e4047b58
--- /dev/null
+++ b/src/libraries/System.Reflection.Emit/tests/PortablePdb/ILGeneratorScopesAndSequencePointsTests.cs
@@ -0,0 +1,372 @@
+// 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 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));
+ il.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 il = method.GetILGenerator();
+ LocalBuilder local = il.DeclareLocal(typeof(int));
+ local.SetLocalSymInfo("myInt1");
+ 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");
+ 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);
+
+ 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()
+ {
+ 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 il = method.GetILGenerator();
+ LocalBuilder local = il.DeclareLocal(typeof(int));
+ il.UsingNamespace("System");
+ local.SetLocalSymInfo("myInt1");
+ 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");
+ 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");
+ 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 = il.DeclareLocal(typeof(int));
+ local5.SetLocalSymInfo("myInt3");
+ 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);
+
+ 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"), 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));
+ }
+
+ [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);
+ 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));
+ 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.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.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);
+ 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(3, 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.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));
+ 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(4, sp.Offset);
+ Assert.Equal(8, sp.StartLine);
+ Assert.Equal(0, sp.StartColumn);
+ 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.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(16, 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 @@
+
+