diff options
author | Virgile Bello <virgile@siliconstudio.co.jp> | 2017-03-14 14:08:45 +0300 |
---|---|---|
committer | Jb Evain <jb@evain.net> | 2017-03-21 03:49:31 +0300 |
commit | 58bd139a71167727f79ec0b6a1190f4f7efbc810 (patch) | |
tree | a7d327afc143d345284be79ca8cf45d11e26cf29 /symbols | |
parent | 0fe363af3c3ec4513dd8c3717816f7a45a0be61a (diff) |
NativePdb roundtrip for iterators, state machine, scopes and using namespace; also improved constant info for reading
Diffstat (limited to 'symbols')
-rw-r--r-- | symbols/pdb/Microsoft.Cci.Pdb/PdbFunction.cs | 6 | ||||
-rw-r--r-- | symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs | 2 | ||||
-rw-r--r-- | symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs | 79 | ||||
-rw-r--r-- | symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs | 237 | ||||
-rw-r--r-- | symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs | 8 | ||||
-rw-r--r-- | symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs | 206 | ||||
-rw-r--r-- | symbols/pdb/Test/Resources/assemblies/ComplexPdb.cs | 71 | ||||
-rw-r--r-- | symbols/pdb/Test/Resources/assemblies/ComplexPdb.dll | bin | 0 -> 7680 bytes | |||
-rw-r--r-- | symbols/pdb/Test/Resources/assemblies/ComplexPdb.pdb | bin | 0 -> 15872 bytes |
9 files changed, 597 insertions, 12 deletions
diff --git a/symbols/pdb/Microsoft.Cci.Pdb/PdbFunction.cs b/symbols/pdb/Microsoft.Cci.Pdb/PdbFunction.cs index 2ef733f..66eb806 100644 --- a/symbols/pdb/Microsoft.Cci.Pdb/PdbFunction.cs +++ b/symbols/pdb/Microsoft.Cci.Pdb/PdbFunction.cs @@ -29,7 +29,7 @@ namespace Microsoft.Cci.Pdb { internal uint segment; internal uint address; - //internal uint length; + internal uint length; //internal byte[] metadata; internal PdbScope[] scopes; @@ -208,7 +208,7 @@ namespace Microsoft.Cci.Pdb { //this.flags = proc.flags; this.segment = proc.seg; this.address = proc.off; - //this.length = proc.len; + this.length = proc.len; if (proc.seg != 1) { throw new PdbDebugException("Segment is {0}, not 1.", proc.seg); @@ -361,7 +361,7 @@ namespace Microsoft.Cci.Pdb { bits.Position = savedPosition+(int)numberOfBytesInItem; } - private void ReadForwardIterator(BitAccess bits) { + private void ReadForwardIterator(BitAccess bits) { this.iteratorClass = bits.ReadString(); } diff --git a/symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs b/symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs index c8d1340..04f4d49 100644 --- a/symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs +++ b/symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs @@ -40,7 +40,7 @@ namespace Mono.Cecil.Pdb { void DefineField_Placeholder (); void DefineGlobalVariable_Placeholder (); void Close (); - void SetSymAttribute_Placeholder (); + void SetSymAttribute (uint parent, string name, uint data, IntPtr signature); void OpenNamespace ([In, MarshalAs (UnmanagedType.LPWStr)] string name); void CloseNamespace (); void UsingNamespace ([In, MarshalAs (UnmanagedType.LPWStr)] string fullName); diff --git a/symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs b/symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs index 1a5991f..4b16d56 100644 --- a/symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs +++ b/symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs @@ -117,6 +117,14 @@ namespace Mono.Cecil.Pdb { if (!function.scopes.IsNullOrEmpty()) symbol.scope = ReadScopeAndLocals (function.scopes [0], symbol); + else + symbol.scope = new ScopeDebugInformation { Start = new InstructionOffset(0), End = new InstructionOffset((int)function.length) }; + + if (function.tokenOfMethodWhoseUsingInfoAppliesToThisMethod != method.MetadataToken.ToUInt32 () && function.tokenOfMethodWhoseUsingInfoAppliesToThisMethod != 0) { + var methodWhoseUsingInfoAppliesToThisMethod = (MethodDefinition)method.Module.LookupToken((int)function.tokenOfMethodWhoseUsingInfoAppliesToThisMethod); + if (methodWhoseUsingInfoAppliesToThisMethod != null && methodWhoseUsingInfoAppliesToThisMethod.DebugInformation.Scope != null) + symbol.scope.Import = methodWhoseUsingInfoAppliesToThisMethod.DebugInformation.Scope.Import; + } if (function.scopes.Length > 1) { for (int i = 1; i < function.scopes.Length; i++) { @@ -126,6 +134,26 @@ namespace Mono.Cecil.Pdb { } } + if (function.iteratorScopes != null) { + foreach (var iteratorScope in function.iteratorScopes) { + symbol.CustomDebugInformations.Add (new StateMachineScopeDebugInformation ((int)iteratorScope.Offset, (int)iteratorScope.Offset + (int)iteratorScope.Length + 1)); + } + } + + if (function.synchronizationInformation != null) { + var asyncDebugInfo = new AsyncMethodBodyDebugInformation ((int)function.synchronizationInformation.GeneratedCatchHandlerOffset); + + foreach (var synchronizationPoint in function.synchronizationInformation.synchronizationPoints) { + asyncDebugInfo.Yields.Add (new InstructionOffset ((int)synchronizationPoint.SynchronizeOffset)); + asyncDebugInfo.Resumes.Add (new InstructionOffset ((int)synchronizationPoint.ContinuationOffset)); + } + + symbol.CustomDebugInformations.Add (asyncDebugInfo); + + asyncDebugInfo.MoveNextMethod = method; + symbol.StateMachineKickOffMethod = (MethodDefinition)method.Module.LookupToken((int)function.synchronizationInformation.kickoffMethodToken); + } + return symbol; } @@ -165,10 +193,55 @@ namespace Mono.Cecil.Pdb { parent.constants = new Collection<ConstantDebugInformation> (scope.constants.Length); foreach (var constant in scope.constants) { - parent.constants.Add (new ConstantDebugInformation ( + var type = (TypeReference) info.method.Module.LookupToken ((int) constant.token); + var value = constant.value; + + // Object "null" is encoded as integer + if (type != null && !type.IsValueType && value is int && (int)value == 0) + value = null; + + parent.constants.Add(new ConstantDebugInformation( constant.name, - (TypeReference) info.method.Module.LookupToken ((int) constant.token), - constant.value)); + type, + value)); + } + } + + if (!scope.usedNamespaces.IsNullOrEmpty()) { + parent.import = new ImportDebugInformation(); + + foreach (var usedNamespace in scope.usedNamespaces) { + var usedNamespaceKind = usedNamespace[0]; + switch (usedNamespaceKind) { + case 'U': + parent.import.Targets.Add(new ImportTarget(ImportTargetKind.ImportNamespace) { @namespace = usedNamespace.Substring(1) }); + break; + case 'T': + { + var type = info.Method.Module.GetType(usedNamespace.Substring(1), true); + if (type != null) + parent.import.Targets.Add(new ImportTarget(ImportTargetKind.ImportType) {type = type}); + break; + } + case 'A': + { + var aliasSplit = usedNamespace.IndexOf(' '); + var alias = usedNamespace.Substring(1, aliasSplit - 1); + var aliasTarget = usedNamespace.Substring(aliasSplit + 2); + switch (usedNamespace[aliasSplit + 1]) + { + case 'U': + parent.import.Targets.Add(new ImportTarget(ImportTargetKind.DefineNamespaceAlias) { alias = alias, @namespace = aliasTarget }); + break; + case 'T': + var type = info.Method.Module.GetType(aliasTarget, true); + if (type != null) + parent.import.Targets.Add(new ImportTarget(ImportTargetKind.DefineTypeAlias) { alias = alias, type = type }); + break; + } + break; + } + } } } diff --git a/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs b/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs index 65be260..81f860c 100644 --- a/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs +++ b/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs @@ -11,8 +11,9 @@ using System; using System.Collections.Generic; using System.Diagnostics.SymbolStore; - +using System.IO; using Mono.Cecil.Cil; +using Mono.Cecil.PE; using Mono.Collections.Generic; #if !READ_ONLY @@ -24,12 +25,14 @@ namespace Mono.Cecil.Pdb { readonly ModuleDefinition module; readonly SymWriter writer; readonly Dictionary<string, SymDocumentWriter> documents; + readonly Dictionary<ImportDebugInformation, uint> importToMethods; internal NativePdbWriter (ModuleDefinition module, SymWriter writer) { this.module = module; this.writer = writer; this.documents = new Dictionary<string, SymDocumentWriter> (); + this.importToMethods = new Dictionary<ImportDebugInformation, uint> (); } public ImageDebugHeader GetDebugHeader () @@ -45,26 +48,197 @@ namespace Mono.Cecil.Pdb { var method_token = info.method.MetadataToken; var sym_token = new SymbolToken (method_token.ToInt32 ()); + // Nothing interesting to save + if (!info.HasSequencePoints && info.scope == null && !info.HasCustomDebugInformations && info.StateMachineKickOffMethod == null) + return; + writer.OpenMethod (sym_token); if (!info.sequence_points.IsNullOrEmpty ()) DefineSequencePoints (info.sequence_points); + uint methodWhoseUsingInfoAppliesToThisMethod = 0; + if (info.scope != null) - DefineScope (info.scope, info); + DefineScope(info.scope, info, out methodWhoseUsingInfoAppliesToThisMethod); + + DefineCustomMetadata (info, methodWhoseUsingInfoAppliesToThisMethod); writer.CloseMethod (); } - void DefineScope (ScopeDebugInformation scope, MethodDebugInformation info) + private void DefineCustomMetadata (MethodDebugInformation info, uint methodWhoseUsingInfoAppliesToThisMethod) + { + // Custom PDB metadata + using (var memoryStream = new MemoryStream()) + { + var metadata = new BinaryStreamWriter(memoryStream); + metadata.WriteByte(4); // version + metadata.WriteByte((byte)1); // count + metadata.WriteInt16(0); // padding + + var metadataStartPosition = metadata.BaseStream.Position; + var customMetadataCount = 0; + + // Using informations + if (methodWhoseUsingInfoAppliesToThisMethod != 0) + { + customMetadataCount++; + metadata.WriteByte(4); // version + metadata.WriteByte(1); // forward info + metadata.Align(4); + using (new PdbBinaryStreamWriterSizeHelper(metadata)) + { + metadata.WriteUInt32(methodWhoseUsingInfoAppliesToThisMethod); + } + } + else if (info.scope != null && info.scope.Import != null && info.scope.Import.HasTargets) + { + customMetadataCount++; + metadata.WriteByte(4); // version + metadata.WriteByte(0); // using info + metadata.Align(4); + using (new PdbBinaryStreamWriterSizeHelper(metadata)) + { + metadata.WriteUInt16((ushort)1); + metadata.WriteUInt16((ushort)info.scope.Import.Targets.Count); + metadata.Align(4); + } + } + + // Note: This code detects state machine attributes automatically (rather than adding an IteratorClassDebugInformation only for Native PDB) + if (info.Method.HasCustomAttributes) + { + foreach (var customAttribute in info.Method.CustomAttributes) + { + if (customAttribute.AttributeType.FullName == "System.Runtime.CompilerServices.IteratorStateMachineAttribute" + || customAttribute.AttributeType.FullName == "System.Runtime.CompilerServices.AsyncStateMachineAttribute") + { + var type = customAttribute.ConstructorArguments[0].Value as TypeReference; + if (type == null) + continue; + + customMetadataCount++; + metadata.WriteByte(4); // version + metadata.WriteByte(4); // forward iterator + metadata.Align(4); + using (new PdbBinaryStreamWriterSizeHelper(metadata)) + { + metadata.WriteString(type.Name); + metadata.Align(4); + } + } + } + } + + // StateMachineScopeDebugInformation + var stateMachineDebugInformationCount = 0; + if (info.HasCustomDebugInformations) + { + // Count + foreach (var customDebugInformation in info.CustomDebugInformations) + { + if (customDebugInformation is StateMachineScopeDebugInformation) + stateMachineDebugInformationCount++; + } + + if (stateMachineDebugInformationCount > 0) + { + customMetadataCount++; + metadata.WriteByte(4); // version + metadata.WriteByte(3); // iterator scopes + metadata.Align(4); + using (new PdbBinaryStreamWriterSizeHelper(metadata)) + { + metadata.WriteInt32(stateMachineDebugInformationCount); + foreach (var customDebugInformation in info.CustomDebugInformations) + { + var stateMachineDebugInformation = customDebugInformation as StateMachineScopeDebugInformation; + if (stateMachineDebugInformation != null) + { + var start = stateMachineDebugInformation.Start.Offset; + var end = stateMachineDebugInformation.End.IsEndOfMethod ? info.code_size : stateMachineDebugInformation.End.Offset; + metadata.WriteInt32(start); + metadata.WriteInt32(end - 1); + } + } + } + } + } + + if (metadata.BaseStream.Position != metadataStartPosition) + { + // Update number of entries + metadata.Flush(); + metadata.BaseStream.Position = 1; + metadata.WriteByte((byte)customMetadataCount); + metadata.Flush(); + + writer.DefineCustomMetadata("MD2", memoryStream.ToArray()); + } + } + + foreach (var customDebugInformation in info.CustomDebugInformations) + { + // Save back asyncMethodInfo + var asyncDebugInfo = customDebugInformation as AsyncMethodBodyDebugInformation; + if (asyncDebugInfo != null) + { + using (var asyncMemoryStream = new MemoryStream()) + { + var asyncMetadata = new BinaryStreamWriter(asyncMemoryStream); + asyncMetadata.WriteUInt32(info.StateMachineKickOffMethod != null ? info.StateMachineKickOffMethod.MetadataToken.ToUInt32() : 0); + asyncMetadata.WriteUInt32((uint)asyncDebugInfo.CatchHandler.Offset); + asyncMetadata.WriteUInt32((uint)asyncDebugInfo.Resumes.Count); + for (int i = 0; i < asyncDebugInfo.Resumes.Count; ++i) + { + asyncMetadata.WriteUInt32((uint)asyncDebugInfo.Yields[i].Offset); + asyncMetadata.WriteUInt32(asyncDebugInfo.MoveNextMethod != null ? asyncDebugInfo.MoveNextMethod.MetadataToken.ToUInt32() : 0); + asyncMetadata.WriteUInt32((uint)asyncDebugInfo.Resumes[i].Offset); + } + + writer.DefineCustomMetadata("asyncMethodInfo", asyncMemoryStream.ToArray()); + } + } + } + } + + void DefineScope (ScopeDebugInformation scope, MethodDebugInformation info, out uint methodWhoseUsingInfoAppliesToThisMethod) { var start_offset = scope.Start.Offset; var end_offset = scope.End.IsEndOfMethod ? info.code_size : scope.End.Offset; + methodWhoseUsingInfoAppliesToThisMethod = 0; + writer.OpenScope (start_offset); + if (scope.Import != null && scope.Import.HasTargets) { + if (!importToMethods.TryGetValue (info.scope.Import, out methodWhoseUsingInfoAppliesToThisMethod)) { + + foreach (var target in scope.Import.Targets) { + switch (target.Kind) + { + case ImportTargetKind.ImportNamespace: + writer.UsingNamespace("U" + target.@namespace); + break; + case ImportTargetKind.ImportType: + writer.UsingNamespace("T" + TypeParser.ToParseable(target.type, false)); + break; + case ImportTargetKind.DefineNamespaceAlias: + writer.UsingNamespace("A" + target.Alias + " U" + target.@namespace); + break; + case ImportTargetKind.DefineTypeAlias: + writer.UsingNamespace("A" + target.Alias + " T" + TypeParser.ToParseable(target.type, false)); + break; + } + } + + importToMethods.Add (info.scope.Import, info.method.MetadataToken.ToUInt32 ()); + } + } + var sym_token = new SymbolToken (info.local_var_token.ToInt32 ()); if (!scope.variables.IsNullOrEmpty ()) { @@ -75,8 +249,10 @@ namespace Mono.Cecil.Pdb { } if (!scope.scopes.IsNullOrEmpty ()) { - for (int i = 0; i < scope.scopes.Count; i++) - DefineScope (scope.scopes [i], info); + for (int i = 0; i < scope.scopes.Count; i++) { + uint ignored; + DefineScope (scope.scopes [i], info, out ignored); + } } writer.CloseScope (end_offset); @@ -139,6 +315,57 @@ namespace Mono.Cecil.Pdb { writer.Close (); } } + + // Helper that will write back total after dispose + struct PdbBinaryStreamWriterSizeHelper : IDisposable + { + private readonly BinaryStreamWriter streamWriter; + private readonly uint startPosition; + + public PdbBinaryStreamWriterSizeHelper(BinaryStreamWriter streamWriter) + { + this.streamWriter = streamWriter; + + // Remember start position + this.startPosition = (uint)streamWriter.BaseStream.Position; + + // Write 0 for now + streamWriter.WriteUInt32(0); + } + + public void Dispose() + { + streamWriter.Flush(); + var endPosition = (uint)streamWriter.BaseStream.Position; + + // Write updated size + streamWriter.BaseStream.Position = startPosition; + streamWriter.WriteUInt32(endPosition - startPosition + 4); // adds 4 for header + streamWriter.Flush(); + + streamWriter.BaseStream.Position = endPosition; + } + } + + static class StreamExtensions + { + public static void Align(this BinaryStreamWriter streamWriter, int alignment) + { + var position = (int)streamWriter.BaseStream.Position; + var paddingLength = (position + alignment - 1) / alignment * alignment - position; + for (var i = 0; i < paddingLength; ++i) + streamWriter.Write((byte)0); + } + + public static void WriteString(this BinaryStreamWriter streamWriter, string str) + { + foreach (var c in str) + { + streamWriter.WriteInt16((short)c); + } + streamWriter.WriteInt16(0); + } + } } #endif diff --git a/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs b/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs index ab0893b..56a8b1c 100644 --- a/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs +++ b/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs @@ -145,6 +145,14 @@ namespace Mono.Cecil.Pdb { m_writer.UsingNamespace (fullName); } + + public void DefineCustomMetadata(string name, byte[] metadata) + { + var metadataPinned = GCHandle.Alloc(metadata, GCHandleType.Pinned); + var metadataPtr = metadataPinned.AddrOfPinnedObject(); + m_writer.SetSymAttribute(0, name, (uint)metadata.Length, metadataPtr); + metadataPinned.Free(); + } } } diff --git a/symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs b/symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs index f9f11bd..837aca6 100644 --- a/symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs +++ b/symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs @@ -155,6 +155,212 @@ namespace Mono.Cecil.Tests { } [Test] + public void LocalVariables() + { + TestModule("ComplexPdb.dll", module => { + var type = module.GetType("ComplexPdb.Program"); + var method = type.GetMethod("Bar"); + var debug_info = method.DebugInformation; + + Assert.IsNotNull(debug_info.Scope); + Assert.IsTrue(debug_info.Scope.HasScopes); + Assert.AreEqual(2, debug_info.Scope.Scopes.Count); + + var scope = debug_info.Scope.Scopes[0]; + + Assert.IsNotNull(scope); + Assert.IsTrue(scope.HasVariables); + Assert.AreEqual(1, scope.Variables.Count); + + var variable = scope.Variables[0]; + + Assert.AreEqual("s", variable.Name); + Assert.IsFalse(variable.IsDebuggerHidden); + Assert.AreEqual(2, variable.Index); + + scope = debug_info.Scope.Scopes[1]; + + Assert.IsNotNull(scope); + Assert.IsTrue(scope.HasVariables); + Assert.AreEqual(1, scope.Variables.Count); + + variable = scope.Variables[0]; + + Assert.AreEqual("s", variable.Name); + Assert.IsFalse(variable.IsDebuggerHidden); + Assert.AreEqual(3, variable.Index); + + Assert.IsTrue(scope.HasScopes); + Assert.AreEqual(1, scope.Scopes.Count); + + scope = scope.Scopes[0]; + + Assert.IsNotNull(scope); + Assert.IsTrue(scope.HasVariables); + Assert.AreEqual(1, scope.Variables.Count); + + variable = scope.Variables[0]; + + Assert.AreEqual("u", variable.Name); + Assert.IsFalse(variable.IsDebuggerHidden); + Assert.AreEqual(5, variable.Index); + }, readOnly: Platform.OnMono, symbolReaderProvider: typeof(PdbReaderProvider), symbolWriterProvider: typeof(PdbWriterProvider)); + } + + [Test] + public void LocalConstants() + { + TestModule("ComplexPdb.dll", module => { + var type = module.GetType("ComplexPdb.Program"); + var method = type.GetMethod("Bar"); + var debug_info = method.DebugInformation; + + Assert.IsNotNull(debug_info.Scope); + Assert.IsTrue(debug_info.Scope.HasScopes); + Assert.AreEqual(2, debug_info.Scope.Scopes.Count); + + var scope = debug_info.Scope.Scopes[1]; + + Assert.IsNotNull(scope); + Assert.IsTrue(scope.HasConstants); + Assert.AreEqual(2, scope.Constants.Count); + + var constant = scope.Constants[0]; + + Assert.AreEqual("b", constant.Name); + Assert.AreEqual(12, constant.Value); + Assert.AreEqual(MetadataType.Int32, constant.ConstantType.MetadataType); + + constant = scope.Constants[1]; + Assert.AreEqual("c", constant.Name); + Assert.AreEqual((decimal)74, constant.Value); + Assert.AreEqual(MetadataType.ValueType, constant.ConstantType.MetadataType); + + method = type.GetMethod("Foo"); + debug_info = method.DebugInformation; + + Assert.IsNotNull(debug_info.Scope); + Assert.IsTrue(debug_info.Scope.HasConstants); + Assert.AreEqual(4, debug_info.Scope.Constants.Count); + + constant = debug_info.Scope.Constants[0]; + Assert.AreEqual("s", constant.Name); + Assert.AreEqual("const string", constant.Value); + Assert.AreEqual(MetadataType.String, constant.ConstantType.MetadataType); + + constant = debug_info.Scope.Constants[1]; + Assert.AreEqual("f", constant.Name); + Assert.AreEqual(1, constant.Value); + Assert.AreEqual(MetadataType.Int32, constant.ConstantType.MetadataType); + + constant = debug_info.Scope.Constants[2]; + Assert.AreEqual("o", constant.Name); + Assert.AreEqual(null, constant.Value); + Assert.AreEqual(MetadataType.Object, constant.ConstantType.MetadataType); + + constant = debug_info.Scope.Constants[3]; + Assert.AreEqual("u", constant.Name); + Assert.AreEqual(null, constant.Value); + Assert.AreEqual(MetadataType.String, constant.ConstantType.MetadataType); + }, readOnly: true, symbolReaderProvider: typeof(PdbReaderProvider), symbolWriterProvider: typeof(PdbWriterProvider)); + } + + [Test] + public void ImportScope() + { + TestModule("ComplexPdb.dll", module => { + var type = module.GetType("ComplexPdb.Program"); + var method = type.GetMethod("Bar"); + var debug_info = method.DebugInformation; + + Assert.IsNotNull(debug_info.Scope); + + var import = debug_info.Scope.Import; + Assert.IsNotNull(import); + + Assert.IsTrue(import.HasTargets); + Assert.AreEqual(6, import.Targets.Count); + var target = import.Targets[0]; + + Assert.AreEqual(ImportTargetKind.ImportNamespace, target.Kind); + Assert.AreEqual("System", target.Namespace); + + target = import.Targets[1]; + + Assert.AreEqual(ImportTargetKind.ImportNamespace, target.Kind); + Assert.AreEqual("System.Collections.Generic", target.Namespace); + + target = import.Targets[2]; + + Assert.AreEqual(ImportTargetKind.ImportNamespace, target.Kind); + Assert.AreEqual("System.Threading.Tasks", target.Namespace); + + target = import.Targets[3]; + + Assert.AreEqual(ImportTargetKind.ImportType, target.Kind); + Assert.AreEqual("System.Console", target.Type.FullName); + + target = import.Targets[4]; + + Assert.AreEqual(ImportTargetKind.DefineTypeAlias, target.Kind); + Assert.AreEqual("Foo1", target.Alias); + Assert.AreEqual("System.Console", target.Type.FullName); + + target = import.Targets[5]; + + Assert.AreEqual(ImportTargetKind.DefineNamespaceAlias, target.Kind); + Assert.AreEqual("Foo2", target.Alias); + Assert.AreEqual("System.Reflection", target.Namespace); + }, readOnly: Platform.OnMono, symbolReaderProvider: typeof(PdbReaderProvider), symbolWriterProvider: typeof(PdbWriterProvider)); + } + + [Test] + public void StateMachineKickOff() + { + TestModule("ComplexPdb.dll", module => { + var state_machine = module.GetType("ComplexPdb.Program/<TestAsync>d__2"); + var move_next = state_machine.GetMethod("MoveNext"); + var symbol = move_next.DebugInformation; + + Assert.IsNotNull(symbol); + Assert.IsNotNull(symbol.StateMachineKickOffMethod); + Assert.AreEqual("System.Threading.Tasks.Task ComplexPdb.Program::TestAsync()", symbol.StateMachineKickOffMethod.FullName); + }, readOnly: Platform.OnMono, symbolReaderProvider: typeof(PdbReaderProvider), symbolWriterProvider: typeof(PdbWriterProvider)); + } + + [Test] + public void Iterators() + { + TestModule("ComplexPdb.dll", module => { + var state_machine = module.GetType("ComplexPdb.Program/<TestAsync>d__2"); + var move_next = state_machine.GetMethod("MoveNext"); + + Assert.IsTrue(move_next.DebugInformation.HasCustomDebugInformations); + Assert.AreEqual(2, move_next.DebugInformation.CustomDebugInformations.Count); + + var state_machine_scope = move_next.DebugInformation.CustomDebugInformations[0] as StateMachineScopeDebugInformation; + Assert.IsNotNull(state_machine_scope); + Assert.AreEqual(142, state_machine_scope.Start.Offset); + Assert.AreEqual(319, state_machine_scope.End.Offset); + + var async_body = move_next.DebugInformation.CustomDebugInformations[1] as AsyncMethodBodyDebugInformation; + Assert.IsNotNull(async_body); + Assert.AreEqual(-1, async_body.CatchHandler.Offset); + + Assert.AreEqual(2, async_body.Yields.Count); + Assert.AreEqual(68, async_body.Yields[0].Offset); + Assert.AreEqual(197, async_body.Yields[1].Offset); + + Assert.AreEqual(2, async_body.Resumes.Count); + Assert.AreEqual(98, async_body.Resumes[0].Offset); + Assert.AreEqual(227, async_body.Resumes[1].Offset); + + Assert.AreEqual(move_next, async_body.MoveNextMethod); + }, readOnly: Platform.OnMono, symbolReaderProvider: typeof(PdbReaderProvider), symbolWriterProvider: typeof(PdbWriterProvider)); + } + + + [Test] public void CreateMethodFromScratch () { IgnoreOnMono (); diff --git a/symbols/pdb/Test/Resources/assemblies/ComplexPdb.cs b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.cs new file mode 100644 index 0000000..ae29775 --- /dev/null +++ b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using static System.Console; +using Foo1 = System.Console; +using Foo2 = System.Reflection; + +namespace ComplexPdb +{ + public class Program + { + private static void Foo() + { + const string s = "const string"; + const int f = 1; + const object o = null; + const string u = null; + WriteLine(s); + WriteLine(2); + } + + private static void Bar(int i) + { + int result; + if (i < 0) + { + int s = 1; + result = s; + } + else + { + const int b = 12; + const decimal c = 74; + int s = -1; + result = s + b + (int)c; + if (result > b) + { + int u = result * b; + Console.WriteLine(u); + } + } + WriteLine(result); + } + + public async Task TestAsync() + { + WriteLine("Hello1"); + await Task.Delay(100); + + for (int i = 0; i < 4; ++i) + { + WriteLine("Hello2"); + await Task.Delay(100); + WriteLine("Hello3"); + } + } + + public IEnumerable<string> TestIterator() + { + WriteLine("Hello1"); + yield return "Hello1"; + + for (int i = 0; i < 4; ++i) + { + WriteLine("Hello1"); + yield return "Hello2"; + WriteLine("Hello3"); + } + } + } +} diff --git a/symbols/pdb/Test/Resources/assemblies/ComplexPdb.dll b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.dll Binary files differnew file mode 100644 index 0000000..353e806 --- /dev/null +++ b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.dll diff --git a/symbols/pdb/Test/Resources/assemblies/ComplexPdb.pdb b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.pdb Binary files differnew file mode 100644 index 0000000..0aef0e3 --- /dev/null +++ b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.pdb |