Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve custom debug information on types #948

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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