diff --git .gitignore .gitignore index 5808bf1..0655206 100644 --- .gitignore +++ .gitignore @@ -1,3 +1,4 @@ +inst *-*-*.def *.a *.bak 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 object.c object.c index ba4f997..d455fb9 100644 --- object.c +++ object.c @@ -2756,6 +2756,21 @@ rb_Hash(VALUE val) /* * call-seq: + * Class#has_subclass? -> bool + * + * Returns true if the class has ever been subclassed. + * + * Object.has_subclass? #=> true + * Class.new.has_subclass? #=> false + */ +VALUE +rb_class_has_subclass_p(VALUE klass) +{ + return FL_TEST(klass, RCLASS_INHERITED_FLAG) ? Qtrue : Qfalse; +} + +/* + * call-seq: * Hash(arg) -> hash * * Converts arg to a Hash by calling @@ -3105,6 +3120,7 @@ Init_Object(void) rb_define_method(rb_cClass, "allocate", rb_obj_alloc, 0); rb_define_method(rb_cClass, "new", rb_class_new_instance, -1); + rb_define_method(rb_cClass, "has_subclass?", rb_class_has_subclass_p, 0); rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1); rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0); rb_define_alloc_func(rb_cClass, rb_class_s_alloc); 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..1a56391 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;