gpt4 book ai didi

c# - DivideByZeroException 编译器检查复杂度 : easier or harder in MSIL vs C# or no difference?

转载 作者:太空狗 更新时间:2023-10-29 18:19:44 25 4
gpt4 key购买 nike

这是一个与this fascinating question about detecting divide by zero exceptions at compile time相关的问题.

从 Eric Lippert 的回答来看,正确实现这一点并非易事(我想这就是为什么还没有提供的原因)。

我的问题是:

无论语言的“级别”如何,进行这些类型的检查的难度是否相同,例如高水平 vs 低水平?

具体来说,C# 编译器将 C# 转换为 MSIL。作为某种二次通过检查的一部分,这些类型的检查在 MSIL 级别会更容易还是更难?

或者,语言本身根本没有什么区别?

阅读 Eric 的回答中列出的问题,我认为检查必须在任何语言中都相同?例如,您可以使用多种语言进行跳转,因此需要实现 Eric 描述的流程检查......?

只是为了保持这个问题的具体性,这种检查在 MSIL 中是否比在 C# 中更容易或更难?

最佳答案

这是一个非常有趣和深刻的问题——尽管这个问题可能不太适合这个网站。

如果我理解的话,问题是在进行静态分析以追求缺陷时对分析语言的选择有何影响;分析器应该查看 IL,还是应该查看源代码?请注意,我已经从最初对被零除缺陷的狭隘关注扩展了这个问题。

答案当然是:视情况而定。这两种技术在静态分析行业中都很常用,而且各有优缺点。这取决于您正在寻找什么缺陷、您使用什么技术来修剪错误路径、抑制误报和推断缺陷,以及您打算如何向开发人员展示发现的缺陷。

分析字节码比源代码有一些明显的好处。主要的一点是:如果你有一个用于 Java 字节码的字节码分析器,你可以通过它运行 Scala,而无需编写 Scala 分析器。如果您有 MSIL 分析器,则可以通过它运行 C# 或 VB 或 F#,而无需为每种语言编写分析器。

在字节码级别分析代码也有好处。当您拥有字节码时,分析控制流非常容易,因为您可以非常快速地将字节码块组织成“基本块”;基本块是一个代码区域,其中没有指令分支到其中间,并且块的每个正常退出都在其底部。 (异常当然可以发生在任何地方。)通过将字节码分解为基本块,我们可以计算出相互分支的块图,然后根据每个块对局部和全局状态的 Action 来总结每个块。字节码很有用,因为它是对代码的抽象,可以在较低级别显示真正发生的事情。

这当然也是它的主要缺点; 字节码丢失有关开发人员意图的信息 .任何需要从源代码获取信息以检测缺陷或防止误报的缺陷检查器在字节码上运行时都会产生不良结果。例如考虑一个 C 程序:

#define DOBAR if(foo)bar();
...
if (blah)
DOBAR
else
baz();

如果这个可怕的代码被降级为机器码或字节码,那么我们将看到的只是一堆分支指令,我们不知道我们应该在这里报告一个缺陷,即 else绑定(bind)到 if(foo)而不是 if(blah)如开发商所愿。

C 预处理器的危险是众所周知的。但是在字节码级别分析复杂的降低代码时也存在很大的困难。例如,考虑像 C# 这样的东西:
async Task Foo(Something x) 
{
if (x == null) return;
await x.Bar();
await x.Blah();
}

说白了 x不能在此处取消引用为 null。但是 C# 会将其降低为一些绝对疯狂的代码;该代码的一部分将如下所示:
int state = 0;
Action doit = () => {
switch(state) {
case 0:
if (x == null) {
state = -1;
return;
};
state = 1;
goto case 1:
case 1:
Task bar = x.Bar();
state = 2;
if (<bar is a completed task>) {
goto case 2;
} else {
<assign doit as the completion of bar>
return;
}
case 2:

等等。 (除了它比那复杂得多。)然后这将被降低为更抽象的字节码;想象一下,试图在将开关降低到 goto 和委托(delegate)降低到闭包的级别上理解此代码。

分析等效字节码的静态分析器完全有权利说“显然 x 可以为空,因为我们在交换机的一个分支上检查它;这是必须在其他分支上检查 x 为空的证据,它是不是,因此我将在其他分支上给出空取消引用缺陷”。

但这将是一个误报。我们知道静态分析器可能不知道的一些事情,即零状态必须在每个其他状态之前执行,并且当协程恢复时,x 将始终已经被检查为空。这从原始源代码中很明显,但很难从字节码中梳理出来。

如果您希望在没有缺点的情况下获得字节码分析的好处,那么您会怎么做?有多种技术;例如,您可以编写自己的比字节码更高级别的中间语言——它具有“yield”或“await”或“for循环”等高级结构——编写一个分析器来分析该中间语言,并且然后编写编译器,将每种目标语言——C#、Java 等等——编译成你的中间语言。这意味着编写很多编译器,但只编写一个分析器,也许编写分析器是最难的部分。

我知道,那是一次非常简短的讨论。这是一个复杂的话题。

如果你对字节码的静态分析器的设计感兴趣,可以考虑研究 Infer 的设计,它是一个开源的 Java 和其他语言的静态分析器,它将 Java 字节码转换成适合分析堆属性的更低级别的字节码;首先阅读用于推断堆属性的分离逻辑。 https://github.com/facebook/infer

关于c# - DivideByZeroException 编译器检查复杂度 : easier or harder in MSIL vs C# or no difference?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48171995/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com