为什么这段代码无法验证?
Why is this code failing to verify?
我有一个可以正确构建和运行的编译器,但 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]: 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.
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[=11=]00)
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
class 是所有 AST 节点的基础 class。 BlockExpression
和 Method
分别是 lambda 和方法的节点 classes,它们都实现了 INodeWithBody
接口。一切看起来都是正确的,无论是在 C# 中(如果存在类型问题就不会构建)和 IL 中(它在 000c
处表示 GetAncestor<Method>
调用的 return 类型是!!0
,方法调用的第一个类型参数。)
是什么导致 PEVerify 认为它正在处理一个 Node
类型的值,而它显然具有 Method
类型的值?有什么办法可以解决吗?
目前尚不清楚它是否在 0x11
上失败,因为 ??
的第一部分或右侧,因为 0x08
正在分支到那里。
我倾向于认为 GetAncestor<>
returns Node
并且 ??
的左侧缺少显式转换。
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
?
正如 Stephane Delcroix 所指出的,有两条代码路径到达 IL_0011
,因为它是分支的目标。它显然不一定具有 Method
.
类型的值
GetAncestor<TAncestor>
returnsTAncestor
。您得到了 GetAncestor<BlockExpression>
的结果,或 GetAncestor<Method>
的结果,所以 BlockExpression
或 Method
。都实现了INodeWithBody
,所以逻辑上,代码还是可以的。
不幸的是,“BlockExpression
或 Method
”对于验证来说太多了。这被简化为 Node
(公共基础),它 而不是 实现 INodeWithBody
。参见 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):
if S := T
then U=S
Otherwise, if T := S
then U=T
Otherwise, if S
and T
are both object types, then let V
be the closest common supertype of S
and T
then U=V
.
Otherwise, the merge shall fail.
如果你检查 C# 编译器做了什么,你会发现它在 dup
.这使得一切正常,因为 INodeWithBody
和 Method
的常见类型是 INodeWithBody
.
我有一个可以正确构建和运行的编译器,但 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]: 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.
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[=11=]00)
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
class 是所有 AST 节点的基础 class。 BlockExpression
和 Method
分别是 lambda 和方法的节点 classes,它们都实现了 INodeWithBody
接口。一切看起来都是正确的,无论是在 C# 中(如果存在类型问题就不会构建)和 IL 中(它在 000c
处表示 GetAncestor<Method>
调用的 return 类型是!!0
,方法调用的第一个类型参数。)
是什么导致 PEVerify 认为它正在处理一个 Node
类型的值,而它显然具有 Method
类型的值?有什么办法可以解决吗?
目前尚不清楚它是否在 0x11
上失败,因为 ??
的第一部分或右侧,因为 0x08
正在分支到那里。
我倾向于认为 GetAncestor<>
returns Node
并且 ??
的左侧缺少显式转换。
What is causing PEVerify to think it's dealing with a value of type
Node
here when it clearly has a value of typeMethod
?
正如 Stephane Delcroix 所指出的,有两条代码路径到达 IL_0011
,因为它是分支的目标。它显然不一定具有 Method
.
GetAncestor<TAncestor>
returnsTAncestor
。您得到了 GetAncestor<BlockExpression>
的结果,或 GetAncestor<Method>
的结果,所以 BlockExpression
或 Method
。都实现了INodeWithBody
,所以逻辑上,代码还是可以的。
不幸的是,“BlockExpression
或 Method
”对于验证来说太多了。这被简化为 Node
(公共基础),它 而不是 实现 INodeWithBody
。参见 ECMA-335 §III.1.8.1.3:
The merged type,
U
, shall be computed as follows (recall thatS := T
is the compatibility function defined in §III.1.8.1.2.2):
if
S := T
thenU=S
Otherwise, if
T := S
thenU=T
Otherwise, if
S
andT
are both object types, then letV
be the closest common supertype ofS
andT
thenU=V
.Otherwise, the merge shall fail.
如果你检查 C# 编译器做了什么,你会发现它在 dup
.这使得一切正常,因为 INodeWithBody
和 Method
的常见类型是 INodeWithBody
.