summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--proc.c118
-rw-r--r--test/-ext-/symbol/test_inadvertent_creation.rb22
-rw-r--r--version.h2
4 files changed, 107 insertions, 43 deletions
diff --git a/ChangeLog b/ChangeLog
index 19afff14d9..e4ab2d1a98 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Wed May 20 02:49:49 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * proc.c (respond_to_missing_p): check if the receiver responds to
+ the given method by respond_to_missing?.
+
+ * proc.c (mnew_missing): create Method object for method_missing.
+ [ruby-core:68564] [Bug #10985]
+
Wed May 20 02:16:05 2015 NAKAMURA Usaku <usa@ruby-lang.org>
* dir.c (replace_real_basename): need to check the return value of
diff --git a/proc.c b/proc.c
index 48d7dcb490..a263470684 100644
--- a/proc.c
+++ b/proc.c
@@ -1131,29 +1131,68 @@ rb_obj_is_method(VALUE m)
}
}
+static int
+respond_to_missing_p(VALUE klass, VALUE obj, VALUE sym, int scope)
+{
+ /* TODO: merge with obj_respond_to() */
+ ID rmiss = idRespond_to_missing;
+
+ if (obj == Qundef) return 0;
+ if (rb_method_basic_definition_p(klass, rmiss)) return 0;
+ return RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue));
+}
+
+
+static VALUE
+mnew_missing(VALUE rclass, VALUE klass, VALUE obj, ID id, ID rid, VALUE mclass)
+{
+ struct METHOD *data;
+ VALUE method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
+ rb_method_entry_t *me;
+ rb_method_definition_t *def;
+
+ data->recv = obj;
+ data->rclass = rclass;
+ data->defined_class = klass;
+ data->id = rid;
+
+ me = ALLOC(rb_method_entry_t);
+ data->me = me;
+ me->flag = 0;
+ me->mark = 0;
+ me->called_id = id;
+ me->klass = klass;
+ me->def = 0;
+
+ def = ALLOC(rb_method_definition_t);
+ me->def = def;
+ def->type = VM_METHOD_TYPE_MISSING;
+ def->original_id = id;
+ def->alias_count = 0;
+
+ data->ume = ALLOC(struct unlinked_method_entry_list_entry);
+ data->me->def->alias_count++;
+
+ OBJ_INFECT(method, klass);
+
+ return method;
+}
+
static VALUE
mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass,
VALUE obj, ID id, VALUE mclass, int scope, int error)
{
- VALUE method;
+ struct METHOD *data;
VALUE rclass = klass;
+ VALUE method;
ID rid = id;
- struct METHOD *data;
rb_method_definition_t *def = 0;
rb_method_flag_t flag = NOEX_UNDEF;
again:
if (UNDEFINED_METHOD_ENTRY_P(me)) {
- ID rmiss = idRespond_to_missing;
- VALUE sym = ID2SYM(id);
-
- if (obj != Qundef && !rb_method_basic_definition_p(klass, rmiss)) {
- if (RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue))) {
- me = 0;
- defined_class = klass;
-
- goto gen_method;
- }
+ if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) {
+ return mnew_missing(rclass, klass, obj, id, rid, mclass);
}
if (!error) return Qnil;
rb_print_undef(klass, id, 0);
@@ -1180,7 +1219,6 @@ mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass,
rclass = RCLASS_SUPER(rclass);
}
- gen_method:
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
data->recv = obj;
@@ -1188,25 +1226,7 @@ mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass,
data->defined_class = defined_class;
data->id = rid;
data->me = ALLOC(rb_method_entry_t);
- if (me) {
- *data->me = *me;
- }
- else {
- me = data->me;
- me->flag = 0;
- me->mark = 0;
- me->called_id = id;
- me->klass = klass;
- me->def = 0;
-
- def = ALLOC(rb_method_definition_t);
- me->def = def;
-
- def->type = VM_METHOD_TYPE_MISSING;
- def->original_id = id;
- def->alias_count = 0;
-
- }
+ *data->me = *me;
data->ume = ALLOC(struct unlinked_method_entry_list_entry);
data->me->def->alias_count++;
@@ -1438,6 +1458,23 @@ rb_method_name_error(VALUE klass, VALUE str)
QUOTE(str), s0, rb_class_name(c));
}
+static VALUE
+obj_method(VALUE obj, VALUE vid, int scope)
+{
+ ID id = rb_check_id(&vid);
+ const VALUE klass = CLASS_OF(obj);
+ const VALUE mclass = rb_cMethod;
+
+ if (!id) {
+ if (respond_to_missing_p(klass, obj, vid, scope)) {
+ id = rb_intern_str(vid);
+ return mnew_missing(klass, klass, obj, id, id, mclass);
+ }
+ rb_method_name_error(klass, vid);
+ }
+ return mnew(klass, obj, id, mclass, scope);
+}
+
/*
* call-seq:
* obj.method(sym) -> method
@@ -1469,11 +1506,7 @@ rb_method_name_error(VALUE klass, VALUE str)
VALUE
rb_obj_method(VALUE obj, VALUE vid)
{
- ID id = rb_check_id(&vid);
- if (!id) {
- rb_method_name_error(CLASS_OF(obj), vid);
- }
- return mnew(CLASS_OF(obj), obj, id, rb_cMethod, FALSE);
+ return obj_method(obj, vid, FALSE);
}
/*
@@ -1486,11 +1519,7 @@ rb_obj_method(VALUE obj, VALUE vid)
VALUE
rb_obj_public_method(VALUE obj, VALUE vid)
{
- ID id = rb_check_id(&vid);
- if (!id) {
- rb_method_name_error(CLASS_OF(obj), vid);
- }
- return mnew(CLASS_OF(obj), obj, id, rb_cMethod, TRUE);
+ return obj_method(obj, vid, TRUE);
}
/*
@@ -1524,6 +1553,11 @@ rb_obj_singleton_method(VALUE obj, VALUE vid)
VALUE klass;
ID id = rb_check_id(&vid);
if (!id) {
+ if (!NIL_P(klass = rb_singleton_class_get(obj)) &&
+ respond_to_missing_p(klass, obj, vid, FALSE)) {
+ id = rb_intern_str(vid);
+ return mnew_missing(klass, klass, obj, id, id, rb_cMethod);
+ }
rb_name_error_str(vid, "undefined singleton method `%"PRIsVALUE"' for `%"PRIsVALUE"'",
QUOTE(vid), obj);
}
diff --git a/test/-ext-/symbol/test_inadvertent_creation.rb b/test/-ext-/symbol/test_inadvertent_creation.rb
index 4e7a85fd91..f772086fed 100644
--- a/test/-ext-/symbol/test_inadvertent_creation.rb
+++ b/test/-ext-/symbol/test_inadvertent_creation.rb
@@ -82,6 +82,28 @@ module Test_Symbol
assert_not_interned_false(c, :class_variable_defined?, noninterned_name("@@"), feature5072)
end
+ def test_missing_method
+ bug10985 = '[ruby-core:68564] [Bug #10985]'
+ m = nil
+ c = Class.new do
+ def self.respond_to_missing?(*)
+ true
+ end
+ end
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.method(s)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.public_method(s.to_sym)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.singleton_method(s.to_sym)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+ end
+
Feature5079 = '[ruby-core:38404]'
def test_undefined_instance_variable
diff --git a/version.h b/version.h
index 206d7ee918..ae7762dc6d 100644
--- a/version.h
+++ b/version.h
@@ -1,6 +1,6 @@
#define RUBY_VERSION "2.2.3"
#define RUBY_RELEASE_DATE "2015-05-20"
-#define RUBY_PATCHLEVEL 105
+#define RUBY_PATCHLEVEL 106
#define RUBY_RELEASE_YEAR 2015
#define RUBY_RELEASE_MONTH 5