kenjuの日記

About Programming, Mathematics and Security

『Rubyのしくみ -Ruby Under a Microscope』の読書メモ<第8~9章>

Rubyのしくみ -Ruby Under a Microscope-

Rubyのしくみ -Ruby Under a Microscope-

第8章 Lispから借用したアイデア

ブロックはクロージャをRuby流に実装したものだ。

Block

  • Blockは、Rubyにおけるクロージャ
  • 1975年にSussmanとSteeleによって定義されたクロージャーの定義
    • 「ラムダ式」ーつまり、引数のセットを取る関数
      • iseqはラムダ式へのポインタ
    • ラムダ(関数)を呼び出すときに使用される環境
      • EPはラムダ(関数)を呼び出した際に使われる環境へのポインタ、つまり周囲のスタックフレームへのポインタ
  • rb_block_t構造体で表現され、vm_core.hで見つけられる(Ruby 2.4においてはrb_block_type
    • 自身のローカル変数には直接rb_control_frame_t構造体を通して参照できる
    • 親のスコープの変数には、動的変数アクセスを使いEPを通して間接的に参照できる

第一級関数とlambda

  • lambdaを呼び出すと、Rubyは現在のYARVスタックフレーム全体の内容をヒープ内にコピーすると同時に、次の2つの新しいオブジェクトをヒープ内に作成する
    • 内部環境オブジェクト
    • Procオブジェクト
  • 実際には、Rubyはヒープにスタックの新しいコピーを作成すると、コピーしたところを示すようrb_control_frame_t構造体中のEPをリセットする

第9章 メタプログラミング

メタプログラミングは、Rubyが内部でどう実装しているかを学べば、はるかに付き合いやすくなる

通常のメソッド処理

  1. Rubyはそれぞれのメソッドの本体を、独立したYARV命令列のコードにコンパイル
  2. Rubyは現在のレキシカルスコープを使って、クラスあるいはモジュールのポインタを取得する
  3. Rubyは新しいメソッドの名前(実際には対応する整数値のID)をクラスのメソッドテーブルに保存する

特異クラスとメタクラス

  • 特異クラス:Rubyが特定のオブジェクトにだけ定義されたメソッドを保持するために内部的に作成される、隠された特別なクラス
  • メタクラス:オブジェクトそれ自身がクラスだった場合の特異クラス

Refinements

  • Ruby 2.0のRefinementsは、あらかじめ定義したメソッドを、任意のタイミングでクラスに追加できるようにしてくれる
    • usingを使って、refinementを有効にする
      • nd_refinementsポインタを利用して、そのスコープ内で有効なrefinementsを追跡する
  • refineに与えたブロック内で新しいメソッドを定義した場合
    • refinementモジュールに保存し、
    • refined_classポインタを辿って対象クラス内の同名メソッドを更新し、
    • メソッドタイプVM_METHOD_TYPE_REFINEDを使うようにする

eval

  • evalに文字列が渡されたとき、Bison文法規則と構文解析エンジンを使って、文字列を字句解析する(通常のRubyスクリプトのパースと同じ)
  • Rubyはevalを呼び出した場所と同じコンテキストで新しいコードの文字列を評価する
  • 第二引数にバインディングを渡すことができる
    • Rubyにバインディングを渡すことは、現在のコンテキストをクロージャの環境として使わず、代わりに別の環境を参照して欲しいということを示す
  • バインディングにはrb_binding_t構造体を利用する
    • Procオブジェクトと同じように、RubyはRTypedData構造体を使って、Cのrb_binding_t構造体にRubyオブジェクトをラップする

instance_eval

  • evalとほぼ同じだが、与えられた文字列をinstance_evalのレシーバのコンテキスト上で評価するという点が異なる

その他

  • Module.nestingを使うと、レキシカルスコープのスタックを示す配列を返す