Reputation: 399
I'm trying to create simple .net compiler for educational purposes. After parsing, scanning and building AST I'm generating .net assembly using Reflection.Emit.ILGenerator
.
Here is my sample code for assembly generation:
static void Main(string[] args)
{
string moduleName = "Test.exe";
AssemblyName assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(moduleName));
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
TypeBuilder typeBuilder = moduleBuilder.DefineType("Program", TypeAttributes.Public);
MethodBuilder mainMethod = typeBuilder.DefineMethod(
"Main", MethodAttributes.Public | MethodAttributes.Static, typeof(void), System.Type.EmptyTypes);
ILGenerator il = mainMethod.GetILGenerator();
il.Emit(OpCodes.Ldstr, "Test!");
il.Emit(OpCodes.Call, typeof(System.Console).GetMethod("WriteLine", new System.Type[] { typeof(string) }));
il.Emit(OpCodes.Ret);
typeBuilder.CreateType();
moduleBuilder.CreateGlobalFunctions();
assemblyBuilder.SetEntryPoint(mainMethod);
assemblyBuilder.Save(moduleName);
}
Everything works fine, this code generates executable with following class:
using System;
public class Program
{
public Program()
{
}
public static void Main()
{
Console.WriteLine("Test!");
}
}
Next I'm creating simple third party library with single class which builds to ThirdPartyLibrary.dll:
using System;
namespace ThirdPartyLibrary
{
public class MyPrint
{
public static void Print(string s)
{
Console.WriteLine("Third party prints: " + s);
}
}
}
Now I wish to replace Console.WriteLine
method call to MyPrint.Print
method call from my library and get result code something like:
using System;
using ThirdPartyLibrary;
public class Program
{
public Program()
{
}
public static void Main()
{
MyPrint.Print("Test!");
}
}
As far as I understand I must read my ThirdPartyLibrary.dll
file, then reflect it somehow to get all types from it and then it will be possible to use MyPrint
type. Finally I wish to be able to path references as myCompiler.exe arguments like using csc.exe.
So the questions are:
Upvotes: 3
Views: 2113
Reputation: 1062975
The only change here is the target method:
var targetMethod = Assembly.LoadFrom("ThirdPartyLibrary.dll")
.GetType("ThirdPartyLibrary.MyPrint")
.GetMethod("Print", new [] {typeof(string)});
...
il.Emit(OpCodes.Ldstr, "Test!");
il.Emit(OpCodes.Call, targetMethod);
il.Emit(OpCodes.Ret);
So instead of using Console.WriteLine
, we call MyPrint.Print
.
IKVM.Reflection.dll
in a few places, because it allows me to do cross-framework targeting, but you probably don't need that. What you might find handy is Sigil, which takes away a lot of the pain of IL-generation (unbalanced stacks, etc) by giving you sane error reports at the time you generate (rather than when you execute). Mono.Cecil is also interestingUpvotes: 4