简体   繁体   English

为什么此代码无法验证?

[英]Why is this code failing to verify?

I have a compiler that builds and runs correctly, but PEVerify is calling it unverifiable at a certain point. 我有一个正确构建和运行的编译器,但PEVerify在某个时刻称其无法验证。 After looking carefully at the error, the corresponding source code, and the ILDasm output for the point in question, I can't find the problem, to the point where I would suspect a bug in PEVerify, except that the .NET and Mono versions report the same error in the same place. 仔细查看错误,相应的源代码和相关点的ILDasm输出后,我找不到问题,我怀疑PEVerify中存在错误,除了.NET和Mono版本在同一个地方报告相同的错误。

The problematic method reads as follows: 有问题的方法如下:

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

The error is reported as: 该错误报告为:

[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. [IL]:错误:[D:\\ SDL-1.3.0-4423 \\ boo \\ build \\ Boo.Lang.Compiler.dll:Boo.Lang.Compiler.TypeSystem.AsyncHelper :: InAsyncMethod] [offset 0x00000011] [找到参考'Boo.Lang.Compiler.Ast.Node'] [预期ref Boo.Lang.Compiler.Ast.INodeWithBody']堆栈上的意外类型。

Offsest 0x11 corresponds to the second half of the ?? Offsest 0x11对应于??后半部分。 expression. 表达。 From ILDasm: 来自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

The Boo.Lang.Compiler.Ast.Node class is the base class for all AST nodes. Boo.Lang.Compiler.Ast.Node类是所有AST节点的基类。 BlockExpression and Method are the node classes for lambdas and methods, respectively, both of which implement the INodeWithBody interface. BlockExpressionMethod分别是lambda和方法的节点类,它们都实现了INodeWithBody接口。 Everything appears correct, both in the C# (which would not build if there were type problems) and in the IL (where it says at 000c that the return type of the GetAncestor<Method> call is !!0 , the first type parameter on the method call.) 一切看起来都是正确的,无论是在C#中(如果存在类型问题都不会构建)和IL中(在000c处它表示GetAncestor<Method>调用的返回类型是!!0 ,第一个类型参数on方法调用。)

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 ? 是什么导致PEVerify认为它处理类型为Node的值,当它显然具有Method类型的值时? And is there any way to fix it? 有没有办法解决它?

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 ? 是什么导致PEVerify认为它处理类型为Node的值,当它显然具有Method类型的值时?

As pointed out by Stephane Delcroix as well, there are two code paths reaching to IL_0011 , as it is the target of a branch. 正如Stephane Delcroix所指出的那样,有两条代码路径可以达到IL_0011 ,因为它是分支的目标。 It clearly does not necessarily have a value of type Method . 它显然不一定具有Method类型的值。

GetAncestor<TAncestor> returns TAncestor . GetAncestor<TAncestor>返回TAncestor You've got either GetAncestor<BlockExpression> 's result, or GetAncestor<Method> 's result, so either BlockExpression or Method . 你有GetAncestor<BlockExpression>的结果,或者GetAncestor<Method>的结果,所以要么是BlockExpression ,要么是Method Both implement INodeWithBody , so logically, the code is still fine. 两者都实现了INodeWithBody ,从逻辑INodeWithBody ,代码仍然很好。

Unfortunately, "either BlockExpression or Method " is too much for verification. 不幸的是,“ BlockExpressionMethod ”对于验证来说太过分了。 This gets simplified to Node (the common base), which does not implement INodeWithBody . 这被简化为Node (共用基),它实现INodeWithBody See ECMA-335 §III.1.8.1.3: 见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): 合并类型U应按如下方式计算(回想S := T是§III.1.8.1.2.2中定义的兼容性函数):

  1. if S := T then U=S 如果S := TU=S

  2. Otherwise, if T := S then U=T 否则,如果T := SU=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 . 否则,如果ST都是对象类型,那么让V成为ST最接近的公共超类型,然后U=V

  4. Otherwise, the merge shall fail. 否则,合并将失败。

If you check what the C# compiler does, you'll see that it emits a stloc.0 / ldloc.0 combination into a local of type INodeWithBody prior to the dup . 如果你检查C#编译器的作用,你会看到它在dup之前将一个stloc.0 / ldloc.0组合发送到一个INodeWithBody类型的本地。 This makes everything work, because the common type of INodeWithBody and Method is then INodeWithBody . 这使一切正常,因为INodeWithBodyMethod的常见类型是INodeWithBody

It's unclear if it fails on 0x11 due to the first or the right part of the ?? 目前还不清楚它是否因为第一部分或右部分而在0x11失败?? as 0x08 is branching to there. 因为0x08正在分支到那里。

I'm leaning toward thinking GetAncestor<> returns a Node and the left part of the ?? 我倾向于认为GetAncestor<>返回一个Node和左侧部分?? is missing an explicit cast. 缺少一个明确的演员。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM