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

背景

尽管可以在运行时编译C#代码,但不可能在当前作用域中包含和运行生成的代码。 相反,所有变量都必须作为显式参数传递。

与Python等动态编程语言相比,人们永远无法真正复制eval的完整行为(如本例中)。

x = 42
print(eval("x + 1")) # Prints 43

这个问题

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

由于.NET为我们提供了Diagnostics.StackTrace类,它允许我们检查调用方法,所以这个问题归结为以下几点: (How)是否可以可靠地访问调用方法的本地语言

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

这种代码是否有可能?

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

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

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

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


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

反射可以对组件的元数据中表示的项目进行运行时操作。

局部变量不是在程序集的元数据中表达的项目。

因此你的问题的答案是“不”。

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

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

请注意,调试器不能可靠地访问具有代码优化的世界中的本地人。 当地人可以优化,抖动可以生成代码,使用相同的寄存器为两个不同的本地人,堆栈帧可以重新使用尾调用,等等。 当然,你最好有PDB文件告诉调试器与每个堆栈帧位置相关的名称是什么。

你的方法必须做的是启动你的自定义调试器作为一个新的进程,然后等待调试器发送一条消息。 调试器会暂停原始进程的线程,执行堆栈帧的询问,然后恢复进程的线程。 然后它会将消息发送到包含您发现的信息的调试对象。 由于调试对象正处于等待状态,等待消息,它将恢复业务。

如果你想要的是一个正在进行的“eval”功能,可以考虑JScript.NET,这是一种专门为此设计的语言。

链接地址: http://www.djcxy.com/p/72315.html

上一篇: How to access locals through stack trace? (Mimicking dynamic scope)

下一篇: Most dynamic dynamic programming language