diff --git a/.travis.yml b/.travis.yml index 582835a5948c3f..87a83ce0849fd0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,6 @@ language: c # The value set here is visible via $CC environment variable. compiler: - gcc - - clang # Dependencies. Some header files are missing in a Travis' worker VM, so we # have to install them. The "1.9.1" here is OK. It is the most adopted @@ -41,8 +40,7 @@ before_script: - "autoconf" - "./configure --with-gcc=$CC" - "make -sj encs" - - "make -sj exts" -script: "make test" +script: "make test TESTOPTS='-v --sets=thread'" # Branch matrix. Not all branches are Travis-ready so we limit branches here. branches: @@ -54,10 +52,9 @@ branches: notifications: irc: channels: - - "irc.freenode.org#ruby-core" - "irc.freenode.org#ruby-ja" - on_success: change # [always|never|change] # default: always - on_failure: change # [always|never|change] # default: always + on_success: always # [always|never|change] # default: always + on_failure: always # [always|never|change] # default: always template: - "%{message} by @%{author}: See %{build_url}" diff --git a/ChangeLog b/ChangeLog index c847055198820d..4e8fa65845b690 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,329 @@ +Tue Oct 9 17:13:27 2012 Nobuyoshi Nakada + + * process.c (rb_execarg_addopt, rb_execarg_run_options): add :uid and + :gid options. [ruby-core:47414] [Feature #6975] + +Tue Oct 9 14:36:11 2012 Koichi Sasada + + * iseq.c (iseq_free): fix memory leak. + rb_iseq_t::callinfo_entries should be freed. + +Tue Oct 9 14:28:18 2012 Koichi Sasada + + * vm_core.h (rb_call_info_t): add new type `rb_call_info_t'. + This data structure contains information including inline method + cache. After that, `struct iseq_inline_cache_entry' does not + need to contain inline cache for method invocation. + Other information will be added to this data structure. + + * vm_core.h (rb_iseq_t): add `callinfo_entries' and `callinfo_size' + members to `rb_iseq_t'. + + * insns.def, compile.c: Use CALL_INFO instead of IC. + + * tool/instruction.rb: support CALL_INFO as operand type. + + * vm_insnhelper.c, vm_insnhelper.h: ditto. + +Sun Oct 7 23:54:33 2012 CHIKANAGA Tomoyuki + + * ext/zlib/zlib.c (zstream_run_func): don't call inflate() when + z->stream.avail_in == 0. it return Z_BUF_ERROR. + but deflate() could be called with z->stream->avail_in == 0 because + it has hidden buffer in z->stream->state (opaque structure). + fix for gem install error. [ruby-dev:46149] [Bug #7040] + +Mon Oct 8 23:55:41 2012 Shugo Maeda + + * eval.c (rb_mod_refinements): new method Module#refinements. + + * test/ruby/test_refinement.rb: add new tests for the above changes. + +Mon Oct 8 23:02:19 2012 Shugo Maeda + + * eval.c, gc.c, iseq.c, node.h, vm_insnhelper.c, vm_insnhelper.h, + vm_method.c: rename omod and overlaid modules to refinements. + + * eval.c (hidden_identity_hash_new): renamed from identity_hash_new. + +Sun Oct 7 04:50:00 2012 Zachary Scott + + * lib/abbrev.rb: Documentation examples for Abbrev. + [ruby-core:47442] [Bug #6985] + +Sun Oct 7 04:50:00 2012 Zachary Scott + + * thread.c (rb_thread_aref): + Grammar in Thread documentation. + Patch by Steve Klabnik [ruby-core:47799] [Bug #7099] + +Sun Oct 7 04:37:00 2012 Zachary Scott + + * string.c (rb_str_match): + Clarify behavior for captured strings and local variable assignment + Patch by Marcus Stollsteimer [ruby-core:47668] [Bug #7062] + +Sat Oct 6 18:31:36 2012 Shugo Maeda + + * vm_opts.h (OPT_GLOBAL_METHOD_CACHE): new build option to + enable/disable global method caching. [ruby-dev:46203] [Bug #7111] + + * vm_method.c (rb_method_entry_get_with_omod): don't use global + method cache if OPT_GLOBAL_METHOD_CACHE is 0. + +Sat Oct 6 16:32:04 2012 Shugo Maeda + + * vm_method.c (search_method): check omod only once for performance. + +Sat Oct 6 09:42:04 2012 Nobuyoshi Nakada + + * enc/encdb.c, enc/utf_16_32.h (ENC_DUMMY_UNICODE): endian-less wide + UTF encodings are dummy but Unicode. + + * encoding.c (rb_encdb_set_unicode): set Unicode flag. + + * template/encdb.h.tmpl: allow ENC_DUMMY variants. + + * encoding.c (rb_enc_unicode_p): oniguruma provides Unicode flag. + +Fri Oct 5 17:18:42 JST 2012 TAKANO Mitsuhiro + + * template/Doxyfile.tmpl: remove SHOW_DIRECTORIES and + HTML_ALIGN_MEMBERS lines. They have been obsolete in + Doxygen version 1.8.2. + +Fri Oct 05 15:26:18 2012 Koichi Sasada + + * ext/objspace/objspace.c: add ObjectSpace#reachable_objects_from. + This method returns an array of objects referenced by given object. + If given object is special objects such as true/false/nil/Fixnum etc + then it returns nil. See rdoc for details. + [ruby-core:39772] + + * test/objspace/test_objspace.rb: add a test for this method. + + * gc.c: add rb_objspace_reachable_objects_from(). + To make this function, add several member `mark_func_data' + to rb_objspace_t. If mark_func_data is not null, then + gc_mark() calls mark_func_data::mark_func. + + * gc.h: export rb_objspace_reachable_objects_from(). + +Thu Oct 4 23:40:04 2012 Narihiro Nakamura + + * gc.c (init_heap): call init_mark_stack before to allocate + altstack. This change avoid the stack overflow at the signal + handler on 32bit, but I don't understand reason... [Feature #7095] + +Thu Oct 4 22:39:27 2012 Koichi Sasada + + * insns.def (getlocal, setlocal): remove old getlocal/setlocal + instructions and rename getdaynmic/setdynamic instructions + to getlocal/setlocal. + + * compile.c: ditto. + + * iseq.c: remove TS_DINDEX. + + * vm_exec.h (dindex_t): remove type definition of `dindex_t'. + + * tool/instruction.rb: ditto. + +Thu Oct 4 21:44:17 2012 Koichi Sasada + + * vm.c (vm_analysis_insn|operand|register): use st_insert + instead of using rb_hash_aset() because rb_hash_aset() + check $SAFE. + +Thu Oct 4 21:15:26 2012 Koichi Sasada + + * vm.c (VM_COLLECT_USAGE_DETAILS): make new VM usage analysis + hooks (old macro name is COLLECT_USAGE_ANALYSIS). + This feature is only for VM developers. (I'm not sure I can use + `VM developers' (the plural form) in this sentence). + If VM_COLLECT_USAGE_DETAILS is not 0, VM enables the following + usage collection features: + (1) instruction: collect instruction usages. + (2) operand: collect operand usages. + (3) register: collect register usages. + The results are stored in + RubyVM::USAGE_ANALYSIS_INSN for (1, 2), + RubyVM::USAGE_ANALYSIS_INSN_BIGRAM for (1) and + RubyVM::USAGE_ANALYSIS_REGS for (3). + You can stop collecting usages with + RubyVM::USAGE_ANALYSIS_INSN_STOP(), + RubyVM::USAGE_ANALYSIS_OPERAND_STOP(), + RubyVM::USAGE_ANALYSIS_REGISTER_STOP() + for (1), (2), (3) respectively. + You can also change the hook functions by setting + C level global variables + `ruby_vm_collect_usage_func_(insn|operand|register)' + for (1), (2), (3) respectively. + See codes for more details. + + * tool/instruction.rb: fix macro names. + + * iseq.c (insn_operand_intern): make it export (used in vm.c). + fix to skip several processes if not needed (pointer is 0). + + * vm_dump.c: move codes for collection features to vm.c. + + * vm_exec.h: rename macro and function names. + + * vm_insnhelper.h: ditto. + +Thu Oct 4 18:59:14 2012 Koichi Sasada + + * test/ruby/test_settracefunc.rb (test_tracepoint): + remove unused test case. + (this test case is redefined by newer tests) + +Thu Oct 4 17:24:51 2012 Narihiro Nakamura + + * gc.c (rb_objspace_call_finalizer): call gc_mark_stacked_objects + at suitable point. + +Thu Oct 4 16:31:29 2012 Nobuyoshi Nakada + + * gc.c (rb_objspace_call_finalizer): mark self-referencing finalizers + before run finalizers, to fix SEGV from btest on 32bit. + + * gc.c (gc_mark_stacked_objects): extract from gc_marks(). + +Thu Oct 4 11:43:28 2012 Nobuyoshi Nakada + + * thread_pthread.c (ruby_init_stack): round stack limit to page size + boundary to calculate stack size more precisely. [ruby-dev:46174] + [Bug #7084] + +Wed Oct 3 19:51:57 2012 Narihiro Nakamura + + * gc.c: Use the non-recursive marking instead of recursion. The + recursion marking of CRuby needs checking stack overflow and the + fail-safe system, but these systems not good at partial points, + for example, marking deep tree structures. [ruby-dev:46184] + [Feature #7095] + + * configure.in (GC_MARK_STACKFRAME_WORD): removed. It's used by + checking stack overflow of marking. + + * win32/Makefile.sub (GC_MARK_STACKFRAME_WORD): ditto. + +Wed Oct 3 15:33:02 2012 Nobuyoshi Nakada + + * thread_pthread.c (ruby_init_stack): use getrlimit() for the main + thread on Mac OS X, since pthread_get_stack{addr,size}_np() + return the default value always, but not the ulimit value. + [ruby-dev:46174] [Bug #7084] + +Wed Oct 3 11:43:15 2012 Nobuyoshi Nakada + + * io.c (rb_io_reopen): improvement to accept optional arguments. + a patch by Glass_saga (Masaki Matsushita) in [ruby-core:47806]. + [Feature #7103] + +Wed Oct 3 04:36:11 2012 Eric Hodel + + * ext/openssl/ossl_x509store.c (ossl_x509store_add_file): Added + documentation + * ext/openssl/ossl_x509store.c (ossl_x509store_set_default_paths): + ditto + * ext/openssl/ossl_x509store.c (ossl_x509store_add_cert): ditto + +Wed Oct 3 02:23:37 2012 Shugo Maeda + + * error.c (exc_to_s, name_err_to_s, name_err_mesg_to_str): do not + taint messages. + +Tue Oct 2 16:47:06 2012 Nobuyoshi Nakada + + * eval.c (identity_hash_new): hide internal hashes for refinements. + + * eval.c (rb_mod_refine): no default value. + +Mon Oct 1 22:54:02 2012 Shugo Maeda + + * eval.c (identity_hash_new): new function to create a new identity + hash. + + * eval.c (rb_overlay_module, rb_mod_using, rb_mod_refine): use + identity_hash_new(). + +Mon Oct 1 02:34:53 2012 Akinori MUSHA + + * configure.in (--with-opt-dir): Make this also work on DLDFLAGS + so LIBRUBY_SO links fine with libexecinfo installed in a + non-system directory. + +Sun Sep 30 23:32:00 2012 Kenta Murata + + * vm_dump.c (rb_vm_bugreport): add /Library/Logs/DiagnosticReports + in the list of locations of crash reports. + +Sun Sep 30 21:18:03 2012 NARUSE, Yui + + * string.c (rb_str_concat): use memcpy to copy a string which contains + NUL characters. [ruby-core:47751] [Bug #7090] + +Sat Sep 29 19:41:53 2012 Hiroshi Shirosaki + + * test/ruby/envutil.rb (EnvUtil#invoke_ruby): kill child process + before Timeout::Error is raised. rmdir of mktmpdir fails with + EACCES if child process is alive on Windows. + + * test/thread/test_queue.rb (TestQueue): increase timeout. + This test takes long time on Windows XP. + +Sat Sep 29 19:41:33 2012 Hiroshi Shirosaki + + * test/net/http/test_http.rb (TestNetHTTP#test_proxy_address): + clear environment variables. If http_proxy environment variable was + set, the test failed. + + * test/net/http/test_http.rb (TestNetHTTP#test_proxy_port): ditto. + +Sat Sep 29 19:41:11 2012 Hiroshi Shirosaki + + * test/drb/drbtest.rb (DRbCore#teardown): + Use Process.kill :KILL on Windows because Process.kill :INT silently + fails on Windows 7 and raises EINVAL on Windows XP for spawned + process with new_pgroup: false. + + * test/drb/drbtest.rb (DRbAry#teardown): ditto. + +Sat Sep 29 19:40:32 2012 Hiroshi Shirosaki + + * test/ruby/test_unicode_escape.rb (TestUnicodeEscape#test_basic): + set script encoding to work with LANG=C. It would work on both + Windows and Unix. Refix of r37051. + +Sat Sep 29 11:21:06 2012 Shugo Maeda + + * vm_insnhelper.c (rb_vm_using_modules): use using_modules before + klass to fix method lookup order, and use klass even if klass is + not a module to make refinements in class_eval invoked on classes + work. + + * eval.c (rb_using_module): accept a class as the second argument. + + * eval.c (rb_mod_using, f_using): raise a TypeError if the argument + is not a module. + + * test/ruby/test_refinement.rb: add new tests for the above changes. + +Sat Sep 29 02:18:57 2012 Hiroshi Shirosaki + + * test/ruby/test_unicode_escape.rb (TestUnicodeEscape#test_basic): + Use ruby only on Windows since the test fails on Unix with LANG=C. + [ruby-core:47709] [Bug #7076] + +Fri Sep 28 22:19:31 2012 Hiroshi Shirosaki + + * test/ruby/test_unicode_escape.rb (TestUnicodeEscape#test_basic): + echo command doesn't work properly against non-ascii character on + Windows with chcp 437. Instead we use ruby. + [ruby-core:47709] [Bug #7076] + Fri Sep 28 17:54:31 2012 Koichi Sasada * vm_insnhelper.c (vm_setup_method): refactoring. diff --git a/Makefile.in b/Makefile.in index 8087065bea7812..8d36063c975d6c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -89,7 +89,9 @@ PROGRAM=$(RUBY_INSTALL_NAME)$(EXEEXT) RUBY = $(RUBY_INSTALL_NAME) MINIRUBY = @MINIRUBY@\ $(MINIRUBYOPT) -RUNRUBY = @RUNRUBY@ $(RUNRUBYOPT) -- $(RUN_OPTS) +RUNRUBY_COMMAND = @RUNRUBY@ $(RUNRUBYOPT) +RUNRUBY = $(RUNRUBY_COMMAND) -- $(RUN_OPTS) +RUNRUBY_DEBUGGER = --debugger='gdb -x run.gdb --quiet --args' XRUBY = @XRUBY@ BTESTRUBY = @BTESTRUBY@\ $(MINIRUBYOPT) diff --git a/common.mk b/common.mk index 910948e2d121f7..d2818f68085707 100644 --- a/common.mk +++ b/common.mk @@ -954,7 +954,7 @@ gdb: miniruby$(EXEEXT) run.gdb PHONY gdb -x run.gdb --quiet --args $(MINIRUBY) $(TESTRUN_SCRIPT) gdb-ruby: $(PROGRAM) run.gdb PHONY - gdb -x run.gdb --quiet --args $(PROGRAM) $(TESTRUN_SCRIPT) + $(Q) $(RUNRUBY_COMMAND) $(RUNRUBY_DEBUGGER) -- $(TESTRUN_SCRIPT) dist: $(BASERUBY) $(srcdir)/tool/make-snapshot tmp $(RELNAME) diff --git a/compile.c b/compile.c index 535084b8b7420f..e03d0f9ac6dfc0 100644 --- a/compile.c +++ b/compile.c @@ -535,7 +535,7 @@ rb_iseq_compile_node(VALUE self, NODE *node) } if (iseq->type == ISEQ_TYPE_RESCUE || iseq->type == ISEQ_TYPE_ENSURE) { - ADD_INSN2(ret, 0, getdynamic, INT2FIX(2), INT2FIX(0)); + ADD_INSN2(ret, 0, getlocal, INT2FIX(2), INT2FIX(0)); ADD_INSN1(ret, 0, throw, INT2FIX(0) /* continue throw */ ); } else { @@ -946,7 +946,7 @@ new_insn_send(rb_iseq_t *iseq, int line_no, operands[1] = argc; operands[2] = block; operands[3] = flag; - operands[4] = INT2FIX(iseq->ic_size++); + operands[4] = INT2FIX(iseq->callinfo_size++); iobj = new_insn_core(iseq, line_no, BIN(send), 5, operands); return iobj; } @@ -1031,6 +1031,17 @@ iseq_set_exception_local_table(rb_iseq_t *iseq) return COMPILE_OK; } +static int +get_lvar_level(rb_iseq_t *iseq) +{ + int lev = 0; + while (iseq != iseq->local_iseq) { + lev++; + iseq = iseq->parent_iseq; + } + return lev; +} + static int get_dyna_var_idx_at_raw(rb_iseq_t *iseq, ID id) { @@ -1387,6 +1398,8 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) line_info_table = ALLOC_N(struct iseq_line_info_entry, k); iseq->ic_entries = ALLOC_N(struct iseq_inline_cache_entry, iseq->ic_size); MEMZERO(iseq->ic_entries, struct iseq_inline_cache_entry, iseq->ic_size); + iseq->callinfo_entries = ALLOC_N(rb_call_info_t, iseq->callinfo_size); + MEMZERO(iseq->callinfo_entries, rb_call_info_t, iseq->callinfo_size); list = FIRST_ELEMENT(anchor); k = pos = sp = 0; @@ -1458,7 +1471,6 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) break; } case TS_LINDEX: - case TS_DINDEX: case TS_NUM: /* ulong */ generated_iseq[pos + 1 + j] = FIX2INT(operands[j]); break; @@ -1485,12 +1497,21 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) int ic_index = FIX2INT(operands[j]); IC ic = &iseq->ic_entries[ic_index]; if (UNLIKELY(ic_index >= iseq->ic_size)) { - rb_bug("iseq_set_sequence: ic_index overflow: index: %d, size: %d", - ic_index, iseq->ic_size); + rb_bug("iseq_set_sequence: ic_index overflow: index: %d, size: %d", ic_index, iseq->ic_size); } generated_iseq[pos + 1 + j] = (VALUE)ic; break; } + case TS_CALLINFO: /* call info */ + { + int ci_index = FIX2INT(operands[j]); + CALL_INFO ci = &iseq->callinfo_entries[ci_index]; + if (UNLIKELY(ci_index >= iseq->callinfo_size)) { + rb_bug("iseq_set_sequence: ci_index overflow: index: %d, size: %d", ci_index, iseq->callinfo_size); + } + generated_iseq[pos + 1 + j] = (VALUE)ci; + break; + } case TS_ID: /* ID */ generated_iseq[pos + 1 + j] = SYM2ID(operands[j]); break; @@ -1849,7 +1870,7 @@ insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id) } for (i=0; ioperand_size; i++) { - iobj->operands[i] = INT2FIX(iseq->ic_size++); + iobj->operands[i] = INT2FIX(iseq->callinfo_size++); } return COMPILE_OK; @@ -3686,7 +3707,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) switch (nd_type(narg)) { case NODE_ARRAY: while (narg) { - ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(2), INT2FIX(0)); + ADD_INSN2(ret, nd_line(node), getlocal, INT2FIX(2), INT2FIX(0)); COMPILE(ret, "rescue arg", narg->nd_head); ADD_INSN1(ret, nd_line(node), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE)); ADD_INSNL(ret, nd_line(node), branchif, label_hit); @@ -3696,7 +3717,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) case NODE_SPLAT: case NODE_ARGSCAT: case NODE_ARGSPUSH: - ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(2), INT2FIX(0)); + ADD_INSN2(ret, nd_line(node), getlocal, INT2FIX(2), INT2FIX(0)); COMPILE(ret, "rescue/cond splat", narg); ADD_INSN1(ret, nd_line(node), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY)); ADD_INSNL(ret, nd_line(node), branchif, label_hit); @@ -3707,7 +3728,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } } else { - ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(2), INT2FIX(0)); + ADD_INSN2(ret, nd_line(node), getlocal, INT2FIX(2), INT2FIX(0)); ADD_INSN1(ret, nd_line(node), putobject, rb_eStandardError); ADD_INSN1(ret, nd_line(node), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE)); ADD_INSNL(ret, nd_line(node), branchif, label_hit); @@ -3804,7 +3825,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) if (!poped) { ADD_INSN(ret, nd_line(node), dup); } - ADD_INSN1(ret, nd_line(node), setlocal, INT2FIX(idx)); + ADD_INSN2(ret, nd_line(node), setlocal, INT2FIX(idx), INT2FIX(get_lvar_level(iseq))); break; } @@ -3824,8 +3845,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) rb_bug("NODE_DASGN(_CURR): unknown id (%s)", rb_id2name(node->nd_vid)); } - ADD_INSN2(ret, nd_line(node), setdynamic, - INT2FIX(ls - idx), INT2FIX(lv)); + ADD_INSN2(ret, nd_line(node), setlocal, INT2FIX(ls - idx), INT2FIX(lv)); break; } case NODE_GASGN:{ @@ -4277,13 +4297,14 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) /* NODE_ZSUPER */ int i; rb_iseq_t *liseq = iseq->local_iseq; + int lvar_level = get_lvar_level(iseq); argc = INT2FIX(liseq->argc); /* normal arguments */ for (i = 0; i < liseq->argc; i++) { int idx = liseq->local_size - i; - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); + ADD_INSN2(args, nd_line(node), getlocal, INT2FIX(idx), INT2FIX(lvar_level)); } if (!liseq->arg_simple) { @@ -4292,7 +4313,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) int j; for (j = 0; j < liseq->arg_opts - 1; j++) { int idx = liseq->local_size - (i + j); - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); + ADD_INSN2(args, nd_line(node), getlocal, INT2FIX(idx), INT2FIX(lvar_level)); } i += j; argc = INT2FIX(i); @@ -4301,7 +4322,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) if (liseq->arg_rest != -1) { /* rest argument */ int idx = liseq->local_size - liseq->arg_rest; - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); + ADD_INSN2(args, nd_line(node), getlocal, INT2FIX(idx), INT2FIX(lvar_level)); argc = INT2FIX(liseq->arg_rest + 1); flag |= VM_CALL_ARGS_SPLAT_BIT; } @@ -4315,7 +4336,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) int j; for (j=0; jlocal_size - (post_start + j); - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); + ADD_INSN2(args, nd_line(node), getlocal, INT2FIX(idx), INT2FIX(lvar_level)); } ADD_INSN1(args, nd_line(node), newarray, INT2FIX(j)); ADD_INSN (args, nd_line(node), concatarray); @@ -4325,7 +4346,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) int j; for (j=0; jlocal_size - (post_start + j); - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); + ADD_INSN2(args, nd_line(node), getlocal, INT2FIX(idx), INT2FIX(lvar_level)); } argc = INT2FIX(post_len + post_start); } @@ -4460,7 +4481,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) int idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id); debugs("id: %s idx: %d\n", rb_id2name(id), idx); - ADD_INSN1(ret, nd_line(node), getlocal, INT2FIX(idx)); + ADD_INSN2(ret, nd_line(node), getlocal, INT2FIX(idx), INT2FIX(get_lvar_level(iseq))); } break; } @@ -4472,7 +4493,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) if (idx < 0) { rb_bug("unknown dvar (%s)", rb_id2name(node->nd_vid)); } - ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(ls - idx), INT2FIX(lv)); + ADD_INSN2(ret, nd_line(node), getlocal, INT2FIX(ls - idx), INT2FIX(lv)); } break; } @@ -4976,7 +4997,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) case NODE_ERRINFO:{ if (!poped) { if (iseq->type == ISEQ_TYPE_RESCUE) { - ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(2), INT2FIX(0)); + ADD_INSN2(ret, nd_line(node), getlocal, INT2FIX(2), INT2FIX(0)); } else { rb_iseq_t *ip = iseq; @@ -4989,7 +5010,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) level++; } if (ip) { - ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(2), INT2FIX(level)); + ADD_INSN2(ret, nd_line(node), getlocal, INT2FIX(2), INT2FIX(level)); } else { ADD_INSN(ret, nd_line(node), putnil); @@ -5050,12 +5071,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) switch (nd_type(node->nd_body)) { case NODE_LASGN: idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id); - ADD_INSN1(ret, nd_line(node), setlocal, INT2FIX(idx)); + ADD_INSN2(ret, nd_line(node), setlocal, INT2FIX(idx), INT2FIX(get_lvar_level(iseq))); break; case NODE_DASGN: case NODE_DASGN_CURR: idx = get_dyna_var_idx(iseq, id, &lv, &ls); - ADD_INSN2(ret, nd_line(node), setdynamic, INT2FIX(ls - idx), INT2FIX(lv)); + ADD_INSN2(ret, nd_line(node), setlocal, INT2FIX(ls - idx), INT2FIX(lv)); break; default: rb_bug("iseq_compile_each (NODE_KW_ARG): unknown node: %s", ruby_node_name(nd_type(node->nd_body))); @@ -5211,7 +5232,6 @@ insn_data_to_s_detail(INSN *iobj) } break; case TS_LINDEX: - case TS_DINDEX: case TS_NUM: /* ulong */ case TS_VALUE: /* VALUE */ rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j))); @@ -5225,9 +5245,12 @@ insn_data_to_s_detail(INSN *iobj) (OPERAND_AT(iobj, j) & (~1)); rb_str_cat2(str, rb_id2name(entry->id)); } - case TS_IC: /* method cache */ + case TS_IC: /* inline cache */ rb_str_catf(str, "", FIX2INT(OPERAND_AT(iobj, j))); break; + case TS_CALLINFO: /* call info */ + rb_str_catf(str, "", FIX2INT(OPERAND_AT(iobj, j))); + break; case TS_CDHASH: /* case/when condition cache */ rb_str_cat2(str, ""); break; @@ -5458,7 +5481,6 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor, break; } case TS_LINDEX: - case TS_DINDEX: case TS_NUM: (void)NUM2INT(op); argv[j] = op; @@ -5492,9 +5514,15 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor, break; case TS_IC: argv[j] = op; - if (NUM2INT(op) >= iseq->ic_size) + if (NUM2INT(op) >= iseq->ic_size) { iseq->ic_size = NUM2INT(op) + 1; + } break; + case TS_CALLINFO: + argv[j] = op; + if (NUM2INT(op) >= iseq->callinfo_size) { + iseq->callinfo_size = NUM2INT(op) + 1; + } case TS_ID: argv[j] = rb_convert_type(op, T_SYMBOL, "Symbol", "to_sym"); diff --git a/configure.in b/configure.in index 8869b01961d46a..e2d43d1cdbb580 100644 --- a/configure.in +++ b/configure.in @@ -1384,62 +1384,6 @@ if test $rb_cv_stack_end_address != no; then AC_DEFINE_UNQUOTED(STACK_END_ADDRESS, $rb_cv_stack_end_address) fi -AC_CACHE_CHECK(for gc_mark and gc_children stack frame approximate size(word), rb_cv_gc_mark_stackframe_word, -[save_CFLAGS="$CFLAGS" -CFLAGS="-O0" -AC_TRY_RUN([ -int word; -char *stack_start; - -void -set_stackframe_word() -{ - int dumy = 42; - int diff; - - if (stack_start < (char *)&dumy) { - diff = (int)((char *)&dumy - stack_start); - } - else { - diff = (int)(stack_start - (char *)&dumy); - } - word = (diff/sizeof(void *)); - if ((diff % sizeof(void *)) != 0) { - word++; - } -} - -void -gc_mark_children(void *p1, void *p2, int lev) -{ - void *obj = p2; - - set_stackframe_word(p1,p2,lev); -} - -void -gc_mark(void *p1, void *p2, int lev) -{ - void *obj = p2; - - gc_mark_children(p1,p2,lev++); -} - -int -main() { - int dumy = 42; - - stack_start = (char *)&dumy; - gc_mark(0, 0, 255); - return word; -} -], - [rb_cv_gc_mark_stackframe_word="$?"], - [rb_cv_gc_mark_stackframe_word="$?"], - [rb_cv_gc_mark_stackframe_word="30"]) -CFLAGS="$save_CFLAGS"]) -AC_DEFINE_UNQUOTED(GC_MARK_STACKFRAME_WORD, $rb_cv_gc_mark_stackframe_word) - AS_CASE(["$target_os"], [openbsd*], [ AC_CACHE_CHECK(for heap align log on openbsd, rb_cv_page_size_log, @@ -2353,7 +2297,9 @@ AC_ARG_WITH(opt-dir, [ CPPFLAGS="$CPPFLAGS -I$withval/include" val=`echo $withval|sed 's/\\//\\\\\\//g'` - LDFLAGS="$LDFLAGS "`echo ${LIBPATHFLAG} ${RPATHFLAG}|sed -E 's/%1\\$-s|%s/'${val}'\/lib/g'` + val=`echo ${LIBPATHFLAG} ${RPATHFLAG}|sed -E 's/%1\\$-s|%s/'${val}'\/lib/g'` + LDFLAGS="$LDFLAGS $val" + DLDFLAGS="$DLDFLAGS $val" ]) AS_CASE(["$target_cpu-$target_os"], diff --git a/enc/encdb.c b/enc/encdb.c index 958b33504edc4d..32def53aeb467f 100644 --- a/enc/encdb.c +++ b/enc/encdb.c @@ -14,11 +14,13 @@ int rb_encdb_alias(const char *alias, const char *orig); int rb_encdb_dummy(const char *name); void rb_encdb_declare(const char *name); void rb_enc_set_base(const char *name, const char *orig); +void rb_encdb_set_unicode(int index); #define ENC_REPLICATE(name, orig) rb_encdb_replicate((name), (orig)) #define ENC_ALIAS(name, orig) rb_encdb_alias((name), (orig)) #define ENC_DUMMY(name) rb_encdb_dummy(name) #define ENC_DEFINE(name) rb_encdb_declare(name) #define ENC_SET_BASE(name, orig) rb_enc_set_base((name), (orig)) +#define ENC_DUMMY_UNICODE(name) rb_encdb_set_unicode(ENC_DUMMY(name)) void Init_encdb(void) diff --git a/enc/utf_16_32.h b/enc/utf_16_32.h index b028a1a12ed9b2..b232767ee3f69d 100644 --- a/enc/utf_16_32.h +++ b/enc/utf_16_32.h @@ -1,4 +1,5 @@ #include "regenc.h" /* dummy for unsupported, statefull encoding */ -ENC_DUMMY("UTF-16"); -ENC_DUMMY("UTF-32"); +#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name) +ENC_DUMMY_UNICODE("UTF-16"); +ENC_DUMMY_UNICODE("UTF-32"); diff --git a/encoding.c b/encoding.c index ae62d674867cb9..b9e820fa295224 100644 --- a/encoding.c +++ b/encoding.c @@ -32,6 +32,7 @@ void rb_encdb_declare(const char *name); int rb_encdb_replicate(const char *name, const char *orig); int rb_encdb_dummy(const char *name); int rb_encdb_alias(const char *alias, const char *orig); +void rb_encdb_set_unicode(int index); #pragma GCC visibility pop #endif @@ -457,8 +458,7 @@ enc_ascii_compatible_p(VALUE enc) int rb_enc_unicode_p(rb_encoding *enc) { - const char *name = rb_enc_name(enc); - return name[0] == 'U' && name[1] == 'T' && name[2] == 'F' && name[4] != '7'; + return ONIGENC_IS_UNICODE(enc); } static st_data_t @@ -513,6 +513,12 @@ rb_encdb_alias(const char *alias, const char *orig) return enc_alias(alias, idx); } +void +rb_encdb_set_unicode(int index) +{ + rb_enc_from_index(index)->flags |= ONIGENC_FLAG_UNICODE; +} + enum { ENCINDEX_ASCII, ENCINDEX_UTF_8, diff --git a/error.c b/error.c index bb1ffa4e7d81f7..5bfa2216eb9ef9 100644 --- a/error.c +++ b/error.c @@ -635,7 +635,6 @@ exc_to_s(VALUE exc) if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc)); r = rb_String(mesg); - OBJ_INFECT(r, exc); return r; } @@ -996,11 +995,7 @@ name_err_to_s(VALUE exc) if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc)); StringValue(str); - if (str != mesg) { - rb_iv_set(exc, "mesg", mesg = str); - } - OBJ_INFECT(mesg, exc); - return mesg; + return str; } /* @@ -1131,7 +1126,6 @@ name_err_mesg_to_str(VALUE obj) args[2] = d; mesg = rb_f_sprintf(NAME_ERR_MESG_COUNT, args); } - OBJ_INFECT(mesg, obj); return mesg; } diff --git a/eval.c b/eval.c index 4d27a8ec6bf553..6dfa3e7399d278 100644 --- a/eval.c +++ b/eval.c @@ -1040,27 +1040,36 @@ void check_class_or_module(VALUE obj) } } +static VALUE +hidden_identity_hash_new() +{ + VALUE hash = rb_hash_new(); + + rb_funcall(hash, rb_intern("compare_by_identity"), 0); + RBASIC(hash)->klass = 0; /* hide from ObjectSpace */ + return hash; +} + void -rb_overlay_module(NODE *cref, VALUE klass, VALUE module) +rb_using_refinement(NODE *cref, VALUE klass, VALUE module) { VALUE iclass, c, superclass = klass; check_class_or_module(klass); Check_Type(module, T_MODULE); - if (NIL_P(cref->nd_omod)) { - cref->nd_omod = rb_hash_new(); - rb_funcall(cref->nd_omod, rb_intern("compare_by_identity"), 0); + if (NIL_P(cref->nd_refinements)) { + cref->nd_refinements = hidden_identity_hash_new(); } else { if (cref->flags & NODE_FL_CREF_OMOD_SHARED) { - cref->nd_omod = rb_hash_dup(cref->nd_omod); + cref->nd_refinements = rb_hash_dup(cref->nd_refinements); cref->flags &= ~NODE_FL_CREF_OMOD_SHARED; } - if (!NIL_P(c = rb_hash_lookup(cref->nd_omod, klass))) { + if (!NIL_P(c = rb_hash_lookup(cref->nd_refinements, klass))) { superclass = c; while (c && TYPE(c) == T_ICLASS) { if (RBASIC(c)->klass == module) { - /* already overlaid module */ + /* already used refinement */ return; } c = RCLASS_SUPER(c); @@ -1077,7 +1086,7 @@ rb_overlay_module(NODE *cref, VALUE klass, VALUE module) RCLASS_REFINED_CLASS(c) = klass; module = RCLASS_SUPER(module); } - rb_hash_aset(cref->nd_omod, klass, iclass); + rb_hash_aset(cref->nd_refinements, klass, iclass); rb_clear_cache_by_class(klass); } @@ -1086,21 +1095,21 @@ using_module_i(VALUE klass, VALUE module, VALUE arg) { NODE *cref = (NODE *) arg; - rb_overlay_module(cref, klass, module); + rb_using_refinement(cref, klass, module); return ST_CONTINUE; } void rb_using_module(NODE *cref, VALUE module) { - ID id_overlaid_modules; - VALUE overlaid_modules; + ID id_refinements; + VALUE refinements; - Check_Type(module, T_MODULE); - CONST_ID(id_overlaid_modules, "__overlaid_modules__"); - overlaid_modules = rb_attr_get(module, id_overlaid_modules); - if (NIL_P(overlaid_modules)) return; - rb_hash_foreach(overlaid_modules, using_module_i, (VALUE) cref); + check_class_or_module(module); + CONST_ID(id_refinements, "__refinements__"); + refinements = rb_attr_get(module, id_refinements); + if (NIL_P(refinements)) return; + rb_hash_foreach(refinements, using_module_i, (VALUE) cref); } /* @@ -1117,11 +1126,11 @@ rb_mod_using(VALUE self, VALUE module) ID id_using_modules; VALUE using_modules; + Check_Type(module, T_MODULE); CONST_ID(id_using_modules, "__using_modules__"); using_modules = rb_attr_get(self, id_using_modules); if (NIL_P(using_modules)) { - using_modules = rb_hash_new(); - rb_funcall(using_modules, rb_intern("compare_by_identity"), 0); + using_modules = hidden_identity_hash_new(); rb_ivar_set(self, id_using_modules, using_modules); } rb_hash_aset(using_modules, module, Qtrue); @@ -1161,8 +1170,8 @@ refinement_module_include(int argc, VALUE *argv, VALUE module) while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) { if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq) && (cref = rb_vm_get_cref(cfp->iseq, cfp->ep)) && - !NIL_P(cref->nd_omod) && - !NIL_P(c = rb_hash_lookup(cref->nd_omod, klass))) { + !NIL_P(cref->nd_refinements) && + !NIL_P(c = rb_hash_lookup(cref->nd_refinements, klass))) { while (argc--) { VALUE mod = argv[argc]; if (rb_class_inherited_p(module, mod)) { @@ -1191,18 +1200,17 @@ rb_mod_refine(VALUE module, VALUE klass) { NODE *cref = rb_vm_cref(); VALUE mod; - ID id_overlaid_modules, id_refined_class; - VALUE overlaid_modules; + ID id_refinements, id_refined_class; + VALUE refinements; check_class_or_module(klass); - CONST_ID(id_overlaid_modules, "__overlaid_modules__"); - overlaid_modules = rb_attr_get(module, id_overlaid_modules); - if (NIL_P(overlaid_modules)) { - overlaid_modules = rb_hash_new(); - rb_funcall(overlaid_modules, rb_intern("compare_by_identity"), 0); - rb_ivar_set(module, id_overlaid_modules, overlaid_modules); - } - mod = rb_hash_aref(overlaid_modules, klass); + CONST_ID(id_refinements, "__refinements__"); + refinements = rb_attr_get(module, id_refinements); + if (NIL_P(refinements)) { + refinements = hidden_identity_hash_new(); + rb_ivar_set(module, id_refinements, refinements); + } + mod = rb_hash_lookup(refinements, klass); if (NIL_P(mod)) { mod = rb_module_new(); CONST_ID(id_refined_class, "__refined_class__"); @@ -1211,13 +1219,44 @@ rb_mod_refine(VALUE module, VALUE klass) refinement_module_method_added, 1); rb_define_singleton_method(mod, "include", refinement_module_include, -1); - rb_overlay_module(cref, klass, mod); - rb_hash_aset(overlaid_modules, klass, mod); + rb_using_refinement(cref, klass, mod); + rb_hash_aset(refinements, klass, mod); } rb_mod_module_eval(0, NULL, mod); return mod; } +static int +refinements_i(VALUE key, VALUE value, VALUE arg) +{ + rb_hash_aset(arg, key, value); + return ST_CONTINUE; +} + +/* + * call-seq: + * refinements -> hash + * + * Returns refinements in the receiver as a hash table, whose key is a + * refined class and whose value is a refinement module. + */ + +static VALUE +rb_mod_refinements(VALUE module) +{ + ID id_refinements; + VALUE refinements, result; + + CONST_ID(id_refinements, "__refinements__"); + refinements = rb_attr_get(module, id_refinements); + if (NIL_P(refinements)) { + return rb_hash_new(); + } + result = rb_hash_new(); + rb_hash_foreach(refinements, refinements_i, result); + return result; +} + void rb_obj_call_init(VALUE obj, int argc, VALUE *argv) { @@ -1345,6 +1384,7 @@ f_using(VALUE self, VALUE module) { NODE *cref = rb_vm_cref(); + Check_Type(module, T_MODULE); rb_using_module(cref, module); return self; } @@ -1515,6 +1555,7 @@ Init_eval(void) rb_define_private_method(rb_cModule, "prepend", rb_mod_prepend, -1); rb_define_private_method(rb_cModule, "using", rb_mod_using, 1); rb_define_private_method(rb_cModule, "refine", rb_mod_refine, 1); + rb_define_method(rb_cModule, "refinements", rb_mod_refinements, 0); rb_undef_method(rb_cClass, "module_function"); diff --git a/ext/date/depend b/ext/date/depend index 43d5d7e61f9395..e6afa5616203fc 100644 --- a/ext/date/depend +++ b/ext/date/depend @@ -1,2 +1,3 @@ -date_core.o: date_tmx.h $(ruby_headers) -date_strftime.o: date_tmx.h $(ruby_headers) +$(OBJS): $(ruby_headers) +date_core.o: date_tmx.h +date_strftime.o: date_tmx.h diff --git a/ext/json/generator/depend b/ext/json/generator/depend index 1a042a2501eb2d..1f0ea0a624e759 100644 --- a/ext/json/generator/depend +++ b/ext/json/generator/depend @@ -1 +1 @@ -generator.o: generator.c generator.h $(srcdir)/../fbuffer/fbuffer.h +generator.o: generator.c generator.h $(srcdir)/../fbuffer/fbuffer.h $(ruby_headers) diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 33106382caa367..18422096413d59 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -37,6 +37,7 @@ size_t rb_ary_memsize(VALUE); size_t rb_io_memsize(const rb_io_t *); size_t rb_generic_ivar_memsize(VALUE); size_t rb_objspace_data_type_memsize(VALUE obj); +VALUE rb_objspace_reachable_objects_from(VALUE obj); static size_t memsize_of(VALUE obj) @@ -626,6 +627,47 @@ count_tdata_objects(int argc, VALUE *argv, VALUE self) return hash; } +/* + * call-seq: + * ObjectSpace.reachable_objects_from(obj) -> array or nil + * + * [MRI specific feature] Return all reachable objects from `obj'. + * + * This method returns all reachable objects from `obj'. + * If `obj' has references two or more references to same object `x', + * them returned array only include one `x' object. + * If `obj' is non-markable (non-heap management) object such as + * true, false, nil, symbols and Fixnums (and Flonum) them it simply + * returns nil. + * + * With this method, you can find memory leaks. + * + * This method is not expected to work except C Ruby. + * + * Example: + * ObjectSpace.reachable_objects_from(['a', 'b', 'c']) + * #=> [Array, 'a', 'b', 'c'] + * + * ObjectSpace.reachable_objects_from(['a', 'a', 'a']) + * #=> [Array, 'a', 'a', 'a'] # all 'a' strings have different object id + * + * ObjectSpace.reachable_objects_from([v = 'a', v, v]) + * #=> [Array, 'a'] + * + * ObjectSpace.reachable_objects_from(1) + * #=> nil # 1 is not markable (heap managed) object + * + * Limitation: Current implementation can't acquire internal objects. + * This means that you can't acquire complete object graph + * (heap snapshot). This is future work. + * + */ +static VALUE +reachable_objects_from(VALUE self, VALUE obj) +{ + return rb_objspace_reachable_objects_from(obj); +} + /* objspace library extends ObjectSpace module and add several * methods to get internal statistic information about * object/memory management. @@ -642,10 +684,11 @@ Init_objspace(void) VALUE rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace")); rb_define_module_function(rb_mObjSpace, "memsize_of", memsize_of_m, 1); - rb_define_module_function(rb_mObjSpace, "memsize_of_all", - memsize_of_all_m, -1); + rb_define_module_function(rb_mObjSpace, "memsize_of_all", memsize_of_all_m, -1); rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1); rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1); rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1); + + rb_define_module_function(rb_mObjSpace, "reachable_objects_from", reachable_objects_from, 1); } diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index c03cf310e40d0c..5e3094ee9f1406 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -204,6 +204,15 @@ ossl_x509store_set_time(VALUE self, VALUE time) return time; } +/* + * call-seq: + * store.add_file(file) -> store + * + * + * Adds the certificates in +file+ to the certificate store. The +file+ can + * contain multiple PEM-encoded certificates. + */ + static VALUE ossl_x509store_add_file(VALUE self, VALUE file) { @@ -246,6 +255,16 @@ ossl_x509store_add_path(VALUE self, VALUE dir) return self; } +/* + * call-seq: + * store.set_default_path + * + * Adds the default certificates to the certificate store. These certificates + * are loaded from the default configuration directory which can usually be + * determined by: + * + * File.dirname OpenSSL::Config::DEFAULT_CONFIG_FILE + */ static VALUE ossl_x509store_set_default_paths(VALUE self) { @@ -259,6 +278,13 @@ ossl_x509store_set_default_paths(VALUE self) return Qnil; } +/* + * call-seq: + * store.add_cert(cert) + * + * Adds the OpenSSL::X509::Certificate +cert+ to the certificate store. + */ + static VALUE ossl_x509store_add_cert(VALUE self, VALUE arg) { diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 6135e8259de4dd..8c3d84d4a36042 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -990,6 +990,14 @@ zstream_run_func(void *ptr) break; } + if (z->stream.avail_in == 0 && z->func == &inflate_funcs) { + /* break here because inflate() return Z_BUF_ERROR when avail_in == 0. */ + /* but deflate() could be called with avail_in == 0 (there's hidden buffer + in zstream->state) */ + z->flags |= ZSTREAM_FLAG_IN_STREAM; + break; + } + if (args->stream_output) { state = (int)(VALUE)rb_thread_call_with_gvl(zstream_expand_buffer_protect, (void *)z); diff --git a/gc.c b/gc.c index a36c607ca2a429..735ae85baad9ed 100644 --- a/gc.c +++ b/gc.c @@ -112,8 +112,6 @@ static ruby_gc_params_t initial_params = { #define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory] -#define MARK_STACK_MAX 1024 - #ifndef GC_PROFILE_MORE_DETAIL #define GC_PROFILE_MORE_DETAIL 0 #endif @@ -208,6 +206,22 @@ struct gc_list { struct gc_list *next; }; +#define STACK_CHUNK_SIZE 500 + +typedef struct stack_chunk { + VALUE data[STACK_CHUNK_SIZE]; + struct stack_chunk *next; +} stack_chunk_t; + +typedef struct mark_stack { + stack_chunk_t *chunk; + stack_chunk_t *cache; + size_t index; + size_t limit; + size_t cache_size; + size_t unused_cache_size; +} mark_stack_t; + #ifndef CALC_EXACT_MALLOC_SIZE #define CALC_EXACT_MALLOC_SIZE 0 #endif @@ -248,11 +262,7 @@ typedef struct rb_objspace { st_table *table; RVALUE *deferred; } final; - struct { - VALUE buffer[MARK_STACK_MAX]; - VALUE *ptr; - int overflow; - } markstack; + mark_stack_t mark_stack; struct { int run; gc_profile_record *record; @@ -263,6 +273,11 @@ typedef struct rb_objspace { struct gc_list *global_list; size_t count; int gc_stress; + + struct mark_func_data_struct { + VALUE data; + void (*mark_func)(struct rb_objspace *objspace, VALUE v); + } *mark_func_data; } rb_objspace_t; #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE @@ -287,9 +302,6 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress; #define finalizing objspace->flags.finalizing #define finalizer_table objspace->final.table #define deferred_final_list objspace->final.deferred -#define mark_stack objspace->markstack.buffer -#define mark_stack_ptr objspace->markstack.ptr -#define mark_stack_overflow objspace->markstack.overflow #define global_List objspace->global_list #define ruby_gc_stress objspace->gc_stress #define initial_malloc_limit initial_params.initial_malloc_limit @@ -308,8 +320,8 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress; #define GET_HEAP_SLOT(x) (GET_HEAP_HEADER(x)->base) #define GET_HEAP_BITMAP(x) (GET_HEAP_HEADER(x)->bits) #define NUM_IN_SLOT(p) (((uintptr_t)(p) & HEAP_ALIGN_MASK)/sizeof(RVALUE)) -#define BITMAP_INDEX(p) (NUM_IN_SLOT(p) / (sizeof(uintptr_t) * 8)) -#define BITMAP_OFFSET(p) (NUM_IN_SLOT(p) & ((sizeof(uintptr_t) * 8)-1)) +#define BITMAP_INDEX(p) (NUM_IN_SLOT(p) / (sizeof(uintptr_t) * CHAR_BIT)) +#define BITMAP_OFFSET(p) (NUM_IN_SLOT(p) & ((sizeof(uintptr_t) * CHAR_BIT)-1)) #define MARKED_IN_BITMAP(bits, p) (bits[BITMAP_INDEX(p)] & ((uintptr_t)1 << BITMAP_OFFSET(p))) #ifndef HEAP_ALIGN_LOG @@ -317,14 +329,16 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress; #define HEAP_ALIGN_LOG 14 #endif -#define HEAP_ALIGN (1UL << HEAP_ALIGN_LOG) -#define HEAP_ALIGN_MASK (~(~0UL << HEAP_ALIGN_LOG)) -#define REQUIRED_SIZE_BY_MALLOC (sizeof(size_t) * 5) -#define HEAP_SIZE (HEAP_ALIGN - REQUIRED_SIZE_BY_MALLOC) #define CEILDIV(i, mod) (((i) + (mod) - 1)/(mod)) -#define HEAP_OBJ_LIMIT (unsigned int)((HEAP_SIZE - sizeof(struct heaps_header))/sizeof(struct RVALUE)) -#define HEAP_BITMAP_LIMIT CEILDIV(CEILDIV(HEAP_SIZE, sizeof(struct RVALUE)), sizeof(uintptr_t)*8) +enum { + HEAP_ALIGN = (1UL << HEAP_ALIGN_LOG), + HEAP_ALIGN_MASK = (~(~0UL << HEAP_ALIGN_LOG)), + REQUIRED_SIZE_BY_MALLOC = (sizeof(size_t) * 5), + HEAP_SIZE = (HEAP_ALIGN - REQUIRED_SIZE_BY_MALLOC), + HEAP_OBJ_LIMIT = (unsigned int)((HEAP_SIZE - sizeof(struct heaps_header))/sizeof(struct RVALUE)), + HEAP_BITMAP_LIMIT = CEILDIV(CEILDIV(HEAP_SIZE, sizeof(struct RVALUE)), sizeof(uintptr_t) * CHAR_BIT) +}; int ruby_gc_debug_indent = 0; VALUE rb_mGC; @@ -342,11 +356,14 @@ static void negative_size_allocation_error(const char *); static void *aligned_malloc(size_t, size_t); static void aligned_free(void *); +static void init_mark_stack(mark_stack_t *stack); + static VALUE lazy_sweep_enable(void); static int garbage_collect(rb_objspace_t *); static int gc_lazy_sweep(rb_objspace_t *); -static void mark_tbl(rb_objspace_t *, st_table *, int); +static void mark_tbl(rb_objspace_t *, st_table *); static void rest_sweep(rb_objspace_t *); +static void gc_mark_stacked_objects(rb_objspace_t *); static double getrusage_time(void); static inline void gc_prof_timer_start(rb_objspace_t *); @@ -378,7 +395,7 @@ rb_objspace_alloc(void) #endif #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE -static void aligned_free(void *); +static void free_stack_chunks(mark_stack_t *); void rb_objspace_free(rb_objspace_t *objspace) @@ -413,6 +430,7 @@ rb_objspace_free(rb_objspace_t *objspace) heaps_used = 0; heaps = 0; } + free_stack_chunks(&objspace->mark_stack); free(objspace); } #endif @@ -574,6 +592,8 @@ static void init_heap(rb_objspace_t *objspace) { add_heap_slots(objspace, HEAP_MIN_SLOTS / HEAP_OBJ_LIMIT); + init_mark_stack(&objspace->mark_stack); + #ifdef USE_SIGALTSTACK { /* altstack of another threads are allocated in another place */ @@ -734,8 +754,8 @@ rb_objspace_data_type_name(VALUE obj) } } -static void gc_mark(rb_objspace_t *objspace, VALUE ptr, int lev); -static void gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev); +static void gc_mark(rb_objspace_t *objspace, VALUE ptr); +static void gc_mark_children(rb_objspace_t *objspace, VALUE ptr); static inline int is_pointer_to_heap(rb_objspace_t *objspace, void *ptr) @@ -1122,31 +1142,41 @@ struct os_each_struct { VALUE of; }; +static int +internal_object_p(VALUE obj) +{ + RVALUE *p = (RVALUE *)obj; + + if (p->as.basic.flags) { + switch (BUILTIN_TYPE(p)) { + case T_NONE: + case T_ICLASS: + case T_NODE: + case T_ZOMBIE: + break; + case T_CLASS: + if (FL_TEST(p, FL_SINGLETON)) + break; + default: + if (!p->as.basic.klass) break; + return 0; + } + } + return 1; +} + static int os_obj_of_i(void *vstart, void *vend, size_t stride, void *data) { struct os_each_struct *oes = (struct os_each_struct *)data; RVALUE *p = (RVALUE *)vstart, *pend = (RVALUE *)vend; - volatile VALUE v; for (; p != pend; p++) { - if (p->as.basic.flags) { - switch (BUILTIN_TYPE(p)) { - case T_NONE: - case T_ICLASS: - case T_NODE: - case T_ZOMBIE: - continue; - case T_CLASS: - if (FL_TEST(p, FL_SINGLETON)) - continue; - default: - if (!p->as.basic.klass) continue; - v = (VALUE)p; - if (!oes->of || rb_obj_is_kind_of(v, oes->of)) { - rb_yield(v); - oes->num++; - } + volatile VALUE v = (VALUE)p; + if (!internal_object_p(v)) { + if (!oes->of || rb_obj_is_kind_of(v, oes->of)) { + rb_yield(v); + oes->num++; } } } @@ -1470,16 +1500,17 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace) RVALUE *final_list = 0; size_t i; - /* run finalizers */ rest_sweep(objspace); if (ATOMIC_EXCHANGE(finalizing, 1)) return; + /* run finalizers */ do { - /* XXX: this loop will make no sense */ - /* because mark will not be removed */ finalize_deferred(objspace); - mark_tbl(objspace, finalizer_table, 0); + /* mark reachable objects from finalizers */ + /* They might be not referred from any place here */ + mark_tbl(objspace, finalizer_table); + gc_mark_stacked_objects(objspace); st_foreach(finalizer_table, chain_finalized_object, (st_data_t)&deferred_final_list); } while (deferred_final_list); @@ -2060,6 +2091,139 @@ gc_sweep(rb_objspace_t *objspace) during_gc = 0; } +/* Marking stack */ + +static void push_mark_stack(mark_stack_t *, VALUE); +static int pop_mark_stack(mark_stack_t *, VALUE *); +static void shrink_stack_chunk_cache(mark_stack_t *stack); + +static stack_chunk_t * +stack_chunk_alloc(void) +{ + stack_chunk_t *res; + + res = malloc(sizeof(stack_chunk_t)); + if (!res) + rb_memerror(); + + return res; +} + +static inline int +is_mark_stask_empty(mark_stack_t *stack) +{ + return stack->chunk == NULL; +} + +static void +add_stack_chunk_cache(mark_stack_t *stack, stack_chunk_t *chunk) +{ + chunk->next = stack->cache; + stack->cache = chunk; + stack->cache_size++; +} + +static void +shrink_stack_chunk_cache(mark_stack_t *stack) +{ + stack_chunk_t *chunk; + + if (stack->unused_cache_size > (stack->cache_size/2)) { + chunk = stack->cache; + stack->cache = stack->cache->next; + stack->cache_size--; + free(chunk); + } + stack->unused_cache_size = stack->cache_size; +} + +static void +push_mark_stack_chunk(mark_stack_t *stack) +{ + stack_chunk_t *next; + + assert(stack->index == stack->limit); + if (stack->cache_size > 0) { + next = stack->cache; + stack->cache = stack->cache->next; + stack->cache_size--; + if (stack->unused_cache_size > stack->cache_size) + stack->unused_cache_size = stack->cache_size; + } + else { + next = stack_chunk_alloc(); + } + next->next = stack->chunk; + stack->chunk = next; + stack->index = 0; +} + +static void +pop_mark_stack_chunk(mark_stack_t *stack) +{ + stack_chunk_t *prev; + + prev = stack->chunk->next; + assert(stack->index == 0); + add_stack_chunk_cache(stack, stack->chunk); + stack->chunk = prev; + stack->index = stack->limit; +} + +#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE +static void +free_stack_chunks(mark_stack_t *stack) +{ + stack_chunk_t *chunk = stack->chunk; + stack_chunk_t *next = NULL; + + while (chunk != NULL) { + next = chunk->next; + free(chunk); + chunk = next; + } +} +#endif + +static void +push_mark_stack(mark_stack_t *stack, VALUE data) +{ + if (stack->index == stack->limit) { + push_mark_stack_chunk(stack); + } + stack->chunk->data[stack->index++] = data; +} + +static int +pop_mark_stack(mark_stack_t *stack, VALUE *data) +{ + if (is_mark_stask_empty(stack)) { + return FALSE; + } + if (stack->index == 1) { + *data = stack->chunk->data[--stack->index]; + pop_mark_stack_chunk(stack); + return TRUE; + } + *data = stack->chunk->data[--stack->index]; + return TRUE; +} + +static void +init_mark_stack(mark_stack_t *stack) +{ + int i; + + push_mark_stack_chunk(stack); + stack->limit = STACK_CHUNK_SIZE; + + for(i=0; i < 4; i++) { + add_stack_chunk_cache(stack, stack_chunk_alloc()); + } + stack->unused_cache_size = stack->cache_size; +} + + /* Marking */ #define MARK_IN_BITMAP(bits, p) (bits[BITMAP_INDEX(p)] = bits[BITMAP_INDEX(p)] | ((uintptr_t)1 << BITMAP_OFFSET(p))) @@ -2096,9 +2260,6 @@ ruby_get_stack_grow_direction(volatile VALUE *addr) } #endif -#define GC_LEVEL_MAX 250 -#define STACKFRAME_FOR_GC_MARK (GC_LEVEL_MAX * GC_MARK_STACKFRAME_WORD) - size_t ruby_stack_length(VALUE **p) { @@ -2108,6 +2269,7 @@ ruby_stack_length(VALUE **p) return STACK_LENGTH; } +#if !(defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)) static int stack_check(int water_mark) { @@ -2123,6 +2285,7 @@ stack_check(int water_mark) #endif return ret; } +#endif #define STACKFRAME_FOR_CALL_CFUNC 512 @@ -2136,13 +2299,6 @@ ruby_stack_check(void) #endif } -static void -init_mark_stack(rb_objspace_t *objspace) -{ - mark_stack_overflow = 0; - mark_stack_ptr = mark_stack; -} - static void mark_locations_array(rb_objspace_t *objspace, register VALUE *x, register long n) { @@ -2151,7 +2307,7 @@ mark_locations_array(rb_objspace_t *objspace, register VALUE *x, register long n v = *x; VALGRIND_MAKE_MEM_DEFINED(&v, sizeof(v)); if (is_pointer_to_heap(objspace, (void *)v)) { - gc_mark(objspace, v, 0); + gc_mark(objspace, v); } x++; } @@ -2177,24 +2333,22 @@ rb_gc_mark_locations(VALUE *start, VALUE *end) struct mark_tbl_arg { rb_objspace_t *objspace; - int lev; }; static int mark_entry(st_data_t key, st_data_t value, st_data_t data) { struct mark_tbl_arg *arg = (void*)data; - gc_mark(arg->objspace, (VALUE)value, arg->lev); + gc_mark(arg->objspace, (VALUE)value); return ST_CONTINUE; } static void -mark_tbl(rb_objspace_t *objspace, st_table *tbl, int lev) +mark_tbl(rb_objspace_t *objspace, st_table *tbl) { struct mark_tbl_arg arg; if (!tbl || tbl->num_entries == 0) return; arg.objspace = objspace; - arg.lev = lev; st_foreach(tbl, mark_entry, (st_data_t)&arg); } @@ -2202,68 +2356,66 @@ static int mark_key(st_data_t key, st_data_t value, st_data_t data) { struct mark_tbl_arg *arg = (void*)data; - gc_mark(arg->objspace, (VALUE)key, arg->lev); + gc_mark(arg->objspace, (VALUE)key); return ST_CONTINUE; } static void -mark_set(rb_objspace_t *objspace, st_table *tbl, int lev) +mark_set(rb_objspace_t *objspace, st_table *tbl) { struct mark_tbl_arg arg; if (!tbl) return; arg.objspace = objspace; - arg.lev = lev; st_foreach(tbl, mark_key, (st_data_t)&arg); } void rb_mark_set(st_table *tbl) { - mark_set(&rb_objspace, tbl, 0); + mark_set(&rb_objspace, tbl); } static int mark_keyvalue(st_data_t key, st_data_t value, st_data_t data) { struct mark_tbl_arg *arg = (void*)data; - gc_mark(arg->objspace, (VALUE)key, arg->lev); - gc_mark(arg->objspace, (VALUE)value, arg->lev); + gc_mark(arg->objspace, (VALUE)key); + gc_mark(arg->objspace, (VALUE)value); return ST_CONTINUE; } static void -mark_hash(rb_objspace_t *objspace, st_table *tbl, int lev) +mark_hash(rb_objspace_t *objspace, st_table *tbl) { struct mark_tbl_arg arg; if (!tbl) return; arg.objspace = objspace; - arg.lev = lev; st_foreach(tbl, mark_keyvalue, (st_data_t)&arg); } void rb_mark_hash(st_table *tbl) { - mark_hash(&rb_objspace, tbl, 0); + mark_hash(&rb_objspace, tbl); } static void -mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me, int lev) +mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me) { const rb_method_definition_t *def = me->def; - gc_mark(objspace, me->klass, lev); + gc_mark(objspace, me->klass); if (!def) return; switch (def->type) { case VM_METHOD_TYPE_ISEQ: - gc_mark(objspace, def->body.iseq->self, lev); + gc_mark(objspace, def->body.iseq->self); break; case VM_METHOD_TYPE_BMETHOD: - gc_mark(objspace, def->body.proc, lev); + gc_mark(objspace, def->body.proc); break; case VM_METHOD_TYPE_ATTRSET: case VM_METHOD_TYPE_IVAR: - gc_mark(objspace, def->body.attr.location, lev); + gc_mark(objspace, def->body.attr.location); break; default: break; /* ignore */ @@ -2273,24 +2425,23 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me, int lev) void rb_mark_method_entry(const rb_method_entry_t *me) { - mark_method_entry(&rb_objspace, me, 0); + mark_method_entry(&rb_objspace, me); } static int mark_method_entry_i(ID key, const rb_method_entry_t *me, st_data_t data) { struct mark_tbl_arg *arg = (void*)data; - mark_method_entry(arg->objspace, me, arg->lev); + mark_method_entry(arg->objspace, me); return ST_CONTINUE; } static void -mark_m_tbl(rb_objspace_t *objspace, st_table *tbl, int lev) +mark_m_tbl(rb_objspace_t *objspace, st_table *tbl) { struct mark_tbl_arg arg; if (!tbl) return; arg.objspace = objspace; - arg.lev = lev; st_foreach(tbl, mark_method_entry_i, (st_data_t)&arg); } @@ -2298,18 +2449,17 @@ static int mark_const_entry_i(ID key, const rb_const_entry_t *ce, st_data_t data) { struct mark_tbl_arg *arg = (void*)data; - gc_mark(arg->objspace, ce->value, arg->lev); - gc_mark(arg->objspace, ce->file, arg->lev); + gc_mark(arg->objspace, ce->value); + gc_mark(arg->objspace, ce->file); return ST_CONTINUE; } static void -mark_const_tbl(rb_objspace_t *objspace, st_table *tbl, int lev) +mark_const_tbl(rb_objspace_t *objspace, st_table *tbl) { struct mark_tbl_arg arg; if (!tbl) return; arg.objspace = objspace; - arg.lev = lev; st_foreach(tbl, mark_const_entry_i, (st_data_t)&arg); } @@ -2369,14 +2519,14 @@ rb_gc_mark_machine_stack(rb_thread_t *th) void rb_mark_tbl(st_table *tbl) { - mark_tbl(&rb_objspace, tbl, 0); + mark_tbl(&rb_objspace, tbl); } void rb_gc_mark_maybe(VALUE obj) { if (is_pointer_to_heap(&rb_objspace, (void *)obj)) { - gc_mark(&rb_objspace, obj, 0); + gc_mark(&rb_objspace, obj); } } @@ -2390,49 +2540,56 @@ gc_mark_ptr(rb_objspace_t *objspace, VALUE ptr) return 1; } -static void -gc_mark(rb_objspace_t *objspace, VALUE ptr, int lev) +static int +markable_object_p(rb_objspace_t *objspace, VALUE ptr) { - register RVALUE *obj; + register RVALUE *obj = RANY(ptr); - obj = RANY(ptr); - if (rb_special_const_p(ptr)) return; /* special const not marked */ - if (obj->as.basic.flags == 0) return; /* free cell */ - if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */ + if (rb_special_const_p(ptr)) return 0; /* special const not marked */ + if (obj->as.basic.flags == 0) return 0 ; /* free cell */ - if (lev > GC_LEVEL_MAX || (lev == 0 && stack_check(STACKFRAME_FOR_GC_MARK))) { - if (!mark_stack_overflow) { - if (mark_stack_ptr - mark_stack < MARK_STACK_MAX) { - *mark_stack_ptr = ptr; - mark_stack_ptr++; - } - else { - mark_stack_overflow = 1; - } - } + return 1; +} + +static void +gc_mark(rb_objspace_t *objspace, VALUE ptr) +{ + if (!markable_object_p(objspace, ptr)) { return; } - gc_mark_children(objspace, ptr, lev+1); + + if (LIKELY(objspace->mark_func_data == 0)) { + if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */ + push_mark_stack(&objspace->mark_stack, ptr); + } + else { + objspace->mark_func_data->mark_func(objspace, ptr); + } } void rb_gc_mark(VALUE ptr) { - gc_mark(&rb_objspace, ptr, 0); + gc_mark(&rb_objspace, ptr); } static void -gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) +gc_mark_children(rb_objspace_t *objspace, VALUE ptr) { register RVALUE *obj = RANY(ptr); goto marking; /* skip */ again: - obj = RANY(ptr); - if (rb_special_const_p(ptr)) return; /* special const not marked */ - if (obj->as.basic.flags == 0) return; /* free cell */ - if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */ + if (LIKELY(objspace->mark_func_data == 0)) { + obj = RANY(ptr); + if (!markable_object_p(objspace, ptr)) return; + if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */ + } + else { + gc_mark(objspace, ptr); + return; + } marking: if (FL_TEST(obj, FL_EXIVAR)) { @@ -2456,7 +2613,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) case NODE_RESBODY: case NODE_CLASS: case NODE_BLOCK_PASS: - gc_mark(objspace, (VALUE)obj->as.node.u2.node, lev); + gc_mark(objspace, (VALUE)obj->as.node.u2.node); /* fall through */ case NODE_BLOCK: /* 1,3 */ case NODE_ARRAY: @@ -2468,7 +2625,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) case NODE_CALL: case NODE_DEFS: case NODE_OP_ASGN1: - gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev); + gc_mark(objspace, (VALUE)obj->as.node.u1.node); /* fall through */ case NODE_SUPER: /* 3 */ case NODE_FCALL: @@ -2495,7 +2652,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) case NODE_ALIAS: case NODE_VALIAS: case NODE_ARGSCAT: - gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev); + gc_mark(objspace, (VALUE)obj->as.node.u1.node); /* fall through */ case NODE_GASGN: /* 2 */ case NODE_LASGN: @@ -2531,7 +2688,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) case NODE_SCOPE: /* 2,3 */ case NODE_CDECL: case NODE_OPT_ARG: - gc_mark(objspace, (VALUE)obj->as.node.u3.node, lev); + gc_mark(objspace, (VALUE)obj->as.node.u3.node); ptr = (VALUE)obj->as.node.u2.node; goto again; @@ -2539,11 +2696,11 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) { struct rb_args_info *args = obj->as.node.u3.args; if (args) { - if (args->pre_init) gc_mark(objspace, (VALUE)args->pre_init, lev); - if (args->post_init) gc_mark(objspace, (VALUE)args->post_init, lev); - if (args->opt_args) gc_mark(objspace, (VALUE)args->opt_args, lev); - if (args->kw_args) gc_mark(objspace, (VALUE)args->kw_args, lev); - if (args->kw_rest_arg) gc_mark(objspace, (VALUE)args->kw_rest_arg, lev); + if (args->pre_init) gc_mark(objspace, (VALUE)args->pre_init); + if (args->post_init) gc_mark(objspace, (VALUE)args->post_init); + if (args->opt_args) gc_mark(objspace, (VALUE)args->opt_args); + if (args->kw_args) gc_mark(objspace, (VALUE)args->kw_args); + if (args->kw_rest_arg) gc_mark(objspace, (VALUE)args->kw_rest_arg); } } ptr = (VALUE)obj->as.node.u2.node; @@ -2572,38 +2729,38 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) mark_locations_array(objspace, (VALUE*)obj->as.node.u1.value, obj->as.node.u3.cnt); - ptr = (VALUE)obj->as.node.u2.node; - goto again; + gc_mark(objspace, (VALUE)obj->as.node.u2.node); + break; case NODE_CREF: - gc_mark(objspace, obj->as.node.nd_omod, lev); - gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev); + gc_mark(objspace, obj->as.node.nd_refinements); + gc_mark(objspace, (VALUE)obj->as.node.u1.node); ptr = (VALUE)obj->as.node.u3.node; goto again; default: /* unlisted NODE */ if (is_pointer_to_heap(objspace, obj->as.node.u1.node)) { - gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev); + gc_mark(objspace, (VALUE)obj->as.node.u1.node); } if (is_pointer_to_heap(objspace, obj->as.node.u2.node)) { - gc_mark(objspace, (VALUE)obj->as.node.u2.node, lev); + gc_mark(objspace, (VALUE)obj->as.node.u2.node); } if (is_pointer_to_heap(objspace, obj->as.node.u3.node)) { - gc_mark(objspace, (VALUE)obj->as.node.u3.node, lev); + gc_mark(objspace, (VALUE)obj->as.node.u3.node); } } return; /* no need to mark class. */ } - gc_mark(objspace, obj->as.basic.klass, lev); + gc_mark(objspace, obj->as.basic.klass); switch (BUILTIN_TYPE(obj)) { case T_ICLASS: case T_CLASS: case T_MODULE: - mark_m_tbl(objspace, RCLASS_M_TBL(obj), lev); + mark_m_tbl(objspace, RCLASS_M_TBL(obj)); if (!RCLASS_EXT(obj)) break; - mark_tbl(objspace, RCLASS_IV_TBL(obj), lev); - mark_const_tbl(objspace, RCLASS_CONST_TBL(obj), lev); + mark_tbl(objspace, RCLASS_IV_TBL(obj)); + mark_const_tbl(objspace, RCLASS_CONST_TBL(obj)); ptr = RCLASS_SUPER(obj); goto again; @@ -2616,13 +2773,13 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) long i, len = RARRAY_LEN(obj); VALUE *ptr = RARRAY_PTR(obj); for (i=0; i < len; i++) { - gc_mark(objspace, *ptr++, lev); + gc_mark(objspace, *ptr++); } } break; case T_HASH: - mark_hash(objspace, obj->as.hash.ntbl, lev); + mark_hash(objspace, obj->as.hash.ntbl); ptr = obj->as.hash.ifnone; goto again; @@ -2649,25 +2806,25 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) long i, len = ROBJECT_NUMIV(obj); VALUE *ptr = ROBJECT_IVPTR(obj); for (i = 0; i < len; i++) { - gc_mark(objspace, *ptr++, lev); + gc_mark(objspace, *ptr++); } } break; case T_FILE: if (obj->as.file.fptr) { - gc_mark(objspace, obj->as.file.fptr->pathv, lev); - gc_mark(objspace, obj->as.file.fptr->tied_io_for_writing, lev); - gc_mark(objspace, obj->as.file.fptr->writeconv_asciicompat, lev); - gc_mark(objspace, obj->as.file.fptr->writeconv_pre_ecopts, lev); - gc_mark(objspace, obj->as.file.fptr->encs.ecopts, lev); - gc_mark(objspace, obj->as.file.fptr->write_lock, lev); + gc_mark(objspace, obj->as.file.fptr->pathv); + gc_mark(objspace, obj->as.file.fptr->tied_io_for_writing); + gc_mark(objspace, obj->as.file.fptr->writeconv_asciicompat); + gc_mark(objspace, obj->as.file.fptr->writeconv_pre_ecopts); + gc_mark(objspace, obj->as.file.fptr->encs.ecopts); + gc_mark(objspace, obj->as.file.fptr->write_lock); } break; case T_REGEXP: - gc_mark(objspace, obj->as.regexp.src, lev); - break; + ptr = obj->as.regexp.src; + goto again; case T_FLOAT: case T_BIGNUM: @@ -2675,7 +2832,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) break; case T_MATCH: - gc_mark(objspace, obj->as.match.regexp, lev); + gc_mark(objspace, obj->as.match.regexp); if (obj->as.match.str) { ptr = obj->as.match.str; goto again; @@ -2683,14 +2840,14 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) break; case T_RATIONAL: - gc_mark(objspace, obj->as.rational.num, lev); - gc_mark(objspace, obj->as.rational.den, lev); - break; + gc_mark(objspace, obj->as.rational.num); + ptr = obj->as.rational.den; + goto again; case T_COMPLEX: - gc_mark(objspace, obj->as.complex.real, lev); - gc_mark(objspace, obj->as.complex.imag, lev); - break; + gc_mark(objspace, obj->as.complex.real); + ptr = obj->as.complex.imag; + goto again; case T_STRUCT: { @@ -2698,7 +2855,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) VALUE *ptr = RSTRUCT_PTR(obj); while (len--) { - gc_mark(objspace, *ptr++, lev); + gc_mark(objspace, *ptr++); } } break; @@ -2711,47 +2868,24 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) } static void -gc_mark_all(rb_objspace_t *objspace) +gc_mark_stacked_objects(rb_objspace_t *objspace) { - RVALUE *p, *pend; - size_t i; + mark_stack_t *mstack = &objspace->mark_stack; + VALUE obj = 0; - init_mark_stack(objspace); - for (i = 0; i < heaps_used; i++) { - p = objspace->heap.sorted[i].start; pend = objspace->heap.sorted[i].end; - while (p < pend) { - if (MARKED_IN_BITMAP(GET_HEAP_BITMAP(p), p) && - p->as.basic.flags) { - gc_mark_children(objspace, (VALUE)p, 0); - } - p++; - } + if (!mstack->index) return; + while (pop_mark_stack(mstack, &obj)) { + gc_mark_children(objspace, obj); } + shrink_stack_chunk_cache(mstack); } -static void -gc_mark_rest(rb_objspace_t *objspace) -{ - VALUE tmp_arry[MARK_STACK_MAX]; - VALUE *p; - - p = (mark_stack_ptr - mark_stack) + tmp_arry; - MEMCPY(tmp_arry, mark_stack, VALUE, p - tmp_arry); - - init_mark_stack(objspace); - while (p != tmp_arry) { - p--; - gc_mark_children(objspace, *p, 0); - } -} - -#define MARK_STACK_EMPTY (mark_stack_ptr == mark_stack) - static void gc_marks(rb_objspace_t *objspace) { struct gc_list *list; rb_thread_t *th = GET_THREAD(); + gc_prof_mark_timer_start(objspace); objspace->heap.live_num = 0; @@ -2760,11 +2894,9 @@ gc_marks(rb_objspace_t *objspace) SET_STACK_END; - init_mark_stack(objspace); - th->vm->self ? rb_gc_mark(th->vm->self) : rb_vm_mark(th->vm); - mark_tbl(objspace, finalizer_table, 0); + mark_tbl(objspace, finalizer_table); mark_current_machine_context(objspace, th); rb_gc_mark_symbols(); @@ -2777,7 +2909,7 @@ gc_marks(rb_objspace_t *objspace) rb_mark_end_proc(); rb_gc_mark_global_tbl(); - mark_tbl(objspace, rb_class_tbl, 0); + mark_tbl(objspace, rb_class_tbl); /* mark generic instance variables for special constants */ rb_mark_generic_ivar_tbl(); @@ -2786,15 +2918,9 @@ gc_marks(rb_objspace_t *objspace) rb_gc_mark_unlinked_live_method_entries(th->vm); - /* gc_mark objects whose marking are not completed*/ - while (!MARK_STACK_EMPTY) { - if (mark_stack_overflow) { - gc_mark_all(objspace); - } - else { - gc_mark_rest(objspace); - } - } + /* marking-loop */ + gc_mark_stacked_objects(objspace); + gc_prof_mark_timer_stop(objspace); } @@ -2865,6 +2991,8 @@ rb_gc_unregister_address(VALUE *addr) static int garbage_collect(rb_objspace_t *objspace) { + struct mark_func_data_struct *prev_mark_func_data; + if (GC_NOTIFY) printf("start garbage_collect()\n"); if (!heaps) { @@ -2876,6 +3004,9 @@ garbage_collect(rb_objspace_t *objspace) gc_prof_timer_start(objspace); + prev_mark_func_data = objspace->mark_func_data; + objspace->mark_func_data = 0; + rest_sweep(objspace); during_gc++; @@ -2885,6 +3016,8 @@ garbage_collect(rb_objspace_t *objspace) gc_sweep(objspace); gc_prof_sweep_timer_stop(objspace); + objspace->mark_func_data = prev_mark_func_data; + gc_prof_timer_stop(objspace, Qtrue); if (GC_NOTIFY) printf("end garbage_collect()\n"); return TRUE; @@ -3153,6 +3286,46 @@ rb_gc_set_params(void) } } +static void +collect_refs(rb_objspace_t *objspace, VALUE obj) +{ + if (markable_object_p(objspace, obj) && !internal_object_p(obj)) { + st_insert((st_table *)objspace->mark_func_data->data, obj, Qtrue); + } +} + +static int +collect_keys(st_data_t key, st_data_t value, st_data_t data) +{ + VALUE ary = (VALUE)data; + rb_ary_push(ary, (VALUE)key); + return ST_CONTINUE; +} + +VALUE +rb_objspace_reachable_objects_from(VALUE obj) +{ + rb_objspace_t *objspace = &rb_objspace; + + if (markable_object_p(objspace, obj)) { + st_table *refs = st_init_numtable(); + struct mark_func_data_struct mfd; + VALUE ret = rb_ary_new(); + mfd.mark_func = collect_refs; + mfd.data = (VALUE)refs; + objspace->mark_func_data = &mfd; + + gc_mark_children(objspace, obj); + + objspace->mark_func_data = 0; + st_foreach(refs, collect_keys, (st_data_t)ret); + return ret; + } + else { + return Qnil; + } +} + /* ------------------------ Extended allocator ------------------------ */ diff --git a/gc.h b/gc.h index 96d22a6e09414b..741945349dc628 100644 --- a/gc.h +++ b/gc.h @@ -87,7 +87,10 @@ int ruby_get_stack_grow_direction(volatile VALUE *addr); #pragma GCC visibility push(default) #endif +/* exports for objspace module */ size_t rb_objspace_data_type_memsize(VALUE obj); +VALUE rb_objspace_reachable_objects_from(VALUE obj); + void rb_objspace_each_objects( int (*callback)(void *start, void *end, size_t stride, void *data), void *data); diff --git a/insns.def b/insns.def index a5d97ad0a40145..ae1998af637619 100644 --- a/insns.def +++ b/insns.def @@ -46,30 +46,44 @@ nop /** @c variable - @e Get value of local variable (pointed to by idx). - @j idx で指定されたローカル変数をスタックに置く。 + @e Get local variable (pointed by `idx' and `level'). + 'level' indicates the nesting depth from the current block. + @j level, idx で指定されたローカル変数の値をスタックに置く。 + level はブロックのネストレベルで、何段上かを示す。 */ DEFINE_INSN getlocal -(lindex_t idx) +(lindex_t idx, rb_num_t level) () (VALUE val) { - val = *(GET_LEP() - idx); + rb_num_t i; + VALUE *ep = GET_EP(); + for (i = 0; i < level; i++) { + ep = GET_PREV_EP(ep); + } + val = *(ep - idx); } /** @c variable - @e Set value of local variable (pointed to by idx) to val. - @j idx で指定されたローカル変数を val に設定する。 + @e Set a local variable (pointed to by 'idx') as val. + 'level' indicates the nesting depth from the current block. + @j level, idx で指定されたローカル変数の値を val にする。 + level はブロックのネストレベルで、何段上かを示す。 */ DEFINE_INSN setlocal -(lindex_t idx) +(lindex_t idx, rb_num_t level) (VALUE val) () { - *(GET_LEP() - idx) = val; + rb_num_t i; + VALUE *ep = GET_EP(); + for (i = 0; i < level; i++) { + ep = GET_PREV_EP(ep); + } + *(ep - idx) = val; } /** @@ -100,48 +114,6 @@ setspecial lep_svar_set(th, GET_LEP(), key, obj); } -/** - @c variable - @e Get value of block local variable (pointed to by idx and level). - 'level' indicates the nesting depth from the current block. - @j level, idx で指定されたブロックローカル変数の値をスタックに置く。 - level はブロックのネストレベルで、何段上かを示す。 - */ -DEFINE_INSN -getdynamic -(dindex_t idx, rb_num_t level) -() -(VALUE val) -{ - rb_num_t i; - VALUE *ep = GET_EP(); - for (i = 0; i < level; i++) { - ep = GET_PREV_EP(ep); - } - val = *(ep - idx); -} - -/** - @c variable - @e Set block local variable (pointed to by 'idx') as val. - 'level' indicates the nesting depth from the current block. - @j level, idx で指定されたブロックローカル変数の値を val にする。 - level はブロックのネストレベルで、何段上かを示す。 - */ -DEFINE_INSN -setdynamic -(dindex_t idx, rb_num_t level) -(VALUE val) -() -{ - rb_num_t i; - VALUE *ep = GET_EP(); - for (i = 0; i < level; i++) { - ep = GET_PREV_EP(ep); - } - *(ep - idx) = val; -} - /** @c variable @e Get value of instance variable id of self. @@ -991,7 +963,7 @@ defineclass */ DEFINE_INSN send -(ID op_id, rb_num_t op_argc, ISEQ blockiseq, rb_num_t op_flag, IC ic) +(ID op_id, rb_num_t op_argc, ISEQ blockiseq, rb_num_t op_flag, CALL_INFO ci) (...) (VALUE val) // inc += - (int)(op_argc + ((op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0)); { @@ -1006,7 +978,7 @@ send /* get receiver */ recv = TOPN(num); klass = CLASS_OF(recv); - me = vm_method_search(id, klass, ic, &defined_class); + me = vm_method_search(id, klass, ci, &defined_class); CALL_METHOD(num, blockptr, flag, id, me, recv, defined_class); } @@ -1055,7 +1027,7 @@ invokesuper if (me && me->def->type == VM_METHOD_TYPE_ISEQ && me->def->body.iseq == ip) { klass = RCLASS_SUPER(klass); - me = rb_method_entry_get_with_omod(Qnil, klass, id, &klass); + me = rb_method_entry_get_with_refinements(Qnil, klass, id, &klass); } CALL_METHOD(num, blockptr, flag, id, me, recv, klass); @@ -1314,7 +1286,7 @@ opt_case_dispatch */ DEFINE_INSN opt_plus -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1377,7 +1349,7 @@ opt_plus */ DEFINE_INSN opt_minus -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1425,7 +1397,7 @@ opt_minus */ DEFINE_INSN opt_mult -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1478,7 +1450,7 @@ opt_mult */ DEFINE_INSN opt_div -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1541,7 +1513,7 @@ opt_div */ DEFINE_INSN opt_mod -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1605,11 +1577,11 @@ opt_mod */ DEFINE_INSN opt_eq -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { - val = opt_eq_func(recv, obj, ic); + val = opt_eq_func(recv, obj, ci); if (val == Qundef) { /* other */ @@ -1626,16 +1598,16 @@ opt_eq */ DEFINE_INSN opt_neq -(IC ic, IC ic_eq) +(CALL_INFO ci, CALL_INFO ci_eq) (VALUE recv, VALUE obj) (VALUE val) { extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2); - const rb_method_entry_t *me = vm_method_search(idNeq, CLASS_OF(recv), ic, 0); + const rb_method_entry_t *me = vm_method_search(idNeq, CLASS_OF(recv), ci, 0); val = Qundef; if (check_cfunc(me, rb_obj_not_equal)) { - val = opt_eq_func(recv, obj, ic_eq); + val = opt_eq_func(recv, obj, ci_eq); if (val != Qundef) { val = RTEST(val) ? Qfalse : Qtrue; @@ -1657,7 +1629,7 @@ opt_neq */ DEFINE_INSN opt_lt -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1701,7 +1673,7 @@ opt_lt */ DEFINE_INSN opt_le -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1736,7 +1708,7 @@ opt_le */ DEFINE_INSN opt_gt -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1780,7 +1752,7 @@ opt_gt */ DEFINE_INSN opt_ge -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1814,7 +1786,7 @@ opt_ge */ DEFINE_INSN opt_ltlt -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1846,7 +1818,7 @@ opt_ltlt */ DEFINE_INSN opt_aref -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj) (VALUE val) { @@ -1876,7 +1848,7 @@ opt_aref */ DEFINE_INSN opt_aset -(IC ic) +(CALL_INFO ci) (VALUE recv, VALUE obj, VALUE set) (VALUE val) { @@ -1909,7 +1881,7 @@ opt_aset */ DEFINE_INSN opt_length -(IC ic) +(CALL_INFO ci) (VALUE recv) (VALUE val) { @@ -1944,7 +1916,7 @@ opt_length */ DEFINE_INSN opt_size -(IC ic) +(CALL_INFO ci) (VALUE recv) (VALUE val) { @@ -1979,7 +1951,7 @@ opt_size */ DEFINE_INSN opt_empty_p -(IC ic) +(CALL_INFO ci) (VALUE recv) (VALUE val) { @@ -2017,7 +1989,7 @@ opt_empty_p */ DEFINE_INSN opt_succ -(IC ic) +(CALL_INFO ci) (VALUE recv) (VALUE val) { @@ -2064,12 +2036,12 @@ opt_succ */ DEFINE_INSN opt_not -(IC ic) +(CALL_INFO ci) (VALUE recv) (VALUE val) { extern VALUE rb_obj_not(VALUE obj); - const rb_method_entry_t *me = vm_method_search(idNot, CLASS_OF(recv), ic, 0); + const rb_method_entry_t *me = vm_method_search(idNot, CLASS_OF(recv), ci, 0); if (check_cfunc(me, rb_obj_not)) { val = RTEST(recv) ? Qfalse : Qtrue; diff --git a/internal.h b/internal.h index 3a2331a867ae10..e538de4404f91d 100644 --- a/internal.h +++ b/internal.h @@ -203,9 +203,13 @@ struct rb_execarg { unsigned chdir_given : 1; unsigned new_pgroup_given : 1; unsigned new_pgroup_flag : 1; + unsigned uid_given : 1; + unsigned gid_given : 1; rb_pid_t pgroup_pgid; /* asis(-1), new pgroup(0), specified pgroup (0encs.enc, &fptr->encs.enc2, NULL); -} - -static void -rb_io_mode_enc(rb_io_t *fptr, const char *modestr) -{ - const char *p = strchr(modestr, ':'); - if (p) { - mode_enc(fptr, p+1); - } -} - int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p) { @@ -6344,12 +6327,12 @@ io_reopen(VALUE io, VALUE nfile) static VALUE rb_io_reopen(int argc, VALUE *argv, VALUE file) { - VALUE fname, nmode; + VALUE fname, nmode, opt; int oflags; rb_io_t *fptr; rb_secure(4); - if (rb_scan_args(argc, argv, "11", &fname, &nmode) == 1) { + if (rb_scan_args(argc, argv, "11:", &fname, &nmode, &opt) == 1) { VALUE tmp = rb_io_check_io(fname); if (!NIL_P(tmp)) { return io_reopen(file, tmp); @@ -6364,18 +6347,11 @@ rb_io_reopen(int argc, VALUE *argv, VALUE file) MEMZERO(fptr, rb_io_t, 1); } - if (!NIL_P(nmode)) { - VALUE intmode = rb_check_to_int(nmode); + if (!NIL_P(nmode) || !NIL_P(opt)) { int fmode; + convconfig_t convconfig; - if (!NIL_P(intmode)) { - oflags = NUM2INT(intmode); - fmode = rb_io_oflags_fmode(oflags); - } - else { - fmode = rb_io_modestr_fmode(StringValueCStr(nmode)); - } - + rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig); if (IS_PREP_STDIO(fptr) && ((fptr->mode & FMODE_READWRITE) & (fmode & FMODE_READWRITE)) != (fptr->mode & FMODE_READWRITE)) { @@ -6385,15 +6361,13 @@ rb_io_reopen(int argc, VALUE *argv, VALUE file) rb_io_fmode_modestr(fmode)); } fptr->mode = fmode; - if (NIL_P(intmode)) { - rb_io_mode_enc(fptr, StringValueCStr(nmode)); - } - fptr->encs.ecflags = 0; - fptr->encs.ecopts = Qnil; + fptr->encs = convconfig; + } + else { + oflags = rb_io_fmode_oflags(fptr->mode); } fptr->pathv = rb_str_new_frozen(fname); - oflags = rb_io_fmode_oflags(fptr->mode); if (fptr->fd < 0) { fptr->fd = rb_sysopen(fptr->pathv, oflags, 0666); fptr->stdio_file = 0; diff --git a/iseq.c b/iseq.c index e34537e8718143..58f5905f338918 100644 --- a/iseq.c +++ b/iseq.c @@ -84,6 +84,7 @@ iseq_free(void *ptr) RUBY_FREE_UNLESS_NULL(iseq->line_info_table); RUBY_FREE_UNLESS_NULL(iseq->local_table); RUBY_FREE_UNLESS_NULL(iseq->ic_entries); + RUBY_FREE_UNLESS_NULL(iseq->callinfo_entries); RUBY_FREE_UNLESS_NULL(iseq->catch_table); RUBY_FREE_UNLESS_NULL(iseq->arg_opt_table); RUBY_FREE_UNLESS_NULL(iseq->arg_keyword_table); @@ -202,11 +203,11 @@ set_relation(rb_iseq_t *iseq, const VALUE parent) if (type == ISEQ_TYPE_TOP) { /* toplevel is private */ iseq->cref_stack = NEW_CREF(rb_cObject); - iseq->cref_stack->nd_omod = Qnil; + iseq->cref_stack->nd_refinements = Qnil; iseq->cref_stack->nd_visi = NOEX_PRIVATE; if (th->top_wrapper) { NODE *cref = NEW_CREF(th->top_wrapper); - cref->nd_omod = Qnil; + cref->nd_refinements = Qnil; cref->nd_visi = NOEX_PRIVATE; cref->nd_next = iseq->cref_stack; iseq->cref_stack = cref; @@ -214,7 +215,7 @@ set_relation(rb_iseq_t *iseq, const VALUE parent) } else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) { iseq->cref_stack = NEW_CREF(0); /* place holder */ - iseq->cref_stack->nd_omod = Qnil; + iseq->cref_stack->nd_refinements = Qnil; } else if (RTEST(parent)) { rb_iseq_t *piseq; @@ -963,7 +964,7 @@ id_to_name(ID id, VALUE default_value) return str; } -static VALUE +VALUE insn_operand_intern(rb_iseq_t *iseq, VALUE insn, int op_no, VALUE op, int len, size_t pos, VALUE *pnop, VALUE child) @@ -981,23 +982,20 @@ insn_operand_intern(rb_iseq_t *iseq, ret = rb_sprintf("%"PRIuVALUE, op); break; - case TS_LINDEX: - { - rb_iseq_t *liseq = iseq->local_iseq; - int lidx = liseq->local_size - (int)op; - - ret = id_to_name(liseq->local_table[lidx], INT2FIX('*')); - break; - } - case TS_DINDEX:{ - if (insn == BIN(getdynamic) || insn == BIN(setdynamic)) { - rb_iseq_t *diseq = iseq; - VALUE level = *pnop, i; + case TS_LINDEX:{ + if (insn == BIN(getlocal) || insn == BIN(setlocal)) { + if (pnop) { + rb_iseq_t *diseq = iseq; + VALUE level = *pnop, i; - for (i = 0; i < level; i++) { - diseq = diseq->parent_iseq; + for (i = 0; i < level; i++) { + diseq = diseq->parent_iseq; + } + ret = id_to_name(diseq->local_table[diseq->local_size - op], INT2FIX('*')); + } + else { + ret = rb_sprintf("%"PRIuVALUE, op); } - ret = id_to_name(diseq->local_table[diseq->local_size - op], INT2FIX('*')); } else { ret = rb_inspect(INT2FIX(op)); @@ -1011,7 +1009,9 @@ insn_operand_intern(rb_iseq_t *iseq, op = obj_resurrect(op); ret = rb_inspect(op); if (CLASS_OF(op) == rb_cISeq) { - rb_ary_push(child, op); + if (child) { + rb_ary_push(child, op); + } } break; @@ -1049,7 +1049,7 @@ insn_operand_intern(rb_iseq_t *iseq, break; default: - rb_bug("rb_iseq_disasm: unknown operand type: %c", type); + rb_bug("insn_operand_intern: unknown operand type: %c", type); } return ret; } @@ -1305,7 +1305,7 @@ rb_iseq_disasm(VALUE self) * 0004 putobject 2 * 0006 opt_plus * 0008 dup - * 0009 setdynamic num, 0 + * 0009 setlocal num, 0 * 0012 leave * */ @@ -1509,7 +1509,6 @@ iseq_data_to_ary(rb_iseq_t *iseq) break; } case TS_LINDEX: - case TS_DINDEX: case TS_NUM: rb_ary_push(ary, INT2FIX(*seq)); break; @@ -1659,7 +1658,7 @@ rb_iseq_clone(VALUE iseqval, VALUE newcbase) } if (newcbase) { iseq1->cref_stack = NEW_CREF(newcbase); - iseq1->cref_stack->nd_omod = iseq0->cref_stack->nd_omod; + iseq1->cref_stack->nd_refinements = iseq0->cref_stack->nd_refinements; iseq1->cref_stack->nd_visi = iseq0->cref_stack->nd_visi; if (iseq0->cref_stack->nd_next) { iseq1->cref_stack->nd_next = iseq0->cref_stack->nd_next; diff --git a/lib/abbrev.rb b/lib/abbrev.rb index aac953aa82bb28..b4b12b88b51666 100644 --- a/lib/abbrev.rb +++ b/lib/abbrev.rb @@ -26,21 +26,45 @@ # "rule" => "rules", # "rules" => "rules" } # -# It also adds an +abbrev+ method to class Array. +# It also provides an array core extension, Array#abbrev. +# +# pp %w{april may}.abbrev +# #=> {"summe"=>"summer", +# "summ"=>"summer", +# "sum"=>"summer", +# "su"=>"summer", +# "s"=>"summer", +# "winte"=>"winter", +# "wint"=>"winter", +# "win"=>"winter", +# "wi"=>"winter", +# "w"=>"winter", +# "summer"=>"summer", +# "winter"=>"winter"} module Abbrev # Given a set of strings, calculate the set of unambiguous # abbreviations for those strings, and return a hash where the keys # are all the possible abbreviations and the values are the full - # strings. Thus, given input of "car" and "cone", the keys pointing - # to "car" would be "ca" and "car", while those pointing to "cone" - # would be "co", "con", and "cone". + # strings. + # + # Thus, given input of "car" and "cone", the keys pointing to "car" would be + # "ca" and "car", while those pointing to "cone" would be "co", "con", and + # "cone". + # + # require 'abbrev' + # require 'pp' + # + # pp Abbrev.abbrev(['car', 'cone']) + # #=> {"ca"=>"car", "con"=>"cone", "co"=>"cone", "car"=>"car", "cone"=>"cone"} # # The optional +pattern+ parameter is a pattern or a string. Only # input strings that match the pattern or start with the string # are included in the output hash. - + # + # pp %w{car box cone}.abbrev(/b/) + # #=> {"bo"=>"box", "b"=>"box", "box"=>"box"} def abbrev(words, pattern = nil) table = {} seen = Hash.new(0) @@ -83,15 +107,20 @@ class Array # Calculates the set of unambiguous abbreviations for the strings in # +self+. # + # abbr = %w{ car cone }.abbrev + # abbr #=> { "ca" => "car", "car" => "car", + # "co" => "cone", "con" => "cone", + # "cone" => "cone" } + # # The optional +pattern+ parameter is a pattern or a string. Only # input strings that match the pattern or start with the string # are included in the output hash. # - # %w{ car cone }.abbrev #=> { "ca" => "car", "car" => "car", - # "co" => "cone", "con" => "cone", - # "cone" => "cone" } + # abbr = %w{ fast boat day }.abbrev(/^.a.*$/) + # abbr #=> {"fas"=>"fast","fa"=>"fast", + # "da"=>"day", "fast"=>"fast", "day"=>"day"} # - # See also Abbrev#abbrev + # See also Abbrev.abbrev def abbrev(pattern = nil) Abbrev::abbrev(self, pattern) end diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb index e077c6d523f78d..f1e8d3467a9914 100644 --- a/lib/cgi/core.rb +++ b/lib/cgi/core.rb @@ -771,6 +771,7 @@ def self.accept_charset=(accept_charset) # "html4":: HTML 4.0 # "html4Tr":: HTML 4.0 Transitional # "html4Fr":: HTML 4.0 with Framesets + # "html5":: HTML 5 # # block:: # If provided, the block is called when an invalid encoding is @@ -831,6 +832,11 @@ def initialize(options = {}, &block) # :yields: name, value extend Html4Fr element_init() extend HtmlExtension + when "html5" + require 'cgi/html' + extend Html5 + element_init() + extend HtmlExtension end end diff --git a/lib/cgi/html.rb b/lib/cgi/html.rb index 1d7a815b954715..3054279b546a25 100644 --- a/lib/cgi/html.rb +++ b/lib/cgi/html.rb @@ -922,7 +922,7 @@ def #{element.downcase}(attributes = {}) # O O or - O for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY - COLGROUP TR TH TD HEAD] + COLGROUP TR TH TD HEAD ] methods << <<-BEGIN + nO_element_def(element) + <<-END def #{element.downcase}(attributes = {}) BEGIN @@ -1020,6 +1020,57 @@ def #{element.downcase}(attributes = {}) end end # Html4Fr -end + # Mixin module for HTML version 5 generation methods. + module Html5 # :nodoc: + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + def element_init + extend TagMaker + methods = "" + # - - + for element in %w[ SECTION NAV ARTICLE ASIDE HGROUP + FOOTER FIGURE FIGCAPTION S TIME U MARK RUBY BDI IFRAME + VIDEO AUDIO CANVAS DATALIST OUTPUT PROGRESS METER DETAILS + SUMMARY MENU DIALOG I B SMALL EM STRONG DFN CODE SAMP KBD + VAR CITE ABBR SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT + H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT + FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT + TEXTAREA FORM A BLOCKQUOTE CAPTION ] + methods += <<-BEGIN + nn_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # - O EMPTY + for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META + COMMAND EMBED KEYGEN SOURCE TRACK WBR ] + methods += <<-BEGIN + nOE_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # O O or - O + for element in %w[ HTML HEAD BODY P DT DD LI OPTION THEAD TFOOT TBODY + OPTGROUP COLGROUP RT RP TR TH TD ] + methods += <<-BEGIN + nO_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + eval(methods) + end + + end # Html5 +end diff --git a/method.h b/method.h index 9e7a615a6c339c..3d8a0e44cefe14 100644 --- a/method.h +++ b/method.h @@ -91,8 +91,8 @@ void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex); rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr); -rb_method_entry_t *rb_method_entry_get_with_omod(VALUE omod, VALUE klass, ID id, VALUE *define_class_ptr); -rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, VALUE omod, ID id, VALUE *define_class_ptr); +rb_method_entry_t *rb_method_entry_get_with_refinements(VALUE refinements, VALUE klass, ID id, VALUE *define_class_ptr); +rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, VALUE refinements, ID id, VALUE *define_class_ptr); rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex); int rb_method_entry_arity(const rb_method_entry_t *me); diff --git a/node.h b/node.h index 19e9129763c6d1..09186094ba603d 100644 --- a/node.h +++ b/node.h @@ -281,7 +281,7 @@ typedef struct RNode { #define nd_set_line(n,l) \ RNODE(n)->flags=((RNODE(n)->flags&~(-1<uid_given) { + rb_raise(rb_eArgError, "uid option specified twice"); + } + check_uid_switch(); + { + PREPARE_GETPWNAM; + eargp->uid = OBJ2UID(val); + eargp->uid_given = 1; + } +#else + rb_raise(rb_eNotImpError, + "uid option is unimplemented on this machine"); +#endif + } + else if (id == rb_intern("gid")) { +#ifdef HAVE_SETGID + if (eargp->gid_given) { + rb_raise(rb_eArgError, "gid option specified twice"); + } + check_gid_switch(); + { + PREPARE_GETGRNAM; + eargp->gid = OBJ2GID(val); + eargp->gid_given = 1; + } +#else + rb_raise(rb_eNotImpError, + "gid option is unimplemented on this machine"); +#endif + } else { return ST_STOP; } @@ -2875,6 +2956,23 @@ rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp, } } +#ifdef HAVE_SETGID + if (eargp->gid_given) { + if (setgid(eargp->gid) < 0) { + ERRMSG("setgid"); + return -1; + } + } +#endif +#ifdef HAVE_SETUID + if (eargp->uid_given) { + if (setuid(eargp->uid) < 0) { + ERRMSG("setuid"); + return -1; + } + } +#endif + if (sargp) { VALUE ary = sargp->fd_dup2; if (ary != Qfalse) { @@ -4581,22 +4679,7 @@ check_gid_switch(void) * Process::UID, and Process::GID modules. */ -#if 1 -#define p_uid_from_name p_uid_from_name -#define p_gid_from_name p_gid_from_name -#endif - #if defined(HAVE_PWD_H) -# ifdef HAVE_GETPWNAM_R -# define PREPARE_GETPWNAM \ - long getpw_buf_len = sysconf(_SC_GETPW_R_SIZE_MAX); \ - char *getpw_buf = ALLOCA_N(char, (getpw_buf_len < 0 ? (getpw_buf_len = 4096) : getpw_buf_len)); -# define OBJ2UID(id) obj2uid((id), getpw_buf, getpw_buf_len) -# else -# define PREPARE_GETPWNAM /* do nothing */ -# define OBJ2UID(id) obj2uid((id)) -# endif - static rb_uid_t obj2uid(VALUE id # ifdef HAVE_GETPWNAM_R @@ -4642,26 +4725,9 @@ p_uid_from_name(VALUE self, VALUE id) return UIDT2NUM(OBJ2UID(id)); } # endif -#else -# define PREPARE_GETPWNAM /* do nothing */ -# define OBJ2UID(id) NUM2UIDT(id) -# ifdef p_uid_from_name -# undef p_uid_from_name -# define p_uid_from_name rb_f_notimplement -# endif #endif #if defined(HAVE_GRP_H) -# ifdef HAVE_GETGRNAM_R -# define PREPARE_GETGRNAM \ - long getgr_buf_len = sysconf(_SC_GETGR_R_SIZE_MAX); \ - char *getgr_buf = ALLOCA_N(char, (getgr_buf_len < 0 ? (getgr_buf_len = 4096) : getgr_buf_len)); -# define OBJ2GID(id) obj2gid((id), getgr_buf, getgr_buf_len) -# else -# define PREPARE_GETGRNAM /* do nothing */ -# define OBJ2GID(id) obj2gid((id)) -# endif - static rb_gid_t obj2gid(VALUE id # ifdef HAVE_GETGRNAM_R @@ -4707,13 +4773,6 @@ p_gid_from_name(VALUE self, VALUE id) return GIDT2NUM(OBJ2GID(id)); } # endif -#else -# define PREPARE_GETGRNAM /* do nothing */ -# define OBJ2GID(id) NUM2GIDT(id) -# ifdef p_gid_from_name -# undef p_gid_from_name -# define p_gid_from_name rb_f_notimplement -# endif #endif #if defined HAVE_SETUID diff --git a/string.c b/string.c index 26463b95e27c17..9281e4cfbb4df8 100644 --- a/string.c +++ b/string.c @@ -2158,7 +2158,7 @@ rb_str_concat(VALUE str1, VALUE str2) rb_raise(rb_eRangeError, "invalid codepoint 0x%X in %s", code, rb_enc_name(enc)); } rb_str_resize(str1, pos+len); - strncpy(RSTRING_PTR(str1) + pos, buf, len); + memcpy(RSTRING_PTR(str1) + pos, buf, len); if (cr == ENC_CODERANGE_7BIT && code > 127) cr = ENC_CODERANGE_VALID; ENC_CODERANGE_SET(str1, cr); @@ -2699,6 +2699,10 @@ rb_str_rindex_m(int argc, VALUE *argv, VALUE str) * obj.=~, passing str as an argument. The default * =~ in Object returns nil. * + * Note: str =~ regexp is not the same as + * regexp =~ str. Strings captured from named capture groups + * are assigned to local variables only in the second case. + * * "cat o' 9 tails" =~ /\d/ #=> 7 * "cat o' 9 tails" =~ 9 #=> nil */ diff --git a/template/Doxyfile.tmpl b/template/Doxyfile.tmpl index aff688a8e49243..6b91e20c1d7cfc 100644 --- a/template/Doxyfile.tmpl +++ b/template/Doxyfile.tmpl @@ -85,7 +85,6 @@ GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES -SHOW_DIRECTORIES = NO SHOW_FILES = YES SHOW_NAMESPACES = YES LAYOUT_FILE = @@ -140,7 +139,6 @@ HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = -HTML_ALIGN_MEMBERS = YES HTML_DYNAMIC_SECTIONS = NO GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" diff --git a/template/encdb.h.tmpl b/template/encdb.h.tmpl index 4275a4c2f0186b..308227e9e9d1b3 100644 --- a/template/encdb.h.tmpl +++ b/template/encdb.h.tmpl @@ -61,7 +61,7 @@ encdirs.each do |encdir| raise ArgumentError, '%s:%d: ENC_ALIAS: %s is not defined yet. (alias %s)' % [fn, $., $2, $1] unless defs[$2.upcase] - when /^ENC_DUMMY\(\s*"([^"]+)"/ + when /^ENC_DUMMY\w*\(\s*"([^"]+)"/ count += 1 else next diff --git a/test/cgi/test_cgi_core.rb b/test/cgi/test_cgi_core.rb index dab22d3114db70..171ff7b8bc179e 100644 --- a/test/cgi/test_cgi_core.rb +++ b/test/cgi/test_cgi_core.rb @@ -353,6 +353,14 @@ def test_cgi_core_htmltype cgi = CGI.new('html4Fr') expected = '' assert_equal(expected, cgi.doctype) + ## html5 + cgi = CGI.new('html5') + expected = '' + assert_equal(expected, cgi.doctype) + # cgi.header not broken + expected = "Content-Type: text/html\r\n\r\n" + actual = cgi.header + assert_equal(expected, actual) end diff --git a/test/drb/drbtest.rb b/test/drb/drbtest.rb index cb553eee59c4a2..ac0f5c6be92d01 100644 --- a/test/drb/drbtest.rb +++ b/test/drb/drbtest.rb @@ -76,7 +76,7 @@ def teardown while (@there&&@there.to_s rescue nil) # nop end - signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :INT : :TERM + signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM Thread.list.each {|th| if th.respond_to?(:pid) && th[:drb_service] == @service_name begin @@ -297,7 +297,7 @@ def teardown while (@there&&@there.to_s rescue nil) # nop end - signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :INT : :TERM + signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM Thread.list.each {|th| if th.respond_to?(:pid) && th[:drb_service] == @service_name begin diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb index fc7bfa980627f4..6a35ca29f15d49 100644 --- a/test/net/http/test_http.rb +++ b/test/net/http/test_http.rb @@ -88,11 +88,13 @@ def test_edit_path_proxy end def test_proxy_address - http = Net::HTTP.new 'example', nil, 'proxy.example' - assert_equal 'proxy.example', http.proxy_address + clean_http_proxy_env do + http = Net::HTTP.new 'example', nil, 'proxy.example' + assert_equal 'proxy.example', http.proxy_address - http = Net::HTTP.new 'example', nil - assert_equal nil, http.proxy_address + http = Net::HTTP.new 'example', nil + assert_equal nil, http.proxy_address + end end def test_proxy_address_ENV @@ -137,13 +139,15 @@ def test_proxy_eh_ENV_no_proxy end def test_proxy_port - http = Net::HTTP.new 'exmaple', nil, 'proxy.example' - assert_equal 'proxy.example', http.proxy_address - assert_equal 80, http.proxy_port - http = Net::HTTP.new 'exmaple', nil, 'proxy.example', 8000 - assert_equal 8000, http.proxy_port - http = Net::HTTP.new 'exmaple', nil - assert_equal nil, http.proxy_port + clean_http_proxy_env do + http = Net::HTTP.new 'exmaple', nil, 'proxy.example' + assert_equal 'proxy.example', http.proxy_address + assert_equal 80, http.proxy_port + http = Net::HTTP.new 'exmaple', nil, 'proxy.example', 8000 + assert_equal 8000, http.proxy_port + http = Net::HTTP.new 'exmaple', nil + assert_equal nil, http.proxy_port + end end def test_proxy_port_ENV diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 345b00f73709ac..723e6e431e4a7f 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -68,4 +68,28 @@ def test_count_tdata_objects ObjectSpace.count_tdata_objects(arg) assert_equal(false, arg.empty?) end + + def test_reachable_objects_from + assert_equal(nil, ObjectSpace.reachable_objects_from(nil)) + assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c'])) + + assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a'])) + assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v])) + assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v])) + + long_ary = Array.new(1_000){''} + max = 0 + + ObjectSpace.each_object{|o| + refs = ObjectSpace.reachable_objects_from(o) + max = [refs.size, max].max + + unless refs.nil? + refs.each_with_index {|ro, i| + assert_not_nil(ro, "#{i}: this referenced object is internal object") + } + end + } + assert_operator(max, :>=, 1_001, "1000 elems + Array class") + end end diff --git a/test/openssl/test_config.rb b/test/openssl/test_config.rb index 604ccaa639ed38..88bb5af7bed779 100644 --- a/test/openssl/test_config.rb +++ b/test/openssl/test_config.rb @@ -21,11 +21,11 @@ def teardown end def test_constants - skip "DEFAULT_CONFIG_FILE may return a wrong path on your platforms. [Bug #6830]" if /mswin|mingw|darwin/ =~ RUBY_PLATFORM - assert(defined?(OpenSSL::Config::DEFAULT_CONFIG_FILE)) + config_file = OpenSSL::Config::DEFAULT_CONFIG_FILE + skip "DEFAULT_CONFIG_FILE may return a wrong path on your platforms. [Bug #6830]" unless File.readable?(config_file) assert_nothing_raised do - OpenSSL::Config.load(OpenSSL::Config::DEFAULT_CONFIG_FILE) + OpenSSL::Config.load(config_file) end end diff --git a/test/ruby/envutil.rb b/test/ruby/envutil.rb index d96da1308fac65..f0c4636981ef6d 100644 --- a/test/ruby/envutil.rb +++ b/test/ruby/envutil.rb @@ -65,6 +65,11 @@ def invoke_ruby(args, stdin_data="", capture_stdout=false, capture_stderr=false, stdout = th_stdout.value if capture_stdout stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout else + signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM + begin + Process.kill signal, pid + rescue Errno::ESRCH + end raise Timeout::Error end out_p.close if capture_stdout diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb index f26514b2083bf3..8b33a5e8263259 100644 --- a/test/ruby/test_enumerator.rb +++ b/test/ruby/test_enumerator.rb @@ -13,7 +13,6 @@ def foo(*a) end def enum_test obj - i = 0 obj.map{|e| e }.sort @@ -349,7 +348,7 @@ def test_inspect e = (0..10).each_cons(2) assert_equal("#", e.inspect) - e = Enumerator.new {|y| x = y.yield; 10 } + e = Enumerator.new {|y| y.yield; 10 } assert_match(/\A#/, e.inspect) a = [] diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 93b24bbdd56402..17492706edbd48 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -451,4 +451,54 @@ def test_rescue_splat_argument end end end + + def test_to_s_taintness_propagation + for exc in [Exception, NameError] + m = "abcdefg" + e = exc.new(m) + e.taint + s = e.to_s + assert_equal(false, m.tainted?, + "#{exc}#to_s should not propagate taintness") + assert_equal(false, s.tainted?, + "#{exc}#to_s should not propagate taintness") + end + + o = Object.new + def o.to_str + "foo" + end + o.taint + e = NameError.new(o) + s = e.to_s + assert_equal(false, s.tainted?) + end + + def test_exception_to_s_should_not_propagate_untrustedness + favorite_lang = "Ruby" + + for exc in [Exception, NameError] + assert_raise(SecurityError) do + lambda { + $SAFE = 4 + exc.new(favorite_lang).to_s + favorite_lang.replace("Python") + }.call + end + end + + assert_raise(SecurityError) do + lambda { + $SAFE = 4 + o = Object.new + o.singleton_class.send(:define_method, :to_str) { + favorite_lang + } + NameError.new(o).to_s + favorite_lang.replace("Python") + }.call + end + + assert_equal("Ruby", favorite_lang) + end end diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 4ee05911fbb50e..c70d972c7408c2 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -1732,6 +1732,74 @@ def test_reopen_mode } end + def test_reopen_opt + feature7103 = '[ruby-core:47806]' + make_tempfile {|t| + open(__FILE__) do |f| + assert_nothing_raised(feature7103) { + f.reopen(t.path, "r", binmode: true) + } + assert_equal("foo\n", f.gets) + end + + open(__FILE__) do |f| + assert_nothing_raised(feature7103) { + f.reopen(t.path, autoclose: false) + } + assert_equal("foo\n", f.gets) + end + } + end + + def make_tempfile_for_encoding + t = make_tempfile + open(t.path, "rb+:utf-8") {|f| f.puts "\u7d05\u7389bar\n"} + if block_given? + yield t + else + t + end + ensure + t.close(true) if t and block_given? + end + + def test_reopen_encoding + make_tempfile_for_encoding {|t| + open(__FILE__) {|f| + f.reopen(t.path, "r:utf-8") + s = f.gets + assert_equal(Encoding::UTF_8, s.encoding) + assert_equal("\u7d05\u7389bar\n", s) + } + + open(__FILE__) {|f| + f.reopen(t.path, "r:UTF-8:EUC-JP") + s = f.gets + assert_equal(Encoding::EUC_JP, s.encoding) + assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s) + } + } + end + + def test_reopen_opt_encoding + feature7103 = '[ruby-core:47806]' + make_tempfile_for_encoding {|t| + open(__FILE__) {|f| + assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "ASCII-8BIT")} + s = f.gets + assert_equal(Encoding::ASCII_8BIT, s.encoding) + assert_equal("\xe7\xb4\x85\xe7\x8e\x89bar\n", s) + } + + open(__FILE__) {|f| + assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "UTF-8:EUC-JP")} + s = f.gets + assert_equal(Encoding::EUC_JP, s.encoding) + assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s) + } + } + end + def test_foreach a = [] IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x } diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index b8edd2467036ce..151825e5e4c3d7 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -21,6 +21,17 @@ def test_dup end end + def test_init_dupclone + cls = Class.new do + def initialize_clone(orig); throw :initialize_clone; end + def initialize_dup(orig); throw :initialize_dup; end + end + + obj = cls.new + assert_throws(:initialize_clone) {obj.clone} + assert_throws(:initialize_dup) {obj.dup} + end + def test_instance_of assert_raise(TypeError) { 1.instance_of?(1) } end diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 134797c2ad018f..e8c4cfa7d9344b 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1478,6 +1478,50 @@ def test_execopts_new_pgroup assert_nothing_raised { IO.popen([*TRUECOMMAND, :new_pgroup=>true]) {} } end + def test_execopts_uid + feature6975 = '[ruby-core:47414]' + + [30000, [ENV["USER"], Process.uid]].each do |user, uid| + assert_nothing_raised(feature6975) do + begin + system(*TRUECOMMAND, uid: user) + rescue Errno::EPERM, NotImplementedError + end + end + + uid = "#{uid || user}" + assert_nothing_raised(feature6975) do + begin + u = IO.popen([RUBY, "-e", "print Process.uid", uid: user], &:read) + assert_equal(uid, u, feature6975) + rescue Errno::EPERM, NotImplementedError + end + end + end + end + + def test_execopts_gid + feature6975 = '[ruby-core:47414]' + + [30000, *Process.groups.map {|g| g = Etc.getgrgid(g); [g.name, g.gid]}].each do |group, gid| + assert_nothing_raised(feature6975) do + begin + system(*TRUECOMMAND, gid: group) + rescue Errno::EPERM, NotImplementedError + end + end + + gid = "#{gid || group}" + assert_nothing_raised(feature6975) do + begin + g = IO.popen([RUBY, "-e", "print Process.gid", gid: group], &:read) + assert_equal(gid, g, feature6975) + rescue Errno::EPERM, NotImplementedError + end + end + end + end + def test_sigpipe system(RUBY, "-e", "") with_pipe {|r, w| diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index 98645745e9ffb8..6a1a1ccf807edf 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -361,6 +361,34 @@ def foo assert_equal([:m1, :m2], m2.module_eval { obj.foo }) end + def test_refine_module_with_double_overriding + m1 = Module.new { + def foo + [:m1] + end + } + c = Class.new { + include m1 + } + m2 = Module.new { + refine m1 do + def foo + super << :m2 + end + end + } + m3 = Module.new { + using m2 + refine m1 do + def foo + super << :m3 + end + end + } + obj = c.new + assert_equal([:m1, :m2, :m3], m3.module_eval { obj.foo }) + end + def test_refine_module_and_call_superclass_method m1 = Module.new c1 = Class.new { @@ -399,4 +427,97 @@ def test_refine_neither_class_nor_module } end end + + def test_refine_in_class_and_class_eval + c = Class.new { + refine Fixnum do + def foo + "c" + end + end + } + assert_equal("c", c.class_eval { 123.foo }) + end + + def test_kernel_using_class + c = Class.new + assert_raise(TypeError) do + using c + end + end + + def test_module_using_class + c = Class.new + m = Module.new + assert_raise(TypeError) do + m.module_eval do + using c + end + end + end + + def test_refinements_empty + m = Module.new + assert(m.refinements.empty?) + end + + def test_refinements_one + c = Class.new + c_ext = nil + m = Module.new { + refine c do + c_ext = self + end + } + assert_equal({c => c_ext}, m.refinements) + end + + def test_refinements_two + c1 = Class.new + c1_ext = nil + c2 = Class.new + c2_ext = nil + m = Module.new { + refine c1 do + c1_ext = self + end + + refine c2 do + c2_ext = self + end + } + assert_equal({c1 => c1_ext, c2 => c2_ext}, m.refinements) + end + + def test_refinements_duplicate_refine + c = Class.new + c_ext = nil + m = Module.new { + refine c do + c_ext = self + end + refine c do + end + } + assert_equal({c => c_ext}, m.refinements) + end + + def test_refinements_no_recursion + c1 = Class.new + c1_ext = nil + m1 = Module.new { + refine c1 do + c1_ext = self + end + } + c2 = Class.new + c2_ext = nil + m2 = Module.new { + using m1 + refine c2 do + c2_ext = self + end + } + assert_equal({c2 => c2_ext}, m2.refinements) + end end diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index d24bcccd4d8a37..64999ddae2e1f6 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -398,59 +398,6 @@ class << self define_method(:method_added, Module.method(:method_added)) end - def test_tracepoint - events = [] - trace = nil - xyzzy = nil - local_var = :outer - eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy' - 1: trace = TracePoint.trace(){|tp| - 2: events << [tp.event, tp.line, tp.file, tp.klass, tp.id, tp.self, tp.binding.eval("local_var")] - 3: } - 4: 1.times{|;local_var| local_var = :inner - 5: tap{} - 6: } - 7: class XYZZY - 8: local_var = :XYZZY_outer - 9: def foo - 10: local_var = :XYZZY_foo - 11: bar - 12: end - 13: def bar - 14: local_var = :XYZZY_bar - 15: tap{} - 16: end - 17: end - 18: xyzzy = XYZZY.new - 19: xyzzy.foo - 20: trace.untrace - EOF - - events.each{|ev| - STDERR.puts [ev[0], ev[1]].inspect - STDERR.puts ev.inspect - } - - [ - # - [:c_return, 1, "xyzzy", self.class, :trace, TracePoint, :outer], - [:line, 4, 'xyzzy', self.class, :test_tracepoint, self, :outer], - [:c_call, 4, 'xyzzy', Integer, :times, 1, :outer], - [:line, 4, 'xyzzy', self.class, :test_tracepoint, self, nil], - [:line, 5, 'xyzzy', self.class, :test_tracepoint, self, :inner], - [:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner], - [:line, 7, 'xyzzy', self.class, :test_tracepoint, self, :outer], - [:c_call, 7, "xyzzy", Class, :new, TestSetTraceFunc::XYZZY, :outer], - [:c_call, 7, "xyzzy", BasicObject, :initialize, xyzzy, :outer], - [:line, 8, 'xyzzy', self.class, :test_tracepoint, self, :outer], - [:c_call, 9, 'xyzzy', TracePoint, :untrace, trace,:outer], - ].each{|e| - assert_equal e, events.shift - } - assert_equal [], events - end - - def trace_by_tracepoint *trace_events events = [] trace = nil diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index bb0d9b04f9f489..d5778ab74751d2 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -473,6 +473,12 @@ def test_clone def test_concat assert_equal(S("world!"), S("world").concat(33)) assert_equal(S("world!"), S("world").concat(S('!'))) + + bug7090 = '[ruby-core:47751]' + result = S("").force_encoding(Encoding::UTF_16LE) + result << 0x0300 + expected = S("\u0300".encode(Encoding::UTF_16LE)) + assert_equal(expected, result, bug7090) end def test_count diff --git a/test/ruby/test_unicode_escape.rb b/test/ruby/test_unicode_escape.rb index 088f81ce144884..c1b1e2ca40b49f 100644 --- a/test/ruby/test_unicode_escape.rb +++ b/test/ruby/test_unicode_escape.rb @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- require 'test/unit' +require_relative 'envutil' class TestUnicodeEscape < Test::Unit::TestCase def test_basic @@ -47,7 +48,8 @@ def test_basic # \u in %x strings assert_match(/^("?)A\1$/, `echo "\u0041"`) #" assert_match(/^("?)A\1$/, %x{echo "\u0041"}) #" - assert_match(/^("?)ü\1$/, `echo "\u{FC}"`.force_encoding("utf-8")) #" + assert_match(/^("?)ü\1$/, + `#{EnvUtil.rubybin} -e "#coding:utf-8\nputs \\"\u{FC}\\""`.force_encoding("utf-8")) #" # \u in quoted symbols assert_equal(:A, :"\u0041") diff --git a/test/thread/test_queue.rb b/test/thread/test_queue.rb index 7a4e60e1aee812..84e60d6b814d41 100644 --- a/test/thread/test_queue.rb +++ b/test/thread/test_queue.rb @@ -101,7 +101,7 @@ def test_sized_queue_push_interrupt def test_thr_kill bug5343 = '[ruby-core:39634]' Dir.mktmpdir {|d| - timeout = 20 + timeout = 30 total_count = 2000 begin assert_normal_exit(<<-"_eom", bug5343, {:timeout => timeout, :chdir=>d}) diff --git a/thread.c b/thread.c index 8320ed29bd42bd..035ccebd7da634 100644 --- a/thread.c +++ b/thread.c @@ -2486,8 +2486,8 @@ rb_thread_local_aref(VALUE thread, ID id) * #: C * * Thread#[] and Thread#[]= are not thread-local but fiber-local. - * This confusion was not exist until Ruby 1.8 because - * fiber is available since Ruby 1.9. + * This confusion did not exist in Ruby 1.8 because + * fibers were only available since Ruby 1.9. * Ruby 1.9 chooses that the methods behaves fiber-local to save * following idiom for dynamic scope. * diff --git a/thread_pthread.c b/thread_pthread.c index be6b017e5019d7..01ad0ce4b50133 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -492,6 +492,8 @@ static rb_thread_t *register_cached_thread_and_wait(void); #define STACKADDR_AVAILABLE 1 #elif defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP #define STACKADDR_AVAILABLE 1 +#undef MAINSTACKADDR_AVAILABLE +#define MAINSTACKADDR_AVAILABLE 0 void *pthread_get_stackaddr_np(pthread_t); size_t pthread_get_stacksize_np(pthread_t); #elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP @@ -500,6 +502,14 @@ size_t pthread_get_stacksize_np(pthread_t); #define STACKADDR_AVAILABLE 1 #endif +#ifndef MAINSTACKADDR_AVAILABLE +# ifdef STACKADDR_AVAILABLE +# define MAINSTACKADDR_AVAILABLE 1 +# else +# define MAINSTACKADDR_AVAILABLE 0 +# endif +#endif + #ifdef STACKADDR_AVAILABLE /* * Get the initial address and size of current thread's stack @@ -578,6 +588,26 @@ static struct { #endif } native_main_thread; + +enum { +#ifdef __SYMBIAN32__ + RUBY_STACK_MIN_LIMIT = 64 * 1024, /* 64KB: Let's be slightly more frugal on mobile platform */ +#else + RUBY_STACK_MIN_LIMIT = 512 * 1024, /* 512KB */ +#endif + RUBY_STACK_SPACE_LIMIT = 1024 * 1024, +#ifdef PTHREAD_STACK_MIN + RUBY_STACK_MIN = ((RUBY_STACK_MIN_LIMIT < PTHREAD_STACK_MIN) ? + PTHREAD_STACK_MIN * 2 : RUBY_STACK_MIN_LIMIT), +#else + RUBY_STACK_MIN = (RUBY_STACK_MIN_LIMIT), +#endif + RUBY_STACK_SPACE_RATIO = 5, + RUBY_STACK_MIN_SPACE = RUBY_STACK_MIN/RUBY_STACK_SPACE_RATIO, + RUBY_STACK_SPACE = ((RUBY_STACK_MIN_SPACE > RUBY_STACK_SPACE_LIMIT) ? + RUBY_STACK_SPACE_LIMIT : RUBY_STACK_MIN_SPACE) +}; + #ifdef STACK_END_ADDRESS extern void *STACK_END_ADDRESS; #endif @@ -614,19 +644,27 @@ ruby_init_stack(volatile VALUE *addr { size_t size = 0; size_t space = 0; -#if defined(STACKADDR_AVAILABLE) +#if MAINSTACKADDR_AVAILABLE void* stackaddr; STACK_GROW_DIR_DETECTION; get_stack(&stackaddr, &size); space = STACK_DIR_UPPER((char *)addr - (char *)stackaddr, (char *)stackaddr - (char *)addr); + native_main_thread.stack_maxsize = size - space; #elif defined(HAVE_GETRLIMIT) + int pagesize = getpagesize(); struct rlimit rlim; if (getrlimit(RLIMIT_STACK, &rlim) == 0) { size = (size_t)rlim.rlim_cur; } - space = size > 5 * 1024 * 1024 ? 1024 * 1024 : size / 5; + addr = native_main_thread.stack_start; + if (IS_STACK_DIR_UPPER()) { + space = ((size_t)((char *)addr + size) / pagesize) * pagesize - (size_t)addr; + } + else { + space = (size_t)addr - ((size_t)((char *)addr - size) / pagesize + 1) * pagesize; + } + native_main_thread.stack_maxsize = space; #endif - native_main_thread.stack_maxsize = size - space; } } @@ -796,24 +834,6 @@ use_cached_thread(rb_thread_t *th) return result; } -enum { -#ifdef __SYMBIAN32__ - RUBY_STACK_MIN_LIMIT = 64 * 1024, /* 64KB: Let's be slightly more frugal on mobile platform */ -#else - RUBY_STACK_MIN_LIMIT = 512 * 1024, /* 512KB */ -#endif - RUBY_STACK_SPACE_LIMIT = 1024 * 1024 -}; - -#ifdef PTHREAD_STACK_MIN -#define RUBY_STACK_MIN ((RUBY_STACK_MIN_LIMIT < PTHREAD_STACK_MIN) ? \ - PTHREAD_STACK_MIN * 2 : RUBY_STACK_MIN_LIMIT) -#else -#define RUBY_STACK_MIN (RUBY_STACK_MIN_LIMIT) -#endif -#define RUBY_STACK_SPACE (RUBY_STACK_MIN/5 > RUBY_STACK_SPACE_LIMIT ? \ - RUBY_STACK_SPACE_LIMIT : RUBY_STACK_MIN/5) - static int native_thread_create(rb_thread_t *th) { @@ -1415,7 +1435,7 @@ ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr) else { return 0; } - size /= 5; + size /= RUBY_STACK_SPACE_RATIO; if (size > water_mark) size = water_mark; if (IS_STACK_DIR_UPPER()) { if (size > ~(size_t)base+1) size = ~(size_t)base+1; diff --git a/tool/instruction.rb b/tool/instruction.rb index 9dc7604170c172..6934b33e804e67 100755 --- a/tool/instruction.rb +++ b/tool/instruction.rb @@ -706,10 +706,13 @@ def make_header_operands insn break end + # skip make operands when body has no reference to this operand + # TODO: really needed? re = /\b#{var}\b/n - if re =~ insn.body or re =~ insn.sp_inc or insn.rets.any?{|t, v| re =~ v} or re =~ 'ic' + if re =~ insn.body or re =~ insn.sp_inc or insn.rets.any?{|t, v| re =~ v} or re =~ 'ic' or re =~ 'ci' ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});" end + n += 1 } @opn = n @@ -791,9 +794,9 @@ def make_header_stack_val insn end def make_header_analysis insn - commit " USAGE_ANALYSIS_INSN(BIN(#{insn.name}));" + commit " COLLECT_USAGE_INSN(BIN(#{insn.name}));" insn.opes.each_with_index{|op, i| - commit " USAGE_ANALYSIS_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});" + commit " COLLECT_USAGE_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});" } end @@ -930,8 +933,6 @@ def op2typesig op "TS_NUM" when /^lindex_t/ "TS_LINDEX" - when /^dindex_t/ - "TS_DINDEX" when /^VALUE/ "TS_VALUE" when /^ID/ @@ -940,6 +941,8 @@ def op2typesig op "TS_GENTRY" when /^IC/ "TS_IC" + when /^CALL_INFO/ + "TS_CALLINFO" when /^\.\.\./ "TS_VARIABLE" when /^CDHASH/ @@ -957,11 +960,11 @@ def op2typesig op 'TS_OFFSET' => 'O', 'TS_NUM' => 'N', 'TS_LINDEX' => 'L', - 'TS_DINDEX' => 'D', 'TS_VALUE' => 'V', 'TS_ID' => 'I', 'TS_GENTRY' => 'G', - 'TS_IC' => 'C', + 'TS_IC' => 'K', + 'TS_CALLINFO' => 'C', 'TS_CDHASH' => 'H', 'TS_ISEQ' => 'S', 'TS_VARIABLE' => '.', @@ -1064,7 +1067,7 @@ def val_as_type op val = op[1] case type - when /^long/, /^rb_num_t/, /^lindex_t/, /^dindex_t/ + when /^long/, /^rb_num_t/, /^lindex_t/ "INT2FIX(#{val})" when /^VALUE/ val diff --git a/version.h b/version.h index 41b95c73562788..996a02ff139a76 100644 --- a/version.h +++ b/version.h @@ -1,11 +1,11 @@ #define RUBY_VERSION "2.0.0" -#define RUBY_RELEASE_DATE "2012-09-28" +#define RUBY_RELEASE_DATE "2012-10-09" #define RUBY_PATCHLEVEL -1 #define RUBY_BRANCH_NAME "trunk" #define RUBY_RELEASE_YEAR 2012 -#define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 28 +#define RUBY_RELEASE_MONTH 10 +#define RUBY_RELEASE_DAY 9 #include "ruby/version.h" diff --git a/vm.c b/vm.c index 348d1349facfad..ae201dcd7f1135 100644 --- a/vm.c +++ b/vm.c @@ -61,6 +61,16 @@ rb_vm_control_frame_block_ptr(rb_control_frame_t *cfp) return VM_CF_BLOCK_PTR(cfp); } +#ifndef VM_COLLECT_USAGE_DETAILS +#define VM_COLLECT_USAGE_DETAILS 0 +#endif + +#if VM_COLLECT_USAGE_DETAILS +static void vm_collect_usage_operand(int insn, int n, VALUE op); +static void vm_collect_usage_register(int reg, int isset); +static void vm_collect_usage_insn(int insn); +#endif + static VALUE vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self, VALUE defined_class, int argc, const VALUE *argv, const rb_block_t *blockptr); @@ -91,10 +101,6 @@ rb_event_flag_t ruby_vm_event_flags; static void thread_free(void *ptr); -void vm_analysis_operand(int insn, int n, VALUE op); -void vm_analysis_register(int reg, int isset); -void vm_analysis_insn(int insn); - void rb_vm_change_state(void) { @@ -2070,6 +2076,12 @@ nsdr(void) return ary; } +#if VM_COLLECT_USAGE_DETAILS +static VALUE usage_analysis_insn_stop(VALUE self); +static VALUE usage_analysis_operand_stop(VALUE self); +static VALUE usage_analysis_register_stop(VALUE self); +#endif + void Init_VM(void) { @@ -2109,10 +2121,18 @@ Init_VM(void) rb_cThread = rb_define_class("Thread", rb_cObject); rb_undef_alloc_func(rb_cThread); +#if VM_COLLECT_USAGE_DETAILS /* ::RubyVM::USAGE_ANALYSIS_* */ rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_INSN", rb_hash_new()); rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_REGS", rb_hash_new()); rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_INSN_BIGRAM", rb_hash_new()); + + rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_INSN_STOP", usage_analysis_insn_stop, 0); + rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_OPERAND_STOP", usage_analysis_operand_stop, 0); + rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_REGISTER_STOP", usage_analysis_register_stop, 0); +#endif + + /* ::RubyVM::OPTS, which shows vm build options */ rb_define_const(rb_cRubyVM, "OPTS", opts = rb_ary_new()); #if OPT_DIRECT_THREADED_CODE @@ -2281,3 +2301,201 @@ rb_ruby_debug_ptr(void) { return ruby_vm_debug_ptr(GET_VM()); } + +#if VM_COLLECT_USAGE_DETAILS + +#define HASH_ASET(h, k, v) st_insert(RHASH_TBL(h), (st_data_t)(k), (st_data_t)(v)) + +/* uh = { + * insn(Fixnum) => ihash(Hash) + * } + * ihash = { + * -1(Fixnum) => count, # insn usage + * 0(Fixnum) => ophash, # operand usage + * } + * ophash = { + * val(interned string) => count(Fixnum) + * } + */ +static void +vm_analysis_insn(int insn) +{ + ID usage_hash; + ID bigram_hash; + static int prev_insn = -1; + + VALUE uh; + VALUE ihash; + VALUE cv; + + CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN"); + CONST_ID(bigram_hash, "USAGE_ANALYSIS_INSN_BIGRAM"); + uh = rb_const_get(rb_cRubyVM, usage_hash); + if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) { + ihash = rb_hash_new(); + HASH_ASET(uh, INT2FIX(insn), ihash); + } + if ((cv = rb_hash_aref(ihash, INT2FIX(-1))) == Qnil) { + cv = INT2FIX(0); + } + HASH_ASET(ihash, INT2FIX(-1), INT2FIX(FIX2INT(cv) + 1)); + + /* calc bigram */ + if (prev_insn != -1) { + VALUE bi; + VALUE ary[2]; + VALUE cv; + + ary[0] = INT2FIX(prev_insn); + ary[1] = INT2FIX(insn); + bi = rb_ary_new4(2, &ary[0]); + + uh = rb_const_get(rb_cRubyVM, bigram_hash); + if ((cv = rb_hash_aref(uh, bi)) == Qnil) { + cv = INT2FIX(0); + } + HASH_ASET(uh, bi, INT2FIX(FIX2INT(cv) + 1)); + } + prev_insn = insn; +} + +/* iseq.c */ +VALUE insn_operand_intern(rb_iseq_t *iseq, + VALUE insn, int op_no, VALUE op, + int len, size_t pos, VALUE *pnop, VALUE child); + +static void +vm_analysis_operand(int insn, int n, VALUE op) +{ + ID usage_hash; + + VALUE uh; + VALUE ihash; + VALUE ophash; + VALUE valstr; + VALUE cv; + + CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN"); + + uh = rb_const_get(rb_cRubyVM, usage_hash); + if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) { + ihash = rb_hash_new(); + HASH_ASET(uh, INT2FIX(insn), ihash); + } + if ((ophash = rb_hash_aref(ihash, INT2FIX(n))) == Qnil) { + ophash = rb_hash_new(); + HASH_ASET(ihash, INT2FIX(n), ophash); + } + /* intern */ + valstr = insn_operand_intern(GET_THREAD()->cfp->iseq, insn, n, op, 0, 0, 0, 0); + + /* set count */ + if ((cv = rb_hash_aref(ophash, valstr)) == Qnil) { + cv = INT2FIX(0); + } + HASH_ASET(ophash, valstr, INT2FIX(FIX2INT(cv) + 1)); +} + +static void +vm_analysis_register(int reg, int isset) +{ + ID usage_hash; + VALUE uh; + VALUE valstr; + static const char regstrs[][5] = { + "pc", /* 0 */ + "sp", /* 1 */ + "ep", /* 2 */ + "cfp", /* 3 */ + "self", /* 4 */ + "iseq", /* 5 */ + }; + static const char getsetstr[][4] = { + "get", + "set", + }; + static VALUE syms[sizeof(regstrs) / sizeof(regstrs[0])][2]; + + VALUE cv; + + CONST_ID(usage_hash, "USAGE_ANALYSIS_REGS"); + if (syms[0] == 0) { + char buff[0x10]; + int i; + + for (i = 0; i < (int)(sizeof(regstrs) / sizeof(regstrs[0])); i++) { + int j; + for (j = 0; j < 2; j++) { + snprintf(buff, 0x10, "%d %s %-4s", i, getsetstr[j], regstrs[i]); + syms[i][j] = ID2SYM(rb_intern(buff)); + } + } + } + valstr = syms[reg][isset]; + + uh = rb_const_get(rb_cRubyVM, usage_hash); + if ((cv = rb_hash_aref(uh, valstr)) == Qnil) { + cv = INT2FIX(0); + } + HASH_ASET(uh, valstr, INT2FIX(FIX2INT(cv) + 1)); +} + +#undef HASH_ASET + +void (*ruby_vm_collect_usage_func_insn)(int insn) = vm_analysis_insn; +void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = vm_analysis_operand; +void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = vm_analysis_register; + +/* :nodoc: */ +static VALUE +usage_analysis_insn_stop(VALUE self) +{ + ruby_vm_collect_usage_func_insn = 0; + return Qnil; +} + +/* :nodoc: */ +static VALUE +usage_analysis_operand_stop(VALUE self) +{ + ruby_vm_collect_usage_func_operand = 0; + return Qnil; +} + +/* :nodoc: */ +static VALUE +usage_analysis_register_stop(VALUE self) +{ + ruby_vm_collect_usage_func_register = 0; + return Qnil; +} + +/* @param insn instruction number */ +static void +vm_collect_usage_insn(int insn) +{ + if (ruby_vm_collect_usage_func_insn) + (*ruby_vm_collect_usage_func_insn)(insn); +} + +/* @param insn instruction number + * @param n n-th operand + * @param op operand value + */ +static void +vm_collect_usage_operand(int insn, int n, VALUE op) +{ + if (ruby_vm_collect_usage_func_operand) + (*ruby_vm_collect_usage_func_operand)(insn, n, op); +} + +/* @param reg register id. see code of vm_analysis_register() */ +/* @param iseset 0: read, 1: write */ +static void +vm_collect_usage_register(int reg, int isset) +{ + if (ruby_vm_collect_usage_func_register) + (*ruby_vm_collect_usage_func_register)(reg, isset); +} + +#endif diff --git a/vm_core.h b/vm_core.h index 8d5140729b6a88..6213863da48447 100644 --- a/vm_core.h +++ b/vm_core.h @@ -134,14 +134,21 @@ struct iseq_inline_cache_entry { VALUE ic_class; union { VALUE value; - rb_method_entry_t *method; long index; } ic_value; - union { - VALUE defined_class; - } ic_value2; }; +/* rb_call_info_t contains calling information including inline cache */ +typedef struct rb_call_info_struct { + /* inline cache: keys */ + VALUE ic_vmstat; + VALUE ic_class; + + /* inline cache: values */ + rb_method_entry_t *method; + VALUE defined_class; +} rb_call_info_t; + #if 1 #define GetCoreDataFromValue(obj, type, ptr) do { \ (ptr) = (type*)DATA_PTR(obj); \ @@ -201,6 +208,9 @@ struct rb_iseq_struct { struct iseq_inline_cache_entry *ic_entries; int ic_size; + rb_call_info_t *callinfo_entries; + int callinfo_size; + /** * argument information * @@ -661,6 +671,7 @@ enum vm_special_object_type { /* inline cache */ typedef struct iseq_inline_cache_entry *IC; +typedef rb_call_info_t *CALL_INFO; void rb_vm_change_state(void); diff --git a/vm_dump.c b/vm_dump.c index 4f7525e8a99b18..8e005bd14e9255 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -395,145 +395,6 @@ rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp #endif } -#ifdef COLLECT_USAGE_ANALYSIS -/* uh = { - * insn(Fixnum) => ihash(Hash) - * } - * ihash = { - * -1(Fixnum) => count, # insn usage - * 0(Fixnum) => ophash, # operand usage - * } - * ophash = { - * val(interned string) => count(Fixnum) - * } - */ -void -vm_analysis_insn(int insn) -{ - ID usage_hash; - ID bigram_hash; - static int prev_insn = -1; - - VALUE uh; - VALUE ihash; - VALUE cv; - - CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN"); - CONST_ID(bigram_hash, "USAGE_ANALYSIS_INSN_BIGRAM"); - uh = rb_const_get(rb_cRubyVM, usage_hash); - if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) { - ihash = rb_hash_new(); - rb_hash_aset(uh, INT2FIX(insn), ihash); - } - if ((cv = rb_hash_aref(ihash, INT2FIX(-1))) == Qnil) { - cv = INT2FIX(0); - } - rb_hash_aset(ihash, INT2FIX(-1), INT2FIX(FIX2INT(cv) + 1)); - - /* calc bigram */ - if (prev_insn != -1) { - VALUE bi; - VALUE ary[2]; - VALUE cv; - - ary[0] = INT2FIX(prev_insn); - ary[1] = INT2FIX(insn); - bi = rb_ary_new4(2, &ary[0]); - - uh = rb_const_get(rb_cRubyVM, bigram_hash); - if ((cv = rb_hash_aref(uh, bi)) == Qnil) { - cv = INT2FIX(0); - } - rb_hash_aset(uh, bi, INT2FIX(FIX2INT(cv) + 1)); - } - prev_insn = insn; -} - -/* from disasm.c */ -extern VALUE insn_operand_intern(int insn, int op_no, VALUE op, - int len, int pos, VALUE child); - -void -vm_analysis_operand(int insn, int n, VALUE op) -{ - ID usage_hash; - - VALUE uh; - VALUE ihash; - VALUE ophash; - VALUE valstr; - VALUE cv; - - CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN"); - - uh = rb_const_get(rb_cRubyVM, usage_hash); - if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) { - ihash = rb_hash_new(); - rb_hash_aset(uh, INT2FIX(insn), ihash); - } - if ((ophash = rb_hash_aref(ihash, INT2FIX(n))) == Qnil) { - ophash = rb_hash_new(); - rb_hash_aset(ihash, INT2FIX(n), ophash); - } - /* intern */ - valstr = insn_operand_intern(insn, n, op, 0, 0, 0); - - /* set count */ - if ((cv = rb_hash_aref(ophash, valstr)) == Qnil) { - cv = INT2FIX(0); - } - rb_hash_aset(ophash, valstr, INT2FIX(FIX2INT(cv) + 1)); -} - -void -vm_analysis_register(int reg, int isset) -{ - ID usage_hash; - VALUE uh; - VALUE rhash; - VALUE valstr; - static const char regstrs[][5] = { - "pc", /* 0 */ - "sp", /* 1 */ - "ep", /* 2 */ - "cfp", /* 3 */ - "self", /* 4 */ - "iseq", /* 5 */ - }; - static const char getsetstr[][4] = { - "get", - "set", - }; - static VALUE syms[sizeof(regstrs) / sizeof(regstrs[0])][2]; - - VALUE cv; - - CONST_ID(usage_hash, "USAGE_ANALYSIS_REGS"); - if (syms[0] == 0) { - char buff[0x10]; - int i; - - for (i = 0; i < sizeof(regstrs) / sizeof(regstrs[0]); i++) { - int j; - for (j = 0; j < 2; j++) { - snfprintf(stderr, buff, 0x10, "%d %s %-4s", i, getsetstr[j], - regstrs[i]); - syms[i][j] = ID2SYM(rb_intern(buff)); - } - } - } - valstr = syms[reg][isset]; - - uh = rb_const_get(rb_cRubyVM, usage_hash); - if ((cv = rb_hash_aref(uh, valstr)) == Qnil) { - cv = INT2FIX(0); - } - rb_hash_aset(uh, valstr, INT2FIX(FIX2INT(cv) + 1)); -} - - -#endif - VALUE rb_vmdebug_thread_dump_state(VALUE self) { @@ -759,9 +620,10 @@ rb_vm_bugreport(void) #if defined __APPLE__ fprintf(stderr, "\n"); fprintf(stderr, " See Crash Report log file under " - "~/Library/Logs/CrashReporter or\n"); - fprintf(stderr, " /Library/Logs/CrashReporter, for " - "the more detail of.\n"); + "~/Library/Logs/CrashReporter,\n"); + fprintf(stderr, " /Library/Logs/CrashReporter, or " + "/Library/Logs/DiagnosticReports, for\n"); + fprintf(stderr, " the more detail of.\n"); #elif HAVE_BACKTRACE #define MAX_NATIVE_TRACE 1024 static void *trace[MAX_NATIVE_TRACE]; diff --git a/vm_eval.c b/vm_eval.c index ef588dd1f6b4ab..1346aea3c3ecd9 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -385,7 +385,8 @@ rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr) rb_id2name(mid), type, (void *)recv, flags, klass); } } - return rb_method_entry_get_with_omod(Qnil, klass, mid, defined_class_ptr); + return rb_method_entry_get_with_refinements(Qnil, klass, mid, + defined_class_ptr); } static inline int diff --git a/vm_exec.h b/vm_exec.h index 2c1e2d55275b3e..75797e02c14626 100644 --- a/vm_exec.h +++ b/vm_exec.h @@ -14,20 +14,9 @@ typedef long OFFSET; typedef unsigned long lindex_t; -typedef unsigned long dindex_t; typedef rb_num_t GENTRY; typedef rb_iseq_t *ISEQ; -#ifdef COLLECT_USAGE_ANALYSIS -#define USAGE_ANALYSIS_INSN(insn) vm_analysis_insn(insn) -#define USAGE_ANALYSIS_OPERAND(insn, n, op) vm_analysis_operand((insn), (n), (VALUE)(op)) -#define USAGE_ANALYSIS_REGISTER(reg, s) vm_analysis_register((reg), (s)) -#else -#define USAGE_ANALYSIS_INSN(insn) /* none */ -#define USAGE_ANALYSIS_OPERAND(insn, n, op) /* none */ -#define USAGE_ANALYSIS_REGISTER(reg, s) /* none */ -#endif - #ifdef __GCC__ /* TODO: machine dependent prefetch instruction */ #define PREFETCH(pc) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index f1344871073b5b..06cc85934af6db 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1140,7 +1140,7 @@ vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr) { rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(th, th->cfp); NODE *cref = NEW_CREF(klass); - cref->nd_omod = Qnil; + cref->nd_refinements = Qnil; cref->nd_visi = noex; if (blockptr) { @@ -1151,7 +1151,7 @@ vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr) } /* TODO: why cref->nd_next is 1? */ if (cref->nd_next && cref->nd_next != (void *) 1 && - !NIL_P(cref->nd_next->nd_omod)) { + !NIL_P(cref->nd_next->nd_refinements)) { COPY_CREF_OMOD(cref, cref->nd_next); } @@ -1430,25 +1430,27 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic) } static inline const rb_method_entry_t * -vm_method_search(VALUE id, VALUE klass, IC ic, VALUE *defined_class_ptr) +vm_method_search(VALUE id, VALUE klass, CALL_INFO ci, VALUE *defined_class_ptr) { rb_method_entry_t *me; #if OPT_INLINE_METHOD_CACHE - if (LIKELY(klass == ic->ic_class && - GET_VM_STATE_VERSION() == ic->ic_vmstat)) { - me = ic->ic_value.method; - if (defined_class_ptr) - *defined_class_ptr = ic->ic_value2.defined_class; + if (LIKELY(klass == ci->ic_class && + GET_VM_STATE_VERSION() == ci->ic_vmstat)) { + me = ci->method; + if (defined_class_ptr) { + *defined_class_ptr = ci->defined_class; + } } else { VALUE defined_class; me = rb_method_entry(klass, id, &defined_class); - if (defined_class_ptr) + if (defined_class_ptr) { *defined_class_ptr = defined_class; - ic->ic_class = klass; - ic->ic_value.method = me; - ic->ic_value2.defined_class = defined_class; - ic->ic_vmstat = GET_VM_STATE_VERSION(); + } + ci->ic_class = klass; + ci->method = me; + ci->defined_class = defined_class; + ci->ic_vmstat = GET_VM_STATE_VERSION(); } #else me = rb_method_entry(klass, id, defined_class_ptr); @@ -1773,7 +1775,7 @@ static inline #endif VALUE -opt_eq_func(VALUE recv, VALUE obj, IC ic) +opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci) { if (FIXNUM_2_P(recv, obj) && BASIC_OP_UNREDEFINED_P(BOP_EQ, FIXNUM_REDEFINED_OP_FLAG)) { @@ -1803,7 +1805,7 @@ opt_eq_func(VALUE recv, VALUE obj, IC ic) } { - const rb_method_entry_t *me = vm_method_search(idEq, CLASS_OF(recv), ic, 0); + const rb_method_entry_t *me = vm_method_search(idEq, CLASS_OF(recv), ci, 0); if (check_cfunc(me, rb_obj_equal)) { return recv == obj ? Qtrue : Qfalse; @@ -1830,27 +1832,22 @@ rb_vm_using_modules(NODE *cref, VALUE klass) ID id_using_modules; VALUE using_modules; + if (NIL_P(klass)) return; CONST_ID(id_using_modules, "__using_modules__"); using_modules = rb_attr_get(klass, id_using_modules); - switch (TYPE(klass)) { - case T_CLASS: - if (NIL_P(using_modules)) { - VALUE super = rb_class_real(RCLASS_SUPER(klass)); - using_modules = rb_attr_get(super, id_using_modules); - if (!NIL_P(using_modules)) { - using_modules = rb_hash_dup(using_modules); - rb_ivar_set(klass, id_using_modules, using_modules); - } + if (NIL_P(using_modules) && BUILTIN_TYPE(klass) == T_CLASS) { + VALUE super = rb_class_real(RCLASS_SUPER(klass)); + using_modules = rb_attr_get(super, id_using_modules); + if (!NIL_P(using_modules)) { + using_modules = rb_hash_dup(using_modules); + rb_ivar_set(klass, id_using_modules, using_modules); } - break; - case T_MODULE: - rb_using_module(cref, klass); - break; } if (!NIL_P(using_modules)) { rb_hash_foreach(using_modules, vm_using_module_i, (VALUE) cref); } + rb_using_module(cref, klass); } static VALUE diff --git a/vm_insnhelper.h b/vm_insnhelper.h index 7c7745ae60ead0..8b8da831a3e947 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -62,6 +62,15 @@ enum { extern char ruby_vm_redefined_flag[BOP_LAST_]; extern VALUE ruby_vm_const_missing_count; +#if VM_COLLECT_USAGE_DETAILS +#define COLLECT_USAGE_INSN(insn) vm_collect_usage_insn(insn) +#define COLLECT_USAGE_OPERAND(insn, n, op) vm_collect_usage_operand((insn), (n), ((VALUE)(op))) +#define COLLECT_USAGE_REGISTER(reg, s) vm_collect_usage_register((reg), (s)) +#else +#define COLLECT_USAGE_INSN(insn) /* none */ +#define COLLECT_USAGE_OPERAND(insn, n, op) /* none */ +#define COLLECT_USAGE_REGISTER(reg, s) /* none */ +#endif /**********************************************************/ /* deal with stack */ @@ -104,16 +113,16 @@ enum vm_regan_acttype { VM_REGAN_ACT_SET = 1, }; -#ifdef COLLECT_USAGE_ANALYSIS -#define USAGE_ANALYSIS_REGISTER_HELPER(a, b, v) \ - (USAGE_ANALYSIS_REGISTER((VM_REGAN_#a), (VM_REGAN_ACT_#b)), (v)) +#if VM_COLLECT_USAGE_DETAILS +#define COLLECT_USAGE_REGISTER_HELPER(a, b, v) \ + (COLLECT_USAGE_REGISTER((VM_REGAN_##a), (VM_REGAN_ACT_##b)), (v)) #else -#define USAGE_ANALYSIS_REGISTER_HELPER(a, b, v) (v) +#define COLLECT_USAGE_REGISTER_HELPER(a, b, v) (v) #endif /* PC */ -#define GET_PC() (USAGE_ANALYSIS_REGISTER_HELPER(PC, GET, REG_PC)) -#define SET_PC(x) (REG_PC = (USAGE_ANALYSIS_REGISTER_HELPER(PC, SET, (x)))) +#define GET_PC() (COLLECT_USAGE_REGISTER_HELPER(PC, GET, REG_PC)) +#define SET_PC(x) (REG_PC = (COLLECT_USAGE_REGISTER_HELPER(PC, SET, (x)))) #define GET_CURRENT_INSN() (*GET_PC()) #define GET_OPERAND(n) (GET_PC()[(n)]) #define ADD_PC(n) (SET_PC(REG_PC + (n))) @@ -122,16 +131,16 @@ enum vm_regan_acttype { #define JUMP(dst) (REG_PC += (dst)) /* frame pointer, environment pointer */ -#define GET_CFP() (USAGE_ANALYSIS_REGISTER_HELPER(CFP, GET, REG_CFP)) -#define GET_EP() (USAGE_ANALYSIS_REGISTER_HELPER(EP, GET, REG_EP)) -#define SET_EP(x) (REG_EP = (USAGE_ANALYSIS_REGISTER_HELPER(EP, SET, (x)))) +#define GET_CFP() (COLLECT_USAGE_REGISTER_HELPER(CFP, GET, REG_CFP)) +#define GET_EP() (COLLECT_USAGE_REGISTER_HELPER(EP, GET, REG_EP)) +#define SET_EP(x) (REG_EP = (COLLECT_USAGE_REGISTER_HELPER(EP, SET, (x)))) #define GET_LEP() (VM_EP_LEP(GET_EP())) /* SP */ -#define GET_SP() (USAGE_ANALYSIS_REGISTER_HELPER(SP, GET, REG_SP)) -#define SET_SP(x) (REG_SP = (USAGE_ANALYSIS_REGISTER_HELPER(SP, SET, (x)))) -#define INC_SP(x) (REG_SP += (USAGE_ANALYSIS_REGISTER_HELPER(SP, SET, (x)))) -#define DEC_SP(x) (REG_SP -= (USAGE_ANALYSIS_REGISTER_HELPER(SP, SET, (x)))) +#define GET_SP() (COLLECT_USAGE_REGISTER_HELPER(SP, GET, REG_SP)) +#define SET_SP(x) (REG_SP = (COLLECT_USAGE_REGISTER_HELPER(SP, SET, (x)))) +#define INC_SP(x) (REG_SP += (COLLECT_USAGE_REGISTER_HELPER(SP, SET, (x)))) +#define DEC_SP(x) (REG_SP -= (COLLECT_USAGE_REGISTER_HELPER(SP, SET, (x)))) #define SET_SV(x) (*GET_SP() = (x)) /* set current stack value as x */ @@ -155,15 +164,15 @@ enum vm_regan_acttype { /* deal with values */ /**********************************************************/ -#define GET_SELF() (USAGE_ANALYSIS_REGISTER_HELPER(5, 0, GET_CFP()->self)) +#define GET_SELF() (COLLECT_USAGE_REGISTER_HELPER(SELF, GET, GET_CFP()->self)) /**********************************************************/ /* deal with control flow 2: method/iterator */ /**********************************************************/ #define COPY_CREF_OMOD(c1, c2) do { \ - (c1)->nd_omod = (c2)->nd_omod; \ - if (!NIL_P((c2)->nd_omod)) { \ + (c1)->nd_refinements = (c2)->nd_refinements; \ + if (!NIL_P((c2)->nd_refinements)) { \ (c1)->flags |= NODE_FL_CREF_OMOD_SHARED; \ (c2)->flags |= NODE_FL_CREF_OMOD_SHARED; \ } \ @@ -230,7 +239,7 @@ enum vm_regan_acttype { #define CALL_SIMPLE_METHOD(num, id, recv) do { \ VALUE klass = CLASS_OF(recv), defined_class; \ - const rb_method_entry_t *me = vm_method_search((id), klass, ic, &defined_class); \ + const rb_method_entry_t *me = vm_method_search((id), klass, ci, &defined_class); \ CALL_METHOD((num), 0, 0, (id), me, (recv), defined_class); \ } while (0) diff --git a/vm_method.c b/vm_method.c index e44782ce6d4a10..fb656b3e8b8ef5 100644 --- a/vm_method.c +++ b/vm_method.c @@ -21,7 +21,7 @@ struct cache_entry { /* method hash table. */ VALUE filled_version; /* filled state version */ ID mid; /* method's id */ VALUE klass; /* receiver's class */ - VALUE omod; /* overlay modules */ + VALUE refinements; /* refinements */ rb_method_entry_t *me; VALUE defined_class; }; @@ -394,19 +394,28 @@ copy_refinement_iclass(VALUE iclass, VALUE superclass) return result; } -static rb_method_entry_t* -search_method(VALUE klass, ID id, VALUE omod, VALUE *defined_class_ptr) +static inline int +lookup_method_table(VALUE klass, ID id, st_data_t *body) +{ + st_table *m_tbl = RCLASS_M_TBL(klass); + if (!m_tbl) { + m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(RBASIC(klass)->klass)); + } + return st_lookup(m_tbl, id, body); +} + +static inline rb_method_entry_t* +search_method_with_refinements(VALUE klass, ID id, VALUE refinements, + VALUE *defined_class_ptr) { st_data_t body; VALUE iclass, skipped_class = Qnil; for (body = 0; klass; klass = RCLASS_SUPER(klass)) { - st_table *m_tbl; - - if (!NIL_P(omod) && klass != skipped_class) { - iclass = rb_hash_lookup(omod, klass); + if (klass != skipped_class) { + iclass = rb_hash_lookup(refinements, klass); if (NIL_P(iclass) && BUILTIN_TYPE(klass) == T_ICLASS) { - iclass = rb_hash_lookup(omod, RBASIC(klass)->klass); + iclass = rb_hash_lookup(refinements, RBASIC(klass)->klass); if (!NIL_P(iclass)) iclass = copy_refinement_iclass(iclass, klass); } @@ -415,11 +424,7 @@ search_method(VALUE klass, ID id, VALUE omod, VALUE *defined_class_ptr) klass = iclass; } } - m_tbl = RCLASS_M_TBL(klass); - if (!m_tbl) { - m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(RBASIC(klass)->klass)); - } - if (st_lookup(m_tbl, id, &body)) break; + if (lookup_method_table(klass, id, &body)) break; } if (defined_class_ptr) @@ -427,6 +432,32 @@ search_method(VALUE klass, ID id, VALUE omod, VALUE *defined_class_ptr) return (rb_method_entry_t *)body; } +static inline rb_method_entry_t* +search_method_without_refinements(VALUE klass, ID id, VALUE *defined_class_ptr) +{ + st_data_t body; + + for (body = 0; klass; klass = RCLASS_SUPER(klass)) { + if (lookup_method_table(klass, id, &body)) break; + } + + if (defined_class_ptr) + *defined_class_ptr = klass; + return (rb_method_entry_t *)body; +} + +static rb_method_entry_t* +search_method(VALUE klass, ID id, VALUE refinements, VALUE *defined_class_ptr) +{ + if (NIL_P(refinements)) { + return search_method_without_refinements(klass, id, defined_class_ptr); + } + else { + return search_method_with_refinements(klass, id, refinements, + defined_class_ptr); + } +} + /* * search method entry without the method cache. * @@ -434,18 +465,19 @@ search_method(VALUE klass, ID id, VALUE omod, VALUE *defined_class_ptr) * rb_method_entry() simply. */ rb_method_entry_t * -rb_method_entry_get_without_cache(VALUE klass, VALUE omod, ID id, +rb_method_entry_get_without_cache(VALUE klass, VALUE refinements, ID id, VALUE *defined_class_ptr) { VALUE defined_class; - rb_method_entry_t *me = search_method(klass, id, omod, &defined_class); + rb_method_entry_t *me = search_method(klass, id, refinements, + &defined_class); if (ruby_running) { struct cache_entry *ent; - ent = cache + EXPR1(klass, omod, id); + ent = cache + EXPR1(klass, refinements, id); ent->filled_version = GET_VM_STATE_VERSION(); ent->klass = klass; - ent->omod = omod; + ent->refinements = refinements; ent->defined_class = defined_class; if (UNDEFINED_METHOD_ENTRY_P(me)) { @@ -465,20 +497,23 @@ rb_method_entry_get_without_cache(VALUE klass, VALUE omod, ID id, } rb_method_entry_t * -rb_method_entry_get_with_omod(VALUE omod, VALUE klass, ID id, - VALUE *defined_class_ptr) +rb_method_entry_get_with_refinements(VALUE refinements, VALUE klass, ID id, + VALUE *defined_class_ptr) { +#if OPT_GLOBAL_METHOD_CACHE struct cache_entry *ent; - ent = cache + EXPR1(klass, omod, id); + ent = cache + EXPR1(klass, refinements, id); if (ent->filled_version == GET_VM_STATE_VERSION() && - ent->mid == id && ent->klass == klass && ent->omod == omod) { + ent->mid == id && ent->klass == klass && + ent->refinements == refinements) { if (defined_class_ptr) *defined_class_ptr = ent->defined_class; return ent->me; } +#endif - return rb_method_entry_get_without_cache(klass, omod, id, + return rb_method_entry_get_without_cache(klass, refinements, id, defined_class_ptr); } @@ -486,12 +521,13 @@ rb_method_entry_t * rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr) { NODE *cref = rb_vm_cref(); - VALUE omod = Qnil; + VALUE refinements = Qnil; - if (cref && !NIL_P(cref->nd_omod)) { - omod = cref->nd_omod; + if (cref && !NIL_P(cref->nd_refinements)) { + refinements = cref->nd_refinements; } - return rb_method_entry_get_with_omod(omod, klass, id, defined_class_ptr); + return rb_method_entry_get_with_refinements(refinements, klass, id, + defined_class_ptr); } static void @@ -681,8 +717,8 @@ rb_undef(VALUE klass, ID id) { rb_method_entry_t *me; NODE *cref = rb_vm_cref(); - VALUE omod = Qnil; - void rb_overlay_module(NODE *cref, VALUE klass, VALUE module); + VALUE refinements = Qnil; + void rb_using_refinement(NODE *cref, VALUE klass, VALUE module); if (NIL_P(klass)) { rb_raise(rb_eTypeError, "no class to undef method"); @@ -698,10 +734,10 @@ rb_undef(VALUE klass, ID id) rb_warn("undefining `%s' may cause serious problems", rb_id2name(id)); } - if (cref && !NIL_P(cref->nd_omod)) { - omod = cref->nd_omod; + if (cref && !NIL_P(cref->nd_refinements)) { + refinements = cref->nd_refinements; } - me = search_method(klass, id, omod, 0); + me = search_method(klass, id, refinements, 0); if (UNDEFINED_METHOD_ENTRY_P(me)) { const char *s0 = " class"; @@ -724,7 +760,7 @@ rb_undef(VALUE klass, ID id) if (!RTEST(rb_class_inherited_p(klass, me->klass))) { VALUE mod = rb_module_new(); - rb_overlay_module(cref, klass, mod); + rb_using_refinement(cref, klass, mod); klass = mod; } rb_add_method(klass, id, VM_METHOD_TYPE_UNDEF, 0, NOEX_PUBLIC); diff --git a/vm_opts.h b/vm_opts.h index 9e357f4c409775..4f7162d1b420c4 100644 --- a/vm_opts.h +++ b/vm_opts.h @@ -37,6 +37,7 @@ /* VM running option */ #define OPT_CHECKED_RUN 1 #define OPT_INLINE_METHOD_CACHE 1 +#define OPT_GLOBAL_METHOD_CACHE 1 #define OPT_BLOCKINLINING 0 /* architecture independent, affects generated code */ diff --git a/win32/Makefile.sub b/win32/Makefile.sub index ee359c2ff7f8af..7fc7d5b1e53841 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -607,7 +607,6 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub #define GETGROUPS_T int #define RETSIGTYPE void #define TYPEOF_TIMEVAL_TV_SEC long -#define GC_MARK_STACKFRAME_WORD 30 #define HAVE_ALLOCA 1 #define HAVE_DUP2 1 #define HAVE_MEMCMP 1