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;