2016-05-13 8 views
3

Rubyのソースコードでもっと勉強しようと思っています。Rubyソースを変更する - メソッドを変更できない

ソースからRubyをコンパイルすると、メソッド定義に加えた変更を認識していないようです。しかし、新しいメソッドを追加すると、文字列に、変更されたメソッドをポイントすると、新しいメソッドは期待通りに機能します。

# string.c 

static VALUE 
rb_str_empty(VALUE str) 
{ 
    return Qtrue; 
} 

... 

rb_define_method(rb_cString, "empty?", rb_str_empty, 0); 
rb_define_method(rb_cString, "my_empty?", rb_str_empty, 0); 

その後のRubyコンソールでは、我々は新しいメソッドが新しい定義を反映していることがわかりますが、方法が変更されていないかのように古い方法はまだ動作します。

$ irb 
> "sdf".my_empty? 
true 
> "sdf".empty? 
false 

元のメソッド定義を「保護する」方法は?変更を登録するにはどうすればよいですか?

+0

以下の@mattによる答えは、同じプロパティを示しています。ここでは、 asdf ".send(:empty?)#=> true'。 –

答えて

4

現在のバージョンのRubyは仮想マシンを使用しています。 Rubyコードを実行すると、最初にバイトコードにコンパイルされ、次にこのバイトコードが仮想マシンによって実行されます。 VMには、変数の割り当て、クラスの作成、メソッドの定義、およびメソッドのディスパッチ(この状況ではインポート)のための命令が含まれています。しかし、一般的に使用されるいくつかのメソッドでは、通常のメソッドディスパッチプロシージャをバイパスする特殊な最適化バイトコード命令もあります。 empty? is one such method

ビットコードは、RubyVM::InstructionSequence.compile(および表示するにはdisasm)で確認できます。 (実在しない方法foo付き)まず「通常の」メソッドディスパッチ:

> puts RubyVM::InstructionSequence.compile('"asdf".foo').disasm 
== disasm: #<ISeq:<compiled>@<compiled>>================================ 
0000 trace   1            ( 1) 
0002 putstring  "asdf" 
0004 opt_send_without_block <callinfo!mid:foo, argc:0, ARGS_SIMPLE>, <callcache> 
0007 leave 

opt_send_without_blockfoomidは「メソッドID」である)をコールしようとすると、メソッドのディスパッチ命令です。 empty?のための最適化されたバイトコードと今

> puts RubyVM::InstructionSequence.compile('"asdf".empty?').disasm 
== disasm: #<ISeq:<compiled>@<compiled>>================================ 
0000 trace   1            ( 1) 
0002 putstring  "asdf" 
0004 opt_empty_p  <callinfo!mid:empty?, argc:0, ARGS_SIMPLE>, <callcache> 
0007 leave 

opt_empty_pempty?方法に特化したバイトコード命令です。

この命令のソースをsource for the normal function implementing String#empty?(変更した機能)と比較すると、case of the receiver being a Stringの命令コードでは、その機能を完全にバイパスしていることがわかりますメソッドのディスパッチコードをバイパスして同じ実装を使用して、実装する関数への直接呼び出し)。

この命令には、Rubyでメソッドが置き換えられていないことを確認するチェックが含まれていますが、明らかにここのようなCソースの変更は含まれていません。

私はあなたがsendを使用している場合、これは最適化された命令にコンパイルされませんので、あなたは、機能の変更したバージョンを取得すべきだと思う:あなたはルビーを編集して再コンパイルするように設定されている場合は、あなたがすべき

'asdf'.send :empty? 

また、ファイルinsns.defの命令自体を変更することができます。このファイルは、ビルドプロセス中に命令のコードを作成するために使用されます。それはC自体ではありませんが、各命令ブロックの内容は単なるCです。

+0

ありがとう@matt。これはすべてのことを学ぶことです。私は実際に私の検索で 'opt_empty_p'を見つけたが、それを理解していなかった。深い答えをありがとう。 – steel

関連する問題