我有一个可以正确构建和运行的编译器,但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 节点的基类。BlockExpression
和Method
分别是 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中定义的兼容性函数):
如果
S := T
则U=S
否则,如果
T := S
则U=T
否则,如果
S
和T
都是对象类型,则让V
成为最接近的S
和T
的常见超类型,然后U=V
。否则,合并将失败。
如果您检查 C# 编译器的功能,您将看到它在dup
之前将stloc.0
/ldloc.0
组合发出到INodeWithBody
类型的本地。这使一切正常,因为常见的INodeWithBody
和Method
类型是INodeWithBody
.
目前还不清楚它是否由于??
的第一部分或右侧而失败0x11
0x08
分支到那里。
我倾向于认为GetAncestor<>
返回一个Node
,而??
的左侧缺少明确的转换。