summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--ext/-test-/symbol/extconf.rb6
-rw-r--r--ext/-test-/symbol/init.c11
-rw-r--r--ext/-test-/symbol/intern.c14
-rw-r--r--test/-ext-/symbol/test_inadvertent_creation.rb44
-rw-r--r--vm_eval.c57
-rw-r--r--vm_insnhelper.c12
-rw-r--r--vm_insnhelper.h4
8 files changed, 138 insertions, 22 deletions
diff --git a/ChangeLog b/ChangeLog
index 06c8ae9362..7f9dd87a0b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+Thu Oct 6 16:29:30 2011 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * vm_eval.c (make_no_method_execption): extract from
+ raise_method_missing().
+
+ * vm_eval.c (send_internal): remove inadvertent symbol creation
+ from public_send. based on a patch by Jeremy Evans <code AT
+ jeremyevans.net> in [ruby-core:38576]. [Feature #5112]
+
+ * vm_insnhelper.c (vm_call_method): remove inadvertent symbol
+ creation from send and __send__, too.
+
Thu Oct 6 14:59:11 2011 Eric Hodel <drbrain@segment7.net>
* lib/time.rb: Clean up Time documentation. Patch by Jake Goulding.
diff --git a/ext/-test-/symbol/extconf.rb b/ext/-test-/symbol/extconf.rb
new file mode 100644
index 0000000000..d093ff682a
--- /dev/null
+++ b/ext/-test-/symbol/extconf.rb
@@ -0,0 +1,6 @@
+$srcs = Dir[File.join($srcdir, "*.{#{SRC_EXT.join(%q{,})}}")]
+inits = $srcs.map {|s| File.basename(s, ".*")}
+inits.delete("init")
+inits.map! {|s|"X(#{s})"}
+$defs << "-DTEST_INIT_FUNCS(X)=\"#{inits.join(' ')}\""
+create_makefile("-test-/symbol/symbol")
diff --git a/ext/-test-/symbol/init.c b/ext/-test-/symbol/init.c
new file mode 100644
index 0000000000..e740345f2a
--- /dev/null
+++ b/ext/-test-/symbol/init.c
@@ -0,0 +1,11 @@
+#include "ruby.h"
+
+#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);}
+
+void
+Init_symbol(void)
+{
+ VALUE mBug = rb_define_module("Bug");
+ VALUE klass = rb_define_class_under(mBug, "Symbol", rb_cSymbol);
+ TEST_INIT_FUNCS(init);
+}
diff --git a/ext/-test-/symbol/intern.c b/ext/-test-/symbol/intern.c
new file mode 100644
index 0000000000..6ae86a6104
--- /dev/null
+++ b/ext/-test-/symbol/intern.c
@@ -0,0 +1,14 @@
+#include "ruby.h"
+
+static VALUE
+bug_sym_interned_p(VALUE self, VALUE name)
+{
+ ID id = rb_check_id(&name);
+ return id ? Qtrue : Qfalse;
+}
+
+void
+Init_intern(VALUE klass)
+{
+ rb_define_singleton_method(klass, "interned?", bug_sym_interned_p, 1);
+}
diff --git a/test/-ext-/symbol/test_inadvertent_creation.rb b/test/-ext-/symbol/test_inadvertent_creation.rb
new file mode 100644
index 0000000000..987cab56ef
--- /dev/null
+++ b/test/-ext-/symbol/test_inadvertent_creation.rb
@@ -0,0 +1,44 @@
+require 'test/unit'
+require "-test-/symbol/symbol"
+
+module Test_Symbol
+ class TestInadvertent < Test::Unit::TestCase
+ def self.noninterned_name
+ th = Thread.current.object_id.to_s(36)
+ begin
+ name = "#{th}.#{rand(0x1000).to_s(16)}.#{Time.now.usec}"
+ end while Bug::Symbol.interned?(name)
+ name
+ end
+
+ def setup
+ @obj = Object.new
+ end
+
+ Feature5112 = '[ruby-core:38576]'
+
+ def test_public_send
+ name = self.class.noninterned_name
+ e = assert_raise(NoMethodError) {@obj.public_send(name, Feature5112)}
+ assert_not_send([Bug::Symbol, :interned?, name])
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+
+ def test_send
+ name = self.class.noninterned_name
+ e = assert_raise(NoMethodError) {@obj.send(name, Feature5112)}
+ assert_not_send([Bug::Symbol, :interned?, name])
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+
+ def test___send__
+ name = self.class.noninterned_name
+ e = assert_raise(NoMethodError) {@obj.__send__(name, Feature5112)}
+ assert_not_send([Bug::Symbol, :interned?, name])
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+ end
+end
diff --git a/vm_eval.c b/vm_eval.c
index 8be6b437c3..da25c45f9e 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -495,6 +495,30 @@ rb_method_missing(int argc, const VALUE *argv, VALUE obj)
#define NOEX_MISSING 0x80
+static VALUE
+make_no_method_execption(VALUE exc, const char *format, VALUE obj, int argc, const VALUE *argv)
+{
+ int n = 0;
+ VALUE mesg;
+ VALUE args[3];
+
+ if (!format) {
+ format = "undefined method `%s' for %s";
+ }
+ mesg = rb_const_get(exc, rb_intern("message"));
+ if (rb_method_basic_definition_p(CLASS_OF(mesg), '!')) {
+ args[n++] = rb_name_err_mesg_new(mesg, rb_str_new2(format), obj, argv[0]);
+ }
+ else {
+ args[n++] = rb_funcall(mesg, '!', 3, rb_str_new2(format), obj, argv[0]);
+ }
+ args[n++] = argv[0];
+ if (exc == rb_eNoMethodError) {
+ args[n++] = rb_ary_new4(argc - 1, argv + 1);
+ }
+ return rb_class_new_instance(n, args, exc);
+}
+
static void
raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv, VALUE obj,
int last_call_status)
@@ -524,28 +548,9 @@ raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv, VALUE obj,
else if (last_call_status & NOEX_SUPER) {
format = "super: no superclass method `%s' for %s";
}
- if (!format) {
- format = "undefined method `%s' for %s";
- }
{
- int n = 0;
- VALUE mesg;
- VALUE args[3];
-
- mesg = rb_const_get(exc, rb_intern("message"));
- if (rb_method_basic_definition_p(CLASS_OF(mesg), '!')) {
- args[n++] = rb_name_err_mesg_new(mesg, rb_str_new2(format), obj, argv[0]);
- }
- else {
- args[n++] = rb_funcall(mesg, '!', 3, rb_str_new2(format), obj, argv[0]);
- }
- args[n++] = argv[0];
- if (exc == rb_eNoMethodError) {
- args[n++] = rb_ary_new4(argc - 1, argv + 1);
- }
- exc = rb_class_new_instance(n, args, exc);
-
+ exc = make_no_method_execption(exc, format, obj, argc, argv);
if (!(last_call_status & NOEX_MISSING)) {
th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
}
@@ -697,6 +702,7 @@ rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv)
static VALUE
send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
{
+ ID id;
VALUE vid;
VALUE self = RUBY_VM_PREVIOUS_CONTROL_FRAME(GET_THREAD()->cfp)->self;
rb_thread_t *th = GET_THREAD();
@@ -708,7 +714,16 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
vid = *argv++; argc--;
PASS_PASSED_BLOCK_TH(th);
- return rb_call0(recv, rb_to_id(vid), argc, argv, scope, self);
+ id = rb_check_id(&vid);
+ if (!id) {
+ if (rb_method_basic_definition_p(CLASS_OF(recv), idMethodMissing)) {
+ VALUE exc = make_no_method_execption(rb_eNoMethodError, NULL,
+ recv, ++argc, --argv);
+ rb_exc_raise(exc);
+ }
+ id = rb_to_id(vid);
+ }
+ return rb_call0(recv, id, argc, argv, scope, self);
}
/*
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 7f38ccf7c1..68eb3093b8 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -584,7 +584,17 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp,
}
sym = TOPN(i);
- id = SYMBOL_P(sym) ? SYM2ID(sym) : rb_to_id(sym);
+ if (SYMBOL_P(sym)) {
+ id = SYM2ID(sym);
+ }
+ else if (!(id = rb_check_id(&sym))) {
+ if (rb_method_basic_definition_p(CLASS_OF(recv), idMethodMissing)) {
+ VALUE exc = make_no_method_execption(rb_eNoMethodError, NULL, recv,
+ rb_long2int(num), &TOPN(i));
+ rb_exc_raise(exc);
+ }
+ id = rb_to_id(sym);
+ }
/* shift arguments */
if (i > 0) {
MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i);
diff --git a/vm_insnhelper.h b/vm_insnhelper.h
index 018adb6d5b..02c0cac1ac 100644
--- a/vm_insnhelper.h
+++ b/vm_insnhelper.h
@@ -226,4 +226,8 @@ static VALUE ruby_vm_global_state_version = 1;
} while (0)
static void vm_clear_all_cache(void);
+static VALUE make_no_method_execption(VALUE exc, const char *format,
+ VALUE obj, int argc, const VALUE *argv);
+
+
#endif /* RUBY_INSNHELPER_H */