This project is read-only.

Debugging ReflectionEmitter generated assembly

Topics: Metadata Model, PDB Reader, PE reader
Jul 6, 2011 at 5:49 PM

Hi, The ReflectionEmmiter looks like it could be a really powerful tool for scripting with c#!

Some gremlins I have found related to debugging:

I can't inspect method parameters in the debugger. PDBReader seems to have no knowledge of method parameters, so I was wondering how this works when VS is debugging regular assemblies? Originally I assumed they would be listed with other local variables in the pdb (shows how much I know).

There is also a problem with namespace scoping but I think it's an issue with the PDBReader

Cheers,

Joe

Jul 6, 2011 at 7:12 PM

I'll need to investigate this some more, since the ISymUnmanagedWriter interface does have a method for emitting PDB information for parameters.

It seems, however, that calling this is not strictly required since the metadata table for parameters does have a column for parameter names and the VS debugger presumably gets to look at this. At any rate, I have not noticed problem with debugging.

Could you perhaps construct a small test case that illustrates your problem?

Jul 7, 2011 at 1:12 PM

Cheers Herman,

I can't seem to attach any files to my post now so here it is. The script project should be setup to build into the hosts debug folder.

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.Cci;
using Microsoft.Cci.ReflectionEmitter;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            var script = LoadAssembly(Path.Combine(Environment.CurrentDirectory, "Script.dll"));
            var testClass = script.GetType("Script.TestClass");
            var testMethod = testClass.GetMethod("Print");

            testMethod.Invoke(
                Activator.CreateInstance(testClass),
                new object[] 
                {
                    "Hello World"
                });
        }

        static Assembly LoadAssembly(string fileName)
        {
            Assembly assembly;

            using (var host = new PeReader.DefaultHost())
            {
                PdbReader pdbReader = null;

                var pdbFileName = Path.ChangeExtension(fileName, "pdb");
                if (File.Exists(pdbFileName))
                {
                    using (var stream = new FileStream(pdbFileName, FileMode.Open))
                    {
                        pdbReader = new PdbReader(stream, host);
                    }
                }

                var reader = new PeReader(host);
                var loader = new DynamicLoader(pdbReader, pdbReader);

                assembly = loader.Load(reader.OpenAssembly(BinaryDocument.GetBinaryDocumentForFile(fileName, host)));

                pdbReader.Dispose();
            }

            return assembly;
        }
    }
}

 

-----------------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Script
{
    public class TestClass
    {
        public void Print(string parameter /* Can't inspect this parameter */) 
        {
            System.Diagnostics.Debugger.Break();

            // Can inspect this local variable
            var local = parameter;

            // The same applies to the locals window (Debug > Windows > Locals)

            Console.WriteLine(parameter);
            Console.WriteLine(local);
        }
    }
}

Jul 9, 2011 at 2:00 AM

PDBReader was a bit of Red Herring. The real problem was that Reflection.Emit.MethodBuilder.DefineParameter requires all parameter indices to be offset by 1 so that parameter index 0 can refer to the return value. The CCI code was using 0 based indexing. This is fixed now.

Jul 11, 2011 at 10:49 AM

Many thanks!