2017-05-21 12 views
4

私は正しくビルドして実行するコンパイラを持っていますが、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); 
} 

誤差は次のように報告される:0x11 Offsest

[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.

??式の後半に相当します。 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(000cGetAncestor<Method>呼び出しの戻り値の型がメソッド呼び出しの最初の型パラメーターであることがわかります)の両方で、すべてが正しく表示されます。

明らかに値がMethodの場合、PEVerifyはタイプNodeの値を扱っていると思う原因になりますか?それを修正する方法はありますか?

+1

長年に渡ってこれらの自分自身をデバッグしたことは、たいていいつも '? : '条件付き演算子。検証者は、長年にわたり条件文を処理するための戦略を変更しており、かなり混乱する可能性があります。 –

+0

@EricLippert今回は '??'演算子でした。 (詳細については、受け入れられた答えを参照してください。)2つの別々のステートメントとして書き直しました。これは、コードが完全に同等であるべきであるため少し奇妙です。私は 'csc'が一義的なILを生成するのを見て驚いています! –

+1

'??'演算子はちょうど派手なドレスの '?:'演算子です。 ( '&&'と '||'と '?.')' csc'がこれを生成している場合、(1)あなたのcscのバージョンが別のバージョンの検証者に対してテストされました。 (2)cscは条件付きコードパスでもう一つのフリークインのバグを持っています。それを報告してください、私は今回はそれを修正する必要はありません喜んで、または(3)両方。 –

答えて

3

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 ?

としては、分岐のターゲットであるとしてIL_0011に到達する2つのコード・パスが存在する、ならびにステファンDelcroixが指摘。明らかに、必ずしも値がMethodであるとは限りません。

GetAncestor<TAncestor>返信TAncestorGetAncestor<BlockExpression>の結果、またはGetAncestor<Method>の結果があるので、BlockExpressionまたはMethodのいずれかになります。どちらもINodeWithBodyを実装しているので、論理的には、コードはまだ問題ありません。

残念ながら、「BlockExpressionまたはMethod」のいずれかが確認には多すぎます。これはNode(共通ベース)に簡略化され、ではなく、INodeWithBodyを実装します。 ECMA-335§III.1.8.1.3を参照してください:あなたはC#コンパイラが何をするのか確認した場合、あなたはそれがdup前のタイプINodeWithBodyのローカルにstloc.0/ldloc.0組み合わせを発していることがわかります

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):

  1. if S := T then U=S

  2. Otherwise, if T := S then U=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 .

  4. Otherwise, the merge shall fail.

。共通タイプのINodeWithBodyMethodは、INodeWithBodyであるため、これですべてが機能します。

+0

ああ、私は完全に条件付きジャンプを逃した!残念なことに、キャストを「?」の反対側に移動するか、または両方に置くことによって、同一の確認不可能なILが作成されます。どのようにこれを正しく修正するための任意のアイデア?あるいは、これは 'csc'のバグとして報告されるべきですか? –

+0

@MasonWheeler待って - あなたはcscからこのILを取得していますか?私はあなたが自分のコンパイラからこれを得ていると思っていました、誤解をおかけして申し訳ありません。 cscはこれを生成すべきではありません。最後の段落では、C#コンパイルから見た動作を説明しました。 Microsoft.Net.Compilersパッケージをインストールして、より最新のコンパイラを強制的に使用するようにできます。それがうまくいかない場合は、実際にはバグとして報告してください。 – hvd

+0

ええ、これはVisual Studio 2013の通常のC#コンパイラから出てきています。MonoのC#コンパイラは明らかに同じ問題のある出力を生成します。 –

1

0x11の最初または右側の部分が、0x08がそこに分岐しているため、??のために失敗したかどうかは不明です。

私は思考に傾いていますGetAncestor<>Nodeを返し、??の左側には明示的なキャストがありません。

+1

これは合理的な推測ですが、 'GetAncestor 'は 'TAncestor'を返し、' BlockExpression'と 'Method'は' Node'から派生し、 'INodeWithBody'を実装しています。 – hvd

関連する問題