ruby-changes:9026
From: nobu <ko1@a...>
Date: Sat, 6 Dec 2008 10:58:55 +0900 (JST)
Subject: [ruby-changes:9026] Ruby:r20562 (mvm): * merged from trunk r20375:20561.
nobu 2008-12-06 10:57:53 +0900 (Sat, 06 Dec 2008) New Revision: 20562 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=20562 Log: * merged from trunk r20375:20561. Added files: branches/mvm/man/rake.1 branches/mvm/test/test_open3.rb Modified files: branches/mvm/.merged-trunk-revision branches/mvm/ChangeLog branches/mvm/bootstraptest/test_autoload.rb branches/mvm/compile.c branches/mvm/complex.c branches/mvm/cont.c branches/mvm/enumerator.c branches/mvm/ext/bigdecimal/bigdecimal.c branches/mvm/ext/curses/curses.c branches/mvm/ext/openssl/ossl_ssl.c branches/mvm/ext/pty/pty.c branches/mvm/ext/socket/socket.c branches/mvm/ext/stringio/stringio.c branches/mvm/ext/tk/lib/tk/menu.rb branches/mvm/ext/tk/lib/tk.rb branches/mvm/ext/tk/tcltklib.c branches/mvm/gc.c branches/mvm/include/ruby/intern.h branches/mvm/io.c branches/mvm/iseq.c branches/mvm/iseq.h branches/mvm/lib/forwardable.rb branches/mvm/lib/gserver.rb branches/mvm/lib/net/protocol.rb branches/mvm/lib/open3.rb branches/mvm/lib/rexml/xpath.rb branches/mvm/lib/rubygems/local_remote_options.rb branches/mvm/lib/rubygems/validator.rb branches/mvm/load.c branches/mvm/man/goruby.1 branches/mvm/man/irb.1 branches/mvm/numeric.c branches/mvm/pack.c branches/mvm/parse.y branches/mvm/proc.c branches/mvm/process.c branches/mvm/rational.c branches/mvm/regparse.c branches/mvm/signal.c branches/mvm/spec/default.mspec branches/mvm/strftime.c branches/mvm/string.c branches/mvm/test/bigdecimal/test_bigdecimal.rb branches/mvm/test/cgi/test_cgi_session.rb branches/mvm/test/openssl/test_ssl.rb branches/mvm/test/ruby/test_complex.rb branches/mvm/test/ruby/test_fiber.rb branches/mvm/test/ruby/test_float.rb branches/mvm/test/ruby/test_method.rb branches/mvm/test/ruby/test_proc.rb branches/mvm/test/ruby/test_process.rb branches/mvm/test/ruby/test_range.rb branches/mvm/test/ruby/test_rational.rb branches/mvm/test/ruby/test_regexp.rb branches/mvm/test/ruby/test_string.rb branches/mvm/test/socket/test_tcp.rb branches/mvm/thread.c branches/mvm/variable.c branches/mvm/version.h branches/mvm/vm.c branches/mvm/vm_method.c branches/mvm/win32/win32.c Index: mvm/complex.c =================================================================== --- mvm/complex.c (revision 20561) +++ mvm/complex.c (revision 20562) @@ -885,15 +885,17 @@ switch (TYPE(x)) { case T_FLOAT: #ifdef HAVE_SIGNBIT - return f_boolcast(signbit(RFLOAT_VALUE(x))); + { + double f = RFLOAT_VALUE(x); + return f_boolcast(!isnan(f) && signbit(f)); + } #else - { - char s[2]; + { + char s[2]; - (void)snprintf(s, sizeof s, "%.0f", RFLOAT_VALUE(x)); - - return f_boolcast(s[0] == '-'); - } + (void)snprintf(s, sizeof s, "%.0f", RFLOAT_VALUE(x)); + return f_boolcast(s[0] == '-'); + } #endif } return f_negative_p(x); @@ -906,7 +908,7 @@ } static VALUE -nucomp_to_s(VALUE self) +nucomp_format(VALUE self, VALUE (*func)(VALUE)) { VALUE s, impos; @@ -914,31 +916,32 @@ impos = f_tpositive_p(dat->imag); - s = f_to_s(dat->real); + s = (*func)(dat->real); rb_str_cat2(s, !impos ? "-" : "+"); - rb_str_concat(s, f_to_s(f_abs(dat->imag))); + rb_str_concat(s, (*func)(f_abs(dat->imag))); + if (!rb_isdigit(RSTRING_PTR(s)[RSTRING_LEN(s) - 1])) + rb_str_cat2(s, "*"); rb_str_cat2(s, "i"); return s; } static VALUE +nucomp_to_s(VALUE self) +{ + return nucomp_format(self, f_to_s); +} + +static VALUE nucomp_inspect(VALUE self) { - VALUE s, impos; + VALUE s; - get_dat1(self); - - impos = f_tpositive_p(dat->imag); - s = rb_str_new2("("); - rb_str_concat(s, f_inspect(dat->real)); - rb_str_cat2(s, !impos ? "-" : "+"); + rb_str_concat(s, nucomp_format(self, f_inspect)); + rb_str_cat2(s, ")"); - rb_str_concat(s, f_inspect(f_abs(dat->imag))); - rb_str_cat2(s, "i)"); - return s; } Index: mvm/regparse.c =================================================================== --- mvm/regparse.c (revision 20561) +++ mvm/regparse.c (revision 20562) @@ -2112,6 +2112,7 @@ return c; } +#if 0 /* no invalid quantifier */ static int is_invalid_quantifier_target(Node* node) { @@ -2143,6 +2144,9 @@ } return 0; } +#else +#define is_invalid_quantifier_target(node) 0 +#endif /* ?:0, *:1, +:2, ??:3, *?:4, +?:5 */ static int Index: mvm/include/ruby/intern.h =================================================================== --- mvm/include/ruby/intern.h (revision 20561) +++ mvm/include/ruby/intern.h (revision 20562) @@ -267,6 +267,7 @@ void rb_load_protect(VALUE, int, int*); NORETURN(void rb_jump_tag(int)); int rb_provided(const char*); +int rb_feature_provided(const char *, const char **); void rb_provide(const char*); VALUE rb_f_require(VALUE, VALUE); VALUE rb_require_safe(VALUE, int); Index: mvm/ChangeLog =================================================================== --- mvm/ChangeLog (revision 20561) +++ mvm/ChangeLog (revision 20562) @@ -1,3 +1,294 @@ +Fri Dec 5 21:45:45 2008 Tadayoshi Funaba <tadf@d...> + + * rational.c (nurat_{to_s,inspect}): performance improvement. + +Fri Dec 5 21:42:44 2008 Tadayoshi Funaba <tadf@d...> + + * complex.c: inpsect should not depend on to_s. + +Fri Dec 5 19:06:04 2008 Tanaka Akira <akr@f...> + + * lib/open3.rb (Open3.pipeline_start): new method. + (Open3.pipeline): ditto. + +Fri Dec 5 18:55:25 2008 Tanaka Akira <akr@f...> + + * process.c (run_exec_dup2): !save is false if Qnil. + +Fri Dec 5 18:07:32 2008 NAKAMURA Usaku <usa@r...> + + * win32/win32.c (rb_w32_read, rb_w32_write, rb_w32_isatty): check + whether fd is valid. + +Fri Dec 5 13:05:45 2008 Nobuyoshi Nakada <nobu@r...> + + * iseq.c (rb_iseq_parameters): proc arguments are always optional. + + * proc.c (get_proc_iseq, rb_proc_parameters): ditto. + +Fri Dec 5 12:38:48 2008 Nobuyoshi Nakada <nobu@r...> + + * compile.c (iseq_set_sequence): uses rb_compile_warning() for + warning at compilation time. + +Fri Dec 5 12:35:46 2008 Nobuyoshi Nakada <nobu@r...> + + * compile.c (ruby_iseq_compile, ruby_iseq_translate_threaded_code), + (ruby_insns_name_array, ruby_iseq_build_from_ary): prefixed with + ruby_. + + * iseq.c (ruby_iseq_load, ruby_insn_make_insn_table): ditto. + +Fri Dec 5 10:01:43 2008 Nobuyoshi Nakada <nobu@r...> + + * string.c (rb_str_cmp_m): fixed rdoc. pointed out by <Thomas + C. Mitchell AT gmail.com> at [ruby-talk:321967] + +Fri Dec 5 07:58:30 2008 Tanaka Akira <akr@f...> + + * io.c (io_binwrite): arg.offset should be updated after retry. + +Fri Dec 5 03:29:17 2008 Nobuyoshi Nakada <nobu@r...> + + * load.c (rb_get_load_path): returns the load path without + touching. + + * load.c (rb_feature_provided): new function to return the loading + path in addition to rb_provided(). + + * load.c (search_required): sets path if loading. + + * variable.c (autoload_provided): load paths are expanded to check + if loading. + + * variable.c (autoload_node): keeps autoload mark while loading. + [ruby-core:20235] + + * variable.c (rb_const_get_0): loops while autoload mark is set. + +Fri Dec 5 01:37:02 2008 NAKAMURA Usaku <usa@r...> + + * win32/win32.c (rb_w32_read): ERROR_BROKEN_PIPE is not a real error + at this point. + + * io.c (pipe_open): use rb_w32_spawn() instead of rb_w32_pipe_exec() + to use our own redirection scheme. + +Fri Dec 5 01:35:08 2008 Nobuyoshi Nakada <nobu@r...> + + * string.c (sym_to_proc): use hidden object. + +Fri Dec 5 01:19:21 2008 Yukihiro Matsumoto <matz@r...> + + * pack.c (pack_pack): propagate taint status from format string to + result string. + +Fri Dec 5 00:34:10 2008 NAKAMURA Usaku <usa@r...> + + * process.c (run_exec_dup2): need to sort by reverted order when + restoring fds. + +Fri Dec 5 00:17:18 2008 Nobuyoshi Nakada <nobu@r...> + + * string.c (sym_to_proc): caches Symbol procs, based on a patch from + Shumpei Akai <admin AT flexfrank.net>. [ruby-dev:37265] + +Thu Dec 4 23:29:34 2008 NAKAMURA Usaku <usa@r...> + + * win32/win32.c (waitpid): fix bug of checking child slot. + + * win32/win32.c (FindChildSlotByHandle): new. + +Thu Dec 4 23:24:05 2008 Tanaka Akira <akr@f...> + + * lib/open3.rb (Open3.poutput3): new method. + (Open3.poutput2): ditto. + (Open3.poutput2e): ditto. + +Thu Dec 4 23:02:13 2008 Yuki Sonoda (Yugui) <yugui@y...> + + * spec/default.mspec: follows changes in rubyspec project. + inherits configurations from ruby.1.9.mspec. + +Thu Dec 4 22:13:55 2008 Tadayoshi Funaba <tadf@d...> + + * test/ruby/test_complex.rb: added some tests. + + * test/ruby/test_rational.rb: ditto. + +Thu Dec 4 19:56:20 2008 Tanaka Akira <akr@f...> + + * lib/open3.rb (Open3.popen3): simplified. + (Open3.popen_run): extracted from Open3.popen3. + (Open3.popen2): new method. + (Open3.popen2e): new method. + (Open3.pipeline_rw): new method. + (Open3.pipeline_r): new method. + (Open3.pipeline_w): new method. + (Open3.pipeline_run): new private method. + +Thu Dec 4 19:16:28 2008 Tanaka Akira <akr@f...> + + * process.c (check_exec_fds): resolve cascaded child fd reference. + +Thu Dec 4 16:58:12 2008 Yukihiro Matsumoto <matz@r...> + + * lib/rubygems/validator.rb (Gem#remove_leading_dot_dir): make + this method private. a patch from okkez in [ruby-dev:37245] + +Thu Dec 4 16:19:18 2008 Yukihiro Matsumoto <matz@r...> + + * ext/openssl/ossl_ssl.c (ossl_ssl_read_nonblock): + OpenSSL::SSL::SSLSocket should implement read_nonblock. a patch + from Aaron Patterson in [ruby-core:20277]. fix: #814 [ruby-core:20241] + +Thu Dec 4 16:16:09 2008 Yukihiro Matsumoto <matz@r...> + + * lib/gserver.rb: fixed type in sample code. a report from Oleg + Puchinin. + +Thu Dec 4 14:54:32 2008 Yukihiro Matsumoto <matz@r...> + + * lib/rubygems/local_remote_options.rb (Gem#add_update_sources_option): + little documentation fix. a patch from okkez. [ruby-dev:37271] + +Thu Dec 4 13:56:31 2008 Yukihiro Matsumoto <matz@r...> + + * ext/curses/curses.c (window_getch): avoid ISPRINT() macro which + has an issue with OpenSolaris. [ruby-core:20189] + + * ext/curses/curses.c (curses_getch): no ISPRINT(). [ruby-core:20294] + + * signal.c (ruby_signal): EINVAL from sigaction(2) is not a bug. + +Thu Dec 4 11:40:56 2008 Akinori MUSHA <knu@i...> + + * enumerator.c (inspect_enumerator): Implement #inspect. + [ruby-dev:37248]-[ruby-dev:37263] + +Thu Dec 4 11:38:40 2008 Akinori MUSHA <knu@i...> + + * vm_method.c (rb_obj_respond_to): Remove a duplicated rdoc + comment and fix a markup error. + +Thu Dec 4 06:04:16 2008 Hidetoshi NAGAI <nagai@a...> + + * ext/tk/lib/tk/menu.rb: TkOptionMenubutton.new fails to treat + 'parent' and 'variable' options on a Hash argument. + +Thu Dec 4 05:06:47 2008 Hidetoshi NAGAI <nagai@a...> + + * ext/tk/lib/tk.rb: bug fix. use ::RubyVM instead of ::VM + [ruby-list:45676] + + * ext/tk/tcltklib.c: update RELEASE_DATE + +Thu Dec 4 01:37:47 2008 Tadayoshi Funaba <tadf@d...> + + * complex.c (nurat_{to_s,inspect}): provides better representation + for in-finite imag part. + +Thu Dec 4 01:22:41 2008 Tadayoshi Funaba <tadf@d...> + + * complex.c (f_signbit): NaN may be signed value. + +Wed Dec 3 23:59:32 2008 Tanaka Akira <akr@f...> + + * process.c (EXEC_OPTION_DUP2_CHILD): defined. + (check_exec_redirect_fd): check :in, :out and :err. + (check_exec_redirect): check [:child, fd]. + (check_exec_fds): validate EXEC_OPTION_DUP2_CHILD array. + (run_exec_dup2_child): new function. + (rb_run_exec_options): call run_exec_dup2_child. + +Wed Dec 3 22:54:39 2008 Nobuyoshi Nakada <nobu@r...> + + * parse.y (expr): keyword_not can continue across newline. + [ruby-core:20252] + +Wed Dec 3 22:40:59 2008 Nobuyoshi Nakada <nobu@r...> + + * iseq.c (simple_default_value): returns simplest assignment only. + [ruby-core:20237] + +Wed Dec 3 21:30:06 2008 Tanaka Akira <akr@f...> + + * process.c (check_exec_redirect): accept :in, :out, :err as redirect + target. + +Wed Dec 3 21:18:27 2008 Tadayoshi Funaba <tadf@d...> + + * test/ruby/test_rational.rb: revert. + +Wed Dec 3 14:48:52 2008 Nobuyoshi Nakada <nobu@r...> + + * ext/tk/tcltklib.c (ip_ruby_cmd, ip_invoke_with_position): must + not access internal union directly. [ruby-list:45670] + +Wed Dec 3 12:24:08 2008 Nobuyoshi Nakada <nobu@r...> + + * io.c (rb_io_getc, rb_io_readchar): documentation correction from + Emiel van de Laar. [ruby-core:20212] + + * ext/stringio/stringio.c (strio_readchar): ditto. + +Wed Dec 3 09:26:29 2008 Yukihiro Matsumoto <matz@r...> + + * lib/rexml/xpath.rb (REXML::XPath.first): apply documentation + patch from Ken Bloom in [ruby-core:20213]. + + * lib/rexml/xpath.rb (REXML::XPath.each): ditto. + +Wed Dec 3 02:56:34 2008 Yusuke Endoh <mame@t...> + + * test/ruby/test_rational.rb: add a test. + +Wed Dec 3 02:53:24 2008 Yusuke Endoh <mame@t...> + + * test/ruby/test_range.rb: add a test. + +Wed Dec 3 02:26:07 2008 Yusuke Endoh <mame@t...> + + * test/ruby/test_string.rb: add some tests. + +Wed Dec 3 02:04:21 2008 Yusuke Endoh <mame@t...> + + * ext/pty/pty.c (Init_pty): fix typo. + +Tue Dec 2 19:22:13 2008 Tanaka Akira <akr@f...> + + * lib/open3.rb (Open3.popen3): merge hash options if given. + +Tue Dec 2 15:31:42 2008 Yukihiro Matsumoto <matz@r...> + + * lib/net/protocol.rb (Net::BufferedIO#rbuf_fill): use + read_nonblock instead of sysread wrapped by timeout to boost + performance. a patch from Aaron Patterson in [ruby-core:20191]. + fix #806 + +Mon Dec 1 23:23:52 2008 Yuki Sonoda (Yugui) <yugui@y...> + + * set 1.9.1-p5000 into version number. [ruby-dev:36998] + +Mon Dec 1 15:48:47 2008 NAKAMURA Usaku <usa@r...> + + * signal.c (register_sigaltstack): no need to define on non-sigaltstack + platform. + +Mon Dec 1 12:00:45 2008 Nobuyoshi Nakada <nobu@r...> + + * cont.c (rb_fiber_start): calls with exact argument number. + [ruby-core:20088] + +Sun Nov 30 21:41:10 2008 Yuki Sonoda (Yugui) <yugui@y...> + + * man/rake.1: new manual page + +Sun Nov 30 18:01:50 2008 Yuki Sonoda (Yugui) <yugui@y...> + + * test/ruby/test_regexp.rb (TestRegexp#test_parse_curly_brace): + now accepts quantifier on anchrs agian by r20391. + Sun Nov 30 15:26:44 2008 Nobuyoshi Nakada <nobu@r...> * inits.c (rb_vm_call_inits): let all InitVM functions use @@ -3,4 +294,67 @@ ruby_vm_t instead of rb_vm_t. +Sat Nov 29 23:56:44 2008 Yuki Sonoda (Yugui) <yugui@y...> + + * man/irb.1 (EXAMPLES): new section + +Sat Nov 29 19:19:32 2008 Yukihiro Matsumoto <matz@r...> + + * regparse.c (is_invalid_quantifier_target): Perl and old Ruby + accepts quantifier on anchors. [ruby-core:20161] + +Sat Nov 29 18:28:57 2008 Yukihiro Matsumoto <matz@r...> + + * ext/socket/socket.c (sock_getaddrinfo): should have updated for + Mac OS X. a patch from Shumpei Akai in [ruby-dev:37234] + +Sat Nov 29 00:18:30 2008 Yukihiro Matsumoto <matz@r...> + + * cont.c (fiber_alloc): separate allocation and initialization. + allow subclass to override #initialize. [ruby-core:20086] + +Fri Nov 28 18:31:21 2008 Yukihiro Matsumoto <matz@r...> + + * ext/socket/socket.c (sock_s_getaddrinfo): refactored to remove + code duplication regarding getaddrinfo. + +Fri Nov 28 17:52:26 2008 Keiju Ishitsuka <keiju@r...> + + * lib/forwardable.rb: should be usable def_single_delegator for + Class and Module. + +Fri Nov 28 13:19:34 2008 Nobuyoshi Nakada <nobu@r...> + + * iseq.c (simple_default_value): extracts simplest default + argument value. + + * iseq.c (rb_iseq_parameters): returns parameter list. + + * proc.c (get_proc_iseq, get_method_iseq): handles ifunc and + bmethod. + + * proc.c (rb_proc_parameters, rb_method_parameters): added + Proc#parameters and Method#parameters. [ruby-core:19759] + +Fri Nov 28 02:18:47 2008 Yukihiro Matsumoto <matz@r...> + + * ext/bigdecimal/bigdecimal.c (BigDecimal_DoDivmod): bigdecimal + division (including modulo) should raise ZeroDivisionError as + integer division. [incompatible] + +Fri Nov 28 00:12:00 2008 Yukihiro Matsumoto <matz@r...> + + * numeric.c (flodivmod): floating point division should raise + ZeroDivisionError as integer division. [incompatible] + +Thu Nov 27 23:54:37 2008 Yukihiro Matsumoto <matz@r...> + + * gc.c (gc_mark): still needs to check stack depth during GC. + + * gc.c (stack_check): ditto. + +Thu Nov 27 21:41:29 2008 Tadayoshi Funaba <tadf@d...> + + * strftime.c (rb_strftime): should add padding for %%. + Thu Nov 27 17:06:46 2008 Nobuyoshi Nakada <nobu@r...> Index: mvm/enumerator.c =================================================================== --- mvm/enumerator.c (revision 20561) +++ mvm/enumerator.c (revision 20562) @@ -542,7 +542,69 @@ return obj; } +static VALUE +inspect_enumerator(VALUE obj, VALUE dummy, int recur) +{ + struct enumerator *e = enumerator_ptr(obj); + const char *cname = rb_obj_classname(obj); + VALUE eobj, str; + int tainted, untrusted; + + if (recur) { + str = rb_sprintf("#<%s: ...>", cname); + OBJ_TAINT(str); + return str; + } + + eobj = e->obj; + + tainted = OBJ_TAINTED(eobj); + untrusted = OBJ_UNTRUSTED(eobj); + + /* (1..100).each_cons(2) => "#<Enumerator: 1..100:each_cons(2)>" */ + str = rb_sprintf("#<%s: ", cname); + rb_str_concat(str, rb_inspect(eobj)); + rb_str_buf_cat2(str, ":"); + rb_str_buf_cat2(str, rb_id2name(e->meth)); + + if (e->args) { + int argc = RARRAY_LEN(e->args); + VALUE *argv = RARRAY_PTR(e->args); + + rb_str_buf_cat2(str, "("); + + while (argc--) { + VALUE arg = *argv++; + + rb_str_concat(str, rb_inspect(arg)); + rb_str_buf_cat2(str, argc > 0 ? ", " : ")"); + + if (OBJ_TAINTED(arg)) tainted = Qtrue; + if (OBJ_UNTRUSTED(arg)) untrusted = Qtrue; + } + } + + rb_str_buf_cat2(str, ">"); + + if (tainted) OBJ_TAINT(str); + if (untrusted) OBJ_UNTRUST(str); + return str; +} + /* + * call-seq: + * e.inspect => string + * + * Create a printable version of <i>e</i>. + */ + +static VALUE +enumerator_inspect(VALUE obj) +{ + return rb_exec_recursive(inspect_enumerator, obj, 0); +} + +/* * Yielder */ static void @@ -782,6 +844,7 @@ rb_define_method(rb_cEnumerator, "with_object", enumerator_with_object, 1); rb_define_method(rb_cEnumerator, "next", enumerator_next, 0); rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0); + rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0); rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError); Index: mvm/variable.c =================================================================== --- mvm/variable.c (revision 20561) +++ mvm/variable.c (revision 20562) @@ -1488,57 +1488,76 @@ return (NODE *)load; } -VALUE -rb_autoload_load(VALUE klass, ID id) +static VALUE +autoload_provided(VALUE arg) { - VALUE file; - NODE *load = autoload_delete(klass, id); - - if (!load || !(file = load->nd_lit)) { - return Qfalse; - } - return rb_require_safe(file, load->nd_nth); + const char **p = (const char **)arg; + return rb_feature_provided(*p, p); } static VALUE -autoload_file(VALUE mod, ID id) +reset_safe(VALUE safe) { + rb_set_safe_level_force((int)safe); + return safe; +} + +static NODE * +autoload_node(VALUE mod, ID id, int noload) +{ VALUE file; struct st_table *tbl; - st_data_t val, load, n = id; + st_data_t val; + NODE *load; + const char *loading; + int safe; if (!st_lookup(RCLASS_IV_TBL(mod), autoload, &val) || - !(tbl = check_autoload_table((VALUE)val)) || !st_lookup(tbl, n, &load)) { - return Qnil; + !(tbl = check_autoload_table((VALUE)val)) || !st_lookup(tbl, (st_data_t)id, &val)) { + return 0; } - file = ((NODE *)load)->nd_lit; + load = (NODE *)val; + file = load->nd_lit; Check_Type(file, T_STRING); if (!RSTRING_PTR(file) || !*RSTRING_PTR(file)) { rb_raise(rb_eArgError, "empty file name"); } - if (!rb_provided(RSTRING_PTR(file))) { - return file; + loading = RSTRING_PTR(file); + safe = rb_safe_level(); + rb_set_safe_level_force(0); + if (!rb_ensure(autoload_provided, (VALUE)&loading, reset_safe, (VALUE)safe)) { + return load; } - - /* already loaded but not defined */ - st_delete(tbl, &n, 0); - if (!tbl->num_entries) { - n = autoload; - st_delete(RCLASS_IV_TBL(mod), &n, &val); + if (!noload && loading) { + return load; } - return Qnil; + return 0; } VALUE +rb_autoload_load(VALUE klass, ID id) +{ + VALUE file; + NODE *load = autoload_node(klass, id, 0); + + if (!load) return Qfalse; + file = load->nd_lit; + return rb_require_safe(file, load->nd_nth); +} + +VALUE rb_autoload_p(VALUE mod, ID id) { struct st_table *tbl = RCLASS_IV_TBL(mod); - VALUE val; + st_data_t val; + NODE *load; + VALUE file; if (!tbl || !st_lookup(tbl, id, &val) || val != Qundef) { return Qnil; } - return autoload_file(mod, id); + load = autoload_node(mod, id, 0); + return load && (file = load->nd_lit) ? file : Qnil; } static VALUE @@ -1552,7 +1571,7 @@ while (RTEST(tmp)) { while (RCLASS_IV_TBL(tmp) && st_lookup(RCLASS_IV_TBL(tmp),id,&value)) { if (value == Qundef) { - if (!RTEST(rb_autoload_load(tmp, id))) break; + rb_autoload_load(tmp, id); continue; } if (exclude && tmp == rb_cObject && klass != rb_cObject) { @@ -1737,7 +1756,7 @@ retry: while (tmp) { if (RCLASS_IV_TBL(tmp) && st_lookup(RCLASS_IV_TBL(tmp), id, &value)) { - if (value == Qundef && NIL_P(autoload_file(klass, id))) + if (value == Qundef && !autoload_node(klass, id, 1)) return Qfalse; return Qtrue; } @@ -1776,7 +1795,7 @@ const char *dest = isconst ? "constant" : "class variable"; if (!OBJ_UNTRUSTED(klass) && rb_safe_level() >= 4) - rb_raise(rb_eSecurityError, "Insecure: can't set %s", dest); + rb_raise(rb_eSecurityError, "Insecure: can't set %s", dest); if (OBJ_FROZEN(klass)) { if (BUILTIN_TYPE(klass) == T_MODULE) { rb_error_frozen("module"); @@ -1799,7 +1818,7 @@ } } - if(isconst){ + if (isconst){ rb_vm_change_state(); } st_insert(RCLASS_IV_TBL(klass), id, val); Index: mvm/bootstraptest/test_autoload.rb =================================================================== --- mvm/bootstraptest/test_autoload.rb (revision 20561) +++ mvm/bootstraptest/test_autoload.rb (revision 20562) @@ -50,3 +50,12 @@ module M; end Thread.new{eval('$SAFE=4; ZZZ.new.hoge')}.value } + +assert_equal 'okok', %q{ + open("zzz.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"} + autoload :ZZZ, "./zzz.rb" + t1 = Thread.new {ZZZ.ok} + t2 = Thread.new {ZZZ.ok} + [t1.value, t2.value].join +} + Index: mvm/iseq.c =================================================================== --- mvm/iseq.c (revision 20561) +++ mvm/iseq.c (revision 20562) @@ -19,10 +19,6 @@ #include "insns.inc" #include "insns_info.inc" -/* compile.c */ -void iseq_compile(VALUE self, NODE *node); -int iseq_translate_threaded_code(rb_iseq_t *iseq); - static void compile_data_free(struct iseq_compile_data *compile_data) { @@ -324,7 +320,7 @@ iseq->self = self; prepare_iseq_build(iseq, name, filename, parent, type, bopt, option); - iseq_compile(self, node); + ruby_iseq_compile(self, node); cleanup_iseq_build(iseq); return self; } @@ -346,17 +342,14 @@ bopt, &COMPILE_OPTION_DEFAULT); } -VALUE iseq_build_from_ary(rb_iseq_t *iseq, VALUE locals, VALUE args, - VALUE exception, VALUE body); - #define CHECK_ARRAY(v) rb_convert_type(v, T_ARRAY, "Array", "to_ary") #define CHECK_STRING(v) rb_convert_type(v, T_STRING, "String", "to_str") #define CHECK_SYMBOL(v) rb_convert_type(v, T_SYMBOL, "Symbol", "to_sym") static inline VALUE CHECK_INTEGER(VALUE v) {NUM2LONG(v); return v;} -VALUE +static VALUE iseq_load(VALUE self, VALUE data, VALUE parent, VALUE opt) { - VALUE iseqval = iseq_alloc(rb_cISeq); + VALUE iseqval = iseq_alloc(self); VALUE magic, version1, version2, format_type, misc; VALUE name, filename; @@ -426,7 +419,7 @@ prepare_iseq_build(iseq, name, filename, parent, iseq_type, 0, &option); - iseq_build_from_ary(iseq, locals, args, exception, body); + ruby_iseq_build_from_ary(iseq, locals, args, exception, body); cleanup_iseq_build(iseq); return iseqval; @@ -441,6 +434,12 @@ return iseq_load(self, data, 0, opt); } +VALUE +ruby_iseq_load(VALUE data, VALUE parent, VALUE opt) +{ + return iseq_load(rb_cISeq, data, parent, opt); +} + static NODE * compile_string(VALUE str, VALUE file, VALUE line) { @@ -1235,7 +1234,7 @@ } struct st_table * -insn_make_insn_table(void) +ruby_insn_make_insn_table(void) { struct st_table *table; int i; @@ -1272,6 +1271,103 @@ return newiseq; } +static VALUE +simple_default_value(const VALUE *seq, const VALUE *eseq) +{ + VALUE val; + + again: + switch (*seq++) { + case BIN(trace): + if (++seq >= eseq) return Qundef; + goto again; + case BIN(putnil): + val = Qnil; + goto got; + case BIN(putobject): + val = *seq++; + got: + switch (*seq++) { + case BIN(setlocal): + if ((seq+=1) == eseq) return val; + break; + case BIN(setdynamic): + if ((seq+=2) == eseq) return val; + break; + } + default: + return Qundef; + } +} + +VALUE +rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc) +{ + int i, r, s; + VALUE a, args = rb_ary_new2(iseq->arg_size); + ID req, opt, rest, block; +#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type)) +#define PARAM_ID(i) iseq->local_table[i] +#define PARAM(i, type) ( \ + PARAM_TYPE(type), \ + rb_id2name(PARAM_ID(i)) ? \ + rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \ + a) + + CONST_ID(req, "req"); + CONST_ID(opt, "opt"); + if (is_proc) { + for (i = 0; i < iseq->argc; i++) { + PARAM_TYPE(opt); + rb_ary_push(a, rb_id2name(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil); + rb_ary_push(a, Qnil); + rb_ary_push(args, a); + } + } + else { + for (i = 0; i < iseq->argc; i++) { + rb_ary_push(args, PARAM(i, req)); + } + } + r = iseq->arg_rest != -1 ? iseq->arg_rest : + iseq->arg_post_len > 0 ? iseq->arg_post_start : + iseq->arg_block != -1 ? iseq->arg_block : + iseq->arg_size; + for (s = i; i < r; i++) { + PARAM_TYPE(opt); + if (rb_id2name(PARAM_ID(i))) { + VALUE defval = simple_default_value(iseq->iseq + iseq->arg_opt_table[i-s], + iseq->iseq + iseq->arg_opt_table[i-s+1]); + rb_ary_push(a, ID2SYM(PARAM_ID(i))); + if (defval != Qundef) rb_ary_push(a, defval); + } + rb_ary_push(args, a); + } + if (iseq->arg_rest != -1) { + CONST_ID(rest, "rest"); + rb_ary_push(args, PARAM(iseq->arg_rest, rest)); + } + r = iseq->arg_post_start + iseq->arg_post_len; + if (is_proc) { + for (i = iseq->arg_post_start; i < r; i++) { + PARAM_TYPE(opt); + rb_ary_push(a, rb_id2name(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil); + rb_ary_push(a, Qnil); + rb_ary_push(args, a); + } + } + else { + for (i = iseq->arg_post_start; i < r; i++) { + rb_ary_push(args, PARAM(i, req)); + } + } + if (iseq->arg_block != -1) { + CONST_ID(block, "block"); + rb_ary_push(args, PARAM(iseq->arg_block, block)); + } + return args; +} + /* ruby2cext */ VALUE @@ -1304,7 +1400,7 @@ iseq->iseq[i+1] = (VALUE)func; } - iseq_translate_threaded_code(iseq); + ruby_iseq_translate_threaded_code(iseq); #define ALLOC_AND_COPY(dst, src, type, size) do { \ if (size) { \ Index: mvm/string.c =================================================================== --- mvm/string.c (revision 20561) +++ mvm/string.c (revision 20562) @@ -2173,8 +2173,8 @@ * call-seq: * str <=> other_str => -1, 0, +1 * - * Comparison---Returns -1 if <i>other_str</i> is less than, 0 if - * <i>other_str</i> is equal to, and +1 if <i>other_str</i> is greater than + * Comparison---Returns -1 if <i>other_str</i> is greater than, 0 if + * <i>other_str</i> is equal to, and +1 if <i>other_str</i> is less than * <i>str</i>. If the strings are of different lengths, and the strings are * equal when compared up to the shortest length, then the longer string is * considered greater than the shorter one. In older versions of Ruby, setting @@ -6939,10 +6939,36 @@ static VALUE sym_to_proc(VALUE sym) { - return rb_proc_new(sym_call, (VALUE)SYM2ID(sym)); + static int sym_proc_cache_index = -1; + enum {SYM_PROC_CACHE_SIZE = 67}; + VALUE *cache_ptr, sym_proc_cache, proc; + long id, index; + VALUE *aryp; + + if (sym_proc_cache_index < 0) { + sym_proc_cache_index = rb_vm_key_create(); /* needs mutex/once */ + } + sym_proc_cache = *(cache_ptr = rb_vm_specific_ptr(sym_proc_cache_index)); + if (NIL_P(sym_proc_cache)) { + sym_proc_cache = *cache_ptr = rb_ary_tmp_new(SYM_PROC_CACHE_SIZE * 2); + rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil); + } + + id = SYM2ID(sym); + index = (id % SYM_PROC_CACHE_SIZE) << 1; + + aryp = RARRAY_PTR(sym_proc_cache); + if (aryp[index] == sym) { + return aryp[index + 1]; + } + else { + proc = rb_proc_new(sym_call, (VALUE)id); + aryp[index] = sym; + aryp[index + 1] = proc; + return proc; + } } - static VALUE sym_succ(VALUE sym) { Index: mvm/iseq.h =================================================================== --- mvm/iseq.h (revision 20561) +++ mvm/iseq.h (revision 20562) @@ -12,8 +12,17 @@ #ifndef RUBY_COMPILE_H #define RUBY_COMPILE_H -VALUE iseq_load(VALUE self, VALUE data, VALUE parent, VALUE opt); +/* compile.c */ +VALUE ruby_iseq_compile(VALUE self, NODE *node); +int ruby_iseq_translate_threaded_code(rb_iseq_t *iseq); +VALUE ruby_insns_name_array(void); +VALUE ruby_iseq_build_from_ary(rb_iseq_t *iseq, VALUE locals, VALUE args, + VALUE exception, VALUE body); +/* iseq.c */ +VALUE ruby_iseq_load(VALUE data, VALUE parent, VALUE opt); +struct st_table *ruby_insn_make_insn_table(void); + #define ISEQ_TYPE_TOP INT2FIX(1) #define ISEQ_TYPE_METHOD INT2FIX(2) #define ISEQ_TYPE_BLOCK INT2FIX(3) Index: mvm/io.c =================================================================== --- mvm/io.c (revision 20561) +++ mvm/io.c (revision 20562) @@ -797,8 +797,8 @@ } arg.fptr = fptr; arg.str = str; + retry: arg.offset = offset; - retry: arg.length = n; if (fptr->write_lock) { r = rb_mutex_synchronize(fptr->write_lock, io_binwrite_string, (VALUE)&arg); @@ -1316,7 +1316,7 @@ * ios.pid => fixnum * * Returns the process ID of a child process associated with - * <em>ios</em>. This will be set by <code>IO::popen</code>. + * <em>ios</em>. This will be set by <code>IO.popen</code>. * * pipe = IO.popen("-") * if pipe @@ -2693,14 +2693,14 @@ /* * call-seq: - * ios.getc => fixnum or nil + * ios.getc => string or nil * * Reads a one-character string from <em>ios</em>. Returns * <code>nil</code> if called at end of file. * * f = File.new("testfile") - * f.getc #=> "8" - * f.getc #=> "1" + * f.getc #=> "h" + * f.getc #=> "e" */ static VALUE @@ -2725,8 +2725,8 @@ * <code>EOFError</code> on end of file. * * f = File.new("testfile") - * f.readchar #=> "8" - * f.readchar #=> "1" + * f.readchar #=> "h" + * f.readchar #=> "e" */ static VALUE @@ -4535,6 +4535,7 @@ const char *exename = NULL; volatile VALUE cmdbuf; struct rb_exec_arg sarg; + int pair[2], write_pair[2]; #endif FILE *fp = 0; int fd = -1; @@ -4659,11 +4660,42 @@ cmd = rb_w32_join_argv(RSTRING_PTR(cmdbuf), args); rb_str_resize(argbuf, 0); } + switch (fmode & (FMODE_READABLE|FMODE_WRITABLE)) { + case FMODE_READABLE|FMODE_WRITABLE: + if (rb_pipe(write_pair) < 0) + rb_sys_fail(cmd); + if (rb_pipe(pair) < 0) { + int e = errno; + close(write_pair[0]); + close(write_pair[1]); + errno = e; + rb_sys_fail(cmd); + } + if (eargp) { + rb_exec_arg_addopt(eargp, INT2FIX(0), INT2FIX(write_pair[0])); + rb_exec_arg_addopt(eargp, INT2FIX(1), INT2FIX(pair[1])); + } + break; + case FMODE_READABLE: + if (rb_pipe(pair) < 0) + rb_sys_fail(cmd); + if (eargp) + rb_exec_arg_addopt(eargp, INT2FIX(1), INT2FIX(pair[1])); + break; + case FMODE_WRITABLE: + if (rb_pipe(pair) < 0) + rb_sys_fail(cmd); + if (eargp) + rb_exec_arg_addopt(eargp, INT2FIX(0), INT2FIX(pair[0])); + break; + default: + rb_sys_fail(cmd); + } if (eargp) { rb_exec_arg_fixup(eargp); rb_run_exec_options(eargp, &sarg); } - while ((pid = rb_w32_pipe_exec(cmd, exename, openmode, &fd, &write_fd)) == -1) { + while ((pid = rb_w32_spawn(P_NOWAIT, cmd, exename)) == -1) { /* exec failed */ switch (errno) { case EAGAIN: @@ -4681,6 +4713,20 @@ } if (eargp) rb_run_exec_options(&sarg, NULL); + if ((fmode & FMODE_READABLE) && (fmode & FMODE_WRITABLE)) { + close(pair[1]); + fd = pair[0]; + close(write_pair[0]); + write_fd = write_pair[1]; + } + else if (fmode & FMODE_READABLE) { + close(pair[1]); + fd = pair[0]; + } + else { + close(pair[0]); + fd = pair[1]; + } #else if (argc) { prog = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" ")); @@ -4774,23 +4820,46 @@ * * Runs the specified command as a subprocess; the subprocess's * standard input and output will be connected to the returned - * <code>IO</code> object. If _cmd_ is a +String+ - * ``<code>-</code>'', then a new instance of Ruby is started as the - * subprocess. If <i>cmd</i> is an +Array+ of +String+, then it will - * be used as the subprocess's +argv+ bypassing a shell. + * <code>IO</code> object. + * + * _cmd_ is a string or an array as follows. + * + * cmd: + * "-" : fork + * commandline : command line string which is passed to a shell + * [env, cmdname, arg1, ..., opts] : command name and arguments (no shell) + * [env, [cmdname, argv0], arg1, ..., opts] : command name and arguments including argv[0] (no shell) + * (env and opts are optional.) + * + * If _cmd_ is a +String+ ``<code>-</code>'', + * then a new instance of Ruby is started as the subprocess. + * + * If <i>cmd</i> is an +Array+ of +String+, + * then it will be used as the subprocess's +argv+ bypassing a shell. * The array can contains a hash at first for environments and - * a hash at last for options similar to <code>spawn</code>. The default - * mode for the new file object is ``r'', but <i>mode</i> may be set - * to any of the modes listed in the description for class IO. + * a hash at last for options similar to <code>spawn</code>. * - * Raises exceptions which <code>IO::pipe</code> and - * <code>Kernel::system</code> raise. + * The default mode for the new file object is ``r'', + * but <i>mode</i> may be set to any of the modes listed in the description for class IO. + * The last argument <i>opt</i> qualifies <i>mode</i>. * + * # set IO encoding + * nkf_io = IO.popen("nkf -e filename", :external_encoding=>"EUC-JP") + * euc_jp_string = nkf_io.read + * + * # merge standard output and standard error using + * # spawn option. See the document of Kernel.spawn. + * ls_io = IO.popen(["ls", "/", STDERR=>[:child, STDOUT]]) + * ls_result_with_error = ls_io.read + * + * Raises exceptions which <code>IO.pipe</code> and + * <code>Kernel.spawn</code> raise. + * * If a block is given, Ruby will run the command as a child connected * to Ruby with a pipe. Ruby's end of the pipe will be passed as a * parameter to the block. * At the end of block, Ruby close the pipe and sets <code>$?</code>. - * In this case <code>IO::popen</code> returns + * In this case <code>IO.popen</code> returns * the value of the block. * * If a block is given with a _cmd_ of ``<code>-</code>'', @@ -4938,10 +5007,10 @@ * IO.open(fd, mode_string="r" [, opt] ) {|io| block } => obj * * With no associated block, <code>open</code> is a synonym for - * <code>IO::new</code>. If the optional code block is given, it will + * <code>IO.new</code>. If the optional code block is given, it will * be passed <i>io</i> as an argument, and the IO object will * automatically be closed when the block terminates. In this instance, - * <code>IO::open</code> returns the value of the block. + * <code>IO.open</code> returns the value of the block. * */ @@ -5825,7 +5894,7 @@ * Returns a new <code>IO</code> object (a stream) for the given * <code>IO</code> object or integer file descriptor and mode * string. See also <code>IO#fileno</code> and - * <code>IO::for_fd</code>. + * <code>IO.for_fd</code>. * * puts IO.new($stdout).fileno # => 1 * @@ -5920,7 +5989,7 @@ * * Returns a new <code>IO</code> object (a stream) for the given * integer file descriptor and mode string. See also - * <code>IO#fileno</code> and <code>IO::for_fd</code>. + * <code>IO#fileno</code> and <code>IO.for_fd</code>. * * a = IO.new(2,"w") # '2' is standard error * $stderr.puts "Hello" @@ -5949,7 +6018,7 @@ * call-seq: * IO.for_fd(fd, mode [, opt]) => io * - * Synonym for <code>IO::new</code>. + * Synonym for <code>IO.new</code>. * */ Index: mvm/pack.c =================================================================== --- mvm/pack.c (revision 20561) +++ mvm/pack.c (revision 20562) @@ -1018,6 +1018,7 @@ if (associates) { rb_str_associate(res, associates); } + OBJ_INFECT(res, fmt); return res; } Index: mvm/load.c =================================================================== --- mvm/load.c (revision 20561) +++ mvm/load.c (revision 20562) @@ -30,13 +30,7 @@ rb_get_load_path(void) { VALUE load_path = GET_VM()->load_path; - VALUE ary = rb_ary_new2(RARRAY_LEN(load_path)); - long i; - - for (i = 0; i < RARRAY_LEN(load_path); ++i) { - rb_ary_push(ary, rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil)); - } - return ary; + return load_path; } static VALUE @@ -198,6 +192,12 @@ int rb_provided(const char *feature) { + return rb_feature_provided(feature, 0); +} + +int +rb_feature_provided(const char *feature, const char **loading) +{ const char *ext = strrchr(feature, '.'); volatile VALUE fullpath = 0; @@ -208,15 +208,15 @@ } if (ext && !strchr(ext, '/')) { if (IS_RBEXT(ext)) { - if (rb_feature_p(feature, ext, Qtrue, Qfalse, 0)) return Qtrue; + if (rb_feature_p(feature, ext, Qtrue, Qfalse, loading)) return Qtrue; return Qfalse; } else if (IS_SOEXT(ext) || IS_DLEXT(ext)) { - if (rb_feature_p(feature, ext, Qfalse, Qfalse, 0)) return Qtrue; + if (rb_feature_p(feature, ext, Qfalse, Qfalse, loading)) return Qtrue; return Qfalse; } } - if (rb_feature_p(feature, feature + strlen(feature), Qtrue, Qfalse, 0)) + if (rb_feature_p(feature, feature + strlen(feature), Qtrue, Qfalse, loading)) return Qtrue; return Qfalse; } @@ -430,9 +430,8 @@ return 'r'; } if ((tmp = rb_find_file(fname)) != 0) { - tmp = rb_file_expand_path(tmp, Qnil); ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (!rb_feature_p(ftptr, ext, Qtrue, Qtrue, 0)) + if (!rb_feature_p(ftptr, ext, Qtrue, Qtrue, &loading) || loading) *path = tmp; return 'r'; } @@ -447,9 +446,8 @@ #ifdef DLEXT2 OBJ_FREEZE(tmp); if (rb_find_file_ext(&tmp, loadable_ext + 1)) { - tmp = rb_file_expand_path(tmp, Qnil); ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (!rb_feature_p(ftptr, ext, Qfalse, Qtrue, 0)) + if (!rb_feature_p(ftptr, ext, Qfalse, Qtrue, &loading) || loading) *path = tmp; return 's'; } @@ -457,9 +455,8 @@ rb_str_cat2(tmp, DLEXT); OBJ_FREEZE(tmp); if ((tmp = rb_find_file(tmp)) != 0) { - tmp = rb_file_expand_path(tmp, Qnil); ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (!rb_feature_p(ftptr, ext, Qfalse, Qtrue, 0)) + if (!rb_feature_p(ftptr, ext, Qfalse, Qtrue, &loading) || loading) *path = tmp; return 's'; } @@ -471,9 +468,8 @@ return 's'; } if ((tmp = rb_find_file(fname)) != 0) { - tmp = rb_file_expand_path(tmp, Qnil); ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); - if (!rb_feature_p(ftptr, ext, Qfalse, Qtrue, 0)) + if (!rb_feature_p(ftptr, ext, Qfalse, Qtrue, &loading) || loading) *path = tmp; return 's'; } Index: mvm/lib/gserver.rb =================================================================== --- mvm/lib/gserver.rb (revision 20561) +++ mvm/lib/gserver.rb (revision 20562) @@ -40,7 +40,7 @@ # super(port, *args) # end # def serve(io) -# io.puts(Time.now.to_i) +# io.puts(Time.now.to_s) # end # end # Index: mvm/lib/open3.rb =================================================================== --- mvm/lib/open3.rb (revision 20561) +++ mvm/lib/open3.rb (revision 20562) @@ -12,76 +12,536 @@ # Open3 grants you access to stdin, stdout, stderr and a thread to wait the # child process when running another program. # -# Example: +# - Open3.popen3 : pipes for stdin, stdout, stderr +# - Open3.popen2 : pipes for stdin, stdout +# - Open3.popen2e : pipes for stdin, merged stdout and stderr +# - Open3.poutput3 : give a string for stdin. get strings for stdout, stderr +# - Open3.poutput2 : give a string for stdin. get a string for stdout +# - Open3.poutput2e : give a string for stdin. get a string for merged stdout and stderr +# - Open3.pipeline_rw : pipes for first stdin and last stdout of a pipeline +# - Open3.pipeline_r : pipe for last stdout of a pipeline +# - Open3.pipeline_w : pipe for first stdin of a pipeline +# - Open3.pipeline_start : a pipeline +# - Open3.pipeline : run a pipline and wait # -# require "open3" -# include Open3 -# -# stdin, stdout, stderr, wait_thr = popen3('nroff -man') -# -# Open3.popen3 can also take a block which will receive stdin, stdout, -# stderr and wait_thr as parameters. -# This ensures stdin, stdout and stderr are closed and -# the process is terminated once the block exits. -# -# Example: -# -# require "open3" -# -# Open3.popen3('nroff -man') { |stdin, stdout, stderr, wait_thr| ... } -# module Open3 - # + # Open stdin, stdout, and stderr streams and start external executable. # In addition, a thread for waiting the started process is noticed. - # The thread has a thread variable :pid which is the pid of the started - # process. + # The thread has a pid method and thread variable :pid which is the pid of + # the started process. # + # Block form: + # + # Open3.popen3(cmd... [, opts]) {|stdin, stdout, stderr, wait_thr| + # pid = wait_thr.pid # pid of the started process. + # ... + # exit_status = wait_thr.value # Process::Status object returned. + # } + # # Non-block form: # - # stdin, stdout, stderr, wait_thr = Open3.popen3(cmd) + # stdin, stdout, stderr, wait_thr = Open3.popen3(cmd... [, opts]) # pid = wait_thr[:pid] # pid of the started process. # ... - # stdin.close # stdin, stdout and stderr should be closed in this form. + # stdin.close # stdin, stdout and stderr should be closed explicitly in this form. # stdout.close # stderr.close # exit_status = wait_thr.value # Process::Status object returned. # - # Block form: + # The parameters +cmd...+ is passed to Kernel#spawn. + # So a commandline string and list of argument strings can be accepted as follows. # - # Open3.popen3(cmd) { |stdin, stdout, stderr, wait_thr| ... } + # Open3.popen3("echo a") {|i, o, e, t| ... } + # Open3.popen3("echo", "a") {|i, o, e, t| ... } + # Open3.popen3(["echo", "argv0"], "a") {|i, o, e, t| ... } # - # The parameter +cmd+ is passed directly to Kernel#spawn. + # If the last parameter, opts, is a Hash, it is recognized as an option for Kernel#spawn. # + # Open3.popen3("pwd", :chdir=>"/") {|i,o,e,t| + # p o.read.chomp #=> "/" + # } + # # wait_thr.value waits the termination of the process. # The block form also waits the process when it returns. # # Closing stdin, stdout and stderr does not wait the process. # - def popen3(*cmd) - pw = IO::pipe # pipe[0] for read, pipe[1] for write - pr = IO::pipe - pe = IO::pipe + def popen3(*cmd, &block) + if Hash === cmd.last + opts = cmd.pop.dup + else + opts = {} + end - pid = spawn(*cmd, STDIN=>pw[0], STDOUT=>pr[1], STDERR=>pe[1]) + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + out_r, out_w = IO.pipe + opts[:out] = out_w + + err_r, err_w = IO.pipe + opts[:err] = err_w + + popen_run(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block) + end + module_function :popen3 + + # Open3.popen2 is similer to Open3.popen3 except it doesn't make a pipe for + # the standard error stream. + # + # Block form: + # + # Open3.popen2(cmd... [, opts]) {|stdin, stdout, wait_thr| + # pid = wait_thr.pid # pid of the started process. + # ... + # exit_status = wait_thr.value # Process::Status object returned. + # } + # + # Non-block form: + # + # stdin, stdout, wait_thr = Open3.popen2(cmd... [, opts]) + # ... + # stdin.close # stdin and stdout should be closed explicitly in this form. + # stdout.close + # + # Example: + # + # Open3.popen2("wc -c") {|i,o,t| + # i.print "answer to life the universe and everything" + # i.close + # p o.gets #=> "42\n" + # } + # + # Open3.popen2("bc -q") {|i,o,t| + # i.puts "obase=13" + # i.puts "6 * 9" + # p o.gets #=> "42\n" + # } + # + # Open3.popen2("dc") {|i,o,t| + # i.print "42P" + # i.close + # p o.read #=> "*" + # } + # + def popen2(*cmd, &block) + if Hash === cmd.last + opts = cmd.pop.dup + else + opts = {} + end + + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + out_r, out_w = IO.pipe + opts[:out] = out_w + + popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block) + end + module_function :popen2 + + # Open3.popen2e is similer to Open3.popen3 except it merges + # the standard output stream and the standard error stream. + # + # Block form: + # + # Open3.popen2e(cmd... [, opts]) {|stdin, stdout_and_stderr, wait_thr| + # pid = wait_thr.pid # pid of the started process. + # ... + # exit_status = wait_thr.value # Process::Status object returned. + # } + # + # Non-block form: + # + # stdin, stdout_and_stderr, wait_thr = Open3.popen2e(cmd... [, opts]) + # ... + # stdin.close # stdin and stdout_and_stderr should be closed explicitly in this form. + # stdout_and_stderr.close + # + def popen2e(*cmd, &block) + if Hash === cmd.last + opts = cmd.pop.dup + else + opts = {} + end + + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + out_r, out_w = IO.pipe + opts[[:out, :err]] = out_w + + popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block) + end + module_function :popen2e + + def popen_run(cmd, opts, child_io, parent_io) # :nodoc: + pid = spawn(*cmd, opts) wait_thr = Process.detach(pid) - pw[0].close - pr[1].close - pe[1].close - pi = [pw[1], pr[0], pe[0], wait_thr] - pw[1].sync = true + child_io.each {|io| io.close } + result = [*parent_io, wait_thr] if defined? yield begin - return yield(*pi) + return yield(*result) ensure - [pw[1], pr[0], pe[0]].each{|p| p.close unless p.closed?} + parent_io.each{|io| io.close unless io.closed?} wait_thr.join end end - pi + result end - module_function :popen3 + module_function :popen_run + class << self + private :popen_run + end + + # Open3.poutput3 captures the standard output and the standard error of a command. + # + # stdout_str, stderr_str, status = Open3.poutput3(cmd... [, opts]) + # + # The arguments cmd and opts are passed to Open3.popen3 except opts[:stdin_data]. + # + # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # + # Example: + # + # # dot is a command of graphviz. + # graph = <<'End' + # digraph g { + # a -> b + # } + # End + # layouted_graph, dot_log = Open3.poutput3("dot -v", :stdin_data=>graph) + # + # o, e, s = Open3.poutput3("echo a; sort >&2", :stdin_data=>"foo\nbar\nbaz\n") + # p o #=> "a\n" + # p e #=> "bar\nbaz\nfoo\n" + # p s #=> #<Process::Status: pid 32682 exit 0> + # + def poutput3(*cmd, &block) + if Hash === cmd.last + opts = cmd.pop.dup + else + opts = {} + end + + stdin_data = opts.delete(:stdin_data) || '' + + popen3(*cmd, opts) {|i, o, e, t| + out_reader = Thread.new { o.read } + err_reader = Thread.new { e.read } + i.write stdin_data + i.close + [out_reader.value, err_reader.value, t.value] + } + end + module_function :poutput3 + + # Open3.poutput2 captures the standard output of a command. + # + # stdout_str, status = Open3.poutput2(cmd... [, opts]) + # + # The arguments cmd and opts are passed to Open3.popen2 except opts[:stdin_data]. + # + # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # + # # factor is a command for integer factorization + # o, s = Open3.poutput2("factor", :stdin_data=>"42") + # p o #=> "42: 2 3 7\n" + # + def poutput2(*cmd, &block) + if Hash === cmd.last + opts = cmd.pop.dup + else + opts = {} + end + + stdin_data = opts.delete(:stdin_data) || '' + + popen2(*cmd, opts) {|i, o, t| + out_reader = Thread.new { o.read } + i.write stdin_data + i.close + [out_reader.value, t.value] + } + end + module_function :poutput2 + + # Open3.poutput2e captures the standard output and the standard error of a command. + # + # stdout_and_stderr_str, status = Open3.poutput2e(cmd... [, opts]) + # + # The arguments cmd and opts are passed to Open3.popen2e except opts[:stdin_data]. + # + # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # + def poutput2e(*cmd, &block) + if Hash === cmd.last + opts = cmd.pop.dup + else + opts = {} + end + + stdin_data = opts.delete(:stdin_data) || '' + + popen2e(*cmd, opts) {|i, oe, t| + outerr_reader = Thread.new { oe.read } + i.write stdin_data + i.close + [outerr_reader.value, t.value] + } + end + module_function :poutput2e + + # Open3.pipeline_rw starts list of commands as a pipeline with pipes + # which connects stdin of the first command and stdout of the last command. + # + # Open3.pipeline_rw(cmd1, cmd2, ... [, opts]) {|first_stdin, last_stdout, wait_threads| + # ... + # } + # + # first_stdin, last_stdout, wait_threads = Open3.pipeline_rw(cmd1, cmd2, ... [, opts]) + # ... + # first_stdin.close + # last_stdout.close + # + # Each cmd is a string or an array. + # If it is an array, the elements are passed to Kernel#spawn. + # + # The option to pass Kernel#spawn is constructed by merging + # +opts+, the last hash element of the array and + # specification for the pipe between each commands. + # + # Example: + # + # Open3.pipeline_rw("tr -dc A-Za-z", "wc -c") {|i,o,ts| + # i.puts "All persons more than a mile high to leave the court." + # i.close + # p o.gets #=> "42\n" + # } + # + # Open3.pipeline_rw("sort", "cat -n") {|stdin, stdout, wait_thrs| + # stdin.puts "foo" + # stdin.puts "bar" + # stdin.puts "baz" + # stdin.close # send EOF to sort. + # p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n" + # } + def pipeline_rw(*cmds, &block) + if Hash === cmds.last + opts = cmds.pop.dup + else + opts = {} + end + + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + out_r, out_w = IO.pipe + opts[:out] = out_w + + pipeline_run(cmds, opts, [in_r, out_w], [in_w, out_r], &block) + end + module_function :pipeline_rw + + # Open3.pipeline_r starts list of commands as a pipeline with a pipe + # which connects stdout of the last command. + # + # Open3.pipeline_r(cmd1, cmd2, ... [, opts]) {|last_stdout, wait_threads| + # ... + # } + # + # last_stdout, wait_threads = Open3.pipeline_r(cmd1, cmd2, ... [, opts]) + # ... + # last_stdout.close + # + # Example: + # + # fname = "/usr/share/man/man1/ls.1.gz" + # Open3.pipeline_r(["zcat", fname], "nroff -man", "colcrt") {|r, ts| + # IO.copy_stream(r, STDOUT) + # } + # + # Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz", + # [{"LANG"=>"C"}, "grep", "GET /favicon.ico"], + # "logresolve") {|r, ts| + # r.each_line {|line| + # ... + # } + # } + # + # Open3.pipeline_r("yes", "head -10") {|r, ts| + # p r.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n" + # p ts[0].value #=> #<Process::Status: pid 24910 SIGPIPE (signal 13)> + # p ts[1].value #=> #<Process::Status: pid 24913 exit 0> + # } + # + def pipeline_r(*cmds, &block) + if Hash === cmds.last + opts = cmds.pop.dup + else + opts = {} + end + + out_r, out_w = IO.pipe + opts[:out] = out_w + + pipeline_run(cmds, opts, [out_w], [out_r], &block) + end + module_function :pipeline_r + + # Open3.pipeline_w starts list of commands as a pipeline with a pipe + # which connects stdin of the first command. + # + # Open3.pipeline_w(cmd1, cmd2, ... [, opts]) {|first_stdin, wait_threads| + # ... + # } + # + # first_stdin, wait_threads = Open3.pipeline_w(cmd1, cmd2, ... [, opts]) + # ... + # first_stdin.close + # + # Example: + # + # Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|w, ts| + # w.puts "hello" + # } + # + def pipeline_w(*cmds, &block) + if Hash === cmds.last + opts = cmds.pop.dup + else + opts = {} + end + + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + pipeline_run(cmds, opts, [in_r], [in_w], &block) + end + module_function :pipeline_w + + # Open3.pipeline_start starts list of commands as a pipeline. + # No pipe made for stdin of the first command and + # stdout of the last command. + # + # Open3.pipeline_start(cmd1, cmd2, ... [, opts]) {|wait_threads| + # ... + # } + # + # wait_threads = Open3.pipeline_start(cmd1, cmd2, ... [, opts]) + # ... + # + def pipeline_start(*cmds, &block) + if Hash === cmds.last + opts = cmds.pop.dup + else + opts = {} + end + + pipeline_run(cmds, opts, [], [], &block) + end + module_function :pipeline_start + + # Open3.pipeline starts list of commands as a pipeline. + # It waits the finish of the commands. + # No pipe made for stdin of the first command and + # stdout of the last command. + # + # status_list = Open3.pipeline(cmd1, cmd2, ... [, opts]) + # + # Example: + # + # fname = "/usr/share/man/man1/ruby.1.gz" + # p Open3.pipeline(["zcat", fname], "nroff -man", "less") + # #=> [#<Process::Status: pid 11817 exit 0>, + # # #<Process::Status: pid 11820 exit 0>, + # # #<Process::Status: pid 11828 exit 0>] + # + # # count lines + # Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count") + # + def pipeline(*cmds) + if Hash === cmds.last + opts = cmds.pop.dup + else + opts = {} + end + + pipeline_run(cmds, opts, [], []) {|ts| + ts.map {|t| t.value } + } + end + module_function :pipeline + + def pipeline_run(cmds, pipeline_opts, child_io, parent_io, &block) # :nodoc: + if cmds.empty? + raise ArgumentError, "no commands" + end + + opts_base = pipeline_opts.dup + opts_base.delete :in + opts_base.delete :out + + wait_thrs = [] + r = nil + cmds.each_with_index {|cmd, i| + cmd_opts = opts_base.dup + if String === cmd + cmd = [cmd] + else + cmd_opts.update cmd.pop if Hash === cmd.last + end + if i == 0 + if !cmd_opts.include?(:in) + if pipeline_opts.include?(:in) + cmd_opts[:in] = pipeline_opts[:in] + end + end + else + cmd_opts[:in] = r + end + if i != cmds.length - 1 + r2, w2 = IO.pipe + cmd_opts[:out] = w2 + else + if !cmd_opts.include?(:out) + if pipeline_opts.include?(:out) + cmd_opts[:out] = pipeline_opts[:out] + end + end + end + pid = spawn(*cmd, cmd_opts) + wait_thrs << Process.detach(pid) + r.close if r + w2.close if w2 + r = r2 + } + result = parent_io + [wait_thrs] + child_io.each {|io| io.close } + if defined? yield + begin + return yield(*result) + ensure + parent_io.each{|io| io.close unless io.closed?} + wait_thrs.each {|t| t.join } + end + end + result + end + module_function :pipeline_run + class << self + private :pipeline_run + end + end if $0 == __FILE__ Index: mvm/lib/forwardable.rb =================================================================== --- mvm/lib/forwardable.rb (revision 20561) +++ mvm/lib/forwardable.rb (revision 20562) @@ -70,9 +70,30 @@ # Ruby # nil # -# Forwardable can be used to setup delegation at the object level as well. +# SingleForwardable can be used to setup delegation at the object level as well. # # printer = String.new +# printer.extend SingleForwardable # prepare object for delegation +# printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts() +# printer.puts "Howdy!" +# +# Also, SingleForwardable can be use to Class or Module. +# +# module Facade +# extend SingleForwardable +# def_delegator :Implementation, :service +# +# class Implementation +# def service... +# end +# end +# +# If you want to use both Forwardable and SingleForwardable, you can +# use methods def_instance_delegator and def_single_delegator, etc. +# +# If the object isn't a Module and Class, You can too extend +# Forwardable module. +# printer = String.new # printer.extend Forwardable # prepare object for delegation # printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts() # printer.puts "Howdy!" @@ -111,8 +132,13 @@ # Also see the example at forwardable.rb. module Forwardable - FORWARDABLE_VERSION = "1.0.0" - + FORWARDABLE_VERSION = "1.1.0" + + @debug = nil + class<<self + attr_accessor :debug + end + # Takes a hash as its argument. The key is a symbol or an array of # symbols. These symbols correspond to method names. The value is # the accessor to which the methods will be delegated. @@ -121,7 +147,7 @@ # delegate method => accessor # delegate [method, method, ...] => accessor # - def delegate(hash) + def instance_delegate(hash) hash.each{ |methods, accessor| methods = methods.to_s unless methods.respond_to?(:each) methods.each{ |method| @@ -144,34 +170,101 @@ def def_instance_delegators(accessor, *methods) methods.delete("__send__") methods.delete("__id__") - methods.each{ |method| + for method in methods def_instance_delegator(accessor, method) - } + end end - # - # Defines a method _method_ which delegates to _obj_ (i.e. it calls - # the method of the same name in _obj_). If _new_name_ is - # provided, it is used as the name for the delegate method. - # def def_instance_delegator(accessor, method, ali = method) - str = %Q{ + line_no = __LINE__; str = %{ def #{ali}(*args, &block) - #{accessor}.send(:#{method}, *args, &block) + begin + #{accessor}.__send__(:#{method}, *args, &block) + rescue Exception + $@.delete_if{|s| %r"#{Regexp.quote(__FILE__)}"o =~ s} unless Forwardable::debug + ::Kernel::raise + end end } - # If it's not a class or module, it's an instance begin - module_eval(str) + module_eval(str, __FILE__, line_no) rescue - instance_eval(str) + instance_eval(str, __FILE__, line_no) end + end + alias delegate instance_delegate alias def_delegators def_instance_delegators alias def_delegator def_instance_delegator end -# compatibility -SingleForwardable = Forwardable +# +# Usage of The SingleForwardable is like Fowadable module. +# +module SingleForwardable + # Takes a hash as its argument. The key is a symbol or an array of + # symbols. These symbols correspond to method names. The value is + # the accessor to which the methods will be delegated. + # + # :call-seq: + # delegate method => accessor + # delegate [method, method, ...] => accessor + # + def single_delegate(hash) + hash.each{ |methods, accessor| + methods = methods.to_s unless methods.respond_to?(:each) + methods.each{ |method| + def_single_delegator(accessor, method) + } + } + end + + # + # Shortcut for defining multiple delegator methods, but with no + # provision for using a different name. The following two code + # samples have the same effect: + # + # def_delegators :@records, :size, :<<, :map + # + # def_delegator :@records, :size + # def_delegator :@records, :<< + # def_delegator :@records, :map + # + def def_single_delegators(accessor, *methods) + methods.delete("__send__") + methods.delete("__id__") + for method in methods + def_single_delegator(accessor, method) + end + end + + # + # Defines a method _method_ which delegates to _obj_ (i.e. it calls + # the method of the same name in _obj_). If _new_name_ is + # provided, it is used as the name for the delegate method. + # + def def_single_delegator(accessor, method, ali = method) + line_no = __LINE__; str = %{ + def #{ali}(*args, &block) + begin + #{accessor}.__send__(:#{method}, *args, &block) + rescue Exception + $@.delete_if{|s| %r"#{Regexp.quote(__FILE__)}"o =~ s} unless Forwardable::debug + ::Kernel::raise + end + end + } + + instance_eval(str, __FILE__, __LINE__) + end + + alias delegate single_delegate + alias def_delegators def_single_delegators + alias def_delegator def_single_delegator +end + + + + Index: mvm/lib/rexml/xpath.rb =================================================================== --- mvm/lib/rexml/xpath.rb (revision 20561) +++ mvm/lib/rexml/xpath.rb (revision 20562) @@ -15,10 +15,15 @@ # node matching '*'. # namespaces:: # If supplied, a Hash which defines a namespace mapping. + # variables:: + # If supplied, a Hash which maps $variables in the query + # to values. This can be used to avoid XPath injection attacks + # or to automatically handle escaping string values. # # XPath.first( node ) # XPath.first( doc, "//b"} ) # XPath.first( node, "a/x:b", { "x"=>"http://doofus" } ) + # XPath.first( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) def XPath::first element, path=nil, namespaces=nil, variables={} raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash) raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash) @@ -38,10 +43,16 @@ # The xpath to search for. If not supplied or nil, defaults to '*' # namespaces:: # If supplied, a Hash which defines a namespace mapping + # variables:: + # If supplied, a Hash which maps $variables in the query + # to values. This can be used to avoid XPath injection attacks + # or to automatically handle escaping string values. # # XPath.each( node ) { |el| ... } # XPath.each( node, '/*[@attr='v']' ) { |el| ... } # XPath.each( node, 'ancestor::x' ) { |el| ... } + # XPath.each( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) \ + # {|el| ... } def XPath::each element, path=nil, namespaces=nil, variables={}, &block raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash) raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash) Index: mvm/lib/rubygems/local_remote_options.rb =================================================================== --- mvm/lib/rubygems/local_remote_options.rb (revision 20561) +++ mvm/lib/rubygems/local_remote_options.rb (revision 20562) @@ -99,7 +99,7 @@ end ## - # Add the --source option + # Add the --update-source option def add_update_sources_option Index: mvm/lib/rubygems/validator.rb =================================================================== --- mvm/lib/rubygems/validator.rb (revision 20561) +++ mvm/lib/rubygems/validator.rb (revision 20562) @@ -200,6 +200,7 @@ Dir.chdir(start_dir) end + private def remove_leading_dot_dir(path) path.sub(/^\.\//, "") end Index: mvm/lib/net/protocol.rb =================================================================== --- mvm/lib/net/protocol.rb (revision 20561) +++ mvm/lib/net/protocol.rb (revision 20562) @@ -131,9 +131,15 @@ BUFSIZE = 1024 * 16 def rbuf_fill - timeout(@read_timeout) { - @rbuf << @io.sysread(BUFSIZE) - } + begin + @rbuf << @io.read_nonblock(BUFSIZE) + rescue Errno::EWOULDBLOCK + if IO.select([@io], nil, nil, @read_timeout) + @rbuf << @io.read_nonblock(BUFSIZE) + else + raise Timeout::TimeoutError + end + end end def rbuf_consume(len) Index: mvm/compile.c =================================================================== --- mvm/compile.c (revision 20561) +++ mvm/compile.c (revision 20562) @@ -415,7 +415,7 @@ } VALUE -iseq_compile(VALUE self, NODE *node) +ruby_iseq_compile(VALUE self, NODE *node) { DECL_ANCHOR(ret); rb_iseq_t *iseq; @@ -502,7 +502,7 @@ } int -iseq_translate_threaded_code(rb_iseq_t *iseq) +ruby_iseq_translate_threaded_code(rb_iseq_t *iseq) { #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE extern const void **vm_get_insns_address_table(void); @@ -960,7 +960,7 @@ iseq_set_optargs_table(iseq); debugs("[compile step 5 (iseq_translate_threaded_code)] \n"); - iseq_translate_threaded_code(iseq); + ruby_iseq_translate_threaded_code(iseq); if (compile_debug > 1) { VALUE str = ruby_iseq_disasm(iseq->self); @@ -1355,7 +1355,8 @@ rb_hash_aset(map, obj, INT2FIX(lobj->position - (pos+len))); } else { - rb_warning("duplicated when clause is ignored"); + rb_compile_warning(RSTRING_PTR(iseq->filename), iobj->line_no, + "duplicated when clause is ignored"); } } generated_iseq[pos + 1 + j] = map; @@ -4974,14 +4975,14 @@ } VALUE -insns_name_array(void) +ruby_insns_name_array(void) { VALUE ary = rb_ary_new(); int i; for (i = 0; i < sizeof(insn_name_info) / sizeof(insn_name_info[0]); i++) { - rb_ary_push(ary, rb_str_new2(insn_name_info[i])); + rb_ary_push(ary, rb_obj_freeze(rb_str_new2(insn_name_info[i]))); } - return ary; + return rb_obj_freeze(ary); } static LABEL * @@ -5051,7 +5052,7 @@ eiseqval = 0; } else { - eiseqval = iseq_load(0, ptr[1], iseq->self, Qnil); + eiseqval = ruby_iseq_load(ptr[1], iseq->self, Qnil); } lstart = register_label(iseq, labels_table, ptr[2]); @@ -5064,8 +5065,6 @@ return COMPILE_OK; } -struct st_table *insn_make_insn_table(void); - static int iseq_build_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor, VALUE body, struct st_table *labels_table) @@ -5081,7 +5080,7 @@ static struct st_table *insn_table; if (insn_table == 0) { - insn_table = insn_make_insn_table(); + insn_table = ruby_insn_make_insn_table(); } for (i=0; i<len; i++) { @@ -5137,7 +5136,7 @@ { if (op != Qnil) { if (TYPE(op) == T_ARRAY) { - argv[j] = iseq_load(0, op, iseq->self, Qnil); + argv[j] = ruby_iseq_load(op, iseq->self, Qnil); } else if (CLASS_OF(op) == rb_cISeq) { argv[j] = op; @@ -5202,8 +5201,8 @@ static inline VALUE CHECK_INTEGER(VALUE v) {NUM2LONG(v); return v;} VALUE -iseq_build_from_ary(rb_iseq_t *iseq, VALUE locals, VALUE args, - VALUE exception, VALUE body) +ruby_iseq_build_from_ary(rb_iseq_t *iseq, VALUE locals, VALUE args, + VALUE exception, VALUE body) { int i; ID *tbl; Index: mvm/proc.c =================================================================== --- mvm/proc.c (revision 20561) +++ mvm/proc.c (revision 20562) @@ -20,9 +20,12 @@ NODE *body; }; +VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc); + static VALUE bmcall(VALUE, VALUE); static int method_arity(VALUE); static VALUE rb_obj_is_method(VALUE m); +static rb_iseq_t *get_method_iseq(VALUE method); /* Proc */ @@ -603,15 +606,23 @@ } static rb_iseq_t * -get_proc_iseq(VALUE self) +get_proc_iseq(VALUE self, int *is_proc) { rb_proc_t *proc; rb_iseq_t *iseq; GetProcPtr(self, proc); iseq = proc->block.iseq; - if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) - return 0; + if (is_proc) *is_proc = !proc->is_lambda; + if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) { + NODE *node = (NODE *)iseq; + iseq = 0; + if (nd_type(node) == NODE_IFUNC && node->nd_cfnc == bmcall) { + /* method(:foo).to_proc */ + iseq = get_method_iseq(node->nd_tval); + if (is_proc) *is_proc = 0; + } + } return iseq; } @@ -642,11 +653,48 @@ VALUE rb_proc_location(VALUE self) { - return iseq_location(get_proc_iseq(self)); + return iseq_location(get_proc_iseq(self, 0)); } +static VALUE +unnamed_parameters(int arity) +{ + VALUE a, param = rb_ary_new2((arity < 0) ? -arity : arity); + int n = (arity < 0) ? ~arity : arity; + ID req, rest; + CONST_ID(req, "req"); + a = rb_ary_new3(1, ID2SYM(req)); + OBJ_FREEZE(a); + for (; n; --n) { + rb_ary_push(param, a); + } + if (arity < 0) { + CONST_ID(rest, "rest"); + rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest))); + } + return param; +} + /* * call-seq: + * proc.parameters => array + * + * returns the parameter information of this proc + */ + +static VALUE +rb_proc_parameters(VALUE self) +{ + int is_proc; + rb_iseq_t *iseq = get_proc_iseq(self, &is_proc); + if (!iseq) { + return unnamed_parameters(proc_arity(self)); + } + return rb_iseq_parameters(iseq, is_proc); +} + +/* + * call-seq: * prc == other_proc => true or false * * Return <code>true</code> if <i>prc</i> is the same object as @@ -1458,6 +1506,8 @@ Data_Get_Struct(method, struct METHOD, data); body = data->body; switch (nd_type(body)) { + case NODE_BMETHOD: + return get_proc_iseq(body->nd_cval, 0); case RUBY_VM_METHOD_NODE: GetISeqPtr((VALUE)body->nd_body, iseq); if (RUBY_VM_NORMAL_ISEQ_P(iseq)) break; @@ -1482,6 +1532,23 @@ } /* + * call-seq: + * meth.parameters => array + * + * returns the parameter information of this method + */ + +static VALUE +rb_method_parameters(VALUE method) +{ + rb_iseq_t *iseq = get_method_iseq(method); + if (!iseq) { + return unnamed_parameters(method_arity(method)); + } + return rb_iseq_parameters(iseq, 0); +} + +/* * call-seq: * meth.to_s => string * meth.inspect => string @@ -1814,6 +1881,7 @@ rb_define_method(rb_cProc, "binding", proc_binding, 0); rb_define_method(rb_cProc, "curry", proc_curry, -1); rb_define_method(rb_cProc, "source_location", rb_proc_location, 0); + rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0); /* Exceptions */ rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError); @@ -1849,6 +1917,7 @@ rb_define_method(rb_cMethod, "owner", method_owner, 0); rb_define_method(rb_cMethod, "unbind", method_unbind, 0); rb_define_method(rb_cMethod, "source_location", rb_method_location, 0); + rb_define_method(rb_cMethod, "parameters", rb_method_parameters, 0); rb_define_method(rb_mKernel, "method", rb_obj_method, 1); rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1); @@ -1867,6 +1936,7 @@ rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0); rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); rb_define_method(rb_cUnboundMethod, "source_location", rb_method_location, 0); + rb_define_method(rb_cUnboundMethod, "parameters", rb_method_parameters, 0); /* Module#*_method */ rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1); Index: mvm/thread.c =================================================================== --- mvm/thread.c (revision 20561) +++ mvm/thread.c (revision 20562) @@ -2087,6 +2087,34 @@ /* for IO */ #if defined(NFDBITS) && defined(HAVE_RB_FD_INIT) + +/* + * several Unix platform supports file descriptors bigger than FD_SETSIZE + * in select(2) system call. + * + * - Linux 2.2.12 (?) + * - NetBSD 1.2 (src/sys/kern/sys_generic.c:1.25) + * select(2) documents how to allocate fd_set dynamically. + * http://netbsd.gw.com/cgi-bin/man-cgi?select++NetBSD-4.0 + * - FreeBSD 2.2 (src/sys/kern/sys_generic.c:1.19) + * - OpenBSD 2.0 (src/sys/kern/sys_generic.c:1.4) + * select(2) documents how to allocate fd_set dynamically. + * http://www.openbsd.org/cgi-bin/man.cgi?query=select&manpath=OpenBSD+4.4 + * - HP-UX documents how to allocate fd_set dynamically. + * http://docs.hp.com/en/B2355-60105/select.2.html + * - Solaris 8 has select_large_fdset + * + * When fd_set is not big enough to hold big file descriptors, + * it should be allocated dynamically. + * Note that this assumes fd_set is structured as bitmap. + * + * rb_fd_init allocates the memory. + * rb_fd_term free the memory. + * rb_fd_set may re-allocates bitmap. + * + * So rb_fd_set doesn't reject file descriptors bigger than FD_SETSIZE. + */ + void rb_fd_init(volatile rb_fdset_t *fds) { Index: mvm/spec/default.mspec =================================================================== --- mvm/spec/default.mspec (revision 20561) +++ mvm/spec/default.mspec (revision 20562) @@ -1,12 +1,5 @@ +load File.dirname(__FILE__) + '/rubyspec/ruby.1.9.mspec' class MSpecScript - # An ordered list of the directories containing specs to run - # as the CI process. - set :ci_files, %w[ - spec/rubyspec/1.9/core - spec/rubyspec/1.9/language - spec/rubyspec/1.9/library - ] - builddir = File.expand_path(File.join(File.dirname(__FILE__), '..')) srcdir = ENV['SRCDIR'] srcdir ||= $1 if File.read("#{builddir}/Makefile")[/^\s*srcdir\s*=\s*(.+)/i] Index: mvm/win32/win32.c =================================================================== --- mvm/win32/win32.c (revision 20561) +++ mvm/win32/win32.c (revision 20562) @@ -533,6 +533,18 @@ return NULL; } +static struct ChildRecord * +FindChildSlotByHandle(HANDLE h) +{ + + FOREACH_CHILD(child) { + if (child->hProcess == h) { + return child; + } + } END_FOREACH_CHILD; + return NULL; +} + static void CloseChildHandle(struct ChildRecord *child) { @@ -3062,7 +3074,7 @@ return -1; } - return poll_child_status(ChildRecord + ret, stat_loc); + return poll_child_status(FindChildSlotByHandle(events[ret]), stat_loc); } else { struct ChildRecord* child = FindChildSlot(pid); @@ -4304,6 +4316,10 @@ if (is_socket(sock)) return rb_w32_recv(fd, buf, size, 0); + // validate fd by using _get_osfhandle() because we cannot access _nhandle + if (_get_osfhandle(fd) == -1) { + return -1; + } if (!(_osfile(fd) & FOPEN)) { errno = EBADF; return -1; @@ -4379,11 +4395,15 @@ if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) && (err = GetLastError()) != ERROR_HANDLE_EOF) { - errno = map_errno(err); + int ret = 0; + if (err != ERROR_BROKEN_PIPE) { + errno = map_errno(err); + ret = -1; + } CloseHandle(ol.hEvent); cancel_io((HANDLE)_osfhnd(fd)); MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); - return -1; + return ret; } } } @@ -4418,6 +4438,10 @@ if (is_socket(sock)) return rb_w32_send(fd, buf, size, 0); + // validate fd by using _get_osfhandle() because we cannot access _nhandle + if (_get_osfhandle(fd) == -1) { + return -1; + } if (!(_osfile(fd) & FOPEN)) { errno = EBADF; return -1; @@ -4670,6 +4694,10 @@ int rb_w32_isatty(int fd) { + // validate fd by using _get_osfhandle() because we cannot access _nhandle + if (_get_osfhandle(fd) == -1) { + return 0; + } if (!(_osfile(fd) & FOPEN)) { errno = EBADF; return 0; Index: mvm/vm_method.c =================================================================== --- mvm/vm_method.c (revision 20561) +++ mvm/vm_method.c (revision 20562) @@ -1058,15 +1058,6 @@ return 0; } -/* - * call-seq: - * obj.respond_to?(symbol, include_private=false) => true or false - * - * Returns +true+> if _obj_ responds to the given - * method. Private methods are included in the search only if the - * optional second parameter evaluates to +true+. - */ - int rb_obj_respond_to(VALUE obj, ID id, int priv) { @@ -1095,7 +1086,7 @@ * call-seq: * obj.respond_to?(symbol, include_private=false) => true or false * - * Returns +true+> if _obj_ responds to the given + * Returns +true+ if _obj_ responds to the given * method. Private methods are included in the search only if the * optional second parameter evaluates to +true+. */ Index: mvm/gc.c =================================================================== --- mvm/gc.c (revision 20561) +++ mvm/gc.c (revision 20562) @@ -1053,10 +1053,10 @@ return STACK_LENGTH; } -int -ruby_stack_check(void) +static int +stack_check(void) { - int ret = 0; + int ret; rb_thread_t *th = GET_THREAD(); SET_STACK_END; @@ -1070,6 +1070,16 @@ return ret; } +int +ruby_stack_check(void) +{ +#if defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK) + return 0; +#else + return stack_check(); +#endif +} + static void init_mark_stack(rb_objspace_t *objspace) { @@ -1276,7 +1286,7 @@ if (obj->as.basic.flags & FL_MARK) return; /* already marked */ obj->as.basic.flags |= FL_MARK; - if (lev > GC_LEVEL_MAX || (lev == 0 && ruby_stack_check())) { + if (lev > GC_LEVEL_MAX || (lev == 0 && stack_check())) { if (!mark_stack_overflow) { if (mark_stack_ptr - mark_stack < MARK_STACK_MAX) { *mark_stack_ptr = ptr; Index: mvm/strftime.c =================================================================== --- mvm/strftime.c (revision 20561) +++ mvm/strftime.c (revision 20562) @@ -319,6 +319,7 @@ goto unknown; case '%': + FILL_PADDING(1); *s++ = '%'; continue; Index: mvm/parse.y =================================================================== --- mvm/parse.y (revision 20561) +++ mvm/parse.y (revision 20562) @@ -1176,12 +1176,12 @@ $$ = dispatch3(binary, $1, ripper_intern("or"), $3); %*/ } - | keyword_not expr + | keyword_not opt_nl expr { /*%%%*/ - $$ = call_uni_op(cond($2), '!'); + $$ = call_uni_op(cond($3), '!'); /*% - $$ = dispatch2(unary, ripper_intern("not"), $2); + $$ = dispatch2(unary, ripper_intern("not"), $3); %*/ } | '!' command_call Index: mvm/process.c =================================================================== --- mvm/process.c (revision 20561) +++ mvm/process.c (revision 20562) @@ -1211,6 +1211,7 @@ EXEC_OPTION_DUP2, EXEC_OPTION_CLOSE, EXEC_OPTION_OPEN, + EXEC_OPTION_DUP2_CHILD, EXEC_OPTION_CLOSE_OTHERS }; @@ -1222,6 +1223,17 @@ if (FIXNUM_P(v)) { fd = FIX2INT(v); } + else if (SYMBOL_P(v)) { + ID id = SYM2ID(v); + if (id == rb_intern("in")) + fd = 0; + else if (id == rb_intern("out")) + fd = 1; + else if (id == rb_intern("err")) + fd = 2; + else + goto wrong; + } else if (!NIL_P(tmp = rb_check_convert_type(v, T_FILE, "IO", "to_io"))) { rb_io_t *fptr; GetOpenFile(tmp, fptr); @@ -1233,6 +1245,7 @@ rb_raise(rb_eArgError, "wrong exec redirect"); } if (fd < 0) { + wrong: rb_raise(rb_eArgError, "negative file descriptor"); } return INT2FIX(fd); @@ -1253,6 +1266,18 @@ index = EXEC_OPTION_CLOSE; param = Qnil; } + else if (id == rb_intern("in")) { + index = EXEC_OPTION_DUP2; + param = INT2FIX(0); + } + else if (id == rb_intern("out")) { + index = EXEC_OPTION_DUP2; + param = INT2FIX(1); + } + else if (id == rb_intern("err")) { + index = EXEC_OPTION_DUP2; + param = INT2FIX(2); + } else { rb_raise(rb_eArgError, "wrong exec redirect symbol: %s", rb_id2name(id)); @@ -1268,20 +1293,27 @@ break; case T_ARRAY: - index = EXEC_OPTION_OPEN; path = rb_ary_entry(val, 0); - FilePathValue(path); - flags = rb_ary_entry(val, 1); - if (NIL_P(flags)) - flags = INT2NUM(O_RDONLY); - else if (TYPE(flags) == T_STRING) - flags = INT2NUM(rb_io_modestr_oflags(StringValueCStr(flags))); - else - flags = rb_to_int(flags); - perm = rb_ary_entry(val, 2); - perm = NIL_P(perm) ? INT2FIX(0644) : rb_to_int(perm); - param = hide_obj(rb_ary_new3(3, hide_obj(rb_str_dup(path)), - flags, perm)); + if (RARRAY_LEN(val) == 2 && SYMBOL_P(path) && + SYM2ID(path) == rb_intern("child")) { + index = EXEC_OPTION_DUP2_CHILD; + param = check_exec_redirect_fd(rb_ary_entry(val, 1)); + } + else { + index = EXEC_OPTION_OPEN; + FilePathValue(path); + flags = rb_ary_entry(val, 1); + if (NIL_P(flags)) + flags = INT2NUM(O_RDONLY); + else if (TYPE(flags) == T_STRING) + flags = INT2NUM(rb_io_modestr_oflags(StringValueCStr(flags))); + else + flags = rb_to_int(flags); + perm = rb_ary_entry(val, 2); + perm = NIL_P(perm) ? INT2FIX(0644) : rb_to_int(perm); + param = hide_obj(rb_ary_new3(3, hide_obj(rb_str_dup(path)), + flags, perm)); + } break; case T_STRING: @@ -1468,7 +1500,7 @@ int index, i; int maxhint = -1; - for (index = EXEC_OPTION_DUP2; index <= EXEC_OPTION_OPEN; index++) { + for (index = EXEC_OPTION_DUP2; index <= EXEC_OPTION_DUP2_CHILD; index++) { ary = rb_ary_entry(options, index); if (NIL_P(ary)) continue; @@ -1478,16 +1510,53 @@ if (RTEST(rb_hash_lookup(h, INT2FIX(fd)))) { rb_raise(rb_eArgError, "fd %d specified twice", fd); } - rb_hash_aset(h, INT2FIX(fd), Qtrue); + if (index == EXEC_OPTION_OPEN || index == EXEC_OPTION_DUP2) + rb_hash_aset(h, INT2FIX(fd), Qtrue); + else if (index == EXEC_OPTION_DUP2_CHILD) + rb_hash_aset(h, INT2FIX(fd), RARRAY_PTR(elt)[1]); + else /* index == EXEC_OPTION_CLOSE */ + rb_hash_aset(h, INT2FIX(fd), INT2FIX(-1)); if (maxhint < fd) maxhint = fd; - if (index == EXEC_OPTION_DUP2) { + if (index == EXEC_OPTION_DUP2 || index == EXEC_OPTION_DUP2_CHILD) { fd = FIX2INT(RARRAY_PTR(elt)[1]); if (maxhint < fd) maxhint = fd; } } } + + ary = rb_ary_entry(options, EXEC_OPTION_DUP2_CHILD); + if (!NIL_P(ary)) { + for (i = 0; i < RARRAY_LEN(ary); i++) { + VALUE elt = RARRAY_PTR(ary)[i]; + int newfd = FIX2INT(RARRAY_PTR(elt)[0]); + int oldfd = FIX2INT(RARRAY_PTR(elt)[1]); + int lastfd = oldfd; + VALUE val = rb_hash_lookup(h, INT2FIX(lastfd)); + long depth = 0; + while (FIXNUM_P(val) && 0 <= FIX2INT(val)) { + lastfd = FIX2INT(val); + val = rb_hash_lookup(h, val); + if (RARRAY_LEN(ary) < depth) + rb_raise(rb_eArgError, "cyclic child fd redirection from %d", oldfd); + depth++; + } + if (val != Qtrue) + rb_raise(rb_eArgError, "child fd %d is not redirected", oldfd); + if (oldfd != lastfd) { + VALUE val2; + rb_ary_store(elt, 1, INT2FIX(lastfd)); + rb_hash_aset(h, INT2FIX(newfd), INT2FIX(lastfd)); + val = INT2FIX(oldfd); + while (FIXNUM_P(val2 = rb_hash_lookup(h, val))) { + rb_hash_aset(h, val, INT2FIX(lastfd)); + val = val2; + } + } + } + } + if (rb_ary_entry(options, EXEC_OPTION_CLOSE_OTHERS) != Qfalse) { rb_ary_store(options, EXEC_OPTION_CLOSE_OTHERS, INT2FIX(maxhint)); } @@ -1637,25 +1706,34 @@ /* * call-seq: - * exec([env,] command [, arg, ...] [,options]) + * exec([env,] command... [,options]) * * Replaces the current process by running the given external _command_. - * If optional arguments, sequence of +arg+, are not given, that argument is - * taken as a line that is subject to shell expansion before being - * executed. If one or more +arg+ given, they - * are passed as parameters to _command_ with no shell - * expansion. If +command+ is a two-element array, the first - * element is the command to be executed, and the second argument is - * used as the <code>argv[0]</code> value, which may show up in process - * listings. In order to execute the command, one of the <code>exec(2)</code> + * _command..._ is one of following forms. + * + * commandline : command line string which is passed to a shell + * cmdname, arg1, ... : command name and one or more arguments (no shell) + * [cmdname, argv0], arg1, ... : command name and arguments including argv[0] (no shell) + * + * If single string is given as the command, + * it is taken as a command line that is subject to shell expansion before being executed. + * + * If two or more +string+ given, + * the first is taken as a command name and + * the rest are passed as parameters to command with no shell expansion. + * + * If a two-element array at the beginning of the command, + * the first element is the command to be executed, + * and the second argument is used as the <code>argv[0]</code> value, + * which may show up in process listings. + * + * In order to execute the command, one of the <code>exec(2)</code> * system calls is used, so the running command may inherit some of the environment * of the original program (including open file descriptors). - * - * The hash arguments, env and options, are same as - * <code>system</code> and <code>spawn</code>. + * This behavior is modified by env and options. * See <code>spawn</code> for details. * - * Raises SystemCallError if the _command_ couldn't execute (typically + * Raises SystemCallError if the command couldn't execute (typically * <code>Errno::ENOENT</code> when it was not found). * * exec "echo *" # echoes list of files in current directory @@ -1692,7 +1770,11 @@ va_list ap; FILE *tty; int save = errno; +#ifdef _WIN32 + tty = fopen("con", "w"); +#else tty = fopen("/dev/tty", "w"); +#endif if (!tty) return; @@ -1801,6 +1883,12 @@ } static int +intrcmp(const void *a, const void *b) +{ + return *(int*)b - *(int*)a; +} + +static int run_exec_dup2(VALUE ary, VALUE save) { int n, i; @@ -1825,7 +1913,10 @@ } /* sort the table by oldfd: O(n log n) */ - qsort(pairs, n, sizeof(struct fd_pair), intcmp); + if (!RTEST(save)) + qsort(pairs, n, sizeof(struct fd_pair), intcmp); + else + qsort(pairs, n, sizeof(struct fd_pair), intrcmp); /* initialize older_index and num_newer: O(n log n) */ for (i = 0; i < n; i++) { @@ -1969,6 +2060,23 @@ return 0; } +static int +run_exec_dup2_child(VALUE ary, VALUE save) +{ + int i, ret; + for (i = 0; i < RARRAY_LEN(ary); i++) { + VALUE elt = RARRAY_PTR(ary)[i]; + int newfd = FIX2INT(RARRAY_PTR(elt)[0]); + int oldfd = FIX2INT(RARRAY_PTR(elt)[1]); + + if (save_redirect_fd(newfd, save) < 0) + return -1; + ret = redirect_dup2(oldfd, newfd); + if (ret == -1) return -1; + } + return 0; +} + #ifdef HAVE_SETPGID static int run_exec_pgroup(VALUE obj, VALUE save) @@ -2137,6 +2245,12 @@ return -1; } + obj = rb_ary_entry(options, EXEC_OPTION_DUP2_CHILD); + if (!NIL_P(obj)) { + if (run_exec_dup2_child(obj, soptions) == -1) + return -1; + } + return 0; } @@ -2668,17 +2782,25 @@ /* * call-seq: - * system([env,] cmd [, arg, ...] [,options]) => true, false or nil + * system([env,] command... [,options]) => true, false or nil * - * Executes _cmd_ in a subshell, returning +true+ if the command - * gives zero exit status, +false+ for non zero exit status. Returns - * +nil+ if command execution fails. An error status is available in - * <code>$?</code>. The arguments are processed in the same way as - * for <code>Kernel::exec</code>. + * Executes _command..._ in a subshell. + * _command..._ is one of following forms. * + * commandline : command line string which is passed to a shell + * cmdname, arg1, ... : command name and one or more arguments (no shell) + * [cmdname, argv0], arg1, ... : command name and arguments including argv[0] (no shell) + * + * system returns +true+ if the command gives zero exit status, + * +false+ for non zero exit status. + * Returns +nil+ if command execution fails. + * An error status is available in <code>$?</code>. + * The arguments are processed in the same way as + * for <code>Kernel.spawn</code>. + * * The hash arguments, env and options, are same as * <code>exec</code> and <code>spawn</code>. - * See <code>spawn</code> for details. + * See <code>Kernel.spawn</code> for details. * * system("echo *") * system("echo", "*") @@ -2722,11 +2844,57 @@ /* * call-seq: - * spawn([env,] cmd [, arg, ...] [,options]) => pid + * spawn([env,] command... [,options]) => pid + * Process.spawn([env,] command... [,options]) => pid * - * Similar to <code>Kernel::system</code> except for not waiting for - * end of _cmd_, but returns its <i>pid</i>. + * spawn executes specified command and return its pid. + * It doesn't wait for end of the command. * + * spawn has bunch of options to specify process attributes: + * + * env: hash + * name => val : set the environment variable + * name => nil : unset the environment variable + * command...: + * commandline : command line string which is passed to a shell + * cmdname, arg1, ... : command name and one or more arguments (no shell) + * [cmdname, argv0], arg1, ... : command name and arguments including argv[0] (no shell) + * options: hash + * clearing environment variables: + * :unsetenv_others => true : clear environment variables except specified by env + * :unsetenv_others => false : don't clear (default) + * process group: + * :pgroup => true or 0 : process leader + * :pgroup => pgid : join to specified process group + * resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit. + * :rlimit_resourcename => limit + * :rlimit_resourcename => [cur_limit, max_limit] + * current directory: + * :chdir => str + * umask: + * :umask => int + * redirection: + * key: + * FD : single file descriptor in child process + * [FD, FD, ...] : multiple file descriptor in child process + * value: + * FD : redirect to the file descriptor in parent process + * string : redirect to file with open(string, "r" or "w") + * [string] : redirect to file with open(string, File::RDONLY) + * [string, open_mode] : redirect to file with open(string, open_mode, 0644) + * [string, open_mode, perm] : redirect to file with open(string, open_mode, perm) + * [:child, FD] : redirect to the redirected file descriptor + * :close : close the file descriptor in child process + * FD is one of follows + * :in : the file descriptor 0 + * :out : the file descriptor 1 + * :err : the file descriptor 2 + * integer : the file descriptor of specified the integer + * io : the file descriptor specified as io.fileno + * file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not + * :close_others => false : inherit fds (default for system and exec) + * :close_others => true : don't inherit (default for spawn and IO.popen) + * * If a hash is given as +env+, the environment is * updated by +env+ before <code>exec(2)</code> in the child process. * If a pair in +env+ has nil as the value, the variable is deleted. @@ -2779,44 +2947,46 @@ * The :in, :out, :err, a fixnum, an IO and an array key specifies a redirect. * The redirection maps a file descriptor in the child process. * - * For example, stderr can be merged into stdout: + * For example, stderr can be merged into stdout as follows: * * pid = spawn(command, :err=>:out) + * pid = spawn(command, 2=>1) + * pid = spawn(command, STDERR=>:out) * pid = spawn(command, STDERR=>STDOUT) - * pid = spawn(command, 2=>1) * * The hash keys specifies a file descriptor * in the child process started by <code>spawn</code>. - * :err, STDERR and 2 specifies the standard error stream. + * :err, 2 and STDERR specifies the standard error stream (stderr). * * The hash values specifies a file descriptor * in the parent process which invokes <code>spawn</code>. - * :out, STDOUT and 1 specifies the standard output stream. + * :out, 1 and STDOUT specifies the standard output stream (stdout). * - * The standard output in the child process is not specified. + * In the above example, + * the standard output in the child process is not specified. * So it is inherited from the parent process. * - * The standard input stream can be specifed by :in, STDIN and 0. + * The standard input stream (stdin) can be specifed by :in, 0 and STDIN. * * A filename can be specified as a hash value. * - * pid = spawn(command, STDIN=>"/dev/null") # read mode - * pid = spawn(command, STDOUT=>"/dev/null") # write mode - * pid = spawn(command, STDERR=>"log") # write mode + * pid = spawn(command, :in=>"/dev/null") # read mode + * pid = spawn(command, :out=>"/dev/null") # write mode + * pid = spawn(command, :err=>"log") # write mode * pid = spawn(command, 3=>"/dev/null") # read mode * - * For standard output and standard error, + * For stdout and stderr, * it is opened in write mode. * Otherwise read mode is used. * * For specifying flags and permission of file creation explicitly, * an array is used instead. * - * pid = spawn(command, STDIN=>["file"]) # read mode is assumed - * pid = spawn(command, STDIN=>["file", "r"]) - * pid = spawn(command, STDOUT=>["log", "w"]) # 0644 assumed - * pid = spawn(command, STDOUT=>["log", "w", 0600]) - * pid = spawn(command, STDOUT=>["log", File::WRONLY|File::EXCL|File::CREAT, 0600]) + * pid = spawn(command, :in=>["file"]) # read mode is assumed + * pid = spawn(command, :in=>["file", "r"]) + * pid = spawn(command, :out=>["log", "w"]) # 0644 assumed + * pid = spawn(command, :out=>["log", "w", 0600]) + * pid = spawn(command, :out=>["log", File::WRONLY|File::EXCL|File::CREAT, 0600]) * * The array specifies a filename, flags and permission. * The flags can be a string or an integer. @@ -2827,9 +2997,28 @@ * If an array of IOs and integers are specified as a hash key, * all the elemetns are redirected. * - * # standard output and standard error is redirected to log file. - * pid = spawn(command, [STDOUT, STDERR]=>["log", "w"]) + * # stdout and stderr is redirected to log file. + * # The file "log" is opened just once. + * pid = spawn(command, [:out, :err]=>["log", "w"]) * + * Another way to merge multiple file descriptors is [:child, fd]. + * [:child, fd] means the file descriptor in the child process. + * This is different from fd. + * For example, :err=>:out means redirecting child stderr to parent stdout. + * But :err=>[:child, :out] means redirecting child stderr to child stdout. + * They differs if stdout is redirected in the child process as follows. + * + * # stdout and stderr is redirected to log file. + * # The file "log" is opened just once. + * pid = spawn(command, :out=>["log", "w"], :err=>[:child, :out]) + * + * [:child, :out] can be used to merge stderr into stdout in IO.popen. + * In this case, IO.popen redirects stdout to a pipe in the child process + * and [:child, :out] refers the redirected stdout. + * + * io = IO.popen(["sh", "-c", "echo out; echo err >&2", :err=>[:child, :out]]) + * p io.read #=> "out\nerr\n" + * * spawn closes all non-standard unspecified descriptors by default. * The "standard" descriptors are 0, 1 and 2. * This behavior is specified by :close_others option. @@ -2845,7 +3034,7 @@ * * # similar to r = IO.popen(command) * r, w = IO.pipe - * pid = spawn(command, STDOUT=>w) # r, w is closed in the child process. + * pid = spawn(command, :out=>w) # r, w is closed in the child process. * w.close * * :close is specified as a hash value to close a fd individualy. @@ -2853,13 +3042,23 @@ * f = open(foo) * system(command, f=>:close) # don't inherit f. * + * If a file descriptor need to be inherited, + * io=>io can be used. + * + * # valgrind has --log-fd option for log destination. + * # log_w=>log_w indicates log_w.fileno inherits to child process. + * log_r, log_w = IO.pipe + * pid = spawn("valgrind", "--log-fd=#{log_w.fileno}", "echo", "a", log_w=>log_w) + * log_w.close + * p log_r.read + * * It is also possible to exchange file descriptors. * - * pid = spawn(command, STDOUT=>STDERR, STDERR=>STDOUT) + * pid = spawn(command, :out=>:err, :err=>:out) * * The hash keys specify file descriptors in the child process. * The hash values specifies file descriptors in the parent process. - * So the above specifies exchanging STDOUT and STDERR. + * So the above specifies exchanging stdout and stderr. * Internally, +spawn+ uses an extra file descriptor to resolve such cyclic * file descriptor mapping. * Index: mvm/ext/bigdecimal/bigdecimal.c =================================================================== --- mvm/ext/bigdecimal/bigdecimal.c (revision 20561) +++ mvm/ext/bigdecimal/bigdecimal.c (revision 20562) @@ -1012,7 +1012,9 @@ if(VpIsNaN(a) || VpIsNaN(b)) goto NaN; if(VpIsInf(a) || VpIsInf(b)) goto NaN; - if(VpIsZero(b)) goto NaN; + if(VpIsZero(b)) { + rb_raise(rb_eZeroDivError, "divided by 0"); + } if(VpIsZero(a)) { GUARD_OBJ(c,VpCreateRbObject(1, "0")); GUARD_OBJ(d,VpCreateRbObject(1, "0")); @@ -1169,9 +1171,6 @@ Real *mod; obj = BigDecimal_DoDivmod(self,b,&div,&mod); if(obj!=(VALUE)0) return obj; - if(VpIsNaN(div) && rb_equal(b, INT2FIX(0))) { - rb_raise(rb_eZeroDivError, "divided by 0"); - } return BigDecimal_to_i(ToValue(div)); } else { /* div in BigDecimal sense */ U_LONG ix = (U_LONG)GetPositiveInt(n); Index: mvm/ext/curses/curses.c =================================================================== --- mvm/ext/curses/curses.c (revision 20561) +++ mvm/ext/curses/curses.c (revision 20562) @@ -416,7 +416,7 @@ curses_stdscr(); c = getch(); if (c == EOF) return Qnil; - if (ISPRINT(c)) { + if (rb_isprint(c)) { char ch = (char)c; return rb_locale_str_new(&ch, 1); @@ -1131,7 +1131,7 @@ GetWINDOW(obj, winp); c = wgetch(winp->window); if (c == EOF) return Qnil; - if (ISPRINT(c)) { + if (rb_isprint(c)) { char ch = (char)c; return rb_locale_str_new(&ch, 1); Index: mvm/ext/pty/pty.c =================================================================== --- mvm/ext/pty/pty.c (revision 20561) +++ mvm/ext/pty/pty.c (revision 20562) @@ -438,7 +438,7 @@ cPTY = rb_define_module("PTY"); rb_define_module_function(cPTY,"getpty",pty_getpty,-1); rb_define_module_function(cPTY,"spawn",pty_getpty,-1); - rb_define_singleton_function(cPTY,"check",pty_check,-1); + rb_define_singleton_method(cPTY,"check",pty_check,-1); eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError); rb_define_method(eChildExited,"status",echild_status,0); Index: mvm/ext/tk/tcltklib.c =================================================================== --- mvm/ext/tk/tcltklib.c (revision 20561) +++ mvm/ext/tk/tcltklib.c (revision 20562) @@ -4,7 +4,7 @@ * Oct. 24, 1997 Y. Matsumoto */ -#define TCLTKLIB_RELEASE_DATE "2008-10-20" +#define TCLTKLIB_RELEASE_DATE "2008-12-03" #include "ruby.h" @@ -3090,22 +3090,20 @@ /* get args */ args = rb_ary_new2(argc - 2); for(i = 3; i < argc; i++) { + VALUE s; #if TCL_MAJOR_VERSION >= 8 str = Tcl_GetStringFromObj(argv[i], &len); + s = rb_tainted_str_new(str, len); +#else /* TCL_MAJOR_VERSION < 8 */ + str = argv[i]; + s = rb_tainted_str_new2(str); +#endif DUMP2("arg:%s",str); #ifndef HAVE_STRUCT_RARRAY_LEN - rb_ary_push(args, rb_tainted_str_new(str, len)); + rb_ary_push(args, s); #else - RARRAY(args)->as.heap.ptr[RARRAY(args)->as.heap.len++] = rb_tainted_str_new(str, len); + RARRAY(args)->ptr[RARRAY(args)->len++] = s; #endif -#else /* TCL_MAJOR_VERSION < 8 */ - DUMP2("arg:%s",argv[i]); -#ifndef HAVE_STRUCT_RARRAY_LEN - rb_ary_push(args, rb_tainted_str_new2(argv[i])); -#else - RARRAY(args)->as.heap.ptr[RARRAY(args)->as.heap.len++] = rb_tainted_str_new2(argv[i]); -#endif -#endif } if (old_gc == Qfalse) rb_gc_enable(); @@ -8295,7 +8293,7 @@ DUMP2("back from handler (current thread:%lx)", current); /* get result & free allocated memory */ - ret = RARRAY(result)->as.heap.ptr[0]; + ret = RARRAY_PTR(result)[0]; #if 0 /* use Tcl_EventuallyFree */ Tcl_EventuallyFree((ClientData)alloc_done, TCL_DYNAMIC); /* XXXXXXXX */ #else Index: mvm/ext/tk/lib/tk.rb =================================================================== --- mvm/ext/tk/lib/tk.rb (revision 20561) +++ mvm/ext/tk/lib/tk.rb (revision 20562) @@ -1109,7 +1109,7 @@ include TkComm extend TkComm - WITH_RUBY_VM = Object.const_defined?(:VM) && ::VM.class == Class + WITH_RUBY_VM = Object.const_defined?(:RubyVM) && ::RubyVM.class == Class WITH_ENCODING = defined?(::Encoding.default_external) && true #WITH_ENCODING = Object.const_defined?(:Encoding) && ::Encoding.class == Class #if TclTkLib::WINDOWING_SYSTEM == 'aqua' @@ -5529,7 +5529,7 @@ #Tk.freeze module Tk - RELEASE_DATE = '2008-10-20'.freeze + RELEASE_DATE = '2008-12-04'.freeze autoload :AUTO_PATH, 'tk/variable' autoload :TCL_PACKAGE_PATH, 'tk/variable' Index: mvm/ext/tk/lib/tk/menu.rb =================================================================== --- mvm/ext/tk/lib/tk/menu.rb (revision 20561) +++ mvm/ext/tk/lib/tk/menu.rb (revision 20562) @@ -569,7 +569,7 @@ keys = _symbolkey2str(keys) parent = nil - if args[0].kind_of?(TkWindow) || args[0] == nil + if !args.empty? && (args[0].kind_of?(TkWindow) || args[0] == nil) keys.delete('parent') # ignore parent = args.shift else @@ -577,7 +577,7 @@ end @variable = nil - if args[0].kind_of?(TkVariable) || args[0] == nil + if !args.empty? && (args[0].kind_of?(TkVariable) || args[0] == nil) keys.delete('variable') # ignore @variable = args.shift else Index: mvm/ext/openssl/ossl_ssl.c =================================================================== --- mvm/ext/openssl/ossl_ssl.c (revision 20561) +++ mvm/ext/openssl/ossl_ssl.c (revision 20562) @@ -12,6 +12,14 @@ */ #include "ossl.h" +#define rb_sys_fail_path(path) rb_sys_fail(NIL_P(path) ? 0 : RSTRING_PTR(path)) + +#if defined(HAVE_FCNTL_H) || defined(_WIN32) +#include <fcntl.h> +#elif defined(HAVE_SYS_FCNTL_H) +#include <sys/fcntl.h> +#endif + #if defined(HAVE_UNISTD_H) # include <unistd.h> /* for read(), and write() */ #endif @@ -1003,6 +1011,71 @@ static VALUE ossl_ssl_read(int argc, VALUE *argv, VALUE self) { + SSL *ssl; + int ilen, nread = 0; + VALUE len, str; + rb_io_t *fptr; + + rb_scan_args(argc, argv, "11", &len, &str); + ilen = NUM2INT(len); + if(NIL_P(str)) str = rb_str_new(0, ilen); + else{ + StringValue(str); + rb_str_modify(str); + rb_str_resize(str, ilen); + } + if(ilen == 0) return str; + + Data_Get_Struct(self, SSL, ssl); + GetOpenFile(ossl_ssl_get_io(self), fptr); + if (ssl) { + if(SSL_pending(ssl) <= 0) + rb_thread_wait_fd(FPTR_TO_FD(fptr)); + for (;;){ + nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LEN(str)); + switch(ssl_get_error(ssl, nread)){ + case SSL_ERROR_NONE: + goto end; + case SSL_ERROR_ZERO_RETURN: + rb_eof_error(); + case SSL_ERROR_WANT_WRITE: + rb_io_wait_writable(FPTR_TO_FD(fptr)); + continue; + case SSL_ERROR_WANT_READ: + rb_io_wait_readable(FPTR_TO_FD(fptr)); + continue; + case SSL_ERROR_SYSCALL: + if(ERR_peek_error() == 0 && nread == 0) rb_eof_error(); + rb_sys_fail(0); + default: + ossl_raise(eSSLError, "SSL_read:"); + } + } + } + else { + rb_warning("SSL session is not started yet."); + return rb_funcall(ossl_ssl_get_io(self), rb_intern("read_nonblock"), 2, len, str); + } + +end: + rb_str_set_len(str, nread); + OBJ_TAINT(str); + + return str; +} + +/* + * call-seq: + * ssl.read_nonblock(length) => string + * ssl.read_nonblock(length, buffer) => buffer + * + * === Parameters + * * +length+ is a positive integer. + * * +buffer+ is a string used to store the result. + */ +static VALUE +ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self) +{ SSL *ssl; int ilen, nread = 0; VALUE len, str; @@ -1020,12 +1093,11 @@ Data_Get_Struct(self, SSL, ssl); GetOpenFile(ossl_ssl_get_io(self), fptr); + rb_io_set_nonblock(fptr); if (ssl) { - if(SSL_pending(ssl) <= 0) - rb_thread_wait_fd(FPTR_TO_FD(fptr)); for (;;){ nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LEN(str)); - switch(ssl_get_error(ssl, nread)){ + switch(SSL_get_error(ssl, nread)){ case SSL_ERROR_NONE: goto end; case SSL_ERROR_ZERO_RETURN: @@ -1034,7 +1106,7 @@ rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: - rb_io_wait_readable(FPTR_TO_FD(fptr)); + rb_sys_fail_path(fptr->pathv); continue; case SSL_ERROR_SYSCALL: if(ERR_peek_error() == 0 && nread == 0) rb_eof_error(); @@ -1045,9 +1117,8 @@ } } else { - ID id_sysread = rb_intern("sysread"); rb_warning("SSL session is not started yet."); - return rb_funcall(ossl_ssl_get_io(self), id_sysread, 2, len, str); + return rb_funcall(ossl_ssl_get_io(self), rb_intern("sysread"), 2, len, str); } end: @@ -1421,6 +1492,7 @@ rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, -1); rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1); rb_define_method(cSSLSocket, "sysclose", ossl_ssl_close, 0); + rb_define_method(cSSLSocket, "read_nonblock", ossl_ssl_read_nonblock, -1); rb_define_method(cSSLSocket, "cert", ossl_ssl_get_cert, 0); rb_define_method(cSSLSocket, "peer_cert", ossl_ssl_get_peer_cert, 0); rb_define_method(cSSLSocket, "peer_cert_chain", ossl_ssl_get_peer_cert_chain, 0); Index: mvm/ext/socket/socket.c =================================================================== --- mvm/ext/socket/socket.c (revision 20561) +++ mvm/ext/socket/socket.c (revision 20562) @@ -926,9 +926,8 @@ #endif static struct addrinfo* -sock_addrinfo(VALUE host, VALUE port, int socktype, int flags) +sock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints) { - struct addrinfo hints; struct addrinfo* res = NULL; char *hostp, *portp; int error; @@ -937,15 +936,11 @@ hostp = host_str(host, hbuf, sizeof(hbuf)); portp = port_str(port, pbuf, sizeof(pbuf)); - if (socktype == 0 && flags == 0 && str_isnumber(portp)) { - socktype = SOCK_DGRAM; + if (hints->ai_socktype == 0 && hints->ai_flags == 0 && str_isnumber(portp)) { + hints->ai_socktype = SOCK_DGRAM; } - MEMZERO(&hints, struct addrinfo, 1); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = socktype; - hints.ai_flags = flags; - error = getaddrinfo(hostp, portp, &hints, &res); + error = getaddrinfo(hostp, portp, hints, &res); if (error) { if (hostp && hostp[strlen(hostp)-1] == '\n') { rb_raise(rb_eSocket, "newline at the end of hostname"); @@ -958,7 +953,7 @@ struct addrinfo *r; r = res; while (r) { - if (! r->ai_socktype) r->ai_socktype = hints.ai_socktype; + if (! r->ai_socktype) r->ai_socktype = hints->ai_socktype; if (! r->ai_protocol) { if (r->ai_socktype == SOCK_DGRAM) { r->ai_protocol = IPPROTO_UDP; @@ -974,6 +969,18 @@ return res; } +static struct addrinfo* +sock_addrinfo(VALUE host, VALUE port, int socktype, int flags) +{ + struct addrinfo hints; + + MEMZERO(&hints, struct addrinfo, 1); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = socktype; + hints.ai_flags = flags; + return sock_getaddrinfo(host, port, &hints); +} + static VALUE ipaddr(struct sockaddr *sockaddr, int norevlookup) { @@ -3271,33 +3278,10 @@ sock_s_getaddrinfo(int argc, VALUE *argv) { VALUE host, port, family, socktype, protocol, flags, ret; - char hbuf[1024], pbuf[1024]; - char *hptr, *pptr, *ap; + char *ap; struct addrinfo hints, *res; - int error; - host = port = family = socktype = protocol = flags = Qnil; rb_scan_args(argc, argv, "24", &host, &port, &family, &socktype, &protocol, &flags); - if (NIL_P(host)) { - hptr = NULL; - } - else { - strncpy(hbuf, StringValuePtr(host), sizeof(hbuf)); - hbuf[sizeof(hbuf) - 1] = '\0'; - hptr = hbuf; - } - if (NIL_P(port)) { - pptr = NULL; - } - else if (FIXNUM_P(port)) { - snprintf(pbuf, sizeof(pbuf), "%ld", FIX2LONG(port)); - pptr = pbuf; - } - else { - strncpy(pbuf, StringValuePtr(port), sizeof(pbuf)); - pbuf[sizeof(pbuf) - 1] = '\0'; - pptr = pbuf; - } MEMZERO(&hints, struct addrinfo, 1); if (NIL_P(family)) { @@ -3326,10 +3310,7 @@ if (!NIL_P(flags)) { hints.ai_flags = NUM2INT(flags); } - error = getaddrinfo(hptr, pptr, &hints, &res); - if (error) { - raise_socket_error("getaddrinfo", error); - } + res = sock_getaddrinfo(host, port, &hints); ret = make_addrinfo(res); freeaddrinfo(res); Index: mvm/ext/stringio/stringio.c =================================================================== --- mvm/ext/stringio/stringio.c (revision 20561) +++ mvm/ext/stringio/stringio.c (revision 20562) @@ -749,7 +749,7 @@ /* * call-seq: - * strio.readchar -> fixnum + * strio.readchar -> string * * See IO#readchar. */ Index: mvm/man/rake.1 =================================================================== --- mvm/man/rake.1 (revision 0) +++ mvm/man/rake.1 (revision 20562) @@ -0,0 +1,169 @@ +.Dd November 30, 2008 +.Dt RAKE(1) "" "Ruby Programmers Reference Guide" +.Os UNIX +.Sh NAME +.Nm rake +.Nd Ruby Make +.Sh SYNOPSIS +.Nm +.Op Fl -f Ar Rakefile +.Op Fl -version +.Op Fl CGNPgnqstv +.Op Fl D Op Ar PATTERN +.Op Fl E Ar CODE +.Op Fl I Ar LIBDIR +.Op Fl R Ar RAKELIBDIR +.Op Fl T Op Ar PATTERN +.Op Fl e Ar CODE +.Op Fl p Ar CODE +.Op Fl r Ar MODULE +.Op Fl -rules +.Op Ar variable Ns = Ns Ar value +.Ar target ... +.Sh DESCRIPTION +.Nm Rake +is a simple +.Xr ruby 1 +build program with capabilities similar to the regular +.Xr make 1 +command. + +.Nm Rake +has the following features: +.Bl -bullet +.It +Rakefiles (Rake's version of Makefiles) are completely defined in standard Ruby syntax. +No XML files to edit. No quirky Makefile syntax to worry about (is that a tab or a space?). +.It +Users can specify tasks with prerequisites. +.It +Rake supports rule patterns to sythesize implicit tasks. +.It +Flexible FileLists that act like arrays but know about manipulating file names and paths. +.It +A library of prepackaged tasks to make building rakefiles easier. +.El +.Pp +.Sh OPTIONS +.Bl -tag -width "--execute-continue" -compact +.Pp +.It Fl -version +Display the program version. +.Pp +.It Fl C +.It Fl -classic-namespace +Put Task and FileTask in the top level namespace +.Pp +.It Fl D Op Ar PATTERN +.It Fl -describe Op Ar PATTERN +Describe the tasks (matching optional +.Ar PATTERN Ns +), then exit. +.Pp +.It Fl E Ar CODE +.It Fl -execute-continue Ar CODE +Execute some Ruby code, then continue with normal task processing. +.Pp +.It Fl G +.It Fl -no-system +.It Fl -nosystem +Use standard project Rakefile search paths, ignore system wide rakefiles. +.Pp +.It Fl I Ar LIBDIR +.It Fl -libdir Ar LIBDIR +Include +.Ar LIBDIR +in the search path for required modules. +.Pp +.It Fl N +.It Fl -no-search +.It Fl -nosearch +Do not search parent directories for the Rakefile. +.Pp +.It Fl P +.It Fl -prereqs +Display the tasks and dependencies, then exit. +.Pp +.It Fl R Ar RAKELIBDIR +.It Fl -rakelib Ar RAKELIBDIR +.It Fl -rakelibdir Ar RAKELIBDIR +Auto-import any .rake files in +.Ar RAKELIBDIR . +(default is +.Pa rakelib +) +.Pp +.It Fl T Op Ar PATTERN +.It Fl -tasks Op Ar PATTERN +Display the tasks (matching optional +.Ar PATTERN Ns +) with descriptions, then exit. +.Pp +.It Fl e Ar CODE +.It Fl -execute Ar CODE +Execute some Ruby code and exit. +.Pp +.It Fl f Ar FILE +.It Fl -rakefile Ar FILE +Use FILE as the rakefile. +.Pp +.It Fl h +.It Fl -help +Prints a summary of options. +.Pp +.It Fl g +.It Fl -system +Using system wide (global) rakefiles (usually +.Pa ~/.rake/*.rake +). +.Pp +.It Fl n +.It Fl -dry-run +Do a dry run without executing actions. +.Pp +.It Fl p Ar CODE +.It Fl -execute-print Ar CODE +Execute some Ruby code, print the result, then exit. +.Pp +.It Fl q +.It Fl -quiet +Do not log messages to standard output. +.Pp +.It Fl r Ar MODULE +.It Fl -require Ar MODULE +Require MODULE before executing rakefile. +.Pp +.It Fl s +.It Fl -silent +Like +.Fl -quiet , +but also suppresses the 'in directory' announcement. +.Pp +.It Fl t +.It Fl -trace +Turn on invoke/execute tracing, enable full backtrace. +.Pp +.It Fl v +.It Fl -verbose +Log message to standard output (default). +.Pp +.It Fl -rules +Trace the rules resolution. +.Pp +.El +.Pp +.Sh SEE ALSO +.Xr ruby 1 +.Xr make 1 +.Pp +http://rake.rubyforge.org/ +.Sh REPORTING BUGS +Bugs, features requests and other issues can be logged at +<\fBhttp://onestepback.org/redmine/projects/show/rake\fR>. +.Pp +You will need an account to before you can post issues. Register at <\fBhttp://onestepback.org/redmine/account/register\fR>. +Or you can send an email to the author. +.Sh AUTHOR +.Nm Rake +is written by +.An Jim Weirich Aq jim@w... Property changes on: mvm/man/rake.1 ___________________________________________________________________ Name: svn:eol-style + LF Name: svn:keywords + Author Id Revision Index: mvm/man/goruby.1 =================================================================== --- mvm/man/goruby.1 (revision 20561) +++ mvm/man/goruby.1 (revision 20562) @@ -13,7 +13,7 @@ .Op Ar argument ... .Sh DESCRIPTION .Sy goruby -is a kind of Ruby language processor, +is a kind of Ruby language processor which recognizes extremely shorten programs as bellow; .Bd -literal -offset indent rq"date";s De.td Index: mvm/man/irb.1 =================================================================== --- mvm/man/irb.1 (revision 20561) +++ mvm/man/irb.1 (revision 20562) @@ -40,7 +40,7 @@ .It Fl m Bc mode (load mathn, fraction or matrix are available) .Pp -.It Fl -r Ar load-module +.It Fl r Ar load-module Same as `ruby -r' .Pp .It Fl I Ar path @@ -108,6 +108,24 @@ .Pp .El .Pp +.Sh EXAMPLES +.Dl % irb +.Dl irb(main):001:0> Ic 1 + 1 +.Dl 2 +.Dl irb(main):002:0> Ic def t(x) +.Dl irb(main):003:1> Ic x+1 +.Dl irb(main):004:1> Ic end +.Dl => nil +.Dl irb(main):005:0> Ic t(3) +.Dl => 4 +.Dl irb(main):006:0> Ic if t(3) == 4 +.Dl irb(main):007:1> Ic p :ok +.Dl irb(main):008:1> Ic end +.Dl :ok +.Dl => :ok +.Dl irb(main):009:0> Ic quit +.Dl % +.Pp .Sh SEE ALSO .Xr ruby 1 . .Pp Index: mvm/.merged-trunk-revision =================================================================== --- mvm/.merged-trunk-revision (revision 20561) +++ mvm/.merged-trunk-revision (revision 20562) @@ -1 +1 @@ -20375 +20561 Index: mvm/numeric.c =================================================================== --- mvm/numeric.c (revision 20561) +++ mvm/numeric.c (revision 20562) @@ -670,6 +670,7 @@ { double div, mod; + if (y == 0.0) rb_num_zerodiv(); #ifdef HAVE_FMOD mod = fmod(x, y); #else Index: mvm/cont.c =================================================================== --- mvm/cont.c (revision 20561) +++ mvm/cont.c (revision 20562) @@ -25,6 +25,7 @@ typedef struct rb_context_struct { enum context_type type; VALUE self; + int argc; VALUE value; VALUE *vm_stack; #ifdef CAPTURE_JUST_VALID_VM_STACK @@ -60,8 +61,10 @@ #define GetContPtr(obj, ptr) \ Data_Get_Struct(obj, rb_context_t, ptr) -#define GetFiberPtr(obj, ptr) \ - Data_Get_Struct(obj, rb_fiber_t, ptr) +#define GetFiberPtr(obj, ptr) do {\ + ptr = (rb_fiber_t*)DATA_PTR(obj);\ + if (!ptr) rb_raise(rb_eFiberError, "uninitialized fiber");\ +} while(0) NOINLINE(static VALUE cont_capture(volatile int *stat)); @@ -552,6 +555,7 @@ } } + cont->argc = argc; cont->value = make_passing_arg(argc, argv); cont_restore_0(cont, &contval); @@ -564,26 +568,33 @@ #define FIBER_VM_STACK_SIZE (4 * 1024) -static rb_fiber_t * +static VALUE fiber_alloc(VALUE klass) { - rb_fiber_t *fib; - volatile VALUE fibval = Data_Make_Struct(klass, rb_fiber_t, fiber_mark, fiber_free, fib); + return Data_Wrap_Struct(klass, fiber_mark, fiber_free, 0); +} +static rb_fiber_t* +fiber_t_alloc(VALUE fibval) +{ + rb_fiber_t *fib = ALLOC(rb_fiber_t); + + memset(fib, 0, sizeof(rb_fiber_t)); fib->cont.self = fibval; fib->cont.type = FIBER_CONTEXT; cont_init(&fib->cont); fib->prev = Qnil; fib->status = CREATED; + DATA_PTR(fibval) = fib; + return fib; } static VALUE -fiber_new(VALUE klass, VALUE proc) +fiber_init(VALUE fibval, VALUE proc) { - rb_fiber_t *fib = fiber_alloc(klass); - VALUE fibval = fib->cont.self; + rb_fiber_t *fib = fiber_t_alloc(fibval); rb_context_t *cont = &fib->cont; rb_thread_t *th = &cont->saved_thread; @@ -619,16 +630,16 @@ return fibval; } -VALUE -rb_fiber_new(VALUE (*func)(ANYARGS), VALUE obj) +static VALUE +rb_fiber_init(VALUE fibval) { - return fiber_new(rb_cFiber, rb_proc_new(func, obj)); + return fiber_init(fibval, rb_block_proc()); } -static VALUE -rb_fiber_s_new(VALUE self) +VALUE +rb_fiber_new(VALUE (*func)(ANYARGS), VALUE obj) { - return fiber_new(self, rb_block_proc()); + return fiber_init(fiber_alloc(rb_cFiber), rb_proc_new(func, obj)); } static VALUE @@ -672,7 +683,6 @@ rb_fiber_t *fib; rb_context_t *cont; rb_proc_t *proc; - VALUE args; int state; GetFiberPtr(th->fiber, fib); @@ -680,15 +690,18 @@ TH_PUSH_TAG(th); if ((state = EXEC_TAG()) == 0) { + int argc; + VALUE *argv, args; GetProcPtr(cont->saved_thread.first_proc, proc); args = cont->value; + argv = (argc = cont->argc) > 1 ? RARRAY_PTR(args) : &args; cont->value = Qnil; th->errinfo = Qnil; th->local_lfp = proc->block.lfp; th->local_svar = Qnil; fib->status = RUNNING; - cont->value = vm_invoke_proc(th, proc, proc->block.self, 1, &args, 0); + cont->value = vm_invoke_proc(th, proc, proc->block.self, argc, argv, 0); } TH_POP_TAG(); @@ -713,7 +726,7 @@ rb_fiber_t *fib; /* no need to allocate vm stack */ - fib = fiber_alloc(rb_cFiber); + fib = fiber_t_alloc(fiber_alloc(rb_cFiber)); fib->cont.type = ROOT_FIBER_CONTEXT; fib->prev_fiber = fib->next_fiber = fib; @@ -785,6 +798,7 @@ fib->prev = rb_fiber_current(); } + cont->argc = argc; cont->value = make_passing_arg(argc, argv); if ((value = fiber_store(fib)) == Qundef) { @@ -863,10 +877,10 @@ InitVM_Cont(ruby_vm_t *vm) { rb_cFiber = rb_define_class("Fiber", rb_cObject); - rb_undef_alloc_func(rb_cFiber); + rb_define_alloc_func(rb_cFiber, fiber_alloc); rb_eFiberError = rb_define_class("FiberError", rb_eStandardError); - rb_define_singleton_method(rb_cFiber, "new", rb_fiber_s_new, 0); rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1); + rb_define_method(rb_cFiber, "initialize", rb_fiber_init, 0); rb_define_method(rb_cFiber, "resume", rb_fiber_m_resume, -1); } Index: mvm/vm.c =================================================================== --- mvm/vm.c (revision 20561) +++ mvm/vm.c (revision 20562) @@ -1726,7 +1726,6 @@ return Qnil; } -VALUE insns_name_array(void); /* debug functions */ @@ -1834,7 +1833,7 @@ #endif /* ::VM::InsnNameArray */ - rb_define_const(rb_cRubyVM, "INSTRUCTION_NAMES", insns_name_array()); + rb_define_const(rb_cRubyVM, "INSTRUCTION_NAMES", ruby_insns_name_array()); /* debug functions ::VM::SDR(), ::VM::NSDR() */ #if VMDEBUG Index: mvm/version.h =================================================================== --- mvm/version.h (revision 20561) +++ mvm/version.h (revision 20562) @@ -1,15 +1,15 @@ -#define RUBY_VERSION "1.9.0" -#define RUBY_RELEASE_DATE "2008-11-30" -#define RUBY_VERSION_CODE 190 -#define RUBY_RELEASE_CODE 20081130 -#define RUBY_PATCHLEVEL 0 +#define RUBY_VERSION "1.9.1" +#define RUBY_RELEASE_DATE "2008-12-06" +#define RUBY_VERSION_CODE 191 +#define RUBY_RELEASE_CODE 20081206 +#define RUBY_PATCHLEVEL 5000 #define RUBY_VERSION_MAJOR 1 #define RUBY_VERSION_MINOR 9 -#define RUBY_VERSION_TEENY 0 +#define RUBY_VERSION_TEENY 1 #define RUBY_RELEASE_YEAR 2008 -#define RUBY_RELEASE_MONTH 11 -#define RUBY_RELEASE_DAY 30 +#define RUBY_RELEASE_MONTH 12 +#define RUBY_RELEASE_DAY 6 #ifdef RUBY_EXTERN RUBY_EXTERN const char ruby_version[]; Index: mvm/test/bigdecimal/test_bigdecimal.rb =================================================================== --- mvm/test/bigdecimal/test_bigdecimal.rb (revision 20561) +++ mvm/test/bigdecimal/test_bigdecimal.rb (revision 20562) @@ -419,9 +419,7 @@ assert_equal([0, 0], BigDecimal.new("0").divmod(2)) BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - a, b = BigDecimal.new("0").divmod(0) - assert_equal(true, a.nan?) - assert_equal(true, b.nan?) + assert_raise(ZeroDivisionError){BigDecimal.new("0").divmod(0)} end def test_add_bigdecimal Index: mvm/test/ruby/test_fiber.rb =================================================================== --- mvm/test/ruby/test_fiber.rb (revision 20561) +++ mvm/test/ruby/test_fiber.rb (revision 20562) @@ -14,6 +14,10 @@ assert_equal([:a, :b], Fiber.new{|a, b| [a, b]}.resume(:a, :b)) end + def test_argument + assert_equal(4, Fiber.new {|i=4| i}.resume) + end + def test_term assert_equal(:ok, Fiber.new{:ok}.resume) assert_equal([:a, :b, :c, :d, :e], Index: mvm/test/ruby/test_regexp.rb =================================================================== --- mvm/test/ruby/test_regexp.rb (revision 20561) +++ mvm/test/ruby/test_regexp.rb (revision 20562) @@ -604,11 +604,13 @@ check(/\Aa{0}+\z/, "", %w(a aa aab)) check(/\Aa{1}+\z/, %w(a aa), ["", "aab"]) check(/\Aa{1,2}b{1,2}\z/, %w(ab aab abb aabb), ["", "aaabb", "abbb"]) + check(/(?!x){0,1}/, [ ['', 'ab'], ['', ''] ]) + check(/c\z{0,1}/, [ ['c', 'abc'], ['c', 'cab']], ['abd']) + check(/\A{0,1}a/, [ ['a', 'abc'], ['a', '____abc']], ['bcd']) failcheck('.{100001}') failcheck('.{0,100001}') failcheck('.{1,0}') failcheck('{0}') - failcheck('(?!x){0,1}') end def test_parse_comment Index: mvm/test/ruby/test_range.rb =================================================================== --- mvm/test/ruby/test_range.rb (revision 20561) +++ mvm/test/ruby/test_range.rb (revision 20562) @@ -267,4 +267,13 @@ def o.end; 0; end assert_equal([], [0, 1, 2][o]) end + + class CyclicRange < Range + def <=>(other); true; end + end + def test_cyclic_range_inspect + o = CyclicRange.allocate + o.instance_eval { initialize(o, 1) } + assert_equal("(... .. ...)..1", o.inspect) + end end Index: mvm/test/ruby/test_proc.rb =================================================================== --- mvm/test/ruby/test_proc.rb (revision 20561) +++ mvm/test/ruby/test_proc.rb (revision 20562) @@ -673,4 +673,47 @@ }.call(1,2,3,4,5) assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]") end + + def test_parameters + assert_equal([], proc {}.parameters) + assert_equal([], proc {||}.parameters) + assert_equal([[:opt, :a, nil]], proc {|a|}.parameters) + assert_equal([[:opt, :a, nil], [:opt, :b, nil]], proc {|a, b|}.parameters) + assert_equal([[:opt, :a, :a], [:block, :b]], proc {|a=:a, &b|}.parameters) + assert_equal([[:opt, :a, nil], [:opt, :b, :b]], proc {|a, b=:b|}.parameters) + assert_equal([[:rest, :a]], proc {|*a|}.parameters) + assert_equal([[:opt, :a, nil], [:rest, :b], [:block, :c]], proc {|a, *b, &c|}.parameters) + assert_equal([[:opt, :a, nil], [:rest, :b], [:opt, :c, nil]], proc {|a, *b, c|}.parameters) + assert_equal([[:opt, :a, nil], [:rest, :b], [:opt, :c, nil], [:block, :d]], proc {|a, *b, c, &d|}.parameters) + assert_equal([[:opt, :a, nil], [:opt, :b, :b], [:rest, :c], [:opt, :d, nil], [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters) + assert_equal([[:opt, nil, nil], [:block, :b]], proc {|(a), &b|}.parameters) + assert_equal([[:opt, :a, nil], [:opt, :b, nil], [:opt, :c, :c], [:opt, :d, :d], [:rest, :e], [:opt, :f, nil], [:opt, :g, nil], [:block, :h]], proc {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) + end + + def pm0() end + def pm1(a) end + def pm2(a, b) end + def pmo1(a = :a, &b) end + def pmo2(a, b = :b) end + def pmo3(*a) end + def pmo4(a, *b, &c) end + def pmo5(a, *b, c) end + def pmo6(a, *b, c, &d) end + def pmo7(a, b = :b, *c, d, &e) end + def pma1((a), &b) end + + + def test_bound_parameters + assert_equal([], method(:pm0).to_proc.parameters) + assert_equal([[:req, :a]], method(:pm1).to_proc.parameters) + assert_equal([[:req, :a], [:req, :b]], method(:pm2).to_proc.parameters) + assert_equal([[:opt, :a, :a], [:block, :b]], method(:pmo1).to_proc.parameters) + assert_equal([[:req, :a], [:opt, :b, :b]], method(:pmo2).to_proc.parameters) + assert_equal([[:rest, :a]], method(:pmo3).to_proc.parameters) + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:pmo4).to_proc.parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:pmo5).to_proc.parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).to_proc.parameters) + assert_equal([[:req, :a], [:opt, :b, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).to_proc.parameters) + assert_equal([[:req], [:block, :b]], method(:pma1).to_proc.parameters) + end end Index: mvm/test/ruby/test_float.rb =================================================================== --- mvm/test/ruby/test_float.rb (revision 20561) +++ mvm/test/ruby/test_float.rb (revision 20562) @@ -172,9 +172,7 @@ assert_raise(TypeError) { 2.0.divmod(nil) } inf = 1.0 / 0.0 - a, b = inf.divmod(0) - assert(a.infinite?) - assert(b.nan?) + assert_raise(ZeroDivisionError) {inf.divmod(0)} a, b = (2.0**32).divmod(1.0) assert_equal(2**32, a) Index: mvm/test/ruby/test_string.rb =================================================================== --- mvm/test/ruby/test_string.rb (revision 20561) +++ mvm/test/ruby/test_string.rb (revision 20562) @@ -1714,4 +1714,47 @@ def test_gsub_enumerator assert_normal_exit %q{"abc".gsub(/./).next}, "[ruby-dev:34828]" end + + def test_clear_nonasciicompat + assert_equal("", "\u3042".encode("ISO-2022-JP").clear) + end + + def test_try_convert + assert_equal(nil, String.try_convert(1)) + assert_equal("foo", String.try_convert("foo")) + end + + def test_substr_negative_begin + assert_equal("\u3042", ("\u3042" * 100)[-1]) + end + + def test_compare_different_encoding_string + s1 = "\xff".force_encoding("UTF-8") + s2 = "\xff".force_encoding("ISO-2022-JP") + #assert_equal([-1, 1], [s1 <=> s2, s2 <=> s1].sort) + end + + def test_casecmp + assert_equal(1, "\u3042B".casecmp("\u3042a")) + end + + def test_upcase2 + assert_equal("\u3042AB", "\u3042aB".upcase) + end + + def test_downcase2 + assert_equal("\u3042ab", "\u3042aB".downcase) + end + + def test_rstrip + assert_equal("\u3042", "\u3042 ".rstrip) + assert_raise(Encoding::CompatibilityError) { "\u3042".encode("ISO-2022-JP").rstrip } + end + + def test_symbol_table_overflow + return + assert_in_out_err([], <<-INPUT, [], /symbol table overflow \(symbol [a-z]{8}\) \(RuntimeError\)/) + ("aaaaaaaa".."zzzzzzzz").each {|s| s.to_sym } + INPUT + end end Index: mvm/test/ruby/test_method.rb =================================================================== --- mvm/test/ruby/test_method.rb (revision 20561) +++ mvm/test/ruby/test_method.rb (revision 20562) @@ -20,6 +20,8 @@ def mo4(a, *b, &c) end def mo5(a, *b, c) end def mo6(a, *b, c, &d) end + def mo7(a, b = nil, *c, d, &e) end + def ma1((a), &b) end class Base def foo() :base end @@ -237,4 +239,72 @@ assert !M.public_instance_methods.include?(:func), 'module methods are private by default' assert M.public_instance_methods.include?(:meth), 'normal methods are public by default' end + + define_method(:pm0) {||} + define_method(:pm1) {|a|} + define_method(:pm2) {|a, b|} + define_method(:pmo1) {|a = nil, &b|} + define_method(:pmo2) {|a, b = nil|} + define_method(:pmo3) {|*a|} + define_method(:pmo4) {|a, *b, &c|} + define_method(:pmo5) {|a, *b, c|} + define_method(:pmo6) {|a, *b, c, &d|} + define_method(:pmo7) {|a, b = nil, *c, d, &e|} + define_method(:pma1) {|(a), &b|} + + def test_bound_parameters + assert_equal([], method(:m0).parameters) + assert_equal([[:req, :a]], method(:m1).parameters) + assert_equal([[:req, :a], [:req, :b]], method(:m2).parameters) + assert_equal([[:opt, :a, nil], [:block, :b]], method(:mo1).parameters) + assert_equal([[:req, :a], [:opt, :b, nil]], method(:mo2).parameters) + assert_equal([[:rest, :a]], method(:mo3).parameters) + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:mo4).parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:mo5).parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:mo6).parameters) + assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], method(:mo7).parameters) + assert_equal([[:req], [:block, :b]], method(:ma1).parameters) + end + + def test_unbound_parameters + assert_equal([], self.class.instance_method(:m0).parameters) + assert_equal([[:req, :a]], self.class.instance_method(:m1).parameters) + assert_equal([[:req, :a], [:req, :b]], self.class.instance_method(:m2).parameters) + assert_equal([[:opt, :a, nil], [:block, :b]], self.class.instance_method(:mo1).parameters) + assert_equal([[:req, :a], [:opt, :b, nil]], self.class.instance_method(:mo2).parameters) + assert_equal([[:rest, :a]], self.class.instance_method(:mo3).parameters) + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], self.class.instance_method(:mo4).parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:mo5).parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:mo6).parameters) + assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:mo7).parameters) + assert_equal([[:req], [:block, :b]], self.class.instance_method(:ma1).parameters) + end + + def test_bmethod_bound_parameters + assert_equal([], method(:pm0).parameters) + assert_equal([[:req, :a]], method(:pm1).parameters) + assert_equal([[:req, :a], [:req, :b]], method(:pm2).parameters) + assert_equal([[:opt, :a, nil], [:block, :b]], method(:pmo1).parameters) + assert_equal([[:req, :a], [:opt, :b, nil]], method(:pmo2).parameters) + assert_equal([[:rest, :a]], method(:pmo3).parameters) + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:pmo4).parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:pmo5).parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).parameters) + assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).parameters) + assert_equal([[:req], [:block, :b]], method(:pma1).parameters) + end + + def test_bmethod_unbound_parameters + assert_equal([], self.class.instance_method(:pm0).parameters) + assert_equal([[:req, :a]], self.class.instance_method(:pm1).parameters) + assert_equal([[:req, :a], [:req, :b]], self.class.instance_method(:pm2).parameters) + assert_equal([[:opt, :a, nil], [:block, :b]], self.class.instance_method(:pmo1).parameters) + assert_equal([[:req, :a], [:opt, :b, nil]], self.class.instance_method(:pmo2).parameters) + assert_equal([[:rest, :a]], self.class.instance_method(:pmo3).parameters) + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], self.class.instance_method(:pmo4).parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:pmo5).parameters) + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:pmo6).parameters) + assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:pmo7).parameters) + assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters) + end end Index: mvm/test/ruby/test_process.rb =================================================================== --- mvm/test/ruby/test_process.rb (revision 20561) +++ mvm/test/ruby/test_process.rb (revision 20562) @@ -127,6 +127,7 @@ end def test_execopts_pgroup + skip "system(:pgroup) is not supported" if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM assert_nothing_raised { system(*TRUECOMMAND, :pgroup=>false) } io = IO.popen([RUBY, "-e", "print Process.getpgrp"]) @@ -306,6 +307,11 @@ Process.wait Process.spawn(*ECHO["e"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644], 3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT) assert_equal("e", File.read("out").chomp) + Process.wait Process.spawn(*ECHO["ee"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644], + 3=>0, 4=>:in, 5=>STDIN, + 6=>1, 7=>:out, 8=>STDOUT, + 9=>2, 10=>:err, 11=>STDERR) + assert_equal("ee", File.read("out").chomp) File.open("out", "w") {|f| h = {STDOUT=>f, f=>STDOUT} 3.upto(30) {|i| h[i] = STDOUT if f.fileno != i } @@ -410,6 +416,35 @@ } end + def test_execopts_redirect_dup2_child + with_tmpchdir {|d| + Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", + STDOUT=>"out", STDERR=>[:child, STDOUT]) + assert_equal("errout", File.read("out")) + + Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", + STDERR=>"out", STDOUT=>[:child, STDERR]) + assert_equal("errout", File.read("out")) + + Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", + STDOUT=>"out", + STDERR=>[:child, 3], + 3=>[:child, 4], + 4=>[:child, STDOUT] + ) + assert_equal("errout", File.read("out")) + + IO.popen([RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", STDERR=>[:child, STDOUT]]) {|io| + assert_equal("errout", io.read) + } + + assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, STDOUT]) } + assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 3]) } + assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 5], 5=>[:child, 3]) } + assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, 3]) } + } + end + def test_execopts_exec with_tmpchdir {|d| write_file("s", 'exec "echo aaa", STDOUT=>"foo"') @@ -461,6 +496,7 @@ end def test_fd_inheritance + skip "inheritance of fd>=3 is not supported" if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM with_pipe {|r, w| system(RUBY, '-e', 'IO.new(ARGV[0].to_i).puts(:ba)', w.fileno.to_s) w.close @@ -564,14 +600,18 @@ end def test_execopts_redirect_self - with_pipe {|r, w| - w << "haha\n" - w.close - r.close_on_exec = true - IO.popen([RUBY, "-e", "print IO.new(#{r.fileno}).read", r.fileno=>r.fileno, :close_others=>false]) {|io| - assert_equal("haha\n", io.read) + begin + with_pipe {|r, w| + w << "haha\n" + w.close + r.close_on_exec = true + IO.popen([RUBY, "-e", "print IO.new(#{r.fileno}).read", r.fileno=>r.fileno, :close_others=>false]) {|io| + assert_equal("haha\n", io.read) + } } - } + rescue NotImplementedError + skip "IO#close_on_exec= is not supported" + end end def test_execopts_duplex_io Index: mvm/test/ruby/test_complex.rb =================================================================== --- mvm/test/ruby/test_complex.rb (revision 20561) +++ mvm/test/ruby/test_complex.rb (revision 20562) @@ -27,7 +27,7 @@ c2 = c - 1 assert_instance_of(ComplexSub, c2) - c3 = c - c + c3 = c - c2 assert_instance_of(ComplexSub, c3) s = Marshal.dump(c) @@ -71,6 +71,12 @@ h[Complex(0.0,0.0)] = 9.0 assert_equal(5, h.size) + + if (0.0/0).nan? && !((0.0/0).eql?(0.0/0)) + h = {} + 3.times{h[Complex(0.0/0)] = 1} + assert_equal(3, h.size) + end end def test_freeze @@ -124,6 +130,13 @@ assert_raise(ArgumentError){Complex(Object.new)} assert_raise(ArgumentError){Complex()} assert_raise(ArgumentError){Complex(1,2,3)} + + if (0.0/0).nan? + assert_nothing_raised{Complex(0.0/0)} + end + if (1.0/0).infinite? + assert_nothing_raised{Complex(1.0/0)} + end end def test_attr @@ -483,6 +496,13 @@ assert_equal(true, Complex(2,1) != Complex(1)) assert_equal(false, Complex(1) == nil) assert_equal(false, Complex(1) == '') + + nan = 0.0 / 0 + if nan.nan? && nan != nan + assert_equal(false, Complex(nan, 0) == Complex(nan, 0)) + assert_equal(false, Complex(0, nan) == Complex(0, nan)) + assert_equal(false, Complex(nan, nan) == Complex(nan, nan)) + end end def test_unify @@ -564,6 +584,16 @@ assert_equal('1-2/3i', Complex(1,Rational(-2,3)).to_s) assert_equal('-1-2/3i', Complex(-1,Rational(-2,3)).to_s) end + + nan = 0.0 / 0 + inf = 1.0 / 0 + if nan.nan? + assert_equal('NaN+NaN*i', Complex(nan,nan).to_s) + end + if inf.infinite? + assert_equal('Infinity+Infinity*i', Complex(inf,inf).to_s) + assert_equal('Infinity-Infinity*i', Complex(inf,-inf).to_s) + end end def test_inspect @@ -812,6 +842,13 @@ c = Complex(1,2).to_c assert_equal([1, 2], [c.real, c.imag]) + + if (0.0/0).nan? + assert_nothing_raised{(0.0/0).to_c} + end + if (1.0/0).infinite? + assert_nothing_raised{(1.0/0).to_c} + end end def test_supp Index: mvm/test/ruby/test_rational.rb =================================================================== --- mvm/test/ruby/test_rational.rb (revision 20561) +++ mvm/test/ruby/test_rational.rb (revision 20562) @@ -129,6 +129,13 @@ assert_raise(ArgumentError){Rational(Object.new)} assert_raise(ArgumentError){Rational()} assert_raise(ArgumentError){Rational(1,2,3)} + + if (0.0/0).nan? + assert_raise(FloatDomainError){Rational(0.0/0)} + end + if (1.0/0).infinite? + assert_raise(FloatDomainError){Rational(1.0/0)} + end end def test_attr @@ -935,6 +942,13 @@ assert_raise(RangeError){Complex(1,2).to_r} end end + + if (0.0/0).nan? + assert_raise(FloatDomainError){(0.0/0).to_r} + end + if (1.0/0).infinite? + assert_raise(FloatDomainError){(1.0/0).to_r} + end end def test_gcdlcm Index: mvm/test/test_open3.rb =================================================================== --- mvm/test/test_open3.rb (revision 0) +++ mvm/test/test_open3.rb (revision 20562) @@ -0,0 +1,234 @@ +require 'test/unit' +require 'open3' +require 'shellwords' +require_relative 'ruby/envutil' + +class TestOpen3 < Test::Unit::TestCase + RUBY = EnvUtil.rubybin + + def test_exit_status + Open3.popen3(RUBY, '-e', 'exit true') {|i,o,e,t| + assert_equal(true, t.value.success?) + } + Open3.popen3(RUBY, '-e', 'exit false') {|i,o,e,t| + assert_equal(false, t.value.success?) + } + end + + def test_stdin + Open3.popen3(RUBY, '-e', 'exit STDIN.gets.chomp == "t"') {|i,o,e,t| + i.puts 't' + assert_equal(true, t.value.success?) + } + Open3.popen3(RUBY, '-e', 'exit STDIN.gets.chomp == "t"') {|i,o,e,t| + i.puts 'f' + assert_equal(false, t.value.success?) + } + end + + def test_stdout + Open3.popen3(RUBY, '-e', 'STDOUT.print "foo"') {|i,o,e,t| + assert_equal("foo", o.read) + } + end + + def test_stderr + Open3.popen3(RUBY, '-e', 'STDERR.print "bar"') {|i,o,e,t| + assert_equal("bar", e.read) + } + end + + def test_block + r = Open3.popen3(RUBY, '-e', 'STDOUT.print STDIN.read') {|i,o,e,t| + i.print "baz" + i.close + assert_equal("baz", o.read) + "qux" + } + assert_equal("qux", r) + end + + def test_noblock + i,o,e,t = Open3.popen3(RUBY, '-e', 'STDOUT.print STDIN.read') + i.print "baz" + i.close + assert_equal("baz", o.read) + ensure + i.close if !i.closed? + o.close if !o.closed? + e.close if !e.closed? + end + + def test_commandline + commandline = Shellwords.join([RUBY, '-e', 'print "quux"']) + Open3.popen3(commandline) {|i,o,e,t| + assert_equal("quux", o.read) + } + end + + def test_pid + Open3.popen3(RUBY, '-e', 'print $$') {|i,o,e,t| + pid = o.read.to_i + assert_equal(pid, t[:pid]) + assert_equal(pid, t.pid) + } + end + + def with_pipe + r, w = IO.pipe + yield r, w + ensure + r.close if !r.closed? + w.close if !w.closed? + end + + def with_reopen(io, arg) + old = io.dup + io.reopen(arg) + yield old + ensure + io.reopen(old) + old.close if old && !old.closed? + end + + def test_popen2 + with_pipe {|r, w| + with_reopen(STDERR, w) {|old| + w.close + Open3.popen2(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDERR.print s+"e"') {|i,o,t| + assert_kind_of(Thread, t) + i.print "z" + i.close + STDERR.reopen(old) + assert_equal("zo", o.read) + assert_equal("ze", r.read) + } + } + } + end + + def test_popen2e + with_pipe {|r, w| + with_reopen(STDERR, w) {|old| + w.close + Open3.popen2e(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDOUT.flush; STDERR.print s+"e"') {|i,o,t| + assert_kind_of(Thread, t) + i.print "y" + i.close + STDERR.reopen(old) + assert_equal("yoye", o.read) + assert_equal("", r.read) + } + } + } + end + + def test_poutput3 + o, e, s = Open3.poutput3(RUBY, '-e', 'i=STDIN.read; print i+"o"; STDOUT.flush; STDERR.print i+"e"', :stdin_data=>"i") + assert_equal("io", o) + assert_equal("ie", e) + assert(s.success?) + end + + def test_poutput3_flip + o, e, s = Open3.poutput3(RUBY, '-e', 'STDOUT.sync=true; 1000.times { print "o"*1000; STDERR.print "e"*1000 }') + assert_equal("o"*1000000, o) + assert_equal("e"*1000000, e) + assert(s.success?) + end + + def test_poutput2 + o, s = Open3.poutput2(RUBY, '-e', 'i=STDIN.read; print i+"o"', :stdin_data=>"i") + assert_equal("io", o) + assert(s.success?) + end + + def test_poutput2e + oe, s = Open3.poutput2e(RUBY, '-e', 'i=STDIN.read; print i+"o"; STDOUT.flush; STDERR.print i+"e"', :stdin_data=>"i") + assert_equal("ioie", oe) + assert(s.success?) + end + + def test_pipeline_rw + Open3.pipeline_rw([RUBY, '-e', 'print STDIN.read + "1"'], + [RUBY, '-e', 'print STDIN.read + "2"']) {|i,o,ts| + assert_kind_of(IO, i) + assert_kind_of(IO, o) + assert_kind_of(Array, ts) + assert_equal(2, ts.length) + ts.each {|t| assert_kind_of(Thread, t) } + i.print "0" + i.close + assert_equal("012", o.read) + ts.each {|t| + assert(t.value.success?) + } + } + end + + def test_pipeline_r + Open3.pipeline_r([RUBY, '-e', 'print "1"'], + [RUBY, '-e', 'print STDIN.read + "2"']) {|o,ts| + assert_kind_of(IO, o) + assert_kind_of(Array, ts) + assert_equal(2, ts.length) + ts.each {|t| assert_kind_of(Thread, t) } + assert_equal("12", o.read) + ts.each {|t| + assert(t.value.success?) + } + } + end + + def test_pipeline_w + command = [RUBY, '-e', 's=STDIN.read; print s[1..-1]; exit s[0] == ?t'] + str = 'ttftff' + Open3.pipeline_w(*[command]*str.length) {|i,ts| + assert_kind_of(IO, i) + assert_kind_of(Array, ts) + assert_equal(str.length, ts.length) + ts.each {|t| assert_kind_of(Thread, t) } + i.print str + i.close + ts.each_with_index {|t, i| + assert_equal(str[i] == ?t, t.value.success?) + } + } + end + + def test_pipeline_start + command = [RUBY, '-e', 's=STDIN.read; print s[1..-1]; exit s[0] == ?t'] + str = 'ttftff' + Open3.pipeline_start([RUBY, '-e', 'print ARGV[0]', str], + *([command]*str.length)) {|ts| + assert_kind_of(Array, ts) + assert_equal(str.length+1, ts.length) + ts.each {|t| assert_kind_of(Thread, t) } + ts.each_with_index {|t, i| + if i == 0 + assert(t.value.success?) + else + assert_equal(str[i-1] == ?t, t.value.success?) + end + } + } + end + + def test_pipeline + command = [RUBY, '-e', 's=STDIN.read; print s[1..-1]; exit s[0] == ?t'] + str = 'ttftff' + ss = Open3.pipeline([RUBY, '-e', 'print ARGV[0]', str], + *([command]*str.length)) + assert_kind_of(Array, ss) + assert_equal(str.length+1, ss.length) + ss.each {|s| assert_kind_of(Process::Status, s) } + ss.each_with_index {|s, i| + if i == 0 + assert(s.success?) + else + assert_equal(str[i-1] == ?t, s.success?) + end + } + end + +end Property changes on: mvm/test/test_open3.rb ___________________________________________________________________ Name: svn:eol-style + LF Name: svn:keywords + Author Id Revision Index: mvm/test/cgi/test_cgi_session.rb =================================================================== --- mvm/test/cgi/test_cgi_session.rb (revision 20561) +++ mvm/test/cgi/test_cgi_session.rb (revision 20562) @@ -7,7 +7,7 @@ class CGISessionTest < Test::Unit::TestCase def setup - @session_dir = Dir.mktmpdir('__test_dir__')+'/session_dir/' + @session_dir = Dir.tmpdir+'/__test_dir__/session_dir/' FileUtils.mkdir_p @session_dir end Index: mvm/test/openssl/test_ssl.rb =================================================================== --- mvm/test/openssl/test_ssl.rb (revision 20561) +++ mvm/test/openssl/test_ssl.rb (revision 20562) @@ -166,6 +166,21 @@ assert_equal(ctx.setup, nil) end + def test_ssl_read_nonblock + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) { |server, port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + ssl.connect + assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { ssl.read_nonblock(100) } + ssl.write("abc\n") + IO.select [ssl] + assert_equal('a', ssl.read_nonblock(1)) + assert_equal("bc\n", ssl.read_nonblock(100)) + assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { ssl.read_nonblock(100) } + } + end + def test_connect_and_close start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| sock = TCPSocket.new("127.0.0.1", port) Index: mvm/test/socket/test_tcp.rb =================================================================== --- mvm/test/socket/test_tcp.rb (revision 20561) +++ mvm/test/socket/test_tcp.rb (revision 20562) @@ -7,7 +7,6 @@ class TestTCPSocket < Test::Unit::TestCase def test_recvfrom -assert false, "TODO: doesn't work on mswin32/64" if /mswin/ =~ RUBY_PLATFORM svr = TCPServer.new("localhost", 0) th = Thread.new { c = svr.accept @@ -15,11 +14,11 @@ c.close } addr = svr.addr - sock = TCPSocket.open(addr[2], addr[1]) + sock = TCPSocket.open(addr[3], addr[1]) assert_equal(["foo", nil], sock.recvfrom(0x10000)) ensure - th.kill - th.join + th.kill if th + th.join if th end def test_encoding @@ -30,14 +29,14 @@ c.close } addr = svr.addr - sock = TCPSocket.open(addr[2], addr[1]) + sock = TCPSocket.open(addr[3], addr[1]) assert_equal(true, sock.binmode?) s = sock.gets assert_equal("foo\r\n", s) assert_equal(Encoding.find("ASCII-8BIT"), s.encoding) ensure - th.kill - th.join + th.kill if th + th.join if th sock.close if sock end end if defined?(TCPSocket) Index: mvm/rational.c =================================================================== --- mvm/rational.c (revision 20561) +++ mvm/rational.c (revision 20562) @@ -25,8 +25,8 @@ #define TWO INT2FIX(2) static ID id_abs, id_cmp, id_convert, id_equal_p, id_expt, id_floor, - id_format, id_hash, id_idiv, id_inspect, id_integer_p, id_negate, - id_to_f, id_to_i, id_to_s, id_truncate; + id_hash, id_idiv, id_inspect, id_integer_p, id_negate, id_to_f, + id_to_i, id_to_s, id_truncate; #define f_boolcast(x) ((x) ? Qtrue : Qfalse) @@ -1109,19 +1109,34 @@ } static VALUE -nurat_to_s(VALUE self) +nurat_format(VALUE self, VALUE (*func)(VALUE)) { + VALUE s; get_dat1(self); - return rb_funcall(rb_mKernel, id_format, 3, - rb_str_new2("%d/%d"), dat->num, dat->den); + + s = (*func)(dat->num); + rb_str_cat2(s, "/"); + rb_str_concat(s, (*func)(dat->den)); + + return s; } static VALUE +nurat_to_s(VALUE self) +{ + return nurat_format(self, f_to_s); +} + +static VALUE nurat_inspect(VALUE self) { - get_dat1(self); - return rb_funcall(rb_mKernel, id_format, 3, - rb_str_new2("(%d/%d)"), dat->num, dat->den); + VALUE s; + + s = rb_str_new2("("); + rb_str_concat(s, nurat_format(self, f_inspect)); + rb_str_cat2(s, ")"); + + return s; } static VALUE @@ -1488,7 +1503,6 @@ id_equal_p = rb_intern("=="); id_expt = rb_intern("**"); id_floor = rb_intern("floor"); - id_format = rb_intern("format"); id_hash = rb_intern("hash"); id_idiv = rb_intern("div"); id_inspect = rb_intern("inspect"); Index: mvm/signal.c =================================================================== --- mvm/signal.c (revision 20561) +++ mvm/signal.c (revision 20562) @@ -16,6 +16,7 @@ #include "eval_intern.h" #include <signal.h> #include <stdio.h> +#include <errno.h> #ifdef _WIN32 typedef LONG rb_atomic_t; @@ -447,8 +448,6 @@ if (sigaltstack(&newSS, &oldSS) < 0) rb_bug("register_sigaltstack. error\n"); } -#else -#define register_sigaltstack() ((void)0) #endif static void @@ -477,8 +476,11 @@ if (altstack) sigact.sa_flags |= SA_ONSTACK; #endif - if (sigaction(signum, &sigact, old) < 0) - rb_bug("sigaction error.\n"); + if (sigaction(signum, &sigact, old) < 0) { + if (errno != 0 && errno != EINVAL) { + rb_bug("sigaction error.\n"); + } + } } static sighandler_t @@ -739,7 +741,9 @@ #ifdef SIGSEGV case SIGSEGV: func = (sighandler_t)sigsegv; +# ifdef USE_SIGALTSTACK register_sigaltstack(); +# endif break; #endif #ifdef SIGPIPE @@ -1134,8 +1138,10 @@ #endif } #ifdef SIGSEGV +# ifdef USE_SIGALTSTACK register_sigaltstack(); ruby_sigaction(SIGSEGV, sigsegv, Qtrue, NULL); +# endif #endif #ifdef SIGPIPE install_sighandler(SIGPIPE, sigpipe); -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/