New APIs:
public abstract partial class ModuleBuilder : System.Reflection.Module
{
+ public virtual ISymbolDocumentWriter DefineDocument(string url, Guid language = default) { }
}
public abstract class ILGenerator
{
public abstract void BeginScope();
public abstract void EndScope();
+ public virtual void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { }
public abstract void UsingNamespace(string usingNamespace);
}
public abstract class LocalBuilder : LocalVariableInfo
{
+ public virtual void SetLocalSymInfo(string name) { }
}
These new APIs will be used for populating PDB tables when populating metadata with MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData)
. This is a new API we added recently in PersistedAssemblyBuilder
.
public sealed class PersistedAssemblyBuilder : AssemblyBuilder
{
public PersistedAssemblyBuilder(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes = null);
public void Save(Stream stream);
public void Save(string assemblyFileName);
public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData)
}
API usage:
static void Test ()
{
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly);
ModuleBuilder mb = ab.DefineDynamicModule("MyModule");
TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder mb1 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]);
ISymbolDocumentWriter srcDoc = mb.DefineDocument("MySourceFile.cs", SymLanguageType.CSharp);
ILGenerator il = mb1.GetILGenerator();
il.MarkSequencePoint(srcDoc, 7, 0, 7, 11);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.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.MarkSequencePoint(srcDoc, 12, 0, 12, 38);
il2.Emit(OpCodes.Call, mb1);
il2.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", [typeof(int)])!);
il2.Emit(OpCodes.Ret);
tb.CreateType();
MetadataBuilder metadataBuilder = ab.GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, addPdb: true);
MetadataRootBuilder mdRootBuilder = new MetadataRootBuilder(metadataBuilder);
MethodDefinitionHandle entryPointHandle = MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken);
PEHeaderBuilder peHeaderBuilder = new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage, subsystem: Subsystem.WindowsCui);
var portablePdbBlob = new BlobBuilder();
PortablePdbBuilder pdbBuilder = new PortablePdbBuilder(metadataBuilder, mdRootBuilder.Sizes.RowCounts, entryPointHandle);
BlobContentId pdbContentId = pdbBuilder.Serialize(portablePdbBlob);
// In case saving PDB to a file
using var pdbFileStream = new FileStream("MyAssembly.pdb", FileMode.Create, FileAccess.Write);
portablePdbBlob.WriteContentTo(pdbFileStream);
DebugDirectoryBuilder debugDirectoryBuilder = new DebugDirectoryBuilder();
// standalone PDB
debugDirectoryBuilder.AddCodeViewEntry("MyAssembly.pdb", pdbContentId, pdbBuilder.FormatVersion);
// in case embedded PDB
// debugDirectoryBuilder.AddEmbeddedPortablePdbEntry(portablePdbBlob, pdbBuilder.FormatVersion);
ManagedPEBuilder peBuilder = new ManagedPEBuilder(
header: peHeaderBuilder,
metadataRootBuilder: mdRootBuilder,
ilStream: ilStream,
mappedFieldData: fieldData,
debugDirectoryBuilder: debugDirectoryBuilder,
entryPoint: entryPointHandle);
BlobBuilder peBlob = new BlobBuilder();
peBuilder.Serialize(peBlob);
using var fileStream = new FileStream("MyAssembly.exe", FileMode.Create, FileAccess.Write);
peBlob.WriteContentTo(fileStream);
Below are the things that will not be included, at least with 1st version:
- .NET framework have no API for adding local constant - so will not added.
- No support for CustomDebugInformation in .NET framework - will not be added
- dotnet framework do not support source embedding and source indexing - will not be supported.
- In .Net framework to generated PDB used module creation
public ModuleBuilder DefineDynamicModule (string name, bool emitSymbolInfo);
andpublic ModuleBuilder DefineDynamicModule (string name, string fileName, bool emitSymbolInfo);
, for we will have option, can be added later (debug info will be populated in metadata if there any added). ModuleBuilder.GetSymWriter()
Method that returnsISymbolWriter
is for calling native code to add debug info, we are not using native APIs - so it will not be added.- Validation difference in sequence points: portable PDB:
If Start Line is equal to End Line then End Column is greater than Start Column.
this was not validated in .NET framework. Now in Core it will throw.