7

事実:

  1. rethrowCIL 命令のオペコードの正しいエンコードは、2 バイト シーケンスFE 1Aです。

  2. OpCodes.Rethrow.Value( type を持つ) は、リトルエンディアン マシンでshort価値があります。0xFE1A

  3. BitConverterバイト シーケンスとの間の変換時に、マシンのエンディアンを尊重します。

  4. 私のリトル エンディアン マシンでは、BitConverter.GetBytes(OpCodes.Rethrow.Value)結果はバイト シーケンス1A FE.

つまり、OpCode.Valueを使用してリトルエンディアン マシンで をシリアルBitConverter化しても、オペコードの正しいエンコーディングは生成されません。バイト順が逆になります。

質問:

  • のバイト順はOpCode.Value文書化されていますか (もしそうなら、どこで?)、それとも「実装の詳細」ですか?

  • ビッグ エンディアン マシンで上記の手順 4 を実行しても、バイト順が正しくありませんか? つまり、ビッグエンディアンのマシンでしょうかOpCodes.Rethrow.Value?0x1AFE

4

3 に答える 3

3

Value プロパティは、参照ソースでは次のようになります。

public short Value
{
    get
    {
        if (m_size == 2)
            return (short) (m_s1 << 8 | m_s2);
        return (short) m_s2;
    }
}

もちろん、それはまったく問題ないように見えます.m_s2は常に最下位バイトです。ILGenerator を見る:

    internal void InternalEmit(OpCode opcode)
    {
        if (opcode.m_size == 1)
        {
            m_ILStream[m_length++] = opcode.m_s2;
        }
        else
        {
            m_ILStream[m_length++] = opcode.m_s1;
            m_ILStream[m_length++] = opcode.m_s2;
        }

        UpdateStackSize(opcode, opcode.StackChange());

    }

あなたが期待したいのは、0xfe バイトが最初に発行されることです。

そのため、フレームワーク コードは、エンディアンに依存することを慎重に避けています。CIL にはエンディアン依存性がありません。可変長データはありません。テキスト ファイル、utf-8 エンコーディング、x86 コア マシン コード命令の場合は true。CIL。したがって、Value プロパティの getter が行うように、可変長データを単一の値に変換する場合、そのコードは必然に、非エンディアン データからエンディアン データへの変換を行います。彼らはそれが間違った方法だったと思っているので、必然的に世界の半分を動揺させます. そして、それに遭遇するすべてのプログラマーの 100%。

おそらく最善の方法は、フレームワークと同じように行い、オペコード タイプの独自のバージョンを使用して m_s1 と m_s2 をできるだけ早く回復することです。簡単にできること:

foo.m_s1 = opc.Value >> 8;
foo.m_s2 = opc.Value & 0xff;
foo.m_size = opc.Size;

エンディアンネスの依存関係はありません。

于 2012-08-18T08:03:43.610 に答える
1

OpCode.Valueプロパティに基づいてオペコード表現をシリアル化するという結論に達しました。

OpCode someOpCode = …;
byte[] someOpCodeEncoding = BitConverter.GetBytes(someOpCode.Value);

は悪い考えですがBitConverter.GetBytes(short)、動作が十分に文書化されている を使用しているためではありません。主な原因はOpCode.Valueプロパティであり、そのドキュメントは次の 2 つの点であいまいです。

  1. このプロパティには、オペコードのエンコーディングを参照する場合と参照しない場合がある「即値オペランドの値」が含まれていると述べています。この用語は、CLI 仕様のどこにも表示されません。

  2. 実際にはオペコードのエンコーディングが含まれていると想定している場合でも、ドキュメントにはバイトオーダーについて何も記載されていませ。( と の間で変換するときに、バイト オーダーが有効にbyte[]なりshortます。)

CLI 標準ではなく、MSDN ドキュメントに基づいて議論を行っているのはなぜですか? System.Reflection.EmitCLI 標準で定義されている Reflection Library の一部ではないためです。このため、この名前空間の MSDN リファレンス ドキュメントは、公式の仕様に限りなく近いと言っても差し支えないと思います。(しかし、@Hans Passant の回答とは異なり、私はさらに一歩進んで、参照元が何らかの形で仕様であると主張しません。)

結論:

OpCode特定のオブジェクトのオペコード エンコーディングを出力するには、次の 2 つの方法があります。

  • System.Reflection.Emit機能性と使用にとどまりますILGenerator.Emit(someOpCode)。これは、状況によっては制限が厳しすぎる場合があります。

  • オペコード エンコーディング (つまりbyte[]、シーケンス) とさまざまなOpCodeオブジェクトの間の独自のマッピングを作成します。

于 2012-08-18T10:06:19.910 に答える
0

試す:

var yourStream = MemoryStream();
var writer = new System.IO.BinaryWriter(yourStream);
writer.Write(OpCodes.Rethrow.Value);

BinaryWriter (またはリーダー) が実装の詳細を処理するため、バイト オーダーについて心配する必要はありません。「間違った」バイトオーダーを取得している理由は、すでにリトルエンディアンとしてデコードされている場合に OpCode 値に BitConverter を適用しているためであり、 BitConverter.GetShort() 呼び出しを再度適用すると、バイトが逆になると思われます注文すると、「間違った」結果が得られます。

于 2012-08-18T12:26:07.440 に答える