Reflection.Emit 中的函数调用
Function Calls in Reflection.Emit
我目前正在用 C# 编写编程语言。我对如何以动态方式执行函数调用感到困惑。我现在确定我将如何调用用户定义的函数。我知道要输出 "hello world" 需要这样的东西:
ilg.Emit(OpCodes.Ldstr, "Hello, World!");
ilg.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
new Type[] {typeof(string)} ));
但是如果有自定义函数怎么办?
最好的(或任何)方法是什么?
你可以传入一个MethodBuilder
作为Emit的参数,因为MethodBuilder继承自MethodInfo,所以调用时会调用正确的方法。使用您的玩具程序,def hello(string msg) { print(msg); } hello("Hello!");
,这里展示了如何为此发出代码:
ILGenerator ilg;
var asmName = new AssemblyName("DynamicAssembly");
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndCollect);
var modBuilder = asmBuilder.DefineDynamicModule("DynamicAssembly");
var type = modBuilder.DefineType("<>CompilerFunctionClass", TypeAttributes.Class | TypeAttributes.Public);
type.DefineDefaultConstructor(MethodAttributes.Public);
var helloBuilder = type.DefineMethod("hello", MethodAttributes.Family | MethodAttributes.Static, typeof(void), new[] { typeof(string) });
// emitting code for hello later
var mainBuilder = type.DefineMethod("Main", MethodAttributes.Public);
ilg = mainBuilder.GetILGenerator();
ilg.Emit(OpCodes.Ldstr, "Hello, World!");
ilg.Emit(OpCodes.Call, helloBuilder);
ilg.Emit(OpCodes.Ret);
// Here we emit the code for hello.
ilg = helloBuilder.GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
new Type[] { typeof(string) }));
ilg.Emit(OpCodes.Ret);
// just to show it works.
var t = type.CreateType();
dynamic d = Activator.CreateInstance(t);
d.Main(); // prints Hello, World!
您的编译器可能会首先发现所有顶级函数名称,并为它们定义方法,然后它可以为每个函数生成代码。
请注意,Reflection.Emit 适用于玩具示例和学习项目,但它的功能不足以完成 full-fledged 编译器所需的工作。请参阅注释 here by Eric Lippert. He suggests using the Common Compiler Infrastructure 以构建编译器。我没用过,不好说。
我目前正在用 C# 编写编程语言。我对如何以动态方式执行函数调用感到困惑。我现在确定我将如何调用用户定义的函数。我知道要输出 "hello world" 需要这样的东西:
ilg.Emit(OpCodes.Ldstr, "Hello, World!");
ilg.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
new Type[] {typeof(string)} ));
但是如果有自定义函数怎么办?
最好的(或任何)方法是什么?
你可以传入一个MethodBuilder
作为Emit的参数,因为MethodBuilder继承自MethodInfo,所以调用时会调用正确的方法。使用您的玩具程序,def hello(string msg) { print(msg); } hello("Hello!");
,这里展示了如何为此发出代码:
ILGenerator ilg;
var asmName = new AssemblyName("DynamicAssembly");
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndCollect);
var modBuilder = asmBuilder.DefineDynamicModule("DynamicAssembly");
var type = modBuilder.DefineType("<>CompilerFunctionClass", TypeAttributes.Class | TypeAttributes.Public);
type.DefineDefaultConstructor(MethodAttributes.Public);
var helloBuilder = type.DefineMethod("hello", MethodAttributes.Family | MethodAttributes.Static, typeof(void), new[] { typeof(string) });
// emitting code for hello later
var mainBuilder = type.DefineMethod("Main", MethodAttributes.Public);
ilg = mainBuilder.GetILGenerator();
ilg.Emit(OpCodes.Ldstr, "Hello, World!");
ilg.Emit(OpCodes.Call, helloBuilder);
ilg.Emit(OpCodes.Ret);
// Here we emit the code for hello.
ilg = helloBuilder.GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
new Type[] { typeof(string) }));
ilg.Emit(OpCodes.Ret);
// just to show it works.
var t = type.CreateType();
dynamic d = Activator.CreateInstance(t);
d.Main(); // prints Hello, World!
您的编译器可能会首先发现所有顶级函数名称,并为它们定义方法,然后它可以为每个函数生成代码。
请注意,Reflection.Emit 适用于玩具示例和学习项目,但它的功能不足以完成 full-fledged 编译器所需的工作。请参阅注释 here by Eric Lippert. He suggests using the Common Compiler Infrastructure 以构建编译器。我没用过,不好说。