しばらくやってた 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 を提供すべき」という話の流れにはなっています。
さすがにいずれは消えると思われます。