diff --git class.c class.c index 36e7b76..26a0f78 100644 --- class.c +++ class.c @@ -80,6 +80,7 @@ rb_class_boot(VALUE super) RCLASS_SUPER(klass) = super; RCLASS_M_TBL(klass) = st_init_numtable(); + FL_SET(super, RCLASS_INHERITED_FLAG); OBJ_INFECT(klass, super); return (VALUE)klass; } diff --git include/ruby/ruby.h include/ruby/ruby.h index 6debe42..bc6e5d4 100644 --- include/ruby/ruby.h +++ include/ruby/ruby.h @@ -748,6 +748,7 @@ struct RClass { #define RMODULE_IS_OVERLAID FL_USER2 #define RMODULE_IS_REFINEMENT FL_USER3 #define RMODULE_INCLUDED_INTO_REFINEMENT FL_USER4 +#define RCLASS_INHERITED_FLAG FL_USER5 struct RFloat { struct RBasic basic; diff --git insns.def insns.def index 85c0a38..7e17d2f 100644 --- insns.def +++ insns.def @@ -975,7 +975,6 @@ defineclass class_iseq->local_size, 0); RESTORE_REGS(); - INC_VM_STATE_VERSION(); NEXT_INSN(); } diff --git vm.c vm.c index 36def2c..63152d5 100644 --- vm.c +++ vm.c @@ -2007,7 +2007,6 @@ vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval, if (!is_singleton && noex == NOEX_MODFUNC) { rb_add_method(rb_singleton_class(klass), id, VM_METHOD_TYPE_ISEQ, miseq, NOEX_PUBLIC); } - INC_VM_STATE_VERSION(); } #define REWIND_CFP(expr) do { \ @@ -2202,6 +2201,22 @@ static VALUE usage_analysis_operand_stop(VALUE self); static VALUE usage_analysis_register_stop(VALUE self); #endif +/* + * call-seq: + * RubyVM.state_version -> int + * + * Returns the current VM state version. The state version is used for + * invalidating inline method and constant caches. + * + * The state version is not guaranteed to be monotonic and may wrap around in + * long running processes. + */ +static VALUE +vm_state_version() +{ + return INT2FIX(ruby_vm_global_state_version); +} + void Init_VM(void) { @@ -2213,6 +2228,7 @@ Init_VM(void) rb_cRubyVM = rb_define_class("RubyVM", rb_cObject); rb_undef_alloc_func(rb_cRubyVM); rb_undef_method(CLASS_OF(rb_cRubyVM), "new"); + rb_define_singleton_method(rb_cRubyVM, "state_version", vm_state_version, 0); /* FrozenCore (hidden) */ fcore = rb_class_new(rb_cBasicObject); diff --git vm_method.c vm_method.c index a82ba5f..1797d57 100644 --- vm_method.c +++ vm_method.c @@ -220,6 +220,9 @@ rb_add_refined_method_entry(VALUE refined_class, ID mid) } } +static inline rb_method_entry_t* +search_method(VALUE klass, ID id, VALUE *defined_class_ptr); + static rb_method_entry_t * rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, rb_method_definition_t *def, rb_method_flag_t noex) @@ -315,7 +318,12 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, me = ALLOC(rb_method_entry_t); - rb_clear_cache_by_id(mid); + if (search_method(klass, mid, NULL) /* clear cache if overriding existing method */ + || FL_TEST(klass, RCLASS_INHERITED_FLAG) /* or this class has subclasses */ + || RB_TYPE_P(klass, T_MODULE) /* or this is a module */) + { + rb_clear_cache_by_id(mid); + } me->flag = NOEX_WITH_SAFE(noex); me->mark = 0; @@ -555,7 +563,7 @@ rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr) ent = cache + EXPR1(klass, id); if (ent->filled_version == GET_VM_STATE_VERSION() && - ent->mid == id && ent->klass == klass) { + ent->mid == id && ent->klass == klass && ent->me) { if (defined_class_ptr) *defined_class_ptr = ent->defined_class; return ent->me;