为什么此代码无法验证?



我有一个可以正确构建和运行的编译器,但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][offset 0x00000011][找到 ref 'Boo.Lang.Compiler.Ast.Node'][预期 ref Boo.Lang.Compiler.Ast.INodeWithBody'] 堆栈上出现意外类型。

Offsest0x11对应于??表达式的后半部分。 来自 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,方法是调用的第一个类型参数)中,一切看起来都是正确的。

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

是什么导致 PEVerify 认为它在这里处理的是Node类型的值,而它显然具有Method类型的值?

正如斯蒂芬·德尔克鲁瓦(Stephane Delcroix)所指出的,有两条代码路径到达IL_0011,因为它是分支的目标。它显然不一定具有类型Method的值。

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

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

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

  1. 如果S := TU=S

  2. 否则,如果T := SU=T

  3. 否则,如果ST都是对象类型,则让V成为最接近的ST的常见超类型,然后U=V

  4. 否则,合并将失败。

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

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

我倾向于认为GetAncestor<>返回一个Node,而??的左侧缺少明确的转换。

相关内容

  • 没有找到相关文章

最新更新