From: "Yoshinori K. Okuji" Date: 2002-09-05T15:11:53+09:00 Subject: [ruby-dev:18184] Re: autoload patch for ruby-1.7 At Thu, 5 Sep 2002 12:28:30 +0900, nobu.nakada@nifty.ne.jp wrote: > load中のファイルからautoloadされる定数を未定義とみなすようにし > てみました。結局ev_const_defined()での対応も必要になったり、関 > 数の構成を変えたりしたので、ちょっと大きいですがパッチを作り直 > しました。 ありがとうございます。非常に良好な動作で、make testはパッチを当てる前 より若干速いほどです。 ただ、わざとおかしいスクリプトを与えるとSegmentation faultが起きました。 $ cp /dev/null foo.rb $ ruby -e 'autoload :FOO, "foo"; autoload :BAR, "foo"; p FOO' -e:1: [BUG] Segmentation fault ruby 1.7.3 (2002-09-05) [i686-linux] Abort autoload_deleteがst_deleteの返り値を見てなくて、st_deleteが失敗した場 合にNULLを返してしまうのが原因だったので、それを直したパッチを添付しま す。 > [ruby-dev:18139]の > > それなり速くて素直なのは、逆方向のハッシュテーブルを別に持ち、あるファ > > イルからautoloadされるべき定数のリストを簡単に見られるようにしておく方 > > 法です。 > とどっちがいいか…。 中田さんの方法は非常に実装がシンプルなので、このままで良いのではないか という気がします。 おくじ Index: class.c =================================================================== RCS file: /src/ruby/class.c,v retrieving revision 1.45 diff -u -r1.45 class.c --- class.c 2002/09/04 06:37:35 1.45 +++ class.c 2002/09/05 06:11:20 @@ -189,9 +189,6 @@ ID id; 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); if (TYPE(klass) != T_CLASS) { @@ -278,9 +275,6 @@ ID id; 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); if (TYPE(module) == T_MODULE) Index: eval.c =================================================================== RCS file: /src/ruby/eval.c,v retrieving revision 1.324 diff -u -r1.324 eval.c --- eval.c 2002/09/05 04:15:50 1.324 +++ eval.c 2002/09/05 06:11:21 @@ -1534,12 +1534,16 @@ VALUE self; { NODE *cbase = cref; + VALUE result; while (cbase && cbase->nd_next) { struct RClass *klass = RCLASS(cbase->nd_clss); 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; } cbase = cbase->nd_next; @@ -1560,7 +1564,11 @@ VALUE klass = cbase->nd_clss; 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; } cbase = cbase->nd_next; @@ -3299,9 +3307,7 @@ super = 0; } - if ((ruby_class == rb_cObject) && rb_autoload_defined(node->nd_cname)) { - rb_autoload_load(node->nd_cname); - } + klass = 0; if (rb_const_defined_at(ruby_class, node->nd_cname)) { klass = rb_const_get(ruby_class, node->nd_cname); if (TYPE(klass) != T_CLASS) { @@ -3341,10 +3347,8 @@ if (NIL_P(ruby_class)) { rb_raise(rb_eTypeError, "no outer class/module"); - } - if ((ruby_class == rb_cObject) && rb_autoload_defined(node->nd_cname)) { - rb_autoload_load(node->nd_cname); } + module = 0; if (rb_const_defined_at(ruby_class, node->nd_cname)) { module = rb_const_get(ruby_class, node->nd_cname); if (TYPE(module) != T_MODULE) { @@ -6161,8 +6165,44 @@ rb_define_virtual_variable("$SAFE", safe_getter, safe_setter); } + +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); +} -VALUE rb_f_autoload(); +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 Init_load() @@ -6177,7 +6217,10 @@ 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); ruby_dln_librefs = rb_ary_new(); Index: intern.h =================================================================== RCS file: /src/ruby/intern.h,v retrieving revision 1.95 diff -u -r1.95 intern.h --- intern.h 2002/09/03 10:00:45 1.95 +++ intern.h 2002/09/05 06:11:21 @@ -399,8 +399,9 @@ void rb_set_class_path _((VALUE, VALUE, const char*)); 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*)); VALUE rb_f_untrace_var _((int, VALUE*)); @@ -424,14 +425,12 @@ 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)); VALUE rb_const_get_at _((VALUE, ID)); 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)); VALUE rb_cvar_get _((VALUE, ID)); Index: variable.c =================================================================== RCS file: /src/ruby/variable.c,v retrieving revision 1.68 diff -u -r1.68 variable.c --- variable.c 2002/09/03 05:20:07 1.68 +++ variable.c 2002/09/05 06:11:21 @@ -20,12 +20,14 @@ static st_table *rb_global_tbl; st_table *rb_class_tbl; +static ID autoload; void Init_var_tables() { rb_global_tbl = st_init_numtable(); rb_class_tbl = st_init_numtable(); + autoload = rb_intern("__autoload__"); } struct fc_result { @@ -253,39 +255,6 @@ rb_iv_set(klass, "__classid__", ID2SYM(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) VALUE klass; @@ -1078,6 +1047,140 @@ return Qnil; /* not reached */ } +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); + + if (!st_delete(tbl, &id, &file)) return Qnil; + + 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) ID id; @@ -1085,13 +1188,6 @@ { /* 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; } @@ -1102,36 +1198,20 @@ { 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; } if (klass == rb_cObject && top_const_get(id, &value)) { 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) VALUE klass; @@ -1143,7 +1223,11 @@ tmp = klass; 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; } if (tmp == rb_cObject && top_const_get(id, &value)) return value; @@ -1155,15 +1239,7 @@ goto retry; } - /* 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); - } - else { /* global_uninitialized */ - rb_name_error(id, "uninitialized constant %s",rb_id2name(id)); - } + uninitialized_constant(klass, id); return Qnil; /* not reached */ } @@ -1182,6 +1258,7 @@ if (OBJ_FROZEN(mod)) rb_error_frozen("class/module"); if (RCLASS(mod)->iv_tbl && st_delete(ROBJECT(mod)->iv_tbl, &id, &val)) { + if (val == Qundef) autoload_delete(mod, id); return val; } if (rb_const_defined_at(mod, id)) { @@ -1207,18 +1284,6 @@ return ST_CONTINUE; } -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) VALUE mod; @@ -1233,9 +1298,6 @@ } 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; } @@ -1289,7 +1351,11 @@ VALUE klass; 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; } if (klass == rb_cObject) { @@ -1299,23 +1365,16 @@ } 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; } tmp = RCLASS(tmp)->super; @@ -1325,7 +1384,7 @@ } if (st_lookup(rb_class_tbl, id, 0)) return Qtrue; - return rb_autoload_defined(id); + return Qfalse; } static void @@ -1344,8 +1403,13 @@ RCLASS(klass)->iv_tbl = st_init_numtable(); } 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)); } } @@ -1383,25 +1447,8 @@ st_insert(RCLASS(rb_cObject)->iv_tbl, id, val); return; } - - /* 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); } void