如何通过堆栈跟踪访问本地?(模仿动态范围)

Dar*_*rio 7 .net c# scope dynamic-languages

背景

即使可以在运行时编译C#代码,也不可能在当前范围中包含和运行生成的代码.相反,所有变量都必须作为显式参数传递.

与像Python这样的动态编程语言相比,人们永远无法真正复制完整的行为eval(如本例所示).

x = 42
print(eval("x + 1")) # Prints 43
Run Code Online (Sandbox Code Playgroud)

这个问题

所以我的问题是(无论它是否真的有用;))是否可以通过使用反射来模拟.NET中的动态范围.

由于.NET为我们提供了Diagnostics.StackTrace允许我们检查调用方法的类,因此这个问题归结为以下内容:( 如何)可以可靠地访问调用方法的本地

堆栈跟踪是否为我们提供了足够的信息来计算内存偏移量,或者托管代码中是否禁止这样做?

这样的代码是否有可能?

void Foo() {
   int x = 42;
   Console.WriteLine(Bar());
}

int Bar() {
   return (int)(DynamicScope.Resolve("x")); // Will access Foo's x = 42
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 8

所以我的问题是,是否可以通过使用反射来模拟.NET中的动态范围.

反射使得其在所表达的项目的运行时操作的元数据的的组件.

局部变量不是在程序集的元数据中表示的项.

因此,你的问题的答案是"不".

(如何)是否可以可靠地访问调用方法的本地?

访问调用方法的本地的设备称为"调试器".所以你的问题的答案是"自己写一个调试器".

请注意,调试器无法可靠地访问具有代码优化的世界中的本地.本地可以被优化掉,抖动可以生成代码,这些代码对两个不同的本地使用相同的寄存器,在尾调用期间可以重复使用堆栈帧,等等.当然,您最好使用PDB文件告诉调试器与每个堆栈帧位置关联的名称是什么.

您的方法必须做的是将自定义调试器作为新进程启动,然后等待调试器向其发送消息.然后调试器将挂起原始进程的线程,查询堆栈帧,然后恢复进程的线程.然后它会向调试对象发送包含您发现的信息的消息.由于调试对象正处于等待状态等待消息,因此它将恢复其业务.

如果您想要的是一个进程内 "eval"功能,请考虑JScript.NET,这是一种专为此类设计的语言.


Tom*_*cek 5

这是不可能的.在编译的.NET代码(中间语言)中,变量简单地表示为堆栈上的索引.例如,加载变量值ldloc指令只接受一个unsigned int16值作为参数.对于在调试模式下编译的应用程序,可能有某种方法可以执行此操作(毕竟,在调试应用程序时,Visual Studio会这样做),但它通常无法正常工作.

在Phalanger(用于.NET的PHP编译器,我部分参与其中),这必须以某种方式解决,因为PHP语言具有eval(它不需要提供动态范围,但它需要按名称访问变量).因此,Phalanger检测函数是否包含任何使用,eval如果有,它将所有变量存储在中Dictionary<string, object>,然后传递给eval函数(以便它可以按名称读取变量).我担心这是唯一的方法......

  • 有时最终的陈述是心理障碍,有时它们是真理(在数学意义上的真理).我的答案当然有一些隐含的期望(例如你不会修改.NET运行时而你不会编写自己的C#编译器),但没有这些期望,你就无法给出任何答案,如果你在每个答案中重申它们,你听起来很傻.在任何情况下,如果您不喜欢答案,可以将其投票. (4认同)