Skip to content

Commit

Permalink
Preserve custom debug information on types (#948)
Browse files Browse the repository at this point in the history
* Preserve custom debug info on types

* Use ICustomDebugInformationProvider in ISymbolReader/ISymbolWriter

* Move get logic to Mixin helpers

* Add test dll/pdb

* PR feedback

- Use static lambdas
- Check HasCustomDebugInformation
- Test writing modified CustomDebugInformation
- Formatting fixes

* Update Test/Mono.Cecil.Tests/PortablePdbTests.cs

Co-authored-by: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>

* Build for netstandard2.0

---------

Co-authored-by: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
  • Loading branch information
sbomer and jtschuster authored Sep 25, 2024
1 parent 50292e7 commit a0f61f9
Show file tree
Hide file tree
Showing 15 changed files with 200 additions and 51 deletions.
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\cecil.snk</AssemblyOriginatorKeyFile>
<DefineConstants Condition=" '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'net8.0' ">$(DefineConstants);NET_CORE</DefineConstants>
<RootNamespace></RootNamespace>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net40' ">
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
Expand Down
21 changes: 21 additions & 0 deletions Mono.Cecil.Cil/PortablePdb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using Mono.Collections.Generic;
using Mono.Cecil.Metadata;
using Mono.Cecil.PE;

Expand Down Expand Up @@ -145,6 +146,11 @@ void ReadStateMachineKickOffMethod (MethodDebugInformation method_info)
method_info.kickoff_method = debug_reader.ReadStateMachineKickoffMethod (method_info.method);
}

public Collection<CustomDebugInformation> Read (ICustomDebugInformationProvider provider)
{
return debug_reader.GetCustomDebugInformation (provider);
}

void ReadCustomDebugInformations (MethodDebugInformation info)
{
info.method.custom_infos = debug_reader.GetCustomDebugInformation (info.method);
Expand Down Expand Up @@ -221,6 +227,11 @@ public MethodDebugInformation Read (MethodDefinition method)
return reader.Read (method);
}

public Collection<CustomDebugInformation> Read (ICustomDebugInformationProvider provider)
{
return reader.Read (provider);
}

public void Dispose ()
{
reader.Dispose ();
Expand Down Expand Up @@ -319,6 +330,11 @@ public void Write ()
}
}

public void Write (ICustomDebugInformationProvider provider)
{
pdb_metadata.AddCustomDebugInformations (provider);
}

public ImageDebugHeader GetDebugHeader ()
{
if (IsEmbedded)
Expand Down Expand Up @@ -519,6 +535,11 @@ public void Write (MethodDebugInformation info)
writer.Write (info);
}

public void Write (ICustomDebugInformationProvider provider)
{
writer.Write (provider);
}

