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を持たせれば良
いと思うようになった。険しい道だろうけど、そのほうが難しいし、いいね!!