Skip to content

Commit a0f61f9

Browse files
sbomerjtschuster
andauthored
Preserve custom debug information on types (#948)
* 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 <[email protected]> * Build for netstandard2.0 --------- Co-authored-by: Jackson Schuster <[email protected]>
1 parent 50292e7 commit a0f61f9

15 files changed

+200
-51
lines changed

Directory.Build.props

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\cecil.snk</AssemblyOriginatorKeyFile>
1010
<DefineConstants Condition=" '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'net8.0' ">$(DefineConstants);NET_CORE</DefineConstants>
1111
<RootNamespace></RootNamespace>
12+
<LangVersion>latest</LangVersion>
1213
</PropertyGroup>
1314
<PropertyGroup Condition=" '$(TargetFramework)' == 'net40' ">
1415
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>

Mono.Cecil.Cil/PortablePdb.cs

+21
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.IO;
1414
using System.IO.Compression;
1515
using System.Security.Cryptography;
16+
using Mono.Collections.Generic;
1617
using Mono.Cecil.Metadata;
1718
using Mono.Cecil.PE;
1819

@@ -145,6 +146,11 @@ void ReadStateMachineKickOffMethod (MethodDebugInformation method_info)
145146
method_info.kickoff_method = debug_reader.ReadStateMachineKickoffMethod (method_info.method);
146147
}
147148

149+
public Collection<CustomDebugInformation> Read (ICustomDebugInformationProvider provider)
150+
{
151+
return debug_reader.GetCustomDebugInformation (provider);
152+
}
153+
148154
void ReadCustomDebugInformations (MethodDebugInformation info)
149155
{
150156
info.method.custom_infos = debug_reader.GetCustomDebugInformation (info.method);
@@ -221,6 +227,11 @@ public MethodDebugInformation Read (MethodDefinition method)
221227
return reader.Read (method);
222228
}
223229

230+
public Collection<CustomDebugInformation> Read (ICustomDebugInformationProvider provider)
231+
{
232+
return reader.Read (provider);
233+
}
234+
224235
public void Dispose ()
225236
{
226237
reader.Dispose ();
@@ -319,6 +330,11 @@ public void Write ()
319330
}
320331
}
321332

333+
public void Write (ICustomDebugInformationProvider provider)
334+
{
335+
pdb_metadata.AddCustomDebugInformations (provider);
336+
}
337+
322338
public ImageDebugHeader GetDebugHeader ()
323339
{
324340
if (IsEmbedded)
@@ -519,6 +535,11 @@ public void Write (MethodDebugInformation info)
519535
writer.Write (info);
520536
}
521537

538+
public void Write (ICustomDebugInformationProvider provider)
539+
{
540+
writer.Write (provider);
541+
}
542+
522543
public ImageDebugHeader GetDebugHeader ()
523544
{
524545
ImageDebugHeader pdbDebugHeader = writer.GetDebugHeader ();

Mono.Cecil.Cil/Symbols.cs

+37
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ public interface ISymbolReader : IDisposable {
854854
ISymbolWriterProvider GetWriterProvider ();
855855
bool ProcessDebugHeader (ImageDebugHeader header);
856856
MethodDebugInformation Read (MethodDefinition method);
857+
Collection<CustomDebugInformation> Read (ICustomDebugInformationProvider provider);
857858
}
858859

859860
public interface ISymbolReaderProvider {
@@ -1116,6 +1117,7 @@ public interface ISymbolWriter : IDisposable {
11161117
ImageDebugHeader GetDebugHeader ();
11171118
void Write (MethodDebugInformation info);
11181119
void Write ();
1120+
void Write (ICustomDebugInformationProvider provider);
11191121
}
11201122

11211123
public interface ISymbolWriterProvider {
@@ -1224,5 +1226,40 @@ public static bool IsPortablePdb (Stream stream)
12241226
stream.Position = position;
12251227
}
12261228
}
1229+
1230+
public static bool GetHasCustomDebugInformations (
1231+
this ICustomDebugInformationProvider self,
1232+
ref Collection<CustomDebugInformation> collection,
1233+
ModuleDefinition module)
1234+
{
1235+
if (module.HasImage ()) {
1236+
module.Read (ref collection, self, static (provider, reader) => {
1237+
var symbol_reader = reader.module.symbol_reader;
1238+
if (symbol_reader != null)
1239+
return symbol_reader.Read (provider);
1240+
return null;
1241+
});
1242+
}
1243+
1244+
return !collection.IsNullOrEmpty ();
1245+
}
1246+
1247+
public static Collection<CustomDebugInformation> GetCustomDebugInformations (
1248+
this ICustomDebugInformationProvider self,
1249+
ref Collection<CustomDebugInformation> collection,
1250+
ModuleDefinition module)
1251+
{
1252+
if (module.HasImage ()) {
1253+
module.Read (ref collection, self, static (provider, reader) => {
1254+
var symbol_reader = reader.module.symbol_reader;
1255+
if (symbol_reader != null)
1256+
return symbol_reader.Read (provider);
1257+
return null;
1258+
});
1259+
}
1260+
1261+
Interlocked.CompareExchange (ref collection, new Collection<CustomDebugInformation> (), null);
1262+
return collection;
1263+
}
12271264
}
12281265
}

