2012-02-15 10 views
14

同僚が、実行時にInvalidProgramException(「CLRが無効なプログラムを検出しました」)でクラッシュする興味深いコードサンプルを渡しました。Enumerable.ToDictionaryを拡張メソッドで使用すると「CLRが無効なプログラムを検出しました」

この問題はJIT時に発生しているようですが、これはうまくコンパイルされますが、「違反」行があるメソッドが呼び出される直前に例外がスローされます.JITされているので推測します。

この行は、Enumerable.ToDictionaryを呼び出し、Funcを第2引数として渡しています。

Func引数がlambdaで完全に指定されている場合は動作します。メソッドグループとして指定されている場合は失敗します。確かにこれらの2つは同等ですか?

これは私を困惑させました(そしてそれを発見した同僚) - それは確かにJITエラーのようです。

[EDIT:申し訳ありません - 私は、パスを持って、ケースにラウンドのコードサンプルで間違った方法を失敗する - 今すぐ修正(上記の説明は正しかった)]

誰もが説明を持っていますか?

using System; 
using System.Linq; 

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     Test.Try(); 
    } 
} 

public class Test 
{ 
    public static readonly int[] integers = new[] { 1, 3, 5 }; 
    public static void Try() 
    { 
     var line = new Line { A = 3, B = 5 }; 

     // PASSES 
     var dict = integers.ToDictionary<int, int, decimal>(i => i, i => line.Compute(i)); 

     // FAILS 
     //var dict = integers.ToDictionary<int, int, decimal>(i => i, line.Compute); 

     Console.WriteLine(string.Join(" ", dict.Select(kv => kv.Key + "-" + kv.Value))); 
    } 
} 

public class Line 
{ 
    public decimal A; 
    public decimal B; 
} 

public static class SimpleCompute 
{ 
    public static decimal Compute(this Line line, int value) 
    { 
     return line.A*value + line.B; 
    } 
} 
+0

実際に私のローカルインストールでは、あなたの "PASSES"行も失敗します –

+0

私の(v4.0、x86)ボックスでうまくいきます - 実行中の.NETのバージョンとアーキテクチャは何ですか? –

+0

興味深い!拡張メソッドを削除した場合(つまり、SimpleComputeをインスタンスメソッドとしてLineに配置した場合)、動作しますか?もし私がこれをすれば、それは私のためになりますか? –

答えて

15

コンパイラのバグ。

私は非同期のCTPを持っていますが、それは関連している可能性があります。 cscレポート:それでは、反射鏡で見てみましょう

public static void TryTwo() // fails 
{ 
    var line = new Line {A = 3, B = 5}; 

    var dict = integers.ToDictionary<int, int, decimal>(i => i, line.Compute); 
    Console.WriteLine("TryTwo complete"); 
} 
public static void TryFive() // works 
{ 
    var line = new Line { A = 3, B = 5 }; 

    Func<int, decimal> func = line.Compute; 
    var dict = integers.ToDictionary<int, int, decimal>(i => i, func); 
    Console.WriteLine("TryFour complete"); 
} 

