summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-02-22 01:43:59 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-02-22 01:43:59 +0000
commit19bbab6140847de08dd0278a7ebaae6b01352513 (patch)
tree45adc1914ed6ec3198dbc48b7a7d91c57f531ac0
parent3b39217fb4f115129522207e083a4875beecf5e9 (diff)
* vm_eval.c (method_missing): should not pop cfp if missing method
is method_missing. [ruby-core:22298] * vm_eval.c (rb_raise_method_missing): new function to directly raise NoMethodError. * vm_insnhelper.c (vm_call_method): fixed the case method_missing is missing. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@22494 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog11
-rw-r--r--eval_intern.h2
-rw-r--r--vm_eval.c33
-rw-r--r--vm_insnhelper.c37
4 files changed, 62 insertions, 21 deletions
diff --git a/ChangeLog b/ChangeLog
index e7ea7f1..0ddd247 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Sun Feb 22 10:43:57 2009 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * vm_eval.c (method_missing): should not pop cfp if missing method
+ is method_missing. [ruby-core:22298]
+
+ * vm_eval.c (rb_raise_method_missing): new function to directly
+ raise NoMethodError.
+
+ * vm_insnhelper.c (vm_call_method): fixed the case method_missing
+ is missing.
+
Sun Feb 22 02:15:40 2009 Tanaka Akira <akr@fsij.org>
* ext/socket/ancdata.c (bsock_recvmsg_internal): handle EMSGSIZE as
diff --git a/eval_intern.h b/eval_intern.h
index ac2999d..a9d29ff 100644
--- a/eval_intern.h
+++ b/eval_intern.h
@@ -195,6 +195,8 @@ NORETURN(void rb_fiber_start(void));
NORETURN(void rb_print_undef(VALUE, ID, int));
NORETURN(void rb_vm_localjump_error(const char *,VALUE, int));
NORETURN(void rb_vm_jump_tag_but_local_jump(int, VALUE));
+NORETURN(void rb_raise_method_missing(rb_thread_t *th, int argc, VALUE *argv,
+ VALUE obj, int call_status));
VALUE rb_vm_make_jump_tag_but_local_jump(int state, VALUE val);
NODE *rb_vm_cref(void);
diff --git a/vm_eval.c b/vm_eval.c
index b43e88d..88113d3 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -256,6 +256,9 @@ rb_call(VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int scope)
return rb_call0(klass, recv, mid, argc, argv, scope, Qundef);
}
+NORETURN(static void raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv,
+ VALUE obj, int call_status));
+
/*
* call-seq:
* obj.method_missing(symbol [, *args] ) => result
@@ -292,11 +295,21 @@ rb_call(VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int scope)
static VALUE
rb_method_missing(int argc, const VALUE *argv, VALUE obj)
{
+ rb_thread_t *th = GET_THREAD();
+ raise_method_missing(th, argc, argv, obj, th->method_missing_reason);
+ return Qnil; /* not reached */
+}
+
+#define NOEX_MISSING 0x80
+
+static void
+raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv, VALUE obj,
+ int last_call_status)
+{
ID id;
VALUE exc = rb_eNoMethodError;
const char *format = 0;
- rb_thread_t *th = GET_THREAD();
- int last_call_status = th->method_missing_reason;
+
if (argc == 0 || !SYMBOL_P(argv[0])) {
rb_raise(rb_eArgError, "no id given");
}
@@ -333,11 +346,11 @@ rb_method_missing(int argc, const VALUE *argv, VALUE obj)
}
exc = rb_class_new_instance(n, args, exc);
- th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
+ if (!(last_call_status & NOEX_MISSING)) {
+ th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
+ }
rb_exc_raise(exc);
}
-
- return Qnil; /* not reached */
}
static inline VALUE
@@ -350,7 +363,7 @@ method_missing(VALUE obj, ID id, int argc, const VALUE *argv, int call_status)
th->passed_block = 0;
if (id == idMethodMissing) {
- rb_method_missing(argc, argv, obj);
+ raise_method_missing(th, argc, argv, obj, call_status | NOEX_MISSING);
}
else if (id == ID_ALLOCATOR) {
rb_raise(rb_eTypeError, "allocator undefined for %s",
@@ -372,6 +385,14 @@ method_missing(VALUE obj, ID id, int argc, const VALUE *argv, int call_status)
return result;
}
+void
+rb_raise_method_missing(rb_thread_t *th, int argc, VALUE *argv,
+ VALUE obj, int call_status)
+{
+ th->passed_block = 0;
+ raise_method_missing(th, argc, argv, obj, call_status | NOEX_MISSING);
+}
+
VALUE
rb_apply(VALUE recv, ID mid, VALUE args)
{
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 0e54ddd..ffb36e9 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -403,20 +403,25 @@ vm_call_bmethod(rb_thread_t *th, ID id, VALUE procval, VALUE recv,
return val;
}
-static inline VALUE
-vm_method_missing(rb_thread_t *th, ID id, VALUE recv,
+static inline void
+vm_method_missing_args(rb_thread_t *th, VALUE *argv,
int num, rb_block_t *blockptr, int opt)
{
- VALUE val;
rb_control_frame_t * const reg_cfp = th->cfp;
- VALUE *argv = ALLOCA_N(VALUE, num + 1);
MEMCPY(argv, STACK_ADDR_FROM_TOP(num + 1), VALUE, num + 1);
- argv[0] = ID2SYM(id);
th->method_missing_reason = opt;
th->passed_block = blockptr;
POPN(num + 1);
- val = rb_funcall2(recv, idMethodMissing, num + 1, argv);
- return val;
+}
+
+static inline VALUE
+vm_method_missing(rb_thread_t *th, ID id, VALUE recv,
+ int num, rb_block_t *blockptr, int opt)
+{
+ VALUE *argv = ALLOCA_N(VALUE, num + 1);
+ vm_method_missing_args(th, argv, num, blockptr, opt);
+ argv[0] = ID2SYM(id);
+ return rb_funcall2(recv, idMethodMissing, num + 1, argv);
}
static inline void
@@ -581,17 +586,19 @@ vm_call_method(rb_thread_t * const th, rb_control_frame_t * const cfp,
}
else {
/* method missing */
+ int stat = 0;
+ if (flag & VM_CALL_VCALL_BIT) {
+ stat |= NOEX_VCALL;
+ }
+ if (flag & VM_CALL_SUPER_BIT) {
+ stat |= NOEX_SUPER;
+ }
if (id == idMethodMissing) {
- rb_bug("method missing");
+ VALUE *argv = ALLOCA_N(VALUE, num);
+ vm_method_missing_args(th, argv, num - 1, 0, stat);
+ rb_raise_method_missing(th, num, argv, recv, stat);
}
else {
- int stat = 0;
- if (flag & VM_CALL_VCALL_BIT) {
- stat |= NOEX_VCALL;
- }
- if (flag & VM_CALL_SUPER_BIT) {
- stat |= NOEX_SUPER;
- }
val = vm_method_missing(th, id, recv, num, blockptr, stat);
}
}