为什么此代码无法验证?

时间:2017-05-21 09:49:03

标签: 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]:错误:[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']堆栈上的意外类型。

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分别是lambdas和方法的节点类,它们都实现了INodeWithBody接口。一切看起来都是正确的,无论是在C#中(如果存在类型问题都不会构建)和IL中(000c处的GetAncestor<Method>调用的返回类型为{{1}) },方法调用的第一个类型参数。)

当PEVerify显然具有!!0类型的值时,是什么导致PEVerify认为它处理类型为Node的值?有没有办法解决它?

2 个答案:

答案 0 :(得分:3)

  

当PEVerify明显具有Node类型的值时,是什么导致PEVerify认为它正在处理Method类型的值?

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

GetAncestor<TAncestor>返回TAncestor。您有GetAncestor<BlockExpression>的结果或GetAncestor<Method>的结果,因此BlockExpressionMethod。两者都实现了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.   
  3. 否则,如果T := SU=T

  4.   
  5. 否则,如果ST都是对象类型,那么让V成为ST最接近的常见超类型U=V

  6.   
  7. 否则,合并将失败。

  8.   

如果您检查C#编译器的功能,您会看到它在stloc.0之前将ldloc.0 / INodeWithBody组合发送到dup类型的本地组合中。这使得一切正常,因为INodeWithBodyMethod的常见类型是INodeWithBody

答案 1 :(得分:1)

由于0x11的{​​{1}}的第一部分或右部分正在分支到??,因此不清楚0x08是否失败。

我倾向于思考GetAncestor<>会返回Node??的左侧部分缺少明确的演员。