From: Tanaka Akira Date: 2008-05-15T01:08:11+09:00 Subject: [ruby-dev:34707] Re: fork 不可能な環境での test_argv0_noarg In article <87ve1hmde3.fsf@fsij.org>, Tanaka Akira writes: > 実行すると NotImplementedError になり、respond_to? で調べる > と偽になる特殊なメソッドを導入するのはどうですかね。 > > undef みたいなかんじで。 実装するとこんな感じでしょうか。 個々のメソッドについてはとりあえず fork と File.lchmod だけ 対処してあります。 GNU/Linux には lchmod がないので、File.respond_to?(:lchmod) は false になり、File.lchmod を呼び出すと NotImplementedError になります。 % ./ruby -ve ' p File.respond_to?(:lchmod) File.lchmod("foo", 0777)' ruby 1.9.0 (2008-05-14 revision 16419) [i686-linux] false -e:3:in `lchmod': lchmod() function is unimplemented on this machine (NotImplementedError) from -e:3:in `
' Index: include/ruby/intern.h =================================================================== --- include/ruby/intern.h (revision 16419) +++ include/ruby/intern.h (working copy) @@ -252,6 +252,7 @@ int rb_method_boundp(VALUE, ID, int); VALUE rb_eval_cmd(VALUE, VALUE, int); int rb_obj_respond_to(VALUE, ID, int); int rb_respond_to(VALUE, ID); +void rb_define_notimplement_method_id(VALUE mod, ID id, int noex); void rb_interrupt(void); VALUE rb_apply(VALUE, ID, VALUE); void rb_backtrace(void); Index: include/ruby/node.h =================================================================== --- include/ruby/node.h (revision 16419) +++ include/ruby/node.h (working copy) @@ -498,6 +498,8 @@ NODE *rb_node_newnode(enum node_type,VAL NODE* rb_method_node(VALUE klass, ID id); int rb_node_arity(NODE* node); +int rb_notimplement_body_p(NODE*); + struct global_entry *rb_global_entry(ID); VALUE rb_gvar_get(struct global_entry *); VALUE rb_gvar_set(struct global_entry *, VALUE); Index: proc.c =================================================================== --- proc.c (revision 16419) +++ proc.c (working copy) @@ -935,6 +935,15 @@ method_owner(VALUE obj) return data->oclass; } +static VALUE +method_notimplemented_p(VALUE obj) +{ + struct METHOD *data; + + Data_Get_Struct(obj, struct METHOD, data); + return rb_notimplement_body_p(data->body) ? Qtrue : Qfalse; +} + /* * call-seq: * obj.method(sym) => method @@ -1763,6 +1772,7 @@ Init_Proc(void) rb_define_method(rb_cMethod, "name", method_name, 0); rb_define_method(rb_cMethod, "owner", method_owner, 0); rb_define_method(rb_cMethod, "unbind", method_unbind, 0); + rb_define_method(rb_cMethod, "notimplemented?", method_notimplemented_p, 0); rb_define_method(rb_mKernel, "method", rb_obj_method, 1); rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1); @@ -1780,6 +1790,7 @@ Init_Proc(void) rb_define_method(rb_cUnboundMethod, "name", method_name, 0); rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0); rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); + rb_define_method(rb_cUnboundMethod, "notimplemented?", method_notimplemented_p, 0); /* Module#*_method */ rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1); Index: class.c =================================================================== --- class.c (revision 16419) +++ class.c (working copy) @@ -759,25 +759,40 @@ rb_obj_singleton_methods(int argc, VALUE void rb_define_method_id(VALUE klass, ID name, VALUE (*func)(ANYARGS), int argc) { - rb_add_method(klass, name, NEW_CFUNC(func,argc), NOEX_PUBLIC); + if (func) + rb_add_method(klass, name, NEW_CFUNC(func,argc), NOEX_PUBLIC); + else + rb_define_notimplement_method_id(klass, name, NOEX_PUBLIC); } void rb_define_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) { - rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PUBLIC); + ID id = rb_intern(name); + if (func) + rb_add_method(klass, id, NEW_CFUNC(func, argc), NOEX_PUBLIC); + else + rb_define_notimplement_method_id(klass, id, NOEX_PUBLIC); } void rb_define_protected_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) { - rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PROTECTED); + ID id = rb_intern(name); + if (func) + rb_add_method(klass, id, NEW_CFUNC(func, argc), NOEX_PROTECTED); + else + rb_define_notimplement_method_id(klass, id, NOEX_PROTECTED); } void rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) { - rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PRIVATE); + ID id = rb_intern(name); + if (func) + rb_add_method(klass, id, NEW_CFUNC(func, argc), NOEX_PRIVATE); + else + rb_define_notimplement_method_id(klass, id, NOEX_PRIVATE); } void Index: process.c =================================================================== --- process.c (revision 16419) +++ process.c (working copy) @@ -2394,10 +2394,10 @@ rb_fork(int *status, int (*chfunc)(void* * fork doesn't copy other threads. */ +#if defined(HAVE_FORK) && !defined(__NetBSD__) static VALUE rb_f_fork(VALUE obj) { -#if defined(HAVE_FORK) && !defined(__NetBSD__) rb_pid_t pid; rb_secure(2); @@ -2423,11 +2423,10 @@ rb_f_fork(VALUE obj) default: return PIDT2NUM(pid); } +} #else - rb_notimplement(); +#define rb_f_fork 0 #endif -} - /* * call-seq: Index: eval_method.c =================================================================== --- eval_method.c (revision 16419) +++ eval_method.c (working copy) @@ -18,6 +18,8 @@ struct cache_entry { /* method hash tab static struct cache_entry cache[CACHE_SIZE]; static int ruby_running = 0; +static NODE *notimplement_body = 0; + void rb_clear_cache(void) { @@ -403,6 +405,12 @@ rb_export_method(VALUE klass, ID name, I } int +rb_notimplement_body_p(NODE *method) +{ + return method == notimplement_body ? Qtrue : Qfalse; +} + +int rb_method_boundp(VALUE klass, ID id, int ex) { NODE *method; @@ -411,6 +419,8 @@ rb_method_boundp(VALUE klass, ID id, int if (ex && (method->nd_noex & NOEX_PRIVATE)) { return Qfalse; } + if (rb_notimplement_body_p(method->nd_body)) + return Qfalse; return Qtrue; } return Qfalse; @@ -645,10 +655,49 @@ rb_mod_alias_method(VALUE mod, VALUE new return mod; } +static VALUE +notimplement_cfunc(VALUE obj) +{ + rb_notimplement(); +} + +void +rb_define_notimplement_method_id(VALUE mod, ID id, int noex) +{ + rb_add_method(mod, id, notimplement_body, noex); +} + +static VALUE +rb_mod_notimplemented_method(int argc, VALUE *argv, VALUE mod) +{ + int i; + if (ruby_cbase() == rb_cObject && mod == rb_cObject) { + rb_secure(4); + } + + for (i = 0; i < argc; i++) { + ID id = rb_to_id(argv[i]); + + if (rb_safe_level() >= 4 && !OBJ_TAINTED(mod)) { + rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'", + rb_id2name(id)); + } + rb_frozen_class_p(mod); + if (id == object_id || id == __send__ || id == init) { + rb_warn("notimplementing `%s' may cause serious problem", rb_id2name(id)); + } + rb_define_notimplement_method_id(mod, id, NOEX_PUBLIC); + } + return mod; +} + static void Init_eval_method(void) { rb_define_private_method(rb_cModule, "remove_method", rb_mod_remove_method, -1); rb_define_private_method(rb_cModule, "undef_method", rb_mod_undef_method, -1); rb_define_private_method(rb_cModule, "alias_method", rb_mod_alias_method, 2); + rb_global_variable(¬implement_body); + notimplement_body = NEW_CFUNC(notimplement_cfunc, -1); + rb_define_private_method(rb_cModule, "notimplemented_method", rb_mod_notimplemented_method, -1); } Index: file.c =================================================================== --- file.c (revision 16419) +++ file.c (working copy) @@ -1901,12 +1901,7 @@ rb_file_s_lchmod(int argc, VALUE *argv) return LONG2FIX(n); } #else -static VALUE -rb_file_s_lchmod(int argc, VALUE *argv) -{ - rb_notimplement(); - return Qnil; /* not reached */ -} +#define rb_file_s_lchmod 0 #endif struct chown_args { Index: test/ruby/test_notimp.rb =================================================================== --- test/ruby/test_notimp.rb (revision 0) +++ test/ruby/test_notimp.rb (revision 0) @@ -0,0 +1,33 @@ +require 'test/unit' + +class TestNotImplement < Test::Unit::TestCase + class C + notimplemented_method :n + def x() end + end + + def test_respond_to + assert_equal(false, C.new.respond_to?(:n)) + assert_equal(true, C.new.respond_to?(:x)) + end + + def test_call + assert_raise(NotImplementedError) { C.new.n } + assert_nothing_raised { C.new.x } + end + + def test_method + m = C.new.method(:n) + assert(m.notimplemented?) + m = C.new.method(:x) + assert(!m.notimplemented?) + end + + def test_umethod + m = C.instance_method(:n) + assert(m.notimplemented?) + m = C.instance_method(:x) + assert(!m.notimplemented?) + end + +end -- [田中 哲][たなか あきら][Tanaka Akira]