しばらくやってた Unsafe シリーズですが、今日は特に凶悪な奴です。

割かし最近なんですが、coreclr にこんなプルリクが出ていました。

これがまあ、なかなか凄いコードを含んでいます。仮想テーブルの中身を覗いて、「特定のビットが立っていたら配列」みたいなコードを書いています。

該当箇所

まず、仮想テーブルのポインターを取得

private static IntPtr GetObjectMethodTablePointer(object obj)
{
    return Unsafe.Add(ref Unsafe.As<byte, IntPtr>(ref JitHelpers.GetPinningHelper(obj).m_data), -1);
}
  • Managed なオブジェクトのアドレスを取得
  • その場所の1ワード手前に仮想テーブルへのポインターが入っているはず

で、それを使って「配列かどうか」を判定。

internal static unsafe bool ObjectHasComponentSize(object obj)
{
    return *(int*)GetObjectMethodTablePointer(obj) < 0;
}
  • 仮想テーブルの最初の4バイトはヘッダーになっている
  • ヘッダーの最上位ビットは「クラスが可変長かどうか」のフラグになっている
  • .NET のクラスで可変長なのは配列と文字列だけ

とまあ、今現在の実装としてはこれで確かに「配列、もしくは、文字列かどうか」を判定できます。

もちろん実装依存

当然ですが、今現在の実装としてできるからといって、将来もそうと言う保証はありません。 仕様として明言されているわけではなく、凄くきわどいコードです。

ギリギリ許されているのは、「coreclr 内の internal コードなので、もしランタイムに手を入れて仮想テーブルの実装が変わるようならその時に併せてここも直せばいい」という感じです。 coreclr の外で真似していいコードではないでしょう。

このプルリク内でも、「一旦はこれでマージしちゃっていいけど、Unsafeクラスを使った実装じゃなくて、ちゃんとランタイム側で判定用の intrinsic な API を提供すべき」という話の流れにはなっています。 さすがにいずれは消えると思われます。