diff --git a/Directory.Build.props b/Directory.Build.props index 5b9f0da9d..a37508076 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,6 +9,7 @@ $(MSBuildThisFileDirectory)\cecil.snk $(DefineConstants);NET_CORE + latest true diff --git a/Mono.Cecil.Cil/PortablePdb.cs b/Mono.Cecil.Cil/PortablePdb.cs index 6664bee32..4f58ebf52 100644 --- a/Mono.Cecil.Cil/PortablePdb.cs +++ b/Mono.Cecil.Cil/PortablePdb.cs @@ -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; @@ -145,6 +146,11 @@ void ReadStateMachineKickOffMethod (MethodDebugInformation method_info) method_info.kickoff_method = debug_reader.ReadStateMachineKickoffMethod (method_info.method); } + public Collection Read (ICustomDebugInformationProvider provider) + { + return debug_reader.GetCustomDebugInformation (provider); + } + void ReadCustomDebugInformations (MethodDebugInformation info) { info.method.custom_infos = debug_reader.GetCustomDebugInformation (info.method); @@ -221,6 +227,11 @@ public MethodDebugInformation Read (MethodDefinition method) return reader.Read (method); } + public Collection Read (ICustomDebugInformationProvider provider) + { + return reader.Read (provider); + } + public void Dispose () { reader.Dispose (); @@ -319,6 +330,11 @@ public void Write () } } + public void Write (ICustomDebugInformationProvider provider) + { + pdb_metadata.AddCustomDebugInformations (provider); + } + public ImageDebugHeader GetDebugHeader () { if (IsEmbedded) @@ -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 (); diff --git a/Mono.Cecil.Cil/Symbols.cs b/Mono.Cecil.Cil/Symbols.cs index 744e7a82b..294eb16db 100644 --- a/Mono.Cecil.Cil/Symbols.cs +++ b/Mono.Cecil.Cil/Symbols.cs @@ -854,6 +854,7 @@ public interface ISymbolReader : IDisposable { ISymbolWriterProvider GetWriterProvider (); bool ProcessDebugHeader (ImageDebugHeader header); MethodDebugInformation Read (MethodDefinition method); + Collection Read (ICustomDebugInformationProvider provider); } public interface ISymbolReaderProvider { @@ -1116,6 +1117,7 @@ public interface ISymbolWriter : IDisposable { ImageDebugHeader GetDebugHeader (); void Write (MethodDebugInformation info); void Write (); + void Write (ICustomDebugInformationProvider provider); } public interface ISymbolWriterProvider { @@ -1224,5 +1226,40 @@ public static bool IsPortablePdb (Stream stream) stream.Position = position; } } + + public static bool GetHasCustomDebugInformations ( + this ICustomDebugInformationProvider self, + ref Collection 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 GetCustomDebugInformations ( + this ICustomDebugInformationProvider self, + ref Collection 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 (), null); + return collection; + } } } diff --git a/Mono.Cecil/AssemblyReader.cs b/Mono.Cecil/AssemblyReader.cs index 4564071e8..a21d0a966 100644 --- a/Mono.Cecil/AssemblyReader.cs +++ b/Mono.Cecil/AssemblyReader.cs @@ -404,6 +404,7 @@ void ReadTypesSymbols (Collection 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); @@ -3160,6 +3161,17 @@ void InitializeCustomDebugInformations () } } + public bool HasCustomDebugInformation (ICustomDebugInformationProvider provider) + { + InitializeCustomDebugInformations (); + + Row [] rows; + if (!metadata.CustomDebugInformations.TryGetValue (provider.MetadataToken, out rows)) + return false; + + return rows.Length > 0; + } + public Collection GetCustomDebugInformation (ICustomDebugInformationProvider provider) { InitializeCustomDebugInformations (); diff --git a/Mono.Cecil/AssemblyWriter.cs b/Mono.Cecil/AssemblyWriter.cs index 836aa2172..ee234c81f 100644 --- a/Mono.Cecil/AssemblyWriter.cs +++ b/Mono.Cecil/AssemblyWriter.cs @@ -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); } diff --git a/Mono.Cecil/TypeDefinition.cs b/Mono.Cecil/TypeDefinition.cs index 872e4c253..92d6ab299 100644 --- a/Mono.Cecil/TypeDefinition.cs +++ b/Mono.Cecil/TypeDefinition.cs @@ -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; @@ -34,6 +35,8 @@ public sealed class TypeDefinition : TypeReference, IMemberDefinition, ISecurity Collection custom_attributes; Collection security_declarations; + internal Collection custom_infos; + public TypeAttributes Attributes { get { return (TypeAttributes) attributes; } set { @@ -284,6 +287,19 @@ public override Collection 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 CustomDebugInformations { + get { return custom_infos ?? (this.GetCustomDebugInformations (ref custom_infos, module)); } + } + #region TypeAttributes public bool IsNotPublic { diff --git a/Test/Mono.Cecil.Tests/BaseTestFixture.cs b/Test/Mono.Cecil.Tests/BaseTestFixture.cs index 046791bfa..12de4227a 100644 --- a/Test/Mono.Cecil.Tests/BaseTestFixture.cs +++ b/Test/Mono.Cecil.Tests/BaseTestFixture.cs @@ -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; @@ -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 { diff --git a/Test/Mono.Cecil.Tests/ILProcessorTests.cs b/Test/Mono.Cecil.Tests/ILProcessorTests.cs index fd462c365..93ca078c8 100644 --- a/Test/Mono.Cecil.Tests/ILProcessorTests.cs +++ b/Test/Mono.Cecil.Tests/ILProcessorTests.cs @@ -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 { @@ -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); @@ -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 - }); - } } } diff --git a/Test/Mono.Cecil.Tests/PortablePdbTests.cs b/Test/Mono.Cecil.Tests/PortablePdbTests.cs index a89f79d83..c2b04524b 100644 --- a/Test/Mono.Cecil.Tests/PortablePdbTests.cs +++ b/Test/Mono.Cecil.Tests/PortablePdbTests.cs @@ -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 ().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 ().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 ().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 ().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 (); @@ -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 (); diff --git a/Test/Resources/assemblies/TypeDefinitionDebugInformation.dll b/Test/Resources/assemblies/TypeDefinitionDebugInformation.dll new file mode 100644 index 000000000..6b41377d0 Binary files /dev/null and b/Test/Resources/assemblies/TypeDefinitionDebugInformation.dll differ diff --git a/Test/Resources/assemblies/TypeDefinitionDebugInformation.pdb b/Test/Resources/assemblies/TypeDefinitionDebugInformation.pdb new file mode 100644 index 000000000..954231334 Binary files /dev/null and b/Test/Resources/assemblies/TypeDefinitionDebugInformation.pdb differ diff --git a/symbols/mdb/Mono.Cecil.Mdb/MdbReader.cs b/symbols/mdb/Mono.Cecil.Mdb/MdbReader.cs index 3e2dd18e4..f19212f60 100644 --- a/symbols/mdb/Mono.Cecil.Mdb/MdbReader.cs +++ b/symbols/mdb/Mono.Cecil.Mdb/MdbReader.cs @@ -186,6 +186,11 @@ SequencePoint LineToSequencePoint (LineNumberEntry line) }; } + public Collection Read (ICustomDebugInformationProvider provider) + { + return new Collection (); + } + public void Dispose () { symbol_file.Dispose (); diff --git a/symbols/mdb/Mono.Cecil.Mdb/MdbWriter.cs b/symbols/mdb/Mono.Cecil.Mdb/MdbWriter.cs index 4d4ce933b..81a0d539d 100644 --- a/symbols/mdb/Mono.Cecil.Mdb/MdbWriter.cs +++ b/symbols/mdb/Mono.Cecil.Mdb/MdbWriter.cs @@ -173,6 +173,10 @@ public void Write () // after the entire image of the assembly is written (since it's computed from the hash of that) } + public void Write (ICustomDebugInformationProvider provider) + { + } + public void Dispose () { writer.WriteSymbolFile (module.Mvid); diff --git a/symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs b/symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs index 484704447..400fb0b48 100644 --- a/symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs +++ b/symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs @@ -355,6 +355,11 @@ Document GetDocument (PdbSource source) return document; } + public Collection Read (ICustomDebugInformationProvider provider) + { + return new Collection (); + } + public void Dispose () { pdb_file.Dispose (); diff --git a/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs b/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs index 9597d7fa4..55515d5e5 100644 --- a/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs +++ b/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs @@ -265,6 +265,10 @@ public void Write () writer.Close (); } + public void Write (ICustomDebugInformationProvider provider) + { + } + public void Dispose () { writer.Close ();