This project is read-only.

Pseudo-source code

Topics: Metadata Model, PDB Writer, PE Writer, Source Model
May 2, 2010 at 1:28 AM
Edited May 2, 2010 at 1:28 AM

Hello.

Is it possible to make CCI generate a fake source code file for an assembly before writing it do disk and use that source file to generate debug symbols?

I mean, you create an assembly in some way (reading a pre-existing assembly, mutating a preexisting assembly, using code model, etc) then you generate the source file (as C# or CIL), you update the assembly setting the location of each metadata element to point to this fake file and then you write both the assembly and the debugging symbols.

It would help to debug synthetic assemblies in visual studio.

Thanks.

 

May 2, 2010 at 5:57 AM

Maybe I found a way: I'm trying to do something like that modifying your C# source code emitter (CSharpSourceEmitter).

 

May 2, 2010 at 10:08 PM

The only thing I miss is: how do I make a ILocalScopeProvider for my assembly?

Usually you use a PdbReader...

May 3, 2010 at 12:00 AM
Edited May 3, 2010 at 12:00 AM

Why Ast.MethodBody (which is a ISourceMethodBody) exposes a LocalScopes property and MutableCodeModel.SourceMethodBody doesn't?

Is it wise to add that property to MutableCodeModel.SourceMethodBody and implement an ILocalScopeProvider for MutableCodeModel along the lines of Ast.LocalScopeProvider?

 

May 4, 2010 at 6:14 PM

 

I've seen class ILDasmPrettyPrinter in file PrettyPrinter.cs

 

 

May 5, 2010 at 2:50 AM
Edited May 5, 2010 at 2:51 AM

C# code is easier to debug that IL. Is there any advantage in using ILDasmPrettyPrinter?

May 5, 2010 at 8:02 AM

If you start with a CodeModel, or if you decompile your MetadataModel to a CodeModel, you can use the CSharpSourceEmitter assembly (hiding away in the tests folder of the ast solution in cciast.codeplex.com) to generate a source file that represents your generated code. Depending on how simple your model is, the result may be valid C# and you could compile it to an assembly and PDB file using the C# compiler.

The ILDasmPrettyPrinter is also primarily an aid for writing metadata reader tests that compare actual metadata models with expected models, by serializing both to text and comparing the text.

A technique often used to debug generated assemblies is to use ildasm to convert it to a source file and then ilasm to compile it again, but this time with debugging symbols. This has the advantage of being able to handle anything you can tnrow at it.

If you want to avoid the post processing, your life becomes a little more difficult. You'll have to adapt the code in CSharpSourceEmitter to keep track of the locations in the generated source that correspond to interesting debug points and you'll have to proffer this up to the PeWriter in the form of an ISourceLocationProvider and an ILocalScopeProvider. All of this will require considerable coding on your part.

Eventually, I'd hope that the infrastructure will have much more support for this scenario, but for now you are pretty much on your own.

May 5, 2010 at 1:37 PM
Edited May 5, 2010 at 3:32 PM

"You'll have to adapt the code in CSharpSourceEmitter to keep track of the locations in the generated source that correspond to interesting debug points and you'll have to proffer this up to the PeWriter in the form of an ISourceLocationProvider and an ILocalScopeProvider. All of this will require considerable coding on your part."

Yes, this is what I was doing. Thanks.

"A technique often used to debug generated assemblies is to use ildasm to convert it to a source file and then ilasm to compile it again, but this time with debugging symbols. This has the advantage of being able to handle anything you can tnrow at it."

Ah, ok. This can be a real advantage when it's needed but I'm working with CodeModel so I should be able to live without it. I'm just generating an assembly of wrappers of other classes.

I was at good point implementing the solution based on CSharpSourceEmitter but I got stuck with the mutator craziness. I'll try again using the two new classes that you suggested in the other discussion.

 

May 6, 2010 at 12:20 AM
Edited May 6, 2010 at 12:21 AM

Herman, in half your answer you attempted to explain me that I had to do what I was already doing. You said:

"You'll have to adapt the code in CSharpSourceEmitter to keep track of the locations in the generated source that correspond to interesting debug points"

when I already wrote "Maybe I found a way: I'm trying to do something like that modifying your C# source code emitter (CSharpSourceEmitter)." and then you added: 

"and you'll have to proffer this up to the PeWriter in the form of an ISourceLocationProvider and an ILocalScopeProvider. All of this will require considerable coding on your part."

when in my previous messages I asked:

"Why Ast.MethodBody (which is a ISourceMethodBody) exposes a LocalScopes property and MutableCodeModel.SourceMethodBody doesn't?

Is it wise to add that property to MutableCodeModel.SourceMethodBody and implement an ILocalScopeProvider for MutableCodeModel along the lines of Ast.LocalScopeProvider?"

Of course I knew I had to proffer that information up to the PeWriter in the form of an ISourceLocationProvider and an ILocalScopeProvide, otherwise why would I ask you about LocalScopes in this context.

But in the end my questions remained unanswered:

1) Why Ast.MethodBody (which is a ISourceMethodBody) exposes a LocalScopes property and MutableCodeModel.SourceMethodBody doesn't?

