Skip to content

Instantly share code, notes, and snippets.

@jbevain
Created April 24, 2010 20:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbevain/377904 to your computer and use it in GitHub Desktop.
Save jbevain/377904 to your computer and use it in GitHub Desktop.
A half working patch to support iterators
diff --git a/Mono.Cecil.Cil/MethodBody.cs b/Mono.Cecil.Cil/MethodBody.cs
index 6f818fe..bce81b4 100644
--- a/Mono.Cecil.Cil/MethodBody.cs
+++ b/Mono.Cecil.Cil/MethodBody.cs
@@ -46,6 +46,7 @@ namespace Mono.Cecil.Cil {
internal Collection<ExceptionHandler> exceptions;
internal Collection<VariableDefinition> variables;
Scope scope;
+ TypeDefinition iterator_type;
public MethodDefinition Method {
get { return method; }
@@ -110,6 +111,11 @@ namespace Mono.Cecil.Cil {
set { scope = value; }
}
+ public TypeDefinition IteratorType {
+ get { return iterator_type; }
+ set { iterator_type = value; }
+ }
+
public ParameterDefinition ThisParameter {
get {
if (method == null || method.DeclaringType == null)
diff --git a/Mono.Cecil.Cil/Symbols.cs b/Mono.Cecil.Cil/Symbols.cs
index 8e668b0..8f21d57 100644
--- a/Mono.Cecil.Cil/Symbols.cs
+++ b/Mono.Cecil.Cil/Symbols.cs
@@ -103,6 +103,7 @@ namespace Mono.Cecil.Cil {
internal int code_size;
internal string method_name;
+ internal string iterator_type;
internal MetadataToken method_token;
internal MetadataToken local_var_token;
internal Collection<VariableDefinition> variables;
@@ -132,6 +133,11 @@ namespace Mono.Cecil.Cil {
get { return method_token; }
}
+ public string IteratorType {
+ get { return iterator_type; }
+ set { iterator_type = value; }
+ }
+
public MetadataToken LocalVarToken {
get { return local_var_token; }
}
diff --git a/Mono.Cecil/AssemblyInfo.cs b/Mono.Cecil/AssemblyInfo.cs
index 74251be..a384994 100644
--- a/Mono.Cecil/AssemblyInfo.cs
+++ b/Mono.Cecil/AssemblyInfo.cs
@@ -43,5 +43,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyFileVersion ("0.9.1.0")]
#endif
+[assembly: InternalsVisibleTo ("Mono.Cecil.Mdb, PublicKey=002400000480000094000000060200000024000052534131000400000100010079159977d2d03a8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fddafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef0065d016df")]
+[assembly: InternalsVisibleTo ("Mono.Cecil.Pdb, PublicKey=002400000480000094000000060200000024000052534131000400000100010079159977d2d03a8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fddafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef0065d016df")]
[assembly: InternalsVisibleTo ("Mono.Cecil.Rocks, PublicKey=002400000480000094000000060200000024000052534131000400000100010079159977d2d03a8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fddafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef0065d016df")]
[assembly: InternalsVisibleTo ("Mono.Cecil.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010079159977d2d03a8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fddafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef0065d016df")]
diff --git a/symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs b/symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs
index 999e579..b869b7b 100644
--- a/symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs
+++ b/symbols/pdb/Mono.Cecil.Pdb/ISymUnmanagedWriter2.cs
@@ -47,7 +47,7 @@ namespace Mono.Cecil.Pdb {
[In] ref Guid documentType,
[Out, MarshalAs (UnmanagedType.Interface)] out ISymUnmanagedDocumentWriter pRetVal);
void SetUserEntryPoint_Placeholder ();
- void OpenMethod ([In] SymbolToken method);
+ void OpenMethod ([In] uint method);
void CloseMethod ();
void OpenScope ([In] int startOffset, [Out] out int pRetVal);
void CloseScope ([In] int endOffset);
@@ -57,7 +57,7 @@ namespace Mono.Cecil.Pdb {
void DefineField_Placeholder ();
void DefineGlobalVariable_Placeholder ();
void Close ();
- void SetSymAttribute_Placeholder ();
+ void SetSymAttribute ([In] uint method, [MarshalAs (UnmanagedType.LPWStr)] string name, uint cData, IntPtr signature);
void OpenNamespace ([In, MarshalAs (UnmanagedType.LPWStr)] string name);
void CloseNamespace ();
void UsingNamespace ([In, MarshalAs (UnmanagedType.LPWStr)] string fullName);
@@ -88,7 +88,7 @@ namespace Mono.Cecil.Pdb {
void DefineLocalVariable2 (
[In, MarshalAs (UnmanagedType.LPWStr)] string name,
[In] int attributes,
- [In] SymbolToken sigToken,
+ [In] uint sigToken,
[In] int addrKind,
[In] int addr1,
[In] int addr2,
diff --git a/symbols/pdb/Mono.Cecil.Pdb/PdbReader.cs b/symbols/pdb/Mono.Cecil.Pdb/PdbReader.cs
index b4b229e..5b345a7 100644
--- a/symbols/pdb/Mono.Cecil.Pdb/PdbReader.cs
+++ b/symbols/pdb/Mono.Cecil.Pdb/PdbReader.cs
@@ -114,6 +114,9 @@ namespace Mono.Cecil.Pdb {
ReadSequencePoints (function, mapper);
ReadScopeAndLocals (function.scopes, null, body, mapper);
+
+ if (!string.IsNullOrEmpty (function.iteratorClass))
+ body.IteratorType = body.Method.DeclaringType.GetNestedType (function.iteratorClass);
}
static void ReadScopeAndLocals (PdbScope [] scopes, Scope parent, MethodBody body, InstructionMapper mapper)
@@ -226,6 +229,9 @@ namespace Mono.Cecil.Pdb {
ReadSequencePoints (function, symbols);
ReadLocals (function.scopes, symbols);
+
+ if (!string.IsNullOrEmpty (function.iteratorClass))
+ symbols.IteratorType = function.iteratorClass;
}
void ReadLocals (PdbScope [] scopes, MethodSymbols symbols)
diff --git a/symbols/pdb/Mono.Cecil.Pdb/PdbWriter.cs b/symbols/pdb/Mono.Cecil.Pdb/PdbWriter.cs
index ddff265..127eba3 100644
--- a/symbols/pdb/Mono.Cecil.Pdb/PdbWriter.cs
+++ b/symbols/pdb/Mono.Cecil.Pdb/PdbWriter.cs
@@ -29,8 +29,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.SymbolStore;
-
+using System.Text;
using Mono.Cecil.Cil;
+using Mono.Cecil.PE;
using Mono.Collections.Generic;
namespace Mono.Cecil.Pdb {
@@ -56,14 +57,63 @@ namespace Mono.Cecil.Pdb {
public void Write (MethodBody body)
{
var method_token = body.Method.MetadataToken;
- var sym_token = new SymbolToken (method_token.ToInt32 ());
- writer.OpenMethod (sym_token);
+ var end_offset = body.Instructions [body.Instructions.Count - 1].Offset;
+
+ writer.OpenMethod (method_token.ToUInt32 ());
+ writer.OpenScope (0);
DefineSequencePoints (body);
- DefineVariables (body);
+ DefineVariables (body, end_offset);
+ if (body.IteratorType != null)
+ DefineIteratorType (method_token, body.IteratorType.Name);
+ if (IsIteratorMethod (body.Method))
+ DefineIteratorScope (method_token, end_offset);
+ writer.CloseScope (end_offset);
writer.CloseMethod ();
}
+ static bool IsIteratorMethod (MethodDefinition method)
+ {
+ if (method.Name != "MoveNext")
+ return false;
+ if (!method.DeclaringType.IsNested)
+ return false;
+ return true;
+
+ foreach (var parent_method in method.DeclaringType.Methods) {
+ if (!parent_method.HasBody)
+ continue;
+
+ var iterator_type = parent_method.Body.IteratorType;
+ if (iterator_type == null)
+ continue;
+
+ if (iterator_type == method.DeclaringType)
+ return true;
+ }
+
+ return false;
+ }
+
+ void DefineIteratorScope (MetadataToken method_token, int end_offset)
+ {
+ var buffer = new ByteBuffer ();
+ buffer.WriteByte (4);
+ buffer.WriteByte (1);
+ Align (buffer, 4);
+ buffer.WriteByte (4);
+ buffer.WriteByte (3);
+ Align (buffer, 4);
+
+ buffer.WriteUInt32 (20);
+ buffer.WriteUInt32 (1);
+
+ buffer.WriteUInt32 (0);
+ buffer.WriteUInt32 ((uint) end_offset);
+
+ writer.SetSymAttribute (method_token.ToUInt32 (), "MD2", buffer.length, buffer.buffer);
+ }
+
Collection<Instruction> CollectInstructions (MethodBody body)
{
var collection = new Collection<Instruction> ();
@@ -82,31 +132,60 @@ namespace Mono.Cecil.Pdb {
return collection;
}
- void DefineVariables (MethodBody body)
+ void DefineVariables (MethodBody body, int end_offset)
{
if (!body.HasVariables)
return;
- var start_offset = 0;
- var end_offset = body.Instructions [body.Instructions.Count - 1].Offset;
-
- writer.OpenScope (start_offset);
-
- var sym_token = new SymbolToken (body.LocalVarToken.ToInt32 ());
+ var sym_token = body.LocalVarToken.ToUInt32 ();
var variables = body.Variables;
for (int i = 0; i < variables.Count; i++) {
var variable = variables [i];
- CreateLocalVariable (variable, sym_token, start_offset, end_offset);
+ CreateLocalVariable (variable, sym_token, 0, end_offset);
}
+ }
- writer.CloseScope (end_offset);
+ static MethodDefinition GetMoveNext (TypeDefinition iterator)
+ {
+ if (!iterator.HasMethods)
+ return null;
+
+ foreach (var method in iterator.Methods)
+ if (method.Name == "MoveNext")
+ return method;
+
+ return null;
+ }
+
+ static SequencePoint GetFirstSequencePoint (MethodDefinition method)
+ {
+ if (!method.HasBody)
+ return null;
+
+ foreach (var instruction in method.Body.Instructions)
+ if (instruction.SequencePoint != null)
+ return instruction.SequencePoint;
+
+ return null;
}
void DefineSequencePoints (MethodBody body)
{
var instructions = CollectInstructions (body);
var count = instructions.Count;
+
+ if (count == 0 && body.IteratorType != null) {
+ var move_next = GetMoveNext (body.IteratorType);
+ var sp = GetFirstSequencePoint (move_next);
+ instructions = new Collection<Instruction> {
+ new Instruction (0, OpCodes.Nop) {
+ SequencePoint = sp,
+ }
+ };
+ count = 1;
+ }
+
if (count == 0)
return;
@@ -141,7 +220,7 @@ namespace Mono.Cecil.Pdb {
end_columns);
}
- void CreateLocalVariable (VariableDefinition variable, SymbolToken local_var_token, int start_offset, int end_offset)
+ void CreateLocalVariable (VariableDefinition variable, uint local_var_token, int start_offset, int end_offset)
{
writer.DefineLocalVariable2 (
variable.Name,
@@ -174,13 +253,45 @@ namespace Mono.Cecil.Pdb {
return doc_writer;
}
- public void Write (MethodSymbols symbols)
+ void DefineIteratorType (MetadataToken method_token, string name)
+ {
+ var buffer = new ByteBuffer ();
+ buffer.WriteByte (4);
+ buffer.WriteByte (1);
+ Align (buffer, 4);
+ buffer.WriteByte (4);
+ buffer.WriteByte (4);
+ Align (buffer, 4);
+
+ var length = 10 + (uint) name.Length * 2;
+ while (length % 4 > 0)
+ length++;
+
+ buffer.WriteUInt32 (length);
+ buffer.WriteBytes (Encoding.Unicode.GetBytes (name));
+ buffer.WriteByte (0);
+ Align (buffer, 4);
+
+ writer.SetSymAttribute (method_token.ToUInt32 (), "MD2", buffer.length, buffer.buffer);
+ }
+
+ static void Align (ByteBuffer buffer, int align)
{
- var sym_token = new SymbolToken (symbols.MethodToken.ToInt32 ());
+ align--;
+ var position = buffer.position;
+ buffer.WriteBytes (((position + align) & ~align) - position);
+ }
- writer.OpenMethod (sym_token);
+ public void Write (MethodSymbols symbols)
+ {
+ var method_token = symbols.MethodToken;
+ writer.OpenMethod (method_token.ToUInt32 ());
DefineSequencePoints (symbols);
DefineVariables (symbols);
+
+ if (!string.IsNullOrEmpty (symbols.IteratorType))
+ DefineIteratorType (method_token, symbols.IteratorType);
+
writer.CloseMethod ();
}
@@ -205,7 +316,7 @@ namespace Mono.Cecil.Pdb {
writer.OpenScope (start_offset);
- var sym_token = new SymbolToken (symbols.LocalVarToken.ToInt32 ());
+ var sym_token = symbols.LocalVarToken.ToUInt32 ();
var variables = symbols.Variables;
for (int i = 0; i < variables.Count; i++) {
diff --git a/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs b/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs
index f3f4e1c..11e800e 100644
--- a/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs
+++ b/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs
@@ -74,10 +74,22 @@ namespace Mono.Cecil.Pdb
return debug_info;
}
+ public void SetSymAttribute (uint method, string name, int length, byte [] data)
+ {
+ var handle = GCHandle.Alloc (data, GCHandleType.Pinned);
+
+ try {
+ m_writer.SetSymAttribute (method, name, (uint) length, handle.AddrOfPinnedObject ());
+ } finally {
+ if (handle.IsAllocated)
+ handle.Free ();
+ }
+ }
+
public void DefineLocalVariable2 (
string name,
FieldAttributes attributes,
- SymbolToken sigToken,
+ uint sigToken,
SymAddressKind addrKind,
int addr1,
int addr2,
@@ -136,7 +148,7 @@ namespace Mono.Cecil.Pdb
m_writer.Initialize (emitter, filename, null, fFullBuild);
}
- public void OpenMethod (SymbolToken method)
+ public void OpenMethod (uint method)
{
m_writer.OpenMethod (method);
}
diff --git a/symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs b/symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs
index 60d93a4..05534af 100644
--- a/symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs
+++ b/symbols/pdb/Test/Mono.Cecil.Tests/PdbTests.cs
@@ -121,6 +121,18 @@ namespace Mono.Cecil.Tests {
Assert.AreEqual ("temp", method.Body.Variables [0].Name);
}
+ [TestModule ("iterator.exe", SymbolReaderProvider = typeof (PdbReaderProvider), SymbolWriterProvider = typeof (PdbWriterProvider))]
+ public void Iterator (ModuleDefinition module)
+ {
+ var type = module.GetType ("Program");
+ var method = type.GetMethod ("GetLittleArgs");
+
+ var iterator_type = method.Body.IteratorType;
+
+ Assert.IsNotNull (iterator_type);
+ Assert.AreEqual ("<GetLittleArgs>d__0", iterator_type.Name);
+ }
+
static void AssertCode (string expected, MethodDefinition method)
{
Assert.IsTrue (method.HasBody);
diff --git a/symbols/pdb/Test/Resources/assemblies/iterator.cs b/symbols/pdb/Test/Resources/assemblies/iterator.cs
new file mode 100644
index 0000000..4fa881c
--- /dev/null
+++ b/symbols/pdb/Test/Resources/assemblies/iterator.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+class Program {
+
+ static IEnumerable<string> GetLittleArgs (string [] args)
+ {
+ for (int i = 0; i < args.Length; i++) {
+ Debugger.Break ();
+ var little_arg = args [i].ToLower ();
+ yield return little_arg;
+ }
+ }
+
+ static void Main (string [] args)
+ {
+ foreach (var arg in GetLittleArgs (args))
+ Console.WriteLine (arg);
+ }
+}
diff --git a/symbols/pdb/Test/Resources/assemblies/iterator.exe b/symbols/pdb/Test/Resources/assemblies/iterator.exe
new file mode 100644
index 0000000..d6f2e77
Binary files /dev/null and b/symbols/pdb/Test/Resources/assemblies/iterator.exe differ
diff --git a/symbols/pdb/Test/Resources/assemblies/iterator.pdb b/symbols/pdb/Test/Resources/assemblies/iterator.pdb
new file mode 100755
index 0000000..f5b2612
Binary files /dev/null and b/symbols/pdb/Test/Resources/assemblies/iterator.pdb differ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment