From: "Yoshinori K. Okuji" Date: 2002-08-30T08:09:41+09:00 Subject: [ruby-dev:18103] autoload patch for ruby-1.7 --Multipart_Fri_Aug_30_08:09:49_2002-1 Content-Type: text/plain; charset=ISO-2022-JP [ruby-dev:16180]でトップレベル以外の定数についてもautoloadができるよう にするパッチを拝見しました。[ruby-dev:16185]でまつもとさんが1.7系に修 正することを提案されてましたが、その後特に動きはないようだったので、 * 今日のCVS版に合わせて修正する * バグっぽかった所を修正する * コーディング・スタイルが一部一致していなかった所を修正する ということを行いました。make testが通ること、[ruby-dev:16180]に添付さ れていたスクリプトが予期された動作をすること、変更後、誤差の範囲内でし かパフォーマンスの違いがないこと(これはmake testで計測しました)、の三 点を確認しました。このパッチを添付いたします。 動機を説明しておきます。私が微妙に(?)関係しているBioRubyでは、すでに たくさんのモジュールやクラスが実装されていて、多くの場合、それらの大部 分を使うことはありません。しかし、現在は利便性を考慮して、全部のファイ ルをrequireするファイルがあり、それをruby-1.6.7でロードすると、それだ けで1秒以上の時間がかかります。 まだBioRuby自体ではテストしていないのですが、私が手元で行った簡単なテ ストで、ほとんど空、つまり、「module モジュール名; end」しか入っていな いファイルを100個作り、それらをrequireした場合とautoloadした場合で時間 を比較すると、約10倍程度の差がありました。 BioRubyではモジュールの下にモジュールやクラスが多く定義されているので、 autoloadがトップレベル以外でも利用できると、その効果は絶大なのではない かと期待しています。 おくじ --Multipart_Fri_Aug_30_08:09:49_2002-1 Content-Type: application/octet-stream; type=patch Content-Disposition: attachment; filename="ruby-1.7-autoload.patch" Content-Transfer-Encoding: 7bit Index: class.c =================================================================== RCS file: /src/ruby/class.c,v retrieving revision 1.43 diff -u -r1.43 class.c --- class.c 2002/08/27 08:31:05 1.43 +++ class.c 2002/08/29 22:35:20 @@ -181,9 +181,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) { @@ -270,9 +267,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.323 diff -u -r1.323 eval.c --- eval.c 2002/08/29 09:08:15 1.323 +++ eval.c 2002/08/29 22:35:21 @@ -3300,9 +3300,6 @@ } 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); } @@ -3346,9 +3343,6 @@ rb_raise(rb_eTypeError, "no outer class/module"); } 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); } @@ -6168,8 +6162,6 @@ rb_define_virtual_variable("$SAFE", safe_getter, safe_setter); } -VALUE rb_f_autoload(); - void Init_load() { @@ -6183,7 +6175,6 @@ rb_define_global_function("load", rb_f_load, -1); rb_define_global_function("require", rb_f_require, 1); - rb_define_global_function("autoload", rb_f_autoload, 2); rb_global_variable(&ruby_wrapper); ruby_dln_librefs = rb_ary_new(); Index: inits.c =================================================================== RCS file: /src/ruby/inits.c,v retrieving revision 1.6 diff -u -r1.6 inits.c --- inits.c 2002/04/18 08:46:18 1.6 +++ inits.c 2002/08/29 22:35:21 @@ -13,6 +13,7 @@ #include "ruby.h" void Init_Array _((void)); +void Init_autoload _((void)); void Init_Bignum _((void)); void Init_Comparable _((void)); void Init_Dir _((void)); @@ -72,6 +73,7 @@ Init_signal(); Init_process(); Init_load(); + Init_autoload(); Init_Proc(); Init_Math(); Init_GC(); Index: intern.h =================================================================== RCS file: /src/ruby/intern.h,v retrieving revision 1.92 diff -u -r1.92 intern.h --- intern.h 2002/08/19 05:56:05 1.92 +++ intern.h 2002/08/29 22:35:21 @@ -398,8 +398,6 @@ 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_gc_mark_global_tbl _((void)); VALUE rb_f_trace_var _((int, VALUE*)); VALUE rb_f_untrace_var _((int, VALUE*)); @@ -423,14 +421,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.66 diff -u -r1.66 variable.c --- variable.c 2002/08/28 14:59:01 1.66 +++ variable.c 2002/08/29 22:35:21 @@ -253,39 +253,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; @@ -1070,6 +1037,136 @@ return Qnil; /* not reached */ } +static VALUE autoload_tbl = Qnil; +#define AUTOLOAD_TARGET Qundef +#define IS_AUTOLOAD_TARGET(obj) ((obj) == Qundef) +#define AUTOLOAD_KEY_NEW(klass,id) rb_ary_new3(2, klass, ID2SYM(id)) + +static void +autoload_add(klass, id, file) + VALUE klass; + ID id; + VALUE file; +{ + VALUE key; + + rb_secure(4); + Check_SafeStr(file); + if (!rb_is_const_id(id)) { + rb_raise(rb_eNameError, "autoload must be constant name", + rb_id2name(id)); + } + + key = AUTOLOAD_KEY_NEW(klass, id); + rb_hash_aset(autoload_tbl, key, file); +} + +static VALUE +rb_mod_autoload(mod, sym, file) + VALUE mod; + VALUE sym; + VALUE file; +{ + ID id = rb_to_id(sym); + + if (rb_const_defined_at(mod,id)) + return Qnil; + + rb_const_set(mod, id, AUTOLOAD_TARGET); + autoload_add(mod, id, file); + + return Qnil; +} + +static VALUE +rb_f_autoload(obj, sym, file) + VALUE obj; + VALUE sym; + VALUE file; +{ + /* I use ruby_class instead of obj, as same as NODE_CDECL, + * assuming autoload is another way of const_set. + */ + return rb_mod_autoload(ruby_class, sym, file); +} + +static void +autoload_delete(klass, id) + VALUE klass; + VALUE id; +{ + VALUE key = AUTOLOAD_KEY_NEW(klass, id); + rb_hash_delete(autoload_tbl, key); +} + +static void +autoload_load(klass, id) + VALUE klass; + ID id; +{ + VALUE key, file; + + if (RCLASS(klass)->iv_tbl) { + VALUE value; + + st_delete(ROBJECT(klass)->iv_tbl, &id, &value); + if (! IS_AUTOLOAD_TARGET(value)) + rb_bug("autoload_laod: removed normal constant: %s::%s", + rb_class2name(klass), rb_id2name(id)); + } + else { + rb_bug("autoload_load: constant not found: %s::%s", + rb_class2name(klass), rb_id2name(id)); + } + + key = AUTOLOAD_KEY_NEW(klass, id); + file = rb_hash_delete(autoload_tbl, key); + + if (NIL_P(file)) { + rb_bug("autoload file not found for %s::%s", + rb_class2name(klass), rb_id2name(id)); + } + + if (rb_provided(RSTRING(file)->ptr)) return; + + FL_UNSET(file, FL_TAINT); + rb_f_require(Qnil, file); +} + +static VALUE +rb_mod_autoload_p(mod, sym) + VALUE mod; + VALUE sym; +{ + ID id = rb_to_id(sym); + + if (NIL_P(rb_hash_aref(autoload_tbl, AUTOLOAD_KEY_NEW(mod,id)))) + return Qfalse; + + return Qtrue; +} + +static VALUE +rb_f_autoload_p(self, sym) + VALUE self; + VALUE sym; +{ + /* use ruby_class as same as rb_f_autoload. */ + return rb_mod_autoload_p(ruby_class, sym); +} + +void +Init_autoload() +{ + autoload_tbl = rb_hash_new(); + rb_global_variable(&autoload_tbl); + + 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); +} + static int top_const_get(id, klassp) ID id; @@ -1077,13 +1174,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; } @@ -1094,7 +1184,12 @@ { VALUE value; + retry: if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &value)) { + if (IS_AUTOLOAD_TARGET(value)) { + autoload_load(klass, id); + goto retry; + } return value; } if (klass == rb_cObject && top_const_get(id, &value)) { @@ -1106,24 +1201,6 @@ 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; @@ -1136,6 +1213,10 @@ retry: while (tmp) { if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) { + if (IS_AUTOLOAD_TARGET(value)) { + autoload_load(tmp, id); + goto retry; + } return value; } if (tmp == rb_cObject && top_const_get(id, &value)) return value; @@ -1174,6 +1255,7 @@ if (OBJ_FROZEN(mod)) rb_error_frozen("class/module"); if (RCLASS(mod)->iv_tbl && st_delete(ROBJECT(mod)->iv_tbl, &id, &val)) { + if (IS_AUTOLOAD_TARGET(val)) autoload_delete(mod, id); return val; } if (rb_const_defined_at(mod, id)) { @@ -1199,18 +1281,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; @@ -1225,9 +1295,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; } @@ -1291,15 +1358,6 @@ } 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; @@ -1317,7 +1375,7 @@ } if (st_lookup(rb_class_tbl, id, 0)) return Qtrue; - return rb_autoload_defined(id); + return Qfalse; } static void @@ -1336,9 +1394,13 @@ RCLASS(klass)->iv_tbl = st_init_numtable(); } else if (isconst) { - if (st_lookup(RCLASS(klass)->iv_tbl, id, 0) || - (klass == rb_cObject && st_lookup(rb_class_tbl, id, 0))) { + VALUE value; + + if (st_lookup(RCLASS(klass)->iv_tbl, id, &value)) { + if (IS_AUTOLOAD_TARGET(value)) autoload_delete(klass, id); rb_warn("already initialized %s %s", dest, rb_id2name(id)); + } else if (klass == rb_cObject && st_lookup(rb_class_tbl, id, 0)) { + rb_warn("already initialized %s %s", dest, rb_id2name(id)); } } @@ -1372,16 +1434,6 @@ /* pre-defined class */ if (st_lookup(rb_class_tbl, id, 0)) { st_delete(rb_class_tbl, id, 0); - 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; } --Multipart_Fri_Aug_30_08:09:49_2002-1--