.method public hidebysig static void TryFive() cil managed 
{ 
    .maxstack 4 
    .locals init (
     [0] class Line line, 
     [1] class [mscorlib]System.Func`2<int32, valuetype [mscorlib]System.Decimal> func, 
     [2] class Line <>g__initLocal9) 
    L_0000: newobj instance void Line::.ctor() 
    L_0005: stloc.2 
    L_0006: ldloc.2 
    L_0007: ldc.i4.3 
    L_0008: newobj instance void [mscorlib]System.Decimal::.ctor(int32) 
    L_000d: stfld valuetype [mscorlib]System.Decimal Line::A 
    L_0012: ldloc.2 
    L_0013: ldc.i4.5 
    L_0014: newobj instance void [mscorlib]System.Decimal::.ctor(int32) 
    L_0019: stfld valuetype [mscorlib]System.Decimal Line::B 
    L_001e: ldloc.2 
    L_001f: stloc.0 
    L_0020: ldloc.0 
    L_0021: ldftn valuetype [mscorlib]System.Decimal SimpleCompute::Compute(class Line, int32) 
    L_0027: newobj instance void [mscorlib]System.Func`2<int32, valuetype [mscorlib]System.Decimal>::.ctor(object, native int) 
    L_002c: stloc.1 
    L_002d: ldsfld int32[] Test::integers 
    L_0032: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegateb 
    L_0037: brtrue.s L_004a 
    L_0039: ldnull 
    L_003a: ldftn int32 Test::<TryFive>b__a(int32) 
    L_0040: newobj instance void [mscorlib]System.Func`2<int32, int32>::.ctor(object, native int) 
    L_0045: stsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegateb 
    L_004a: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegateb 
    L_004f: ldloc.1 
    L_0050: call class [mscorlib]System.Collections.Generic.Dictionary`2<!!1, !!2> [System.Core]System.Linq.Enumerable::ToDictionary<int32, int32, valuetype [mscorlib]System.Decimal>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>, class [mscorlib]System.Func`2<!!0, !!2>) 
    L_0055: pop 
    L_0056: ldstr "TryFour complete" 
    L_005b: call void [mscorlib]System.Console::WriteLine(string) 
    L_0060: ret 
} 

.method public hidebysig static void TryTwo() cil managed 
{ 
    .maxstack 4 
    .locals init (
     [0] class Line <>g__initLocal6) 
    L_0000: newobj instance void Line::.ctor() 
    L_0005: stloc.0 
    L_0006: ldloc.0 
    L_0007: ldc.i4.3 
    L_0008: newobj instance void [mscorlib]System.Decimal::.ctor(int32) 
    L_000d: stfld valuetype [mscorlib]System.Decimal Line::A 
    L_0012: ldloc.0 
    L_0013: ldc.i4.5 
    L_0014: newobj instance void [mscorlib]System.Decimal::.ctor(int32) 
    L_0019: stfld valuetype [mscorlib]System.Decimal Line::B 
    L_001e: ldsfld int32[] Test::integers 
    L_0023: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
    L_0028: brtrue.s L_003b 
    L_002a: ldnull 
    L_002b: ldftn int32 Test::<TryTwo>b__7(int32) 
    L_0031: newobj instance void [mscorlib]System.Func`2<int32, int32>::.ctor(object, native int) 
    L_0036: stsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
    L_003b: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
    L_0040: call class [mscorlib]System.Collections.Generic.Dictionary`2<!!1, !!2> [System.Core]System.Linq.Enumerable::ToDictionary<int32, int32, valuetype [mscorlib]System.Decimal>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>, class [mscorlib]System.Func`2<!!0, !!2>) 
    L_0045: pop 
    L_0046: ldstr "TryTwo complete" 
    L_004b: call void [mscorlib]System.Console::WriteLine(string) 
    L_0050: ret 
} 

あなたが見れば4.0.30319.440

は違いしているようですは、1つのデリゲートのみを読み込みます。基本的にはコンパイラのバグ、:

L_0023: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
L_0028: brtrue.s L_003b 
L_002a: ldnull 
L_002b: ldftn int32 Test::<TryTwo>b__7(int32) 
L_0031: newobj instance void [mscorlib]System.Func`2<int32, int32>::.ctor(object, native int) 
L_0036: stsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
L_003b: ldsfld class [mscorlib]System.Func`2<int32, int32> Test::CS$<>9__CachedAnonymousMethodDelegate8 
L_0040: call class [mscorlib]System.Collections.Generic.Dictionary`2<!!1, !!2> [System.Core]System.Linq.Enumerable::ToDictionary<int32, int32, valuetype [mscorlib]System.Decimal>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>, class [mscorlib]System.Func`2<!!0, !!2>) 

上記のすべては、「キャッシュされたi => iが存在するかどうかをチェックし、それを作成しない場合は、それをロードする」です。 は、第2の代理人と何も何もしません。したがって、メソッド呼び出しを行うのに十分な値がスタックにありません。

+0

ありがとうMarc。 PEVerifyがアセンブリで報告したStackUnderflowエラーについても説明しています(元々の質問で言及したはずです)。とても有難い! –

+0

@Robちょうど追加する:これは修正のための適切なチャンネルに渡されている。 –

+0

もう一度おねがいします。Marc - 私はそれについて何をすべきか疑問に思っていましたが、あなたはそれをソートしました。再び、非常に感謝します。 –

関連する問題