c# - 为什么这段代码无法验证?

标签 c# .net mono cil peverify

我有一个可以正确构建和运行的编译器,但 PEVerify 在某个时刻称它为无法验证。仔细查看错误、相应的源代码和问题点的 ILDasm 输出后,我找不到问题,以至于我怀疑 PEVerify 中的错误,除了 .NET 和 Mono 版本在同一个地方报告同样的错误。

The problematic method全文如下:

internal static bool InAsyncMethod(Expression value)
{
    INodeWithBody ancestor = value.GetAncestor<BlockExpression>() ?? (INodeWithBody) value.GetAncestor<Method>();
    return ContextAnnotations.IsAsync(ancestor);
}

报错如下:

[IL]: Error: [D:\SDL-1.3.0-4423\boo\build\Boo.Lang.Compiler.dll : Boo.Lang.Compiler.TypeSystem.AsyncHelper::InAsyncMethod][offset 0x00000011][found ref 'Boo.Lang.Compiler.Ast.Node'][expected ref Boo.Lang.Compiler.Ast.INodeWithBody'] Unexpected type on the stack.

偏移量 0x11对应于??的后半部分表达。来自 ILDasm:

.method assembly hidebysig static bool  InAsyncMethod(class Boo.Lang.Compiler.Ast.Expression 'value') cil managed
{
  // Code size       29 (0x1d)
  .maxstack  2
  .locals init ([0] class Boo.Lang.Compiler.Ast.INodeWithBody ancestor,
           [1] bool CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  callvirt   instance !!0 Boo.Lang.Compiler.Ast.Node::GetAncestor<class Boo.Lang.Compiler.Ast.BlockExpression>()
  IL_0007:  dup
  IL_0008:  brtrue.s   IL_0011
  IL_000a:  pop
  IL_000b:  ldarg.0
  IL_000c:  callvirt   instance !!0 Boo.Lang.Compiler.Ast.Node::GetAncestor<class Boo.Lang.Compiler.Ast.Method>()
  IL_0011:  stloc.0
  IL_0012:  ldloc.0
  IL_0013:  call       bool Boo.Lang.Compiler.Steps.ContextAnnotations::IsAsync(class Boo.Lang.Compiler.Ast.INodeWithBody)
  IL_0018:  stloc.1
  IL_0019:  br.s       IL_001b
  IL_001b:  ldloc.1
  IL_001c:  ret
} // end of method AsyncHelper::InAsyncMethod

Boo.Lang.Compiler.Ast.Node class 是所有 AST 节点的基类。 BlockExpressionMethod分别是 lambda 和方法的节点类,它们都实现了 INodeWithBody界面。一切看起来都是正确的,无论是在 C# 中(如果存在类型问题就不会构建)和 IL 中(它在 000c 处表示 GetAncestor<Method> 调用的返回类型是 !!0,第一个类型参数在方法调用上。)

是什么导致 PEVerify 认为它正在处理类型为 Node 的值?在这里,当它显然具有 Method 类型的值时?有什么办法可以解决吗?

最佳答案

What is causing PEVerify to think it's dealing with a value of type Node here when it clearly has a value of type Method?

正如 Stephane Delcroix 所指出的,有两条代码路径可以到达 IL_0011。 ,因为它是分支的目标。它显然不一定具有 Method 类型的值.

GetAncestor<TAncestor>返回 TAncestor .你有 GetAncestor<BlockExpression>的结果,或 GetAncestor<Method>的结果,所以 BlockExpressionMethod .两者都实现 INodeWithBody ,所以从逻辑上讲,代码仍然没问题。

不幸的是,“BlockExpressionMethod”对于验证来说太多了。这被简化为 Node (公共(public)基础),实现INodeWithBody .请参阅 ECMA-335 §III.1.8.1.3:

The merged type, U, shall be computed as follows (recall that S := T is the compatibility function defined in §III.1.8.1.2.2):

  1. if S := T then U=S

  2. Otherwise, if T := S then U=T

  3. Otherwise, if S and T are both object types, then let V be the closest common supertype of S and T then U=V.

  4. Otherwise, the merge shall fail.

如果您检查 C# 编译器的功能,您会看到它发出一个 stloc.0/ldloc.0组合成一个本地类型 INodeWithBodydup 之前.这使得一切正常,因为 INodeWithBody 的常见类型和 Method然后是INodeWithBody .

关于c# - 为什么这段代码无法验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44095408/

相关文章:

c# - 是否存在可以在编译的 IL 中以某种方式引用属性的情况?

.net - 带有图像和文本的 WinForms 按钮

c# - Linux 上单声道中的 WLAN 信息

c# - Windows 窗体将 List 传递给新窗体

c# - 如何中断 Console.ReadLine

c# - 动态程序集中不支持调用的成员。

dll - 找不到类型或命名空间名称 `HolidayService2'。您是否缺少程序集引用?

c# - Request.Params 和 Request.Form 什么时候不同?

c# - 优化 C# 中的 HTML 标记删除

c# - 匹配 1-5000 之间数字的正则表达式