MacRuby on Rails #20110321-1
Module.class_evalやModule.module_evalがレシーバの定数しか探索しない不具合
不具合の説明
現在のMacRubyでは以下のスクリプトを実行すると定数が見つからないという例
外が発生する。当然cRubyでは動作する。ActiveSupportの随所でこのような実
装があるため、Railsを動かすためには必須となる。
(1)
module A B = 10 Object.class_eval { p B } # => uninitialized constant Object::B (NameError) end
(2)
module A B = 10 def f class_eval { p B } # => uninitialized constant K::B (NameError) end end class K extend A f end
(3)
module A B = 10 def f class_eval <<-EOS # => uninitialized constant K::B (NameError) p B EOS end end class K extend A f end
関連する(と思われる)チケット
- #619: Constant scope in a block is determined at run-time?
- #828: A NameError, raised from evalling, gets the wrong constant name.
- #1095: Segfault on uninitialized constant in Rspec2 test
- #1167: NameError: uninitialized constant Hpricot::Container::Trav::Traverse
- #1192: Did not find nested constants.
作業ログ
以下の修正を行って動作を追ってみる。
diff --git a/vm.cpp b/vm.cpp index 450f46a..8205c7f 100644 --- a/vm.cpp +++ b/vm.cpp @@ -1247,6 +1247,7 @@ rb_vm_const_lookup_level(VALUE outer, uint64_t outer_mask, ID path, { rb_vm_check_if_module(outer); + printf("rb_vm_const_lookup_level: outer(%s) path(%s) lexical(%s)\n", class_getName((Class)outer), rb_id2name(path), lexical ? "true" : "false"); if (lexical) { // Let's do a lexical lookup before a hierarchical one, by looking for // the given constant in all modules under the given outer.
出力結果。outerがNSObject(Object)であることがBをlookupできない原因だろう。
set outer of A to NSObject (0x0) define module NSObject::A rb_vm_const_lookup_level: outer(A) path(Object) lexical(true) rb_vm_const_lookup_level: outer(NSObject) path(B) lexical(true) resolving -[RubyObject initialize:] /Users/kouji/work/MacRuby/work/demo/test.rb:3:in `block': uninitialized constant B (NameError) from /Users/kouji/work/MacRuby/work/demo/test.rb:1:in `<main>' functions all=166 compiled=2
しかし、どうやってouterをAに設定すればよいのだろうか。
無理矢理outerをAに設定してみるとたしかに動作するようになった。
diff --git a/vm.cpp b/vm.cpp index 450f46a..7dee534 100644 --- a/vm.cpp +++ b/vm.cpp @@ -1245,8 +1245,20 @@ VALUE rb_vm_const_lookup_level(VALUE outer, uint64_t outer_mask, ID path, bool lexical, bool defined) { + static VALUE save_value = Qnil; + rb_vm_check_if_module(outer); + printf("rb_vm_const_lookup_level: outer(%s) path(%s) lexical(%s)\n", class_getName((Class)outer), rb_id2name(path), lexical ? "true" : "false"); + + if (save_value != Qnil) { + printf("changed outer to B\n"); + outer = save_value; + printf("rb_vm_const_lookup_level: outer(%s) path(%s) lexical(%s)\n", class_getName((Class)outer), rb_id2name(path), lexical ? "true" : "false"); + } + + save_value = outer; + if (lexical) { // Let's do a lexical lookup before a hierarchical one, by looking for // the given constant in all modules under the given outer.
以下、実行結果。
set outer of A to NSObject (0x0) define module NSObject::A rb_vm_const_lookup_level: outer(A) path(Object) lexical(true) rb_vm_const_lookup_level: outer(NSObject) path(B) lexical(true) changed outer to A rb_vm_const_lookup_level: outer(A) path(B) lexical(true)
いろいろ調査した結果、次の修正で(1)(2)が動作するようになった。(3)はまだ
対応できていない。これは実行時に定数の参照を行うため、現在の修正では手
遅れなのだ。
diff --git a/kernel.c b/kernel.c index d37cf32..52e757e 100644 --- a/kernel.c +++ b/kernel.c @@ -148,8 +148,22 @@ vm_get_const(VALUE outer, uint64_t outer_mask, void *cache_p, ID path, if (dynamic_class) { Class k = rb_vm_get_current_class(); - if (lexical_lookup && k != NULL) { - outer = (VALUE)k; + if (lexical_lookup && k != NULL && outer != (VALUE)k) { + if (cache->outer == outer && cache->outer_mask == outer_mask + && cache->val != Qundef) { + return cache->val; + } + else { + VALUE val = rb_vm_const_lookup_level(outer, outer_mask, path, + lexical_lookup, false, true); + if (val != Qundef) { + cache->outer = outer; + cache->outer_mask = outer_mask; + cache->val = val; + return val; + } + } + outer = (VALUE)k; } } @@ -160,7 +174,7 @@ vm_get_const(VALUE outer, uint64_t outer_mask, void *cache_p, ID path, } else { val = rb_vm_const_lookup_level(outer, outer_mask, path, - lexical_lookup, false); + lexical_lookup, false, false); cache->outer = outer; cache->outer_mask = outer_mask; cache->val = val; diff --git a/vm.cpp b/vm.cpp index 8024f70..e023908 100644 --- a/vm.cpp +++ b/vm.cpp @@ -1275,7 +1275,7 @@ retry: extern "C" VALUE rb_vm_const_lookup_level(VALUE outer, uint64_t outer_mask, ID path, - bool lexical, bool defined) + bool lexical, bool defined, bool only_lookup) { rb_vm_check_if_module(outer); @@ -1304,7 +1304,7 @@ rb_vm_const_lookup_level(VALUE outer, uint64_t outer_mask, ID path, } // Nothing was found earlier so here we do a hierarchical lookup. - return defined ? rb_const_defined(outer, path) : rb_const_get(outer, path); + return defined ? rb_const_defined(outer, path) : (only_lookup ? rb_const_get_direct(outer, path) : rb_const_get(outer, path)); } extern "C" diff --git a/vm.h b/vm.h index 4e7db95..e7f4568 100644 --- a/vm.h +++ b/vm.h @@ -321,11 +321,11 @@ void rb_vm_const_is_defined(ID path); VALUE rb_vm_resolve_const_value(VALUE val, VALUE klass, ID name); VALUE rb_vm_const_lookup_level(VALUE outer, uint64_t outer_mask, ID path, - bool lexical, bool defined); + bool lexical, bool defined, bool only_lookup); static inline VALUE rb_vm_const_lookup(VALUE outer, ID path, bool lexical, bool defined) { - return rb_vm_const_lookup_level(outer, 0, path, lexical, defined); + return rb_vm_const_lookup_level(outer, 0, path, lexical, defined, false); } bool rb_vm_lookup_method(Class klass, SEL sel, IMP *pimp,
修正後にrake spec:rubyspecを実行して、問題の有無をチェックする。
Kernel.autoloadのspecでフリーズしてしまうため、それをコメントアウトした。
Changes in HEAD Modified spec/frozen/core/kernel/autoload_spec.rb diff --git a/spec/frozen/core/kernel/autoload_spec.rb b/spec/frozen/core/kernel/autoload_spec.rb index 096d0d1..600d0fb 100644 --- a/spec/frozen/core/kernel/autoload_spec.rb +++ b/spec/frozen/core/kernel/autoload_spec.rb @@ -37,17 +37,17 @@ describe "Kernel#autoload" do Object.should have_constant(:KSAutoloadA) end - it "loads the file when the constant is accessed" do - KSAutoloadB.loaded.should == :ksautoload_b - end - - it "does not call Kernel.require or Kernel.load to load the file" do - filename = fixture(__FILE__, "autoload_c.rb") - autoload :KSAutoloadC, filename - Kernel.should_not_receive(:require) - Kernel.should_not_receive(:load) - KSAutoloadC.loaded.should == :ksautoload_c - end +# it "loads the file when the constant is accessed" do +# KSAutoloadB.loaded.should == :ksautoload_b +# end +# +# it "does not call Kernel.require or Kernel.load to load the file" do +# filename = fixture(__FILE__, "autoload_c.rb") +# autoload :KSAutoloadC, filename +# Kernel.should_not_receive(:require) +# Kernel.should_not_receive(:load) +# KSAutoloadC.loaded.should == :ksautoload_c +# end end describe "Kernel#autoload?" do
その後、rake spec:rubyspecを実行したところ以下の結果になった。
1) Fixnum#* overflows to Bignum when the result does not fit in Fixnum FAILED Expected 0 to equal 1237940039285380274899124224 /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/fixnum/multiply_spec.rb:27:in `block' /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/fixnum/multiply_spec.rb:3:in `<main>' 2) Kernel#sleep pauses execution indefinitely if not given a duration ERROR ThreadError: stopping only thread note: use sleep to stop forever /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/kernel/sleep_spec.rb:33:in `block' /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/kernel/sleep_spec.rb:4:in `<main>' Finished in 182.760528 seconds 3091 files, 13427 examples, 53509 expectations, 1 failure, 1 error rake aborted! Command failed with status (1): [./mspec/bin/mspec ci -B ./spec/macruby.msp...] /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:995:in `sh' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1010:in `call' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1010:in `sh' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1094:in `sh' /Users/kouji/work/MacRuby/MacRuby/rakelib/spec.rake:7:in `mspec' /Users/kouji/work/MacRuby/MacRuby/rakelib/spec.rake:34 /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `call' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `execute' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `each' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `execute' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:597:in `invoke_with_call_chain' /Users/kouji/local/lib/ruby/1.8/monitor.rb:242:in `synchronize' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:590:in `invoke_with_call_chain' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:583:in `invoke' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2051:in `invoke_task' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `each' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2023:in `top_level' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2001:in `run' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1998:in `run' /Users/kouji/local/lib/ruby/gems/1.8/gems/rake-0.8.7/bin/rake:31 /Users/kouji/local/bin/rake:19:in `load' /Users/kouji/local/bin/rake:19
修正前は以下の結果となった。autoloadのテストは全てパスしているため、私
の修正でバグが入ったようだ。しかしながら、それはおいといて(3)に対応させ
る。
1) Fixnum#* overflows to Bignum when the result does not fit in Fixnum FAILED Expected 0 to equal 1237940039285380274899124224 /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/fixnum/multiply_spec.rb:27:in `block' /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/fixnum/multiply_spec.rb:3:in `<main>' 2) Kernel#sleep pauses execution indefinitely if not given a duration ERROR ThreadError: stopping only thread note: use sleep to stop forever /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/kernel/sleep_spec.rb:33:in `block' /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/kernel/sleep_spec.rb:4:in `<main>' 3) Zlib::GzipFile#close finishes the stream and closes the io FAILED Expected "\x00\x00\x00\x00\x00\x00\x00\x00\x00" to equal "\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00" /Users/kouji/work/MacRuby/MacRuby/spec/frozen/library/zlib/gzipfile/close_spec.rb:20:in `block' /Users/kouji/work/MacRuby/MacRuby/spec/frozen/library/zlib/gzipfile/close_spec.rb:6:in `<main>' 4) GzipWriter#write writes some compressed data FAILED Expected "4261MLJNI\x05\x00\x9D\x05\x00$\n\x00\x00\x00" to equal "34261MLJNI\x05\x00\x9D\x05\x00$\n\x00\x00\x00" /Users/kouji/work/MacRuby/MacRuby/spec/frozen/library/zlib/gzipwriter/write_spec.rb:20:in `block' /Users/kouji/work/MacRuby/MacRuby/spec/frozen/library/zlib/gzipwriter/write_spec.rb:6:in `<main>' Finished in 183.767537 seconds 3091 files, 13429 examples, 53514 expectations, 3 failures, 1 error
-
-
- 2011-03-20 21:20:34
-
(3)の対応を優先するのは間違いないが、少しだけautoloadが失敗する原因を追っ
てみた。
$ cd ~/work/MacRuby/MacRuby $ env DYLD_LIBRARY_PATH=. ./macruby -I. -I./lib -I./ext -I./ext/ripper/lib -v ./mspec/bin/mspec-ci -B ./spec/macruby.mspec --verbose ./spec/frozen/core/kernel/autoload_spec.rb > issues/1192/output.autoload_spec.log $ grep dynamic -B2 -A2 issues/1192/output.autoload_spec.log (以下、実行結果) set outer of RBAnonymous18 to MSpec (0x101641840) vm_get_const: outer(MSpec) vm_get_const: dynamic outer(RBAnonymous18) resolving -[String to_path] /Users/kouji/work/MacRuby/MacRuby/spec/frozen/core/kernel/autoload_spec.rb:129 ... NODE_SCOPE -- vm_get_const: outer(RBAnonymous20) vm_get_const: outer(Inner) vm_get_const: dynamic outer(RBAnonymous20) set outer of EvalTest to KernelSpecs (0x104c5da80) define class KernelSpecs::EvalTest < NSObject
私の修正が影響するのは上記のdynamic outerが記録されている部分である。
たかだか2カ所のようなので、時間があれば原因を追えないこともないだろう。
しかしながら、今は(3)の対応を優先する。
なぜならば、(3)に対応するときに現在の修正内容をガラッと変える予定だからだ。
-
-
- 2011-03-20 21:45:52
-
さて、気を取り直して(3)に対応する。
(3)
module A B = 10 def f class_eval <<-EOS # => uninitialized constant K::B (NameError) p B EOS end end class K extend A f end
$ env DYLD_LIBRARY_PATH=. ./macruby -I. -I./lib -I./ext -I./ext/ripper/lib issues/1192/test3.rb > issues/1192/output.test3.log $ tail -n 5 issues/1192/output.test3.log (以下、実行結果) /Users/kouji/work/MacRuby/MacRuby/(eval):1:in `<main>': uninitialized constant K::B (NameError) from /Users/kouji/work/MacRuby/MacRuby/issues/1192/test3.rb:4:in `f' from /Users/kouji/work/MacRuby/MacRuby/issues/1192/test3.rb:12:in `block' from /Users/kouji/work/MacRuby/MacRuby/issues/1192/test3.rb:10:in `<main>' functions all=173 compiled=4
定数の探索をcRubyのようにすればよい。つまり、メソッド呼び出し時に
ruby_frame->cbaseを記録しておき、定数の探索の基点をruby_frame->cbaseと
すればよい。
MacRubyにおける現在パーズしているクラスはcompile_current_classで取得で
きる。メソッド定義のタイミングでそれを格納しておけばよさそう。ただし、
ruby_frame->cbaseに相当するようなよい格納場所がないように思う。superの
呼び出しを実装しているくらいだから、argcやargsを格納している構造体に
compile_current_classも格納できればよいのだと想像している。が、実際のと
ころはソースコードを解析してみないと分からない。
それでは調査や実験などを開始する。
-
-
- 2011-03-20 22:10:50
-
調査の過程で、あらためてcRubyのev_const_get(cref, id, self)を読んでみる
と、MacRubyの実装とは異なる部分があるように思う。もう少し詳しく追ってみ
ないと判断できないが、あとで確認してみる。
あと、定数の探索にcrefとselfは必要な気がしてきた。いまはMacRubyの
rb_vm_const_lookup_levelにはselfのクラスがouterという名前で渡されている。
typedef struct { Function *func; rb_vm_arity_t arity; int flags; } rb_vm_method_source_t;
- > これにklassを追加?method_sourcesにClassとメソッド定義の実体を示すmapがある。
これが実体であればいいのだが。つまり、KではなくA。
compiler.cppのcompile_prepare_methodは期待していたものではないようだ。
copy_node_scope相当の処理はない。
以下のNODEをたどるときにcurrent_classを記録する必要がある。
issues/1192/test3.rb:3 .................. NODE_DEFN issues/1192/test3.rb:7 ..................... NODE_SCOPE issues/1192/test3.rb:6 ........................ NODE_FCALL issues/1192/test3.rb:4 ........................ NODE_STR
そして、以下でFCALLのときにcurrent_classを使う。
resolving +[K f] defining +[RBAnonymous5 f] with imp 0x101700ea0/0x101700db0 types @@: flags 0 arity 0 (eval):1 ... NODE_SCOPE (eval):1 ...... NODE_FCALL (eval):1 ...... NODE_CONST
resolving +[K f]のときに[A f]が分かればよいのだろうか?
-
-
- 2011-03-21 14:04:42
-
以下の実行結果から全然、cRubyと挙動が違うことが分かった。
cymric$ env DYLD_LIBRARY_PATH=. ./macruby -I. -I./lib -I./ext -I./ext/ripper/lib issues/1192/test5.rb > issues/1192/output.test5.log Array's L A's B
修正前はどうだろうか?
cymric$ env DYLD_LIBRARY_PATH=. ./macruby -I. -I./lib -I./ext -I./ext/ripper/lib issues/1192/test5.rb Array's L Array's B
なんだ、修正前でもだめじゃん。それならば私の修正で改善はしているね。
cRubyを実行した結果、Array::Lを見つけてはダメなようだ。つまり、Kは定数探索の対象外になる。
cymric$ ruby187 issues/1192/test5.rb issues/1192/test5.rb:10:in `f': uninitialized constant A::L (NameError) from issues/1192/test5.rb:9:in `class_eval' from issues/1192/test5.rb:9:in `f' from issues/1192/test5.rb:19 cymric$ ruby19trunk issues/1192/test5.rb issues/1192/test5.rb:10:in `block in f': uninitialized constant A::L (NameError) from issues/1192/test5.rb:9:in `class_eval' from issues/1192/test5.rb:9:in `f' from issues/1192/test5.rb:19:in `<class:K>' from issues/1192/test5.rb:16:in `<main>'
もう少し問題を整理する。
まずは(1)(2)のケースで、class_evalをブロック付きメソッド呼び出しする場合。
issues/1192/test5.rb
class Array B = "Array's B" L = "Array's L" end module A B = "A's B" def f Array.class_eval { begin $stderr.puts L rescue NameError $stderr.puts $! end begin $stderr.puts B rescue NameError $stderr.puts $! end } class_eval { begin $stderr.puts L rescue NameError $stderr.puts $! end begin $stderr.puts B rescue NameError $stderr.puts $! end } end end class K L = "K's L" extend A f end
cRubyとMacRubyの実行結果。
$ ruby19trunk issues/1192/test5.rb uninitialized constant A::L A's B uninitialized constant A::L A's B $ env DYLD_LIBRARY_PATH=. ./macruby -I. -I./lib -I./ext -I./ext/ripper/lib issues/1192/test5.rb > /dev/nullcompilation of LLVM function 0x101398610 done, took 28842081 ns Array's L A's B K's L A's B
class_evalにブロックを渡したときはclass_evalのレシーバを探索してしまっ
ている。その結果、Array's LやK's Lを見つけている。見つけられないのが正
しい挙動。
次に(3)のケースで、class_evalに文字列を指定した場合。
issues/1192/test4.rb
class Array B = "Array's B" L = "Array's L" end module A B = "A's B" def f Array.class_eval <<-EOS begin $stderr.puts L rescue NameError $stderr.puts $! end begin $stderr.puts B rescue NameError $stderr.puts $! end EOS class_eval <<-EOS begin $stderr.puts L rescue NameError $stderr.puts $! end begin $stderr.puts B rescue NameError $stderr.puts $! end EOS end end class K L = "K's L" extend A f end
cRubyとMacRubyの実行結果。
$ ruby19trunk issues/1192/test4.rb Array's L Array's B K's L A's B $ env DYLD_LIBRARY_PATH=. ./macruby -I. -I./lib -I./ext -I./ext/ripper/lib issues/1192/test4.rb > /dev/null Array's L Array's B K's L uninitialized constant K::B
class_evalに文字列を指定したときはclass_evalの呼び出しを記述しているAを探索していない。
ている。その結果、Array's LやK's Lを見つけている。見つけられないのが正
しい挙動。
-
-
- 2011-03-21 16:42:18
-
vm_get_const()の中でdynamic_classが設定されているときにouterを実行時に変える。その処理を実行しないようにするとtest5.rbが期待通りに動作する。
それではdynamic_classが設定されるのはどんなときか?
ブロック。Procのためかな。定数の探索では考慮する必要がないような気がする。
Value * RoxorCompiler::compile_block(NODE *node) { ... dynamic_class = true; ... } inline Value * RoxorCompiler::compile_node0(NODE *node) { ... case NODE_CLASS: case NODE_SCLASS: case NODE_MODULE: ... dynamic_class = false; ... }
コードを見ているとouterをよく見る。私はouterはスーパークラスのことだと
想像している。今回必要なのはouterではなく、ソースコード上でメソッド定義
が出現したときのcrefなのだが。
-
-
- 2011-03-21 19:36:47
-
その後の調査でclass_evalのときのレシーバは定数の探索から無視しても良い
ことが分かった。一度1.9系で現在のMacRubyの仕様になったあと元に戻された
ようだ。ということで、以下がその挙動にするパッチ。とても簡単でかつ、
(3)のケース以外はパスする。
Changes in HEAD Modified compiler.cpp diff --git a/compiler.cpp b/compiler.cpp index 94af918..526ed1c 100644 --- a/compiler.cpp +++ b/compiler.cpp @@ -1623,9 +1623,6 @@ RoxorCompiler::compile_const(ID id, Value *outer) if (!outer_given) { flags |= CONST_LOOKUP_LEXICAL; } - if (dynamic_class) { - flags |= CONST_LOOKUP_DYNAMIC_CLASS; - } Value *args[] = { outer, Modified kernel.c diff --git a/kernel.c b/kernel.c index d37cf32..59fe0e3 100644 --- a/kernel.c +++ b/kernel.c @@ -144,14 +144,6 @@ vm_get_const(VALUE outer, uint64_t outer_mask, void *cache_p, ID path, { struct ccache *cache = (struct ccache *)cache_p; const bool lexical_lookup = (flags & CONST_LOOKUP_LEXICAL); - const bool dynamic_class = (flags & CONST_LOOKUP_DYNAMIC_CLASS); - - if (dynamic_class) { - Class k = rb_vm_get_current_class(); - if (lexical_lookup && k != NULL) { - outer = (VALUE)k; - } - } VALUE val; if (cache->outer == outer && cache->outer_mask == outer_mask
しかし、rubyspecを実行するとうまく動作しない。これだけではダメだ。
原因はselfの探索をしていないからだろうか。
と、ここまで調べたところで、そもそもcRubyと同じようにcrefを持たせれば良
いと思うようになった。険しい道だろうけど、そのほうが難しいし、いいね!!