Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/cecil.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVirgile Bello <virgile@siliconstudio.co.jp>2017-03-14 14:08:45 +0300
committerJb Evain <jb@evain.net>2017-03-21 03:49:31 +0300
commit58bd139a71167727f79ec0b6a1190f4f7efbc810 (patch)
treea7d327afc143d345284be79ca8cf45d11e26cf29 /symbols
parent0fe363af3c3ec4513dd8c3717816f7a45a0be61a (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.cs6
-rw-r--r--symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs2
-rw-r--r--symbols/pdb/Mono.Cecil.Pdb/NativePdbReader.cs79
-rw-r--r--symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs237
-rw-r--r--symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs8
-rw-r--r--symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs206
-rw-r--r--symbols/pdb/Test/Resources/assemblies/ComplexPdb.cs71
-rw-r--r--symbols/pdb/Test/Resources/assemblies/ComplexPdb.dllbin0 -> 7680 bytes
-rw-r--r--symbols/pdb/Test/Resources/assemblies/ComplexPdb.pdbbin0 -> 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
new file mode 100644
index 0000000..353e806
--- /dev/null
+++ b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.dll
Binary files differ
diff --git a/symbols/pdb/Test/Resources/assemblies/ComplexPdb.pdb b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.pdb
new file mode 100644
index 0000000..0aef0e3
--- /dev/null
+++ b/symbols/pdb/Test/Resources/assemblies/ComplexPdb.pdb
Binary files differ