diff options
-rw-r--r-- | NEWS.md | 7 | ||||
-rw-r--r-- | benchmark/README.md | 2 | ||||
-rw-r--r-- | doc/distribution.md | 2 | ||||
-rw-r--r-- | gems/bundled_gems | 12 | ||||
-rw-r--r-- | marshal.c | 6 | ||||
-rw-r--r-- | spec/ruby/core/kernel/caller_locations_spec.rb | 14 | ||||
-rw-r--r-- | spec/ruby/core/kernel/caller_spec.rb | 25 | ||||
-rw-r--r-- | test/ruby/test_backtrace.rb | 6 | ||||
-rw-r--r-- | tool/lib/_tmpdir.rb | 6 | ||||
-rw-r--r-- | vm_backtrace.c | 68 |
10 files changed, 97 insertions, 51 deletions
@@ -108,11 +108,11 @@ Note: We're only listing outstanding class updates. The following bundled gems are promoted from default gems. -* ostruct 0.6.1 +* ostruct 0.6.2 * pstore 0.2.0 * benchmark 0.4.1 * logger 1.7.0 -* rdoc 6.14.0 +* rdoc 6.14.1 * win32ole 1.9.2 * irb 1.15.2 * reline 0.6.1 @@ -153,7 +153,10 @@ The following bundled gems are updated. * rexml 3.4.1 * net-imap 0.5.8 * net-smtp 0.5.1 +* matrix 0.4.3 +* prime 0.1.4 * rbs 3.9.4 +* debug 1.11.0 * base64 0.3.0 * bigdecimal 3.2.2 * drb 2.2.3 diff --git a/benchmark/README.md b/benchmark/README.md index c5c29d0daf..9f9192685e 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -40,7 +40,7 @@ Usage: benchmark-driver [options] RUBY|YAML... --filter REGEXP Filter out benchmarks with given regexp --run-duration SECONDS Warmup estimates loop_count to run for this duration (default: 3) --timeout SECONDS Timeout ruby command execution with timeout(1) - -v, --verbose Verbose mode. Multiple -v options increase visilibity (max: 2) + -v, --verbose Verbose mode. Multiple -v options increase visibility (max: 2) ``` ## make benchmark diff --git a/doc/distribution.md b/doc/distribution.md index 5a4d51da6f..164e1b7109 100644 --- a/doc/distribution.md +++ b/doc/distribution.md @@ -8,7 +8,7 @@ This document outlines the expected way to distribute Ruby, with a specific focu The tarball for official releases is created by the release manager. The release manager uploads the tarball to the [Ruby website](https://www.ruby-lang.org/en/downloads/). -Downstream distributors should use the official release tarballs as part of their build process. This ensures that the tarball is created in a consistent way, and that the tarball is crytographically verified. +Downstream distributors should use the official release tarballs as part of their build process. This ensures that the tarball is created in a consistent way, and that the tarball is cryptographically verified. ### Using the nightly tarball for testing diff --git a/gems/bundled_gems b/gems/bundled_gems index 6b24757a10..15a9df6cce 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -16,11 +16,11 @@ net-ftp 0.3.8 https://github.com/ruby/net-ftp net-imap 0.5.8 https://github.com/ruby/net-imap net-pop 0.1.2 https://github.com/ruby/net-pop net-smtp 0.5.1 https://github.com/ruby/net-smtp -matrix 0.4.2 https://github.com/ruby/matrix 200efebc35dc1a8d16fad671f7006c85cbd0e3f5 -prime 0.1.3 https://github.com/ruby/prime d97973271103f2bdde91f3f0bd3e42526401ad77 +matrix 0.4.3 https://github.com/ruby/matrix +prime 0.1.4 https://github.com/ruby/prime rbs 3.9.4 https://github.com/ruby/rbs typeprof 0.30.1 https://github.com/ruby/typeprof -debug 1.10.0 https://github.com/ruby/debug cf469f2b21710727abdd153b25a1e5123b002bb0 +debug 1.11.0 https://github.com/ruby/debug racc 1.8.1 https://github.com/ruby/racc mutex_m 0.3.0 https://github.com/ruby/mutex_m getoptlong 0.2.1 https://github.com/ruby/getoptlong @@ -35,13 +35,13 @@ nkf 0.2.0 https://github.com/ruby/nkf syslog 0.3.0 https://github.com/ruby/syslog csv 3.3.5 https://github.com/ruby/csv repl_type_completor 0.1.11 https://github.com/ruby/repl_type_completor 25108aa8d69ddaba0b5da3feff1c0035371524b2 -ostruct 0.6.1 https://github.com/ruby/ostruct 50d51248bec5560a102a1024aff4174b31dca8cc +ostruct 0.6.2 https://github.com/ruby/ostruct pstore 0.2.0 https://github.com/ruby/pstore benchmark 0.4.1 https://github.com/ruby/benchmark logger 1.7.0 https://github.com/ruby/logger -rdoc 6.14.0 https://github.com/ruby/rdoc +rdoc 6.14.1 https://github.com/ruby/rdoc win32ole 1.9.2 https://github.com/ruby/win32ole -irb 1.15.2 https://github.com/ruby/irb +irb 1.15.2 https://github.com/ruby/irb 331c4e851296b115db766c291e8cf54a2492fb36 reline 0.6.1 https://github.com/ruby/reline readline 0.0.4 https://github.com/ruby/readline fiddle 1.1.8 https://github.com/ruby/fiddle @@ -145,12 +145,14 @@ rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE (*dumper)(VALUE), compat_allocator_table(); compat = ALLOC(marshal_compat_t); - RB_OBJ_WRITE(compat_allocator_tbl_wrapper, &compat->newclass, newclass); - RB_OBJ_WRITE(compat_allocator_tbl_wrapper, &compat->oldclass, oldclass); + compat->newclass = newclass; + compat->oldclass = oldclass; compat->dumper = dumper; compat->loader = loader; st_insert(compat_allocator_table(), (st_data_t)allocator, (st_data_t)compat); + RB_OBJ_WRITTEN(compat_allocator_tbl_wrapper, Qundef, newclass); + RB_OBJ_WRITTEN(compat_allocator_tbl_wrapper, Qundef, oldclass); } struct dump_arg { diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb index aaacd9a910..6074879d59 100644 --- a/spec/ruby/core/kernel/caller_locations_spec.rb +++ b/spec/ruby/core/kernel/caller_locations_spec.rb @@ -83,7 +83,7 @@ describe 'Kernel#caller_locations' do end end - ruby_version_is "3.4" do + ruby_version_is "3.4"..."3.5" do it "includes core library methods defined in Ruby" do file, line = Kernel.instance_method(:tap).source_location file.should.start_with?('<internal:') @@ -94,5 +94,17 @@ describe 'Kernel#caller_locations' do loc.path.should.start_with? "<internal:" end end + + ruby_version_is "3.5" do + it "does not include core library methods defined in Ruby" do + file, line = Kernel.instance_method(:tap).source_location + file.should.start_with?('<internal:') + + loc = nil + tap { loc = caller_locations(1, 1)[0] } + loc.label.should == "Kernel#tap" + loc.path.should == __FILE__ + end + end end end diff --git a/spec/ruby/core/kernel/caller_spec.rb b/spec/ruby/core/kernel/caller_spec.rb index 33c7929a31..4bf9f7c2c2 100644 --- a/spec/ruby/core/kernel/caller_spec.rb +++ b/spec/ruby/core/kernel/caller_spec.rb @@ -84,13 +84,26 @@ describe 'Kernel#caller' do end guard -> { Kernel.instance_method(:tap).source_location } do - it "includes core library methods defined in Ruby" do - file, line = Kernel.instance_method(:tap).source_location - file.should.start_with?('<internal:') + ruby_version_is ""..."3.5" do + it "includes core library methods defined in Ruby" do + file, line = Kernel.instance_method(:tap).source_location + file.should.start_with?('<internal:') + + loc = nil + tap { loc = caller(1, 1)[0] } + loc.should =~ /\A<internal:.*in [`'](?:Kernel#)?tap'\z/ + end + end + + ruby_version_is "3.5" do + it "includes core library methods defined in Ruby" do + file, line = Kernel.instance_method(:tap).source_location + file.should.start_with?('<internal:') - loc = nil - tap { loc = caller(1, 1)[0] } - loc.should =~ /\A<internal:.*in [`'](?:Kernel#)?tap'\z/ + loc = nil + tap { loc = caller(1, 1)[0] } + loc.should =~ /\A#{ __FILE__ }:.*in [`'](?:Kernel#)?tap'\z/ + end end end end diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb index fca7b62030..01a757f827 100644 --- a/test/ruby/test_backtrace.rb +++ b/test/ruby/test_backtrace.rb @@ -454,4 +454,10 @@ class TestBacktrace < Test::Unit::TestCase foo::Bar.baz end; end + + def test_backtrace_internal_frame + backtrace = tap { break caller_locations(0) } + assert_equal(__FILE__, backtrace[1].path) # not "<internal:kernel>" + assert_equal("Kernel#tap", backtrace[1].label) + end end diff --git a/tool/lib/_tmpdir.rb b/tool/lib/_tmpdir.rb index fd429dab37..daa1a1f235 100644 --- a/tool/lib/_tmpdir.rb +++ b/tool/lib/_tmpdir.rb @@ -4,11 +4,11 @@ template = "rubytest." # Assume the directory by these environment variables are safe. base = [ENV["TMPDIR"], ENV["TMP"], "/tmp"].find do |tmp| next unless tmp and tmp.size <= 50 and File.directory?(tmp) - # On macOS, the default TMPDIR is very long, inspite of UNIX socket - # path length is limited. + # On macOS, the default TMPDIR is very long, in spite of UNIX socket + # path length being limited. # # Also Rubygems creates its own temporary directory per tests, and - # some tests copy the full path of gemhome there. In that caes, the + # some tests copy the full path of gemhome there. In that case, the # path contains both temporary names twice, and can exceed path name # limit very easily. tmp diff --git a/vm_backtrace.c b/vm_backtrace.c index 9046f4aa29..68fc2b987b 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -262,6 +262,15 @@ retry: } } +static bool +is_internal_location(const rb_iseq_t *iseq) +{ + static const char prefix[] = "<internal:"; + const size_t prefix_len = sizeof(prefix) - 1; + VALUE file = rb_iseq_path(iseq); + return strncmp(prefix, RSTRING_PTR(file), prefix_len) == 0; +} + // Return true if a given location is a C method or supposed to behave like one. static inline bool location_cfunc_p(rb_backtrace_location_t *loc) @@ -272,7 +281,7 @@ location_cfunc_p(rb_backtrace_location_t *loc) case VM_METHOD_TYPE_CFUNC: return true; case VM_METHOD_TYPE_ISEQ: - return rb_iseq_attr_p(loc->cme->def->body.iseq.iseqptr, BUILTIN_ATTR_C_TRACE); + return is_internal_location(loc->cme->def->body.iseq.iseqptr); default: return false; } @@ -605,15 +614,6 @@ backtrace_size(const rb_execution_context_t *ec) } static bool -is_internal_location(const rb_control_frame_t *cfp) -{ - static const char prefix[] = "<internal:"; - const size_t prefix_len = sizeof(prefix) - 1; - VALUE file = rb_iseq_path(cfp->iseq); - return strncmp(prefix, RSTRING_PTR(file), prefix_len) == 0; -} - -static bool is_rescue_or_ensure_frame(const rb_control_frame_t *cfp) { enum rb_iseq_type type = ISEQ_BODY(cfp->iseq)->type; @@ -621,11 +621,11 @@ is_rescue_or_ensure_frame(const rb_control_frame_t *cfp) } static void -bt_update_cfunc_loc(unsigned long cfunc_counter, rb_backtrace_location_t *cfunc_loc, const rb_iseq_t *iseq, const VALUE *pc) +bt_backpatch_loc(unsigned long backpatch_counter, rb_backtrace_location_t *loc, const rb_iseq_t *iseq, const VALUE *pc) { - for (; cfunc_counter > 0; cfunc_counter--, cfunc_loc--) { - cfunc_loc->iseq = iseq; - cfunc_loc->pc = pc; + for (; backpatch_counter > 0; backpatch_counter--, loc--) { + loc->iseq = iseq; + loc->pc = pc; } } @@ -648,7 +648,7 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram rb_backtrace_t *bt = NULL; VALUE btobj = Qnil; rb_backtrace_location_t *loc = NULL; - unsigned long cfunc_counter = 0; + unsigned long backpatch_counter = 0; bool skip_next_frame = FALSE; // In the case the thread vm_stack or cfp is not initialized, there is no backtrace. @@ -691,26 +691,36 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram if (start_frame > 0) { start_frame--; } - else if (!(skip_internal && is_internal_location(cfp))) { + else { + bool internal = is_internal_location(cfp->iseq); + if (skip_internal && internal) continue; if (!skip_next_frame) { const rb_iseq_t *iseq = cfp->iseq; const VALUE *pc = cfp->pc; + if (internal && backpatch_counter > 0) { + // To keep only one internal frame, discard the previous backpatch frames + bt->backtrace_size -= backpatch_counter; + backpatch_counter = 0; + } loc = &bt->backtrace[bt->backtrace_size++]; RB_OBJ_WRITE(btobj, &loc->cme, rb_vm_frame_method_entry(cfp)); - // Ruby methods with `Primitive.attr! :c_trace` should behave like C methods - if (rb_iseq_attr_p(cfp->iseq, BUILTIN_ATTR_C_TRACE)) { - loc->iseq = NULL; - loc->pc = NULL; - cfunc_counter++; + // internal frames (`<internal:...>`) should behave like C methods + if (internal) { + // Typically, these iseq and pc are not needed because they will be backpatched later. + // But when the call stack starts with an internal frame (i.e., prelude.rb), + // they will be used to show the `<internal:...>` location. + RB_OBJ_WRITE(btobj, &loc->iseq, iseq); + loc->pc = pc; + backpatch_counter++; } else { RB_OBJ_WRITE(btobj, &loc->iseq, iseq); loc->pc = pc; - bt_update_cfunc_loc(cfunc_counter, loc-1, iseq, pc); + bt_backpatch_loc(backpatch_counter, loc-1, iseq, pc); if (do_yield) { - bt_yield_loc(loc - cfunc_counter, cfunc_counter+1, btobj); + bt_yield_loc(loc - backpatch_counter, backpatch_counter+1, btobj); } - cfunc_counter = 0; + backpatch_counter = 0; } } skip_next_frame = is_rescue_or_ensure_frame(cfp); @@ -727,21 +737,21 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram RB_OBJ_WRITE(btobj, &loc->cme, rb_vm_frame_method_entry(cfp)); loc->iseq = NULL; loc->pc = NULL; - cfunc_counter++; + backpatch_counter++; } } } // When a backtrace entry corresponds to a method defined in C (e.g. rb_define_method), the reported file:line // is the one of the caller Ruby frame, so if the last entry is a C frame we find the caller Ruby frame here. - if (cfunc_counter > 0) { + if (backpatch_counter > 0) { for (; cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) { - if (cfp->iseq && cfp->pc && !(skip_internal && is_internal_location(cfp))) { + if (cfp->iseq && cfp->pc && !(skip_internal && is_internal_location(cfp->iseq))) { VM_ASSERT(!skip_next_frame); // ISEQ_TYPE_RESCUE/ISEQ_TYPE_ENSURE should have a caller Ruby ISEQ, not a cfunc - bt_update_cfunc_loc(cfunc_counter, loc, cfp->iseq, cfp->pc); + bt_backpatch_loc(backpatch_counter, loc, cfp->iseq, cfp->pc); RB_OBJ_WRITTEN(btobj, Qundef, cfp->iseq); if (do_yield) { - bt_yield_loc(loc - cfunc_counter, cfunc_counter, btobj); + bt_yield_loc(loc - backpatch_counter, backpatch_counter, btobj); } break; } |