From: nobu.nakada@... Date: 2002-09-05T12:28:30+09:00 Subject: [ruby-dev:18181] Re: autoload patch for ruby-1.7 なかだです。 At Mon, 2 Sep 2002 18:08:44 +0900, Yoshinori K. Okuji wrote: > > 失敗はしないと思います。warningは出ますが。 > > すいません。ここは私の書き方が良くなかったです。定数の場合は確かに > warningだけですが、モジュールやクラスの場合には結構奇妙なエラーになっ > てしまいます。 > > 例えば、foo.rbにこういうコードを書いて、 > > module Foo; def self.msg; puts 'foo'; end; end > module Bar; def self.msg; puts 'bar'; end; end > > シェルで、 > > $ ruby -e 'autoload :Foo, "foo"; autoload :Bar, "foo"; Foo.msg(); Bar.msg()' > > とすると、 > > uninitialized constant Bar (NameError) > > になるはずです。 load中のファイルからautoloadされる定数を未定義とみなすようにし てみました。結局ev_const_defined()での対応も必要になったり、関 数の構成を変えたりしたので、ちょっと大きいですがパッチを作り直 しました。 [ruby-dev:18139]の > それなり速くて素直なのは、逆方向のハッシュテーブルを別に持ち、あるファ > イルからautoloadされるべき定数のリストを簡単に見られるようにしておく方 > 法です。 とどっちがいいか…。 Index: class.c =================================================================== RCS file: /cvs/ruby/src/ruby/class.c,v retrieving revision 1.45 diff -u -2 -p -r1.45 class.c --- class.c 4 Sep 2002 06:37:35 -0000 1.45 +++ class.c 4 Sep 2002 13:58:21 -0000 @@ -190,7 +190,4 @@ rb_define_class(name, super) id = rb_intern(name); - if (rb_autoload_defined(id)) { - rb_autoload_load(id); - } if (rb_const_defined(rb_cObject, id)) { klass = rb_const_get(rb_cObject, id); @@ -279,7 +276,4 @@ rb_define_module(name) id = rb_intern(name); - if (rb_autoload_defined(id)) { - rb_autoload_load(id); - } if (rb_const_defined(rb_cObject, id)) { module = rb_const_get(rb_cObject, id); Index: eval.c =================================================================== RCS file: /cvs/ruby/src/ruby/eval.c,v retrieving revision 1.323 diff -u -2 -p -r1.323 eval.c --- eval.c 29 Aug 2002 09:08:15 -0000 1.323 +++ eval.c 2 Sep 2002 01:30:54 -0000 @@ -1535,4 +1535,5 @@ ev_const_defined(cref, id, self) { NODE *cbase = cref; + VALUE result; while (cbase && cbase->nd_next) { @@ -1540,5 +1541,8 @@ ev_const_defined(cref, id, self) if (NIL_P(klass)) return rb_const_defined(CLASS_OF(self), id); - if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, 0)) { + if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, &result)) { + if (result == Qundef && NIL_P(rb_autoload_p((VALUE)klass, id))) { + return Qfalse; + } return Qtrue; } @@ -1561,5 +1565,9 @@ ev_const_get(cref, id, self) if (NIL_P(klass)) return rb_const_get(CLASS_OF(self), id); - if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &result)) { + while (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &result)) { + if (result == Qundef) { + rb_autoload_load(klass, id); + continue; + } return result; } @@ -3301,7 +3309,4 @@ rb_eval(self, n) klass = 0; - if ((ruby_class == rb_cObject) && rb_autoload_defined(node->nd_cname)) { - rb_autoload_load(node->nd_cname); - } if (rb_const_defined_at(ruby_class, node->nd_cname)) { klass = rb_const_get(ruby_class, node->nd_cname); @@ -3347,7 +3352,4 @@ rb_eval(self, n) } module = 0; - if ((ruby_class == rb_cObject) && rb_autoload_defined(node->nd_cname)) { - rb_autoload_load(node->nd_cname); - } if (rb_const_defined_at(ruby_class, node->nd_cname)) { module = rb_const_get(ruby_class, node->nd_cname); @@ -6169,5 +6171,41 @@ Init_eval() } -VALUE rb_f_autoload(); +static VALUE +rb_mod_autoload(mod, sym, file) + VALUE mod; + VALUE sym; + VALUE file; +{ + ID id = rb_to_id(sym); + + Check_SafeStr(file); + rb_autoload(mod, id, RSTRING(file)->ptr); + return Qnil; +} + +static VALUE +rb_mod_autoload_p(mod, sym) + VALUE mod, sym; +{ + return rb_autoload_p(mod, rb_to_id(sym)); +} + +static VALUE +rb_f_autoload(obj, sym, file) + VALUE obj; + VALUE sym; + VALUE file; +{ + return rb_mod_autoload(ruby_class, sym, file); +} + +static VALUE +rb_f_autoload_p(obj, sym) + VALUE obj; + VALUE sym; +{ + /* use ruby_class as same as rb_f_autoload. */ + return rb_mod_autoload_p(ruby_class, sym); +} void @@ -6184,5 +6222,8 @@ Init_load() rb_define_global_function("load", rb_f_load, -1); rb_define_global_function("require", rb_f_require, 1); + rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2); + rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1); rb_define_global_function("autoload", rb_f_autoload, 2); + rb_define_global_function("autoload?", rb_f_autoload_p, 1); rb_global_variable(&ruby_wrapper); Index: intern.h =================================================================== RCS file: /cvs/ruby/src/ruby/intern.h,v retrieving revision 1.95 diff -u -2 -p -r1.95 intern.h --- intern.h 3 Sep 2002 10:00:45 -0000 1.95 +++ intern.h 4 Sep 2002 07:37:19 -0000 @@ -400,6 +400,7 @@ void rb_set_class_path _((VALUE, VALUE, VALUE rb_path2class _((const char*)); void rb_name_class _((VALUE, ID)); -void rb_autoload _((const char*, const char*)); -VALUE rb_f_autoload _((VALUE, VALUE, VALUE)); +void rb_autoload _((VALUE, ID, const char*)); +void rb_autoload_load _((VALUE, ID)); +VALUE rb_autoload_p _((VALUE, ID)); void rb_gc_mark_global_tbl _((void)); VALUE rb_f_trace_var _((int, VALUE*)); @@ -425,5 +426,4 @@ VALUE rb_mod_constants _((VALUE)); VALUE rb_mod_remove_const _((VALUE, VALUE)); int rb_const_defined_at _((VALUE, ID)); -int rb_autoload_defined _((ID)); int rb_const_defined _((VALUE, ID)); VALUE rb_const_get _((VALUE, ID)); @@ -432,5 +432,4 @@ void rb_const_set _((VALUE, ID, VALUE)); void rb_const_assign _((VALUE, ID, VALUE)); VALUE rb_mod_constants _((VALUE)); -void rb_autoload_load _((ID)); VALUE rb_cvar_defined _((VALUE, ID)); void rb_cvar_set _((VALUE, ID, VALUE, int)); Index: variable.c =================================================================== RCS file: /cvs/ruby/src/ruby/variable.c,v retrieving revision 1.68 diff -u -2 -p -r1.68 variable.c --- variable.c 3 Sep 2002 05:20:07 -0000 1.68 +++ variable.c 3 Sep 2002 08:48:05 -0000 @@ -21,4 +21,5 @@ static st_table *rb_global_tbl; st_table *rb_class_tbl; +static ID autoload; void @@ -27,4 +28,5 @@ Init_var_tables() rb_global_tbl = st_init_numtable(); rb_class_tbl = st_init_numtable(); + autoload = rb_intern("__autoload__"); } @@ -254,37 +256,4 @@ rb_name_class(klass, id) } -static st_table *autoload_tbl = 0; - -static void -rb_autoload_id(id, filename) - ID id; - const char *filename; -{ - rb_secure(4); - if (!rb_is_const_id(id)) { - rb_name_error(id, "autoload must be constant name"); - } - - if (!autoload_tbl) { - autoload_tbl = st_init_numtable(); - } - st_insert(autoload_tbl, id, strdup(filename)); -} - -void -rb_autoload(klass, filename) - const char *klass, *filename; -{ - rb_autoload_id(rb_intern(klass), filename); -} - -VALUE -rb_f_autoload(obj, klass, file) - VALUE obj, klass, file; -{ - rb_autoload_id(rb_to_id(klass), StringValuePtr(file)); - return Qnil; -} - char * rb_class2name(klass) @@ -1079,4 +1048,137 @@ rb_obj_remove_instance_variable(obj, nam } +NORETURN(static void uninitialized_constant _((VALUE, ID))); +static void +uninitialized_constant(klass, id) + VALUE klass; + ID id; +{ + if (klass && klass != rb_cObject) + rb_name_error(id, "uninitialized constant %s::%s", + RSTRING(rb_class_path(klass))->ptr, + rb_id2name(id)); + else { + rb_name_error(id, "uninitialized constant %s",rb_id2name(id)); + } +} + +void +rb_autoload(mod, id, file) + VALUE mod; + ID id; + const char *file; +{ + VALUE av; + struct st_table *tbl; + + if (!rb_is_const_id(id)) { + rb_raise(rb_eNameError, "autoload must be constant name", + rb_id2name(id)); + } + if (!file || !*file) { + rb_raise(rb_eArgError, "empty file name"); + } + + if ((tbl = RCLASS(mod)->iv_tbl) && st_lookup(tbl, id, &av) && av != Qundef) + return; + + rb_const_set(mod, id, Qundef); + tbl = RCLASS(mod)->iv_tbl; + if (st_lookup(tbl, autoload, &av)) { + tbl = (struct st_table*)DATA_PTR(av); + } + else { + av = Data_Wrap_Struct(rb_cData, rb_mark_tbl, st_free_table, 0); + st_add_direct(tbl, autoload, av); + DATA_PTR(av) = tbl = st_init_numtable(); + } + st_insert(tbl, id, rb_str_new2(file)); +} + +static VALUE +autoload_delete(mod, id) + VALUE mod; + ID id; +{ + VALUE val, file = Qnil; + + if (st_lookup(RCLASS(mod)->iv_tbl, autoload, &val)) { + struct st_table *tbl = DATA_PTR(val); + + st_delete(tbl, &id, &file); + if (!tbl->num_entries) { + DATA_PTR(val) = 0; + st_free_table(tbl); + id = autoload; + if (st_delete(RCLASS(mod)->iv_tbl, &id, &val)) { + rb_gc_force_recycle(val); + } + } + } + + return file; +} + +void +rb_autoload_load(klass, id) + VALUE klass; + ID id; +{ + VALUE file, value; + + file = autoload_delete(klass, id); + if (NIL_P(file)) { + uninitialized_constant(klass, id); + } + if (rb_provided(RSTRING(file)->ptr)) { + uninitialized_constant(klass, id); + } + FL_UNSET(file, FL_TAINT); + rb_f_require(Qnil, file); +} + +static VALUE +autoload_file(mod, id) + VALUE mod; + ID id; +{ + VALUE val, file; + struct st_table *tbl; + + if (!st_lookup(RCLASS(mod)->iv_tbl, autoload, &val) || + !(tbl = DATA_PTR(val)) || !st_lookup(tbl, id, &file)) { + return Qnil; + } + if (!rb_provided(RSTRING(file)->ptr)) { + return file; + } + + /* already loaded but not defined */ + st_delete(tbl, &id, 0); + if (!tbl->num_entries) { + DATA_PTR(val) = 0; + st_free_table(tbl); + id = autoload; + if (st_delete(RCLASS(mod)->iv_tbl, &id, &val)) { + rb_gc_force_recycle(val); + } + } + return Qnil; +} + +VALUE +rb_autoload_p(mod, id) + VALUE mod; + ID id; +{ + struct st_table *tbl = RCLASS(mod)->iv_tbl; + VALUE val; + + if (!tbl || !st_lookup(tbl, id, &val) || val != Qundef) { + return Qnil; + } + return autoload_file(mod, id); +} + static int top_const_get(id, klassp) @@ -1086,11 +1188,4 @@ top_const_get(id, klassp) /* pre-defined class */ if (st_lookup(rb_class_tbl, id, klassp)) return Qtrue; - - /* autoload */ - if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) { - rb_autoload_load(id); - *klassp = rb_const_get(rb_cObject, id); - return Qtrue; - } return Qfalse; } @@ -1103,5 +1198,9 @@ rb_const_get_at(klass, id) VALUE value; - if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &value)) { + while (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &value)) { + if (value == Qundef) { + rb_autoload_load(klass, id); + continue; + } return value; } @@ -1109,28 +1208,8 @@ rb_const_get_at(klass, id) return value; } - rb_name_error(id, "uninitialized constant %s::%s", - RSTRING(rb_class_path(klass))->ptr, - rb_id2name(id)); + uninitialized_constant(klass, id); return Qnil; /* not reached */ } -void -rb_autoload_load(id) - ID id; -{ - char *modname; - VALUE module; - - st_delete(autoload_tbl, &id, &modname); - if (rb_provided(modname)) { - free(modname); - return; - } - module = rb_str_new2(modname); - free(modname); - FL_UNSET(module, FL_TAINT); - rb_f_require(Qnil, module); -} - VALUE rb_const_get(klass, id) @@ -1144,5 +1223,9 @@ rb_const_get(klass, id) retry: while (tmp) { - if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) { + while (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) { + if (value == Qundef) { + rb_autoload_load(tmp, id); + continue; + } return value; } @@ -1156,14 +1239,8 @@ rb_const_get(klass, id) } - /* Uninitialized constant */ - if (klass && klass != rb_cObject) { - rb_name_error(id, "uninitialized constant %s at %s", - rb_id2name(id), - RSTRING(rb_class_path(klass))->ptr); + uninitialized_constant(klass, id); + return Qnil; /* not reached */ } - else { /* global_uninitialized */ - rb_name_error(id, "uninitialized constant %s",rb_id2name(id)); } - return Qnil; /* not reached */ } @@ -1183,4 +1260,5 @@ rb_mod_remove_const(mod, name) if (RCLASS(mod)->iv_tbl && st_delete(ROBJECT(mod)->iv_tbl, &id, &val)) { + if (val == Qundef) autoload_delete(mod, id); return val; } @@ -1208,16 +1286,4 @@ sv_i(key, value, tbl) } -static int -autoload_i(key, name, tbl) - ID key; - const char *name; - st_table *tbl; -{ - if (!st_lookup(tbl, key, 0)) { - st_insert(tbl, key, key); - } - return ST_CONTINUE; -} - void* rb_mod_const_at(mod, data) @@ -1234,7 +1300,4 @@ rb_mod_const_at(mod, data) if ((VALUE)mod == rb_cObject) { st_foreach(rb_class_tbl, sv_i, tbl); - if (autoload_tbl) { - st_foreach(autoload_tbl, autoload_i, tbl); - } } return tbl; @@ -1290,5 +1353,9 @@ rb_const_defined_at(klass, id) ID id; { - if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, 0)) { + VALUE value; + + if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &value)) { + if (value == Qundef && NIL_P(autoload_file(klass, id))) + return Qfalse; return Qtrue; } @@ -1300,21 +1367,14 @@ rb_const_defined_at(klass, id) int -rb_autoload_defined(id) - ID id; -{ - if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) - return Qtrue; - return Qfalse; -} - -int rb_const_defined(klass, id) VALUE klass; ID id; { - VALUE tmp = klass; + VALUE tmp = klass, value; while (tmp) { - if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,0)) { + if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl, id, &value)) { + if (value == Qundef && NIL_P(autoload_file(klass, id))) + return Qfalse; return Qtrue; } @@ -1326,5 +1386,5 @@ rb_const_defined(klass, id) if (st_lookup(rb_class_tbl, id, 0)) return Qtrue; - return rb_autoload_defined(id); + return Qfalse; } @@ -1345,6 +1405,11 @@ mod_av_set(klass, id, val, isconst) } else if (isconst) { - if (st_lookup(RCLASS(klass)->iv_tbl, id, 0) || + VALUE value = Qfalse; + + if (st_lookup(RCLASS(klass)->iv_tbl, id, &value) || (klass == rb_cObject && st_lookup(rb_class_tbl, id, 0))) { + if (value == Qundef) + autoload_delete(klass, id); + else rb_warn("already initialized %s %s", dest, rb_id2name(id)); } @@ -1385,22 +1450,5 @@ rb_const_assign(klass, id, val) } - /* autoload */ - if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) { - char *modname; - - st_delete(autoload_tbl, &id, &modname); - free(modname); - st_insert(RCLASS(rb_cObject)->iv_tbl, id, val); - return; - } - - /* Uninitialized constant */ - if (klass && klass != rb_cObject) - rb_name_error(id, "uninitialized constant %s::%s", - RSTRING(rb_class_path(klass))->ptr, - rb_id2name(id)); - else { - rb_name_error(id, "uninitialized constant %s",rb_id2name(id)); - } + uninitialized_constant(klass, id); } -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