Created
April 24, 2010 20:00
-
-
Save jbevain/377904 to your computer and use it in GitHub Desktop.
A half working patch to support iterators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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