4

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

有问题的方法如下:

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

错误报告为:

[IL]:错误:[D:\SDL-1.3.0-4423\boo\build\Boo.Lang.Compiler.dll:Boo.Lang.Compiler.TypeSystem.AsyncHelper::InAsyncMethod][偏移量 0x00000011][找到参考'Boo.Lang.Compiler.Ast.Node'][expected ref Boo.Lang.Compiler.Ast.INodeWithBody'] 堆栈上的意外类型。

Offsest对应于表达式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是所有 AST 节点的基类。 BlockExpressionMethod分别是 lambda 和方法的节点类,它们都实现了INodeWithBody接口。一切看起来都是正确的,无论是在 C#(如果存在类型问题,它将不会构建)和 IL(它说调用000c的返回类型是GetAncestor<Method>!!0,方法调用的第一个类型参数。)

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

4

2 回答 2

3

Node当 PEVerify 显然具有type 值时,是什么导致它认为它在这里处理的是 type值Method

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

GetAncestor<TAncestor>返回TAncestor。你得到了GetAncestor<BlockExpression>的结果,或者GetAncestor<Method>的结果,所以要么BlockExpression要么Method。两者都实现INodeWithBody了,所以从逻辑上讲,代码仍然很好。

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

合并类型 ,U应按如下方式计算(回想一下,这S := T是在 §III.1.8.1.2.2 中定义的兼容性函数):

  1. 如果S := T那么U=S

  2. 否则,如果T := S那么U=T

  3. 否则,如果SandT都是对象类型,那么 let是Vand 的最接近的公共超S类型。TU=V

  4. 否则,合并将失败。

stloc.0如果你检查 C# 编译器做了什么,你会发现它ldloc.0在. 这使得一切正常,因为and的常见类型是 then 。INodeWithBodydupINodeWithBodyMethodINodeWithBody

于 2017-05-21T11:01:41.237 回答
1

目前尚不清楚它是否0x11由于??as的第一部分或右侧部分0x08分支到那里而失败。

我倾向于认为GetAncestor<>返回 aNode并且左侧部分??缺少明确的演员表。

于 2017-05-21T10:37:06.947 回答