views:

450

answers:

5

I recently bought Ayende's book Building DSLs in Boo (buy it, read it, it's awesome) but I'm coming up against an implementation problem and I want to see what the generated code looks like. I would normally use reflector to look at the code but in this case the assemblies are dynamic and only in memory. Is there a way to save dynamic assemblies to disk so that I can reflect them?

EDIT / My Answer:

Wow, it took awhile to come back to this one. Unfortunately I left an important bit out from the original question.

Important Bit: I'm using Ayende's RhinoDSL library as he recommends in the book. I have access to the boo compiler in my subclass of DslEngine which looks like this:

public class JobEngine : DslEngine
{
    protected override void CustomizeCompiler(Boo.Lang.Compiler.BooCompiler compiler, Boo.Lang.Compiler.CompilerPipeline pipeline, string[] urls)
    {
        pipeline.Insert(1, new ImplicitBaseClassCompilerStep(typeof (JobBase), "Prepare", "JobLanguage", "log4net", "Quartz"));
    }
}

To change the least and get what I wanted I needed to add one line...

public class JobEngine : DslEngine
{
    protected override void CustomizeCompiler(Boo.Lang.Compiler.BooCompiler compiler, Boo.Lang.Compiler.CompilerPipeline pipeline, string[] urls)
    {
        compiler.Parameters.GenerateInMemory = false; // <--- This one.
        pipeline.Insert(1, new ImplicitBaseClassCompilerStep(typeof (JobBase), "Prepare", "JobLanguage", "log4net", "Quartz"));
    }
}

This caused the compiler to output the assembly to my ~\LocalSettings\Temp directory and then I could then reflect it. It's important to note that making that change caused the rest of the program to break (RhinoDSL could no longer find the assemblies in memory because I output them to disk), so this is only useful as a debugging tool.

+3  A: 

Yes, the AssemblyBuilder class has a Save method for this purpose. You need to use the appropriate mode for this, which is most likely RunAndSave:

AssemblyBuilder builder =
    AppDomain.CurrentDomain.DefineDynamicAssembly(
        name, AssemblyBuilderAccess.RunAndSave);
// build assembly
builder.Save(path);
David M
The assembly is already built for me in memory by the Boo Compiler. So I don't have access to the builder...
Jason Punyon
It's a dynamic assembly, and if it hasn't been saved and loaded from disk, should cast to `AssemblyBuilder` in that case.
David M
@David M: Hmm...when I cast and hit the save method I get an InvalidOperationException saying that it's been saved.
Jason Punyon
A: 

If you can get the Assembly at runtime.

i.e.

Assembly assembly = typeof(YourDynamicType).Assembly;

You can then cast this assembly into AssemblyBuilder and call the Save method.

AssemblyBuilder assemblyBuilder = (AssemblyBuilder)assembly;
assemblyBuilder.Save(yourPath);
SelflessCoder
Actually the cast works, but when I run the save method it dies saying that the assembly has already been saved...
Jason Punyon
A: 

There may be an easier way to do it, but if you don't mind using WinDbg you can save any loaded managed assembly from memory (WinDbg uses the term module, but it works for managed assemblies as well).

Use the !savemodule command with the address of the assembly. If you don't have the address use the lm vm command with the module name. Following that you get a regular DLL assembly, that you can inspect in Reflector.

Of course you may also just look at the IL code in memory.

Brian Rasmussen
+2  A: 

Look up where BooCompiler is instantiated, change the pipeline from CompileToMemory to CompileToFile

Mauricio Scheffer
This is what ultimately led me to the answer I needed so 1 accept to you sir.
Jason Punyon
A: 

I tried the WinDBG option, with sos.dll extension. I found that I can list Dynamic Module loaded in memory. But I cannot write them to disk:

0:016> !SaveModule 0x03ef0000 c:\temp\out.tmp
Successfully saved file: c:\temp\out.tmp

(file isn't really saved into the filesystem)

Tha

Assembly: 0x001f2708 [Resources, Version=1.0.0.0, Culture=neutral] Dynamic Module: 0x00d89c28 loaded at: 0x03ef0000 Size: 0x00015c00(89,088)

Assembly: 0x00201198 [ICSharpCode.NRefactory, Version=3.0.0.3630, Culture=neutral] Dynamic Module: 0x037929a0 loaded at: 0x04250000 Size: 0x00079000(495,616)

Assembly: 0x0024f028 [ActiproSoftware.SyntaxEditor.Net20, Version=4.0.277.0, Culture=neutral] Dynamic Module: 0x0379c340 loaded at: 0x0cd60000 Size: 0x00101000(1,052,672)

Assembly: 0x0024f098 [ActiproSoftware.Shared.Net20, Version=1.0.96.0, Culture=neutral] Dynamic Module: 0x0379e080 loaded at: 0x0cf70000 Size: 0x00038000(229,376)

Assembly: 0x0024e3f8 [ActiproSoftware.WinUICore.Net20, Version=1.0.96.0, Culture=neutral] Dynamic Module: 0x0379ee7c loaded at: 0x0cfb0000 Size: 0x00029000(167,936)

Assembly: 0x0021d7b0 [ActiproSoftware.SyntaxEditor.Addons.DotNet.Net20, Version=4.0.277.0, Culture=neutral] Dynamic Module: 0x0d00db9c loaded at: 0x0c410000 Size: 0x0008d000(577,536)

Assembly: 0x0cbfabe8 [ICSharpCode.SharpDevelop.Dom, Version=3.0.0.3630, Culture=neutral] Dynamic Module: 0x0c3f4c40 loaded at: 0x0d070000 Size: 0x00053000(339,968)

--------------------------------------
Total 7 Dynamic Assemblies, Total size: 0x0(0) bytes.
jrojo