public ImageDebugHeader GetDebugHeader ()
{
ImageDebugHeader pdbDebugHeader = writer.GetDebugHeader ();
Expand Down
37 changes: 37 additions & 0 deletions Mono.Cecil.Cil/Symbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,7 @@ public interface ISymbolReader : IDisposable {
ISymbolWriterProvider GetWriterProvider ();
bool ProcessDebugHeader (ImageDebugHeader header);
MethodDebugInformation Read (MethodDefinition method);
Collection<CustomDebugInformation> Read (ICustomDebugInformationProvider provider);
}

public interface ISymbolReaderProvider {
Expand Down Expand Up @@ -1116,6 +1117,7 @@ public interface ISymbolWriter : IDisposable {
ImageDebugHeader GetDebugHeader ();
void Write (MethodDebugInformation info);
void Write ();
void Write (ICustomDebugInformationProvider provider);
}

public interface ISymbolWriterProvider {
Expand Down Expand Up @@ -1224,5 +1226,40 @@ public static bool IsPortablePdb (Stream stream)
stream.Position = position;
}
}

public static bool GetHasCustomDebugInformations (
this ICustomDebugInformationProvider self,
ref Collection<CustomDebugInformation> collection,
ModuleDefinition module)
{
if (module.HasImage ()) {
module.Read (ref collection, self, static (provider, reader) => {
var symbol_reader = reader.module.symbol_reader;
if (symbol_reader != null)
return symbol_reader.Read (provider);
return null;
});
}

return !collection.IsNullOrEmpty ();
}

public static Collection<CustomDebugInformation> GetCustomDebugInformations (
this ICustomDebugInformationProvider self,
ref Collection<CustomDebugInformation> collection,
ModuleDefinition module)
{
if (module.HasImage ()) {
module.Read (ref collection, self, static (provider, reader) => {
var symbol_reader = reader.module.symbol_reader;
if (symbol_reader != null)
return symbol_reader.Read (provider);
return null;
});
}

Interlocked.CompareExchange (ref collection, new Collection<CustomDebugInformation> (), null);
return collection;
}
}
}
12 changes: 12 additions & 0 deletions Mono.Cecil/AssemblyReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ void ReadTypesSymbols (Collection<TypeDefinition> types, ISymbolReader symbol_re
{
for (int i = 0; i < types.Count; i++) {
var type = types [i];
type.custom_infos = symbol_reader.Read (type);

if (type.HasNestedTypes)
ReadTypesSymbols (type.NestedTypes, symbol_reader);
Expand Down Expand Up @@ -3160,6 +3161,17 @@ void InitializeCustomDebugInformations ()
}
}

public bool HasCustomDebugInformation (ICustomDebugInformationProvider provider)
{
InitializeCustomDebugInformations ();

Row<Guid, uint, uint> [] rows;
if (!metadata.CustomDebugInformations.TryGetValue (provider.MetadataToken, out rows))
return false;

return rows.Length > 0;
}

public Collection<CustomDebugInformation> GetCustomDebugInformation (ICustomDebugInformationProvider provider)
{
InitializeCustomDebugInformations ();
Expand Down
3 changes: 3 additions & 0 deletions Mono.Cecil/AssemblyWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,9 @@ void AddType (TypeDefinition type)
if (type.HasNestedTypes)
AddNestedTypes (type);

if (symbol_writer != null && type.HasCustomDebugInformations)
symbol_writer.Write (type);

WindowsRuntimeProjections.ApplyProjection (type, treatment);
}

Expand Down
18 changes: 17 additions & 1 deletion Mono.Cecil/TypeDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

using System;
using System.Threading;
using Mono.Cecil.Cil;
using Mono.Cecil.Metadata;
using Mono.Collections.Generic;

namespace Mono.Cecil {

public sealed class TypeDefinition : TypeReference, IMemberDefinition, ISecurityDeclarationProvider {
public sealed class TypeDefinition : TypeReference, IMemberDefinition, ISecurityDeclarationProvider, ICustomDebugInformationProvider {

uint attributes;
TypeReference base_type;
Expand All @@ -34,6 +35,8 @@ public sealed class TypeDefinition : TypeReference, IMemberDefinition, ISecurity
Collection<CustomAttribute> custom_attributes;
Collection<SecurityDeclaration> security_declarations;

internal Collection<CustomDebugInformation> custom_infos;

public TypeAttributes Attributes {
get { return (TypeAttributes) attributes; }
set {
Expand Down Expand Up @@ -284,6 +287,19 @@ public override Collection<GenericParameter> GenericParameters {
get { return generic_parameters ?? (this.GetGenericParameters (ref generic_parameters, Module)); }
}

public bool HasCustomDebugInformations {
get {
if (custom_infos != null)
return custom_infos.Count > 0;

return this.GetHasCustomDebugInformations (ref custom_infos, Module);
}
}

public Collection<CustomDebugInformation> CustomDebugInformations {
get { return custom_infos ?? (this.GetCustomDebugInformations (ref custom_infos, module)); }
}

#region TypeAttributes

public bool IsNotPublic {
Expand Down
49 changes: 49 additions & 0 deletions Test/Mono.Cecil.Tests/BaseTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.IO;
using System.Runtime.CompilerServices;
using Mono.Cecil.Cil;
using Mono.Cecil.Pdb;
using NUnit.Framework;

using Mono.Cecil.PE;
Expand Down Expand Up @@ -154,6 +155,54 @@ static void Run (TestCase testCase)
using (var runner = new TestRunner (testCase, TestCaseType.WriteFromImmediate))
runner.RunTest ();
}

public enum RoundtripType {
None,
Pdb,
PortablePdb
}

protected static ModuleDefinition RoundtripModule(ModuleDefinition module, RoundtripType roundtripType)
{
if (roundtripType == RoundtripType.None)
return module;

var file = Path.Combine (Path.GetTempPath (), "RoundtripModule.dll");
if (File.Exists (file))
File.Delete (file);

ISymbolWriterProvider symbolWriterProvider;
switch (roundtripType) {
case RoundtripType.Pdb when Platform.HasNativePdbSupport:
symbolWriterProvider = new PdbWriterProvider ();
break;
case RoundtripType.PortablePdb:
default:
symbolWriterProvider = new PortablePdbWriterProvider ();
break;
}

module.Write (file, new WriterParameters {
SymbolWriterProvider = symbolWriterProvider,
});
module.Dispose ();

ISymbolReaderProvider symbolReaderProvider;
switch (roundtripType) {
case RoundtripType.Pdb when Platform.HasNativePdbSupport:
symbolReaderProvider = new PdbReaderProvider ();
break;
case RoundtripType.PortablePdb:
default:
symbolReaderProvider = new PortablePdbReaderProvider ();
break;
}

return ModuleDefinition.ReadModule (file, new ReaderParameters {
SymbolReaderProvider = symbolReaderProvider,
InMemory = true
});
}
}

abstract class TestCase {
Expand Down
50 changes: 0 additions & 50 deletions Test/Mono.Cecil.Tests/ILProcessorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
using System.IO;
using System.Linq;

using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Mdb;
using Mono.Cecil.Pdb;
using NUnit.Framework;

namespace Mono.Cecil.Tests {
Expand Down Expand Up @@ -499,12 +497,6 @@ static MethodBody CreateTestMethodWithLocalScopes (RoundtripType roundtripType,
return methodBody;
}

public enum RoundtripType {
None,
Pdb,
PortablePdb
}

static MethodBody RoundtripMethodBody(MethodBody methodBody, RoundtripType roundtripType, bool forceUnresolvedScopes = false, bool reverseScopeOrder = false)
{
var newModule = RoundtripModule (methodBody.Method.Module, roundtripType);
Expand Down Expand Up @@ -540,47 +532,5 @@ static void ReverseScopeOrder(ScopeDebugInformation scope)
foreach (var subScope in scope.Scopes)
ReverseScopeOrder (subScope);
}

static ModuleDefinition RoundtripModule(ModuleDefinition module, RoundtripType roundtripType)
{
if (roundtripType == RoundtripType.None)
return module;

var file = Path.Combine (Path.GetTempPath (), "TestILProcessor.dll");
if (File.Exists (file))
File.Delete (file);

ISymbolWriterProvider symbolWriterProvider;
switch (roundtripType) {
case RoundtripType.Pdb when Platform.HasNativePdbSupport:
symbolWriterProvider = new PdbWriterProvider ();
break;
case RoundtripType.PortablePdb:
default:
symbolWriterProvider = new PortablePdbWriterProvider ();
break;
}

module.Write (file, new WriterParameters {
SymbolWriterProvider = symbolWriterProvider,
});
module.Dispose ();

ISymbolReaderProvider symbolReaderProvider;
switch (roundtripType) {
case RoundtripType.Pdb when Platform.HasNativePdbSupport:
symbolReaderProvider = new PdbReaderProvider ();
break;
case RoundtripType.PortablePdb:
default:
symbolReaderProvider = new PortablePdbReaderProvider ();
break;
}

return ModuleDefinition.ReadModule (file, new ReaderParameters {
SymbolReaderProvider = symbolReaderProvider,
InMemory = true
});
}
}
}
42 changes: 42 additions & 0 deletions Test/Mono.Cecil.Tests/PortablePdbTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,43 @@ public void PortablePdbLineInfo()
}, symbolReaderProvider: typeof (PortablePdbReaderProvider), symbolWriterProvider: typeof (PortablePdbWriterProvider));
}

[Test]
public void TypeDefinitionDebugInformation ()
{
TestModule ("TypeDefinitionDebugInformation.dll", module => {
var enum_type = module.GetType ("TypeDefinitionDebugInformation.Enum");
Assert.IsTrue (enum_type.HasCustomDebugInformations);
var binary_custom_debug_info = enum_type.CustomDebugInformations.OfType<BinaryCustomDebugInformation> ().FirstOrDefault ();
Assert.IsNotNull (binary_custom_debug_info);
Assert.AreEqual (new Guid ("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3"), binary_custom_debug_info.Identifier);
Assert.AreEqual (new byte [] { 0x1 }, binary_custom_debug_info.Data);
var interface_type = module.GetType ("TypeDefinitionDebugInformation.Interface");
Assert.IsTrue (interface_type.HasCustomDebugInformations);
binary_custom_debug_info = interface_type.CustomDebugInformations.OfType<BinaryCustomDebugInformation> ().FirstOrDefault ();
Assert.IsNotNull (binary_custom_debug_info);
Assert.AreEqual (new Guid ("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3"), binary_custom_debug_info.Identifier);
Assert.AreEqual (new byte [] { 0x1 }, binary_custom_debug_info.Data);
}, symbolReaderProvider: typeof (PortablePdbReaderProvider), symbolWriterProvider: typeof (PortablePdbWriterProvider));
}

[Test]
public void ModifyTypeDefinitionDebugInformation ()
{
using (var module = GetResourceModule ("TypeDefinitionDebugInformation.dll", new ReaderParameters { SymbolReaderProvider = new PortablePdbReaderProvider () })) {
var enum_type = module.GetType ("TypeDefinitionDebugInformation.Enum");
var binary_custom_debug_info = enum_type.CustomDebugInformations.OfType<BinaryCustomDebugInformation> ().FirstOrDefault ();
Assert.AreEqual (new byte [] { 0x1 }, binary_custom_debug_info.Data);
binary_custom_debug_info.Data = new byte [] { 0x2 };

var outputModule = RoundtripModule (module, RoundtripType.None);
enum_type = outputModule.GetType ("TypeDefinitionDebugInformation.Enum");
binary_custom_debug_info = enum_type.CustomDebugInformations.OfType<BinaryCustomDebugInformation> ().FirstOrDefault ();
Assert.IsNotNull (binary_custom_debug_info);
Assert.AreEqual (new byte [] { 0x2 }, binary_custom_debug_info.Data);
}
}

public sealed class SymbolWriterProvider : ISymbolWriterProvider {

readonly DefaultSymbolWriterProvider writer_provider = new DefaultSymbolWriterProvider ();
Expand Down Expand Up @@ -730,6 +767,11 @@ public void Write ()
symbol_writer.Write ();
}

public void Write (ICustomDebugInformationProvider provider)
{
symbol_writer.Write (provider);
}

public void Dispose ()
{
symbol_writer.Dispose ();
Expand Down
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit a0f61f9

Please sign in to comment.