Rubyのしくみ Ruby Under a Microscope

前表紙
株式会社 オーム社, 2014/11/29 - 408 ページ

 もっと知りたい、Rubyのしくみ

VMベースのインタプリタ型言語処理系であるRubyがコードをどのように解釈し、どうやって実行するか、そのしくみを解説。Rubyについての基礎知識がなくても、図版と短いコードの実験を多用した構成により、そのしくみについて理解することができます。

実務でRubyは使えるけれど、基礎知識について自信がない人や、学びたくてもまとまった時間がとれずに悩んでいる人などもっとRubyを活用するためにRubyを知りたい人に最適。

Rubyインタプリタを題材にプログラミング言語処理系の仕組みを解説するNo Starch Press社の“Ruby Under a Microscope” の翻訳発行です。

日本語版には、Rubyの開発者であるまつもとゆきひろ氏の序文とYARVの開発者である笹田耕一氏の付録が加筆されています。


日本語版序文
序文
謝辞
はじめに
対象読者
検証にRubyを使う
どのRuby処理系なの?
本書の構成
第1章 字句解析と構文解析
字句解析:Rubyを構成する言葉
parser_yylex 関数
実験1-1:Ripperを使ってさまざまなRubyスクリプトを字句解析す
構文解析:Rubyはコードをどのように理解するか
LALR 構文解析アルゴリズムを理解する
実際のRubyの文法規則をいくつか
Bisonの文法規則を読む
実験1-2:Ripperを使ってさまざまなRubyスクリプトを構文解析す
まとめ
第2章 コンパイル
Ruby 1.8 にコンパイラはない
Ruby 1.9 以降はコンパイラを導入する
単純なスクリプトをRubyはどうコンパイルするか
ブロック呼び出しのコンパイル
RubyはAST の中をどう反復していくか
実験2-1:YARV 命令を表示する
ローカルテーブル
オプション引数のコンパイル
キーワード引数のコンパイル
実験2-2:ローカルテーブルを表示する
まとめ
第3章 Rubyはどのようにコードを実行するか
YARVの内部スタックとRubyのコールスタック
Rubyが単純なスクリプトをどう実行するかを見ていく
ブロック呼び出しの実行
YARV命令を間近に見てみる
実験3-1:Ruby 2.0/Ruby 1.9 とRuby 1.8 のベンチマーク比較
Ruby変数のローカルアクセスと動的アクセス
ローカル変数アクセス
メソッド引数はローカル変数とみなされる
動的変数アクセス
Cの世界でEPのはしごを上る
実験3-2:特殊変数を調査する
特殊変数リストの決定版
まとめ
第4章 制御構造とメソッドディスパッチ
Rubyがどうやってif 文を実行するか
あるスコープから別のスコープへジャンプする
捕捉テーブル
捕捉テーブルの別の利用方法
実験4-1:Rubyがループを内部でどう実装しているかをテストする
send 命令:Rubyで最も複雑な制御構造
メソッド探索とメソッドディスパッチ
11種類のメソッドタイプ
通常のメソッド呼び出し
通常のメソッド向け引数の準備
組み込みメソッド呼び出し
attr_readerとattr_writerの呼び出し
attr_readerとattr_writerにおけるメソッドディスパッチの最適化
実験4-2:Rubyがキーワード引数をどう実装しているかの調査
まとめ
第5章 オブジェクトとクラス
Rubyオブジェクトの内側
klassとivptrの調査
クラスの2つのインスタンスの可視化
一般的なオブジェクト
単純なRubyの値は構造体を全く必要としない
一般的なオブジェクトはインスタンス変数を持つか
RBasic構造体とRObject構造体の定義を読む
Rubyは一般的なオブジェクト用のインスタンス変数をどこに保存するか?
実験5-1:新しいインスタンス変数を保存するのにどれくらい時間がかかるか
RClass 構造体の内側は何
継承
クラスインスタンス変数vs. クラス変数
クラス変数の取得と設定
定数
実際のRClass構造体
RClass構造体の定義を読む
実験5-2:Rubyはクラスメソッドをどこに保存する?
まとめ
第6章 メソッド探索と定数探索
Rubyがモジュールをどう実装しているか
モジュールはクラスである
クラスにモジュールをインクルードする
Rubyのメソッド探索アルゴリズム
メソッド探索の例
実際のメソッド探索アルゴリズム
Rubyにおける多重継承
グローバルメソッドキャッシュ
インラインメソッドキャッシュ
Rubyのメソッドキャッシュをクリアする
あるクラスに2つのモジュールをインクルードする
あるモジュールを別のモジュールにインクルードする
Module#prependの例
RubyがModule#prepend をどう実装しているか
実験6-1:インクルードした後でモジュールを変更する
クラスはモジュールのメソッドを後で確認する
クラスはインクルードされたサブモジュールを後では確認しない
インクルードされたクラスは元のモジュールとメソッドテーブルを共有する
Rubyがモジュールをどうコピーしているかを詳しく見る
定数探索
スーパークラス内の定数を見つける
Rubyはどうやって親の名前空間内の定数を見つけるか?
Rubyにおけるレキシカルスコープ
新しいクラスやモジュール用に定数を生成する
レキシカルスコープを使って親の名前空間内の定数を見つける
Rubyの定数探索アルゴリズム
実験6-2:Rubyはどの定数をはじめに見つけるのか?
Rubyの実際の定数探索アルゴリズム
まとめ
第7章 ハッシュテーブル:Ruby内部の働き者
Rubyにおけるハッシュテーブル
ハッシュテーブルに値を保存する
ハッシュテーブルから値を取り出す
実験7-1:さまざまなサイズのハッシュテーブルから値を取り出す
ハッシュテーブルを拡張して、より多くの値を格納する仕組み
ハッシュの衝突
エントリの再ハッシュ
Rubyはハッシュテーブル内のエントリをどう再ハッシュするか? 
実験7-2:さまざまなサイズのハッシュに新しい要素を1つ追加する
マジックナンバー57と67はどこからくるのか
Rubyはハッシュ関数をどう実装しているか
実験7-3:ハッシュのキーとしてオブジェクトを使用する
Ruby 2.0におけるハッシュ最適化
まとめ
第8章 Lisp から借用したアイデア
ブロック:Rubyにおけるクロージャ
Rubyのブロック呼び出しを一歩ずつ確認する
1975 年から借用したアイデア
rb_block_tとrb_control_frame_t構造体
実験8-1:whileループとeachにブロックを渡すのとどちらが速いか
ラムダとProc:関数を第一級市民として扱う
スタックvs. ヒープメモリ
Rubyが文字列の値をどう保存するかを詳しく見る
Rubyはラムダをどう作るか
Rubyはラムダをどう呼び出すか
Proc オブジェクト
実験8-2:ラムダを呼び出した後でローカル変数を変更する
同じスコープでラムダを1回以上呼び出す
まとめ
第9章 メタプログラミング
メソッドを定義する別のやり方
通常のメソッド定義処理
オブジェクトのプレフィックスを使ってクラスメソッドを定義する
新しいレキシカルスコープを使ってクラスメソッドを定義する
特異クラスを使ってメソッドを定義する
レキシカルスコープ内の特異クラスを使ってメソッドを定義する
Refinementsを作成する
Refinementsを使用する
実験9-1:わたしは誰? レキシカルスコープを使うとselfはどう変わるか
トップレベルスコープでのself
クラススコープでのself
メタクラススコープでのself
クラスメソッドの内側でのself
メタプログラミングとクロージャ:eval、instance_eval、binding
コードを生成するコード
bindingと共にevalを呼び出す
instance_evalを使う例
Rubyのクロージャにおけるもう1つの重要な側面
instance_evalはself をレシーバに変更する
instance_evalは新しいレキシカルスコープ用に特異クラスを作成する
ブロック用のレキシカルスコープをRubyはどう監視し続けるか
実験9-2:メソッドを定義するためにクロージャを使う
define_methodを使う
メソッドはクロージャとして機能する
まとめ
第10章 JRuby:JVM上のRuby
MRIとJRubyでプログラムを実行する
JRubyはコードをどうパースし、コンパイルするか
JRubyはコードをどう実行するか
RubyクラスをJavaクラスを使って実装する
実験10-1:JRubyのJITコンパイラを観察する
実験コード
-J-XX:+PrintCompilationオプションの使用
JITはJRubyプログラムを高速化するか
JRubyとMRI における文字列
JRubyとMRI は文字列データをどう保存するか
コピー・オン・ライト
実験10-2:コピー・オン・ライトの性能を計測する
ユニークで共有されていない文字列の生成
実験コード
コピー・オン・ライトの視覚化
共有文字列を編集することは速度を落とす
まとめ
第11章 Rubinius:Rubyで実装されたRuby
Rubiniusカーネルと仮想マシン
字句解析と構文解析
Rubyを使ってRubyをコンパイルする
Rubiniusバイトコード命令列
RubyとC++とで一緒に動く
RubyのオブジェクトをC++オブジェクトで実装する
実験11-1:MRIとRubiniusのバックトレースの比較
Rubiniusのバックトレース
RubiniusとMRIの配列
MRIの配列の内側
RArray構造体の定義
Rubiniusの配列の内側
実験11-2:RubiniusのArray#shiftの実装を調査する
Array#shiftを読む
Array#shiftを変更する
まとめ
第12章 MRI・JRuby・RubiniusにおけるGC
ガベージコレクションは3つの問題を解決する
MRIにおけるガベージコレクション:マークスイープ
フリーリスト
MRIの複数のフリーリストの用途
マーク
MRIは生きているオブジェクトをどうマークするか
スイープ
遅延スイープ
RVALUE構造体
マークスイープのデメリット
実験12-1:MRIのガベージコレクションを実際に見ていく
MRIの遅延スイープの様子を見る
MRIのフルGCを見る
GCのプロファイリングレポートを翻訳する
JRubyとRubiniusにおけるGC
コピーGC
バンプ・アロケーション
半空間アルゴリズム
Edenヒープ
世代別GC
弱い世代別仮説
新世代オブジェクト用に半空間アルゴリズムを使う
オブジェクトを昇格する
旧世代オブジェクト用のガベージコレクション
世代間の参照
並行GC
オブジェクトグラフが変わっている間にもマークする
三色マーキング
JVMにおける3つのGC
実験12-2:JRubyのVerbose GCモードを使う
メジャーGCを始動する
参考文献
まとめ
付録A さらにそのほかのRuby仮想マシン
YARV:Yet Another RubyVM
YARVの設計方針
YARVの開発までの経緯
さらにそのほかのRuby仮想マシン
訳者あとがき
著者・訳者について

 

