diff --git a/class.c b/class.c index 0cee5caae2..6c71ec8bd0 100644 --- a/class.c +++ b/class.c @@ -537,7 +537,6 @@ boot_defclass(const char *name, VALUE super) VALUE obj = rb_class_boot(super); ID id = rb_intern(name); - rb_name_class(obj, id); rb_const_set((rb_cObject ? rb_cObject : obj), id, obj); rb_vm_add_root_module(id, obj); return obj; @@ -551,7 +550,7 @@ Init_class_hierarchy(void) rb_gc_register_mark_object(rb_cObject); /* resolve class name ASAP for order-independence */ - rb_class_name(rb_cObject); + rb_set_class_path_string(rb_cObject, rb_cObject, rb_fstring_lit("Object")); rb_cModule = boot_defclass("Module", rb_cObject); rb_cClass = boot_defclass("Class", rb_cModule); @@ -669,7 +668,6 @@ rb_define_class(const char *name, VALUE super) } klass = rb_define_class_id(id, super); rb_vm_add_root_module(id, klass); - rb_name_class(klass, id); rb_const_set(rb_cObject, id, klass); rb_class_inherited(super, klass); @@ -767,7 +765,6 @@ rb_define_module_id(ID id) VALUE mdl; mdl = rb_module_new(); - rb_name_class(mdl, id); return mdl; } diff --git a/gc.c b/gc.c index 394fd277e2..6df81b959c 100644 --- a/gc.c +++ b/gc.c @@ -11097,7 +11097,7 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj) } else { if (RTEST(RBASIC(obj)->klass)) { - VALUE class_path = rb_class_path_cached(RBASIC(obj)->klass); + VALUE class_path = rb_class_path(RBASIC(obj)->klass); if (!NIL_P(class_path)) { APPENDF((BUFF_ARGS, "(%s)", RSTRING_PTR(class_path))); } @@ -11151,7 +11151,7 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj) case T_CLASS: case T_MODULE: { - VALUE class_path = rb_class_path_cached(obj); + VALUE class_path = rb_class_path(obj); if (!NIL_P(class_path)) { APPENDF((BUFF_ARGS, "%s", RSTRING_PTR(class_path))); } @@ -11159,7 +11159,7 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj) } case T_ICLASS: { - VALUE class_path = rb_class_path_cached(RBASIC_CLASS(obj)); + VALUE class_path = rb_class_path(RBASIC_CLASS(obj)); if (!NIL_P(class_path)) { APPENDF((BUFF_ARGS, "src:%s", RSTRING_PTR(class_path))); } diff --git a/include/ruby/intern.h b/include/ruby/intern.h index d629d08830..bcbf8424e4 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -949,7 +949,6 @@ void rb_set_class_path(VALUE, VALUE, const char*); void rb_set_class_path_string(VALUE, VALUE, VALUE); VALUE rb_path_to_class(VALUE); VALUE rb_path2class(const char*); -void rb_name_class(VALUE, ID); VALUE rb_class_name(VALUE); VALUE rb_autoload_load(VALUE, ID); VALUE rb_autoload_p(VALUE, ID); diff --git a/spec/ruby/core/module/name_spec.rb b/spec/ruby/core/module/name_spec.rb index f56e35f204..5b0fd88729 100644 --- a/spec/ruby/core/module/name_spec.rb +++ b/spec/ruby/core/module/name_spec.rb @@ -18,6 +18,33 @@ describe "Module#name" do m::N.name.should =~ /#::N/ end + it "changes when the module is reachable through a constant path" do + m = Module.new + module m::N; end + m::N.name.should =~ /#::N/ + ModuleSpecs::Anonymous::WasAnnon = m::N + m::N.name.should == "ModuleSpecs::Anonymous::WasAnnon" + end + + it "is set after it is removed from a constant" do + module ModuleSpecs + module ModuleToRemove + end + + mod = ModuleToRemove + remove_const(:ModuleToRemove) + mod.name.should == "ModuleSpecs::ModuleToRemove" + end + end + + it "is set after it is removed from a constant under an anonymous module" do + m = Module.new + module m::Child; end + child = m::Child + m.send(:remove_const, :Child) + child.name.should =~ /#::Child/ + end + it "is set when opened with the module keyword" do ModuleSpecs.name.should == "ModuleSpecs" end @@ -40,6 +67,15 @@ describe "Module#name" do m.name.should == "ModuleSpecs::Anonymous::B" end + it "is not modified when assigned to a different anonymous module" do + m = Module.new + module m::M; end + first_name = m::M.name.dup + module m::N; end + m::N::F = m::M + m::M.name.should == first_name + end + # http://bugs.ruby-lang.org/issues/6067 it "is set with a conditional assignment to a nested constant" do eval("ModuleSpecs::Anonymous::F ||= Module.new") diff --git a/variable.c b/variable.c index f09637fb04..08ff3d1dd0 100644 --- a/variable.c +++ b/variable.c @@ -25,7 +25,7 @@ #include "transient_heap.h" static struct rb_id_table *rb_global_tbl; -static ID autoload, classpath, tmp_classpath, classid; +static ID autoload, classpath, tmp_classpath; static VALUE autoload_featuremap; /* feature => autoload_i */ static void check_before_mod_set(VALUE, ID, VALUE, const char *); @@ -59,8 +59,6 @@ Init_var_tables(void) classpath = rb_intern_const("__classpath__"); /* __tmp_classpath__: temporary class path which contains anonymous names */ tmp_classpath = rb_intern_const("__tmp_classpath__"); - /* __classid__: name given to class/module under an anonymous namespace */ - classid = rb_intern_const("__classid__"); } static inline bool @@ -73,163 +71,29 @@ rb_namespace_p(VALUE obj) return false; } -struct fc_result { - ID name, preferred; - VALUE klass; - VALUE path; - VALUE track; - struct fc_result *prev; -}; - -static VALUE -fc_path(struct fc_result *fc, ID name) -{ - VALUE path, tmp; - - path = rb_id2str(name); - while (fc) { - st_data_t n; - if (fc->track == rb_cObject) break; - if (RCLASS_IV_TBL(fc->track) && - st_lookup(RCLASS_IV_TBL(fc->track), (st_data_t)classpath, &n)) { - tmp = rb_str_dup((VALUE)n); - rb_str_cat2(tmp, "::"); - rb_str_append(tmp, path); - path = tmp; - break; - } - tmp = rb_str_dup(rb_id2str(fc->name)); - rb_str_cat2(tmp, "::"); - rb_str_append(tmp, path); - path = tmp; - fc = fc->prev; - } - OBJ_FREEZE(path); - return path; -} - -static enum rb_id_table_iterator_result -fc_i(ID key, VALUE v, void *a) -{ - rb_const_entry_t *ce = (rb_const_entry_t *)v; - struct fc_result *res = a; - VALUE value = ce->value; - if (!rb_is_const_id(key)) return ID_TABLE_CONTINUE; - - if (value == res->klass && (!res->preferred || key == res->preferred)) { - res->path = fc_path(res, key); - return ID_TABLE_STOP; - } - if (rb_namespace_p(value)) { - if (!RCLASS_CONST_TBL(value)) return ID_TABLE_CONTINUE; - else { - struct fc_result arg; - struct fc_result *list; - - list = res; - while (list) { - if (list->track == value) return ID_TABLE_CONTINUE; - list = list->prev; - } - - arg.name = key; - arg.preferred = res->preferred; - arg.path = 0; - arg.klass = res->klass; - arg.track = value; - arg.prev = res; - rb_id_table_foreach(RCLASS_CONST_TBL(value), fc_i, &arg); - if (arg.path) { - res->path = arg.path; - return ID_TABLE_STOP; - } - } - } - return ID_TABLE_CONTINUE; -} - -/** - * Traverse constant namespace and find +classpath+ for _klass_. If - * _preferred_ is not 0, choice the path whose base name is set to it. - * If +classpath+ is found, the hidden instance variable __classpath__ - * is set to the found path, and __tmp_classpath__ is removed. - * The path is frozen. - */ -static VALUE -find_class_path(VALUE klass, ID preferred) -{ - struct fc_result arg; - - arg.preferred = preferred; - arg.name = 0; - arg.path = 0; - arg.klass = klass; - arg.track = rb_cObject; - arg.prev = 0; - if (RCLASS_CONST_TBL(rb_cObject)) { - rb_id_table_foreach(RCLASS_CONST_TBL(rb_cObject), fc_i, &arg); - } - if (arg.path) { - st_data_t tmp = tmp_classpath; - if (!RCLASS_IV_TBL(klass)) { - RCLASS_IV_TBL(klass) = st_init_numtable(); - } - rb_class_ivar_set(klass, classpath, arg.path); - - st_delete(RCLASS_IV_TBL(klass), &tmp, 0); - return arg.path; - } - return Qnil; -} - /** * Returns +classpath+ of _klass_, if it is named, or +nil+ for - * anonymous +class+/+module+. The last part of named +classpath+ is - * never anonymous, but anonymous +class+/+module+ names may be - * contained. If the path is "permanent", that means it has no - * anonymous names, *permanent is set to 1. + * anonymous +class+/+module+. A named +classpath+ may contain + * an anonymous component, but the last component is guaranteed + * to not be anonymous. *permanent is set to 1 + * if +classpath+ has no anonymous components. There is no builtin + * Ruby level APIs that can change a permanent +classpath+. */ static VALUE classname(VALUE klass, int *permanent) { - VALUE path = Qnil; + st_table *ivtbl; st_data_t n; - if (!klass) klass = rb_cObject; - *permanent = 1; - if (RCLASS_IV_TBL(klass)) { - if (!st_lookup(RCLASS_IV_TBL(klass), (st_data_t)classpath, &n)) { - ID cid = 0; - if (st_lookup(RCLASS_IV_TBL(klass), (st_data_t)classid, &n)) { - VALUE cname = (VALUE)n; - cid = rb_check_id(&cname); - if (cid) path = find_class_path(klass, cid); - } - if (NIL_P(path)) { - path = find_class_path(klass, (ID)0); - } - if (NIL_P(path)) { - if (!cid) { - return Qnil; - } - if (!st_lookup(RCLASS_IV_TBL(klass), (st_data_t)tmp_classpath, &n)) { - path = rb_id2str(cid); - return path; - } - *permanent = 0; - path = (VALUE)n; - return path; - } - } - else { - path = (VALUE)n; - } - if (!RB_TYPE_P(path, T_STRING)) { - rb_bug("class path is not set properly"); - } - return path; + *permanent = 0; + if (!RCLASS_EXT(klass)) return Qnil; + if (!(ivtbl = RCLASS_IV_TBL(klass))) return Qnil; + if (st_lookup(ivtbl, (st_data_t)classpath, &n)) { + *permanent = 1; + return (VALUE)n; } - return find_class_path(klass, (ID)0); + if (st_lookup(ivtbl, (st_data_t)tmp_classpath, &n)) return (VALUE)n; + return Qnil; } /* @@ -268,22 +132,16 @@ make_temporary_path(VALUE obj, VALUE klass) return path; } -typedef VALUE (*path_cache_func)(VALUE obj, VALUE name); +typedef VALUE (*fallback_func)(VALUE obj, VALUE name); static VALUE -rb_tmp_class_path(VALUE klass, int *permanent, path_cache_func cache_path) +rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback) { VALUE path = classname(klass, permanent); - st_data_t n = (st_data_t)path; if (!NIL_P(path)) { return path; } - if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass), - (st_data_t)tmp_classpath, &n)) { - *permanent = 0; - return (VALUE)n; - } else { if (RB_TYPE_P(klass, T_MODULE)) { if (rb_obj_class(klass) == rb_cModule) { @@ -291,40 +149,19 @@ rb_tmp_class_path(VALUE klass, int *permanent, path_cache_func cache_path) } else { int perm; - path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, cache_path); + path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, fallback); } } *permanent = 0; - return cache_path(klass, path); + return fallback(klass, path); } } -static VALUE -ivar_cache(VALUE obj, VALUE name) -{ - return rb_ivar_set(obj, tmp_classpath, make_temporary_path(obj, name)); -} - VALUE rb_class_path(VALUE klass) { int permanent; - VALUE path = rb_tmp_class_path(klass, &permanent, ivar_cache); - if (!NIL_P(path)) path = rb_str_dup(path); - return path; -} - -static VALUE -null_cache(VALUE obj, VALUE name) -{ - return make_temporary_path(obj, name); -} - -VALUE -rb_class_path_no_cache(VALUE klass) -{ - int permanent; - VALUE path = rb_tmp_class_path(klass, &permanent, null_cache); + VALUE path = rb_tmp_class_path(klass, &permanent, make_temporary_path); if (!NIL_P(path)) path = rb_str_dup(path); return path; } @@ -332,18 +169,12 @@ rb_class_path_no_cache(VALUE klass) VALUE rb_class_path_cached(VALUE klass) { - st_table *ivtbl; - st_data_t n; - - if (!RCLASS_EXT(klass)) return Qnil; - if (!(ivtbl = RCLASS_IV_TBL(klass))) return Qnil; - if (st_lookup(ivtbl, (st_data_t)classpath, &n)) return (VALUE)n; - if (st_lookup(ivtbl, (st_data_t)tmp_classpath, &n)) return (VALUE)n; - return Qnil; + int permanent; + return classname(klass, &permanent); } static VALUE -never_cache(VALUE obj, VALUE name) +no_fallback(VALUE obj, VALUE name) { return name; } @@ -352,7 +183,13 @@ VALUE rb_search_class_path(VALUE klass) { int permanent; - return rb_tmp_class_path(klass, &permanent, never_cache); + return rb_tmp_class_path(klass, &permanent, no_fallback); +} + +static VALUE +save_temporary_path(VALUE obj, VALUE name) +{ + return rb_ivar_set(obj, tmp_classpath, make_temporary_path(obj, name)); } void @@ -366,13 +203,12 @@ rb_set_class_path_string(VALUE klass, VALUE under, VALUE name) } else { int permanent; - str = rb_str_dup(rb_tmp_class_path(under, &permanent, ivar_cache)); + str = rb_str_dup(rb_tmp_class_path(under, &permanent, save_temporary_path)); rb_str_cat2(str, "::"); rb_str_append(str, name); OBJ_FREEZE(str); if (!permanent) { pathid = tmp_classpath; - rb_ivar_set(klass, classid, rb_str_intern(name)); } } rb_ivar_set(klass, pathid, str); @@ -389,12 +225,11 @@ rb_set_class_path(VALUE klass, VALUE under, const char *name) } else { int permanent; - str = rb_str_dup(rb_tmp_class_path(under, &permanent, ivar_cache)); + str = rb_str_dup(rb_tmp_class_path(under, &permanent, save_temporary_path)); rb_str_cat2(str, "::"); rb_str_cat2(str, name); if (!permanent) { pathid = tmp_classpath; - rb_ivar_set(klass, classid, rb_str_intern(rb_str_new_cstr(name))); } } OBJ_FREEZE(str); @@ -449,12 +284,6 @@ rb_path2class(const char *path) return rb_path_to_class(rb_str_new_cstr(path)); } -void -rb_name_class(VALUE klass, ID id) -{ - rb_ivar_set(klass, classid, ID2SYM(id)); -} - VALUE rb_class_name(VALUE klass) { @@ -465,7 +294,7 @@ const char * rb_class2name(VALUE klass) { int permanent; - VALUE path = rb_tmp_class_path(rb_class_real(klass), &permanent, ivar_cache); + VALUE path = rb_tmp_class_path(rb_class_real(klass), &permanent, make_temporary_path); if (NIL_P(path)) return NULL; return RSTRING_PTR(path); } @@ -2853,6 +2682,64 @@ check_before_mod_set(VALUE klass, ID id, VALUE val, const char *dest) rb_check_frozen(klass); } +static VALUE +build_const_path(VALUE head, ID tail) +{ + VALUE path = rb_str_dup(head); + rb_str_cat2(path, "::"); + rb_str_append(path, rb_id2str(tail)); + OBJ_FREEZE(path); + return path; +} + +static void finalize_classpath_for_children(VALUE named_namespace); + +static enum rb_id_table_iterator_result +finalize_classpath_i(ID id, VALUE v, void *payload) +{ + rb_const_entry_t *ce = (rb_const_entry_t *)v; + VALUE value = ce->value; + int has_permanent_classpath; + VALUE parental_path = *((VALUE *) payload); + if (!rb_is_const_id(id)) { + return ID_TABLE_CONTINUE; + } + if (!rb_namespace_p(value)) { + return ID_TABLE_CONTINUE; + } + classname(value, &has_permanent_classpath); + if (has_permanent_classpath) { + return ID_TABLE_CONTINUE; + } + rb_ivar_set(value, classpath, build_const_path(parental_path, id)); + if (RCLASS_IV_TBL(value)) { + st_data_t tmp = tmp_classpath; + st_delete(RCLASS_IV_TBL(value), &tmp, 0); + } + finalize_classpath_for_children(value); + + return ID_TABLE_CONTINUE; +} + +/* + * Assign permanent classpaths to all namespaces that are directly or indirectly + * nested under +named_namespace+. +named_namespace+ must have a permanent + * classpath. + */ +static void +finalize_classpath_for_children(VALUE named_namespace) +{ + struct rb_id_table *const_table = RCLASS_CONST_TBL(named_namespace); + + if (const_table) { + int permanent; + VALUE parental_path = classname(named_namespace, &permanent); + VM_ASSERT(RB_TYPE_P(parental_path, T_STRING)); + VM_ASSERT(permanent); + rb_id_table_foreach(const_table, finalize_classpath_i, &parental_path); + } +} + void rb_const_set(VALUE klass, ID id, VALUE val) { @@ -2885,24 +2772,23 @@ rb_const_set(VALUE klass, ID id, VALUE val) * and avoid order-dependency on const_tbl */ if (rb_cObject && rb_namespace_p(val)) { - if (NIL_P(rb_class_path_cached(val))) { + int val_path_permanent; + VALUE val_path = classname(val, &val_path_permanent); + if (NIL_P(val_path) || !val_path_permanent) { if (klass == rb_cObject) { rb_ivar_set(val, classpath, rb_id2str(id)); - rb_name_class(val, id); + finalize_classpath_for_children(val); } else { - VALUE path; - ID pathid; - st_data_t n; - st_table *ivtbl = RCLASS_IV_TBL(klass); - if (ivtbl && - (st_lookup(ivtbl, (st_data_t)(pathid = classpath), &n) || - st_lookup(ivtbl, (st_data_t)(pathid = tmp_classpath), &n))) { - path = rb_str_dup((VALUE)n); - rb_str_append(rb_str_cat2(path, "::"), rb_id2str(id)); - OBJ_FREEZE(path); - rb_ivar_set(val, pathid, path); - rb_name_class(val, id); + int parental_path_permanent; + VALUE parental_path = classname(klass, &parental_path_permanent); + if (!NIL_P(parental_path)) { + if (parental_path_permanent && !val_path_permanent) { + rb_ivar_set(val, classpath, build_const_path(parental_path, id)); + finalize_classpath_for_children(val); + } else if (!parental_path_permanent && NIL_P(val_path)) { + rb_ivar_set(val, tmp_classpath, build_const_path(parental_path, id)); + } } } } diff --git a/vm.c b/vm.c index 9b6600393c..1bc1ed757f 100644 --- a/vm.c +++ b/vm.c @@ -374,8 +374,6 @@ rb_vm_inc_const_missing_count(void) ruby_vm_const_missing_count +=1; } -VALUE rb_class_path_no_cache(VALUE _klass); - MJIT_FUNC_EXPORTED int rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id, struct ruby_dtrace_method_hook_args *args) @@ -395,7 +393,7 @@ rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id, } type = BUILTIN_TYPE(klass); if (type == T_CLASS || type == T_ICLASS || type == T_MODULE) { - VALUE name = rb_class_path_no_cache(klass); + VALUE name = rb_class_path(klass); const char *classname, *filename; const char *methodname = rb_id2name(id); if (methodname && (filename = rb_source_location_cstr(&args->line_no)) != 0) {