Mono.Cecil/AssemblyReader.cs

+12
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ void ReadTypesSymbols (Collection<TypeDefinition> types, ISymbolReader symbol_re
404404
{
405405
for (int i = 0; i < types.Count; i++) {
406406
var type = types [i];
407+
type.custom_infos = symbol_reader.Read (type);
407408

408409
if (type.HasNestedTypes)
409410
ReadTypesSymbols (type.NestedTypes, symbol_reader);
@@ -3160,6 +3161,17 @@ void InitializeCustomDebugInformations ()
31603161
}
31613162
}
31623163

3164+
public bool HasCustomDebugInformation (ICustomDebugInformationProvider provider)
3165+
{
3166+
InitializeCustomDebugInformations ();
3167+
3168+
Row<Guid, uint, uint> [] rows;
3169+
if (!metadata.CustomDebugInformations.TryGetValue (provider.MetadataToken, out rows))
3170+
return false;
3171+
3172+
return rows.Length > 0;
3173+
}
3174+
31633175
public Collection<CustomDebugInformation> GetCustomDebugInformation (ICustomDebugInformationProvider provider)
31643176
{
31653177
InitializeCustomDebugInformations ();

Mono.Cecil/AssemblyWriter.cs

+3
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,9 @@ void AddType (TypeDefinition type)
14871487
if (type.HasNestedTypes)
14881488
AddNestedTypes (type);
14891489

1490+
if (symbol_writer != null && type.HasCustomDebugInformations)
1491+
symbol_writer.Write (type);
1492+
14901493
WindowsRuntimeProjections.ApplyProjection (type, treatment);
14911494
}
14921495

Mono.Cecil/TypeDefinition.cs

+17-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010

1111
using System;
1212
using System.Threading;
13+
using Mono.Cecil.Cil;
1314
using Mono.Cecil.Metadata;
1415
using Mono.Collections.Generic;
1516

1617
namespace Mono.Cecil {
1718

18-
public sealed class TypeDefinition : TypeReference, IMemberDefinition, ISecurityDeclarationProvider {
19+
public sealed class TypeDefinition : TypeReference, IMemberDefinition, ISecurityDeclarationProvider, ICustomDebugInformationProvider {
1920

2021
uint attributes;
2122
TypeReference base_type;
@@ -34,6 +35,8 @@ public sealed class TypeDefinition : TypeReference, IMemberDefinition, ISecurity
3435
Collection<CustomAttribute> custom_attributes;
3536
Collection<SecurityDeclaration> security_declarations;
3637

38+
internal Collection<CustomDebugInformation> custom_infos;
39+
3740
public TypeAttributes Attributes {
3841
get { return (TypeAttributes) attributes; }
3942
set {
@@ -284,6 +287,19 @@ public override Collection<GenericParameter> GenericParameters {
284287
get { return generic_parameters ?? (this.GetGenericParameters (ref generic_parameters, Module)); }
285288
}
286289

290+
public bool HasCustomDebugInformations {
291+
get {
292+
if (custom_infos != null)
293+
return custom_infos.Count > 0;
294+
295+
return this.GetHasCustomDebugInformations (ref custom_infos, Module);
296+
}
297+
}
298+
299+
public Collection<CustomDebugInformation> CustomDebugInformations {
300+
get { return custom_infos ?? (this.GetCustomDebugInformations (ref custom_infos, module)); }
301+
}
302+
287303
#region TypeAttributes
288304

289305
public bool IsNotPublic {

Test/Mono.Cecil.Tests/BaseTestFixture.cs

+49
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.IO;
33
using System.Runtime.CompilerServices;
44
using Mono.Cecil.Cil;
5+
using Mono.Cecil.Pdb;
56
using NUnit.Framework;
67

78
using Mono.Cecil.PE;
@@ -154,6 +155,54 @@ static void Run (TestCase testCase)
154155
using (var runner = new TestRunner (testCase, TestCaseType.WriteFromImmediate))
155156
runner.RunTest ();
156157
}
158+
159+
public enum RoundtripType {
160+
None,
161+
Pdb,
162+
PortablePdb
163+
}
164+
165+
protected static ModuleDefinition RoundtripModule(ModuleDefinition module, RoundtripType roundtripType)
166+
{
167+
if (roundtripType == RoundtripType.None)
168+
return module;
169+
170+
var file = Path.Combine (Path.GetTempPath (), "RoundtripModule.dll");
171+
if (File.Exists (file))
172+
File.Delete (file);
173+
174+
ISymbolWriterProvider symbolWriterProvider;
175+
switch (roundtripType) {
176+
case RoundtripType.Pdb when Platform.HasNativePdbSupport:
177+
symbolWriterProvider = new PdbWriterProvider ();
178+
break;
179+
case RoundtripType.PortablePdb:
180+
default:
181+
symbolWriterProvider = new PortablePdbWriterProvider ();
182+
break;
183+
}
184+
185+
module.Write (file, new WriterParameters {
186+
SymbolWriterProvider = symbolWriterProvider,
187+
});
188+
module.Dispose ();
189+
190+
ISymbolReaderProvider symbolReaderProvider;
191+
switch (roundtripType) {
192+
case RoundtripType.Pdb when Platform.HasNativePdbSupport:
193+
symbolReaderProvider = new PdbReaderProvider ();
194+
break;
195+
case RoundtripType.PortablePdb:
196+
default:
197+
symbolReaderProvider = new PortablePdbReaderProvider ();
198+
break;
199+
}
200+
201+
return ModuleDefinition.ReadModule (file, new ReaderParameters {
202+
SymbolReaderProvider = symbolReaderProvider,
203+
InMemory = true
204+
});
205+
}
157206
}
158207

159208
abstract class TestCase {

Test/Mono.Cecil.Tests/ILProcessorTests.cs

-50
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
using System.IO;
44
using System.Linq;
55

6-
using Mono.Cecil;
76
using Mono.Cecil.Cil;
87
using Mono.Cecil.Mdb;
9-
using Mono.Cecil.Pdb;
108
using NUnit.Framework;
119

1210
namespace Mono.Cecil.Tests {
@@ -499,12 +497,6 @@ static MethodBody CreateTestMethodWithLocalScopes (RoundtripType roundtripType,
499497
return methodBody;
500498
}
501499

502-
public enum RoundtripType {
503-
None,
504-
Pdb,
505-
PortablePdb
506-
}
507-
508500
static MethodBody RoundtripMethodBody(MethodBody methodBody, RoundtripType roundtripType, bool forceUnresolvedScopes = false, bool reverseScopeOrder = false)
509501
{
510502
var newModule = RoundtripModule (methodBody.Method.Module, roundtripType);
@@ -540,47 +532,5 @@ static void ReverseScopeOrder(ScopeDebugInformation scope)
540532
foreach (var subScope in scope.Scopes)
541533
ReverseScopeOrder (subScope);
542534
}
543-
544-
static ModuleDefinition RoundtripModule(ModuleDefinition module, RoundtripType roundtripType)
545-
{
546-
if (roundtripType == RoundtripType.None)
547-
return module;
548-
549-
var file = Path.Combine (Path.GetTempPath (), "TestILProcessor.dll");
550-
if (File.Exists (file))
551-
File.Delete (file);
552-
553-
ISymbolWriterProvider symbolWriterProvider;
554-
switch (roundtripType) {
555-
case RoundtripType.Pdb when Platform.HasNativePdbSupport:
556-
symbolWriterProvider = new PdbWriterProvider ();
557-
break;
558-
case RoundtripType.PortablePdb:
559-
default:
560-
symbolWriterProvider = new PortablePdbWriterProvider ();
561-
break;
562-
}
563-
564-
module.Write (file, new WriterParameters {
565-
SymbolWriterProvider = symbolWriterProvider,
566-
});
567-
module.Dispose ();
568-
569-
ISymbolReaderProvider symbolReaderProvider;
570-
switch (roundtripType) {
571-
case RoundtripType.Pdb when Platform.HasNativePdbSupport:
572-
symbolReaderProvider = new PdbReaderProvider ();
573-
break;
574-
case RoundtripType.PortablePdb:
575-
default:
576-
symbolReaderProvider = new PortablePdbReaderProvider ();
577-
break;
578-
}
579-
580-
return ModuleDefinition.ReadModule (file, new ReaderParameters {
581-
SymbolReaderProvider = symbolReaderProvider,
582-
InMemory = true
583-
});
584-
}
585535
}
586536
}

Test/Mono.Cecil.Tests/PortablePdbTests.cs

+42
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,43 @@ public void PortablePdbLineInfo()
646646
}, symbolReaderProvider: typeof (PortablePdbReaderProvider), symbolWriterProvider: typeof (PortablePdbWriterProvider));
647647
}
648648

649+
[Test]
650+
public void TypeDefinitionDebugInformation ()
651+
{
652+
TestModule ("TypeDefinitionDebugInformation.dll", module => {
653+
var enum_type = module.GetType ("TypeDefinitionDebugInformation.Enum");
654+
Assert.IsTrue (enum_type.HasCustomDebugInformations);
655+
var binary_custom_debug_info = enum_type.CustomDebugInformations.OfType<BinaryCustomDebugInformation> ().FirstOrDefault ();
656+
Assert.IsNotNull (binary_custom_debug_info);
657+
Assert.AreEqual (new Guid ("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3"), binary_custom_debug_info.Identifier);
658+
Assert.AreEqual (new byte [] { 0x1 }, binary_custom_debug_info.Data);
659+
660+
var interface_type = module.GetType ("TypeDefinitionDebugInformation.Interface");
661+
Assert.IsTrue (interface_type.HasCustomDebugInformations);
662+
binary_custom_debug_info = interface_type.CustomDebugInformations.OfType<BinaryCustomDebugInformation> ().FirstOrDefault ();
663+
Assert.IsNotNull (binary_custom_debug_info);
664+
Assert.AreEqual (new Guid ("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3"), binary_custom_debug_info.Identifier);
665+
Assert.AreEqual (new byte [] { 0x1 }, binary_custom_debug_info.Data);
666+
}, symbolReaderProvider: typeof (PortablePdbReaderProvider), symbolWriterProvider: typeof (PortablePdbWriterProvider));
667+
}
668+
669+
[Test]
670+
public void ModifyTypeDefinitionDebugInformation ()
671+
{
672+
using (var module = GetResourceModule ("TypeDefinitionDebugInformation.dll", new ReaderParameters { SymbolReaderProvider = new PortablePdbReaderProvider () })) {
673+
var enum_type = module.GetType ("TypeDefinitionDebugInformation.Enum");
674+
var binary_custom_debug_info = enum_type.CustomDebugInformations.OfType<BinaryCustomDebugInformation> ().FirstOrDefault ();
675+
Assert.AreEqual (new byte [] { 0x1 }, binary_custom_debug_info.Data);
676+
binary_custom_debug_info.Data = new byte [] { 0x2 };
677+
678+
var outputModule = RoundtripModule (module, RoundtripType.None);
679+
enum_type = outputModule.GetType ("TypeDefinitionDebugInformation.Enum");
680+
binary_custom_debug_info = enum_type.CustomDebugInformations.OfType<BinaryCustomDebugInformation> ().FirstOrDefault ();
681+
Assert.IsNotNull (binary_custom_debug_info);
682+
Assert.AreEqual (new byte [] { 0x2 }, binary_custom_debug_info.Data);
683+
}
684+
}
685+
649686
public sealed class SymbolWriterProvider : ISymbolWriterProvider {
650687

651688
readonly DefaultSymbolWriterProvider writer_provider = new DefaultSymbolWriterProvider ();
@@ -730,6 +767,11 @@ public void Write ()
730767
symbol_writer.Write ();
731768
}
732769

770+
public void Write (ICustomDebugInformationProvider provider)
771+
{
772+
symbol_writer.Write (provider);
773+
}
774+
733775
public void Dispose ()
734776
{
735777
symbol_writer.Dispose ();
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)