ページのサンプル

目次

第1章字句解析と構文解析
3
Rubyを構成する言葉
4
parser_yylex関数
7
Ripperを使ってさまざまなRubyスクリプトを字句解析する
9
Rubyはコードをどのように理解するか
12
LALR構文解析アルゴリズムを理解する
14
実際のRubyの文法規則をいくつか
21
Bisonの文法規則を読む
23
Rubyはハッシュ関数をどう実装しているか
202
ハッシュのキーとしてオブジェクトを使用する
205
Ruby 20におけるハッシュ最適化
210
まとめ
211
第8章 Lispから借用したアイデア
213
Rubyにおけるクロージャ
214
Rubyのブロック呼び出しを一歩ずつ確認する
215
1975年から借用したアイデア
218

Ripperを使ってさまざまなRubyスクリプトを構文解析する
25
まとめ
31
第2章コンパイル
33
Ruby18にコンパイラはない
34
Ruby19以降はコンパイラを導入する
35
単純なスクリプトをRubyはどうコンパイルするか
36
ブロック呼び出しのコンパイル
41
RubyはASTの中をどう反復していくか
45
YARV命令を表示する
48
ローカルテーブル
49
オプション引数のコンパイル
52
キーワード引数のコンパイル
54
ローカルテーブルを表示する
56
まとめ
58
第3章 Rubyはどのようにコードを実行するか
61
YARVの内部スタックとRubyのコールスタック
62
Rubyが単純なスクリプトをどう実行するかを見ていく
63
ブロック呼び出しの実行
66
YARV命令を間近に見てみる
68
Ruby20Ruby19とRuby18のベンチマーク比較
71
Ruby変数のローカルアクセスと動的アクセス
74
メソッド引数はローカル変数とみなされる
77
動的変数アクセス
78
Cの世界でEPのはしごを上る
81
特殊変数を調査する
83
特殊変数リストの決定版
87
まとめ
89
第4章制御構造とメソッドディスパッチ
91
Rubyがどうやってif文を実行するか
92
あるスコープから別のスコープへジャンプする
95
捕捉テーブル
96
捕捉テーブルの別の利用方法
98
Rubyがループを内部でどう実装しているかをテストする
99
Rubyで最も複雑な制御構造
101
11種類のメソッドタイプ
102
通常のメソッド呼び出し
104
通常のメソッド向け引数の準備
105
組み込みメソッド呼び出し
106
attr_readerとattr_writerの呼び出し
107
attr_readerとattr_writerにおけるメソッドディスパッチの最適化
108
Rubyがキーワード引数をどう実装しているかの調査
109
まとめ
113
第5章オブジェクトとクラス
115
Rubyオブジェクトの内側
116
klassとivptrの調査
117
クラスの2つのインスタンスの可視化
118
一般的なオブジェクト
119
単純なRubyの値は構造体を全く必要としない
120
一般的なオブジェクトはインスタンス変数を持つか?
121
RBasic構造体とRObject構造体の定義を読む
122
Rubyは一般的なオブジェクト用のインスタンス変数をどこに保存するか?
123
新しいインスタンス変数を保存するのにどれくらい時間がかかるか?
124
RClass構造体の内側は何?
127
継承
130
クラスインスタンス変数vsクラス変数
132
クラス変数の取得と設定
134
定数
137
実際のRClass構造体
138
RClass構造体の定義を読む
140
Rubyはクラスメソッドをどこに保存する?
141
まとめ
144
第6章メソッド探索と定数探索
147
Rubyがモジュールをどう実装しているか
148
クラスにモジュールをインクルードする
150
Rubyのメソッド探索アルゴリズム
151
メソッド探索の例
152
実際のメソッド探索アルゴリズム
154
Rubyにおける多重継承
156
インラインメソッドキャッシュ
157
Rubyのメソッドキャッシュをクリアする
158
あるモジュールを別のモジュールにインクルードする
160
Moduleprependの例
162
RubyがModuleprependをどう実装しているか
166
インクルードした後でモジュールを変更する
167
クラスはモジュールのメソッドを後で確認する
168
クラスはインクルードされたサブモジュールを後では確認しない
169
インクルードされたクラスは元のモジュールとメソッドテーブルを共有する
170
Rubyがモジュールをどうコピーしているかを詳しく見る
171
定数探索
173
Rubyはどうやって親の名前空間内の定数を見つけるか?
175
Rubyにおけるレキシカルスコープ
176
新しいクラスやモジュール用に定数を生成する
177
レキシカルスコープを使って親の名前空間内の定数を見つける
179
Rubyの定数探索アルゴリズム
181
Rubyはどの定数をはじめに見つけるのか?
182
Rubyの実際の定数探索アルゴリズム
183
まとめ
184
Ruby内部の働き者
187
Rubyにおけるハッシュテーブル
188
ハッシュテーブルから値を取り出す
191
さまざまなサイズのハッシュテーブルから値を取り出す
192
ハッシュテーブルを拡張してより多くの値を格納する仕組み
194
ハッシュの衝突
195
エントリの再ハッシュ
196
さまざまなサイズのハッシュに新しい要素を1つ追加する
197
マジックナンバー57と67はどこからくるのか?
200
rb_block_tとrb_control_frame_t構造体
221
whileループとeachにブロックを渡すのとどちらが速いか
222
関数を第一級市民として扱う
226
スタックvsヒープメモリ
227
Rubyが文字列の値をどう保存するかを詳しく見る
228
Rubyはラムダをどう作るか
230
Rubyはラムダをどう呼び出すか
233
Procオブジェクト
235
ラムダを呼び出した後でローカル変数を変更する
238
同じスコープでラムダを1回以上呼び出す
241
まとめ
243
第9章メタプログラミング
245
メソッドを定義する別のやり方
246
オブジェクトのプレフィックスを使ってクラスメソッドを定義する
248
新しいレキシカルスコープを使ってクラスメソッドを定義する
250
特異クラスを使ってメソッドを定義する
251
レキシカルスコープ内の特異クラスを使ってメソッドを定義する
253
Refinementsを作成する
254
Refinementsを使用する
256
わたしは誰? レキシカルスコープを使うとselfはどう変わるか
257
トップレベルスコープでのself
258
クラススコープでのself
259
メタクラススコープでのself
260
クラスメソッドの内側でのself
261
evalinstance_evalbinding
263
bindingと共にevalを呼び出す
266
instance_evalを使う例
268
Rubyのクロージャにおけるもう1つの重要な側面
269
instance_evalはselfをレシーバに変更する
271
instance_evalは新しいレキシカルスコープ用に特異クラスを作成する
272
ブロック用のレキシカルスコープをRubyはどう監視し続けるか
273
メソッドを定義するためにクロージャを使う
275
メソッドはクロージャとして機能する
276
まとめ
278
JVM上のRuby
281
MRIとJRubyでプログラムを実行する
282
JRubyはコードをどうパースしコンパイルするか
284
JRubyはコードをどう実行するか
285
RubyクラスをJavaクラスを使って実装する
288
JRubyのJITコンパイラを観察する
290
実験コード
291
+PrintCompilationオプションの使用
292
JITはJRubyプログラムを高速化するか
293
JRubyとMRIにおける文字列
295
コピーオンライト
297
コピーオンライトの性能を計測する
300
実験コード
302
共有文字列を編集することは速度を落とす
304
まとめ
305
Rubyで実装されたRuby
309
Rubiniusカーネルと仮想マシン
310
字句解析と構文解析
312
Rubyを使ってRubyをコンパイルする
313
RubyとC++とで一緒に動く
315
RubyのオブジェクトをC++オブジェクトで実装する
317
Rubiniusのバックトレース
318
RubiniusとMRIの配列
321
RArray構造体の定義
323
Rubiniusの配列の内側
324
RubiniusのArrayshiftの実装を調査する
325
Arrayshiftを読む
326
Arrayshiftを変更する
327
まとめ
330
第12章 MRIJRubyRubiniusにおけるGC
333
ガベージコレクションは3つの問題を解決する
334
MRIの複数のフリーリストの用途
335
マーク
336
MRIは生きているオブジェクトをどうマークするか
337
スイープ
338
遅延スイープ
339
マークスイープのデメリット
340
MRIの遅延スイープの様子を見る
342
MRIのフルGCを見る
343
GCのプロファイリングレポートを翻訳する
344
JRubyとRubiniusにおけるGC
348
バンプアロケーション
349
半空間アルゴリズム
350
Edenヒープ
352
弱い世代別仮説
353
旧世代オブジェクト用のガベージコレクション
354
世代間の参照
355
並行GC
357
三色マーキング
359
JVMにおける3つのGC
360
JRubyのVerbose GCモードを使う
362
メジャーGCを始動する
363
参考文献
365
付録A さらにそのほかのRuby仮想マシン
369
YARVの設計方針
370
YARVの開発までの経緯
371
さらにそのほかのRuby仮想マシン
372
訳者あとがき
373
索引
375
著者訳者について
382
奥付
383
著作権

書誌情報