diff options
Diffstat (limited to 'variable.c')
| -rw-r--r-- | variable.c | 1360 |
1 files changed, 592 insertions, 768 deletions
diff --git a/variable.c b/variable.c index 3146168924..bfb6fa2edd 100644 --- a/variable.c +++ b/variable.c @@ -11,10 +11,9 @@ **********************************************************************/ -#include "ruby/encoding.h" +#include "internal.h" #include "ruby/st.h" #include "ruby/util.h" -#include "internal.h" #include "id_table.h" #include "constant.h" #include "id.h" @@ -22,12 +21,9 @@ #include "id_table.h" #include "debug_counter.h" #include "vm_core.h" -#include "transient_heap.h" -#include "variable.h" -static struct rb_id_table *rb_global_tbl; -static ID autoload, classpath, tmp_classpath; -static VALUE autoload_featuremap; /* feature => autoload_i */ +struct rb_id_table *rb_global_tbl; +static ID autoload, classpath, tmp_classpath, classid; static void check_before_mod_set(VALUE, ID, VALUE, const char *); static void setup_const_entry(rb_const_entry_t *, VALUE, VALUE, rb_const_flag_t); @@ -35,6 +31,12 @@ static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int v static st_table *generic_iv_tbl; static st_table *generic_iv_tbl_compat; +/* per-object */ +struct gen_ivtbl { + uint32_t numiv; + VALUE ivptr[1]; /* flexible array */ +}; + struct ivar_update { union { st_table *iv_index_tbl; @@ -54,41 +56,167 @@ 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 -rb_namespace_p(VALUE obj) +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) { - if (RB_SPECIAL_CONST_P(obj)) return false; - switch (RB_BUILTIN_TYPE(obj)) { - case T_MODULE: case T_CLASS: return true; + 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; } - return false; + if (RB_TYPE_P(value, T_MODULE) || RB_TYPE_P(value, T_CLASS)) { + 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+. A named +classpath+ may contain - * an anonymous component, but the last component is guaranteed - * to not be anonymous. <code>*permanent</code> is set to 1 - * if +classpath+ has no anonymous components. There is no builtin - * Ruby level APIs that can change a permanent +classpath+. + * 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, <code>*permanent</code> is set to 1. */ static VALUE classname(VALUE klass, int *permanent) { - st_table *ivtbl; + VALUE path = Qnil; st_data_t n; - *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; + 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; } - if (st_lookup(ivtbl, (st_data_t)tmp_classpath, &n)) return (VALUE)n; - return Qnil; + return find_class_path(klass, (ID)0); } /* @@ -102,7 +230,10 @@ VALUE rb_mod_name(VALUE mod) { int permanent; - return classname(mod, &permanent); + VALUE path = classname(mod, &permanent); + + if (!NIL_P(path)) return rb_str_dup(path); + return path; } static VALUE @@ -124,16 +255,22 @@ make_temporary_path(VALUE obj, VALUE klass) return path; } -typedef VALUE (*fallback_func)(VALUE obj, VALUE name); +typedef VALUE (*path_cache_func)(VALUE obj, VALUE name); static VALUE -rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback) +rb_tmp_class_path(VALUE klass, int *permanent, path_cache_func cache_path) { 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) { @@ -141,57 +278,68 @@ rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback) } else { int perm; - path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, fallback); + path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, cache_path); } } *permanent = 0; - return fallback(klass, path); + return cache_path(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, make_temporary_path); + VALUE path = rb_tmp_class_path(klass, &permanent, ivar_cache); if (!NIL_P(path)) path = rb_str_dup(path); return path; } -VALUE -rb_class_path_cached(VALUE klass) +static VALUE +null_cache(VALUE obj, VALUE name) { - int permanent; - return classname(klass, &permanent); + return make_temporary_path(obj, name); } -static VALUE -no_fallback(VALUE obj, VALUE name) +VALUE +rb_class_path_no_cache(VALUE klass) { - return name; + int permanent; + VALUE path = rb_tmp_class_path(klass, &permanent, null_cache); + if (!NIL_P(path)) path = rb_str_dup(path); + return path; } VALUE -rb_search_class_path(VALUE klass) +rb_class_path_cached(VALUE klass) { - int permanent; - return rb_tmp_class_path(klass, &permanent, no_fallback); + 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; } static VALUE -build_const_pathname(VALUE head, VALUE tail) +never_cache(VALUE obj, VALUE name) { - VALUE path = rb_str_dup(head); - rb_str_cat2(path, "::"); - rb_str_append(path, tail); - OBJ_FREEZE(path); - return path; + return name; } -static VALUE -build_const_path(VALUE head, ID tail) +VALUE +rb_search_class_path(VALUE klass) { - return build_const_pathname(head, rb_id2str(tail)); + int permanent; + return rb_tmp_class_path(klass, &permanent, never_cache); } void @@ -205,10 +353,13 @@ rb_set_class_path_string(VALUE klass, VALUE under, VALUE name) } else { int permanent; - str = rb_tmp_class_path(under, &permanent, make_temporary_path); - str = build_const_pathname(str, name); + str = rb_str_dup(rb_tmp_class_path(under, &permanent, ivar_cache)); + 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); @@ -217,9 +368,24 @@ rb_set_class_path_string(VALUE klass, VALUE under, VALUE name) void rb_set_class_path(VALUE klass, VALUE under, const char *name) { - VALUE str = rb_str_new2(name); + VALUE str; + ID pathid = classpath; + + if (under == rb_cObject) { + str = rb_str_new2(name); + } + else { + int permanent; + str = rb_str_dup(rb_tmp_class_path(under, &permanent, ivar_cache)); + 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); - rb_set_class_path_string(klass, under, str); + rb_ivar_set(klass, pathid, str); } VALUE @@ -254,7 +420,7 @@ rb_path_to_class(VALUE pathname) } c = rb_const_search(c, id, TRUE, FALSE, FALSE); if (c == Qundef) goto undefined_class; - if (!rb_namespace_p(c)) { + if (!RB_TYPE_P(c, T_MODULE) && !RB_TYPE_P(c, T_CLASS)) { rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module", pathname); } @@ -270,6 +436,12 @@ 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) { @@ -280,7 +452,7 @@ const char * rb_class2name(VALUE klass) { int permanent; - VALUE path = rb_tmp_class_path(rb_class_real(klass), &permanent, make_temporary_path); + VALUE path = rb_tmp_class_path(rb_class_real(klass), &permanent, ivar_cache); if (NIL_P(path)) return NULL; return RSTRING_PTR(path); } @@ -301,32 +473,20 @@ struct trace_var { struct rb_global_variable { int counter; int block_trace; - VALUE *data; + void *data; rb_gvar_getter_t *getter; rb_gvar_setter_t *setter; rb_gvar_marker_t *marker; struct trace_var *trace; }; -static struct rb_global_entry* -rb_find_global_entry(ID id) +struct rb_global_entry* +rb_global_entry(ID id) { struct rb_global_entry *entry; VALUE data; if (!rb_id_table_lookup(rb_global_tbl, id, &data)) { - return NULL; - } - entry = (struct rb_global_entry *)data; - ASSUME(entry != NULL); - return entry; -} - -MJIT_FUNC_EXPORTED struct rb_global_entry* -rb_global_entry(ID id) -{ - struct rb_global_entry *entry = rb_find_global_entry(id); - if (!entry) { struct rb_global_variable *var; entry = ALLOC(struct rb_global_entry); var = ALLOC(struct rb_global_variable); @@ -342,11 +502,14 @@ rb_global_entry(ID id) var->trace = 0; rb_id_table_insert(rb_global_tbl, id, (VALUE)entry); } + else { + entry = (struct rb_global_entry *)data; + } return entry; } VALUE -rb_gvar_undef_getter(ID id, VALUE *_) +rb_gvar_undef_getter(ID id, void *data, struct rb_global_variable *var) { rb_warning("global variable `%"PRIsVALUE"' not initialized", QUOTE_ID(id)); @@ -354,9 +517,8 @@ rb_gvar_undef_getter(ID id, VALUE *_) } void -rb_gvar_undef_setter(VALUE val, ID id, VALUE *_) +rb_gvar_undef_setter(VALUE val, ID id, void *d, struct rb_global_variable *var) { - struct rb_global_variable *var = rb_global_entry(id)->var; var->getter = rb_gvar_val_getter; var->setter = rb_gvar_val_setter; var->marker = rb_gvar_val_marker; @@ -370,15 +532,14 @@ rb_gvar_undef_marker(VALUE *var) } VALUE -rb_gvar_val_getter(ID id, VALUE *data) +rb_gvar_val_getter(ID id, void *data, struct rb_global_variable *var) { return (VALUE)data; } void -rb_gvar_val_setter(VALUE val, ID id, VALUE *_) +rb_gvar_val_setter(VALUE val, ID id, void *data, struct rb_global_variable *var) { - struct rb_global_variable *var = rb_global_entry(id)->var; var->data = (void*)val; } @@ -390,16 +551,17 @@ rb_gvar_val_marker(VALUE *var) } VALUE -rb_gvar_var_getter(ID id, VALUE *var) +rb_gvar_var_getter(ID id, void *data, struct rb_global_variable *gvar) { + VALUE *var = data; if (!var) return Qnil; return *var; } void -rb_gvar_var_setter(VALUE val, ID id, VALUE *data) +rb_gvar_var_setter(VALUE val, ID id, void *data, struct rb_global_variable *g) { - *data = val; + *(VALUE *)data = val; } void @@ -409,7 +571,7 @@ rb_gvar_var_marker(VALUE *var) } void -rb_gvar_readonly_setter(VALUE v, ID id, VALUE *_) +rb_gvar_readonly_setter(VALUE v, ID id, void *d, struct rb_global_variable *g) { rb_name_error(id, "%"PRIsVALUE" is a read-only variable", QUOTE_ID(id)); } @@ -445,34 +607,11 @@ global_id(const char *name) if (name[0] == '$') id = rb_intern(name); else { size_t len = strlen(name); - VALUE vbuf = 0; - char *buf = ALLOCV_N(char, vbuf, len+1); + char *buf = ALLOCA_N(char, len+1); buf[0] = '$'; memcpy(buf+1, name, len); id = rb_intern2(buf, len+1); - ALLOCV_END(vbuf); - } - return id; -} - -static ID -find_global_id(const char *name) -{ - ID id; - size_t len = strlen(name); - - if (name[0] == '$') { - id = rb_check_id_cstr(name, len, NULL); - } - else { - VALUE vbuf = 0; - char *buf = ALLOCV_N(char, vbuf, len+1); - buf[0] = '$'; - memcpy(buf+1, name, len); - id = rb_check_id_cstr(buf, len+1, NULL); - ALLOCV_END(vbuf); } - return id; } @@ -480,8 +619,8 @@ void rb_define_hooked_variable( const char *name, VALUE *var, - rb_gvar_getter_t *getter, - rb_gvar_setter_t *setter) + VALUE (*getter)(ANYARGS), + void (*setter)(ANYARGS)) { volatile VALUE tmp = var ? *var : Qnil; ID id = global_id(name); @@ -510,8 +649,8 @@ rb_define_readonly_variable(const char *name, const VALUE *var) void rb_define_virtual_variable( const char *name, - rb_gvar_getter_t *getter, - rb_gvar_setter_t *setter) + VALUE (*getter)(ANYARGS), + void (*setter)(ANYARGS)) { if (!getter) getter = rb_gvar_val_getter; if (!setter) setter = rb_gvar_readonly_setter; @@ -521,9 +660,32 @@ rb_define_virtual_variable( static void rb_trace_eval(VALUE cmd, VALUE val) { - rb_eval_cmd_kw(cmd, rb_ary_new3(1, val), RB_NO_KEYWORDS); + rb_eval_cmd(cmd, rb_ary_new3(1, val), 0); } +/* + * call-seq: + * trace_var(symbol, cmd ) -> nil + * trace_var(symbol) {|val| block } -> nil + * + * Controls tracing of assignments to global variables. The parameter + * +symbol+ identifies the variable (as either a string name or a + * symbol identifier). _cmd_ (which may be a string or a + * +Proc+ object) or block is executed whenever the variable + * is assigned. The block or +Proc+ object receives the + * variable's new value as a parameter. Also see + * <code>Kernel::untrace_var</code>. + * + * trace_var :$_, proc {|v| puts "$_ is now '#{v}'" } + * $_ = "hello" + * $_ = ' there' + * + * <em>produces:</em> + * + * $_ is now 'hello' + * $_ is now ' there' + */ + VALUE rb_f_trace_var(int argc, const VALUE *argv) { @@ -538,6 +700,9 @@ rb_f_trace_var(int argc, const VALUE *argv) return rb_f_untrace_var(argc, argv); } entry = rb_global_entry(rb_to_id(var)); + if (OBJ_TAINTED(cmd)) { + rb_raise(rb_eSecurityError, "Insecure: tainted variable trace"); + } trace = ALLOC(struct trace_var); trace->next = entry->var->trace; trace->func = rb_trace_eval; @@ -570,6 +735,16 @@ remove_trace(struct rb_global_variable *var) var->trace = t.next; } +/* + * call-seq: + * untrace_var(symbol [, cmd] ) -> array or nil + * + * Removes tracing for the specified command on the given global + * variable and returns +nil+. If no command is specified, + * removes all tracing for that variable and returns an array + * containing the commands actually removed. + */ + VALUE rb_f_untrace_var(int argc, const VALUE *argv) { @@ -615,11 +790,11 @@ rb_f_untrace_var(int argc, const VALUE *argv) return Qnil; } -MJIT_FUNC_EXPORTED VALUE +VALUE rb_gvar_get(struct rb_global_entry *entry) { struct rb_global_variable *var = entry->var; - return (*var->getter)(entry->id, var->data); + return (*var->getter)(entry->id, var->data, var); } struct trace_data { @@ -628,9 +803,8 @@ struct trace_data { }; static VALUE -trace_ev(VALUE v) +trace_ev(struct trace_data *data) { - struct trace_data *data = (void *)v; struct trace_var *trace = data->trace; while (trace) { @@ -642,21 +816,20 @@ trace_ev(VALUE v) } static VALUE -trace_en(VALUE v) +trace_en(struct rb_global_variable *var) { - struct rb_global_variable *var = (void *)v; var->block_trace = 0; remove_trace(var); return Qnil; /* not reached */ } -MJIT_FUNC_EXPORTED VALUE +VALUE rb_gvar_set(struct rb_global_entry *entry, VALUE val) { struct trace_data trace; struct rb_global_variable *var = entry->var; - (*var->setter)(val, entry->id, var->data); + (*var->setter)(val, entry->id, var->data, var); if (var->trace && !var->block_trace) { var->block_trace = 1; @@ -680,42 +853,18 @@ VALUE rb_gv_get(const char *name) { struct rb_global_entry *entry; - ID id = find_global_id(name); - if (!id) { - rb_warning("global variable `%s' not initialized", name); - return Qnil; - } - - entry = rb_global_entry(id); + entry = rb_global_entry(global_id(name)); return rb_gvar_get(entry); } -MJIT_FUNC_EXPORTED VALUE +VALUE rb_gvar_defined(struct rb_global_entry *entry) { if (entry->var->getter == rb_gvar_undef_getter) return Qfalse; return Qtrue; } -rb_gvar_getter_t * -rb_gvar_getter_function_of(const struct rb_global_entry *entry) -{ - return entry->var->getter; -} - -rb_gvar_setter_t * -rb_gvar_setter_function_of(const struct rb_global_entry *entry) -{ - return entry->var->setter; -} - -bool -rb_gvar_is_traced(const struct rb_global_entry *entry) -{ - return !!entry->var->trace; -} - static enum rb_id_table_iterator_result gvar_i(ID key, VALUE val, void *a) { @@ -724,6 +873,15 @@ gvar_i(ID key, VALUE val, void *a) return ID_TABLE_CONTINUE; } +/* + * call-seq: + * global_variables -> array + * + * Returns an array of the names of global variables. + * + * global_variables.grep /std/ #=> [:$stdin, :$stdout, :$stderr] + */ + VALUE rb_f_global_variables(void) { @@ -799,12 +957,6 @@ gen_ivtbl_get(VALUE obj, struct gen_ivtbl **ivtbl) return 0; } -MJIT_FUNC_EXPORTED struct st_table * -rb_ivar_generic_ivtbl(void) -{ - return generic_iv_tbl; -} - static VALUE generic_ivar_delete(VALUE obj, ID id, VALUE undef) { @@ -849,7 +1001,7 @@ generic_ivar_get(VALUE obj, ID id, VALUE undef) static size_t gen_ivtbl_bytes(size_t n) { - return offsetof(struct gen_ivtbl, ivptr) + n * sizeof(VALUE); + return sizeof(struct gen_ivtbl) + n * sizeof(VALUE) - sizeof(VALUE); } static struct gen_ivtbl * @@ -980,16 +1132,6 @@ rb_mark_generic_ivar(VALUE obj) } void -rb_mv_generic_ivar(VALUE rsrc, VALUE dst) -{ - st_data_t key = (st_data_t)rsrc; - struct gen_ivtbl *ivtbl; - - if (st_delete(generic_iv_tbl, &key, (st_data_t *)&ivtbl)) - st_insert(generic_iv_tbl, (st_data_t)dst, (st_data_t)ivtbl); -} - -void rb_free_generic_ivar(VALUE obj) { st_data_t key = (st_data_t)obj; @@ -1171,148 +1313,65 @@ generic_ivar_set(VALUE obj, ID id, VALUE val) RB_OBJ_WRITTEN(obj, Qundef, val); } -static VALUE * -obj_ivar_heap_alloc(VALUE obj, size_t newsize) -{ - VALUE *newptr = rb_transient_heap_alloc(obj, sizeof(VALUE) * newsize); - - if (newptr != NULL) { - ROBJ_TRANSIENT_SET(obj); - } - else { - ROBJ_TRANSIENT_UNSET(obj); - newptr = ALLOC_N(VALUE, newsize); - } - return newptr; -} - -static VALUE * -obj_ivar_heap_realloc(VALUE obj, int32_t len, size_t newsize) -{ - VALUE *newptr; - int i; - - if (ROBJ_TRANSIENT_P(obj)) { - const VALUE *orig_ptr = ROBJECT(obj)->as.heap.ivptr; - newptr = obj_ivar_heap_alloc(obj, newsize); - - assert(newptr); - ROBJECT(obj)->as.heap.ivptr = newptr; - for (i=0; i<(int)len; i++) { - newptr[i] = orig_ptr[i]; - } - } - else { - REALLOC_N(ROBJECT(obj)->as.heap.ivptr, VALUE, newsize); - newptr = ROBJECT(obj)->as.heap.ivptr; - } - - return newptr; -} - -#if USE_TRANSIENT_HEAP -void -rb_obj_transient_heap_evacuate(VALUE obj, int promote) -{ - if (ROBJ_TRANSIENT_P(obj)) { - uint32_t len = ROBJECT_NUMIV(obj); - const VALUE *old_ptr = ROBJECT_IVPTR(obj); - VALUE *new_ptr; - - if (promote) { - new_ptr = ALLOC_N(VALUE, len); - ROBJ_TRANSIENT_UNSET(obj); - } - else { - new_ptr = obj_ivar_heap_alloc(obj, len); - } - MEMCPY(new_ptr, old_ptr, VALUE, len); - ROBJECT(obj)->as.heap.ivptr = new_ptr; - } -} -#endif - -static VALUE -obj_ivar_set(VALUE obj, ID id, VALUE val) +VALUE +rb_ivar_set(VALUE obj, ID id, VALUE val) { struct ivar_update ivup; uint32_t i, len; - ivup.iv_extended = 0; - ivup.u.iv_index_tbl = iv_index_tbl_make(obj); - iv_index_tbl_extend(&ivup, id); - len = ROBJECT_NUMIV(obj); - if (len <= ivup.index) { - VALUE *ptr = ROBJECT_IVPTR(obj); - if (ivup.index < ROBJECT_EMBED_LEN_MAX) { - RBASIC(obj)->flags |= ROBJECT_EMBED; - ptr = ROBJECT(obj)->as.ary; - for (i = 0; i < ROBJECT_EMBED_LEN_MAX; i++) { - ptr[i] = Qundef; - } - } - else { - VALUE *newptr; - uint32_t newsize = iv_index_tbl_newsize(&ivup); - - if (RBASIC(obj)->flags & ROBJECT_EMBED) { - newptr = obj_ivar_heap_alloc(obj, newsize); - MEMCPY(newptr, ptr, VALUE, len); - RBASIC(obj)->flags &= ~ROBJECT_EMBED; - ROBJECT(obj)->as.heap.ivptr = newptr; - } - else { - newptr = obj_ivar_heap_realloc(obj, len, newsize); - } - for (; len < newsize; len++) { - newptr[len] = Qundef; - } - ROBJECT(obj)->as.heap.numiv = newsize; - ROBJECT(obj)->as.heap.iv_index_tbl = ivup.u.iv_index_tbl; - } - } - RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[ivup.index], val); - - return val; -} - -static void -ivar_set(VALUE obj, ID id, VALUE val) -{ RB_DEBUG_COUNTER_INC(ivar_set_base); + rb_check_frozen(obj); + switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - obj_ivar_set(obj, id, val); - break; + ivup.iv_extended = 0; + ivup.u.iv_index_tbl = iv_index_tbl_make(obj); + iv_index_tbl_extend(&ivup, id); + len = ROBJECT_NUMIV(obj); + if (len <= ivup.index) { + VALUE *ptr = ROBJECT_IVPTR(obj); + if (ivup.index < ROBJECT_EMBED_LEN_MAX) { + RBASIC(obj)->flags |= ROBJECT_EMBED; + ptr = ROBJECT(obj)->as.ary; + for (i = 0; i < ROBJECT_EMBED_LEN_MAX; i++) { + ptr[i] = Qundef; + } + } + else { + VALUE *newptr; + uint32_t newsize = iv_index_tbl_newsize(&ivup); + + if (RBASIC(obj)->flags & ROBJECT_EMBED) { + newptr = ALLOC_N(VALUE, newsize); + MEMCPY(newptr, ptr, VALUE, len); + RBASIC(obj)->flags &= ~ROBJECT_EMBED; + ROBJECT(obj)->as.heap.ivptr = newptr; + } + else { + REALLOC_N(ROBJECT(obj)->as.heap.ivptr, VALUE, newsize); + newptr = ROBJECT(obj)->as.heap.ivptr; + } + for (; len < newsize; len++) + newptr[len] = Qundef; + ROBJECT(obj)->as.heap.numiv = newsize; + ROBJECT(obj)->as.heap.iv_index_tbl = ivup.u.iv_index_tbl; + } + } + RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[ivup.index], val); + break; case T_CLASS: case T_MODULE: - if (!RCLASS_IV_TBL(obj)) RCLASS_IV_TBL(obj) = st_init_numtable(); - rb_class_ivar_set(obj, id, val); + if (!RCLASS_IV_TBL(obj)) RCLASS_IV_TBL(obj) = st_init_numtable(); + rb_class_ivar_set(obj, id, val); break; default: - generic_ivar_set(obj, id, val); - break; + generic_ivar_set(obj, id, val); + break; } -} - -VALUE -rb_ivar_set(VALUE obj, ID id, VALUE val) -{ - rb_check_frozen(obj); - ivar_set(obj, id, val); return val; } -void -rb_ivar_set_internal(VALUE obj, ID id, VALUE val) -{ - // should be internal instance variable name (no @ prefix) - VM_ASSERT(!rb_is_instance_id(id)); - - ivar_set(obj, id, val); -} - VALUE rb_ivar_defined(VALUE obj, ID id) { @@ -1333,7 +1392,7 @@ rb_ivar_defined(VALUE obj, ID id) break; case T_CLASS: case T_MODULE: - if (RCLASS_IV_TBL(obj) && st_is_member(RCLASS_IV_TBL(obj), (st_data_t)id)) + if (RCLASS_IV_TBL(obj) && st_lookup(RCLASS_IV_TBL(obj), (st_data_t)id, 0)) return Qtrue; break; default: @@ -1344,11 +1403,9 @@ rb_ivar_defined(VALUE obj, ID id) return Qfalse; } -typedef int rb_ivar_foreach_callback_func(ID key, VALUE val, st_data_t arg); - struct obj_ivar_tag { VALUE obj; - rb_ivar_foreach_callback_func *func; + int (*func)(ID key, VALUE val, st_data_t arg); st_data_t arg; }; @@ -1366,7 +1423,7 @@ obj_ivar_i(st_data_t key, st_data_t index, st_data_t arg) } static void -obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) +obj_ivar_each(VALUE obj, int (*func)(ANYARGS), st_data_t arg) { st_table *tbl; struct obj_ivar_tag data; @@ -1384,7 +1441,7 @@ obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) struct gen_ivar_tag { struct gen_ivtbl *ivtbl; - rb_ivar_foreach_callback_func *func; + int (*func)(ID key, VALUE val, st_data_t arg); st_data_t arg; }; @@ -1403,7 +1460,7 @@ gen_ivar_each_i(st_data_t key, st_data_t index, st_data_t data) } static void -gen_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) +gen_ivar_each(VALUE obj, int (*func)(ANYARGS), st_data_t arg) { struct gen_ivar_tag data; st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); @@ -1486,7 +1543,7 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj) } void -rb_ivar_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) +rb_ivar_foreach(VALUE obj, int (*func)(ANYARGS), st_data_t arg) { if (SPECIAL_CONST_P(obj)) return; switch (BUILTIN_TYPE(obj)) { @@ -1609,11 +1666,9 @@ check_id_type(VALUE obj, VALUE *pname, /* * call-seq: * obj.remove_instance_variable(symbol) -> obj - * obj.remove_instance_variable(string) -> obj * * Removes the named instance variable from <i>obj</i>, returning that * variable's value. - * String arguments are converted to symbols. * * class Dummy * attr_reader :var @@ -1675,7 +1730,7 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name) not_defined: rb_name_err_raise("instance variable %1$s not defined", obj, name); - UNREACHABLE_RETURN(Qnil); + UNREACHABLE; } NORETURN(static void uninitialized_constant(VALUE, VALUE)); @@ -1693,7 +1748,7 @@ uninitialized_constant(VALUE klass, VALUE name) VALUE rb_const_missing(VALUE klass, VALUE name) { - VALUE value = rb_funcallv(klass, idConst_missing, 1, &name); + VALUE value = rb_funcallv(klass, rb_intern("const_missing"), 1, &name); rb_vm_inc_const_missing_count(); return value; } @@ -1738,21 +1793,16 @@ rb_const_missing(VALUE klass, VALUE name) VALUE rb_mod_const_missing(VALUE klass, VALUE name) { - VALUE ref = GET_EC()->private_const_reference; rb_vm_pop_cfunc_frame(); - if (ref) { - rb_name_err_raise("private constant %2$s::%1$s referenced", - ref, name); - } uninitialized_constant(klass, name); - UNREACHABLE_RETURN(Qnil); + UNREACHABLE; } static void autoload_mark(void *ptr) { - rb_mark_tbl_no_pin((st_table *)ptr); + rb_mark_tbl((st_table *)ptr); } static void @@ -1768,15 +1818,9 @@ autoload_memsize(const void *ptr) return st_memsize(tbl); } -static void -autoload_compact(void *ptr) -{ - rb_gc_update_tbl_refs((st_table *)ptr); -} - static const rb_data_type_t autoload_data_type = { "autoload", - {autoload_mark, autoload_free, autoload_memsize, autoload_compact,}, + {autoload_mark, autoload_free, autoload_memsize,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; @@ -1797,61 +1841,34 @@ autoload_data(VALUE mod, ID id) return (VALUE)val; } -struct autoload_const { - struct list_node cnode; /* <=> autoload_data_i.constants */ - VALUE mod; - VALUE ad; /* autoload_data_i */ - VALUE value; - VALUE file; - ID id; - rb_const_flag_t flag; - int line; -}; - /* always on stack, no need to mark */ struct autoload_state { - struct autoload_const *ac; + struct autoload_data_i *ele; + VALUE mod; VALUE result; + ID id; VALUE thread; - struct list_node waitq; + union { + struct list_node node; + struct list_head head; + } waitq; }; struct autoload_data_i { VALUE feature; + int safe_level; + rb_const_flag_t flag; + VALUE value; struct autoload_state *state; /* points to on-stack struct */ rb_serial_t fork_gen; - struct list_head constants; /* <=> autoload_const.cnode */ }; static void -autoload_i_compact(void *ptr) -{ - struct autoload_data_i *p = ptr; - p->feature = rb_gc_location(p->feature); -} - -static void autoload_i_mark(void *ptr) { struct autoload_data_i *p = ptr; - - rb_gc_mark_movable(p->feature); - - /* allow GC to free us if no modules refer to this via autoload_const.ad */ - if (list_empty(&p->constants)) { - rb_hash_delete(autoload_featuremap, p->feature); - } -} - -static void -autoload_i_free(void *ptr) -{ - struct autoload_data_i *p = ptr; - - /* we may leak some memory at VM shutdown time, no big deal */ - if (list_empty(&p->constants)) { - xfree(p); - } + rb_gc_mark(p->feature); + rb_gc_mark(p->value); } static size_t @@ -1862,65 +1879,20 @@ autoload_i_memsize(const void *ptr) static const rb_data_type_t autoload_data_i_type = { "autoload_i", - {autoload_i_mark, autoload_i_free, autoload_i_memsize, autoload_i_compact}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; - -static void -autoload_c_compact(void *ptr) -{ - struct autoload_const *ac = ptr; - - ac->mod = rb_gc_location(ac->mod); - ac->ad = rb_gc_location(ac->ad); - ac->value = rb_gc_location(ac->value); - ac->file = rb_gc_location(ac->file); -} - -static void -autoload_c_mark(void *ptr) -{ - struct autoload_const *ac = ptr; - - rb_gc_mark_movable(ac->mod); - rb_gc_mark_movable(ac->ad); - rb_gc_mark_movable(ac->value); - rb_gc_mark_movable(ac->file); -} - -static void -autoload_c_free(void *ptr) -{ - struct autoload_const *ac = ptr; - list_del(&ac->cnode); - xfree(ac); -} - -static size_t -autoload_c_memsize(const void *ptr) -{ - return sizeof(struct autoload_const); -} - -static const rb_data_type_t autoload_const_type = { - "autoload_const", - {autoload_c_mark, autoload_c_free, autoload_c_memsize, autoload_c_compact,}, + {autoload_i_mark, RUBY_TYPED_DEFAULT_FREE, autoload_i_memsize,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; static struct autoload_data_i * -get_autoload_data(VALUE acv, struct autoload_const **acp) +get_autoload_data(VALUE av) { - struct autoload_const *ac = rb_check_typeddata(acv, &autoload_const_type); - struct autoload_data_i *ele; + struct autoload_data_i *ele = rb_check_typeddata(av, &autoload_data_i_type); - ele = rb_check_typeddata(ac->ad, &autoload_data_i_type); /* do not reach across stack for ->state after forking: */ if (ele && ele->state && ele->fork_gen != GET_VM()->fork_gen) { ele->state = 0; ele->fork_gen = 0; } - if (acp) *acp = ac; return ele; } @@ -1970,37 +1942,17 @@ rb_autoload_str(VALUE mod, ID id, VALUE file) DATA_PTR(av) = tbl = st_init_numtable(); } - file = rb_fstring(file); - if (!autoload_featuremap) { - autoload_featuremap = rb_ident_hash_new(); - rb_obj_hide(autoload_featuremap); - rb_gc_register_mark_object(autoload_featuremap); - } - ad = rb_hash_aref(autoload_featuremap, file); - if (NIL_P(ad)) { - ad = TypedData_Make_Struct(0, struct autoload_data_i, - &autoload_data_i_type, ele); - ele->feature = file; - ele->state = 0; - list_head_init(&ele->constants); - rb_hash_aset(autoload_featuremap, file, ad); - } - else { - ele = rb_check_typeddata(ad, &autoload_data_i_type); - } - { - VALUE acv; - struct autoload_const *ac; - acv = TypedData_Make_Struct(0, struct autoload_const, - &autoload_const_type, ac); - ac->mod = mod; - ac->id = id; - ac->value = Qundef; - ac->flag = CONST_PUBLIC; - ac->ad = ad; - list_add_tail(&ele->constants, &ac->cnode); - st_insert(tbl, (st_data_t)id, (st_data_t)acv); + ad = TypedData_Make_Struct(0, struct autoload_data_i, &autoload_data_i_type, ele); + if (OBJ_TAINTED(file)) { + file = rb_str_dup(file); + FL_UNSET(file, FL_TAINT); } + ele->feature = rb_fstring(file); + ele->safe_level = rb_safe_level(); + ele->value = Qundef; + ele->state = 0; + ele->flag = CONST_PUBLIC; + st_insert(tbl, (st_data_t)id, (st_data_t)ad); } static void @@ -2010,22 +1962,8 @@ autoload_delete(VALUE mod, ID id) if (st_lookup(RCLASS_IV_TBL(mod), (st_data_t)autoload, &val)) { struct st_table *tbl = check_autoload_table((VALUE)val); - struct autoload_data_i *ele; - struct autoload_const *ac; st_delete(tbl, &n, &load); - ele = get_autoload_data((VALUE)load, &ac); - VM_ASSERT(ele); - if (ele) { - VM_ASSERT(!list_empty(&ele->constants)); - } - - /* - * we must delete here to avoid "already initialized" warnings - * with parallel autoload. Using list_del_init here so list_del - * works in autoload_c_free - */ - list_del_init(&ac->cnode); if (tbl->num_entries == 0) { n = autoload; @@ -2035,14 +1973,28 @@ autoload_delete(VALUE mod, ID id) } static VALUE +autoload_provided(VALUE arg) +{ + const char **p = (const char **)arg; + return rb_feature_provided(*p, p); +} + +static VALUE +reset_safe(VALUE safe) +{ + rb_set_safe_level_force((int)safe); + return safe; +} + +static VALUE check_autoload_required(VALUE mod, ID id, const char **loadingpath) { - VALUE file; - VALUE load = autoload_data(mod, id); + VALUE file, load; struct autoload_data_i *ele; const char *loading; + int safe; - if (!load || !(ele = get_autoload_data(load, 0))) { + if (!(load = autoload_data(mod, id)) || !(ele = get_autoload_data(load))) { return 0; } file = ele->feature; @@ -2062,7 +2014,9 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath) } loading = RSTRING_PTR(file); - if (!rb_feature_provided(loading, &loading)) { + safe = rb_safe_level(); + rb_set_safe_level_force(0); + if (!rb_ensure(autoload_provided, (VALUE)&loading, reset_safe, (VALUE)safe)) { return load; } if (loadingpath && loading) { @@ -2072,37 +2026,24 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath) return 0; } -static struct autoload_const *autoloading_const_entry(VALUE mod, ID id); - -MJIT_FUNC_EXPORTED int +int rb_autoloading_value(VALUE mod, ID id, VALUE* value, rb_const_flag_t *flag) { - struct autoload_const *ac = autoloading_const_entry(mod, id); - if (!ac) return FALSE; - - if (value) { - *value = ac->value; - } - if (flag) { - *flag = ac->flag; - } - return TRUE; -} - -struct autoload_const * -autoloading_const_entry(VALUE mod, ID id) -{ - VALUE load = autoload_data(mod, id); + VALUE load; struct autoload_data_i *ele; - struct autoload_const *ac; - if (!load || !(ele = get_autoload_data(load, &ac))) { - return 0; + if (!(load = autoload_data(mod, id)) || !(ele = get_autoload_data(load))) { + return 0; } - if (ele->state && ele->state->thread == rb_thread_current()) { - if (ac->value != Qundef) { - return ac; + if (ele->value != Qundef) { + if (value) { + *value = ele->value; + } + if (flag) { + *flag = ele->flag; + } + return 1; } } return 0; @@ -2119,16 +2060,23 @@ autoload_defined_p(VALUE mod, ID id) return !rb_autoloading_value(mod, id, NULL, NULL); } -static void const_tbl_update(struct autoload_const *); +struct autoload_const_set_args { + VALUE mod; + ID id; + VALUE value; + rb_const_flag_t flag; +}; + +static void const_tbl_update(struct autoload_const_set_args *); static VALUE autoload_const_set(VALUE arg) { - struct autoload_const *ac = (struct autoload_const *)arg; - VALUE klass = ac->mod; - ID id = ac->id; - check_before_mod_set(klass, id, ac->value, "constant"); - const_tbl_update(ac); + struct autoload_const_set_args* args = (struct autoload_const_set_args *)arg; + VALUE klass = args->mod; + ID id = args->id; + check_before_mod_set(klass, id, args->value, "constant"); + const_tbl_update(args); return 0; /* ignored */ } @@ -2136,13 +2084,10 @@ static VALUE autoload_require(VALUE arg) { struct autoload_state *state = (struct autoload_state *)arg; - struct autoload_const *ac = state->ac; - struct autoload_data_i *ele; - ele = rb_check_typeddata(ac->ad, &autoload_data_i_type); /* this may release GVL and switch threads: */ state->result = rb_funcall(rb_vm_top_self(), rb_intern("require"), 1, - ele->feature); + state->ele->feature); return state->result; } @@ -2152,36 +2097,37 @@ autoload_reset(VALUE arg) { struct autoload_state *state = (struct autoload_state *)arg; int need_wakeups = 0; - struct autoload_const *ac = state->ac; - struct autoload_data_i *ele; - ele = rb_check_typeddata(ac->ad, &autoload_data_i_type); - if (ele->state == state) { + if (state->ele->state == state) { need_wakeups = 1; - ele->state = 0; - ele->fork_gen = 0; + state->ele->state = 0; + state->ele->fork_gen = 0; } /* At the last, move a value defined in autoload to constant table */ - if (RTEST(state->result)) { - struct autoload_const *next; + if (RTEST(state->result) && state->ele->value != Qundef) { + int safe_backup; + struct autoload_const_set_args args; - list_for_each_safe(&ele->constants, ac, next, cnode) { - if (ac->value != Qundef) { - autoload_const_set((VALUE)ac); - } - } + args.mod = state->mod; + args.id = state->id; + args.value = state->ele->value; + args.flag = state->ele->flag; + safe_backup = rb_safe_level(); + rb_set_safe_level_force(state->ele->safe_level); + rb_ensure(autoload_const_set, (VALUE)&args, + reset_safe, (VALUE)safe_backup); } /* wakeup any waiters we had */ if (need_wakeups) { struct autoload_state *cur = 0, *nxt; - list_for_each_safe((struct list_head *)&state->waitq, cur, nxt, waitq) { + list_for_each_safe(&state->waitq.head, cur, nxt, waitq.node) { VALUE th = cur->thread; cur->thread = Qfalse; - list_del_init(&cur->waitq); /* idempotent */ + list_del_init(&cur->waitq.node); /* idempotent */ /* * cur is stored on the stack of cur->waiting_th, @@ -2216,7 +2162,7 @@ autoload_sleep_done(VALUE arg) struct autoload_state *state = (struct autoload_state *)arg; if (state->thread != Qfalse && rb_thread_to_be_killed(state->thread)) { - list_del(&state->waitq); /* idempotent after list_del_init */ + list_del(&state->waitq.node); /* idempotent after list_del_init */ } return Qfalse; @@ -2228,10 +2174,7 @@ rb_autoload_load(VALUE mod, ID id) VALUE load, result; const char *loading = 0, *src; struct autoload_data_i *ele; - struct autoload_const *ac; struct autoload_state state; - int flag = -1; - rb_const_entry_t *ce; if (!autoload_defined_p(mod, id)) return Qfalse; load = check_autoload_required(mod, id, &loading); @@ -2239,15 +2182,14 @@ rb_autoload_load(VALUE mod, ID id) src = rb_sourcefile(); if (src && loading && strcmp(src, loading) == 0) return Qfalse; - if ((ce = rb_const_lookup(mod, id))) { - flag = ce->flag & (CONST_DEPRECATED | CONST_VISIBILITY_MASK); - } - /* set ele->state for a marker of autoloading thread */ - if (!(ele = get_autoload_data(load, &ac))) { + if (!(ele = get_autoload_data(load))) { return Qfalse; } - state.ac = ac; + + state.ele = ele; + state.mod = mod; + state.id = id; state.thread = rb_thread_current(); if (!ele->state) { ele->state = &state; @@ -2257,26 +2199,22 @@ rb_autoload_load(VALUE mod, ID id) * autoload_reset will wake up any threads added to this * iff the GVL is released during autoload_require */ - list_head_init((struct list_head *)&state.waitq); + list_head_init(&state.waitq.head); } else if (state.thread == ele->state->thread) { return Qfalse; } else { - list_add_tail((struct list_head *)&ele->state->waitq, &state.waitq); + list_add_tail(&ele->state->waitq.head, &state.waitq.node); rb_ensure(autoload_sleep, (VALUE)&state, autoload_sleep_done, (VALUE)&state); } /* autoload_data_i can be deleted by another thread while require */ - state.result = Qfalse; result = rb_ensure(autoload_require, (VALUE)&state, autoload_reset, (VALUE)&state); - if (flag > 0 && (ce = rb_const_lookup(mod, id))) { - ce->flag |= flag; - } RB_GC_GUARD(load); return result; } @@ -2284,30 +2222,22 @@ rb_autoload_load(VALUE mod, ID id) VALUE rb_autoload_p(VALUE mod, ID id) { - return rb_autoload_at_p(mod, id, TRUE); -} - -VALUE -rb_autoload_at_p(VALUE mod, ID id, int recur) -{ VALUE load; struct autoload_data_i *ele; while (!autoload_defined_p(mod, id)) { - if (!recur) return Qnil; mod = RCLASS_SUPER(mod); if (!mod) return Qnil; } load = check_autoload_required(mod, id, 0); if (!load) return Qnil; - return (ele = get_autoload_data(load, 0)) ? ele->feature : Qnil; + return (ele = get_autoload_data(load)) ? ele->feature : Qnil; } -MJIT_FUNC_EXPORTED void +void rb_const_warn_if_deprecated(const rb_const_entry_t *ce, VALUE klass, ID id) { - if (RB_CONST_DEPRECATED_P(ce) && - rb_warning_category_enabled_p(RB_WARN_CATEGORY_DEPRECATED)) { + if (RB_CONST_DEPRECATED_P(ce)) { if (klass == rb_cObject) { rb_warn("constant ::%"PRIsVALUE" is deprecated", QUOTE_ID(id)); } @@ -2327,11 +2257,14 @@ rb_const_get_0(VALUE klass, ID id, int exclude, int recurse, int visibility) } static VALUE -rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibility) +rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility) { - VALUE value, tmp; + VALUE value, tmp, av; + rb_const_flag_t flag; + int mod_retry = 0; tmp = klass; + retry: while (RTEST(tmp)) { VALUE am = 0; rb_const_entry_t *ce; @@ -2339,48 +2272,40 @@ rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibilit while ((ce = rb_const_lookup(tmp, id))) { if (visibility && RB_CONST_PRIVATE_P(ce)) { if (BUILTIN_TYPE(tmp) == T_ICLASS) tmp = RBASIC(tmp)->klass; - GET_EC()->private_const_reference = tmp; - return Qundef; + rb_name_err_raise("private constant %2$s::%1$s referenced", + tmp, ID2SYM(id)); } rb_const_warn_if_deprecated(ce, tmp, id); value = ce->value; if (value == Qundef) { - struct autoload_const *ac; if (am == tmp) break; am = tmp; - ac = autoloading_const_entry(tmp, id); - if (ac) return ac->value; + if (rb_autoloading_value(tmp, id, &av, &flag)) return av; rb_autoload_load(tmp, id); continue; } - if (exclude && tmp == rb_cObject) { - goto not_found; + if (exclude && tmp == rb_cObject && klass != rb_cObject) { +#if 0 + rb_warn("toplevel constant %"PRIsVALUE" referenced by %"PRIsVALUE"::%"PRIsVALUE"", + QUOTE_ID(id), rb_class_name(klass), QUOTE_ID(id)); +#else + return Qundef; +#endif } return value; } if (!recurse) break; tmp = RCLASS_SUPER(tmp); } + if (!exclude && !mod_retry && BUILTIN_TYPE(klass) == T_MODULE) { + mod_retry = 1; + tmp = rb_cObject; + goto retry; + } - not_found: - GET_EC()->private_const_reference = 0; return Qundef; } -static VALUE -rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility) -{ - VALUE value; - - if (klass == rb_cObject) exclude = FALSE; - value = rb_const_search_from(klass, id, exclude, recurse, visibility); - if (value != Qundef) return value; - if (exclude) return value; - if (BUILTIN_TYPE(klass) != T_MODULE) return value; - /* search global const too, if klass is a module */ - return rb_const_search_from(rb_cObject, id, FALSE, recurse, visibility); -} - VALUE rb_const_get_from(VALUE klass, ID id) { @@ -2399,74 +2324,22 @@ rb_const_get_at(VALUE klass, ID id) return rb_const_get_0(klass, id, TRUE, FALSE, FALSE); } -MJIT_FUNC_EXPORTED VALUE +VALUE rb_public_const_get_from(VALUE klass, ID id) { return rb_const_get_0(klass, id, TRUE, TRUE, TRUE); } -MJIT_FUNC_EXPORTED VALUE -rb_public_const_get_at(VALUE klass, ID id) -{ - return rb_const_get_0(klass, id, TRUE, FALSE, TRUE); -} - -NORETURN(static void undefined_constant(VALUE mod, VALUE name)); -static void -undefined_constant(VALUE mod, VALUE name) -{ - rb_name_err_raise("constant %2$s::%1$s not defined", - mod, name); -} - -static VALUE -rb_const_location_from(VALUE klass, ID id, int exclude, int recurse, int visibility) -{ - while (RTEST(klass)) { - rb_const_entry_t *ce; - - while ((ce = rb_const_lookup(klass, id))) { - if (visibility && RB_CONST_PRIVATE_P(ce)) { - return Qnil; - } - if (exclude && klass == rb_cObject) { - goto not_found; - } - if (NIL_P(ce->file)) return rb_ary_new(); - return rb_assoc_new(ce->file, INT2NUM(ce->line)); - } - if (!recurse) break; - klass = RCLASS_SUPER(klass); - } - - not_found: - return Qnil; -} - -static VALUE -rb_const_location(VALUE klass, ID id, int exclude, int recurse, int visibility) -{ - VALUE loc; - - if (klass == rb_cObject) exclude = FALSE; - loc = rb_const_location_from(klass, id, exclude, recurse, visibility); - if (!NIL_P(loc)) return loc; - if (exclude) return loc; - if (BUILTIN_TYPE(klass) != T_MODULE) return loc; - /* search global const too, if klass is a module */ - return rb_const_location_from(rb_cObject, id, FALSE, recurse, visibility); -} - VALUE -rb_const_source_location(VALUE klass, ID id) +rb_public_const_get(VALUE klass, ID id) { - return rb_const_location(klass, id, FALSE, TRUE, FALSE); + return rb_const_get_0(klass, id, FALSE, TRUE, TRUE); } -MJIT_FUNC_EXPORTED VALUE -rb_const_source_location_at(VALUE klass, ID id) +VALUE +rb_public_const_get_at(VALUE klass, ID id) { - return rb_const_location(klass, id, TRUE, FALSE, FALSE); + return rb_const_get_0(klass, id, TRUE, FALSE, TRUE); } /* @@ -2485,7 +2358,8 @@ rb_mod_remove_const(VALUE mod, VALUE name) const ID id = id_for_var(mod, name, a, constant); if (!id) { - undefined_constant(mod, name); + rb_name_err_raise("constant %2$s::%1$s not defined", + mod, name); } return rb_const_remove(mod, id); } @@ -2503,7 +2377,8 @@ rb_const_remove(VALUE mod, ID id) rb_name_err_raise("cannot remove %2$s::%1$s", mod, ID2SYM(id)); } - undefined_constant(mod, ID2SYM(id)); + rb_name_err_raise("constant %2$s::%1$s not defined", + mod, ID2SYM(id)); } rb_clear_constant_cache(); @@ -2623,17 +2498,22 @@ rb_const_list(void *data) * IO.constants.include?(:SYNC) #=> true * IO.constants(false).include?(:SYNC) #=> false * - * Also see Module#const_defined?. + * Also see <code>Module::const_defined?</code>. */ VALUE rb_mod_constants(int argc, const VALUE *argv, VALUE mod) { - bool inherit = true; + VALUE inherit; - if (rb_check_arity(argc, 0, 1)) inherit = RTEST(argv[0]); + if (argc == 0) { + inherit = Qtrue; + } + else { + rb_scan_args(argc, argv, "01", &inherit); + } - if (inherit) { + if (RTEST(inherit)) { return rb_const_list(rb_mod_const_of(mod, 0)); } else { @@ -2694,62 +2574,28 @@ rb_const_defined_at(VALUE klass, ID id) return rb_const_defined_0(klass, id, TRUE, FALSE, FALSE); } -MJIT_FUNC_EXPORTED int +int rb_public_const_defined_from(VALUE klass, ID id) { return rb_const_defined_0(klass, id, TRUE, TRUE, TRUE); } -static void -check_before_mod_set(VALUE klass, ID id, VALUE val, const char *dest) +int +rb_public_const_defined(VALUE klass, ID id) { - rb_check_frozen(klass); + return rb_const_defined_0(klass, id, FALSE, TRUE, TRUE); } -static void set_namespace_path(VALUE named_namespace, VALUE name); - -static enum rb_id_table_iterator_result -set_namespace_path_i(ID id, VALUE v, void *payload) +int +rb_public_const_defined_at(VALUE klass, ID id) { - 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; - } - set_namespace_path(value, 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); - } - - return ID_TABLE_CONTINUE; + return rb_const_defined_0(klass, id, TRUE, FALSE, TRUE); } -/* - * Assign permanent classpaths to all namespaces that are directly or indirectly - * nested under +named_namespace+. +named_namespace+ must have a permanent - * classpath. - */ static void -set_namespace_path(VALUE named_namespace, VALUE namespace_path) +check_before_mod_set(VALUE klass, ID id, VALUE val, const char *dest) { - struct rb_id_table *const_table = RCLASS_CONST_TBL(named_namespace); - if (!RCLASS_IV_TBL(named_namespace)) { - RCLASS_IV_TBL(named_namespace) = st_init_numtable(); - } - rb_class_ivar_set(named_namespace, classpath, namespace_path); - if (const_table) { - rb_id_table_foreach(const_table, set_namespace_path_i, &namespace_path); - } + rb_check_frozen(klass); } void @@ -2772,34 +2618,36 @@ rb_const_set(VALUE klass, ID id, VALUE val) setup_const_entry(ce, klass, val, CONST_PUBLIC); } else { - struct autoload_const ac = { - .mod = klass, .id = id, - .value = val, .flag = CONST_PUBLIC, - /* fill the rest with 0 */ - }; - const_tbl_update(&ac); + struct autoload_const_set_args args; + args.mod = klass; + args.id = id; + args.value = val; + args.flag = CONST_PUBLIC; + const_tbl_update(&args); } /* * Resolve and cache class name immediately to resolve ambiguity * and avoid order-dependency on const_tbl */ - if (rb_cObject && rb_namespace_p(val)) { - int val_path_permanent; - VALUE val_path = classname(val, &val_path_permanent); - if (NIL_P(val_path) || !val_path_permanent) { + if (rb_cObject && (RB_TYPE_P(val, T_MODULE) || RB_TYPE_P(val, T_CLASS))) { + if (NIL_P(rb_class_path_cached(val))) { if (klass == rb_cObject) { - set_namespace_path(val, rb_id2str(id)); + rb_ivar_set(val, classpath, rb_id2str(id)); + rb_name_class(val, id); } else { - int parental_path_permanent; - VALUE parental_path = classname(klass, &parental_path_permanent); - if (!NIL_P(parental_path)) { - if (parental_path_permanent && !val_path_permanent) { - set_namespace_path(val, build_const_path(parental_path, id)); - } - else if (!parental_path_permanent && NIL_P(val_path)) { - rb_ivar_set(val, tmp_classpath, build_const_path(parental_path, id)); - } + 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); } } } @@ -2807,12 +2655,12 @@ rb_const_set(VALUE klass, ID id, VALUE val) } static struct autoload_data_i * -current_autoload_data(VALUE mod, ID id, struct autoload_const **acp) +current_autoload_data(VALUE mod, ID id) { struct autoload_data_i *ele; VALUE load = autoload_data(mod, id); if (!load) return 0; - ele = get_autoload_data(load, acp); + ele = get_autoload_data(load); if (!ele) return 0; /* for autoloading thread, keep the defined value to autoloading storage */ if (ele->state && (ele->state->thread == rb_thread_current())) { @@ -2822,36 +2670,29 @@ current_autoload_data(VALUE mod, ID id, struct autoload_const **acp) } static void -const_tbl_update(struct autoload_const *ac) +const_tbl_update(struct autoload_const_set_args *args) { VALUE value; - VALUE klass = ac->mod; - VALUE val = ac->value; - ID id = ac->id; + VALUE klass = args->mod; + VALUE val = args->value; + ID id = args->id; struct rb_id_table *tbl = RCLASS_CONST_TBL(klass); - rb_const_flag_t visibility = ac->flag; + rb_const_flag_t visibility = args->flag; rb_const_entry_t *ce; if (rb_id_table_lookup(tbl, id, &value)) { ce = (rb_const_entry_t *)value; if (ce->value == Qundef) { - struct autoload_data_i *ele = current_autoload_data(klass, id, &ac); + struct autoload_data_i *ele = current_autoload_data(klass, id); if (ele) { rb_clear_constant_cache(); - ac->value = val; /* autoload_i is non-WB-protected */ - ac->file = rb_source_location(&ac->line); + ele->value = val; /* autoload_i is non-WB-protected */ + return; } - else { - /* otherwise autoloaded constant, allow to override */ - autoload_delete(klass, id); - ce->flag = visibility; - RB_OBJ_WRITE(klass, &ce->value, val); - RB_OBJ_WRITE(klass, &ce->file, ac->file); - ce->line = ac->line; - } - return; + /* otherwise, allow to override */ + autoload_delete(klass, id); } else { VALUE name = QUOTE_ID(id); @@ -2895,7 +2736,6 @@ rb_define_const(VALUE klass, const char *name, VALUE val) if (!rb_is_const_id(id)) { rb_warn("rb_define_const: invalid name `%s' for constant", name); } - rb_gc_register_mark_object(val); rb_const_set(klass, id, val); } @@ -2913,7 +2753,7 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv, rb_const_entry_t *ce; ID id; - rb_class_modify_check(mod); + rb_frozen_class_p(mod); if (argc == 0) { rb_warning("%"PRIsVALUE" with no argument is just ignored", QUOTE_ID(rb_frame_callee())); @@ -2921,7 +2761,6 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv, } for (i = 0; i < argc; i++) { - struct autoload_const *ac; VALUE val = argv[i]; id = rb_check_id(&val); if (!id) { @@ -2929,18 +2768,17 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv, rb_clear_constant_cache(); } - undefined_constant(mod, val); + rb_name_err_raise("constant %2$s::%1$s not defined", + mod, val); } if ((ce = rb_const_lookup(mod, id))) { ce->flag &= ~mask; ce->flag |= flag; if (ce->value == Qundef) { - struct autoload_data_i *ele; - - ele = current_autoload_data(mod, id, &ac); + struct autoload_data_i *ele = current_autoload_data(mod, id); if (ele) { - ac->flag &= ~mask; - ac->flag |= flag; + ele->flag &= ~mask; + ele->flag |= flag; } } } @@ -2948,7 +2786,8 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv, if (i > 0) { rb_clear_constant_cache(); } - undefined_constant(mod, ID2SYM(id)); + rb_name_err_raise("constant %2$s::%1$s not defined", + mod, ID2SYM(id)); } } rb_clear_constant_cache(); @@ -2961,12 +2800,11 @@ rb_deprecate_constant(VALUE mod, const char *name) ID id; long len = strlen(name); - rb_class_modify_check(mod); - if (!(id = rb_check_id_cstr(name, len, NULL))) { - undefined_constant(mod, rb_fstring_new(name, len)); - } - if (!(ce = rb_const_lookup(mod, id))) { - undefined_constant(mod, ID2SYM(id)); + rb_frozen_class_p(mod); + if (!(id = rb_check_id_cstr(name, len, NULL)) || + !(ce = rb_const_lookup(mod, id))) { + rb_name_err_raise("constant %2$s::%1$s not defined", + mod, rb_fstring_new(name, len)); } ce->flag |= CONST_DEPRECATED; } @@ -3003,19 +2841,7 @@ rb_mod_public_constant(int argc, const VALUE *argv, VALUE obj) * call-seq: * mod.deprecate_constant(symbol, ...) => mod * - * Makes a list of existing constants deprecated. Attempt - * to refer to them will produce a warning. - * - * module HTTP - * NotFound = Exception.new - * NOT_FOUND = NotFound # previous version of the library used this name - * - * deprecate_constant :NOT_FOUND - * end - * - * HTTP::NOT_FOUND - * # warning: constant HTTP::NOT_FOUND is deprecated - * + * Makes a list of existing constants deprecated. */ VALUE @@ -3045,30 +2871,13 @@ cvar_front_klass(VALUE klass) { if (FL_TEST(klass, FL_SINGLETON)) { VALUE obj = rb_ivar_get(klass, id__attached__); - if (rb_namespace_p(obj)) { + if (RB_TYPE_P(obj, T_MODULE) || RB_TYPE_P(obj, T_CLASS)) { return obj; } } return RCLASS_SUPER(klass); } -static void -cvar_overtaken(VALUE front, VALUE target, ID id) -{ - if (front && target != front) { - st_data_t did = (st_data_t)id; - - if (RTEST(ruby_verbose) && original_module(front) != original_module(target)) { - rb_warning("class variable % "PRIsVALUE" of %"PRIsVALUE" is overtaken by %"PRIsVALUE"", - ID2SYM(id), rb_class_name(original_module(front)), - rb_class_name(original_module(target))); - } - if (BUILTIN_TYPE(front) == T_CLASS) { - st_delete(RCLASS_IV_TBL(front), &did, 0); - } - } -} - #define CVAR_FOREACH_ANCESTORS(klass, v, r) \ for (klass = cvar_front_klass(klass); klass; klass = RCLASS_SUPER(klass)) { \ if (cvar_lookup_at(klass, id, (v))) { \ @@ -3089,7 +2898,18 @@ rb_cvar_set(VALUE klass, ID id, VALUE val) tmp = klass; CVAR_LOOKUP(0, {if (!front) front = klass; target = klass;}); if (target) { - cvar_overtaken(front, target, id); + if (front && target != front) { + st_data_t did = id; + + if (RTEST(ruby_verbose)) { + rb_warning("class variable %"PRIsVALUE" of %"PRIsVALUE" is overtaken by %"PRIsVALUE"", + QUOTE_ID(id), rb_class_name(original_module(front)), + rb_class_name(original_module(target))); + } + if (BUILTIN_TYPE(front) == T_CLASS) { + st_delete(RCLASS_IV_TBL(front),&did,0); + } + } } else { target = tmp; @@ -3115,7 +2935,18 @@ rb_cvar_get(VALUE klass, ID id) rb_name_err_raise("uninitialized class variable %1$s in %2$s", tmp, ID2SYM(id)); } - cvar_overtaken(front, target, id); + if (front && target != front) { + st_data_t did = id; + + if (RTEST(ruby_verbose)) { + rb_warning("class variable %"PRIsVALUE" of %"PRIsVALUE" is overtaken by %"PRIsVALUE"", + QUOTE_ID(id), rb_class_name(original_module(front)), + rb_class_name(original_module(target))); + } + if (BUILTIN_TYPE(front) == T_CLASS) { + st_delete(RCLASS_IV_TBL(front),&did,0); + } + } return (VALUE)value; } @@ -3188,12 +3019,6 @@ static void* mod_cvar_of(VALUE mod, void *data) { VALUE tmp = mod; - if (FL_TEST(mod, FL_SINGLETON)) { - if (rb_namespace_p(rb_ivar_get(mod, id__attached__))) { - data = mod_cvar_at(tmp, data); - tmp = cvar_front_klass(tmp); - } - } for (;;) { data = mod_cvar_at(tmp, data); tmp = RCLASS_SUPER(tmp); @@ -3247,11 +3072,16 @@ cvar_list(void *data) VALUE rb_mod_class_variables(int argc, const VALUE *argv, VALUE mod) { - bool inherit = true; + VALUE inherit; st_table *tbl; - if (rb_check_arity(argc, 0, 1)) inherit = RTEST(argv[0]); - if (inherit) { + if (argc == 0) { + inherit = Qtrue; + } + else { + rb_scan_args(argc, argv, "01", &inherit); + } + if (RTEST(inherit)) { tbl = mod_cvar_of(mod, 0); } else { @@ -3304,13 +3134,8 @@ rb_mod_remove_cvar(VALUE mod, VALUE name) VALUE rb_iv_get(VALUE obj, const char *name) { - ID id = rb_check_id_cstr(name, strlen(name), rb_usascii_encoding()); + ID id = rb_intern(name); - if (!id) { - if (RTEST(ruby_verbose)) - rb_warning("instance variable %s not initialized", name); - return Qnil; - } return rb_ivar_get(obj, id); } @@ -3339,16 +3164,15 @@ tbl_copy_i(st_data_t key, st_data_t value, st_data_t data) return ST_CONTINUE; } -void -rb_iv_tbl_copy(VALUE dst, VALUE src) +st_table * +rb_st_copy(VALUE obj, struct st_table *orig_tbl) { - st_table *orig_tbl = RCLASS_IV_TBL(src); st_table *new_tbl = st_copy(orig_tbl); - st_foreach(new_tbl, tbl_copy_i, (st_data_t)dst); - RCLASS_IV_TBL(dst) = new_tbl; + st_foreach(new_tbl, tbl_copy_i, (st_data_t)obj); + return new_tbl; } -MJIT_FUNC_EXPORTED rb_const_entry_t * +rb_const_entry_t * rb_const_lookup(VALUE klass, ID id) { struct rb_id_table *tbl = RCLASS_CONST_TBL(klass); |
