最近ひたすらRubyの元コードを読んで、う〜んと頭をもたげています。
頑張って、早くRubyに対してCommit出来るくらいの知識を身につけたいと思います。(そして、いつの日かコミッターとやらになるぞー!おー!)
今回の趣旨
今回の趣旨は、書きながら説明しながらだと適当に読み飛ばすこともないので、勉強にはもってこいだなって感じで、書いてます。だから、自分の理解用のものなのでわかりにくいところや、これは理解が違うってところがあるかもしれませんが、そしたら叱ってやってください。
ちなみにC言語の知識もそこまでないので、勉強しながら調べながら進んでいきます。
寄り道もたくさんします。
rubyのVersionは2.3.1です。
今回のコードはこちら
struct RObject {
struct RBasic basic;
union {
struct {
long numiv; /* only uses 32-bits */
VALUE *ivptr;
void *iv_index_tbl; /* shortcut for RCLASS_IV_INDEX_TBL(rb_obj_class(obj)) */
} heap;
VALUE ary[ROBJECT_EMBED_LEN_MAX];
} as;
};
これは、RObjectの構造体を定義している部分です。
RObjectのメンバ変数は
- RBasic basic
- union(struct{long numiv, VALUE *ivptr, void *iv_index_tbl})
- VALUE ary[ROBJECT_EMBED_LEN_MAX];
の三つです。
まだまだ全然意味がわかりません笑
勉強していきます
RBasic
まず、RBasicを見てみます。もうどの構造体もメンバに持っていないので
どうやら、RBasicがrubyが持つ構造体の元になっているようです。
struct RBasic {
VALUE flags;
const VALUE klass;
}
RBasicは、VALUE flagsとconst VALUE klassをメンバに持つ構造体でした。
VALUE型
まず、VALUE型は、再定義された型なので調べると
typedef unsigned long VALUE;
と出てきます。
つまり、VALUE型は、32bitで、符号なし(倍長)整数型で、範囲を0~4294967295これだけ持つ型です。
Rubyだとなんかめっちゃ出てきます。
flagsの持つ意味
さて、ではflagsが持つ意味について深めていきます。
flagsは、
flagsは多目的のフラグだが、最も重要な用途は構造体の型 (struct RObjectだとか)を記憶しておくことである。型を示すフラグは T_xxxxという名前で定義されていて、VALUEからはマクロTYPE()で 得ることができる。
Rubyソースコード完全解説
http://i.loveruby.net/ja/rhg/book/
とのことです。
flagsは構造体の型を記憶して、引っ張り出してくるためのものみたいですね。
確かに、RBasicという元祖の構造体に定義してあるので、RBasicをメンバに持つ構造体は、RBasicのflagsにアクセスすれば、何の型なのかというのが一瞬でわかって便利ですね。
せっかくなので、マクロTYPEもちゃんと調べておきます。
マクロTYPE
マクロTYPEはここに定義してありました。
static inline int rb_type(VALUE obj);
#define TYPE(x) rb_type((VALUE)(x))
上のrb_typeの関数宣言文にある、static inline
は、インライン関数というものらしいです。(初めて聞いた)
先頭に inline という言葉を付けて関数を宣言すると,コンパイラーはそれをヒントにコードをインライン化――関数のコードを呼出し元に展開する。これにより,関数呼び出しのオーバーヘッドが取り除かれ実行が早くなる。
こちらに記載してありました。
http://d.hatena.ne.jp/wocota/20090219/1235058524
つまり、コンパイルするときに呼び出しに行くんじゃなくて、処理部分をここに書いちゃうよ!ってことみたいです。なるほど!
はい!じゃあ、処理部分を見に行きましょう。
static inline int
rb_type(VALUE obj)
{
if (RB_IMMEDIATE_P(obj)) {
if (RB_FIXNUM_P(obj)) return RUBY_T_FIXNUM;
if (RB_FLONUM_P(obj)) return RUBY_T_FLOAT;
if (obj == RUBY_Qtrue) return RUBY_T_TRUE;
if (RB_STATIC_SYM_P(obj)) return RUBY_T_SYMBOL;
if (obj == RUBY_Qundef) return RUBY_T_UNDEF;
}
else if (!RTEST(obj)) {
if (obj == RUBY_Qnil) return RUBY_T_NIL;
if (obj == RUBY_Qfalse) return RUBY_T_FALSE;
}
return RB_BUILTIN_TYPE(obj);
}
VALUE型のオブジェクトを渡すと、何やらT_〇〇というTYPEが返ってきそうな感じです。
RB_IMMEDIATE_P(obj)
上から読んで行きましょう。まずは、RB_IMMEDIATE_P(obj)
の条件分岐です。
こいつも、マクロみたいです。
ここにありました。
#define RB_IMMEDIATE_P(x) ((VALUE)(x) & RUBY_IMMEDIATE_MASK)
ぐぬぬ、、、どういうことや
&ってアドレス演算子じゃないんか!って思って調べたら、ビット演算子っていうのが、あった。
まだまだ知らないことがいっぱいですね笑
&はビットANDっていう演算子らしく、左辺と右辺の同じ位置にあるビットだけ1に残す演算子みたい。if文の条件分岐なのに、true, falseとかじゃないんだね。
具体的には
0000000000000111 = 7
0000000000001110 = 14
----------------
0000000000000110 = 6
みたいな感じらしい。うおお。なんかPCっぽいと感動してます笑
RUBY_IMMEDIATE_MASK
ほいでは、比較する左辺であるRUBY_IMMEDIATE_MASK
の調査を開始したいと思います。
これはすぐ近くにありました。
/* special constants - i.e. non-zero and non-fixnum constants */
enum ruby_special_consts {
#if USE_FLONUM
RUBY_Qfalse = 0x00, /* ...0000 0000 */
RUBY_Qtrue = 0x14, /* ...0001 0100 */
RUBY_Qnil = 0x08, /* ...0000 1000 */
RUBY_Qundef = 0x34, /* ...0011 0100 */
RUBY_IMMEDIATE_MASK = 0x07,
RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */
RUBY_FLONUM_MASK = 0x03,
RUBY_FLONUM_FLAG = 0x02, /* ...xxxx xx10 */
RUBY_SYMBOL_FLAG = 0x0c, /* ...0000 1100 */
#else
RUBY_Qfalse = 0, /* ...0000 0000 */
RUBY_Qtrue = 2, /* ...0000 0010 */
RUBY_Qnil = 4, /* ...0000 0100 */
RUBY_Qundef = 6, /* ...0000 0110 */
RUBY_IMMEDIATE_MASK = 0x03,
RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */
RUBY_FLONUM_MASK = 0x00, /* any values ANDed with FLONUM_MASK cannot be FLONUM_FLAG */
RUBY_FLONUM_FLAG = 0x02,
RUBY_SYMBOL_FLAG = 0x0e, /* ...0000 1110 */
#endif
RUBY_SPECIAL_SHIFT = 8
};
ruby_special_constsと書かれるとちょっとテンションが上がりますね笑
rubyのスペシャルな定数とは一体!!!?
って、書いてありますよね。笑
non-zero and non-fixnum constants
0じゃなくて、fixnumじゃない定数のことみたいです。
さてさて、関係あるところだけを切り取って考えましょう。
/* special constants - i.e. non-zero and non-fixnum constants */
enum ruby_special_consts {
#if USE_FLONUM
RUBY_IMMEDIATE_MASK = 0x07,
#else
RUBY_IMMEDIATE_MASK = 0x03,
#endif
RUBY_SPECIAL_SHIFT = 8
};
USE_FLONUM
いちいちつまづく。本題には一体いつたどり着くのだろうか。。
でも、面白いからよしとします笑
#if USE_FLONUM
について調べます。
#ifndef USE_FLONUM
#if SIZEOF_VALUE >= SIZEOF_DOUBLE
#define USE_FLONUM 1
#else
#define USE_FLONUM 0
#endif
#endif
#ifndef は #ifdef の逆です。つまり #ifndef MACRONAME ~~ #endif とあったら、 MACRONAME が #define されてない時にかぎり、~~の部分をコンパイルするという事です。
参考サイトhttp://www.geocities.co.jp/SiliconValley/6071/technic/16.html
とりあえず、USE_FLONUMが定義されてないとして、次に進みます。
次はここみたいです。深いなーw
#if defined HAVE_UINTPTR_T && 0
typedef uintptr_t VALUE;
typedef uintptr_t ID;
# define SIGNED_VALUE intptr_t
# define SIZEOF_VALUE SIZEOF_UINTPTR_T
# undef PRI_VALUE_PREFIX
#elif SIZEOF_LONG == SIZEOF_VOIDP
typedef unsigned long VALUE;
typedef unsigned long ID;
# define SIGNED_VALUE long
# define SIZEOF_VALUE SIZEOF_LONG
# define PRI_VALUE_PREFIX "l"
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
typedef unsigned LONG_LONG VALUE;
typedef unsigned LONG_LONG ID;
# define SIGNED_VALUE LONG_LONG
# define LONG_LONG_VALUE 1
# define SIZEOF_VALUE SIZEOF_LONG_LONG
# define PRI_VALUE_PREFIX PRI_LL_PREFIX
#else
# error ---->> ruby requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
#endif
さっきみたいに関係あるところだけ抜き出します。
#if defined HAVE_UINTPTR_T && 0
# define SIZEOF_VALUE SIZEOF_UINTPTR_T
#elif SIZEOF_LONG == SIZEOF_VOIDP
# define SIZEOF_VALUE SIZEOF_LONG
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
# define SIZEOF_VALUE SIZEOF_LONG_LONG
#else
# error ---->> ruby requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
#endif
まず最初の条件分岐は、HAVE_UINTPTR_Tが定義されていて、かつ、0だった時ということです。
grepして、HAVE_UINTPTR_Tを探してみると、
include/ruby/ruby.h:#if defined HAVE_UINTPTR_T && 0
win32/Makefile.sub:#define HAVE_UINTPTR_T 1
しか出てきませんでした。
つまり、windows32では1と定義されるので、とりあえずここは関係ないのかな?
#If 0ってC,C++では、#endifまで問答無用でコメントにします
http://blog.seasons.cc/entry/20090504/1241390314
って書いてありました。今回は、elifがあるので、そこまではコメントになっているということみたいです。
つまり、今は使われてない的なやつなのかなと推測しておきます。
次は、if SIZEOF_LONG == SIZEOF_VOIDP
の部分です。
SIZEOF_LONGは結構遠いところにありました。
rb_define_const(mFiddle, "SIZEOF_LONG", INT2NUM(sizeof(long)));
ちなみに、fiddleはヴァイオリンとか詐欺とかいう意味です。なぜなんだろう…。。
rb_define_constは名前の通り、定数を定義するものっぽいですよね。
正解っぽいです
void rb_define_const(VALUE klass, const char *name, VALUE val)
クラス klass の定数 name を初期値 val で 定義します。既に同名の定数が定義されていたら警告します。
http://docs.ruby-lang.org/ja/2.3.0/function/rb_define_const.html
つまり今回の場合、SIZEOF_LONGを呼び出すと、INT2NUM(sizeof(long))が呼び出されます。
sizeof(long)は、longのsizeを求める関数です。
INT2NUMは、INT型から、NUM型にキャストする関数です。
longは4byteのはずです。
一回復習
なので、SIZEOF_VOIDPがlongのサイズと同じであれば、SIZEOF_VALUEはlongのサイズとなります。
そして、SIZEOF_VALUEがlongのサイズとすると、SIZEOF_DOUBLEと比較できて、
SIZEOF_DOUBLEの方が小さければ、USE_FLONUMが1となり、
RUBY_IMMEDIATE_MASKは0x07となる。
で、0x07とobjとをビットANDして、
その解が真であれば、if文の中に入っていきます!
では、SIZEOF_VOIDPを調べていきます。
rb_define_const(mFiddle, "SIZEOF_VOIDP", INT2NUM(sizeof(void*)));
さっきと同じで、void*のサイズをはかる関数のようです。
何やら、mFiddleは、サイズを測ったりするクラスのようです。また、SIZEOF_〇〇とは、〇〇のサイズを測ってINTからNUMに変換する関数のようです。なんとなくわかってきました。
ポインタは、型によらず4byteのようです。
http://rainbow.pc.uec.ac.jp/edu/program/b1/Ex4-1.htm
よしきました!
# define SIZEOF_VALUE SIZEOF_LONG
が実行されます。
SIZEOF_VALUEは4byteになります。
じゃあ、帰ってきてUSE_FLONUMの部分いきましょう
#ifndef USE_FLONUM
#if SIZEOF_VALUE >= SIZEOF_DOUBLE
#define USE_FLONUM 1
#else
#define USE_FLONUM 0
#endif
#endif
4byteより、SIZEOF_DOUBLEが大きければ、USE_FLONUMが1となります。
SIZEOF_〇〇は、サイズをはかるやつです。
rb_define_const(mFiddle, "SIZEOF_DOUBLE",INT2NUM(sizeof(double)));
doubleは8byteです。ですので、USE_FLONUMは、0となります。
さっきのSIZEOF_VALUEの定義が間違ってました
やばい。SIZEOF_VALUEの定義が間違っていることにここで気づきました。
doubleは8byteで固定なため、SIZEOF_VALUEが可変でないとおかしいはずです。
どこで間違ったのか
6月2日
jkr_2255さんのコメントで判明しました!(ありがとうございました!)
可搬性
Rubyはいろいろな環境でコンパイルできるように、型のサイズを決め打ちにしないようにしてあります。
Windows/Linux x86…ポインタもlongも32ビット
Windows x64…ポインタは64ビット、longは32ビット
Linux x64…ポインタもlongも64ビット
SIZEOF_XXXマクロは、これと似たような仕組みでソースコードの外から供給されますが、基本的には「sizeof(xxx)」の結果と同じ、と考えて問題ありません。
USE_FLONUMも、環境によって0か1かが決まってくるものなので、VALUEが32ビット、doubleが64ビットの環境では0になります。
ということだったので、longが4byte、ポインタも、型によらず4byteって決めつけていたのが間違いでした。
Windows/Linux x86の時は、longもポインタも32bitと同じなので、SIZEOF_VALUEはSIZEOF_LONGと同じと定義され、4byteです。x86のdoubleは8byteでした。(参考:http://d.hatena.ne.jp/bellbind/20081118/1227032924)
なので、#if SIZEOF_VALUE >= SIZEOF_DOUBLE
は、falseが買えるので、USE_FLONUMは0になって、RUBY_IMMEDIATE_MASK = 0x03
になります。
0x03は16進数で、10進数に直すと、3です。3は2進数に直すと0011
です。
この0011
とobjのVALUEをビットANDして、この先どおしたらええんや笑
#define RB_IMMEDIATE_P(x) ((VALUE)(x) & RUBY_IMMEDIATE_MASK)
IMMEDIATEは、”直接の、じかの、即座の、即時の、早速の、すぐ隣の、隣接した、当面の、目下の、ごく近い”って意味みたいです。
_Pは、
~pという名前はLisp由来の記法で、真偽値を返す手続きであること を示している。
http://i.loveruby.net/ja/rhg/book/object.html
ということです。
つまり、RB_IMMEDIATE_P(x)は、xが直に何かしてるかどうか?みたいなニュアンス?のようです。
惜しい感じ。
と、思って調べまくってたら、おおおおお!って感じで発見しました。
例えば、第 1 ビットに 1 が入っているとすると、第 1 ビットが立っているという言い方をします。ある数の第 1 ビットが立っているかどうかを調べる時、論理積 ( & ) 演算子を使います。
a = ある数 & 0X02;
上記の演算を行って a が 0 なら、第 1 ビットは立っていません。0 以外なら、第 1 ビットは立っています。
http://www1.cts.ne.jp/~clab/hsample/Bit/Bit2.html
つまり、0011
の部分がかぶっていたらtrue!、
どこも被らずに0になったら、false!ということみたいです。
ここで、jkr_2255さんのコメントに書いてあった、
ポインタの下位数ビットが0となるように配置されます。
Rubyではこれを利用して、最下位ビットが立っているものはFixnumとして扱います(参考)。また、4バイトアラインだと「4で割って2余る数」も有効なポインタとして使われないことになるので、ビット数が十分ある状況ならdoubleをシフトして、下位ビットを調整した上でVALUEに突っ込んでしまいます。
が活きてくるような気がします。
まず、Fixnumだったら、最下位ビットが必ず1が立つので、trueです。
中を見てましょう。
if (RB_IMMEDIATE_P(obj)) {
if (RB_FIXNUM_P(obj)) return RUBY_T_FIXNUM;
if (RB_FLONUM_P(obj)) return RUBY_T_FLOAT;
if (obj == RUBY_Qtrue) return RUBY_T_TRUE;
if (RB_STATIC_SYM_P(obj)) return RUBY_T_SYMBOL;
if (obj == RUBY_Qundef) return RUBY_T_UNDEF;
}
どうやら正解みたいです!
if (RB_FIXNUM_P(obj)) return RUBY_T_FIXNUM;
があるので、objがFIXNUMの場合、RUBY_T_FIXNUMが返るみたいです。
やりました!
一応、RB_FIXNUM_P(obj)と、RUBY_T_FIXNUMについても勉強しておきます。
RB_FIXNUM_P
#define RB_FIXNUM_P(f) (((int)(SIGNED_VALUE)(f))&RUBY_FIXNUM_FLAG)
RUBY_FIXNUM_FLAGは実は、ruby_special_constsの部分で定義していました。
RUBY_FIXNUM_FLAG = 0x01
です。2進数だと、0001
なので
最下位ビットのみが立っていれば、FIXNUMということになります。
RUBY_T_FIXNUM
enum ruby_value_type {
RUBY_T_NONE = 0x00,
RUBY_T_OBJECT = 0x01,
RUBY_T_CLASS = 0x02,
RUBY_T_MODULE = 0x03,
RUBY_T_FLOAT = 0x04,
RUBY_T_STRING = 0x05,
RUBY_T_REGEXP = 0x06,
RUBY_T_ARRAY = 0x07,
RUBY_T_HASH = 0x08,
RUBY_T_STRUCT = 0x09,
RUBY_T_BIGNUM = 0x0a,
RUBY_T_FILE = 0x0b,
RUBY_T_DATA = 0x0c,
RUBY_T_MATCH = 0x0d,
RUBY_T_COMPLEX = 0x0e,
RUBY_T_RATIONAL = 0x0f,
RUBY_T_NIL = 0x11,
RUBY_T_TRUE = 0x12,
RUBY_T_FALSE = 0x13,
RUBY_T_SYMBOL = 0x14,
RUBY_T_FIXNUM = 0x15,
RUBY_T_UNDEF = 0x16,
RUBY_T_IMEMO = 0x1a,
RUBY_T_NODE = 0x1b,
RUBY_T_ICLASS = 0x1c,
RUBY_T_ZOMBIE = 0x1d,
RUBY_T_MASK = 0x1f
};
TYPEは472行めでたくさん定義してありました。
RUBY_T_FIXNUM = 0x15です。
いやあ、長かったですね。一応、TYPEのゴールに行き着きました。
ついでに、0011
でビットANDで真になる他のものを調べてみましょう。
0001(FIXNUM)
0010
0101
0110
0111
1001
1010
1011
1101
1110
1111
が、真になるものとなる気がします。
if (RB_IMMEDIATE_P(obj)) {
if (RB_FIXNUM_P(obj)) return RUBY_T_FIXNUM;
if (RB_FLONUM_P(obj)) return RUBY_T_FLOAT;
if (obj == RUBY_Qtrue) return RUBY_T_TRUE;
if (RB_STATIC_SYM_P(obj)) return RUBY_T_SYMBOL;
if (obj == RUBY_Qundef) return RUBY_T_UNDEF;
}
で、おそらく、このCodeのFIXNUMの下にあるものが、上記の形で表現されているはずです。
調べてあっていたら、理解が正しいということになります。
じゃあ、調べます。
FLONUM
FLONUMどこかで見たぞと思ったけど、USE_FLONUMをさっき定義したところでした。
先ほどは、USE_FLONUMが0になりましたので、今回は使用しないのでスルーします。
RUBY_Qtrue
RUBY_Qtrueも実は、先ほどのruby_special_constsで定義してあります。
RUBY_Qtrue = 2, /* ...0000 0010 */
こう定義されていました。
狙いどうりでしたね!
0001(FIXNUM)
0010(Qtrue)
0101
0110
0111
1001
1010
1011
1101
1110
1111
RUBY_T_SYMBOL
これも、おそらくRUBY_SYMBOL_FLAG = 0x0e, /* ...0000 1110 */
の部分と思います。
これも正解みたいです。
0001(FIXNUM)
0010(Qtrue)
0101
0110
0111
1001
1010
1011
1101
1110(SYMBOL_FLAG)
1111
RUBY_Qundef
ラストー!!!
RUBY_Qundef
RUBY_Qundef = 6, /* ...0000 0110 */
これもこのように定義されていました。
これもビンゴです!
0001(FIXNUM)
0010(Qtrue)
0101
0110(Qundef)
0111
1001
1010
1011
1101
1110(SYMBOL_FLAG)
1111
ちなみにQundefはこんな感じのものです
st_delete と似ているが、その場ですぐに削除するのではなく never を
書きこんでおく。st_cleanup_safe() で本当に削除できる。
Ruby では never には Qundef を使う。
http://docs.ruby-lang.org/ja/search/query:st_delete/version:1.8.7/
論理削除に使う型なのかなと推測しておきます。
#define RB_IMMEDIATE_P(x) ((VALUE)(x) & RUBY_IMMEDIATE_MASK)
つまり、RB_IMMEDIATE_P(x) は
- FIXNUM
- Qtrue
- SYMBOL_FLAG
- Qundef(never)
の時のみtrueになる関数みたいです。
今日も少しですが進みました。
また明日これたらいいなー笑
2016/06/02/03:12
6月3日
今日は始めるのが遅かったので、ちょっとだけ進めます。
昨日のことで間違ってることが少しありました。
0001(FIXNUM)
0010
0101
0110
0111
1001
1010
1011
1101
1110
1111
が、真になるものとなる気がします。
と、このように記述したのですが、冷静に最下位ビットが1のものはFIXNUMとなるので、正しくは
0001(FIXNUM)
0010
0110
1010
1110
となるはずでした。
これに、他の調査した型を当てはめると
0001(FIXNUM)
0010(Qtrue)
0110(Qundef)
1010
1110(SYMBOL_FLAG)
と、このようにピッタリ当てはまります。今日の朝ハッ!っと気づいて、少し嬉しかったです。笑
1010は一体何なのかもきになるところですね。気になって、grepをかけてみたのですが、1010ではhitしませんでした。
1010は、16進数だと0x10なのでこちらで検索してみたら
dir.c:#define FNM_EXTGLOB 0x10
とかが出てきました、、が、あっているかどうかはイマイチわかりません。またわかったら記述します。
ではでは、次に進みます。
static inline int
rb_type(VALUE obj)
{
if (RB_IMMEDIATE_P(obj)) {
if (RB_FIXNUM_P(obj)) return RUBY_T_FIXNUM;
if (RB_FLONUM_P(obj)) return RUBY_T_FLOAT;
if (obj == RUBY_Qtrue) return RUBY_T_TRUE;
if (RB_STATIC_SYM_P(obj)) return RUBY_T_SYMBOL;
if (obj == RUBY_Qundef) return RUBY_T_UNDEF;
}
↑ここまでは理解
------------------------------------
else if (!RTEST(obj)) {
if (obj == RUBY_Qnil) return RUBY_T_NIL;
if (obj == RUBY_Qfalse) return RUBY_T_FALSE;
}
return RB_BUILTIN_TYPE(obj);
}
if (!RTEST(obj))
というわけで、次はif (!RTEST(obj))
の部分ですね。
こちらは、ruby.hの467行めにありました。
#define RTEST(v) !(((VALUE)(v) & ~Qnil) == 0)
vと~QnilのビットANDが0でない時、真って感じですね。
今回の、条件分岐には、!が付いているので、逆でvと~QnilのビットANDが0の時、中に入っていくことになります。
では、~Qnilがどういう意味なのか確認しましょう。
まず、~が何を意味しているのかがわかりません。
C言語やC++言語では、NOT演算子は "~" (チルダ)である。
https://ja.wikipedia.org/wiki/%E3%83%93%E3%83%83%E3%83%88%E6%BC%94%E7%AE%97
なるほど、Qnilのbitが反転するわけですね。
Qnilのbitが反転して、ビットANDして、0になるということは、Qnilのbitと一致しているということになるので
処理部分に入って行って、return Nilされるということですね。なるほど。
では、実際にQnilのbitを確認してみます。
RUBY_Qnil = 4, /* ...0000 0100 */
つまり、NOT演算子(チルダ)で反転すると
1111 1011
となるわけです。これとビットANDして0ということは
0000 0100
であるわけですから、Qnilで問題ありません。
ここで、問題になるのは、RUBY_Qfalse
ですよね。
こちらについても調べてみましょう。
RUBY_Qfalse = 0, /* ...0000 0000 */
こう書いてありました。これは盲点でした。
全部、0だったらビットANDシテモ、絶対0になりますよね。
これで、長かったTYPE()関数ともお別れです。
最後は RB_BUILTIN_TYPE(obj);
return RB_BUILTIN_TYPE(obj);
とりあえず、いつも通り調べてみましょう。
ruby.hの534行めにありました。
#define RB_BUILTIN_TYPE(x) (int)(((struct RBasic*)(x))->flags & RUBY_T_MASK)
きたー!RBasicのflagsの勉強とビットANDが繋がりました。
今までの努力が身を結びました。
すべてのオブジェクトが持つ構造体である、RBasicのflagsにアクセスして、RUBY_T_MASKとビットANDするわけです。
それで、TYPEがわかるIntを返すと。
ちなみに、RUBY_T_MASKは
RUBY_T_MASK = 0x1f
と先ほど定義されていました。
0x1fとは31のことで2進数で表すと
0001 1111
のことです。
戻り値であるIntからTYPEを判明するのは、ruby_value_typeを調べればいけそうな予感です。
長かった、、、、笑
これでようやっと、flagsの使い方と必要性が理解できました。
今日はこれくらいにしておきます笑
明日は、flagsの代入部分と、klassについての勉強をしていこうと思いやす。
2016/06/03/03:57
6月4日
さて、今日はflagsの代入部分とklassについて勉強していきます。
とりあえず、flagsの代入部分を探しましょう。
flagsの代入部分
おそらく、何かのインスタンスを作成するときにflagsが設定されるはずなので、
今回は、この関数について追っていこうと思います。
rb_str_new();
これはstring.cにありました。やっと、headerファイルから抜けました笑
static VALUE
str_new0(VALUE klass, const char *ptr, long len, int termlen)
{
VALUE str;
if (len < 0) {
rb_raise(rb_eArgError, "negative string size (or size too big)");
}
RUBY_DTRACE_CREATE_HOOK(STRING, len);
str = str_alloc(klass);
if (len > RSTRING_EMBED_LEN_MAX) {
RSTRING(str)->as.heap.aux.capa = len;
RSTRING(str)->as.heap.ptr = ALLOC_N(char, len + termlen);
STR_SET_NOEMBED(str);
}
else if (len == 0) {
ENC_CODERANGE_SET(str, ENC_CODERANGE_7BIT);
}
if (ptr) {
memcpy(RSTRING_PTR(str), ptr, len);
}
STR_SET_LEN(str, len);
TERM_FILL(RSTRING_PTR(str) + len, termlen);
return str;
}
static VALUE
str_new(VALUE klass, const char *ptr, long len)
{
return str_new0(klass, ptr, len, 1);
}
VALUE
rb_str_new(const char *ptr, long len)
{
return str_new(rb_cString, ptr, len);
}
rb_cString以外のklassが入ることもあるみたいね。
ちなみに、rb_cStringはここで定義されています。
rb_cString = rb_define_class("String", rb_cObject);
せっかくなので、rb_define_classもみておきましょう。
と、思ったのですが、また長くなりそうな雰囲気のあるコードが出てきたので
なので、一旦flagsの代入部分を済ませてから帰ってこようと思います。
とりあえず、コードの流れ的には、
rb_str_new → str_new → str_new0の順番で呼ばれます。
rb_str_newから、rb_cStringをstr_newに渡します。で、そのままstr_new0に渡します。
str_newがある意味ってなんなんだろうかともちょっと思いました笑
では、str_new0のメソッドを見ていきます。
まず最初に文字列の長さチェックがあるみたいです。
if (len < 0) {
rb_raise(rb_eArgError, "negative string size (or size too big)");
}
lenが0以下であれば、rb_raise、rb_eArgErrorの例外処理が発生します。
後で、rb_raiseの方も見ていきましょう!
でも、とりあえず今は本筋を進めていきます。
例外処理を超えると、RUBY_DTRACE_CREATE_HOOK(STRING, len);
が表れます。
なんだこれはということで調べます。
#define RUBY_DTRACE_CREATE_HOOK(name, arg) \
RUBY_DTRACE_HOOK(name##_CREATE, arg)
#define RUBY_DTRACE_HOOK(name, arg) \
do { \
if (UNLIKELY(RUBY_DTRACE_##name##_ENABLED())) { \
int dtrace_line; \
const char *dtrace_file = rb_source_loc(&dtrace_line); \
if (!dtrace_file) dtrace_file = ""; \
RUBY_DTRACE_##name(arg, dtrace_file, dtrace_line); \
} \
} while (0)
コード中に出てくるバックスラッシュはなんだ、一行のコードを改行するやつかな?
rb_raise
rb_define_class
VALUE
rb_define_class(const char *name, VALUE super)
{
VALUE klass;
ID id;
id = rb_intern(name);
if (rb_const_defined(rb_cObject, id)) {
klass = rb_const_get(rb_cObject, id);
if (!RB_TYPE_P(klass, T_CLASS)) {
rb_raise(rb_eTypeError, "%s is not a class (%"PRIsVALUE")",
name, rb_obj_class(klass));
}
if (rb_class_real(RCLASS_SUPER(klass)) != super) {
rb_raise(rb_eTypeError, "superclass mismatch for class %s", name);
}
return klass;
}
if (!super) {
rb_warn("no super class for `%s', Object assumed", name);
}
klass = rb_define_class_id(id, super);
rb_vm_add_root_module(id, klass);
rb_name_class(klass, id);
rb_const_set(rb_cObject, id, klass);
rb_class_inherited(super, klass);
return klass;
}
ちなみに、macがどんなbitかわからなかったので、調べてみたら
#include<stdio.h>
int main() {
printf("long_size is %lu byte\npointer_size is %lu byte\n", sizeof(long), sizeof(void*));
return 0;
}
long_size is 8 byte
pointer_size is 8 byte
とのように出たので、longもpointerも64bitなので、Linux x64と同じ状態です。
union(struct{long numiv, VALUE *ivptr, void *iv_index_tbl})
VALUE ary[ROBJECT_EMBED_LEN_MAX];
いつもお世話になっています
Rubyソースコード完全解説
http://i.loveruby.net/ja/rhg/book/