I modified slightly MutableCodeModel.SourceMethodBody and related classes to expose that information. Is it ok? Should I live with an hacked version of MutableCodeModel.SourceMethodBody? Should these changes go in your version of CCI too?

 Thanks.

May 15, 2010 at 1:45 AM

I think you're right in expecting the mutable CodeModel to provide a LocalScopes property. Feel free to submit your changes as a patch.

May 18, 2010 at 1:38 AM

This is the patch with the changes I needed to make to both metadata and code/ast in order to expose the information required for this task.

I made this with Tortoise SVN from the Cci Ast project root directory, after updating my copy of the sources. The ILocalScopeProvider implementation that I'm using is not included.

 

Index: Metadata/Sources/ILGenerator/ILGenerator.cs
===================================================================
--- Metadata/Sources/ILGenerator/ILGenerator.cs	(revision 46501)
+++ Metadata/Sources/ILGenerator/ILGenerator.cs	(working copy)
@@ -36,6 +36,14 @@
     Stack scopeStack = new Stack(); //TODO: write own stack so that dependecy on System.dll can go.
     Stack tryBodyStack = new Stack();
 
+      public IEnumerable NamespaceScopes {
+          get {
+              foreach (var ilGeneratorScope in scopeStack) {
+                  yield return ilGeneratorScope;
+              }
+          }
+      }
+
     /// 
     /// Allocates an object that helps with the generation of Microsoft intermediate language (MSIL) instructions corresponding to a method body.
     /// 
Index: Sources/CodeModelToIL/Visitor.cs
===================================================================
--- Sources/CodeModelToIL/Visitor.cs	(revision 46501)
+++ Sources/CodeModelToIL/Visitor.cs	(working copy)
@@ -22,6 +22,12 @@
   /// 
   public class CodeModelToILConverter : BaseCodeAndContractTraverser, ISourceToILConverter {
 
+            public IEnumerable NamespaceScopes {
+          get {
+                return generator.NamespaceScopes;
+          }
+      }
+
     /// 
     /// Initializes an object with a method that converts a given block of statements to a list of IL operations, exception information and possibly some private 
     /// helper types.
Index: Sources/MutableCodeModel/Members.cs
===================================================================
--- Sources/MutableCodeModel/Members.cs	(revision 46501)
+++ Sources/MutableCodeModel/Members.cs	(working copy)
@@ -79,19 +79,51 @@
       visitor.Visit(this);
     }
 
+
+
+
+     IEnumerable localScopes;
+     public IEnumerable LocalScopes {
+          get {
+                if (!this.ilWasGenerated) this.GenerateIL();
+              return localScopes;
+          }
+      }
+           IEnumerable namespaceScopes;
+     public IEnumerable NamespaceScopes {
+          get {
+                if (!this.ilWasGenerated) this.GenerateIL();
+              return namespaceScopes;
+          }
+      }
+          public bool IsIteratorBody {
+      get {
+        if (!this.ilWasGenerated) this.GenerateIL();
+        return this.isIteratorBody;
+      }
+    }
+    bool isIteratorBody;
+
+
     private void GenerateIL() {
       IEnumerable localVariables;
       ushort maxStack;
+      IEnumerable localScopes;
+        IEnumerable namespaceScopes;
       IEnumerable iteratorScopes;
       IEnumerable operations;
       IEnumerable operationExceptionInformation;
       List/*?*/ privateHelperTypes = this.privateHelperTypes;
+        bool isIteratorBody = false;
 
       if (this.isNormalized) {
         var converter = new CodeModelToILConverter(this.host, this.MethodDefinition, this.sourceLocationProvider, this.contractProvider, this.iteratorLocalCount);
         converter.ConvertToIL(this.Block);
+          localScopes = converter.GetLocalScopes();
+          namespaceScopes = converter.NamespaceScopes;
         iteratorScopes = converter.GetIteratorScopes();
         localVariables = converter.GetLocalVariables();
+
         maxStack = converter.MaximumStackSizeNeeded;
         operations = converter.GetOperations();
         operationExceptionInformation = converter.GetOperationExceptionInformation();
@@ -102,6 +134,9 @@
         var normalizedBody = (SourceMethodBody)normalizer.GetNormalizedSourceMethodBodyFor(this.MethodDefinition, this.Block);
         normalizedBody.isNormalized = true;
         iteratorScopes = normalizedBody.iteratorScopes;
+          localScopes = normalizedBody.LocalScopes;
+          namespaceScopes = normalizedBody.NamespaceScopes;
+          isIteratorBody = normalizedBody.IsIteratorBody;
         localVariables = normalizedBody.LocalVariables;
         maxStack = normalizedBody.MaxStack;
         operations = normalizedBody.Operations;
@@ -115,6 +150,9 @@
       lock (this) {
         if (this.ilWasGenerated) return;
         this.ilWasGenerated = true;
+          this.localScopes = localScopes;
+          this.namespaceScopes = namespaceScopes;
+          this.isIteratorBody = isIteratorBody;
         this.iteratorScopes = iteratorScopes;
         this.localVariables = localVariables;
         this.maxStack = maxStack;

 

I have no experience with patches. Please tell me if it's not good enough.