From 1a66d42fca86b1d47f91cf9de1443c72785fe222 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 11 Oct 2022 16:25:09 +0900 Subject: [PATCH 001/139] [DOC] use the local rdoc reference than the external URI --- time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/time.c b/time.c index b3ad4159352e66..b16a0865378ce7 100644 --- a/time.c +++ b/time.c @@ -4030,7 +4030,7 @@ static VALUE strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding * Returns a string representation of +self+, * formatted by strftime('%a %b %e %T %Y') * or its shorthand version strftime('%c'); - * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]: + * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]: * * t = Time.new(2000, 12, 31, 23, 59, 59, 0.5) * t.ctime # => "Sun Dec 31 23:59:59 2000" From ccf61c01d514da3cac73e44557b8f29762f11d83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 15:22:30 +0000 Subject: [PATCH 002/139] [rubygems/rubygems] Bump rb-sys in /test/rubygems/test_gem_ext_cargo_builder/custom_name Bumps [rb-sys](https://github.com/oxidize-rb/rb-sys) from 0.9.30 to 0.9.31. - [Release notes](https://github.com/oxidize-rb/rb-sys/releases) - [Commits](https://github.com/oxidize-rb/rb-sys/compare/v0.9.30...v0.9.31) --- updated-dependencies: - dependency-name: rb-sys dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] https://github.com/rubygems/rubygems/commit/476490d529 --- .../test_gem_ext_cargo_builder/custom_name/Cargo.lock | 8 ++++---- .../test_gem_ext_cargo_builder/custom_name/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock index 0f1fa7c430e5a8..cf11ab247b86d0 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.30" +version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b22a374fc2e92eb6f49d7efe4eb7663655c6e9455d9259ed3342cc1599da85" +checksum = "bfc41b26ea88da6100f538d31467941e41ab0c002999d687315e67d3b371b796" dependencies = [ "bindgen", "linkify", @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "rb-sys-build" -version = "0.9.30" +version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd23b6dd929b7d50ccb35a6d3aa77dec364328ab9cb304dd32c629332491671" +checksum = "79be4233eabd2bf9e19eb8116391aeaf4b89b87a7ab38e0ded44de9158006e46" dependencies = [ "regex", "shell-words", diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml index c9ba5c27bd696e..8dfbaf5799f097 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = { version = "0.9.30", features = ["gem"] } +rb-sys = { version = "0.9.31", features = ["gem"] } From 5ffbb2be187681a37f2722ce1d7db4ec5c128464 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 15:22:27 +0000 Subject: [PATCH 003/139] [rubygems/rubygems] Bump rb-sys Bumps [rb-sys](https://github.com/oxidize-rb/rb-sys) from 0.9.30 to 0.9.31. - [Release notes](https://github.com/oxidize-rb/rb-sys/releases) - [Commits](https://github.com/oxidize-rb/rb-sys/compare/v0.9.30...v0.9.31) --- updated-dependencies: - dependency-name: rb-sys dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] https://github.com/rubygems/rubygems/commit/4ad57bacea --- .../rust_ruby_example/Cargo.lock | 8 ++++---- .../rust_ruby_example/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock index 5e602fcf92db1f..23f571b1e77430 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock @@ -153,9 +153,9 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.30" +version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b22a374fc2e92eb6f49d7efe4eb7663655c6e9455d9259ed3342cc1599da85" +checksum = "bfc41b26ea88da6100f538d31467941e41ab0c002999d687315e67d3b371b796" dependencies = [ "bindgen", "linkify", @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "rb-sys-build" -version = "0.9.30" +version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd23b6dd929b7d50ccb35a6d3aa77dec364328ab9cb304dd32c629332491671" +checksum = "79be4233eabd2bf9e19eb8116391aeaf4b89b87a7ab38e0ded44de9158006e46" dependencies = [ "regex", "shell-words", diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml index 8e3f623728b3ce..3a37a73f730b1a 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = { version = "0.9.30", features = ["gem"] } +rb-sys = { version = "0.9.31", features = ["gem"] } From ad63b668e22e21c352b852f3119ae98a7acf99f1 Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Mon, 3 Oct 2022 11:14:32 -0400 Subject: [PATCH 004/139] Revert "Revert "This commit implements the Object Shapes technique in CRuby."" This reverts commit 9a6803c90b817f70389cae10d60b50ad752da48f. --- bootstraptest/test_attr.rb | 16 + common.mk | 322 ++++++++++ compile.c | 26 +- debug_counter.h | 9 +- ext/coverage/depend | 4 + ext/objspace/depend | 5 + gc.c | 50 +- include/ruby/internal/core/robject.h | 3 +- include/ruby/internal/fl_type.h | 19 +- inits.c | 1 + internal.h | 3 - internal/class.h | 11 +- internal/object.h | 22 - internal/variable.h | 5 + iseq.c | 14 +- lib/mjit/compiler.rb | 117 ++-- marshal.c | 10 +- misc/lldb_cruby.py | 1 + mjit_c.rb | 35 +- mjit_compiler.h | 2 +- object.c | 46 +- ractor_core.h | 6 +- shape.c | 523 ++++++++++++++++ shape.h | 150 +++++ test/-ext-/marshal/test_internal_ivar.rb | 1 + test/ruby/test_mjit.rb | 4 +- test/ruby/test_object.rb | 9 + test/ruby/test_shapes.rb | 173 ++++++ tool/mjit/bindgen.rb | 10 +- variable.c | 739 +++++++++++------------ variable.h | 10 +- vm.c | 31 + vm_callinfo.h | 108 +++- vm_core.h | 11 +- vm_eval.c | 4 +- vm_insnhelper.c | 485 ++++++++++----- yjit/bindgen/src/main.rs | 7 + yjit/src/asm/x86_64/mod.rs | 2 +- yjit/src/codegen.rs | 133 ++-- yjit/src/cruby.rs | 12 +- yjit/src/cruby_bindings.inc.rs | 39 +- 41 files changed, 2298 insertions(+), 880 deletions(-) create mode 100644 shape.c create mode 100644 shape.h create mode 100644 test/ruby/test_shapes.rb diff --git a/bootstraptest/test_attr.rb b/bootstraptest/test_attr.rb index 721a847145a797..3cb9d3eb39541c 100644 --- a/bootstraptest/test_attr.rb +++ b/bootstraptest/test_attr.rb @@ -34,3 +34,19 @@ class A print "ok" end }, '[ruby-core:15120]' + +assert_equal %{ok}, %{ + class Big + attr_reader :foo + def initialize + @foo = "ok" + end + end + + obj = Big.new + 100.times do |i| + obj.instance_variable_set(:"@ivar_\#{i}", i) + end + + Big.new.foo +} diff --git a/common.mk b/common.mk index c56817d9571288..0fe52175662d36 100644 --- a/common.mk +++ b/common.mk @@ -136,6 +136,7 @@ COMMONOBJS = array.$(OBJEXT) \ regsyntax.$(OBJEXT) \ ruby.$(OBJEXT) \ scheduler.$(OBJEXT) \ + shape.$(OBJEXT) \ signal.$(OBJEXT) \ sprintf.$(OBJEXT) \ st.$(OBJEXT) \ @@ -1832,6 +1833,7 @@ array.$(OBJEXT): $(top_srcdir)/internal/proc.h array.$(OBJEXT): $(top_srcdir)/internal/rational.h array.$(OBJEXT): $(top_srcdir)/internal/serial.h array.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +array.$(OBJEXT): $(top_srcdir)/internal/variable.h array.$(OBJEXT): $(top_srcdir)/internal/vm.h array.$(OBJEXT): $(top_srcdir)/internal/warnings.h array.$(OBJEXT): {$(VPATH)}array.c @@ -1848,6 +1850,7 @@ array.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h array.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h array.$(OBJEXT): {$(VPATH)}builtin.h array.$(OBJEXT): {$(VPATH)}config.h +array.$(OBJEXT): {$(VPATH)}constant.h array.$(OBJEXT): {$(VPATH)}debug_counter.h array.$(OBJEXT): {$(VPATH)}defines.h array.$(OBJEXT): {$(VPATH)}encoding.h @@ -2010,6 +2013,7 @@ array.$(OBJEXT): {$(VPATH)}oniguruma.h array.$(OBJEXT): {$(VPATH)}probes.dmyh array.$(OBJEXT): {$(VPATH)}probes.h array.$(OBJEXT): {$(VPATH)}ruby_assert.h +array.$(OBJEXT): {$(VPATH)}shape.h array.$(OBJEXT): {$(VPATH)}st.h array.$(OBJEXT): {$(VPATH)}subst.h array.$(OBJEXT): {$(VPATH)}transient_heap.h @@ -2028,6 +2032,7 @@ ast.$(OBJEXT): $(top_srcdir)/internal/parse.h ast.$(OBJEXT): $(top_srcdir)/internal/serial.h ast.$(OBJEXT): $(top_srcdir)/internal/static_assert.h ast.$(OBJEXT): $(top_srcdir)/internal/symbol.h +ast.$(OBJEXT): $(top_srcdir)/internal/variable.h ast.$(OBJEXT): $(top_srcdir)/internal/vm.h ast.$(OBJEXT): $(top_srcdir)/internal/warnings.h ast.$(OBJEXT): {$(VPATH)}assert.h @@ -2045,9 +2050,11 @@ ast.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h ast.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h ast.$(OBJEXT): {$(VPATH)}builtin.h ast.$(OBJEXT): {$(VPATH)}config.h +ast.$(OBJEXT): {$(VPATH)}constant.h ast.$(OBJEXT): {$(VPATH)}defines.h ast.$(OBJEXT): {$(VPATH)}encoding.h ast.$(OBJEXT): {$(VPATH)}id.h +ast.$(OBJEXT): {$(VPATH)}id_table.h ast.$(OBJEXT): {$(VPATH)}intern.h ast.$(OBJEXT): {$(VPATH)}internal.h ast.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -2207,6 +2214,7 @@ ast.$(OBJEXT): {$(VPATH)}onigmo.h ast.$(OBJEXT): {$(VPATH)}oniguruma.h ast.$(OBJEXT): {$(VPATH)}ruby_assert.h ast.$(OBJEXT): {$(VPATH)}ruby_atomic.h +ast.$(OBJEXT): {$(VPATH)}shape.h ast.$(OBJEXT): {$(VPATH)}st.h ast.$(OBJEXT): {$(VPATH)}subst.h ast.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -2390,6 +2398,7 @@ bignum.$(OBJEXT): {$(VPATH)}internal/warning_push.h bignum.$(OBJEXT): {$(VPATH)}internal/xmalloc.h bignum.$(OBJEXT): {$(VPATH)}missing.h bignum.$(OBJEXT): {$(VPATH)}ruby_assert.h +bignum.$(OBJEXT): {$(VPATH)}shape.h bignum.$(OBJEXT): {$(VPATH)}st.h bignum.$(OBJEXT): {$(VPATH)}subst.h bignum.$(OBJEXT): {$(VPATH)}thread.h @@ -2405,6 +2414,7 @@ builtin.$(OBJEXT): $(top_srcdir)/internal/gc.h builtin.$(OBJEXT): $(top_srcdir)/internal/imemo.h builtin.$(OBJEXT): $(top_srcdir)/internal/serial.h builtin.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +builtin.$(OBJEXT): $(top_srcdir)/internal/variable.h builtin.$(OBJEXT): $(top_srcdir)/internal/vm.h builtin.$(OBJEXT): $(top_srcdir)/internal/warnings.h builtin.$(OBJEXT): {$(VPATH)}assert.h @@ -2422,8 +2432,10 @@ builtin.$(OBJEXT): {$(VPATH)}builtin.c builtin.$(OBJEXT): {$(VPATH)}builtin.h builtin.$(OBJEXT): {$(VPATH)}builtin_binary.inc builtin.$(OBJEXT): {$(VPATH)}config.h +builtin.$(OBJEXT): {$(VPATH)}constant.h builtin.$(OBJEXT): {$(VPATH)}defines.h builtin.$(OBJEXT): {$(VPATH)}id.h +builtin.$(OBJEXT): {$(VPATH)}id_table.h builtin.$(OBJEXT): {$(VPATH)}intern.h builtin.$(OBJEXT): {$(VPATH)}internal.h builtin.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -2572,6 +2584,7 @@ builtin.$(OBJEXT): {$(VPATH)}missing.h builtin.$(OBJEXT): {$(VPATH)}node.h builtin.$(OBJEXT): {$(VPATH)}ruby_assert.h builtin.$(OBJEXT): {$(VPATH)}ruby_atomic.h +builtin.$(OBJEXT): {$(VPATH)}shape.h builtin.$(OBJEXT): {$(VPATH)}st.h builtin.$(OBJEXT): {$(VPATH)}subst.h builtin.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -2774,6 +2787,7 @@ class.$(OBJEXT): {$(VPATH)}onigmo.h class.$(OBJEXT): {$(VPATH)}oniguruma.h class.$(OBJEXT): {$(VPATH)}ruby_assert.h class.$(OBJEXT): {$(VPATH)}ruby_atomic.h +class.$(OBJEXT): {$(VPATH)}shape.h class.$(OBJEXT): {$(VPATH)}st.h class.$(OBJEXT): {$(VPATH)}subst.h class.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -3177,6 +3191,7 @@ compile.$(OBJEXT): {$(VPATH)}re.h compile.$(OBJEXT): {$(VPATH)}regex.h compile.$(OBJEXT): {$(VPATH)}ruby_assert.h compile.$(OBJEXT): {$(VPATH)}ruby_atomic.h +compile.$(OBJEXT): {$(VPATH)}shape.h compile.$(OBJEXT): {$(VPATH)}st.h compile.$(OBJEXT): {$(VPATH)}subst.h compile.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -3201,6 +3216,7 @@ complex.$(OBJEXT): $(top_srcdir)/internal/object.h complex.$(OBJEXT): $(top_srcdir)/internal/rational.h complex.$(OBJEXT): $(top_srcdir)/internal/serial.h complex.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +complex.$(OBJEXT): $(top_srcdir)/internal/variable.h complex.$(OBJEXT): $(top_srcdir)/internal/vm.h complex.$(OBJEXT): $(top_srcdir)/internal/warnings.h complex.$(OBJEXT): {$(VPATH)}assert.h @@ -3215,6 +3231,7 @@ complex.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h complex.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h complex.$(OBJEXT): {$(VPATH)}complex.c complex.$(OBJEXT): {$(VPATH)}config.h +complex.$(OBJEXT): {$(VPATH)}constant.h complex.$(OBJEXT): {$(VPATH)}defines.h complex.$(OBJEXT): {$(VPATH)}id.h complex.$(OBJEXT): {$(VPATH)}id_table.h @@ -3362,6 +3379,7 @@ complex.$(OBJEXT): {$(VPATH)}internal/warning_push.h complex.$(OBJEXT): {$(VPATH)}internal/xmalloc.h complex.$(OBJEXT): {$(VPATH)}missing.h complex.$(OBJEXT): {$(VPATH)}ruby_assert.h +complex.$(OBJEXT): {$(VPATH)}shape.h complex.$(OBJEXT): {$(VPATH)}st.h complex.$(OBJEXT): {$(VPATH)}subst.h cont.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h @@ -3379,6 +3397,7 @@ cont.$(OBJEXT): $(top_srcdir)/internal/proc.h cont.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h cont.$(OBJEXT): $(top_srcdir)/internal/serial.h cont.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +cont.$(OBJEXT): $(top_srcdir)/internal/variable.h cont.$(OBJEXT): $(top_srcdir)/internal/vm.h cont.$(OBJEXT): $(top_srcdir)/internal/warnings.h cont.$(OBJEXT): {$(VPATH)}$(COROUTINE_H) @@ -3394,6 +3413,7 @@ cont.$(OBJEXT): {$(VPATH)}backward/2/long_long.h cont.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h cont.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h cont.$(OBJEXT): {$(VPATH)}config.h +cont.$(OBJEXT): {$(VPATH)}constant.h cont.$(OBJEXT): {$(VPATH)}cont.c cont.$(OBJEXT): {$(VPATH)}debug_counter.h cont.$(OBJEXT): {$(VPATH)}defines.h @@ -3552,6 +3572,7 @@ cont.$(OBJEXT): {$(VPATH)}ractor.h cont.$(OBJEXT): {$(VPATH)}ractor_core.h cont.$(OBJEXT): {$(VPATH)}ruby_assert.h cont.$(OBJEXT): {$(VPATH)}ruby_atomic.h +cont.$(OBJEXT): {$(VPATH)}shape.h cont.$(OBJEXT): {$(VPATH)}st.h cont.$(OBJEXT): {$(VPATH)}subst.h cont.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -3572,6 +3593,7 @@ debug.$(OBJEXT): $(top_srcdir)/internal/imemo.h debug.$(OBJEXT): $(top_srcdir)/internal/serial.h debug.$(OBJEXT): $(top_srcdir)/internal/signal.h debug.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +debug.$(OBJEXT): $(top_srcdir)/internal/variable.h debug.$(OBJEXT): $(top_srcdir)/internal/vm.h debug.$(OBJEXT): $(top_srcdir)/internal/warnings.h debug.$(OBJEXT): {$(VPATH)}assert.h @@ -3586,6 +3608,7 @@ debug.$(OBJEXT): {$(VPATH)}backward/2/long_long.h debug.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h debug.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h debug.$(OBJEXT): {$(VPATH)}config.h +debug.$(OBJEXT): {$(VPATH)}constant.h debug.$(OBJEXT): {$(VPATH)}debug.c debug.$(OBJEXT): {$(VPATH)}debug_counter.h debug.$(OBJEXT): {$(VPATH)}defines.h @@ -3756,6 +3779,7 @@ debug.$(OBJEXT): {$(VPATH)}ractor.h debug.$(OBJEXT): {$(VPATH)}ractor_core.h debug.$(OBJEXT): {$(VPATH)}ruby_assert.h debug.$(OBJEXT): {$(VPATH)}ruby_atomic.h +debug.$(OBJEXT): {$(VPATH)}shape.h debug.$(OBJEXT): {$(VPATH)}st.h debug.$(OBJEXT): {$(VPATH)}subst.h debug.$(OBJEXT): {$(VPATH)}symbol.h @@ -3940,6 +3964,7 @@ dir.$(OBJEXT): $(top_srcdir)/internal/object.h dir.$(OBJEXT): $(top_srcdir)/internal/serial.h dir.$(OBJEXT): $(top_srcdir)/internal/static_assert.h dir.$(OBJEXT): $(top_srcdir)/internal/string.h +dir.$(OBJEXT): $(top_srcdir)/internal/variable.h dir.$(OBJEXT): $(top_srcdir)/internal/vm.h dir.$(OBJEXT): $(top_srcdir)/internal/warnings.h dir.$(OBJEXT): {$(VPATH)}assert.h @@ -3954,6 +3979,7 @@ dir.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h dir.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h dir.$(OBJEXT): {$(VPATH)}builtin.h dir.$(OBJEXT): {$(VPATH)}config.h +dir.$(OBJEXT): {$(VPATH)}constant.h dir.$(OBJEXT): {$(VPATH)}defines.h dir.$(OBJEXT): {$(VPATH)}dir.c dir.$(OBJEXT): {$(VPATH)}dir.rbinc @@ -4116,6 +4142,7 @@ dir.$(OBJEXT): {$(VPATH)}io.h dir.$(OBJEXT): {$(VPATH)}missing.h dir.$(OBJEXT): {$(VPATH)}onigmo.h dir.$(OBJEXT): {$(VPATH)}oniguruma.h +dir.$(OBJEXT): {$(VPATH)}shape.h dir.$(OBJEXT): {$(VPATH)}st.h dir.$(OBJEXT): {$(VPATH)}subst.h dir.$(OBJEXT): {$(VPATH)}thread.h @@ -5441,6 +5468,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/object.h encoding.$(OBJEXT): $(top_srcdir)/internal/serial.h encoding.$(OBJEXT): $(top_srcdir)/internal/static_assert.h encoding.$(OBJEXT): $(top_srcdir)/internal/string.h +encoding.$(OBJEXT): $(top_srcdir)/internal/variable.h encoding.$(OBJEXT): $(top_srcdir)/internal/vm.h encoding.$(OBJEXT): $(top_srcdir)/internal/warnings.h encoding.$(OBJEXT): {$(VPATH)}assert.h @@ -5454,6 +5482,7 @@ encoding.$(OBJEXT): {$(VPATH)}backward/2/long_long.h encoding.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h encoding.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h encoding.$(OBJEXT): {$(VPATH)}config.h +encoding.$(OBJEXT): {$(VPATH)}constant.h encoding.$(OBJEXT): {$(VPATH)}debug_counter.h encoding.$(OBJEXT): {$(VPATH)}defines.h encoding.$(OBJEXT): {$(VPATH)}encindex.h @@ -5616,6 +5645,7 @@ encoding.$(OBJEXT): {$(VPATH)}onigmo.h encoding.$(OBJEXT): {$(VPATH)}oniguruma.h encoding.$(OBJEXT): {$(VPATH)}regenc.h encoding.$(OBJEXT): {$(VPATH)}ruby_assert.h +encoding.$(OBJEXT): {$(VPATH)}shape.h encoding.$(OBJEXT): {$(VPATH)}st.h encoding.$(OBJEXT): {$(VPATH)}subst.h encoding.$(OBJEXT): {$(VPATH)}util.h @@ -5640,6 +5670,7 @@ enum.$(OBJEXT): $(top_srcdir)/internal/rational.h enum.$(OBJEXT): $(top_srcdir)/internal/re.h enum.$(OBJEXT): $(top_srcdir)/internal/serial.h enum.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +enum.$(OBJEXT): $(top_srcdir)/internal/variable.h enum.$(OBJEXT): $(top_srcdir)/internal/vm.h enum.$(OBJEXT): $(top_srcdir)/internal/warnings.h enum.$(OBJEXT): {$(VPATH)}assert.h @@ -5653,6 +5684,7 @@ enum.$(OBJEXT): {$(VPATH)}backward/2/long_long.h enum.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h enum.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h enum.$(OBJEXT): {$(VPATH)}config.h +enum.$(OBJEXT): {$(VPATH)}constant.h enum.$(OBJEXT): {$(VPATH)}defines.h enum.$(OBJEXT): {$(VPATH)}encoding.h enum.$(OBJEXT): {$(VPATH)}enum.c @@ -5813,6 +5845,7 @@ enum.$(OBJEXT): {$(VPATH)}missing.h enum.$(OBJEXT): {$(VPATH)}onigmo.h enum.$(OBJEXT): {$(VPATH)}oniguruma.h enum.$(OBJEXT): {$(VPATH)}ruby_assert.h +enum.$(OBJEXT): {$(VPATH)}shape.h enum.$(OBJEXT): {$(VPATH)}st.h enum.$(OBJEXT): {$(VPATH)}subst.h enum.$(OBJEXT): {$(VPATH)}symbol.h @@ -6212,6 +6245,7 @@ error.$(OBJEXT): {$(VPATH)}onigmo.h error.$(OBJEXT): {$(VPATH)}oniguruma.h error.$(OBJEXT): {$(VPATH)}ruby_assert.h error.$(OBJEXT): {$(VPATH)}ruby_atomic.h +error.$(OBJEXT): {$(VPATH)}shape.h error.$(OBJEXT): {$(VPATH)}st.h error.$(OBJEXT): {$(VPATH)}subst.h error.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -6433,6 +6467,7 @@ eval.$(OBJEXT): {$(VPATH)}ractor.h eval.$(OBJEXT): {$(VPATH)}ractor_core.h eval.$(OBJEXT): {$(VPATH)}ruby_assert.h eval.$(OBJEXT): {$(VPATH)}ruby_atomic.h +eval.$(OBJEXT): {$(VPATH)}shape.h eval.$(OBJEXT): {$(VPATH)}st.h eval.$(OBJEXT): {$(VPATH)}subst.h eval.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -6473,6 +6508,7 @@ file.$(OBJEXT): $(top_srcdir)/internal/serial.h file.$(OBJEXT): $(top_srcdir)/internal/static_assert.h file.$(OBJEXT): $(top_srcdir)/internal/string.h file.$(OBJEXT): $(top_srcdir)/internal/thread.h +file.$(OBJEXT): $(top_srcdir)/internal/variable.h file.$(OBJEXT): $(top_srcdir)/internal/vm.h file.$(OBJEXT): $(top_srcdir)/internal/warnings.h file.$(OBJEXT): {$(VPATH)}assert.h @@ -6486,6 +6522,7 @@ file.$(OBJEXT): {$(VPATH)}backward/2/long_long.h file.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h file.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h file.$(OBJEXT): {$(VPATH)}config.h +file.$(OBJEXT): {$(VPATH)}constant.h file.$(OBJEXT): {$(VPATH)}defines.h file.$(OBJEXT): {$(VPATH)}dln.h file.$(OBJEXT): {$(VPATH)}encindex.h @@ -6648,6 +6685,7 @@ file.$(OBJEXT): {$(VPATH)}io.h file.$(OBJEXT): {$(VPATH)}missing.h file.$(OBJEXT): {$(VPATH)}onigmo.h file.$(OBJEXT): {$(VPATH)}oniguruma.h +file.$(OBJEXT): {$(VPATH)}shape.h file.$(OBJEXT): {$(VPATH)}st.h file.$(OBJEXT): {$(VPATH)}subst.h file.$(OBJEXT): {$(VPATH)}thread.h @@ -6880,6 +6918,7 @@ gc.$(OBJEXT): {$(VPATH)}regex.h gc.$(OBJEXT): {$(VPATH)}regint.h gc.$(OBJEXT): {$(VPATH)}ruby_assert.h gc.$(OBJEXT): {$(VPATH)}ruby_atomic.h +gc.$(OBJEXT): {$(VPATH)}shape.h gc.$(OBJEXT): {$(VPATH)}st.h gc.$(OBJEXT): {$(VPATH)}subst.h gc.$(OBJEXT): {$(VPATH)}symbol.h @@ -6905,6 +6944,7 @@ goruby.$(OBJEXT): $(top_srcdir)/internal/gc.h goruby.$(OBJEXT): $(top_srcdir)/internal/imemo.h goruby.$(OBJEXT): $(top_srcdir)/internal/serial.h goruby.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +goruby.$(OBJEXT): $(top_srcdir)/internal/variable.h goruby.$(OBJEXT): $(top_srcdir)/internal/vm.h goruby.$(OBJEXT): $(top_srcdir)/internal/warnings.h goruby.$(OBJEXT): {$(VPATH)}assert.h @@ -6920,11 +6960,13 @@ goruby.$(OBJEXT): {$(VPATH)}backward/2/long_long.h goruby.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h goruby.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h goruby.$(OBJEXT): {$(VPATH)}config.h +goruby.$(OBJEXT): {$(VPATH)}constant.h goruby.$(OBJEXT): {$(VPATH)}defines.h goruby.$(OBJEXT): {$(VPATH)}golf_prelude.c goruby.$(OBJEXT): {$(VPATH)}golf_prelude.rb goruby.$(OBJEXT): {$(VPATH)}goruby.c goruby.$(OBJEXT): {$(VPATH)}id.h +goruby.$(OBJEXT): {$(VPATH)}id_table.h goruby.$(OBJEXT): {$(VPATH)}intern.h goruby.$(OBJEXT): {$(VPATH)}internal.h goruby.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -7074,6 +7116,7 @@ goruby.$(OBJEXT): {$(VPATH)}missing.h goruby.$(OBJEXT): {$(VPATH)}node.h goruby.$(OBJEXT): {$(VPATH)}ruby_assert.h goruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h +goruby.$(OBJEXT): {$(VPATH)}shape.h goruby.$(OBJEXT): {$(VPATH)}st.h goruby.$(OBJEXT): {$(VPATH)}subst.h goruby.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -7099,6 +7142,7 @@ hash.$(OBJEXT): $(top_srcdir)/internal/string.h hash.$(OBJEXT): $(top_srcdir)/internal/symbol.h hash.$(OBJEXT): $(top_srcdir)/internal/thread.h hash.$(OBJEXT): $(top_srcdir)/internal/time.h +hash.$(OBJEXT): $(top_srcdir)/internal/variable.h hash.$(OBJEXT): $(top_srcdir)/internal/vm.h hash.$(OBJEXT): $(top_srcdir)/internal/warnings.h hash.$(OBJEXT): {$(VPATH)}assert.h @@ -7112,6 +7156,7 @@ hash.$(OBJEXT): {$(VPATH)}backward/2/long_long.h hash.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h hash.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h hash.$(OBJEXT): {$(VPATH)}config.h +hash.$(OBJEXT): {$(VPATH)}constant.h hash.$(OBJEXT): {$(VPATH)}debug_counter.h hash.$(OBJEXT): {$(VPATH)}defines.h hash.$(OBJEXT): {$(VPATH)}encoding.h @@ -7276,6 +7321,7 @@ hash.$(OBJEXT): {$(VPATH)}probes.dmyh hash.$(OBJEXT): {$(VPATH)}probes.h hash.$(OBJEXT): {$(VPATH)}ractor.h hash.$(OBJEXT): {$(VPATH)}ruby_assert.h +hash.$(OBJEXT): {$(VPATH)}shape.h hash.$(OBJEXT): {$(VPATH)}st.h hash.$(OBJEXT): {$(VPATH)}subst.h hash.$(OBJEXT): {$(VPATH)}symbol.h @@ -7660,6 +7706,7 @@ io.$(OBJEXT): {$(VPATH)}oniguruma.h io.$(OBJEXT): {$(VPATH)}ractor.h io.$(OBJEXT): {$(VPATH)}ruby_assert.h io.$(OBJEXT): {$(VPATH)}ruby_atomic.h +io.$(OBJEXT): {$(VPATH)}shape.h io.$(OBJEXT): {$(VPATH)}st.h io.$(OBJEXT): {$(VPATH)}subst.h io.$(OBJEXT): {$(VPATH)}thread.h @@ -8062,6 +8109,7 @@ iseq.$(OBJEXT): {$(VPATH)}oniguruma.h iseq.$(OBJEXT): {$(VPATH)}ractor.h iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h +iseq.$(OBJEXT): {$(VPATH)}shape.h iseq.$(OBJEXT): {$(VPATH)}st.h iseq.$(OBJEXT): {$(VPATH)}subst.h iseq.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -8274,6 +8322,7 @@ load.$(OBJEXT): {$(VPATH)}probes.dmyh load.$(OBJEXT): {$(VPATH)}probes.h load.$(OBJEXT): {$(VPATH)}ruby_assert.h load.$(OBJEXT): {$(VPATH)}ruby_atomic.h +load.$(OBJEXT): {$(VPATH)}shape.h load.$(OBJEXT): {$(VPATH)}st.h load.$(OBJEXT): {$(VPATH)}subst.h load.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -8792,6 +8841,7 @@ marshal.$(OBJEXT): $(top_srcdir)/internal/string.h marshal.$(OBJEXT): $(top_srcdir)/internal/struct.h marshal.$(OBJEXT): $(top_srcdir)/internal/symbol.h marshal.$(OBJEXT): $(top_srcdir)/internal/util.h +marshal.$(OBJEXT): $(top_srcdir)/internal/variable.h marshal.$(OBJEXT): $(top_srcdir)/internal/vm.h marshal.$(OBJEXT): $(top_srcdir)/internal/warnings.h marshal.$(OBJEXT): {$(VPATH)}assert.h @@ -8806,6 +8856,7 @@ marshal.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h marshal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h marshal.$(OBJEXT): {$(VPATH)}builtin.h marshal.$(OBJEXT): {$(VPATH)}config.h +marshal.$(OBJEXT): {$(VPATH)}constant.h marshal.$(OBJEXT): {$(VPATH)}defines.h marshal.$(OBJEXT): {$(VPATH)}encindex.h marshal.$(OBJEXT): {$(VPATH)}encoding.h @@ -8967,6 +9018,7 @@ marshal.$(OBJEXT): {$(VPATH)}marshal.rbinc marshal.$(OBJEXT): {$(VPATH)}missing.h marshal.$(OBJEXT): {$(VPATH)}onigmo.h marshal.$(OBJEXT): {$(VPATH)}oniguruma.h +marshal.$(OBJEXT): {$(VPATH)}shape.h marshal.$(OBJEXT): {$(VPATH)}st.h marshal.$(OBJEXT): {$(VPATH)}subst.h marshal.$(OBJEXT): {$(VPATH)}util.h @@ -8980,6 +9032,7 @@ math.$(OBJEXT): $(top_srcdir)/internal/math.h math.$(OBJEXT): $(top_srcdir)/internal/object.h math.$(OBJEXT): $(top_srcdir)/internal/serial.h math.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +math.$(OBJEXT): $(top_srcdir)/internal/variable.h math.$(OBJEXT): $(top_srcdir)/internal/vm.h math.$(OBJEXT): $(top_srcdir)/internal/warnings.h math.$(OBJEXT): {$(VPATH)}assert.h @@ -8993,6 +9046,7 @@ math.$(OBJEXT): {$(VPATH)}backward/2/long_long.h math.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h math.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h math.$(OBJEXT): {$(VPATH)}config.h +math.$(OBJEXT): {$(VPATH)}constant.h math.$(OBJEXT): {$(VPATH)}defines.h math.$(OBJEXT): {$(VPATH)}id_table.h math.$(OBJEXT): {$(VPATH)}intern.h @@ -9139,6 +9193,7 @@ math.$(OBJEXT): {$(VPATH)}internal/warning_push.h math.$(OBJEXT): {$(VPATH)}internal/xmalloc.h math.$(OBJEXT): {$(VPATH)}math.c math.$(OBJEXT): {$(VPATH)}missing.h +math.$(OBJEXT): {$(VPATH)}shape.h math.$(OBJEXT): {$(VPATH)}st.h math.$(OBJEXT): {$(VPATH)}subst.h memory_view.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -9320,6 +9375,7 @@ miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h miniinit.$(OBJEXT): $(top_srcdir)/internal/imemo.h miniinit.$(OBJEXT): $(top_srcdir)/internal/serial.h miniinit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +miniinit.$(OBJEXT): $(top_srcdir)/internal/variable.h miniinit.$(OBJEXT): $(top_srcdir)/internal/vm.h miniinit.$(OBJEXT): $(top_srcdir)/internal/warnings.h miniinit.$(OBJEXT): {$(VPATH)}array.rb @@ -9337,12 +9393,14 @@ miniinit.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h miniinit.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h miniinit.$(OBJEXT): {$(VPATH)}builtin.h miniinit.$(OBJEXT): {$(VPATH)}config.h +miniinit.$(OBJEXT): {$(VPATH)}constant.h miniinit.$(OBJEXT): {$(VPATH)}defines.h miniinit.$(OBJEXT): {$(VPATH)}dir.rb miniinit.$(OBJEXT): {$(VPATH)}encoding.h miniinit.$(OBJEXT): {$(VPATH)}gc.rb miniinit.$(OBJEXT): {$(VPATH)}gem_prelude.rb miniinit.$(OBJEXT): {$(VPATH)}id.h +miniinit.$(OBJEXT): {$(VPATH)}id_table.h miniinit.$(OBJEXT): {$(VPATH)}intern.h miniinit.$(OBJEXT): {$(VPATH)}internal.h miniinit.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -9516,6 +9574,7 @@ miniinit.$(OBJEXT): {$(VPATH)}prelude.rb miniinit.$(OBJEXT): {$(VPATH)}ractor.rb miniinit.$(OBJEXT): {$(VPATH)}ruby_assert.h miniinit.$(OBJEXT): {$(VPATH)}ruby_atomic.h +miniinit.$(OBJEXT): {$(VPATH)}shape.h miniinit.$(OBJEXT): {$(VPATH)}st.h miniinit.$(OBJEXT): {$(VPATH)}subst.h miniinit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -9547,6 +9606,7 @@ mjit.$(OBJEXT): $(top_srcdir)/internal/imemo.h mjit.$(OBJEXT): $(top_srcdir)/internal/process.h mjit.$(OBJEXT): $(top_srcdir)/internal/serial.h mjit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +mjit.$(OBJEXT): $(top_srcdir)/internal/variable.h mjit.$(OBJEXT): $(top_srcdir)/internal/vm.h mjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h mjit.$(OBJEXT): {$(VPATH)}assert.h @@ -9741,6 +9801,7 @@ mjit.$(OBJEXT): {$(VPATH)}ractor.h mjit.$(OBJEXT): {$(VPATH)}ractor_core.h mjit.$(OBJEXT): {$(VPATH)}ruby_assert.h mjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h +mjit.$(OBJEXT): {$(VPATH)}shape.h mjit.$(OBJEXT): {$(VPATH)}st.h mjit.$(OBJEXT): {$(VPATH)}subst.h mjit.$(OBJEXT): {$(VPATH)}thread.h @@ -9951,6 +10012,7 @@ mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_unit.h mjit_compiler.$(OBJEXT): {$(VPATH)}node.h mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_assert.h mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_atomic.h +mjit_compiler.$(OBJEXT): {$(VPATH)}shape.h mjit_compiler.$(OBJEXT): {$(VPATH)}st.h mjit_compiler.$(OBJEXT): {$(VPATH)}subst.h mjit_compiler.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -10140,6 +10202,7 @@ node.$(OBJEXT): {$(VPATH)}node.c node.$(OBJEXT): {$(VPATH)}node.h node.$(OBJEXT): {$(VPATH)}ruby_assert.h node.$(OBJEXT): {$(VPATH)}ruby_atomic.h +node.$(OBJEXT): {$(VPATH)}shape.h node.$(OBJEXT): {$(VPATH)}st.h node.$(OBJEXT): {$(VPATH)}subst.h node.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -10342,6 +10405,7 @@ numeric.$(OBJEXT): {$(VPATH)}numeric.rbinc numeric.$(OBJEXT): {$(VPATH)}onigmo.h numeric.$(OBJEXT): {$(VPATH)}oniguruma.h numeric.$(OBJEXT): {$(VPATH)}ruby_assert.h +numeric.$(OBJEXT): {$(VPATH)}shape.h numeric.$(OBJEXT): {$(VPATH)}st.h numeric.$(OBJEXT): {$(VPATH)}subst.h numeric.$(OBJEXT): {$(VPATH)}util.h @@ -10543,6 +10607,7 @@ object.$(OBJEXT): {$(VPATH)}onigmo.h object.$(OBJEXT): {$(VPATH)}oniguruma.h object.$(OBJEXT): {$(VPATH)}probes.dmyh object.$(OBJEXT): {$(VPATH)}probes.h +object.$(OBJEXT): {$(VPATH)}shape.h object.$(OBJEXT): {$(VPATH)}st.h object.$(OBJEXT): {$(VPATH)}subst.h object.$(OBJEXT): {$(VPATH)}util.h @@ -10961,6 +11026,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/serial.h proc.$(OBJEXT): $(top_srcdir)/internal/static_assert.h proc.$(OBJEXT): $(top_srcdir)/internal/string.h proc.$(OBJEXT): $(top_srcdir)/internal/symbol.h +proc.$(OBJEXT): $(top_srcdir)/internal/variable.h proc.$(OBJEXT): $(top_srcdir)/internal/vm.h proc.$(OBJEXT): $(top_srcdir)/internal/warnings.h proc.$(OBJEXT): {$(VPATH)}assert.h @@ -10975,6 +11041,7 @@ proc.$(OBJEXT): {$(VPATH)}backward/2/long_long.h proc.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h proc.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h proc.$(OBJEXT): {$(VPATH)}config.h +proc.$(OBJEXT): {$(VPATH)}constant.h proc.$(OBJEXT): {$(VPATH)}defines.h proc.$(OBJEXT): {$(VPATH)}encoding.h proc.$(OBJEXT): {$(VPATH)}eval_intern.h @@ -11141,6 +11208,7 @@ proc.$(OBJEXT): {$(VPATH)}oniguruma.h proc.$(OBJEXT): {$(VPATH)}proc.c proc.$(OBJEXT): {$(VPATH)}ruby_assert.h proc.$(OBJEXT): {$(VPATH)}ruby_atomic.h +proc.$(OBJEXT): {$(VPATH)}shape.h proc.$(OBJEXT): {$(VPATH)}st.h proc.$(OBJEXT): {$(VPATH)}subst.h proc.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -11360,6 +11428,7 @@ process.$(OBJEXT): {$(VPATH)}process.c process.$(OBJEXT): {$(VPATH)}ractor.h process.$(OBJEXT): {$(VPATH)}ruby_assert.h process.$(OBJEXT): {$(VPATH)}ruby_atomic.h +process.$(OBJEXT): {$(VPATH)}shape.h process.$(OBJEXT): {$(VPATH)}st.h process.$(OBJEXT): {$(VPATH)}subst.h process.$(OBJEXT): {$(VPATH)}thread.h @@ -11390,6 +11459,7 @@ ractor.$(OBJEXT): $(top_srcdir)/internal/static_assert.h ractor.$(OBJEXT): $(top_srcdir)/internal/string.h ractor.$(OBJEXT): $(top_srcdir)/internal/struct.h ractor.$(OBJEXT): $(top_srcdir)/internal/thread.h +ractor.$(OBJEXT): $(top_srcdir)/internal/variable.h ractor.$(OBJEXT): $(top_srcdir)/internal/vm.h ractor.$(OBJEXT): $(top_srcdir)/internal/warnings.h ractor.$(OBJEXT): {$(VPATH)}assert.h @@ -11405,6 +11475,7 @@ ractor.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h ractor.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h ractor.$(OBJEXT): {$(VPATH)}builtin.h ractor.$(OBJEXT): {$(VPATH)}config.h +ractor.$(OBJEXT): {$(VPATH)}constant.h ractor.$(OBJEXT): {$(VPATH)}debug_counter.h ractor.$(OBJEXT): {$(VPATH)}defines.h ractor.$(OBJEXT): {$(VPATH)}encoding.h @@ -11574,6 +11645,7 @@ ractor.$(OBJEXT): {$(VPATH)}ractor.rbinc ractor.$(OBJEXT): {$(VPATH)}ractor_core.h ractor.$(OBJEXT): {$(VPATH)}ruby_assert.h ractor.$(OBJEXT): {$(VPATH)}ruby_atomic.h +ractor.$(OBJEXT): {$(VPATH)}shape.h ractor.$(OBJEXT): {$(VPATH)}st.h ractor.$(OBJEXT): {$(VPATH)}subst.h ractor.$(OBJEXT): {$(VPATH)}thread.h @@ -11971,6 +12043,7 @@ rational.$(OBJEXT): $(top_srcdir)/internal/object.h rational.$(OBJEXT): $(top_srcdir)/internal/rational.h rational.$(OBJEXT): $(top_srcdir)/internal/serial.h rational.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +rational.$(OBJEXT): $(top_srcdir)/internal/variable.h rational.$(OBJEXT): $(top_srcdir)/internal/vm.h rational.$(OBJEXT): $(top_srcdir)/internal/warnings.h rational.$(OBJEXT): {$(VPATH)}assert.h @@ -11984,6 +12057,7 @@ rational.$(OBJEXT): {$(VPATH)}backward/2/long_long.h rational.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h rational.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h rational.$(OBJEXT): {$(VPATH)}config.h +rational.$(OBJEXT): {$(VPATH)}constant.h rational.$(OBJEXT): {$(VPATH)}defines.h rational.$(OBJEXT): {$(VPATH)}id.h rational.$(OBJEXT): {$(VPATH)}id_table.h @@ -12132,6 +12206,7 @@ rational.$(OBJEXT): {$(VPATH)}internal/xmalloc.h rational.$(OBJEXT): {$(VPATH)}missing.h rational.$(OBJEXT): {$(VPATH)}rational.c rational.$(OBJEXT): {$(VPATH)}ruby_assert.h +rational.$(OBJEXT): {$(VPATH)}shape.h rational.$(OBJEXT): {$(VPATH)}st.h rational.$(OBJEXT): {$(VPATH)}subst.h re.$(OBJEXT): $(hdrdir)/ruby.h @@ -12329,6 +12404,7 @@ re.$(OBJEXT): {$(VPATH)}re.h re.$(OBJEXT): {$(VPATH)}regenc.h re.$(OBJEXT): {$(VPATH)}regex.h re.$(OBJEXT): {$(VPATH)}regint.h +re.$(OBJEXT): {$(VPATH)}shape.h re.$(OBJEXT): {$(VPATH)}st.h re.$(OBJEXT): {$(VPATH)}subst.h re.$(OBJEXT): {$(VPATH)}util.h @@ -13526,6 +13602,7 @@ ruby.$(OBJEXT): {$(VPATH)}oniguruma.h ruby.$(OBJEXT): {$(VPATH)}ruby.c ruby.$(OBJEXT): {$(VPATH)}ruby_assert.h ruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h +ruby.$(OBJEXT): {$(VPATH)}shape.h ruby.$(OBJEXT): {$(VPATH)}st.h ruby.$(OBJEXT): {$(VPATH)}subst.h ruby.$(OBJEXT): {$(VPATH)}thread.h @@ -13547,6 +13624,7 @@ scheduler.$(OBJEXT): $(top_srcdir)/internal/imemo.h scheduler.$(OBJEXT): $(top_srcdir)/internal/serial.h scheduler.$(OBJEXT): $(top_srcdir)/internal/static_assert.h scheduler.$(OBJEXT): $(top_srcdir)/internal/thread.h +scheduler.$(OBJEXT): $(top_srcdir)/internal/variable.h scheduler.$(OBJEXT): $(top_srcdir)/internal/vm.h scheduler.$(OBJEXT): $(top_srcdir)/internal/warnings.h scheduler.$(OBJEXT): {$(VPATH)}assert.h @@ -13561,10 +13639,12 @@ scheduler.$(OBJEXT): {$(VPATH)}backward/2/long_long.h scheduler.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h scheduler.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h scheduler.$(OBJEXT): {$(VPATH)}config.h +scheduler.$(OBJEXT): {$(VPATH)}constant.h scheduler.$(OBJEXT): {$(VPATH)}defines.h scheduler.$(OBJEXT): {$(VPATH)}encoding.h scheduler.$(OBJEXT): {$(VPATH)}fiber/scheduler.h scheduler.$(OBJEXT): {$(VPATH)}id.h +scheduler.$(OBJEXT): {$(VPATH)}id_table.h scheduler.$(OBJEXT): {$(VPATH)}intern.h scheduler.$(OBJEXT): {$(VPATH)}internal.h scheduler.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -13726,6 +13806,7 @@ scheduler.$(OBJEXT): {$(VPATH)}oniguruma.h scheduler.$(OBJEXT): {$(VPATH)}ruby_assert.h scheduler.$(OBJEXT): {$(VPATH)}ruby_atomic.h scheduler.$(OBJEXT): {$(VPATH)}scheduler.c +scheduler.$(OBJEXT): {$(VPATH)}shape.h scheduler.$(OBJEXT): {$(VPATH)}st.h scheduler.$(OBJEXT): {$(VPATH)}subst.h scheduler.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -13891,6 +13972,208 @@ setproctitle.$(OBJEXT): {$(VPATH)}setproctitle.c setproctitle.$(OBJEXT): {$(VPATH)}st.h setproctitle.$(OBJEXT): {$(VPATH)}subst.h setproctitle.$(OBJEXT): {$(VPATH)}util.h +shape.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +shape.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +shape.$(OBJEXT): $(CCAN_DIR)/list/list.h +shape.$(OBJEXT): $(CCAN_DIR)/str/str.h +shape.$(OBJEXT): $(hdrdir)/ruby/ruby.h +shape.$(OBJEXT): $(top_srcdir)/internal/array.h +shape.$(OBJEXT): $(top_srcdir)/internal/class.h +shape.$(OBJEXT): $(top_srcdir)/internal/compilers.h +shape.$(OBJEXT): $(top_srcdir)/internal/gc.h +shape.$(OBJEXT): $(top_srcdir)/internal/imemo.h +shape.$(OBJEXT): $(top_srcdir)/internal/serial.h +shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +shape.$(OBJEXT): $(top_srcdir)/internal/symbol.h +shape.$(OBJEXT): $(top_srcdir)/internal/variable.h +shape.$(OBJEXT): $(top_srcdir)/internal/vm.h +shape.$(OBJEXT): $(top_srcdir)/internal/warnings.h +shape.$(OBJEXT): {$(VPATH)}assert.h +shape.$(OBJEXT): {$(VPATH)}atomic.h +shape.$(OBJEXT): {$(VPATH)}backward/2/assume.h +shape.$(OBJEXT): {$(VPATH)}backward/2/attributes.h +shape.$(OBJEXT): {$(VPATH)}backward/2/bool.h +shape.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h +shape.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h +shape.$(OBJEXT): {$(VPATH)}backward/2/limits.h +shape.$(OBJEXT): {$(VPATH)}backward/2/long_long.h +shape.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h +shape.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h +shape.$(OBJEXT): {$(VPATH)}config.h +shape.$(OBJEXT): {$(VPATH)}constant.h +shape.$(OBJEXT): {$(VPATH)}debug_counter.h +shape.$(OBJEXT): {$(VPATH)}defines.h +shape.$(OBJEXT): {$(VPATH)}encoding.h +shape.$(OBJEXT): {$(VPATH)}id.h +shape.$(OBJEXT): {$(VPATH)}id_table.h +shape.$(OBJEXT): {$(VPATH)}intern.h +shape.$(OBJEXT): {$(VPATH)}internal.h +shape.$(OBJEXT): {$(VPATH)}internal/abi.h +shape.$(OBJEXT): {$(VPATH)}internal/anyargs.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h +shape.$(OBJEXT): {$(VPATH)}internal/assume.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/cold.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/const.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/error.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/format.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/pure.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/warning.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h +shape.$(OBJEXT): {$(VPATH)}internal/cast.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_since.h +shape.$(OBJEXT): {$(VPATH)}internal/config.h +shape.$(OBJEXT): {$(VPATH)}internal/constant_p.h +shape.$(OBJEXT): {$(VPATH)}internal/core.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rarray.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rclass.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rdata.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rfile.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rhash.h +shape.$(OBJEXT): {$(VPATH)}internal/core/robject.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rstring.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h +shape.$(OBJEXT): {$(VPATH)}internal/ctype.h +shape.$(OBJEXT): {$(VPATH)}internal/dllexport.h +shape.$(OBJEXT): {$(VPATH)}internal/dosish.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/re.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/string.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h +shape.$(OBJEXT): {$(VPATH)}internal/error.h +shape.$(OBJEXT): {$(VPATH)}internal/eval.h +shape.$(OBJEXT): {$(VPATH)}internal/event.h +shape.$(OBJEXT): {$(VPATH)}internal/fl_type.h +shape.$(OBJEXT): {$(VPATH)}internal/gc.h +shape.$(OBJEXT): {$(VPATH)}internal/glob.h +shape.$(OBJEXT): {$(VPATH)}internal/globals.h +shape.$(OBJEXT): {$(VPATH)}internal/has/attribute.h +shape.$(OBJEXT): {$(VPATH)}internal/has/builtin.h +shape.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h +shape.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h +shape.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h +shape.$(OBJEXT): {$(VPATH)}internal/has/extension.h +shape.$(OBJEXT): {$(VPATH)}internal/has/feature.h +shape.$(OBJEXT): {$(VPATH)}internal/has/warning.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/array.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/class.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/compar.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/complex.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/cont.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/dir.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/enum.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/error.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/eval.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/file.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/gc.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/hash.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/io.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/load.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/object.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/parse.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/proc.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/process.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/random.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/range.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/rational.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/re.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/select.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/signal.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/string.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/struct.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/thread.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/time.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/variable.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/vm.h +shape.$(OBJEXT): {$(VPATH)}internal/interpreter.h +shape.$(OBJEXT): {$(VPATH)}internal/iterator.h +shape.$(OBJEXT): {$(VPATH)}internal/memory.h +shape.$(OBJEXT): {$(VPATH)}internal/method.h +shape.$(OBJEXT): {$(VPATH)}internal/module.h +shape.$(OBJEXT): {$(VPATH)}internal/newobj.h +shape.$(OBJEXT): {$(VPATH)}internal/rgengc.h +shape.$(OBJEXT): {$(VPATH)}internal/scan_args.h +shape.$(OBJEXT): {$(VPATH)}internal/special_consts.h +shape.$(OBJEXT): {$(VPATH)}internal/static_assert.h +shape.$(OBJEXT): {$(VPATH)}internal/stdalign.h +shape.$(OBJEXT): {$(VPATH)}internal/stdbool.h +shape.$(OBJEXT): {$(VPATH)}internal/symbol.h +shape.$(OBJEXT): {$(VPATH)}internal/value.h +shape.$(OBJEXT): {$(VPATH)}internal/value_type.h +shape.$(OBJEXT): {$(VPATH)}internal/variable.h +shape.$(OBJEXT): {$(VPATH)}internal/warning_push.h +shape.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +shape.$(OBJEXT): {$(VPATH)}method.h +shape.$(OBJEXT): {$(VPATH)}missing.h +shape.$(OBJEXT): {$(VPATH)}node.h +shape.$(OBJEXT): {$(VPATH)}onigmo.h +shape.$(OBJEXT): {$(VPATH)}oniguruma.h +shape.$(OBJEXT): {$(VPATH)}ruby_assert.h +shape.$(OBJEXT): {$(VPATH)}ruby_atomic.h +shape.$(OBJEXT): {$(VPATH)}shape.c +shape.$(OBJEXT): {$(VPATH)}shape.h +shape.$(OBJEXT): {$(VPATH)}st.h +shape.$(OBJEXT): {$(VPATH)}subst.h +shape.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h +shape.$(OBJEXT): {$(VPATH)}thread_native.h +shape.$(OBJEXT): {$(VPATH)}vm_core.h +shape.$(OBJEXT): {$(VPATH)}vm_debug.h +shape.$(OBJEXT): {$(VPATH)}vm_opts.h +shape.$(OBJEXT): {$(VPATH)}vm_sync.h signal.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h signal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h signal.$(OBJEXT): $(CCAN_DIR)/list/list.h @@ -13907,6 +14190,7 @@ signal.$(OBJEXT): $(top_srcdir)/internal/signal.h signal.$(OBJEXT): $(top_srcdir)/internal/static_assert.h signal.$(OBJEXT): $(top_srcdir)/internal/string.h signal.$(OBJEXT): $(top_srcdir)/internal/thread.h +signal.$(OBJEXT): $(top_srcdir)/internal/variable.h signal.$(OBJEXT): $(top_srcdir)/internal/vm.h signal.$(OBJEXT): $(top_srcdir)/internal/warnings.h signal.$(OBJEXT): {$(VPATH)}assert.h @@ -13921,6 +14205,7 @@ signal.$(OBJEXT): {$(VPATH)}backward/2/long_long.h signal.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h signal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h signal.$(OBJEXT): {$(VPATH)}config.h +signal.$(OBJEXT): {$(VPATH)}constant.h signal.$(OBJEXT): {$(VPATH)}debug_counter.h signal.$(OBJEXT): {$(VPATH)}defines.h signal.$(OBJEXT): {$(VPATH)}encoding.h @@ -14087,6 +14372,7 @@ signal.$(OBJEXT): {$(VPATH)}ractor.h signal.$(OBJEXT): {$(VPATH)}ractor_core.h signal.$(OBJEXT): {$(VPATH)}ruby_assert.h signal.$(OBJEXT): {$(VPATH)}ruby_atomic.h +signal.$(OBJEXT): {$(VPATH)}shape.h signal.$(OBJEXT): {$(VPATH)}signal.c signal.$(OBJEXT): {$(VPATH)}st.h signal.$(OBJEXT): {$(VPATH)}subst.h @@ -14111,6 +14397,7 @@ sprintf.$(OBJEXT): $(top_srcdir)/internal/serial.h sprintf.$(OBJEXT): $(top_srcdir)/internal/static_assert.h sprintf.$(OBJEXT): $(top_srcdir)/internal/string.h sprintf.$(OBJEXT): $(top_srcdir)/internal/symbol.h +sprintf.$(OBJEXT): $(top_srcdir)/internal/variable.h sprintf.$(OBJEXT): $(top_srcdir)/internal/vm.h sprintf.$(OBJEXT): $(top_srcdir)/internal/warnings.h sprintf.$(OBJEXT): {$(VPATH)}assert.h @@ -14124,6 +14411,7 @@ sprintf.$(OBJEXT): {$(VPATH)}backward/2/long_long.h sprintf.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h sprintf.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h sprintf.$(OBJEXT): {$(VPATH)}config.h +sprintf.$(OBJEXT): {$(VPATH)}constant.h sprintf.$(OBJEXT): {$(VPATH)}defines.h sprintf.$(OBJEXT): {$(VPATH)}encoding.h sprintf.$(OBJEXT): {$(VPATH)}id.h @@ -14285,6 +14573,7 @@ sprintf.$(OBJEXT): {$(VPATH)}onigmo.h sprintf.$(OBJEXT): {$(VPATH)}oniguruma.h sprintf.$(OBJEXT): {$(VPATH)}re.h sprintf.$(OBJEXT): {$(VPATH)}regex.h +sprintf.$(OBJEXT): {$(VPATH)}shape.h sprintf.$(OBJEXT): {$(VPATH)}sprintf.c sprintf.$(OBJEXT): {$(VPATH)}st.h sprintf.$(OBJEXT): {$(VPATH)}subst.h @@ -14653,6 +14942,7 @@ string.$(OBJEXT): $(top_srcdir)/internal/serial.h string.$(OBJEXT): $(top_srcdir)/internal/static_assert.h string.$(OBJEXT): $(top_srcdir)/internal/string.h string.$(OBJEXT): $(top_srcdir)/internal/transcode.h +string.$(OBJEXT): $(top_srcdir)/internal/variable.h string.$(OBJEXT): $(top_srcdir)/internal/vm.h string.$(OBJEXT): $(top_srcdir)/internal/warnings.h string.$(OBJEXT): {$(VPATH)}assert.h @@ -14667,6 +14957,7 @@ string.$(OBJEXT): {$(VPATH)}backward/2/long_long.h string.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h string.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h string.$(OBJEXT): {$(VPATH)}config.h +string.$(OBJEXT): {$(VPATH)}constant.h string.$(OBJEXT): {$(VPATH)}debug_counter.h string.$(OBJEXT): {$(VPATH)}defines.h string.$(OBJEXT): {$(VPATH)}encindex.h @@ -14834,6 +15125,7 @@ string.$(OBJEXT): {$(VPATH)}probes.h string.$(OBJEXT): {$(VPATH)}re.h string.$(OBJEXT): {$(VPATH)}regex.h string.$(OBJEXT): {$(VPATH)}ruby_assert.h +string.$(OBJEXT): {$(VPATH)}shape.h string.$(OBJEXT): {$(VPATH)}st.h string.$(OBJEXT): {$(VPATH)}string.c string.$(OBJEXT): {$(VPATH)}subst.h @@ -14890,6 +15182,7 @@ struct.$(OBJEXT): $(top_srcdir)/internal/static_assert.h struct.$(OBJEXT): $(top_srcdir)/internal/string.h struct.$(OBJEXT): $(top_srcdir)/internal/struct.h struct.$(OBJEXT): $(top_srcdir)/internal/symbol.h +struct.$(OBJEXT): $(top_srcdir)/internal/variable.h struct.$(OBJEXT): $(top_srcdir)/internal/vm.h struct.$(OBJEXT): $(top_srcdir)/internal/warnings.h struct.$(OBJEXT): {$(VPATH)}assert.h @@ -14905,6 +15198,7 @@ struct.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h struct.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h struct.$(OBJEXT): {$(VPATH)}builtin.h struct.$(OBJEXT): {$(VPATH)}config.h +struct.$(OBJEXT): {$(VPATH)}constant.h struct.$(OBJEXT): {$(VPATH)}defines.h struct.$(OBJEXT): {$(VPATH)}encoding.h struct.$(OBJEXT): {$(VPATH)}id.h @@ -15067,6 +15361,7 @@ struct.$(OBJEXT): {$(VPATH)}onigmo.h struct.$(OBJEXT): {$(VPATH)}oniguruma.h struct.$(OBJEXT): {$(VPATH)}ruby_assert.h struct.$(OBJEXT): {$(VPATH)}ruby_atomic.h +struct.$(OBJEXT): {$(VPATH)}shape.h struct.$(OBJEXT): {$(VPATH)}st.h struct.$(OBJEXT): {$(VPATH)}struct.c struct.$(OBJEXT): {$(VPATH)}subst.h @@ -15086,6 +15381,7 @@ symbol.$(OBJEXT): $(top_srcdir)/internal/serial.h symbol.$(OBJEXT): $(top_srcdir)/internal/static_assert.h symbol.$(OBJEXT): $(top_srcdir)/internal/string.h symbol.$(OBJEXT): $(top_srcdir)/internal/symbol.h +symbol.$(OBJEXT): $(top_srcdir)/internal/variable.h symbol.$(OBJEXT): $(top_srcdir)/internal/vm.h symbol.$(OBJEXT): $(top_srcdir)/internal/warnings.h symbol.$(OBJEXT): {$(VPATH)}assert.h @@ -15099,6 +15395,7 @@ symbol.$(OBJEXT): {$(VPATH)}backward/2/long_long.h symbol.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h symbol.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h symbol.$(OBJEXT): {$(VPATH)}config.h +symbol.$(OBJEXT): {$(VPATH)}constant.h symbol.$(OBJEXT): {$(VPATH)}debug_counter.h symbol.$(OBJEXT): {$(VPATH)}defines.h symbol.$(OBJEXT): {$(VPATH)}encoding.h @@ -15264,6 +15561,7 @@ symbol.$(OBJEXT): {$(VPATH)}oniguruma.h symbol.$(OBJEXT): {$(VPATH)}probes.dmyh symbol.$(OBJEXT): {$(VPATH)}probes.h symbol.$(OBJEXT): {$(VPATH)}ruby_assert.h +symbol.$(OBJEXT): {$(VPATH)}shape.h symbol.$(OBJEXT): {$(VPATH)}st.h symbol.$(OBJEXT): {$(VPATH)}subst.h symbol.$(OBJEXT): {$(VPATH)}symbol.c @@ -15294,6 +15592,7 @@ thread.$(OBJEXT): $(top_srcdir)/internal/static_assert.h thread.$(OBJEXT): $(top_srcdir)/internal/string.h thread.$(OBJEXT): $(top_srcdir)/internal/thread.h thread.$(OBJEXT): $(top_srcdir)/internal/time.h +thread.$(OBJEXT): $(top_srcdir)/internal/variable.h thread.$(OBJEXT): $(top_srcdir)/internal/vm.h thread.$(OBJEXT): $(top_srcdir)/internal/warnings.h thread.$(OBJEXT): {$(VPATH)}assert.h @@ -15309,6 +15608,7 @@ thread.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h thread.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h thread.$(OBJEXT): {$(VPATH)}builtin.h thread.$(OBJEXT): {$(VPATH)}config.h +thread.$(OBJEXT): {$(VPATH)}constant.h thread.$(OBJEXT): {$(VPATH)}debug.h thread.$(OBJEXT): {$(VPATH)}debug_counter.h thread.$(OBJEXT): {$(VPATH)}defines.h @@ -15482,6 +15782,7 @@ thread.$(OBJEXT): {$(VPATH)}ractor.h thread.$(OBJEXT): {$(VPATH)}ractor_core.h thread.$(OBJEXT): {$(VPATH)}ruby_assert.h thread.$(OBJEXT): {$(VPATH)}ruby_atomic.h +thread.$(OBJEXT): {$(VPATH)}shape.h thread.$(OBJEXT): {$(VPATH)}st.h thread.$(OBJEXT): {$(VPATH)}subst.h thread.$(OBJEXT): {$(VPATH)}thread.c @@ -15702,6 +16003,7 @@ transcode.$(OBJEXT): $(top_srcdir)/internal/serial.h transcode.$(OBJEXT): $(top_srcdir)/internal/static_assert.h transcode.$(OBJEXT): $(top_srcdir)/internal/string.h transcode.$(OBJEXT): $(top_srcdir)/internal/transcode.h +transcode.$(OBJEXT): $(top_srcdir)/internal/variable.h transcode.$(OBJEXT): $(top_srcdir)/internal/warnings.h transcode.$(OBJEXT): {$(VPATH)}assert.h transcode.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -15714,6 +16016,7 @@ transcode.$(OBJEXT): {$(VPATH)}backward/2/long_long.h transcode.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h transcode.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h transcode.$(OBJEXT): {$(VPATH)}config.h +transcode.$(OBJEXT): {$(VPATH)}constant.h transcode.$(OBJEXT): {$(VPATH)}defines.h transcode.$(OBJEXT): {$(VPATH)}encoding.h transcode.$(OBJEXT): {$(VPATH)}id.h @@ -15872,6 +16175,7 @@ transcode.$(OBJEXT): {$(VPATH)}internal/xmalloc.h transcode.$(OBJEXT): {$(VPATH)}missing.h transcode.$(OBJEXT): {$(VPATH)}onigmo.h transcode.$(OBJEXT): {$(VPATH)}oniguruma.h +transcode.$(OBJEXT): {$(VPATH)}shape.h transcode.$(OBJEXT): {$(VPATH)}st.h transcode.$(OBJEXT): {$(VPATH)}subst.h transcode.$(OBJEXT): {$(VPATH)}transcode.c @@ -16421,6 +16725,7 @@ variable.$(OBJEXT): {$(VPATH)}ractor.h variable.$(OBJEXT): {$(VPATH)}ractor_core.h variable.$(OBJEXT): {$(VPATH)}ruby_assert.h variable.$(OBJEXT): {$(VPATH)}ruby_atomic.h +variable.$(OBJEXT): {$(VPATH)}shape.h variable.$(OBJEXT): {$(VPATH)}st.h variable.$(OBJEXT): {$(VPATH)}subst.h variable.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -16446,6 +16751,7 @@ version.$(OBJEXT): $(top_srcdir)/internal/gc.h version.$(OBJEXT): $(top_srcdir)/internal/imemo.h version.$(OBJEXT): $(top_srcdir)/internal/serial.h version.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +version.$(OBJEXT): $(top_srcdir)/internal/variable.h version.$(OBJEXT): $(top_srcdir)/internal/vm.h version.$(OBJEXT): $(top_srcdir)/internal/warnings.h version.$(OBJEXT): $(top_srcdir)/revision.h @@ -16462,9 +16768,11 @@ version.$(OBJEXT): {$(VPATH)}backward/2/long_long.h version.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h version.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h version.$(OBJEXT): {$(VPATH)}config.h +version.$(OBJEXT): {$(VPATH)}constant.h version.$(OBJEXT): {$(VPATH)}debug_counter.h version.$(OBJEXT): {$(VPATH)}defines.h version.$(OBJEXT): {$(VPATH)}id.h +version.$(OBJEXT): {$(VPATH)}id_table.h version.$(OBJEXT): {$(VPATH)}intern.h version.$(OBJEXT): {$(VPATH)}internal.h version.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -16613,6 +16921,7 @@ version.$(OBJEXT): {$(VPATH)}mjit.h version.$(OBJEXT): {$(VPATH)}node.h version.$(OBJEXT): {$(VPATH)}ruby_assert.h version.$(OBJEXT): {$(VPATH)}ruby_atomic.h +version.$(OBJEXT): {$(VPATH)}shape.h version.$(OBJEXT): {$(VPATH)}st.h version.$(OBJEXT): {$(VPATH)}subst.h version.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -16848,6 +17157,7 @@ vm.$(OBJEXT): {$(VPATH)}ractor.h vm.$(OBJEXT): {$(VPATH)}ractor_core.h vm.$(OBJEXT): {$(VPATH)}ruby_assert.h vm.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm.$(OBJEXT): {$(VPATH)}shape.h vm.$(OBJEXT): {$(VPATH)}st.h vm.$(OBJEXT): {$(VPATH)}subst.h vm.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -16884,6 +17194,7 @@ vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/imemo.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/serial.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/string.h +vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/warnings.h vm_backtrace.$(OBJEXT): {$(VPATH)}assert.h @@ -16898,11 +17209,13 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/long_long.h vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h vm_backtrace.$(OBJEXT): {$(VPATH)}config.h +vm_backtrace.$(OBJEXT): {$(VPATH)}constant.h vm_backtrace.$(OBJEXT): {$(VPATH)}debug.h vm_backtrace.$(OBJEXT): {$(VPATH)}defines.h vm_backtrace.$(OBJEXT): {$(VPATH)}encoding.h vm_backtrace.$(OBJEXT): {$(VPATH)}eval_intern.h vm_backtrace.$(OBJEXT): {$(VPATH)}id.h +vm_backtrace.$(OBJEXT): {$(VPATH)}id_table.h vm_backtrace.$(OBJEXT): {$(VPATH)}intern.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -17062,6 +17375,7 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}onigmo.h vm_backtrace.$(OBJEXT): {$(VPATH)}oniguruma.h vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm_backtrace.$(OBJEXT): {$(VPATH)}shape.h vm_backtrace.$(OBJEXT): {$(VPATH)}st.h vm_backtrace.$(OBJEXT): {$(VPATH)}subst.h vm_backtrace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -17252,6 +17566,7 @@ vm_dump.$(OBJEXT): {$(VPATH)}ractor.h vm_dump.$(OBJEXT): {$(VPATH)}ractor_core.h vm_dump.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_dump.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm_dump.$(OBJEXT): {$(VPATH)}shape.h vm_dump.$(OBJEXT): {$(VPATH)}st.h vm_dump.$(OBJEXT): {$(VPATH)}subst.h vm_dump.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -17271,6 +17586,7 @@ vm_sync.$(OBJEXT): $(top_srcdir)/internal/gc.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/imemo.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/serial.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +vm_sync.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/warnings.h vm_sync.$(OBJEXT): {$(VPATH)}assert.h @@ -17285,6 +17601,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}backward/2/long_long.h vm_sync.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h vm_sync.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h vm_sync.$(OBJEXT): {$(VPATH)}config.h +vm_sync.$(OBJEXT): {$(VPATH)}constant.h vm_sync.$(OBJEXT): {$(VPATH)}debug_counter.h vm_sync.$(OBJEXT): {$(VPATH)}defines.h vm_sync.$(OBJEXT): {$(VPATH)}gc.h @@ -17439,6 +17756,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}ractor.h vm_sync.$(OBJEXT): {$(VPATH)}ractor_core.h vm_sync.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_sync.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm_sync.$(OBJEXT): {$(VPATH)}shape.h vm_sync.$(OBJEXT): {$(VPATH)}st.h vm_sync.$(OBJEXT): {$(VPATH)}subst.h vm_sync.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -17462,6 +17780,7 @@ vm_trace.$(OBJEXT): $(top_srcdir)/internal/imemo.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/serial.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/symbol.h +vm_trace.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/warnings.h vm_trace.$(OBJEXT): {$(VPATH)}assert.h @@ -17477,12 +17796,14 @@ vm_trace.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h vm_trace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h vm_trace.$(OBJEXT): {$(VPATH)}builtin.h vm_trace.$(OBJEXT): {$(VPATH)}config.h +vm_trace.$(OBJEXT): {$(VPATH)}constant.h vm_trace.$(OBJEXT): {$(VPATH)}debug.h vm_trace.$(OBJEXT): {$(VPATH)}debug_counter.h vm_trace.$(OBJEXT): {$(VPATH)}defines.h vm_trace.$(OBJEXT): {$(VPATH)}encoding.h vm_trace.$(OBJEXT): {$(VPATH)}eval_intern.h vm_trace.$(OBJEXT): {$(VPATH)}id.h +vm_trace.$(OBJEXT): {$(VPATH)}id_table.h vm_trace.$(OBJEXT): {$(VPATH)}intern.h vm_trace.$(OBJEXT): {$(VPATH)}internal.h vm_trace.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -17644,6 +17965,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}oniguruma.h vm_trace.$(OBJEXT): {$(VPATH)}ractor.h vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_trace.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm_trace.$(OBJEXT): {$(VPATH)}shape.h vm_trace.$(OBJEXT): {$(VPATH)}st.h vm_trace.$(OBJEXT): {$(VPATH)}subst.h vm_trace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h diff --git a/compile.c b/compile.c index a5da919c0ae4e9..9051ecfcd69ea1 100644 --- a/compile.c +++ b/compile.c @@ -2058,20 +2058,7 @@ cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr) static inline VALUE get_ivar_ic_value(rb_iseq_t *iseq,ID id) { - VALUE val; - struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table; - if (tbl) { - if (rb_id_table_lookup(tbl,id,&val)) { - return val; - } - } - else { - tbl = rb_id_table_create(1); - ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl; - } - val = INT2FIX(ISEQ_BODY(iseq)->ivc_size++); - rb_id_table_insert(tbl,id,val); - return val; + return INT2FIX(ISEQ_BODY(iseq)->ivc_size++); } static inline VALUE @@ -2472,9 +2459,13 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) generated_iseq[code_index + 1 + j] = (VALUE)ic; } break; + case TS_IVC: /* inline ivar cache */ + { + unsigned int ic_index = FIX2UINT(operands[j]); + vm_ic_attr_index_initialize(((IVC)&body->is_entries[ic_index]), INVALID_SHAPE_ID); + } case TS_ISE: /* inline storage entry: `once` insn */ case TS_ICVARC: /* inline cvar cache */ - case TS_IVC: /* inline ivar cache */ { unsigned int ic_index = FIX2UINT(operands[j]); IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache; @@ -11514,6 +11505,11 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op; code[code_index] = (VALUE)ic; + + if (operand_type == TS_IVC) { + vm_ic_attr_index_initialize(((IVC)code[code_index]), INVALID_SHAPE_ID); + } + } break; case TS_CALLDATA: diff --git a/debug_counter.h b/debug_counter.h index c6f4176e9752de..f3a799913b5291 100644 --- a/debug_counter.h +++ b/debug_counter.h @@ -130,7 +130,6 @@ RB_DEBUG_COUNTER(frame_C2R) /* instance variable counts * * * ivar_get_ic_hit/miss: ivar_get inline cache (ic) hit/miss counts (VM insn) - * * ivar_get_ic_miss_serial: ivar_get ic miss reason by serial (VM insn) * * ivar_get_ic_miss_unset: ... by unset (VM insn) * * ivar_get_ic_miss_noobject: ... by "not T_OBJECT" (VM insn) * * ivar_set_...: same counts with ivar_set (VM insn) @@ -140,17 +139,17 @@ RB_DEBUG_COUNTER(frame_C2R) */ RB_DEBUG_COUNTER(ivar_get_ic_hit) RB_DEBUG_COUNTER(ivar_get_ic_miss) -RB_DEBUG_COUNTER(ivar_get_ic_miss_serial) -RB_DEBUG_COUNTER(ivar_get_ic_miss_unset) RB_DEBUG_COUNTER(ivar_get_ic_miss_noobject) RB_DEBUG_COUNTER(ivar_set_ic_hit) RB_DEBUG_COUNTER(ivar_set_ic_miss) -RB_DEBUG_COUNTER(ivar_set_ic_miss_serial) -RB_DEBUG_COUNTER(ivar_set_ic_miss_unset) RB_DEBUG_COUNTER(ivar_set_ic_miss_iv_hit) RB_DEBUG_COUNTER(ivar_set_ic_miss_noobject) RB_DEBUG_COUNTER(ivar_get_base) RB_DEBUG_COUNTER(ivar_set_base) +RB_DEBUG_COUNTER(ivar_get_ic_miss_set) +RB_DEBUG_COUNTER(ivar_get_cc_miss_set) +RB_DEBUG_COUNTER(ivar_get_ic_miss_unset) +RB_DEBUG_COUNTER(ivar_get_cc_miss_unset) /* local variable counts * diff --git a/ext/coverage/depend b/ext/coverage/depend index 57d368d3f53660..719c6c6e791551 100644 --- a/ext/coverage/depend +++ b/ext/coverage/depend @@ -165,7 +165,9 @@ coverage.o: $(top_srcdir)/ccan/check_type/check_type.h coverage.o: $(top_srcdir)/ccan/container_of/container_of.h coverage.o: $(top_srcdir)/ccan/list/list.h coverage.o: $(top_srcdir)/ccan/str/str.h +coverage.o: $(top_srcdir)/constant.h coverage.o: $(top_srcdir)/gc.h +coverage.o: $(top_srcdir)/id_table.h coverage.o: $(top_srcdir)/internal.h coverage.o: $(top_srcdir)/internal/array.h coverage.o: $(top_srcdir)/internal/compilers.h @@ -176,12 +178,14 @@ coverage.o: $(top_srcdir)/internal/sanitizers.h coverage.o: $(top_srcdir)/internal/serial.h coverage.o: $(top_srcdir)/internal/static_assert.h coverage.o: $(top_srcdir)/internal/thread.h +coverage.o: $(top_srcdir)/internal/variable.h coverage.o: $(top_srcdir)/internal/vm.h coverage.o: $(top_srcdir)/internal/warnings.h coverage.o: $(top_srcdir)/method.h coverage.o: $(top_srcdir)/node.h coverage.o: $(top_srcdir)/ruby_assert.h coverage.o: $(top_srcdir)/ruby_atomic.h +coverage.o: $(top_srcdir)/shape.h coverage.o: $(top_srcdir)/thread_pthread.h coverage.o: $(top_srcdir)/vm_core.h coverage.o: $(top_srcdir)/vm_opts.h diff --git a/ext/objspace/depend b/ext/objspace/depend index c4da8031cc7087..88c66a232b7f57 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -350,6 +350,7 @@ objspace.o: $(top_srcdir)/internal/serial.h objspace.o: $(top_srcdir)/internal/static_assert.h objspace.o: $(top_srcdir)/internal/warnings.h objspace.o: $(top_srcdir)/node.h +objspace.o: $(top_srcdir)/shape.h objspace.o: $(top_srcdir)/symbol.h objspace.o: objspace.c objspace.o: {$(VPATH)}id.h @@ -533,7 +534,9 @@ objspace_dump.o: $(top_srcdir)/ccan/check_type/check_type.h objspace_dump.o: $(top_srcdir)/ccan/container_of/container_of.h objspace_dump.o: $(top_srcdir)/ccan/list/list.h objspace_dump.o: $(top_srcdir)/ccan/str/str.h +objspace_dump.o: $(top_srcdir)/constant.h objspace_dump.o: $(top_srcdir)/gc.h +objspace_dump.o: $(top_srcdir)/id_table.h objspace_dump.o: $(top_srcdir)/internal.h objspace_dump.o: $(top_srcdir)/internal/array.h objspace_dump.o: $(top_srcdir)/internal/compilers.h @@ -544,12 +547,14 @@ objspace_dump.o: $(top_srcdir)/internal/sanitizers.h objspace_dump.o: $(top_srcdir)/internal/serial.h objspace_dump.o: $(top_srcdir)/internal/static_assert.h objspace_dump.o: $(top_srcdir)/internal/string.h +objspace_dump.o: $(top_srcdir)/internal/variable.h objspace_dump.o: $(top_srcdir)/internal/vm.h objspace_dump.o: $(top_srcdir)/internal/warnings.h objspace_dump.o: $(top_srcdir)/method.h objspace_dump.o: $(top_srcdir)/node.h objspace_dump.o: $(top_srcdir)/ruby_assert.h objspace_dump.o: $(top_srcdir)/ruby_atomic.h +objspace_dump.o: $(top_srcdir)/shape.h objspace_dump.o: $(top_srcdir)/thread_pthread.h objspace_dump.o: $(top_srcdir)/vm_core.h objspace_dump.o: $(top_srcdir)/vm_opts.h diff --git a/gc.c b/gc.c index 916584ed350d99..a5e37b72e23c54 100644 --- a/gc.c +++ b/gc.c @@ -2895,8 +2895,7 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) GC_ASSERT((flags & RUBY_T_MASK) == T_OBJECT); GC_ASSERT(flags & ROBJECT_EMBED); - st_table *index_tbl = RCLASS_IV_INDEX_TBL(klass); - uint32_t index_tbl_num_entries = index_tbl == NULL ? 0 : (uint32_t)index_tbl->num_entries; + uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count; size_t size; bool embed = true; @@ -2931,7 +2930,7 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) #endif } else { - rb_init_iv_list(obj); + rb_ensure_iv_list_size(obj, 0, index_tbl_num_entries); } return obj; @@ -3206,20 +3205,6 @@ rb_free_const_table(struct rb_id_table *tbl) rb_id_table_free(tbl); } -static int -free_iv_index_tbl_free_i(st_data_t key, st_data_t value, st_data_t data) -{ - xfree((void *)value); - return ST_CONTINUE; -} - -static void -iv_index_tbl_free(struct st_table *tbl) -{ - st_foreach(tbl, free_iv_index_tbl_free_i, 0); - st_free_table(tbl); -} - // alive: if false, target pointers can be freed already. // To check it, we need objspace parameter. static void @@ -3435,6 +3420,16 @@ obj_free(rb_objspace_t *objspace, VALUE obj) RB_DEBUG_COUNTER_INC(obj_obj_transient); } else { + rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); + if (shape) { + VALUE klass = RBASIC_CLASS(obj); + + // Increment max_iv_count if applicable, used to determine size pool allocation + uint32_t num_of_ivs = shape->iv_count; + if (RCLASS_EXT(klass)->max_iv_count < num_of_ivs) { + RCLASS_EXT(klass)->max_iv_count = num_of_ivs; + } + } xfree(RANY(obj)->as.object.as.heap.ivptr); RB_DEBUG_COUNTER_INC(obj_obj_ptr); } @@ -3449,9 +3444,6 @@ obj_free(rb_objspace_t *objspace, VALUE obj) if (RCLASS_CONST_TBL(obj)) { rb_free_const_table(RCLASS_CONST_TBL(obj)); } - if (RCLASS_IV_INDEX_TBL(obj)) { - iv_index_tbl_free(RCLASS_IV_INDEX_TBL(obj)); - } if (RCLASS_CVC_TBL(obj)) { rb_id_table_foreach_values(RCLASS_CVC_TBL(obj), cvar_table_free_i, NULL); rb_id_table_free(RCLASS_CVC_TBL(obj)); @@ -4873,10 +4865,6 @@ obj_memsize_of(VALUE obj, int use_all_types) if (RCLASS_CVC_TBL(obj)) { size += rb_id_table_memsize(RCLASS_CVC_TBL(obj)); } - if (RCLASS_IV_INDEX_TBL(obj)) { - // TODO: more correct value - size += st_memsize(RCLASS_IV_INDEX_TBL(obj)); - } if (RCLASS_EXT(obj)->iv_tbl) { size += st_memsize(RCLASS_EXT(obj)->iv_tbl); } @@ -10408,15 +10396,6 @@ update_subclass_entries(rb_objspace_t *objspace, rb_subclass_entry_t *entry) } } -static int -update_iv_index_tbl_i(st_data_t key, st_data_t value, st_data_t arg) -{ - rb_objspace_t *objspace = (rb_objspace_t *)arg; - struct rb_iv_index_tbl_entry *ent = (struct rb_iv_index_tbl_entry *)value; - UPDATE_IF_MOVED(objspace, ent->class_value); - return ST_CONTINUE; -} - static void update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext) { @@ -10424,11 +10403,6 @@ update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext) UPDATE_IF_MOVED(objspace, ext->includer); UPDATE_IF_MOVED(objspace, ext->refined_class); update_subclass_entries(objspace, ext->subclasses); - - // ext->iv_index_tbl - if (ext->iv_index_tbl) { - st_foreach(ext->iv_index_tbl, update_iv_index_tbl_i, (st_data_t)objspace); - } } static void diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h index 7823061d8ffbf7..bec0b45fd4e566 100644 --- a/include/ruby/internal/core/robject.h +++ b/include/ruby/internal/core/robject.h @@ -46,7 +46,6 @@ #define ROBJECT_EMBED ROBJECT_EMBED #define ROBJECT_NUMIV ROBJECT_NUMIV #define ROBJECT_IVPTR ROBJECT_IVPTR -#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL /** @endcond */ /** @@ -132,7 +131,7 @@ struct RObject { * * This is a shortcut for `RCLASS_IV_INDEX_TBL(rb_obj_class(obj))`. */ - struct st_table *iv_index_tbl; + struct rb_id_table *iv_index_tbl; } heap; #if USE_RVARGC diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h index c51bd2e9d946f2..7383426b2358a2 100644 --- a/include/ruby/internal/fl_type.h +++ b/include/ruby/internal/fl_type.h @@ -941,21 +941,8 @@ RB_OBJ_FREEZE_RAW(VALUE obj) RB_FL_SET_RAW(obj, RUBY_FL_FREEZE); } -/** - * Prevents further modifications to the given object. ::rb_eFrozenError shall - * be raised if modification is attempted. - * - * @param[out] x Object in question. - */ -static inline void -rb_obj_freeze_inline(VALUE x) -{ - if (RB_FL_ABLE(x)) { - RB_OBJ_FREEZE_RAW(x); - if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) { - rb_freeze_singleton_class(x); - } - } -} +RUBY_SYMBOL_EXPORT_BEGIN +void rb_obj_freeze_inline(VALUE obj); +RUBY_SYMBOL_EXPORT_END #endif /* RBIMPL_FL_TYPE_H */ diff --git a/inits.c b/inits.c index 9decba3c11006a..d1204c1324f73b 100644 --- a/inits.c +++ b/inits.c @@ -77,6 +77,7 @@ rb_call_inits(void) CALL(vm_stack_canary); CALL(ast); CALL(gc_stress); + CALL(shape); // enable builtin loading CALL(builtin); diff --git a/internal.h b/internal.h index 0740ae99e5e3d3..695c9cfb7e76f2 100644 --- a/internal.h +++ b/internal.h @@ -48,9 +48,6 @@ #undef RHASH_TBL #undef RHASH_EMPTY_P -/* internal/object.h */ -#undef ROBJECT_IV_INDEX_TBL - /* internal/struct.h */ #undef RSTRUCT_LEN #undef RSTRUCT_PTR diff --git a/internal/class.h b/internal/class.h index ae680564a6c317..4a3e09ddc7156c 100644 --- a/internal/class.h +++ b/internal/class.h @@ -14,6 +14,7 @@ #include "ruby/internal/stdbool.h" /* for bool */ #include "ruby/intern.h" /* for rb_alloc_func_t */ #include "ruby/ruby.h" /* for struct RBasic */ +#include "shape.h" #ifdef RCLASS_SUPER # undef RCLASS_SUPER @@ -27,8 +28,8 @@ struct rb_subclass_entry { struct rb_iv_index_tbl_entry { uint32_t index; - rb_serial_t class_serial; - VALUE class_value; + shape_id_t source_shape_id; + shape_id_t dest_shape_id; }; struct rb_cvar_class_tbl_entry { @@ -38,7 +39,6 @@ struct rb_cvar_class_tbl_entry { }; struct rb_classext_struct { - struct st_table *iv_index_tbl; // ID -> struct rb_iv_index_tbl_entry struct st_table *iv_tbl; #if SIZEOF_SERIAL_T == SIZEOF_VALUE /* otherwise m_tbl is in struct RClass */ struct rb_id_table *m_tbl; @@ -64,6 +64,10 @@ struct rb_classext_struct { const VALUE refined_class; rb_alloc_func_t allocator; const VALUE includer; + uint32_t max_iv_count; +#if !SHAPE_IN_BASIC_FLAGS + shape_id_t shape_id; +#endif }; struct RClass { @@ -102,7 +106,6 @@ typedef struct rb_classext_struct rb_classext_t; #define RCLASS_CALLABLE_M_TBL(c) (RCLASS_EXT(c)->callable_m_tbl) #define RCLASS_CC_TBL(c) (RCLASS_EXT(c)->cc_tbl) #define RCLASS_CVC_TBL(c) (RCLASS_EXT(c)->cvc_tbl) -#define RCLASS_IV_INDEX_TBL(c) (RCLASS_EXT(c)->iv_index_tbl) #define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin_) #define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class) #if SIZEOF_SERIAL_T == SIZEOF_VALUE diff --git a/internal/object.h b/internal/object.h index 88f3a44bc6f866..7b54e13dd2ae32 100644 --- a/internal/object.h +++ b/internal/object.h @@ -9,11 +9,6 @@ * @brief Internal header for Object. */ #include "ruby/ruby.h" /* for VALUE */ -#include "internal/class.h" /* for RCLASS_IV_INDEX_TBL */ - -#ifdef ROBJECT_IV_INDEX_TBL -# undef ROBJECT_IV_INDEX_TBL -#endif /* object.c */ VALUE rb_class_search_ancestor(VALUE klass, VALUE super); @@ -26,7 +21,6 @@ int rb_bool_expected(VALUE, const char *, int raise); static inline void RBASIC_CLEAR_CLASS(VALUE obj); static inline void RBASIC_SET_CLASS_RAW(VALUE obj, VALUE klass); static inline void RBASIC_SET_CLASS(VALUE obj, VALUE klass); -static inline struct st_table *ROBJECT_IV_INDEX_TBL_inline(VALUE obj); RUBY_SYMBOL_EXPORT_BEGIN /* object.c (export) */ @@ -64,20 +58,4 @@ RBASIC_SET_CLASS(VALUE obj, VALUE klass) RBASIC_SET_CLASS_RAW(obj, klass); RB_OBJ_WRITTEN(obj, oldv, klass); } - -RBIMPL_ATTR_PURE() -static inline struct st_table * -ROBJECT_IV_INDEX_TBL_inline(VALUE obj) -{ - if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) { - VALUE klass = rb_obj_class(obj); - return RCLASS_IV_INDEX_TBL(klass); - } - else { - const struct RObject *const ptr = ROBJECT(obj); - return ptr->as.heap.iv_index_tbl; - } -} -#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL_inline - #endif /* INTERNAL_OBJECT_H */ diff --git a/internal/variable.h b/internal/variable.h index 1a19e8964b9f8e..47037a33923265 100644 --- a/internal/variable.h +++ b/internal/variable.h @@ -37,6 +37,9 @@ static inline void ROBJ_TRANSIENT_SET(VALUE obj); static inline void ROBJ_TRANSIENT_UNSET(VALUE obj); uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id); +struct gen_ivtbl; +int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl); + RUBY_SYMBOL_EXPORT_BEGIN /* variable.c (export) */ void rb_mark_generic_ivar(VALUE); @@ -52,6 +55,8 @@ VALUE rb_gvar_set(ID, VALUE); VALUE rb_gvar_defined(ID); void rb_const_warn_if_deprecated(const rb_const_entry_t *, VALUE, ID); void rb_init_iv_list(VALUE obj); +void rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize); +struct gen_ivtbl * rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize); MJIT_SYMBOL_EXPORT_END static inline bool diff --git a/iseq.c b/iseq.c index aea781038ba8ea..5b1d9de106c47a 100644 --- a/iseq.c +++ b/iseq.c @@ -230,18 +230,8 @@ rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data) union iseq_inline_storage_entry *is_entries = body->is_entries; if (body->is_entries) { - // IVC entries - for (unsigned int i = 0; i < body->ivc_size; i++, is_entries++) { - IVC ivc = (IVC)is_entries; - if (ivc->entry) { - RUBY_ASSERT(!RB_TYPE_P(ivc->entry->class_value, T_NONE)); - - VALUE nv = func(data, ivc->entry->class_value); - if (ivc->entry->class_value != nv) { - ivc->entry->class_value = nv; - } - } - } + // Skip iterating over ivc caches + is_entries += body->ivc_size; // ICVARC entries for (unsigned int i = 0; i < body->icvarc_size; i++, is_entries++) { diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 49f28ab6908612..06f018c9346920 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -73,23 +73,6 @@ def compile_body(f, iseq, status) src << "#undef GET_SELF\n" src << "#define GET_SELF() cfp_self\n" - # Generate merged ivar guards first if needed - if !status.compile_info.disable_ivar_cache && status.merge_ivar_guards_p - src << " if (UNLIKELY(!(RB_TYPE_P(GET_SELF(), T_OBJECT) && (rb_serial_t)#{status.ivar_serial} == RCLASS_SERIAL(RBASIC(GET_SELF())->klass) &&" - if USE_RVARGC - src << "#{status.max_ivar_index} < ROBJECT_NUMIV(GET_SELF())" # index < ROBJECT_NUMIV(obj) - else - if status.max_ivar_index >= ROBJECT_EMBED_LEN_MAX - src << "#{status.max_ivar_index} < ROBJECT_NUMIV(GET_SELF())" # index < ROBJECT_NUMIV(obj) && !RB_FL_ANY_RAW(obj, ROBJECT_EMBED) - else - src << "ROBJECT_EMBED_LEN_MAX == ROBJECT_NUMIV(GET_SELF())" # index < ROBJECT_NUMIV(obj) && RB_FL_ANY_RAW(obj, ROBJECT_EMBED) - end - end - src << "))) {\n" - src << " goto ivar_cancel;\n" - src << " }\n" - end - # Simulate `opt_pc` in setup_parameters_complex. Other PCs which may be passed by catch tables # are not considered since vm_exec doesn't call jit_exec for catch tables. if iseq.body.param.flags.has_opt @@ -103,6 +86,13 @@ def compile_body(f, iseq, status) src << " }\n" end + # Generate merged ivar guards first if needed + if !status.compile_info.disable_ivar_cache && status.merge_ivar_guards_p + src << " if (UNLIKELY(!(RB_TYPE_P(GET_SELF(), T_OBJECT)))) {" + src << " goto ivar_cancel;\n" + src << " }\n" + end + C.fprintf(f, src) compile_insns(0, 0, status, iseq.body, f) compile_cancel_handler(f, iseq.body, status) @@ -363,52 +353,37 @@ def compile_ivar(insn_name, stack_size, pos, status, operands, body) ic_copy = (status.is_entries + (C.iseq_inline_storage_entry.new(operands[1]) - body.is_entries)).iv_cache src = +'' - if !status.compile_info.disable_ivar_cache && ic_copy.entry + if !status.compile_info.disable_ivar_cache && ic_copy.source_shape_id != C.INVALID_SHAPE_ID # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`. # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos) # JIT: prepare vm_getivar/vm_setivar arguments and variables src << "{\n" src << " VALUE obj = GET_SELF();\n" - src << " const uint32_t index = #{ic_copy.entry.index};\n" - if status.merge_ivar_guards_p - # JIT: Access ivar without checking these VM_ASSERTed prerequisites as we checked them in the beginning of `mjit_compile_body` - src << " VM_ASSERT(RB_TYPE_P(obj, T_OBJECT));\n" - src << " VM_ASSERT((rb_serial_t)#{ic_copy.entry.class_serial} == RCLASS_SERIAL(RBASIC(obj)->klass));\n" - src << " VM_ASSERT(index < ROBJECT_NUMIV(obj));\n" - if insn_name == :setinstancevariable - if USE_RVARGC - src << " if (LIKELY(!RB_OBJ_FROZEN_RAW(obj) && index < ROBJECT_NUMIV(obj))) {\n" - src << " RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], stack[#{stack_size - 1}]);\n" - else - heap_ivar_p = status.max_ivar_index >= ROBJECT_EMBED_LEN_MAX - src << " if (LIKELY(!RB_OBJ_FROZEN_RAW(obj) && #{heap_ivar_p ? 'true' : 'RB_FL_ANY_RAW(obj, ROBJECT_EMBED)'})) {\n" - src << " RB_OBJ_WRITE(obj, &ROBJECT(obj)->as.#{heap_ivar_p ? 'heap.ivptr[index]' : 'ary[index]'}, stack[#{stack_size - 1}]);\n" - end - src << " }\n" - else - src << " VALUE val;\n" - if USE_RVARGC - src << " if (LIKELY(index < ROBJECT_NUMIV(obj) && (val = ROBJECT_IVPTR(obj)[index]) != Qundef)) {\n" - else - heap_ivar_p = status.max_ivar_index >= ROBJECT_EMBED_LEN_MAX - src << " if (LIKELY(#{heap_ivar_p ? 'true' : 'RB_FL_ANY_RAW(obj, ROBJECT_EMBED)'} && (val = ROBJECT(obj)->as.#{heap_ivar_p ? 'heap.ivptr[index]' : 'ary[index]'}) != Qundef)) {\n" - end - src << " stack[#{stack_size}] = val;\n" - src << " }\n" - end + src << " const shape_id_t source_shape_id = (rb_serial_t)#{ic_copy.source_shape_id};\n" + # JIT: cache hit path of vm_getivar/vm_setivar, or cancel JIT (recompile it with exivar) + if insn_name == :setinstancevariable + src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" + src << " const shape_id_t dest_shape_id = (rb_serial_t)#{ic_copy.dest_shape_id};\n" + src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj) && \n" + src << " dest_shape_id != ROBJECT_SHAPE_ID(obj)) {\n" + src << " if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) {\n" + src << " rb_init_iv_list(obj);\n" + src << " }\n" + src << " ROBJECT_SET_SHAPE_ID(obj, dest_shape_id);\n" + src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n" + src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n" + src << " }\n" else - src << " const rb_serial_t ic_serial = (rb_serial_t)#{ic_copy.entry.class_serial};\n" - # JIT: cache hit path of vm_getivar/vm_setivar, or cancel JIT (recompile it with exivar) - if insn_name == :setinstancevariable - src << " if (LIKELY(RB_TYPE_P(obj, T_OBJECT) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && index < ROBJECT_NUMIV(obj) && !RB_OBJ_FROZEN_RAW(obj))) {\n" - src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n" - src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n" + if ic_copy.attr_index == 0 # cache hit, but uninitialized iv + src << " /* Uninitialized instance variable */\n" + src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n" + src << " stack[#{stack_size}] = Qnil;\n" src << " }\n" else - src << " VALUE val;\n" - src << " if (LIKELY(RB_TYPE_P(obj, T_OBJECT) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && index < ROBJECT_NUMIV(obj) && (val = ROBJECT_IVPTR(obj)[index]) != Qundef)) {\n" - src << " stack[#{stack_size}] = val;\n" + src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" + src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n" + src << " stack[#{stack_size}] = ROBJECT_IVPTR(obj)[index];\n" src << " }\n" end end @@ -419,20 +394,19 @@ def compile_ivar(insn_name, stack_size, pos, status, operands, body) src << " }\n" src << "}\n" return src - elsif insn_name == :getinstancevariable && !status.compile_info.disable_exivar_cache && ic_copy.entry + elsif insn_name == :getinstancevariable && !status.compile_info.disable_exivar_cache && ic_copy.source_shape_id != C.INVALID_SHAPE_ID # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`. # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos) # JIT: prepare vm_getivar's arguments and variables src << "{\n" src << " VALUE obj = GET_SELF();\n" - src << " const rb_serial_t ic_serial = (rb_serial_t)#{ic_copy.entry.class_serial};\n" - src << " const uint32_t index = #{ic_copy.entry.index};\n" + src << " const shape_id_t source_shape_id = (rb_serial_t)#{ic_copy.source_shape_id};\n" + src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" # JIT: cache hit path of vm_getivar, or cancel JIT (recompile it without any ivar optimization) src << " struct gen_ivtbl *ivtbl;\n" - src << " VALUE val;\n" - src << " if (LIKELY(FL_TEST_RAW(obj, FL_EXIVAR) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && rb_ivar_generic_ivtbl_lookup(obj, &ivtbl) && index < ivtbl->numiv && (val = ivtbl->ivptr[index]) != Qundef)) {\n" - src << " stack[#{stack_size}] = val;\n" + src << " if (LIKELY(FL_TEST_RAW(obj, FL_EXIVAR) && source_shape_id == rb_shape_get_shape_id(obj) && rb_ivar_generic_ivtbl_lookup(obj, &ivtbl))) {\n" + src << " stack[#{stack_size}] = ivtbl->ivptr[index];\n" src << " }\n" src << " else {\n" src << " reg_cfp->pc = original_body_iseq + #{pos};\n" @@ -832,35 +806,16 @@ def init_compile_status(status, body, compile_root_p) def init_ivar_compile_status(body, status) C.mjit_capture_is_entries(body, status.is_entries) - num_ivars = 0 pos = 0 - status.max_ivar_index = 0 - status.ivar_serial = 0 while pos < body.iseq_size insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos])) if insn.name == :getinstancevariable || insn.name == :setinstancevariable - ic = body.iseq_encoded[pos+2] - ic_copy = (status.is_entries + (C.iseq_inline_storage_entry.new(ic) - body.is_entries)).iv_cache - if ic_copy.entry # Only initialized (ic_serial > 0) IVCs are optimized - num_ivars += 1 - - if status.max_ivar_index < ic_copy.entry.index - status.max_ivar_index = ic_copy.entry.index - end - - if status.ivar_serial == 0 - status.ivar_serial = ic_copy.entry.class_serial - elsif status.ivar_serial != ic_copy.entry.class_serial - # Multiple classes have used this ISeq. Give up assuming one serial. - status.merge_ivar_guards_p = false - return - end - end + status.merge_ivar_guards_p = true + return end pos += insn.len end - status.merge_ivar_guards_p = status.ivar_serial > 0 && num_ivars >= 2 end # Expand simple macro that doesn't require dynamic C code. diff --git a/marshal.c b/marshal.c index e4b40c0607b608..59edbfe53a2a88 100644 --- a/marshal.c +++ b/marshal.c @@ -39,6 +39,7 @@ #include "ruby/st.h" #include "ruby/util.h" #include "builtin.h" +#include "shape.h" #define BITSPERSHORT (2*CHAR_BIT) #define SHORTMASK ((1<num_ivar) { - rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance", - CLASS_OF(arg->obj)); - } --ivarg->num_ivar; w_symbol(ID2SYM(id), arg->arg); w_object(value, arg->arg, arg->limit); @@ -720,6 +717,7 @@ has_ivars(VALUE obj, VALUE encname, VALUE *ivobj) static void w_ivar_each(VALUE obj, st_index_t num, struct dump_call_arg *arg) { + shape_id_t shape_id = rb_shape_get_shape_id(arg->obj); struct w_ivar_arg ivarg = {arg, num}; if (!num) return; rb_ivar_foreach(obj, w_obj_each, (st_data_t)&ivarg); @@ -727,6 +725,10 @@ w_ivar_each(VALUE obj, st_index_t num, struct dump_call_arg *arg) rb_raise(rb_eRuntimeError, "instance variable removed from %"PRIsVALUE" instance", CLASS_OF(arg->obj)); } + if (shape_id != rb_shape_get_shape_id(arg->obj)) { + rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance", + CLASS_OF(arg->obj)); + } } static void diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py index 595d54dfab3dde..9ef9d3967b6e86 100755 --- a/misc/lldb_cruby.py +++ b/misc/lldb_cruby.py @@ -418,6 +418,7 @@ def lldb_inspect(debugger, target, result, val): elif flType == RUBY_T_IMEMO: # I'm not sure how to get IMEMO_MASK out of lldb. It's not in globals() imemo_type = (flags >> RUBY_FL_USHIFT) & 0x0F # IMEMO_MASK + print("T_IMEMO: ", file=result) append_command_output(debugger, "p (enum imemo_type) %d" % imemo_type, result) append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result) diff --git a/mjit_c.rb b/mjit_c.rb index d8e5628bda57d4..0f8e11cbe5d766 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -5,6 +5,10 @@ module RubyVM::MJIT C = Object.new class << C + def SHAPE_BITS + RubyVM::Shape::SHAPE_BITS + end + def ROBJECT_EMBED_LEN_MAX Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' end @@ -165,6 +169,14 @@ def C.VM_METHOD_TYPE_ISEQ Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) } end + def C.INVALID_SHAPE_ID + Primitive.cexpr! %q{ ULONG2NUM(INVALID_SHAPE_ID) } + end + + def C.SHAPE_MASK + Primitive.cexpr! %q{ ULONG2NUM(SHAPE_MASK) } + end + def C.CALL_DATA @CALL_DATA ||= self.rb_call_data end @@ -181,6 +193,10 @@ def C.RB_BUILTIN @RB_BUILTIN ||= self.rb_builtin_function end + def C.attr_index_t + @attr_index_t ||= CType::Immediate.parse("uint32_t") + end + def C.compile_branch @compile_branch ||= CType::Struct.new( "compile_branch", Primitive.cexpr!("SIZEOF(struct compile_branch)"), @@ -201,7 +217,6 @@ def C.compile_status compiled_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_id)")], compile_info: [CType::Pointer.new { self.rb_mjit_compile_info }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compile_info)")], merge_ivar_guards_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), merge_ivar_guards_p)")], - ivar_serial: [self.rb_serial_t, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), ivar_serial)")], max_ivar_index: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), max_ivar_index)")], inlined_iseqs: [CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inlined_iseqs)")], inline_context: [self.inlined_call_context, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inline_context)")], @@ -240,7 +255,9 @@ def C.iseq_inline_constant_cache_entry def C.iseq_inline_iv_cache_entry @iseq_inline_iv_cache_entry ||= CType::Struct.new( "iseq_inline_iv_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_iv_cache_entry)"), - entry: [CType::Pointer.new { self.rb_iv_index_tbl_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), entry)")], + source_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), source_shape_id)")], + dest_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), dest_shape_id)")], + attr_index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), attr_index)")], ) end @@ -313,7 +330,11 @@ def C.rb_callcache call_: [self.vm_call_handler, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), call_)")], aux_: [CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_)"), - attr_index: CType::Immediate.parse("unsigned int"), + attr: CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_.attr)"), + index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, index)")], + dest_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, dest_shape_id)")], + ), method_missing_reason: self.method_missing_reason, v: self.VALUE, ), Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)")], @@ -503,8 +524,8 @@ def C.rb_iv_index_tbl_entry @rb_iv_index_tbl_entry ||= CType::Struct.new( "rb_iv_index_tbl_entry", Primitive.cexpr!("SIZEOF(struct rb_iv_index_tbl_entry)"), index: [CType::Immediate.parse("uint32_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), index)")], - class_serial: [self.rb_serial_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_serial)")], - class_value: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_value)")], + source_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), source_shape_id)")], + dest_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), dest_shape_id)")], ) end @@ -577,6 +598,10 @@ def C.VALUE @VALUE ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(VALUE)"), Primitive.cexpr!("SIGNED_TYPE_P(VALUE)")) end + def C.shape_id_t + @shape_id_t ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(shape_id_t)"), Primitive.cexpr!("SIGNED_TYPE_P(shape_id_t)")) + end + def C._Bool CType::Bool.new end diff --git a/mjit_compiler.h b/mjit_compiler.h index da7905442025f5..b465be00fd678a 100644 --- a/mjit_compiler.h +++ b/mjit_compiler.h @@ -8,6 +8,7 @@ #include "builtin.h" #include "mjit.h" #include "mjit_unit.h" +#include "shape.h" // Macros to check if a position is already compiled using compile_status.stack_size_for_pos #define NOT_COMPILED_STACK_SIZE -1 @@ -48,7 +49,6 @@ struct compile_status { // Mutated optimization levels struct rb_mjit_compile_info *compile_info; bool merge_ivar_guards_p; // If true, merge guards of ivar accesses - rb_serial_t ivar_serial; // ic_serial of IVC in is_entries (used only when merge_ivar_guards_p) size_t max_ivar_index; // Max IVC index in is_entries (used only when merge_ivar_guards_p) // If `inlined_iseqs[pos]` is not NULL, `mjit_compile_body` tries to inline ISeq there. const struct rb_iseq_constant_body **inlined_iseqs; diff --git a/object.c b/object.c index c47d9ab3b24fff..648946beb3e04f 100644 --- a/object.c +++ b/object.c @@ -39,6 +39,7 @@ #include "ruby/util.h" #include "ruby/assert.h" #include "builtin.h" +#include "shape.h" /*! * \addtogroup object @@ -271,9 +272,33 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) VALUE *src_buf = ROBJECT_IVPTR(obj); uint32_t dest_len = ROBJECT_NUMIV(dest); uint32_t src_len = ROBJECT_NUMIV(obj); - uint32_t len = dest_len < src_len ? dest_len : src_len; + uint32_t max_len = dest_len < src_len ? src_len : dest_len; - MEMCPY(dest_buf, src_buf, VALUE, len); + rb_ensure_iv_list_size(dest, dest_len, max_len); + + dest_len = ROBJECT_NUMIV(dest); + uint32_t min_len = dest_len > src_len ? src_len : dest_len; + + if (RBASIC(obj)->flags & ROBJECT_EMBED) { + src_buf = ROBJECT(obj)->as.ary; + + // embedded -> embedded + if (RBASIC(dest)->flags & ROBJECT_EMBED) { + dest_buf = ROBJECT(dest)->as.ary; + } + // embedded -> extended + else { + dest_buf = ROBJECT(dest)->as.heap.ivptr; + } + } + // extended -> extended + else { + RUBY_ASSERT(!(RBASIC(dest)->flags & ROBJECT_EMBED)); + dest_buf = ROBJECT(dest)->as.heap.ivptr; + src_buf = ROBJECT(obj)->as.heap.ivptr; + } + + MEMCPY(dest_buf, src_buf, VALUE, min_len); } static void @@ -283,10 +308,23 @@ init_copy(VALUE dest, VALUE obj) rb_raise(rb_eTypeError, "[bug] frozen object (%s) allocated", rb_obj_classname(dest)); } RBASIC(dest)->flags &= ~(T_MASK|FL_EXIVAR); + // Copies the shape id from obj to dest RBASIC(dest)->flags |= RBASIC(obj)->flags & (T_MASK|FL_EXIVAR); rb_copy_wb_protected_attribute(dest, obj); rb_copy_generic_ivar(dest, obj); rb_gc_copy_finalizer(dest, obj); + + rb_shape_t *shape_to_set = rb_shape_get_shape(obj); + + // If the object is frozen, the "dup"'d object will *not* be frozen, + // so we need to copy the frozen shape's parent to the new object. + if (rb_shape_frozen_shape_p(shape_to_set)) { + shape_to_set = shape_to_set->parent; + } + + // shape ids are different + rb_shape_set_shape(dest, shape_to_set); + if (RB_TYPE_P(obj, T_OBJECT)) { rb_obj_copy_ivar(dest, obj); } @@ -392,6 +430,9 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze) case Qnil: rb_funcall(clone, id_init_clone, 1, obj); RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE; + if (RB_OBJ_FROZEN(obj)) { + rb_shape_transition_shape_frozen(clone); + } break; case Qtrue: { @@ -407,6 +448,7 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze) argv[1] = freeze_true_hash; rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS); RBASIC(clone)->flags |= FL_FREEZE; + rb_shape_transition_shape_frozen(clone); break; } case Qfalse: diff --git a/ractor_core.h b/ractor_core.h index a065f5f809d0cf..9d4b8387c734b0 100644 --- a/ractor_core.h +++ b/ractor_core.h @@ -289,11 +289,13 @@ rb_ractor_id(const rb_ractor_t *r) #if RACTOR_CHECK_MODE > 0 uint32_t rb_ractor_current_id(void); +// If ractor check mode is enabled, shape bits needs to be smaller +STATIC_ASSERT(shape_bits, SHAPE_BITS == 16); static inline void rb_ractor_setup_belonging_to(VALUE obj, uint32_t rid) { - VALUE flags = RBASIC(obj)->flags & 0xffffffff; // 4B + VALUE flags = RBASIC(obj)->flags & 0xffff0000ffffffff; // 4B RBASIC(obj)->flags = flags | ((VALUE)rid << 32); } @@ -310,7 +312,7 @@ rb_ractor_belonging(VALUE obj) return 0; } else { - return RBASIC(obj)->flags >> 32; + return RBASIC(obj)->flags >> 32 & 0xFFFF; } } diff --git a/shape.c b/shape.c new file mode 100644 index 00000000000000..a6a5adf8541c25 --- /dev/null +++ b/shape.c @@ -0,0 +1,523 @@ +#include "vm_core.h" +#include "vm_sync.h" +#include "shape.h" +#include "internal/class.h" +#include "internal/symbol.h" +#include "internal/variable.h" +#include + +/* + * Shape getters + */ +static rb_shape_t* +rb_shape_get_root_shape(void) { + return GET_VM()->root_shape; +} + +shape_id_t +rb_shape_id(rb_shape_t * shape) +{ + return (shape_id_t)(shape - GET_VM()->shape_list); +} + +static rb_shape_t* +rb_shape_get_frozen_root_shape(void) { + return GET_VM()->frozen_root_shape; +} + +bool +rb_shape_root_shape_p(rb_shape_t* shape) { + return shape == rb_shape_get_root_shape(); +} + +rb_shape_t* +rb_shape_get_shape_by_id(shape_id_t shape_id) +{ + RUBY_ASSERT(shape_id != INVALID_SHAPE_ID); + + rb_vm_t *vm = GET_VM(); + rb_shape_t *shape = &vm->shape_list[shape_id]; + return shape; +} + +rb_shape_t* +rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id) +{ + RUBY_ASSERT(shape_id != INVALID_SHAPE_ID); + + rb_vm_t *vm = GET_VM(); + rb_shape_t *shape = &vm->shape_list[shape_id]; + return shape; +} + +#if !SHAPE_IN_BASIC_FLAGS +static inline shape_id_t +RCLASS_SHAPE_ID(VALUE obj) +{ + return RCLASS_EXT(obj)->shape_id; +} + +shape_id_t rb_generic_shape_id(VALUE obj); +#endif + +shape_id_t +rb_shape_get_shape_id(VALUE obj) +{ + if (RB_SPECIAL_CONST_P(obj)) { + return FROZEN_ROOT_SHAPE_ID; + } + +#if SHAPE_IN_BASIC_FLAGS + return RBASIC_SHAPE_ID(obj); +#else + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + return ROBJECT_SHAPE_ID(obj); + break; + case T_CLASS: + case T_MODULE: + return RCLASS_SHAPE_ID(obj); + default: + return rb_generic_shape_id(obj); + } +#endif +} + +rb_shape_t* +rb_shape_get_shape(VALUE obj) +{ + return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj)); +} + +static rb_shape_t * +rb_shape_lookup_id(rb_shape_t* shape, ID id, enum shape_type shape_type) { + while (shape->parent) { + if (shape->edge_name == id) { + // If the shape type is different, we don't + // want this to count as a "found" ID + if (shape_type == (enum shape_type)shape->type) { + return shape; + } + else { + return NULL; + } + } + shape = shape->parent; + } + return NULL; +} + +static rb_shape_t* +get_next_shape_internal(rb_shape_t* shape, ID id, VALUE obj, enum shape_type shape_type) +{ + rb_shape_t *res = NULL; + RUBY_ASSERT(SHAPE_FROZEN != (enum shape_type)shape->type); + RB_VM_LOCK_ENTER(); + { + if (rb_shape_lookup_id(shape, id, shape_type)) { + // If shape already contains the ivar that is being set, we'll return shape + res = shape; + } + else { + if (!shape->edges) { + shape->edges = rb_id_table_create(0); + } + + // Lookup the shape in edges - if there's already an edge and a corresponding shape for it, + // we can return that. Otherwise, we'll need to get a new shape + if (!rb_id_table_lookup(shape->edges, id, (VALUE *)&res)) { + // In this case, the shape exists, but the shape is garbage, so we need to recreate it + if (res) { + rb_id_table_delete(shape->edges, id); + res->parent = NULL; + } + + rb_shape_t * new_shape = rb_shape_alloc(id, shape); + + new_shape->type = (uint8_t)shape_type; + + switch(shape_type) { + case SHAPE_IVAR: + new_shape->iv_count = new_shape->parent->iv_count + 1; + + // Check if we should update max_iv_count on the object's class + if (BUILTIN_TYPE(obj) == T_OBJECT) { + VALUE klass = rb_obj_class(obj); + if (new_shape->iv_count > RCLASS_EXT(klass)->max_iv_count) { + RCLASS_EXT(klass)->max_iv_count = new_shape->iv_count; + } + } + break; + case SHAPE_IVAR_UNDEF: + case SHAPE_FROZEN: + new_shape->iv_count = new_shape->parent->iv_count; + break; + case SHAPE_ROOT: + rb_bug("Unreachable"); + break; + } + + rb_id_table_insert(shape->edges, id, (VALUE)new_shape); + + res = new_shape; + } + } + } + RB_VM_LOCK_LEAVE(); + return res; +} + +MJIT_FUNC_EXPORTED int +rb_shape_frozen_shape_p(rb_shape_t* shape) +{ + return SHAPE_FROZEN == (enum shape_type)shape->type; +} + +void +rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape) +{ + rb_shape_t* next_shape = get_next_shape_internal(shape, id, obj, SHAPE_IVAR_UNDEF); + + if (shape == next_shape) { + return; + } + + rb_shape_set_shape(obj, next_shape); +} + +void +rb_shape_transition_shape_frozen(VALUE obj) +{ + rb_shape_t* shape = rb_shape_get_shape(obj); + RUBY_ASSERT(shape); + RUBY_ASSERT(RB_OBJ_FROZEN(obj)); + + if (rb_shape_frozen_shape_p(shape)) { + return; + } + + rb_shape_t* next_shape; + + if (shape == rb_shape_get_root_shape()) { + switch(BUILTIN_TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + return; + } + next_shape = rb_shape_get_frozen_root_shape(); + } + else { + static ID id_frozen; + if (!id_frozen) { + id_frozen = rb_make_internal_id(); + } + + next_shape = get_next_shape_internal(shape, (ID)id_frozen, obj, SHAPE_FROZEN); + } + + RUBY_ASSERT(next_shape); + rb_shape_set_shape(obj, next_shape); +} + +void +rb_shape_transition_shape(VALUE obj, ID id, rb_shape_t *shape) +{ + rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); + if (shape == next_shape) { + return; + } + rb_shape_set_shape(obj, next_shape); +} + +rb_shape_t* +rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id) +{ + return get_next_shape_internal(shape, id, obj, SHAPE_IVAR); +} + +bool +rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) { + while (shape->parent) { + if (shape->edge_name == id) { + enum shape_type shape_type; + shape_type = (enum shape_type)shape->type; + + switch(shape_type) { + case SHAPE_IVAR: + RUBY_ASSERT(shape->iv_count > 0); + *value = shape->iv_count - 1; + return true; + case SHAPE_IVAR_UNDEF: + case SHAPE_ROOT: + return false; + case SHAPE_FROZEN: + rb_bug("Ivar should not exist on frozen transition\n"); + } + } + shape = shape->parent; + } + return false; +} + +static rb_shape_t * +shape_alloc(void) +{ + rb_vm_t *vm = GET_VM(); + shape_id_t shape_id = vm->next_shape_id; + vm->next_shape_id++; + + if (shape_id == MAX_SHAPE_ID) { + // TODO: Make an OutOfShapesError ?? + rb_bug("Out of shapes\n"); + } + + return &GET_VM()->shape_list[shape_id]; +} + +rb_shape_t * +rb_shape_alloc(ID edge_name, rb_shape_t * parent) +{ + rb_shape_t * shape = shape_alloc(); + + shape->edge_name = edge_name; + shape->iv_count = 0; + shape->parent = parent; + + return shape; +} + +MJIT_FUNC_EXPORTED void +rb_shape_set_shape(VALUE obj, rb_shape_t* shape) +{ + rb_shape_set_shape_id(obj, rb_shape_id(shape)); +} + +VALUE rb_cShape; + +/* + * Exposing Shape to Ruby via RubyVM.debug_shape + */ +static const rb_data_type_t shape_data_type = { + "Shape", + {NULL, NULL, NULL,}, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED +}; + +static VALUE +rb_wrapped_shape_id(VALUE self) { + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + return INT2NUM(rb_shape_id(shape)); +} + +static VALUE +rb_shape_type(VALUE self) { + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + return INT2NUM(shape->type); +} + +static VALUE +rb_shape_parent_id(VALUE self) +{ + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + if (shape->parent) { + return INT2NUM(rb_shape_id(shape->parent)); + } + else { + return Qnil; + } +} + +static VALUE parse_key(ID key) { + if ((key & RUBY_ID_INTERNAL) == RUBY_ID_INTERNAL) { + return LONG2NUM(key); + } else { + return ID2SYM(key); + } +} + +static VALUE +rb_shape_t_to_rb_cShape(rb_shape_t *shape) { + union { const rb_shape_t *in; void *out; } deconst; + VALUE res; + deconst.in = shape; + res = TypedData_Wrap_Struct(rb_cShape, &shape_data_type, deconst.out); + + return res; +} + +static enum rb_id_table_iterator_result rb_edges_to_hash(ID key, VALUE value, void *ref) +{ + rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_shape_t_to_rb_cShape((rb_shape_t*)value)); + return ID_TABLE_CONTINUE; +} + +static VALUE +rb_shape_edges(VALUE self) +{ + rb_shape_t* shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + VALUE hash = rb_hash_new(); + + if (shape->edges) { + rb_id_table_foreach(shape->edges, rb_edges_to_hash, &hash); + } + + return hash; +} + +static VALUE +rb_shape_edge_name(VALUE self) +{ + rb_shape_t* shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + if (shape->edge_name) { + return ID2SYM(shape->edge_name); + } + else { + return Qnil; + } +} + +static VALUE +rb_shape_iv_count(VALUE self) +{ + rb_shape_t* shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + return INT2NUM(shape->iv_count); +} + +static VALUE +rb_shape_export_depth(VALUE self) +{ + rb_shape_t* shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + unsigned int depth = 0; + while (shape->parent) { + depth++; + shape = shape->parent; + } + return INT2NUM(depth); +} + +static VALUE +rb_shape_parent(VALUE self) +{ + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + if (shape->parent) { + return rb_shape_t_to_rb_cShape(shape->parent); + } + else { + return Qnil; + } +} + +VALUE rb_shape_debug_shape(VALUE self, VALUE obj) { + return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj)); +} + +VALUE rb_shape_debug_root_shape(VALUE self) { + return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape()); +} + +VALUE rb_shape_debug_frozen_root_shape(VALUE self) { + return rb_shape_t_to_rb_cShape(rb_shape_get_frozen_root_shape()); +} + +VALUE rb_obj_shape(rb_shape_t* shape); + +static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref) +{ + rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_obj_shape((rb_shape_t*)value)); + return ID_TABLE_CONTINUE; +} + +static VALUE edges(struct rb_id_table* edges) +{ + VALUE hash = rb_hash_new(); + if (edges) + rb_id_table_foreach(edges, collect_keys_and_values, &hash); + return hash; +} + +VALUE rb_obj_shape(rb_shape_t* shape) { + VALUE rb_shape = rb_hash_new(); + + rb_hash_aset(rb_shape, ID2SYM(rb_intern("id")), INT2NUM(rb_shape_id(shape))); + rb_hash_aset(rb_shape, ID2SYM(rb_intern("edges")), edges(shape->edges)); + + if (shape == rb_shape_get_root_shape()) { + rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(ROOT_SHAPE_ID)); + } + else { + rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(rb_shape_id(shape->parent))); + } + + rb_hash_aset(rb_shape, ID2SYM(rb_intern("edge_name")), rb_id2str(shape->edge_name)); + return rb_shape; +} + +static VALUE shape_transition_tree(VALUE self) { + return rb_obj_shape(rb_shape_get_root_shape()); +} + +static VALUE shape_count(VALUE self) { + int shape_count = 0; + rb_vm_t *vm = GET_VM(); + for(shape_id_t i = 0; i < vm->next_shape_id; i++) { + if(rb_shape_get_shape_by_id_without_assertion(i)) { + shape_count++; + } + } + return INT2NUM(shape_count); +} + +static VALUE +shape_max_shape_count(VALUE self) +{ + return INT2NUM(GET_VM()->next_shape_id); +} + +VALUE +rb_shape_flags_mask(void) +{ + return SHAPE_FLAG_MASK; +} + +void +Init_shape(void) +{ + rb_cShape = rb_define_class_under(rb_cRubyVM, "Shape", rb_cObject); + rb_undef_alloc_func(rb_cShape); + + rb_define_method(rb_cShape, "parent_id", rb_shape_parent_id, 0); + rb_define_method(rb_cShape, "parent", rb_shape_parent, 0); + rb_define_method(rb_cShape, "edges", rb_shape_edges, 0); + rb_define_method(rb_cShape, "edge_name", rb_shape_edge_name, 0); + rb_define_method(rb_cShape, "iv_count", rb_shape_iv_count, 0); + rb_define_method(rb_cShape, "depth", rb_shape_export_depth, 0); + rb_define_method(rb_cShape, "id", rb_wrapped_shape_id, 0); + rb_define_method(rb_cShape, "type", rb_shape_type, 0); + rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT)); + rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR)); + rb_define_const(rb_cShape, "SHAPE_IVAR_UNDEF", INT2NUM(SHAPE_IVAR_UNDEF)); + rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN)); + rb_define_const(rb_cShape, "SHAPE_BITS", INT2NUM(SHAPE_BITS)); + + rb_define_module_function(rb_cRubyVM, "debug_shape_transition_tree", shape_transition_tree, 0); + rb_define_module_function(rb_cRubyVM, "debug_shape_count", shape_count, 0); + rb_define_singleton_method(rb_cRubyVM, "debug_shape", rb_shape_debug_shape, 1); + rb_define_singleton_method(rb_cRubyVM, "debug_max_shape_count", shape_max_shape_count, 0); + rb_define_singleton_method(rb_cRubyVM, "debug_root_shape", rb_shape_debug_root_shape, 0); + rb_define_singleton_method(rb_cRubyVM, "debug_frozen_root_shape", rb_shape_debug_frozen_root_shape, 0); +} diff --git a/shape.h b/shape.h new file mode 100644 index 00000000000000..66b8e580c9ecc7 --- /dev/null +++ b/shape.h @@ -0,0 +1,150 @@ +#ifndef RUBY_SHAPE_H +#define RUBY_SHAPE_H +#if (SIZEOF_UINT64_T == SIZEOF_VALUE) +#define SIZEOF_SHAPE_T 4 +#define SHAPE_IN_BASIC_FLAGS 1 +typedef uint32_t attr_index_t; +#else +#define SIZEOF_SHAPE_T 2 +#define SHAPE_IN_BASIC_FLAGS 0 +typedef uint16_t attr_index_t; +#endif + +#define MAX_IVARS (attr_index_t)(-1) + +#if RUBY_DEBUG || (defined(VM_CHECK_MODE) && VM_CHECK_MODE > 0) +# if SIZEOF_SHAPE_T == 4 +typedef uint32_t shape_id_t; +# define SHAPE_BITS 16 +# else +typedef uint16_t shape_id_t; +# define SHAPE_BITS 16 +# endif +#else +# if SIZEOF_SHAPE_T == 4 +typedef uint32_t shape_id_t; +# define SHAPE_BITS 32 +# else +typedef uint16_t shape_id_t; +# define SHAPE_BITS 16 +# endif +#endif + +# define SHAPE_MASK (((uintptr_t)1 << SHAPE_BITS) - 1) +# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_BITS) + +# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_BITS) + +# define SHAPE_BITMAP_SIZE 16384 + +# define MAX_SHAPE_ID (SHAPE_MASK - 1) +# define INVALID_SHAPE_ID SHAPE_MASK +# define ROOT_SHAPE_ID 0x0 +# define FROZEN_ROOT_SHAPE_ID 0x1 + +struct rb_shape { + struct rb_shape * parent; // Pointer to the parent + struct rb_id_table * edges; // id_table from ID (ivar) to next shape + ID edge_name; // ID (ivar) for transition from parent to rb_shape + attr_index_t iv_count; + uint8_t type; +}; + +typedef struct rb_shape rb_shape_t; + +enum shape_type { + SHAPE_ROOT, + SHAPE_IVAR, + SHAPE_FROZEN, + SHAPE_IVAR_UNDEF, +}; + +static inline shape_id_t +IMEMO_CACHED_SHAPE_ID(VALUE cc) +{ + RBIMPL_ASSERT_TYPE((VALUE)cc, RUBY_T_IMEMO); + return (shape_id_t)(SHAPE_MASK & (RBASIC(cc)->flags >> SHAPE_FLAG_SHIFT)); +} + +static inline void +IMEMO_SET_CACHED_SHAPE_ID(VALUE cc, shape_id_t shape_id) +{ + RBIMPL_ASSERT_TYPE((VALUE)cc, RUBY_T_IMEMO); + RBASIC(cc)->flags &= SHAPE_FLAG_MASK; + RBASIC(cc)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); +} + +#if SHAPE_IN_BASIC_FLAGS +static inline shape_id_t +RBASIC_SHAPE_ID(VALUE obj) +{ + RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj)); + return (shape_id_t)(SHAPE_MASK & ((RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT)); +} + +static inline void +RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) +{ + // Ractors are occupying the upper 32 bits of flags, but only in debug mode + // Object shapes are occupying top bits + RBASIC(obj)->flags &= SHAPE_FLAG_MASK; + RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); +} + +static inline shape_id_t +ROBJECT_SHAPE_ID(VALUE obj) +{ + RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); + return RBASIC_SHAPE_ID(obj); +} + +static inline void +ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) +{ + RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); + RBASIC_SET_SHAPE_ID(obj, shape_id); +} + +#else + +static inline shape_id_t +ROBJECT_SHAPE_ID(VALUE obj) +{ + RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); + return (shape_id_t)(SHAPE_MASK & (RBASIC(obj)->flags >> SHAPE_FLAG_SHIFT)); +} + +static inline void +ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) +{ + RBASIC(obj)->flags &= SHAPE_FLAG_MASK; + RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); +} +#endif + +bool rb_shape_root_shape_p(rb_shape_t* shape); + +rb_shape_t* rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id); + +MJIT_SYMBOL_EXPORT_BEGIN +rb_shape_t* rb_shape_get_shape_by_id(shape_id_t shape_id); +void rb_shape_set_shape(VALUE obj, rb_shape_t* shape); +shape_id_t rb_shape_get_shape_id(VALUE obj); +rb_shape_t* rb_shape_get_shape(VALUE obj); +int rb_shape_frozen_shape_p(rb_shape_t* shape); +void rb_shape_transition_shape_frozen(VALUE obj); +void rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape); +void rb_shape_transition_shape(VALUE obj, ID id, rb_shape_t *shape); +rb_shape_t* rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id); +bool rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t * value); +shape_id_t rb_shape_id(rb_shape_t * shape); +MJIT_SYMBOL_EXPORT_END + +rb_shape_t * rb_shape_alloc(ID edge_name, rb_shape_t * parent); + +bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id); + +VALUE rb_obj_debug_shape(VALUE self, VALUE obj); +VALUE rb_shape_flags_mask(void); + +#endif diff --git a/test/-ext-/marshal/test_internal_ivar.rb b/test/-ext-/marshal/test_internal_ivar.rb index a32138f6e8f50d..9359c7f11320c3 100644 --- a/test/-ext-/marshal/test_internal_ivar.rb +++ b/test/-ext-/marshal/test_internal_ivar.rb @@ -7,6 +7,7 @@ module Bug end module Bug::Marshal class TestInternalIVar < Test::Unit::TestCase def test_marshal + pend "We don't support IVs with ID of 0" v = InternalIVar.new("hello", "world", "bye") assert_equal("hello", v.normal) assert_equal("world", v.internal) diff --git a/test/ruby/test_mjit.rb b/test/ruby/test_mjit.rb index e49195f7631c67..4c6cc6f39fdfd9 100644 --- a/test/ruby/test_mjit.rb +++ b/test/ruby/test_mjit.rb @@ -831,7 +831,7 @@ def test(obj, recursive: nil) end def test_inlined_exivar - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 3, recompile_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 4, recompile_count: 2, min_calls: 2) begin; class Foo < Hash def initialize @@ -850,7 +850,7 @@ def bar end def test_inlined_undefined_ivar - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 3, min_calls: 3) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 2, min_calls: 2) begin; class Foo def initialize diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index 83208bbcdb5c64..a9d5d4b13e48d7 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -993,4 +993,13 @@ def test_clone_object_should_not_be_old end EOS end + + def test_frozen_inspect + obj = Object.new + obj.instance_variable_set(:@a, "a") + ins = obj.inspect + obj.freeze + + assert_equal(ins, obj.inspect) + end end diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb new file mode 100644 index 00000000000000..807d485354a8ec --- /dev/null +++ b/test/ruby/test_shapes.rb @@ -0,0 +1,173 @@ +# frozen_string_literal: false +require 'test/unit' + +# These test the functionality of object shapes +class TestShapes < Test::Unit::TestCase + class Example + def initialize + @a = 1 + end + end + + class RemoveAndAdd + def add_foo + @foo = 1 + end + + def remove + remove_instance_variable(:@foo) + end + + def add_bar + @bar = 1 + end + end + + # RubyVM.debug_shape returns new instances of shape objects for + # each call. This helper method allows us to define equality for + # shapes + def assert_shape_equal(shape1, shape2) + assert_equal(shape1.id, shape2.id) + assert_equal(shape1.parent_id, shape2.parent_id) + assert_equal(shape1.depth, shape2.depth) + assert_equal(shape1.type, shape2.type) + end + + def refute_shape_equal(shape1, shape2) + refute_equal(shape1.id, shape2.id) + end + + def test_iv_index + example = RemoveAndAdd.new + shape = RubyVM.debug_shape(example) + assert_equal 0, shape.iv_count + + example.add_foo # makes a transition + new_shape = RubyVM.debug_shape(example) + assert_equal([:@foo], example.instance_variables) + assert_equal(shape.id, new_shape.parent.id) + assert_equal(1, new_shape.iv_count) + + example.remove # makes a transition + remove_shape = RubyVM.debug_shape(example) + assert_equal([], example.instance_variables) + assert_equal(new_shape.id, remove_shape.parent.id) + assert_equal(1, remove_shape.iv_count) + + example.add_bar # makes a transition + bar_shape = RubyVM.debug_shape(example) + assert_equal([:@bar], example.instance_variables) + assert_equal(remove_shape.id, bar_shape.parent.id) + assert_equal(2, bar_shape.iv_count) + end + + def test_new_obj_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape(Object.new)) + end + + def test_frozen_new_obj_has_frozen_root_shape + assert_shape_equal( + RubyVM.debug_frozen_root_shape, + RubyVM.debug_shape(Object.new.freeze) + ) + end + + def test_str_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape("")) + end + + def test_array_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape([])) + end + + def test_hash_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape({})) + end + + def test_true_has_frozen_root_shape + assert_shape_equal(RubyVM.debug_frozen_root_shape, RubyVM.debug_shape(true)) + end + + def test_nil_has_frozen_root_shape + assert_shape_equal(RubyVM.debug_frozen_root_shape, RubyVM.debug_shape(nil)) + end + + def test_basic_shape_transition + obj = Example.new + refute_equal(RubyVM.debug_root_shape, RubyVM.debug_shape(obj)) + assert_shape_equal(RubyVM.debug_root_shape.edges[:@a], RubyVM.debug_shape(obj)) + assert_equal(obj.instance_variable_get(:@a), 1) + end + + def test_different_objects_make_same_transition + obj = Example.new + obj2 = "" + obj2.instance_variable_set(:@a, 1) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_duplicating_objects + obj = Example.new + obj2 = obj.dup + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_freezing_and_duplicating_object + obj = Object.new.freeze + obj2 = obj.dup + refute_predicate(obj2, :frozen?) + # dup'd objects shouldn't be frozen, and the shape should be the + # parent shape of the copied object + assert_equal(RubyVM.debug_shape(obj).parent.id, RubyVM.debug_shape(obj2).id) + end + + def test_freezing_and_duplicating_object_with_ivars + obj = Example.new.freeze + obj2 = obj.dup + refute_predicate(obj2, :frozen?) + refute_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_equal(obj2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_duplicating_string_with_ivars + str = "str" + str.instance_variable_set(:@a, 1) + str.freeze + str2 = str.dup + refute_predicate(str2, :frozen?) + refute_equal(RubyVM.debug_shape(str).id, RubyVM.debug_shape(str2).id) + assert_equal(str2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_cloning_objects + obj = Object.new.freeze + obj2 = obj.clone(freeze: true) + assert_predicate(obj2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_freezing_and_cloning_object_with_ivars + obj = Example.new.freeze + obj2 = obj.clone(freeze: true) + assert_predicate(obj2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_equal(obj2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_cloning_string + str = "str".freeze + str2 = str.clone(freeze: true) + assert_predicate(str2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(str), RubyVM.debug_shape(str2)) + end + + def test_freezing_and_cloning_string_with_ivars + str = "str" + str.instance_variable_set(:@a, 1) + str.freeze + str2 = str.clone(freeze: true) + assert_predicate(str2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(str), RubyVM.debug_shape(str2)) + assert_equal(str2.instance_variable_get(:@a), 1) + end +end diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index d0f9bf527bf4c3..8c21d424495c0c 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -341,12 +341,17 @@ def push_target(target) VM_METHOD_TYPE_CFUNC VM_METHOD_TYPE_ISEQ ], + ULONG: %w[ + INVALID_SHAPE_ID + SHAPE_MASK + ], }, types: %w[ CALL_DATA IC IVC RB_BUILTIN + attr_index_t compile_branch compile_status inlined_call_context @@ -360,10 +365,10 @@ def push_target(target) rb_callable_method_entry_struct rb_callcache rb_callinfo - rb_cref_t rb_control_frame_t - rb_execution_context_t + rb_cref_t rb_execution_context_struct + rb_execution_context_t rb_iseq_constant_body rb_iseq_location_t rb_iseq_struct @@ -378,6 +383,7 @@ def push_target(target) ], dynamic_types: %w[ VALUE + shape_id_t ], skip_fields: { 'rb_execution_context_struct.machine': %w[regs], # differs between macOS and Linux diff --git a/variable.c b/variable.c index 056a1000b8edee..b5e95b7f1c9c98 100644 --- a/variable.c +++ b/variable.c @@ -34,6 +34,7 @@ #include "ruby/st.h" #include "ruby/util.h" #include "transient_heap.h" +#include "shape.h" #include "variable.h" #include "vm_core.h" #include "ractor_core.h" @@ -63,12 +64,9 @@ static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int v static st_table *generic_iv_tbl_; struct ivar_update { - union { - st_table *iv_index_tbl; - struct gen_ivtbl *ivtbl; - } u; - st_data_t index; - int iv_extended; + struct gen_ivtbl *ivtbl; + uint32_t iv_index; + rb_shape_t* shape; }; void @@ -896,30 +894,6 @@ rb_alias_variable(ID name1, ID name2) entry1->var = entry2->var; } -static bool -iv_index_tbl_lookup(struct st_table *tbl, ID id, uint32_t *indexp) -{ - st_data_t ent_data; - int r; - - if (tbl == NULL) return false; - - RB_VM_LOCK_ENTER(); - { - r = st_lookup(tbl, (st_data_t)id, &ent_data); - } - RB_VM_LOCK_LEAVE(); - - if (r) { - struct rb_iv_index_tbl_entry *ent = (void *)ent_data; - *indexp = ent->index; - return true; - } - else { - return false; - } -} - static void IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(ID id) { @@ -957,7 +931,20 @@ generic_ivtbl_no_ractor_check(VALUE obj) } static int -gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl) +gen_ivtbl_get_unlocked(VALUE obj, ID id, struct gen_ivtbl **ivtbl) +{ + st_data_t data; + + if (st_lookup(generic_ivtbl(obj, id, false), (st_data_t)obj, &data)) { + *ivtbl = (struct gen_ivtbl *)data; + return 1; + } + + return 0; +} + +MJIT_FUNC_EXPORTED int +rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl) { st_data_t data; int r = 0; @@ -977,63 +964,7 @@ gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl) MJIT_FUNC_EXPORTED int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **ivtbl) { - return gen_ivtbl_get(obj, 0, ivtbl); -} - -MJIT_FUNC_EXPORTED VALUE -rb_ivar_generic_lookup_with_index(VALUE obj, ID id, uint32_t index) -{ - struct gen_ivtbl *ivtbl; - - if (gen_ivtbl_get(obj, id, &ivtbl)) { - if (LIKELY(index < ivtbl->numiv)) { - VALUE val = ivtbl->ivptr[index]; - return val; - } - } - - return Qundef; -} - -static VALUE -generic_ivar_delete(VALUE obj, ID id, VALUE undef) -{ - struct gen_ivtbl *ivtbl; - - if (gen_ivtbl_get(obj, id, &ivtbl)) { - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - uint32_t index; - - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &index)) { - if (index < ivtbl->numiv) { - VALUE ret = ivtbl->ivptr[index]; - - ivtbl->ivptr[index] = Qundef; - return ret == Qundef ? undef : ret; - } - } - } - return undef; -} - -static VALUE -generic_ivar_get(VALUE obj, ID id, VALUE undef) -{ - struct gen_ivtbl *ivtbl; - - if (gen_ivtbl_get(obj, id, &ivtbl)) { - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - uint32_t index; - - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &index)) { - if (index < ivtbl->numiv) { - VALUE ret = ivtbl->ivptr[index]; - - return ret == Qundef ? undef : ret; - } - } - } - return undef; + return rb_gen_ivtbl_get(obj, 0, ivtbl); } static size_t @@ -1045,6 +976,8 @@ gen_ivtbl_bytes(size_t n) static struct gen_ivtbl * gen_ivtbl_resize(struct gen_ivtbl *old, uint32_t n) { + RUBY_ASSERT(n > 0); + uint32_t len = old ? old->numiv : 0; struct gen_ivtbl *ivtbl = xrealloc(old, gen_ivtbl_bytes(n)); @@ -1069,18 +1002,6 @@ gen_ivtbl_dup(const struct gen_ivtbl *orig) } #endif -static uint32_t -iv_index_tbl_newsize(struct ivar_update *ivup) -{ - if (!ivup->iv_extended) { - return (uint32_t)ivup->u.iv_index_tbl->num_entries; - } - else { - uint32_t index = (uint32_t)ivup->index; /* should not overflow */ - return (index+1) + (index+1)/4; /* (index+1)*1.25 */ - } -} - static int generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing) { @@ -1091,53 +1012,22 @@ generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing) if (existing) { ivtbl = (struct gen_ivtbl *)*v; - if (ivup->index < ivtbl->numiv) { - ivup->u.ivtbl = ivtbl; + if (ivup->iv_index < ivtbl->numiv) { + ivup->ivtbl = ivtbl; return ST_STOP; } } FL_SET((VALUE)*k, FL_EXIVAR); - uint32_t newsize = iv_index_tbl_newsize(ivup); - ivtbl = gen_ivtbl_resize(ivtbl, newsize); + ivtbl = gen_ivtbl_resize(ivtbl, ivup->shape->iv_count); + // Reinsert in to the hash table because ivtbl might be a newly resized chunk of memory *v = (st_data_t)ivtbl; - ivup->u.ivtbl = ivtbl; + ivup->ivtbl = ivtbl; +#if !SHAPE_IN_BASIC_FLAGS + ivtbl->shape_id = rb_shape_id(ivup->shape); +#endif return ST_CONTINUE; } -static VALUE -generic_ivar_defined(VALUE obj, ID id) -{ - struct gen_ivtbl *ivtbl; - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - uint32_t index; - - if (!iv_index_tbl_lookup(iv_index_tbl, id, &index)) return Qfalse; - if (!gen_ivtbl_get(obj, id, &ivtbl)) return Qfalse; - - return RBOOL((index < ivtbl->numiv) && (ivtbl->ivptr[index] != Qundef)); -} - -static int -generic_ivar_remove(VALUE obj, ID id, VALUE *valp) -{ - struct gen_ivtbl *ivtbl; - uint32_t index; - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - - if (!iv_index_tbl) return 0; - if (!iv_index_tbl_lookup(iv_index_tbl, id, &index)) return 0; - if (!gen_ivtbl_get(obj, id, &ivtbl)) return 0; - - if (index < ivtbl->numiv) { - if (ivtbl->ivptr[index] != Qundef) { - *valp = ivtbl->ivptr[index]; - ivtbl->ivptr[index] = Qundef; - return 1; - } - } - return 0; -} - static void gen_ivtbl_mark(const struct gen_ivtbl *ivtbl) { @@ -1153,7 +1043,7 @@ rb_mark_generic_ivar(VALUE obj) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) { + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { gen_ivtbl_mark(ivtbl); } } @@ -1182,11 +1072,35 @@ rb_generic_ivar_memsize(VALUE obj) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) return gen_ivtbl_bytes(ivtbl->numiv); return 0; } +#if !SHAPE_IN_BASIC_FLAGS +MJIT_FUNC_EXPORTED shape_id_t +rb_generic_shape_id(VALUE obj) +{ + struct gen_ivtbl *ivtbl = 0; + shape_id_t shape_id = 0; + + RB_VM_LOCK_ENTER(); + { + st_table* global_iv_table = generic_ivtbl(obj, 0, false); + + if (global_iv_table && st_lookup(global_iv_table, obj, (st_data_t *)&ivtbl)) { + shape_id = ivtbl->shape_id; + } + else if (OBJ_FROZEN(obj)) { + shape_id = FROZEN_ROOT_SHAPE_ID; + } + } + RB_VM_LOCK_LEAVE(); + + return shape_id; +} +#endif + static size_t gen_ivtbl_count(const struct gen_ivtbl *ivtbl) { @@ -1254,23 +1168,16 @@ VALUE rb_ivar_lookup(VALUE obj, ID id, VALUE undef) { if (SPECIAL_CONST_P(obj)) return undef; + + shape_id_t shape_id; + VALUE * ivar_list; + rb_shape_t * shape; + +#if SHAPE_IN_BASIC_FLAGS + shape_id = RBASIC_SHAPE_ID(obj); +#endif + switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - { - uint32_t index; - uint32_t len = ROBJECT_NUMIV(obj); - VALUE *ptr = ROBJECT_IVPTR(obj); - VALUE val; - - if (iv_index_tbl_lookup(ROBJECT_IV_INDEX_TBL(obj), id, &index) && - index < len && - (val = ptr[index]) != Qundef) { - return val; - } - else { - break; - } - } case T_CLASS: case T_MODULE: { @@ -1287,14 +1194,37 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) return val; } else { - break; + return undef; } } + case T_OBJECT: + { +#if !SHAPE_IN_BASIC_FLAGS + shape_id = ROBJECT_SHAPE_ID(obj); +#endif + ivar_list = ROBJECT_IVPTR(obj); + break; + } default: - if (FL_TEST(obj, FL_EXIVAR)) - return generic_ivar_get(obj, id, undef); + if (FL_TEST_RAW(obj, FL_EXIVAR)) { + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); +#if !SHAPE_IN_BASIC_FLAGS + shape_id = ivtbl->shape_id; +#endif + ivar_list = ivtbl->ivptr; + } else { + return undef; + } break; } + + attr_index_t index = 0; + shape = rb_shape_get_shape_by_id(shape_id); + if (rb_shape_get_iv_index(shape, id, &index)) { + return ivar_list[index]; + } + return undef; } @@ -1315,26 +1245,12 @@ rb_attr_get(VALUE obj, ID id) static VALUE rb_ivar_delete(VALUE obj, ID id, VALUE undef) { - VALUE *ptr; - struct st_table *iv_index_tbl; - uint32_t len, index; - rb_check_frozen(obj); - switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - len = ROBJECT_NUMIV(obj); - ptr = ROBJECT_IVPTR(obj); - iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (iv_index_tbl_lookup(iv_index_tbl, id, &index) && - index < len) { - VALUE val = ptr[index]; - ptr[index] = Qundef; - if (val != Qundef) { - return val; - } - } - break; + VALUE val = Qnil; + attr_index_t index; + + switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id); @@ -1345,11 +1261,33 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) } } break; - default: - if (FL_TEST(obj, FL_EXIVAR)) - return generic_ivar_delete(obj, id, undef); + case T_OBJECT: { + rb_shape_t * shape = rb_shape_get_shape(obj); + if (rb_shape_get_iv_index(shape, id, &index)) { + rb_shape_transition_shape_remove_ivar(obj, id, shape); + val = ROBJECT_IVPTR(obj)[index]; + ROBJECT_IVPTR(obj)[index] = Qundef; + return val; + } + break; + } + default: { + rb_shape_t * shape = rb_shape_get_shape(obj); + + if (rb_shape_get_iv_index(shape, id, &index)) { + rb_shape_transition_shape_remove_ivar(obj, id, shape); + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); + val = ivtbl->ivptr[index]; + ivtbl->ivptr[index] = Qundef; + return val; + } + + break; + } } + return undef; } @@ -1359,67 +1297,31 @@ rb_attr_delete(VALUE obj, ID id) return rb_ivar_delete(obj, id, Qnil); } -static st_table * -iv_index_tbl_make(VALUE obj, VALUE klass) -{ - st_table *iv_index_tbl; - - if (UNLIKELY(!klass)) { - rb_raise(rb_eTypeError, "hidden object cannot have instance variables"); - } - - if ((iv_index_tbl = RCLASS_IV_INDEX_TBL(klass)) == NULL) { - RB_VM_LOCK_ENTER(); - if ((iv_index_tbl = RCLASS_IV_INDEX_TBL(klass)) == NULL) { - iv_index_tbl = RCLASS_IV_INDEX_TBL(klass) = st_init_numtable(); - } - RB_VM_LOCK_LEAVE(); - } - - return iv_index_tbl; -} - -static void -iv_index_tbl_extend(struct ivar_update *ivup, ID id, VALUE klass) -{ - ASSERT_vm_locking(); - st_data_t ent_data; - struct rb_iv_index_tbl_entry *ent; - - if (st_lookup(ivup->u.iv_index_tbl, (st_data_t)id, &ent_data)) { - ent = (void *)ent_data; - ivup->index = ent->index; - return; - } - if (ivup->u.iv_index_tbl->num_entries >= INT_MAX) { - rb_raise(rb_eArgError, "too many instance variables"); - } - ent = ALLOC(struct rb_iv_index_tbl_entry); - ent->index = ivup->index = (uint32_t)ivup->u.iv_index_tbl->num_entries; - ent->class_value = klass; - ent->class_serial = RCLASS_SERIAL(klass); - st_add_direct(ivup->u.iv_index_tbl, (st_data_t)id, (st_data_t)ent); - ivup->iv_extended = 1; -} - static void generic_ivar_set(VALUE obj, ID id, VALUE val) { - VALUE klass = rb_obj_class(obj); struct ivar_update ivup; - ivup.iv_extended = 0; - ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass); + // The returned shape will have `id` in its iv_table + rb_shape_t * shape = rb_shape_get_next(rb_shape_get_shape(obj), obj, id); + ivup.shape = shape; RB_VM_LOCK_ENTER(); { - iv_index_tbl_extend(&ivup, id, klass); - st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update, - (st_data_t)&ivup); + attr_index_t ent_data; + if (rb_shape_get_iv_index(shape, id, &ent_data)) { + ivup.iv_index = (uint32_t) ent_data; + } + else { + rb_bug("unreachable. Shape was not found for id: %s", rb_id2name(id)); + } + + st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update, (st_data_t)&ivup); } RB_VM_LOCK_LEAVE(); - ivup.u.ivtbl->ivptr[ivup.index] = val; + ivup.ivtbl->ivptr[ivup.iv_index] = val; + rb_shape_set_shape(obj, shape); RB_OBJ_WRITTEN(obj, Qundef, val); } @@ -1486,8 +1388,8 @@ rb_obj_transient_heap_evacuate(VALUE obj, int promote) } #endif -static void -init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl) +void +rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize) { VALUE *ptr = ROBJECT_IVPTR(obj); VALUE *newptr; @@ -1510,35 +1412,34 @@ init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl) #else ROBJECT(obj)->as.heap.numiv = newsize; #endif - ROBJECT(obj)->as.heap.iv_index_tbl = index_tbl; -} - -void -rb_init_iv_list(VALUE obj) -{ - st_table *index_tbl = ROBJECT_IV_INDEX_TBL(obj); - uint32_t newsize = (uint32_t)index_tbl->num_entries; - uint32_t len = ROBJECT_NUMIV(obj); - init_iv_list(obj, len, newsize, index_tbl); } -// Retrieve or create the id-to-index mapping for a given object and an -// instance variable name. -static struct ivar_update -obj_ensure_iv_index_mapping(VALUE obj, ID id) +struct gen_ivtbl * +rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize) { - VALUE klass = rb_obj_class(obj); - struct ivar_update ivup; - ivup.iv_extended = 0; - ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass); + struct gen_ivtbl * ivtbl = 0; RB_VM_LOCK_ENTER(); { - iv_index_tbl_extend(&ivup, id, klass); + if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->numiv)) { + ivtbl = gen_ivtbl_resize(ivtbl, newsize); + st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)obj, (st_data_t)ivtbl); + FL_SET_RAW(obj, FL_EXIVAR); + } } RB_VM_LOCK_LEAVE(); - return ivup; + RUBY_ASSERT(ivtbl); + + return ivtbl; +} + +void +rb_init_iv_list(VALUE obj) +{ + uint32_t newsize = rb_shape_get_shape(obj)->iv_count * 2.0; + uint32_t len = ROBJECT_NUMIV(obj); + rb_ensure_iv_list_size(obj, len, newsize < len ? len : newsize); } // Return the instance variable index for a given name and T_OBJECT object. The @@ -1552,26 +1453,108 @@ uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id) { RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); - // This uint32_t cast shouldn't lose information as it's checked in - // iv_index_tbl_extend(). The index is stored as an uint32_t in - // struct rb_iv_index_tbl_entry. - return (uint32_t)obj_ensure_iv_index_mapping(obj, id).index; + attr_index_t index; + + // Ensure there is a transition for IVAR +id+ + rb_shape_transition_shape(obj, id, rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))); + + // Get the current shape + rb_shape_t * shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); + + if (!rb_shape_get_iv_index(shape, id, &index)) { + rb_bug("unreachable. Shape was not found for id: %s", rb_id2name(id)); + } + + uint32_t len = ROBJECT_NUMIV(obj); + if (len <= index) { + uint32_t newsize = (shape->iv_count + 1) * 1.25; + rb_ensure_iv_list_size(obj, len, newsize); + } + RUBY_ASSERT(index <= ROBJECT_NUMIV(obj)); + return index; } static VALUE obj_ivar_set(VALUE obj, ID id, VALUE val) { - uint32_t len; - struct ivar_update ivup = obj_ensure_iv_index_mapping(obj, id); + attr_index_t index = rb_obj_ensure_iv_index_mapping(obj, id); + RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], val); + return val; +} + +/* Set the instance variable +val+ on object +obj+ at ivar name +id+. + * This function only works with T_OBJECT objects, so make sure + * +obj+ is of type T_OBJECT before using this function. + */ +VALUE +rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val) +{ + rb_check_frozen_internal(obj); + obj_ivar_set(obj, id, val); + return val; +} - len = ROBJECT_NUMIV(obj); - if (len <= ivup.index) { - uint32_t newsize = iv_index_tbl_newsize(&ivup); - init_iv_list(obj, len, newsize, ivup.u.iv_index_tbl); +bool +rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id) +{ + if (rb_shape_get_shape_id(obj) == shape_id) { + return false; } - RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[ivup.index], val); - return val; +#if SHAPE_IN_BASIC_FLAGS + RBASIC_SET_SHAPE_ID(obj, shape_id); +#else + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + ROBJECT_SET_SHAPE_ID(obj, shape_id); + break; + case T_CLASS: + case T_MODULE: + { + RCLASS_EXT(obj)->shape_id = shape_id; + break; + } + default: + { + if (shape_id != FROZEN_ROOT_SHAPE_ID) { + struct gen_ivtbl *ivtbl = 0; + RB_VM_LOCK_ENTER(); + { + st_table* global_iv_table = generic_ivtbl(obj, 0, false); + + if (st_lookup(global_iv_table, obj, (st_data_t *)&ivtbl)) { + ivtbl->shape_id = shape_id; + } + else { + rb_bug("Expected shape_id entry in global iv table"); + } + } + RB_VM_LOCK_LEAVE(); + } + } + } +#endif + + return true; +} + +/** + * Prevents further modifications to the given object. ::rb_eFrozenError shall + * be raised if modification is attempted. + * + * @param[out] x Object in question. + */ +void rb_obj_freeze_inline(VALUE x) +{ + if (RB_FL_ABLE(x)) { + RB_OBJ_FREEZE_RAW(x); + + rb_shape_transition_shape_frozen(x); + + if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) { + rb_freeze_singleton_class(x); + } + } } static void @@ -1581,10 +1564,14 @@ ivar_set(VALUE obj, ID id, VALUE val) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - obj_ivar_set(obj, id, val); - break; + { + obj_ivar_set(obj, id, val); + break; + } case T_CLASS: case T_MODULE: + // TODO: Transition shapes on classes + //rb_shape_transition_shape(obj, id, rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(obj))); IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id); rb_class_ivar_set(obj, id, val); break; @@ -1614,161 +1601,86 @@ rb_ivar_set_internal(VALUE obj, ID id, VALUE val) VALUE rb_ivar_defined(VALUE obj, ID id) { - VALUE val; - struct st_table *iv_index_tbl; - uint32_t index; + attr_index_t index; if (SPECIAL_CONST_P(obj)) return Qfalse; switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (iv_index_tbl_lookup(iv_index_tbl, id, &index) && - index < ROBJECT_NUMIV(obj) && - (val = ROBJECT_IVPTR(obj)[index]) != Qundef) { - return Qtrue; - } - break; case T_CLASS: case T_MODULE: - if (RCLASS_IV_TBL(obj) && lock_st_is_member(RCLASS_IV_TBL(obj), (st_data_t)id)) + if (RCLASS_IV_TBL(obj) && lock_st_is_member(RCLASS_IV_TBL(obj), (st_data_t)id)) { return Qtrue; - break; + } + else { + return Qfalse; + } default: - if (FL_TEST(obj, FL_EXIVAR)) - return generic_ivar_defined(obj, id); - break; + return RBOOL(rb_shape_get_iv_index(rb_shape_get_shape(obj), id, &index)); } - return Qfalse; } typedef int rb_ivar_foreach_callback_func(ID key, VALUE val, st_data_t arg); st_data_t rb_st_nth_key(st_table *tab, st_index_t index); -static ID -iv_index_tbl_nth_id(st_table *iv_index_tbl, uint32_t index) -{ - st_data_t key; - RB_VM_LOCK_ENTER(); - { - key = rb_st_nth_key(iv_index_tbl, index); - } - RB_VM_LOCK_LEAVE(); - return (ID)key; -} - -static inline bool -ivar_each_i(st_table *iv_index_tbl, VALUE val, uint32_t i, rb_ivar_foreach_callback_func *func, st_data_t arg) -{ - if (val != Qundef) { - ID id = iv_index_tbl_nth_id(iv_index_tbl, i); - switch (func(id, val, arg)) { - case ST_CHECK: - case ST_CONTINUE: - break; - case ST_STOP: - return true; - default: - rb_bug("unreachable"); - } +static void +iterate_over_shapes_with_callback(rb_shape_t *shape, VALUE* iv_list, rb_ivar_foreach_callback_func *callback, st_data_t arg) { + switch ((enum shape_type)shape->type) { + case SHAPE_ROOT: + return; + case SHAPE_IVAR: + iterate_over_shapes_with_callback(shape->parent, iv_list, callback, arg); + VALUE val = iv_list[shape->iv_count - 1]; + if (val != Qundef) { + callback(shape->edge_name, val, arg); + } + return; + case SHAPE_IVAR_UNDEF: + case SHAPE_FROZEN: + iterate_over_shapes_with_callback(shape->parent, iv_list, callback, arg); + return; } - return false; } static void obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) { - st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (!iv_index_tbl) return; - uint32_t i=0; - - for (i=0; i < ROBJECT_NUMIV(obj); i++) { - VALUE val = ROBJECT_IVPTR(obj)[i]; - if (ivar_each_i(iv_index_tbl, val, i, func, arg)) { - return; - } - } + rb_shape_t* shape = rb_shape_get_shape(obj); + iterate_over_shapes_with_callback(shape, ROBJECT_IVPTR(obj), func, arg); } static void gen_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) { + rb_shape_t *shape = rb_shape_get_shape(obj); struct gen_ivtbl *ivtbl; - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - if (!iv_index_tbl) return; - if (!gen_ivtbl_get(obj, 0, &ivtbl)) return; + if (!rb_gen_ivtbl_get(obj, 0, &ivtbl)) return; - for (uint32_t i=0; inumiv; i++) { - VALUE val = ivtbl->ivptr[i]; - if (ivar_each_i(iv_index_tbl, val, i, func, arg)) { - return; - } - } -} - -struct givar_copy { - VALUE obj; - VALUE klass; - st_table *iv_index_tbl; - struct gen_ivtbl *ivtbl; -}; - -static int -gen_ivar_copy(ID id, VALUE val, st_data_t arg) -{ - struct givar_copy *c = (struct givar_copy *)arg; - struct ivar_update ivup; - - ivup.iv_extended = 0; - ivup.u.iv_index_tbl = c->iv_index_tbl; - - RB_VM_LOCK_ENTER(); - { - iv_index_tbl_extend(&ivup, id, c->klass); - } - RB_VM_LOCK_LEAVE(); - - if (ivup.index >= c->ivtbl->numiv) { - uint32_t newsize = iv_index_tbl_newsize(&ivup); - c->ivtbl = gen_ivtbl_resize(c->ivtbl, newsize); - } - c->ivtbl->ivptr[ivup.index] = val; - - RB_OBJ_WRITTEN(c->obj, Qundef, val); - - return ST_CONTINUE; + iterate_over_shapes_with_callback(shape, ivtbl->ivptr, func, arg); } void rb_copy_generic_ivar(VALUE clone, VALUE obj) { - struct gen_ivtbl *ivtbl; + struct gen_ivtbl *obj_ivtbl; + struct gen_ivtbl *new_ivtbl; rb_check_frozen(clone); if (!FL_TEST(obj, FL_EXIVAR)) { goto clear; } - if (gen_ivtbl_get(obj, 0, &ivtbl)) { - struct givar_copy c; - uint32_t i; - if (gen_ivtbl_count(ivtbl) == 0) + if (rb_gen_ivtbl_get(obj, 0, &obj_ivtbl)) { + if (gen_ivtbl_count(obj_ivtbl) == 0) goto clear; - if (gen_ivtbl_get(clone, 0, &c.ivtbl)) { - for (i = 0; i < c.ivtbl->numiv; i++) - c.ivtbl->ivptr[i] = Qundef; - } - else { - c.ivtbl = gen_ivtbl_resize(0, ivtbl->numiv); - FL_SET(clone, FL_EXIVAR); + new_ivtbl = gen_ivtbl_resize(0, obj_ivtbl->numiv); + FL_SET(clone, FL_EXIVAR); + + for (uint32_t i=0; inumiv; i++) { + new_ivtbl->ivptr[i] = obj_ivtbl->ivptr[i]; + RB_OBJ_WRITTEN(clone, Qundef, &new_ivtbl[i]); } - VALUE klass = rb_obj_class(clone); - c.iv_index_tbl = iv_index_tbl_make(clone, klass); - c.obj = clone; - c.klass = klass; - gen_ivar_each(obj, gen_ivar_copy, (st_data_t)&c); /* * c.ivtbl may change in gen_ivar_copy due to realloc, * no need to free @@ -1776,9 +1688,17 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj) RB_VM_LOCK_ENTER(); { generic_ivtbl_no_ractor_check(clone); - st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)clone, (st_data_t)c.ivtbl); + st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)clone, (st_data_t)new_ivtbl); } RB_VM_LOCK_LEAVE(); + + rb_shape_t * obj_shape = rb_shape_get_shape(obj); + if (rb_shape_frozen_shape_p(obj_shape)) { + rb_shape_set_shape(clone, obj_shape->parent); + } + else { + rb_shape_set_shape(clone, obj_shape); + } } return; @@ -1846,7 +1766,7 @@ rb_ivar_count(VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - if (ROBJECT_IV_INDEX_TBL(obj) != 0) { + if (rb_shape_get_shape(obj)->iv_count > 0) { st_index_t i, count, num = ROBJECT_NUMIV(obj); const VALUE *const ivptr = ROBJECT_IVPTR(obj); for (i = count = 0; i < num; ++i) { @@ -1867,7 +1787,7 @@ rb_ivar_count(VALUE obj) if (FL_TEST(obj, FL_EXIVAR)) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) { + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { return gen_ivtbl_count(ivtbl); } } @@ -1965,40 +1885,53 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name) { VALUE val = Qnil; const ID id = id_for_var(obj, name, an, instance); - st_data_t n, v; - struct st_table *iv_index_tbl; - uint32_t index; + // Frozen check comes here because it's expected that we raise a + // NameError (from the id_for_var check) before we raise a FrozenError rb_check_frozen(obj); + + attr_index_t index; + if (!id) { goto not_defined; } switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (iv_index_tbl_lookup(iv_index_tbl, id, &index) && - index < ROBJECT_NUMIV(obj) && - (val = ROBJECT_IVPTR(obj)[index]) != Qundef) { - ROBJECT_IVPTR(obj)[index] = Qundef; - return val; - } - break; case T_CLASS: case T_MODULE: IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id); - n = id; - if (RCLASS_IV_TBL(obj) && lock_st_delete(RCLASS_IV_TBL(obj), &n, &v)) { - return (VALUE)v; + if (RCLASS_IV_TBL(obj)) { + st_data_t id_data = (st_data_t)id, val; + if (lock_st_delete(RCLASS_IV_TBL(obj), &id_data, &val)) { + return (VALUE)val; + } } break; - default: - if (FL_TEST(obj, FL_EXIVAR)) { - if (generic_ivar_remove(obj, id, &val)) { - return val; - } + case T_OBJECT: { + rb_shape_t * shape = rb_shape_get_shape(obj); + if (rb_shape_get_iv_index(shape, id, &index)) { + rb_shape_transition_shape_remove_ivar(obj, id, shape); + val = ROBJECT_IVPTR(obj)[index]; + ROBJECT_IVPTR(obj)[index] = Qundef; + return val; } + + break; + } + default: { + rb_shape_t * shape = rb_shape_get_shape(obj); + + if (rb_shape_get_iv_index(shape, id, &index)) { + rb_shape_transition_shape_remove_ivar(obj, id, shape); + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); + val = ivtbl->ivptr[index]; + ivtbl->ivptr[index] = Qundef; + return val; + } + break; + } } not_defined: diff --git a/variable.h b/variable.h index 55596b00de6213..314ac82df0dbc9 100644 --- a/variable.h +++ b/variable.h @@ -11,11 +11,19 @@ /* per-object */ struct gen_ivtbl { +#if !SHAPE_IN_BASIC_FLAGS + uint16_t shape_id; +#endif uint32_t numiv; VALUE ivptr[FLEX_ARY_LEN]; }; int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **); -VALUE rb_ivar_generic_lookup_with_index(VALUE obj, ID id, uint32_t index); + +#include "shape.h" +#if !SHAPE_IN_BASIC_FLAGS +shape_id_t rb_generic_shape_id(VALUE obj); +#endif + #endif /* RUBY_TOPLEVEL_VARIABLE_H */ diff --git a/vm.c b/vm.c index 0de461392f398b..a52d3efa7b2dc7 100644 --- a/vm.c +++ b/vm.c @@ -26,6 +26,7 @@ #include "internal/thread.h" #include "internal/vm.h" #include "internal/sanitizers.h" +#include "internal/variable.h" #include "iseq.h" #include "mjit.h" #include "yjit.h" @@ -4021,6 +4022,11 @@ Init_BareVM(void) rb_native_cond_initialize(&vm->ractor.sync.terminate_cond); } +#ifndef _WIN32 +#include +#include +#endif + void Init_vm_objects(void) { @@ -4032,6 +4038,31 @@ Init_vm_objects(void) vm->mark_object_ary = rb_ary_hidden_new(128); vm->loading_table = st_init_strtable(); vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000); + +#if HAVE_MMAP + vm->shape_list = (rb_shape_t *)mmap(NULL, rb_size_mul_or_raise(SHAPE_BITMAP_SIZE * 32, sizeof(rb_shape_t), rb_eRuntimeError), + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (vm->shape_list == MAP_FAILED) { + vm->shape_list = 0; + } +#else + vm->shape_list = xcalloc(SHAPE_BITMAP_SIZE * 32, sizeof(rb_shape_t)); +#endif + + if (!vm->shape_list) { + rb_memerror(); + } + + // Root shape + vm->root_shape = rb_shape_alloc(0, 0); + RUBY_ASSERT(rb_shape_id(vm->root_shape) == ROOT_SHAPE_ID); + + // Frozen root shape + vm->frozen_root_shape = rb_shape_alloc(rb_make_internal_id(), vm->root_shape); + vm->frozen_root_shape->type = (uint8_t)SHAPE_FROZEN; + RUBY_ASSERT(rb_shape_id(vm->frozen_root_shape) == FROZEN_ROOT_SHAPE_ID); + + vm->next_shape_id = 2; } /* Stub for builtin function when not building YJIT units*/ diff --git a/vm_callinfo.h b/vm_callinfo.h index fd2215be7dd7af..e5b04c0709e883 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -10,6 +10,7 @@ #include "debug_counter.h" #include "internal/class.h" +#include "shape.h" enum vm_call_flag_bits { VM_CALL_ARGS_SPLAT_bit, /* m(*args) */ @@ -284,14 +285,32 @@ struct rb_callcache { const vm_call_handler call_; union { - const unsigned int attr_index; + struct { + const attr_index_t index; + shape_id_t dest_shape_id; + } attr; const enum method_missing_reason method_missing_reason; /* used by method_missing */ VALUE v; } aux_; }; -#define VM_CALLCACHE_UNMARKABLE IMEMO_FL_USER0 -#define VM_CALLCACHE_ON_STACK IMEMO_FL_USER1 +#define VM_CALLCACHE_UNMARKABLE FL_FREEZE +#define VM_CALLCACHE_ON_STACK FL_EXIVAR + +extern const struct rb_callcache *rb_vm_empty_cc(void); +extern const struct rb_callcache *rb_vm_empty_cc_for_super(void); + +#define vm_cc_empty() rb_vm_empty_cc() + +static inline void +vm_cc_attr_index_initialize(const struct rb_callcache *cc, shape_id_t shape_id) +{ + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + VM_ASSERT(cc != vm_cc_empty()); + IMEMO_SET_CACHED_SHAPE_ID((VALUE)cc, shape_id); + *(attr_index_t *)&cc->aux_.attr.index = 0; + *(shape_id_t *)&cc->aux_.attr.dest_shape_id = shape_id; +} static inline const struct rb_callcache * vm_cc_new(VALUE klass, @@ -299,6 +318,7 @@ vm_cc_new(VALUE klass, vm_call_handler call) { const struct rb_callcache *cc = (const struct rb_callcache *)rb_imemo_new(imemo_callcache, (VALUE)cme, (VALUE)call, 0, klass); + vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); RB_DEBUG_COUNTER_INC(cc_new); return cc; } @@ -350,30 +370,71 @@ vm_cc_call(const struct rb_callcache *cc) return cc->call_; } -static inline unsigned int +static inline attr_index_t vm_cc_attr_index(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - return cc->aux_.attr_index - 1; + return cc->aux_.attr.index - 1; } static inline bool vm_cc_attr_index_p(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - return cc->aux_.attr_index > 0; + return cc->aux_.attr.index != 0; +} + +static inline shape_id_t +vm_cc_attr_index_source_shape_id(const struct rb_callcache *cc) +{ + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + + return IMEMO_CACHED_SHAPE_ID((VALUE)cc); } -static inline uint32_t -vm_ic_entry_index(const struct iseq_inline_iv_cache_entry *ic) +static inline shape_id_t +vm_cc_attr_shape_id(const struct rb_callcache *cc) { - return ic->entry->index; + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + return vm_cc_attr_index_source_shape_id(cc); +} + +static inline shape_id_t +vm_cc_attr_index_dest_shape_id(const struct rb_callcache *cc) +{ + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + + return cc->aux_.attr.dest_shape_id; +} + +static inline attr_index_t +vm_ic_attr_index(const struct iseq_inline_iv_cache_entry *ic) +{ + return ic->attr_index - 1; } static inline bool -vm_ic_entry_p(const struct iseq_inline_iv_cache_entry *ic) +vm_ic_attr_index_p(const struct iseq_inline_iv_cache_entry *ic) +{ + return ic->attr_index > 0; +} + +static inline shape_id_t +vm_ic_attr_shape_id(const struct iseq_inline_iv_cache_entry *ic) +{ + return ic->source_shape_id; +} + +static inline shape_id_t +vm_ic_attr_index_source_shape_id(const struct iseq_inline_iv_cache_entry *ic) { - return ic->entry; + return ic->source_shape_id; +} + +static inline shape_id_t +vm_ic_attr_index_dest_shape_id(const struct iseq_inline_iv_cache_entry *ic) +{ + return ic->dest_shape_id; } static inline unsigned int @@ -407,10 +468,6 @@ vm_cc_valid_p(const struct rb_callcache *cc, const rb_callable_method_entry_t *c } } -extern const struct rb_callcache *rb_vm_empty_cc(void); -extern const struct rb_callcache *rb_vm_empty_cc_for_super(void); -#define vm_cc_empty() rb_vm_empty_cc() - /* callcache: mutate */ static inline void @@ -422,26 +479,29 @@ vm_cc_call_set(const struct rb_callcache *cc, vm_call_handler call) } static inline void -vm_cc_attr_index_set(const struct rb_callcache *cc, int index) +vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id_t source_shape_id, shape_id_t dest_shape_id) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(cc != vm_cc_empty()); - *(int *)&cc->aux_.attr_index = index + 1; + IMEMO_SET_CACHED_SHAPE_ID((VALUE)cc, source_shape_id); + *(attr_index_t *)&cc->aux_.attr.index = (index + 1); + *(shape_id_t *)&cc->aux_.attr.dest_shape_id = dest_shape_id; } static inline void -vm_ic_entry_set(struct iseq_inline_iv_cache_entry *ic, struct rb_iv_index_tbl_entry *entry, const rb_iseq_t *iseq) +vm_ic_attr_index_set(const rb_iseq_t *iseq, const struct iseq_inline_iv_cache_entry *ic, attr_index_t index, shape_id_t source_shape_id, shape_id_t dest_shape_id) { - ic->entry = entry; - RB_OBJ_WRITTEN(iseq, Qundef, entry->class_value); + *(shape_id_t *)&ic->source_shape_id = source_shape_id; + *(shape_id_t *)&ic->dest_shape_id = dest_shape_id; + *(attr_index_t *)&ic->attr_index = index + 1; } static inline void -vm_cc_attr_index_initialize(const struct rb_callcache *cc) +vm_ic_attr_index_initialize(const struct iseq_inline_iv_cache_entry *ic, shape_id_t shape_id) { - VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - VM_ASSERT(cc != vm_cc_empty()); - *(int *)&cc->aux_.attr_index = 0; + *(shape_id_t *)&ic->source_shape_id = shape_id; + *(shape_id_t *)&ic->dest_shape_id = shape_id; + *(attr_index_t *)&ic->attr_index = 0; } static inline void diff --git a/vm_core.h b/vm_core.h index e11838a59a3aaa..4dd873c1f87df4 100644 --- a/vm_core.h +++ b/vm_core.h @@ -99,6 +99,7 @@ extern int ruby_assert_critical_section_entered; #include "ruby/st.h" #include "ruby_atomic.h" #include "vm_opts.h" +#include "shape.h" #include "ruby/thread_native.h" @@ -272,7 +273,9 @@ struct iseq_inline_constant_cache { }; struct iseq_inline_iv_cache_entry { - struct rb_iv_index_tbl_entry *entry; + shape_id_t source_shape_id; + shape_id_t dest_shape_id; + attr_index_t attr_index; }; struct iseq_inline_cvar_cache_entry { @@ -687,6 +690,12 @@ typedef struct rb_vm_struct { VALUE mark_object_ary; const VALUE special_exceptions[ruby_special_error_count]; + /* object shapes */ + rb_shape_t *shape_list; + rb_shape_t *root_shape; + rb_shape_t *frozen_root_shape; + shape_id_t next_shape_id; + /* load */ VALUE top_self; VALUE load_path; diff --git a/vm_eval.c b/vm_eval.c index a71688c6bc07fb..eddc6f80da7cfc 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -48,7 +48,7 @@ rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE { struct rb_calling_info calling = { .ci = &VM_CI_ON_STACK(id, kw_splat ? VM_CALL_KW_SPLAT : 0, argc, NULL), - .cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, { 0 }, cme), + .cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme), .block_handler = vm_passed_block_handler(ec), .recv = recv, .argc = argc, @@ -90,7 +90,7 @@ vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE static VALUE vm_call0_cme(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, const rb_callable_method_entry_t *cme) { - calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, { 0 }, cme); + calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme); return vm_call0_body(ec, calling, argv); } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 03796d2ad197c9..b8cb8c1fdd873f 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -50,6 +50,11 @@ MJIT_STATIC VALUE ruby_vm_special_exception_copy(VALUE exc) { VALUE e = rb_obj_alloc(rb_class_real(RBASIC_CLASS(exc))); + rb_shape_t * shape = rb_shape_get_shape(exc); + if (rb_shape_frozen_shape_p(shape)) { + shape = shape->parent; + } + rb_shape_set_shape(e, shape); rb_obj_copy_ivar(e, exc); return e; } @@ -1086,35 +1091,17 @@ vm_get_cvar_base(const rb_cref_t *cref, const rb_control_frame_t *cfp, int top_l return klass; } -static bool -iv_index_tbl_lookup(struct st_table *iv_index_tbl, ID id, struct rb_iv_index_tbl_entry **ent) -{ - int found; - st_data_t ent_data; - - if (iv_index_tbl == NULL) return false; - - RB_VM_LOCK_ENTER(); - { - found = st_lookup(iv_index_tbl, (st_data_t)id, &ent_data); - } - RB_VM_LOCK_LEAVE(); - if (found) *ent = (struct rb_iv_index_tbl_entry *)ent_data; - - return found ? true : false; -} - -ALWAYS_INLINE(static void fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, struct rb_iv_index_tbl_entry *ent)); - +ALWAYS_INLINE(static void fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, attr_index_t index, shape_id_t shape_id)); static inline void -fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, struct rb_iv_index_tbl_entry *ent) +fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, attr_index_t index, shape_id_t shape_id) { - // fill cache - if (!is_attr) { - vm_ic_entry_set(ic, ent, iseq); + if (is_attr) { + if (vm_cc_markable(cc)) { + vm_cc_attr_index_set(cc, index, shape_id, shape_id); + } } else { - vm_cc_attr_index_set(cc, ent->index); + vm_ic_attr_index_set(iseq, ic, index, shape_id, shape_id); } } @@ -1129,68 +1116,120 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call { #if OPT_IC_FOR_IVAR VALUE val = Qundef; + shape_id_t shape_id; + VALUE * ivar_list; if (SPECIAL_CONST_P(obj)) { - // frozen? + return Qnil; } - else if (LIKELY(is_attr ? - RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_unset, vm_cc_attr_index_p(cc)) : - RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_serial, vm_ic_entry_p(ic) && ic->entry->class_serial == RCLASS_SERIAL(RBASIC(obj)->klass)))) { - uint32_t index = !is_attr ? vm_ic_entry_index(ic): (vm_cc_attr_index(cc)); - RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); +#if SHAPE_IN_BASIC_FLAGS + shape_id = RBASIC_SHAPE_ID(obj); +#endif - if (LIKELY(BUILTIN_TYPE(obj) == T_OBJECT) && - LIKELY(index < ROBJECT_NUMIV(obj))) { - val = ROBJECT_IVPTR(obj)[index]; + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + ivar_list = ROBJECT_IVPTR(obj); + VM_ASSERT(rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); - VM_ASSERT(ractor_object_incidental_shareable_p(obj, val)); - } - else if (FL_TEST_RAW(obj, FL_EXIVAR)) { - val = rb_ivar_generic_lookup_with_index(obj, id, index); - } +#if !SHAPE_IN_BASIC_FLAGS + shape_id = ROBJECT_SHAPE_ID(obj); +#endif + break; + case T_CLASS: + case T_MODULE: + { + goto general_path; + } + default: + if (FL_TEST_RAW(obj, FL_EXIVAR)) { + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); +#if !SHAPE_IN_BASIC_FLAGS + shape_id = ivtbl->shape_id; +#endif + ivar_list = ivtbl->ivptr; + } else { + return Qnil; + } + } + + shape_id_t cached_id; - goto ret; + if (is_attr) { + cached_id = vm_cc_attr_shape_id(cc); } else { - struct rb_iv_index_tbl_entry *ent; - - if (BUILTIN_TYPE(obj) == T_OBJECT) { - struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); + cached_id = vm_ic_attr_shape_id(ic); + } - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &ent)) { - fill_ivar_cache(iseq, ic, cc, is_attr, ent); + attr_index_t index; - // get value - if (ent->index < ROBJECT_NUMIV(obj)) { - val = ROBJECT_IVPTR(obj)[ent->index]; + if (LIKELY(cached_id == shape_id)) { + RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); - VM_ASSERT(ractor_object_incidental_shareable_p(obj, val)); - } - } + if (is_attr && vm_cc_attr_index_p(cc)) { + index = vm_cc_attr_index(cc); + } + else if (!is_attr && vm_ic_attr_index_p(ic)) { + index = vm_ic_attr_index(ic); + } + else { + return Qnil; } - else if (FL_TEST_RAW(obj, FL_EXIVAR)) { - struct st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &ent)) { - fill_ivar_cache(iseq, ic, cc, is_attr, ent); - val = rb_ivar_generic_lookup_with_index(obj, id, ent->index); + val = ivar_list[index]; + VM_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT && rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); + } + else { // cache miss case +#if RUBY_DEBUG + if (is_attr) { + if (cached_id != INVALID_SHAPE_ID) { + RB_DEBUG_COUNTER_INC(ivar_get_cc_miss_set); + } else { + RB_DEBUG_COUNTER_INC(ivar_get_cc_miss_unset); } } else { - // T_CLASS / T_MODULE - goto general_path; + if (cached_id != INVALID_SHAPE_ID) { + RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_set); + } else { + RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_unset); + } } +#endif - ret: - if (LIKELY(val != Qundef)) { - return val; + attr_index_t index; + rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id); + + if (rb_shape_get_iv_index(shape, id, &index)) { + // This fills in the cache with the shared cache object. + // "ent" is the shared cache object + fill_ivar_cache(iseq, ic, cc, is_attr, index, shape_id); + + // We fetched the ivar list above + val = ivar_list[index]; } else { - return Qnil; + if (is_attr) { + if (vm_cc_markable(cc)) { + vm_cc_attr_index_initialize(cc, shape_id); + } + } + else { + vm_ic_attr_index_initialize(ic, shape_id); + } + + val = Qnil; } + } - general_path: + + RUBY_ASSERT(val != Qundef); + + return val; + +general_path: #endif /* OPT_IC_FOR_IVAR */ RB_DEBUG_COUNTER_INC(ivar_get_ic_miss); @@ -1202,6 +1241,20 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } } +static void +populate_cache(attr_index_t index, shape_id_t shape_id, shape_id_t next_shape_id, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, bool is_attr) +{ + // Cache population code + if (is_attr) { + if (vm_cc_markable(cc)) { + vm_cc_attr_index_set(cc, index, shape_id, next_shape_id); + } + } + else { + vm_ic_attr_index_set(iseq, ic, index, shape_id, next_shape_id); + } +} + ALWAYS_INLINE(static VALUE vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr)); NOINLINE(static VALUE vm_setivar_slowpath_ivar(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic)); NOINLINE(static VALUE vm_setivar_slowpath_attr(VALUE obj, ID id, VALUE val, const struct rb_callcache *cc)); @@ -1209,35 +1262,72 @@ NOINLINE(static VALUE vm_setivar_slowpath_attr(VALUE obj, ID id, VALUE val, cons static VALUE vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr) { - rb_check_frozen_internal(obj); - #if OPT_IC_FOR_IVAR - if (RB_TYPE_P(obj, T_OBJECT)) { - struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - struct rb_iv_index_tbl_entry *ent; + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + { + rb_check_frozen_internal(obj); - if (iv_index_tbl_lookup(iv_index_tbl, id, &ent)) { - if (!is_attr) { - vm_ic_entry_set(ic, ent, iseq); - } - else if (ent->index >= INT_MAX) { - rb_raise(rb_eArgError, "too many instance variables"); - } - else { - vm_cc_attr_index_set(cc, (int)(ent->index)); - } + attr_index_t index; + + uint32_t num_iv = ROBJECT_NUMIV(obj); + rb_shape_t* shape = rb_shape_get_shape(obj); + shape_id_t current_shape_id = ROBJECT_SHAPE_ID(obj); + shape_id_t next_shape_id = current_shape_id; - uint32_t index = ent->index; + rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - rb_init_iv_list(obj); + if (shape != next_shape) { + rb_shape_set_shape(obj, next_shape); + next_shape_id = ROBJECT_SHAPE_ID(obj); + } + + if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree + if (index >= MAX_IVARS) { + rb_raise(rb_eArgError, "too many instance variables"); + } + + populate_cache(index, current_shape_id, next_shape_id, id, iseq, ic, cc, is_attr); + } + else { + rb_bug("Didn't find instance variable %s\n", rb_id2name(id)); + } + + // Ensure the IV buffer is wide enough to store the IV + if (UNLIKELY(index >= num_iv)) { + rb_init_iv_list(obj); + } + + VALUE *ptr = ROBJECT_IVPTR(obj); + RB_OBJ_WRITE(obj, &ptr[index], val); + RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); + + return val; } - VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); - RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); + case T_CLASS: + case T_MODULE: + break; + default: + { + shape_id_t shape_id = rb_shape_get_shape_id(obj); + rb_ivar_set(obj, id, val); + shape_id_t next_shape_id = rb_shape_get_shape_id(obj); + rb_shape_t *next_shape = rb_shape_get_shape_by_id(next_shape_id); + attr_index_t index; + + if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree + if (index >= MAX_IVARS) { + rb_raise(rb_eArgError, "too many instance variables"); + } - return val; - } + populate_cache(index, shape_id, next_shape_id, id, iseq, ic, cc, is_attr); + } + else { + rb_bug("didn't find the id\n"); + } + + return val; + } } #endif RB_DEBUG_COUNTER_INC(ivar_set_ic_miss); @@ -1256,39 +1346,94 @@ vm_setivar_slowpath_attr(VALUE obj, ID id, VALUE val, const struct rb_callcache return vm_setivar_slowpath(obj, id, val, NULL, NULL, cc, true); } +NOINLINE(static VALUE vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index)); +static VALUE +vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index) +{ +#if SHAPE_IN_BASIC_FLAGS + shape_id_t shape_id = RBASIC_SHAPE_ID(obj); +#else + shape_id_t shape_id = rb_generic_shape_id(obj); +#endif + + // Cache hit case + if (shape_id == source_shape_id) { + RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); + + struct gen_ivtbl *ivtbl = 0; + if (dest_shape_id != shape_id) { + ivtbl = rb_ensure_generic_iv_list_size(obj, index + 1); +#if SHAPE_IN_BASIC_FLAGS + RBASIC_SET_SHAPE_ID(obj, dest_shape_id); +#else + ivtbl->shape_id = dest_shape_id; +#endif + } + else { + // Just get the IV table + rb_gen_ivtbl_get(obj, 0, &ivtbl); + } + + VALUE *ptr = ivtbl->ivptr; + + RB_OBJ_WRITE(obj, &ptr[index], val); + + RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); + + return val; + } + + return Qundef; +} + static inline VALUE -vm_setivar(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr) +vm_setivar(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index) { #if OPT_IC_FOR_IVAR - if (LIKELY(RB_TYPE_P(obj, T_OBJECT)) && - LIKELY(!RB_OBJ_FROZEN_RAW(obj))) { + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + { + VM_ASSERT(!rb_ractor_shareable_p(obj) || rb_obj_frozen_p(obj)); + // If object's shape id is the same as the source + // then do the shape transition and write the ivar + // If object's shape id is the same as the dest + // then write the ivar + shape_id_t shape_id = ROBJECT_SHAPE_ID(obj); + + // Do we have a cache hit *and* is the CC intitialized + if (shape_id == source_shape_id) { + RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); + + VM_ASSERT(!rb_ractor_shareable_p(obj)); + + if (dest_shape_id != shape_id) { + if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { + rb_init_iv_list(obj); + } + ROBJECT_SET_SHAPE_ID(obj, dest_shape_id); + } + + RUBY_ASSERT(index < ROBJECT_NUMIV(obj)); - VM_ASSERT(!rb_ractor_shareable_p(obj)); + VALUE *ptr = ROBJECT_IVPTR(obj); - if (LIKELY( - (!is_attr && RB_DEBUG_COUNTER_INC_UNLESS(ivar_set_ic_miss_serial, vm_ic_entry_p(ic) && ic->entry->class_serial == RCLASS_SERIAL(RBASIC(obj)->klass))) || - ( is_attr && RB_DEBUG_COUNTER_INC_UNLESS(ivar_set_ic_miss_unset, vm_cc_attr_index_p(cc))))) { - uint32_t index = !is_attr ? vm_ic_entry_index(ic) : vm_cc_attr_index(cc); + RB_OBJ_WRITE(obj, &ptr[index], val); - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - rb_init_iv_list(obj); + RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); + + return val; + } } - VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); - RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); - return val; /* inline cache hit */ - } - } - else { + break; + case T_CLASS: + case T_MODULE: RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_noobject); + default: + break; } + + return Qundef; #endif /* OPT_IC_FOR_IVAR */ - if (is_attr) { - return vm_setivar_slowpath_attr(obj, id, val, cc); - } - else { - return vm_setivar_slowpath_ivar(obj, id, val, iseq, ic); - } } static VALUE @@ -1383,7 +1528,22 @@ vm_getinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, IVC ic) static inline void vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IVC ic) { - vm_setivar(obj, id, val, iseq, ic, 0, 0); + shape_id_t source_shape_id = vm_ic_attr_index_source_shape_id(ic); + attr_index_t index = vm_ic_attr_index(ic); + shape_id_t dest_shape_id = vm_ic_attr_index_dest_shape_id(ic); + if (UNLIKELY(vm_setivar(obj, id, val, source_shape_id, dest_shape_id, index) == Qundef)) { + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + if (vm_setivar_default(obj, id, val, source_shape_id, dest_shape_id, index) != Qundef) { + return; + } + } + vm_setivar_slowpath_ivar(obj, id, val, iseq, ic); + } } void @@ -1392,28 +1552,6 @@ rb_vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IV vm_setinstancevariable(iseq, obj, id, val, ic); } -/* Set the instance variable +val+ on object +obj+ at the +index+. - * This function only works with T_OBJECT objects, so make sure - * +obj+ is of type T_OBJECT before using this function. - */ -VALUE -rb_vm_set_ivar_idx(VALUE obj, uint32_t index, VALUE val) -{ - RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); - - rb_check_frozen_internal(obj); - - VM_ASSERT(!rb_ractor_shareable_p(obj)); - - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - rb_init_iv_list(obj); - } - VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); - - return val; -} - static VALUE vm_throw_continue(const rb_execution_context_t *ec, VALUE err) { @@ -3106,17 +3244,45 @@ vm_call_ivar(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_call const struct rb_callcache *cc = calling->cc; RB_DEBUG_COUNTER_INC(ccf_ivar); cfp->sp -= 1; - return vm_getivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, NULL, NULL, cc, TRUE); + VALUE ivar = vm_getivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, NULL, NULL, cc, TRUE); + return ivar; } static VALUE -vm_call_attrset(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) +vm_call_attrset_direct(rb_execution_context_t *ec, rb_control_frame_t *cfp, const struct rb_callcache *cc, VALUE obj) { - const struct rb_callcache *cc = calling->cc; RB_DEBUG_COUNTER_INC(ccf_attrset); VALUE val = *(cfp->sp - 1); cfp->sp -= 2; - return vm_setivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, val, NULL, NULL, cc, 1); + shape_id_t source_shape_id = vm_cc_attr_index_source_shape_id(cc); + attr_index_t index = vm_cc_attr_index(cc); + shape_id_t dest_shape_id = vm_cc_attr_index_dest_shape_id(cc); + ID id = vm_cc_cme(cc)->def->body.attr.id; + rb_check_frozen_internal(obj); + VALUE res = vm_setivar(obj, id, val, source_shape_id, dest_shape_id, index); + if (res == Qundef) { + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + { + res = vm_setivar_default(obj, id, val, source_shape_id, dest_shape_id, index); + if (res != Qundef) { + return res; + } + } + } + res = vm_setivar_slowpath_attr(obj, id, val, cc); + } + return res; +} + +static VALUE +vm_call_attrset(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) +{ + return vm_call_attrset_direct(ec, cfp, calling->cc, calling->recv); } bool @@ -3225,7 +3391,7 @@ vm_call_alias(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_cal { calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, - { 0 }, + {{0}}, aliased_callable_method_entry(vm_cc_cme(calling->cc))); return vm_call_method_each_type(ec, cfp, calling); @@ -3395,7 +3561,7 @@ vm_call_method_missing_body(rb_execution_context_t *ec, rb_control_frame_t *reg_ ec->method_missing_reason = reason; calling->ci = &VM_CI_ON_STACK(idMethodMissing, flag, argc, vm_ci_kwarg(orig_ci)); - calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, { 0 }, + calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, rb_callable_method_entry_without_refinements(CLASS_OF(calling->recv), idMethodMissing, NULL)); return vm_call_method(ec, reg_cfp, calling); } @@ -3421,7 +3587,7 @@ vm_call_zsuper(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_ca cme = refined_method_callable_without_refinement(cme); } - calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, { 0 }, cme); + calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, cme); return vm_call_method_each_type(ec, cfp, calling); } @@ -3528,7 +3694,7 @@ search_refined_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struc static VALUE vm_call_refined(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) { - struct rb_callcache *ref_cc = &VM_CC_ON_STACK(Qundef, vm_call_general, { 0 }, + struct rb_callcache *ref_cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, search_refined_method(ec, cfp, calling)); if (vm_cc_cme(ref_cc)) { @@ -3708,18 +3874,45 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); rb_check_arity(calling->argc, 1, 1); - vm_cc_attr_index_initialize(cc); + const unsigned int aset_mask = (VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT | VM_CALL_KWARG); - VM_CALL_METHOD_ATTR(v, - vm_call_attrset(ec, cfp, calling), - CC_SET_FASTPATH(cc, vm_call_attrset, !(vm_ci_flag(ci) & aset_mask))); + + if (vm_cc_markable(cc)) { + vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); + VM_CALL_METHOD_ATTR(v, + vm_call_attrset_direct(ec, cfp, cc, calling->recv), + CC_SET_FASTPATH(cc, vm_call_attrset, !(vm_ci_flag(ci) & aset_mask))); + } else { + cc = &((struct rb_callcache) { + .flags = T_IMEMO | + (imemo_callcache << FL_USHIFT) | + VM_CALLCACHE_UNMARKABLE | + ((VALUE)INVALID_SHAPE_ID << SHAPE_FLAG_SHIFT) | + VM_CALLCACHE_ON_STACK, + .klass = cc->klass, + .cme_ = cc->cme_, + .call_ = cc->call_, + .aux_ = { + .attr = { + .index = 0, + .dest_shape_id = INVALID_SHAPE_ID, + } + }, + }); + + VM_CALL_METHOD_ATTR(v, + vm_call_attrset_direct(ec, cfp, cc, calling->recv), + CC_SET_FASTPATH(cc, vm_call_attrset, !(vm_ci_flag(ci) & aset_mask))); + } return v; case VM_METHOD_TYPE_IVAR: CALLER_SETUP_ARG(cfp, calling, ci); CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); rb_check_arity(calling->argc, 0, 0); - vm_cc_attr_index_initialize(cc); + if (vm_cc_markable(cc)) { + vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); + } const unsigned int ivar_mask = (VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT); VM_CALL_METHOD_ATTR(v, vm_call_ivar(ec, cfp, calling), diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index ebeeab14b12bd8..7ae9f06adc4a6d 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -40,6 +40,7 @@ fn main() { .header("internal.h") .header("internal/re.h") .header("include/ruby/ruby.h") + .header("shape.h") .header("vm_core.h") .header("vm_callinfo.h") @@ -81,6 +82,12 @@ fn main() { // This function prints info about a value and is useful for debugging .allowlist_function("rb_obj_info_dump") + // From shape.h + .allowlist_function("rb_shape_get_shape_id") + .allowlist_function("rb_shape_get_shape_by_id") + .allowlist_function("rb_shape_flags_mask") + .allowlist_function("rb_shape_get_iv_index") + // From ruby/internal/intern/object.h .allowlist_function("rb_obj_is_kind_of") diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs index d310e3bf129137..42d97b7e80b338 100644 --- a/yjit/src/asm/x86_64/mod.rs +++ b/yjit/src/asm/x86_64/mod.rs @@ -617,7 +617,7 @@ fn write_rm_multi(cb: &mut CodeBlock, op_mem_reg8: u8, op_mem_reg_pref: u8, op_r write_rm(cb, sz_pref, rex_w, X86Opnd::None, opnd0, op_ext_imm, &[op_mem_imm_lrg]); cb.write_int(uimm.value, if opnd_size > 32 { 32 } else { opnd_size.into() }); } else { - panic!("immediate value too large"); + panic!("immediate value too large (num_bits={})", num_bits); } }, _ => unreachable!() diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 22634a7de00838..f453f133d92f52 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1938,11 +1938,9 @@ fn gen_set_ivar( let val_opnd = ctx.stack_pop(1); let recv_opnd = ctx.stack_pop(1); - let ivar_index: u32 = unsafe { rb_obj_ensure_iv_index_mapping(recv, ivar_name) }; - - // Call rb_vm_set_ivar_idx with the receiver, the index of the ivar, and the value + // Call rb_vm_set_ivar_id with the receiver, the ivar name, and the value let val = asm.ccall( - rb_vm_set_ivar_idx as *const u8, + rb_vm_set_ivar_id as *const u8, vec![ recv_opnd, ivar_index.into(), @@ -2023,81 +2021,82 @@ fn gen_get_ivar( return EndBlock; } - // FIXME: Mapping the index could fail when there is too many ivar names. If we're - // compiling for a branch stub that can cause the exception to be thrown from the - // wrong PC. - let ivar_index = - unsafe { rb_obj_ensure_iv_index_mapping(comptime_receiver, ivar_name) }.as_usize(); + let ivar_index = unsafe { + let shape_id = comptime_receiver.shape_of(); + let shape = rb_shape_get_shape_by_id(shape_id); + let mut ivar_index: u32 = 0; + if rb_shape_get_iv_index(shape, ivar_name, &mut ivar_index) { + Some(ivar_index as usize) + } else { + None + } + }; + + // must be before stack_pop + let recv_type = ctx.get_opnd_type(recv_opnd); + + // Upgrade type + if !recv_type.is_heap() { + ctx.upgrade_opnd_type(recv_opnd, Type::UnknownHeap); + } // Pop receiver if it's on the temp stack if recv_opnd != SelfOpnd { ctx.stack_pop(1); } - if USE_RVARGC != 0 { - // Check that the ivar table is big enough - // Check that the slot is inside the ivar table (num_slots > index) - let num_slots = Opnd::mem(32, recv, ROBJECT_OFFSET_NUMIV); - asm.cmp(num_slots, Opnd::UImm(ivar_index as u64)); - asm.jbe(counted_exit!(ocb, side_exit, getivar_idx_out_of_range).into()); + // Guard heap object + if !recv_type.is_heap() { + guard_object_is_heap(asm, recv, side_exit); } // Compile time self is embedded and the ivar index lands within the object - let test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) }; - if test_result { - // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h + let embed_test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) }; - // Guard that self is embedded - // TODO: BT and JC is shorter - asm.comment("guard embedded getivar"); - let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); - asm.test(flags_opnd, Opnd::UImm(ROBJECT_EMBED as u64)); - let side_exit = counted_exit!(ocb, side_exit, getivar_megamorphic); - jit_chain_guard( - JCC_JZ, - jit, - &starting_context, - asm, - ocb, - max_chain_depth, - side_exit, - ); + let flags_mask: usize = unsafe { rb_shape_flags_mask() }.as_usize(); + let expected_flags_mask: usize = (RUBY_T_MASK as usize) | !flags_mask | (ROBJECT_EMBED as usize); + let expected_flags = comptime_receiver.builtin_flags() & expected_flags_mask; + + // Combined guard for all flags: shape, embeddedness, and T_OBJECT + let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); + + asm.comment("guard shape, embedded, and T_OBJECT"); + let flags_opnd = asm.and(flags_opnd, Opnd::UImm(expected_flags_mask as u64)); + asm.cmp(flags_opnd, Opnd::UImm(expected_flags as u64)); + jit_chain_guard( + JCC_JNE, + jit, + &starting_context, + asm, + ocb, + max_chain_depth, + side_exit, + ); + + // If there is no IVAR index, then the ivar was undefined + // when we entered the compiler. That means we can just return + // nil for this shape + iv name + if ivar_index.is_none() { + let out_opnd = ctx.stack_push(Type::Nil); + asm.mov(out_opnd, Qnil.into()); + } else if embed_test_result { + // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h // Load the variable - let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index * SIZEOF_VALUE) as i32; + let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index.unwrap() * SIZEOF_VALUE) as i32; let ivar_opnd = Opnd::mem(64, recv, offs); - // Guard that the variable is not Qundef - asm.cmp(ivar_opnd, Qundef.into()); - let out_val = asm.csel_e(Qnil.into(), ivar_opnd); - // Push the ivar on the stack let out_opnd = ctx.stack_push(Type::Unknown); - asm.mov(out_opnd, out_val); + asm.mov(out_opnd, ivar_opnd); } else { // Compile time value is *not* embedded. - // Guard that value is *not* embedded - // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h - asm.comment("guard extended getivar"); - let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); - asm.test(flags_opnd, Opnd::UImm(ROBJECT_EMBED as u64)); - let megamorphic_side_exit = counted_exit!(ocb, side_exit, getivar_megamorphic); - jit_chain_guard( - JCC_JNZ, - jit, - &starting_context, - asm, - ocb, - max_chain_depth, - megamorphic_side_exit, - ); - if USE_RVARGC == 0 { // Check that the extended table is big enough // Check that the slot is inside the extended table (num_slots > index) let num_slots = Opnd::mem(32, recv, ROBJECT_OFFSET_NUMIV); - asm.cmp(num_slots, Opnd::UImm(ivar_index as u64)); + asm.cmp(num_slots, Opnd::UImm(ivar_index.unwrap() as u64)); asm.jbe(counted_exit!(ocb, side_exit, getivar_idx_out_of_range).into()); } @@ -2105,15 +2104,10 @@ fn gen_get_ivar( let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_IVPTR)); // Read the ivar from the extended table - let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index) as i32); - - // Check that the ivar is not Qundef - asm.cmp(ivar_opnd, Qundef.into()); - let out_val = asm.csel_ne(ivar_opnd, Qnil.into()); + let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index.unwrap()) as i32); - // Push the ivar on the stack let out_opnd = ctx.stack_push(Type::Unknown); - asm.mov(out_opnd, out_val); + asm.mov(out_opnd, ivar_opnd); } // Jump to next instruction. This allows guard chains to share the same successor. @@ -2136,25 +2130,12 @@ fn gen_getinstancevariable( let ivar_name = jit_get_arg(jit, 0).as_u64(); let comptime_val = jit_peek_at_self(jit); - let comptime_val_klass = comptime_val.class_of(); // Generate a side exit let side_exit = get_side_exit(jit, ocb, ctx); // Guard that the receiver has the same class as the one from compile time. let self_asm_opnd = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF); - jit_guard_known_klass( - jit, - ctx, - asm, - ocb, - comptime_val_klass, - self_asm_opnd, - SelfOpnd, - comptime_val, - GET_IVAR_MAX_DEPTH, - side_exit, - ); gen_get_ivar( jit, diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index c379034a4c238e..4307937707a837 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -120,7 +120,7 @@ extern "C" { obj: VALUE, v: VALUE, ) -> bool; - pub fn rb_vm_set_ivar_idx(obj: VALUE, idx: u32, val: VALUE) -> VALUE; + pub fn rb_vm_set_ivar_id(obj: VALUE, idx: u32, val: VALUE) -> VALUE; pub fn rb_vm_setinstancevariable(iseq: IseqPtr, obj: VALUE, id: ID, val: VALUE, ic: IVC); pub fn rb_aliased_callable_method_entry( me: *const rb_callable_method_entry_t, @@ -354,18 +354,26 @@ impl VALUE { /// Read the flags bits from the RBasic object, then return a Ruby type enum (e.g. RUBY_T_ARRAY) pub fn builtin_type(self) -> ruby_value_type { + (self.builtin_flags() & (RUBY_T_MASK as usize)) as ruby_value_type + } + + pub fn builtin_flags(self) -> usize { assert!(!self.special_const_p()); let VALUE(cval) = self; let rbasic_ptr = cval as *const RBasic; let flags_bits: usize = unsafe { (*rbasic_ptr).flags }.as_usize(); - (flags_bits & (RUBY_T_MASK as usize)) as ruby_value_type + return flags_bits; } pub fn class_of(self) -> VALUE { unsafe { CLASS_OF(self) } } + pub fn shape_of(self) -> u32 { + unsafe { rb_shape_get_shape_id(self) } + } + pub fn as_isize(self) -> isize { let VALUE(is) = self; is as isize diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 3bffc3073126c7..a00c44f05a2e3c 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -402,6 +402,29 @@ extern "C" { extern "C" { pub fn rb_reg_new_ary(ary: VALUE, options: ::std::os::raw::c_int) -> VALUE; } +pub type attr_index_t = u32; +pub type shape_id_t = u32; +#[repr(C)] +pub struct rb_shape { + pub parent: *mut rb_shape, + pub edges: *mut rb_id_table, + pub edge_name: ID, + pub iv_count: attr_index_t, + pub type_: u8, +} +pub type rb_shape_t = rb_shape; +extern "C" { + pub fn rb_shape_get_shape_by_id(shape_id: shape_id_t) -> *mut rb_shape_t; +} +extern "C" { + pub fn rb_shape_get_shape_id(obj: VALUE) -> shape_id_t; +} +extern "C" { + pub fn rb_shape_get_iv_index(shape: *mut rb_shape_t, id: ID, value: *mut attr_index_t) -> bool; +} +extern "C" { + pub fn rb_shape_flags_mask() -> VALUE; +} pub const idDot2: ruby_method_ids = 128; pub const idDot3: ruby_method_ids = 129; pub const idUPlus: ruby_method_ids = 132; @@ -719,6 +742,11 @@ pub const OPTIMIZED_METHOD_TYPE_STRUCT_AREF: method_optimized_type = 3; pub const OPTIMIZED_METHOD_TYPE_STRUCT_ASET: method_optimized_type = 4; pub const OPTIMIZED_METHOD_TYPE__MAX: method_optimized_type = 5; pub type method_optimized_type = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct rb_id_table { + _unused: [u8; 0], +} extern "C" { pub fn rb_method_entry_at(obj: VALUE, id: ID) -> *const rb_method_entry_t; } @@ -747,9 +775,10 @@ pub struct iseq_inline_constant_cache { pub segments: *const ID, } #[repr(C)] -#[derive(Debug, Copy, Clone)] pub struct iseq_inline_iv_cache_entry { - pub entry: *mut rb_iv_index_tbl_entry, + pub source_shape_id: shape_id_t, + pub dest_shape_id: shape_id_t, + pub attr_index: attr_index_t, } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -938,12 +967,6 @@ extern "C" { ) -> *const rb_callable_method_entry_t; } #[repr(C)] -pub struct rb_iv_index_tbl_entry { - pub index: u32, - pub class_serial: rb_serial_t, - pub class_value: VALUE, -} -#[repr(C)] pub struct rb_cvar_class_tbl_entry { pub index: u32, pub global_cvar_state: rb_serial_t, From 913979bede2a1b79109fa2072352882560d55fe0 Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Mon, 3 Oct 2022 13:52:40 -0400 Subject: [PATCH 005/139] Make inline cache reads / writes atomic with object shapes Prior to this commit, we were reading and writing ivar index and shape ID in inline caches in two separate instructions when getting and setting ivars. This meant there was a race condition with ractors and these caches where one ractor could change a value in the cache while another was still reading from it. This commit instead reads and writes shape ID and ivar index to inline caches atomically so there is no longer a race condition. Co-Authored-By: Aaron Patterson Co-Authored-By: John Hawthorn --- lib/mjit/compiler.rb | 30 +++++-- mjit_c.rb | 11 +-- object.c | 2 +- shape.c | 82 +++++++++--------- shape.h | 18 +--- test/ruby/test_shapes.rb | 57 +++++++------ variable.c | 6 +- vm.c | 4 +- vm_callinfo.h | 81 +++++------------- vm_core.h | 4 +- vm_insnhelper.c | 148 +++++++++++++++++---------------- yjit/src/codegen.rs | 2 +- yjit/src/cruby_bindings.inc.rs | 7 +- 13 files changed, 215 insertions(+), 237 deletions(-) diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 06f018c9346920..0d2910bf4ec2d5 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -351,20 +351,27 @@ def compile_send(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, status, # _mjit_compile_ivar.erb def compile_ivar(insn_name, stack_size, pos, status, operands, body) ic_copy = (status.is_entries + (C.iseq_inline_storage_entry.new(operands[1]) - body.is_entries)).iv_cache + dest_shape_id = ic_copy.value >> C.SHAPE_FLAG_SHIFT + attr_index = ic_copy.value & ((1 << C.SHAPE_FLAG_SHIFT) - 1) + source_shape_id = if dest_shape_id == C.INVALID_SHAPE_ID + dest_shape_id + else + RubyVM::Shape.find_by_id(dest_shape_id).parent_id + end src = +'' - if !status.compile_info.disable_ivar_cache && ic_copy.source_shape_id != C.INVALID_SHAPE_ID + if !status.compile_info.disable_ivar_cache && source_shape_id != C.INVALID_SHAPE_ID # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`. # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos) # JIT: prepare vm_getivar/vm_setivar arguments and variables src << "{\n" src << " VALUE obj = GET_SELF();\n" - src << " const shape_id_t source_shape_id = (rb_serial_t)#{ic_copy.source_shape_id};\n" # JIT: cache hit path of vm_getivar/vm_setivar, or cancel JIT (recompile it with exivar) if insn_name == :setinstancevariable - src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" - src << " const shape_id_t dest_shape_id = (rb_serial_t)#{ic_copy.dest_shape_id};\n" + src << " const shape_id_t source_shape_id = (shape_id_t)#{source_shape_id};\n" + src << " const uint32_t index = #{attr_index - 1};\n" + src << " const shape_id_t dest_shape_id = (shape_id_t)#{dest_shape_id};\n" src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj) && \n" src << " dest_shape_id != ROBJECT_SHAPE_ID(obj)) {\n" src << " if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) {\n" @@ -374,14 +381,19 @@ def compile_ivar(insn_name, stack_size, pos, status, operands, body) src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n" src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n" src << " }\n" + src << " else if (dest_shape_id == ROBJECT_SHAPE_ID(obj)) {\n" + src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n" + src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n" + src << " }\n" else - if ic_copy.attr_index == 0 # cache hit, but uninitialized iv + src << " const shape_id_t source_shape_id = (shape_id_t)#{dest_shape_id};\n" + if attr_index == 0 # cache hit, but uninitialized iv src << " /* Uninitialized instance variable */\n" src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n" src << " stack[#{stack_size}] = Qnil;\n" src << " }\n" else - src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" + src << " const uint32_t index = #{attr_index - 1};\n" src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n" src << " stack[#{stack_size}] = ROBJECT_IVPTR(obj)[index];\n" src << " }\n" @@ -394,15 +406,15 @@ def compile_ivar(insn_name, stack_size, pos, status, operands, body) src << " }\n" src << "}\n" return src - elsif insn_name == :getinstancevariable && !status.compile_info.disable_exivar_cache && ic_copy.source_shape_id != C.INVALID_SHAPE_ID + elsif insn_name == :getinstancevariable && !status.compile_info.disable_exivar_cache && source_shape_id != C.INVALID_SHAPE_ID # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`. # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos) # JIT: prepare vm_getivar's arguments and variables src << "{\n" src << " VALUE obj = GET_SELF();\n" - src << " const shape_id_t source_shape_id = (rb_serial_t)#{ic_copy.source_shape_id};\n" - src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" + src << " const shape_id_t source_shape_id = (shape_id_t)#{dest_shape_id};\n" + src << " const uint32_t index = #{attr_index - 1};\n" # JIT: cache hit path of vm_getivar, or cancel JIT (recompile it without any ivar optimization) src << " struct gen_ivtbl *ivtbl;\n" src << " if (LIKELY(FL_TEST_RAW(obj, FL_EXIVAR) && source_shape_id == rb_shape_get_shape_id(obj) && rb_ivar_generic_ivtbl_lookup(obj, &ivtbl))) {\n" diff --git a/mjit_c.rb b/mjit_c.rb index 0f8e11cbe5d766..1858f86e4d4afb 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -9,6 +9,10 @@ def SHAPE_BITS RubyVM::Shape::SHAPE_BITS end + def SHAPE_FLAG_SHIFT + RubyVM::Shape::SHAPE_FLAG_SHIFT + end + def ROBJECT_EMBED_LEN_MAX Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' end @@ -255,9 +259,7 @@ def C.iseq_inline_constant_cache_entry def C.iseq_inline_iv_cache_entry @iseq_inline_iv_cache_entry ||= CType::Struct.new( "iseq_inline_iv_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_iv_cache_entry)"), - source_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), source_shape_id)")], - dest_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), dest_shape_id)")], - attr_index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), attr_index)")], + value: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), value)")], ) end @@ -332,8 +334,7 @@ def C.rb_callcache "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_)"), attr: CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_.attr)"), - index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, index)")], - dest_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, dest_shape_id)")], + value: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, value)")], ), method_missing_reason: self.method_missing_reason, v: self.VALUE, diff --git a/object.c b/object.c index 648946beb3e04f..929968c4cbf13e 100644 --- a/object.c +++ b/object.c @@ -319,7 +319,7 @@ init_copy(VALUE dest, VALUE obj) // If the object is frozen, the "dup"'d object will *not* be frozen, // so we need to copy the frozen shape's parent to the new object. if (rb_shape_frozen_shape_p(shape_to_set)) { - shape_to_set = shape_to_set->parent; + shape_to_set = rb_shape_get_shape_by_id(shape_to_set->parent_id); } // shape ids are different diff --git a/shape.c b/shape.c index a6a5adf8541c25..0f48fa1a865baf 100644 --- a/shape.c +++ b/shape.c @@ -91,7 +91,7 @@ rb_shape_get_shape(VALUE obj) static rb_shape_t * rb_shape_lookup_id(rb_shape_t* shape, ID id, enum shape_type shape_type) { - while (shape->parent) { + while (shape->parent_id != INVALID_SHAPE_ID) { if (shape->edge_name == id) { // If the shape type is different, we don't // want this to count as a "found" ID @@ -102,7 +102,7 @@ rb_shape_lookup_id(rb_shape_t* shape, ID id, enum shape_type shape_type) { return NULL; } } - shape = shape->parent; + shape = rb_shape_get_shape_by_id(shape->parent_id); } return NULL; } @@ -129,7 +129,7 @@ get_next_shape_internal(rb_shape_t* shape, ID id, VALUE obj, enum shape_type sha // In this case, the shape exists, but the shape is garbage, so we need to recreate it if (res) { rb_id_table_delete(shape->edges, id); - res->parent = NULL; + res->parent_id = INVALID_SHAPE_ID; } rb_shape_t * new_shape = rb_shape_alloc(id, shape); @@ -138,7 +138,7 @@ get_next_shape_internal(rb_shape_t* shape, ID id, VALUE obj, enum shape_type sha switch(shape_type) { case SHAPE_IVAR: - new_shape->iv_count = new_shape->parent->iv_count + 1; + new_shape->iv_count = rb_shape_get_shape_by_id(new_shape->parent_id)->iv_count + 1; // Check if we should update max_iv_count on the object's class if (BUILTIN_TYPE(obj) == T_OBJECT) { @@ -150,7 +150,7 @@ get_next_shape_internal(rb_shape_t* shape, ID id, VALUE obj, enum shape_type sha break; case SHAPE_IVAR_UNDEF: case SHAPE_FROZEN: - new_shape->iv_count = new_shape->parent->iv_count; + new_shape->iv_count = rb_shape_get_shape_by_id(new_shape->parent_id)->iv_count; break; case SHAPE_ROOT: rb_bug("Unreachable"); @@ -240,7 +240,7 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id) bool rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) { - while (shape->parent) { + while (shape->parent_id != INVALID_SHAPE_ID) { if (shape->edge_name == id) { enum shape_type shape_type; shape_type = (enum shape_type)shape->type; @@ -257,7 +257,7 @@ rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) { rb_bug("Ivar should not exist on frozen transition\n"); } } - shape = shape->parent; + shape = rb_shape_get_shape_by_id(shape->parent_id); } return false; } @@ -278,17 +278,23 @@ shape_alloc(void) } rb_shape_t * -rb_shape_alloc(ID edge_name, rb_shape_t * parent) +rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id) { rb_shape_t * shape = shape_alloc(); shape->edge_name = edge_name; shape->iv_count = 0; - shape->parent = parent; + shape->parent_id = parent_id; return shape; } +rb_shape_t * +rb_shape_alloc(ID edge_name, rb_shape_t * parent) +{ + return rb_shape_alloc_with_parent_id(edge_name, rb_shape_id(parent)); +} + MJIT_FUNC_EXPORTED void rb_shape_set_shape(VALUE obj, rb_shape_t* shape) { @@ -325,8 +331,8 @@ rb_shape_parent_id(VALUE self) { rb_shape_t * shape; TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); - if (shape->parent) { - return INT2NUM(rb_shape_id(shape->parent)); + if (shape->parent_id != INVALID_SHAPE_ID) { + return INT2NUM(shape->parent_id); } else { return Qnil; @@ -402,9 +408,9 @@ rb_shape_export_depth(VALUE self) TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); unsigned int depth = 0; - while (shape->parent) { + while (shape->parent_id != INVALID_SHAPE_ID) { depth++; - shape = shape->parent; + shape = rb_shape_get_shape_by_id(shape->parent_id); } return INT2NUM(depth); } @@ -414,8 +420,8 @@ rb_shape_parent(VALUE self) { rb_shape_t * shape; TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); - if (shape->parent) { - return rb_shape_t_to_rb_cShape(shape->parent); + if (shape->parent_id != INVALID_SHAPE_ID) { + return rb_shape_t_to_rb_cShape(rb_shape_get_shape_by_id(shape->parent_id)); } else { return Qnil; @@ -426,11 +432,11 @@ VALUE rb_shape_debug_shape(VALUE self, VALUE obj) { return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj)); } -VALUE rb_shape_debug_root_shape(VALUE self) { +VALUE rb_shape_root_shape(VALUE self) { return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape()); } -VALUE rb_shape_debug_frozen_root_shape(VALUE self) { +VALUE rb_shape_frozen_root_shape(VALUE self) { return rb_shape_t_to_rb_cShape(rb_shape_get_frozen_root_shape()); } @@ -460,7 +466,7 @@ VALUE rb_obj_shape(rb_shape_t* shape) { rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(ROOT_SHAPE_ID)); } else { - rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(rb_shape_id(shape->parent))); + rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(shape->parent_id)); } rb_hash_aset(rb_shape, ID2SYM(rb_intern("edge_name")), rb_id2str(shape->edge_name)); @@ -471,20 +477,7 @@ static VALUE shape_transition_tree(VALUE self) { return rb_obj_shape(rb_shape_get_root_shape()); } -static VALUE shape_count(VALUE self) { - int shape_count = 0; - rb_vm_t *vm = GET_VM(); - for(shape_id_t i = 0; i < vm->next_shape_id; i++) { - if(rb_shape_get_shape_by_id_without_assertion(i)) { - shape_count++; - } - } - return INT2NUM(shape_count); -} - -static VALUE -shape_max_shape_count(VALUE self) -{ +static VALUE next_shape_id(VALUE self) { return INT2NUM(GET_VM()->next_shape_id); } @@ -494,6 +487,16 @@ rb_shape_flags_mask(void) return SHAPE_FLAG_MASK; } +static VALUE +rb_shape_find_by_id(VALUE mod, VALUE id) +{ + shape_id_t shape_id = NUM2INT(id); + if (shape_id < 0 || shape_id >= GET_VM()->next_shape_id) { + rb_raise(rb_eArgError, "Shape ID %d is out of bounds\n", shape_id); + } + return rb_shape_t_to_rb_cShape(rb_shape_get_shape_by_id(shape_id)); +} + void Init_shape(void) { @@ -513,11 +516,12 @@ Init_shape(void) rb_define_const(rb_cShape, "SHAPE_IVAR_UNDEF", INT2NUM(SHAPE_IVAR_UNDEF)); rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN)); rb_define_const(rb_cShape, "SHAPE_BITS", INT2NUM(SHAPE_BITS)); - - rb_define_module_function(rb_cRubyVM, "debug_shape_transition_tree", shape_transition_tree, 0); - rb_define_module_function(rb_cRubyVM, "debug_shape_count", shape_count, 0); - rb_define_singleton_method(rb_cRubyVM, "debug_shape", rb_shape_debug_shape, 1); - rb_define_singleton_method(rb_cRubyVM, "debug_max_shape_count", shape_max_shape_count, 0); - rb_define_singleton_method(rb_cRubyVM, "debug_root_shape", rb_shape_debug_root_shape, 0); - rb_define_singleton_method(rb_cRubyVM, "debug_frozen_root_shape", rb_shape_debug_frozen_root_shape, 0); + rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT)); + + rb_define_singleton_method(rb_cShape, "transition_tree", shape_transition_tree, 0); + rb_define_singleton_method(rb_cShape, "find_by_id", rb_shape_find_by_id, 1); + rb_define_singleton_method(rb_cShape, "next_shape_id", next_shape_id, 0); + rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1); + rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0); + rb_define_singleton_method(rb_cShape, "frozen_root_shape", rb_shape_frozen_root_shape, 0); } diff --git a/shape.h b/shape.h index 66b8e580c9ecc7..c99fd1bfaaa057 100644 --- a/shape.h +++ b/shape.h @@ -43,11 +43,11 @@ typedef uint16_t shape_id_t; # define FROZEN_ROOT_SHAPE_ID 0x1 struct rb_shape { - struct rb_shape * parent; // Pointer to the parent struct rb_id_table * edges; // id_table from ID (ivar) to next shape ID edge_name; // ID (ivar) for transition from parent to rb_shape attr_index_t iv_count; uint8_t type; + shape_id_t parent_id; }; typedef struct rb_shape rb_shape_t; @@ -59,21 +59,6 @@ enum shape_type { SHAPE_IVAR_UNDEF, }; -static inline shape_id_t -IMEMO_CACHED_SHAPE_ID(VALUE cc) -{ - RBIMPL_ASSERT_TYPE((VALUE)cc, RUBY_T_IMEMO); - return (shape_id_t)(SHAPE_MASK & (RBASIC(cc)->flags >> SHAPE_FLAG_SHIFT)); -} - -static inline void -IMEMO_SET_CACHED_SHAPE_ID(VALUE cc, shape_id_t shape_id) -{ - RBIMPL_ASSERT_TYPE((VALUE)cc, RUBY_T_IMEMO); - RBASIC(cc)->flags &= SHAPE_FLAG_MASK; - RBASIC(cc)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); -} - #if SHAPE_IN_BASIC_FLAGS static inline shape_id_t RBASIC_SHAPE_ID(VALUE obj) @@ -141,6 +126,7 @@ shape_id_t rb_shape_id(rb_shape_t * shape); MJIT_SYMBOL_EXPORT_END rb_shape_t * rb_shape_alloc(ID edge_name, rb_shape_t * parent); +rb_shape_t * rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id); bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id); diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 807d485354a8ec..7142c30cd557d4 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -23,7 +23,7 @@ def add_bar end end - # RubyVM.debug_shape returns new instances of shape objects for + # RubyVM::Shape.of returns new instances of shape objects for # each call. This helper method allows us to define equality for # shapes def assert_shape_equal(shape1, shape2) @@ -39,63 +39,63 @@ def refute_shape_equal(shape1, shape2) def test_iv_index example = RemoveAndAdd.new - shape = RubyVM.debug_shape(example) + shape = RubyVM::Shape.of(example) assert_equal 0, shape.iv_count example.add_foo # makes a transition - new_shape = RubyVM.debug_shape(example) + new_shape = RubyVM::Shape.of(example) assert_equal([:@foo], example.instance_variables) assert_equal(shape.id, new_shape.parent.id) assert_equal(1, new_shape.iv_count) example.remove # makes a transition - remove_shape = RubyVM.debug_shape(example) + remove_shape = RubyVM::Shape.of(example) assert_equal([], example.instance_variables) assert_equal(new_shape.id, remove_shape.parent.id) assert_equal(1, remove_shape.iv_count) example.add_bar # makes a transition - bar_shape = RubyVM.debug_shape(example) + bar_shape = RubyVM::Shape.of(example) assert_equal([:@bar], example.instance_variables) assert_equal(remove_shape.id, bar_shape.parent.id) assert_equal(2, bar_shape.iv_count) end def test_new_obj_has_root_shape - assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape(Object.new)) + assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(Object.new)) end def test_frozen_new_obj_has_frozen_root_shape assert_shape_equal( - RubyVM.debug_frozen_root_shape, - RubyVM.debug_shape(Object.new.freeze) + RubyVM::Shape.frozen_root_shape, + RubyVM::Shape.of(Object.new.freeze) ) end def test_str_has_root_shape - assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape("")) + assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of("")) end def test_array_has_root_shape - assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape([])) + assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of([])) end def test_hash_has_root_shape - assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape({})) + assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of({})) end def test_true_has_frozen_root_shape - assert_shape_equal(RubyVM.debug_frozen_root_shape, RubyVM.debug_shape(true)) + assert_shape_equal(RubyVM::Shape.frozen_root_shape, RubyVM::Shape.of(true)) end def test_nil_has_frozen_root_shape - assert_shape_equal(RubyVM.debug_frozen_root_shape, RubyVM.debug_shape(nil)) + assert_shape_equal(RubyVM::Shape.frozen_root_shape, RubyVM::Shape.of(nil)) end def test_basic_shape_transition obj = Example.new - refute_equal(RubyVM.debug_root_shape, RubyVM.debug_shape(obj)) - assert_shape_equal(RubyVM.debug_root_shape.edges[:@a], RubyVM.debug_shape(obj)) + refute_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(obj)) + assert_shape_equal(RubyVM::Shape.root_shape.edges[:@a], RubyVM::Shape.of(obj)) assert_equal(obj.instance_variable_get(:@a), 1) end @@ -103,13 +103,13 @@ def test_different_objects_make_same_transition obj = Example.new obj2 = "" obj2.instance_variable_set(:@a, 1) - assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) end def test_duplicating_objects obj = Example.new obj2 = obj.dup - assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) end def test_freezing_and_duplicating_object @@ -118,14 +118,14 @@ def test_freezing_and_duplicating_object refute_predicate(obj2, :frozen?) # dup'd objects shouldn't be frozen, and the shape should be the # parent shape of the copied object - assert_equal(RubyVM.debug_shape(obj).parent.id, RubyVM.debug_shape(obj2).id) + assert_equal(RubyVM::Shape.of(obj).parent.id, RubyVM::Shape.of(obj2).id) end def test_freezing_and_duplicating_object_with_ivars obj = Example.new.freeze obj2 = obj.dup refute_predicate(obj2, :frozen?) - refute_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + refute_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) assert_equal(obj2.instance_variable_get(:@a), 1) end @@ -135,7 +135,7 @@ def test_freezing_and_duplicating_string_with_ivars str.freeze str2 = str.dup refute_predicate(str2, :frozen?) - refute_equal(RubyVM.debug_shape(str).id, RubyVM.debug_shape(str2).id) + refute_equal(RubyVM::Shape.of(str).id, RubyVM::Shape.of(str2).id) assert_equal(str2.instance_variable_get(:@a), 1) end @@ -143,14 +143,14 @@ def test_freezing_and_cloning_objects obj = Object.new.freeze obj2 = obj.clone(freeze: true) assert_predicate(obj2, :frozen?) - assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) end def test_freezing_and_cloning_object_with_ivars obj = Example.new.freeze obj2 = obj.clone(freeze: true) assert_predicate(obj2, :frozen?) - assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) assert_equal(obj2.instance_variable_get(:@a), 1) end @@ -158,7 +158,7 @@ def test_freezing_and_cloning_string str = "str".freeze str2 = str.clone(freeze: true) assert_predicate(str2, :frozen?) - assert_shape_equal(RubyVM.debug_shape(str), RubyVM.debug_shape(str2)) + assert_shape_equal(RubyVM::Shape.of(str), RubyVM::Shape.of(str2)) end def test_freezing_and_cloning_string_with_ivars @@ -167,7 +167,16 @@ def test_freezing_and_cloning_string_with_ivars str.freeze str2 = str.clone(freeze: true) assert_predicate(str2, :frozen?) - assert_shape_equal(RubyVM.debug_shape(str), RubyVM.debug_shape(str2)) + assert_shape_equal(RubyVM::Shape.of(str), RubyVM::Shape.of(str2)) assert_equal(str2.instance_variable_get(:@a), 1) end + + def test_out_of_bounds_shape + assert_raise ArgumentError do + RubyVM::Shape.find_by_id(RubyVM::Shape.next_shape_id) + end + assert_raise ArgumentError do + RubyVM::Shape.find_by_id(-1) + end + end end diff --git a/variable.c b/variable.c index b5e95b7f1c9c98..1f532f2154eaab 100644 --- a/variable.c +++ b/variable.c @@ -1627,7 +1627,7 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, VALUE* iv_list, rb_ivar_for case SHAPE_ROOT: return; case SHAPE_IVAR: - iterate_over_shapes_with_callback(shape->parent, iv_list, callback, arg); + iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), iv_list, callback, arg); VALUE val = iv_list[shape->iv_count - 1]; if (val != Qundef) { callback(shape->edge_name, val, arg); @@ -1635,7 +1635,7 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, VALUE* iv_list, rb_ivar_for return; case SHAPE_IVAR_UNDEF: case SHAPE_FROZEN: - iterate_over_shapes_with_callback(shape->parent, iv_list, callback, arg); + iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), iv_list, callback, arg); return; } } @@ -1694,7 +1694,7 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj) rb_shape_t * obj_shape = rb_shape_get_shape(obj); if (rb_shape_frozen_shape_p(obj_shape)) { - rb_shape_set_shape(clone, obj_shape->parent); + rb_shape_set_shape_id(clone, obj_shape->parent_id); } else { rb_shape_set_shape(clone, obj_shape); diff --git a/vm.c b/vm.c index a52d3efa7b2dc7..ed38192670ac7b 100644 --- a/vm.c +++ b/vm.c @@ -4054,11 +4054,11 @@ Init_vm_objects(void) } // Root shape - vm->root_shape = rb_shape_alloc(0, 0); + vm->root_shape = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID); RUBY_ASSERT(rb_shape_id(vm->root_shape) == ROOT_SHAPE_ID); // Frozen root shape - vm->frozen_root_shape = rb_shape_alloc(rb_make_internal_id(), vm->root_shape); + vm->frozen_root_shape = rb_shape_alloc_with_parent_id(rb_make_internal_id(), rb_shape_id(vm->root_shape)); vm->frozen_root_shape->type = (uint8_t)SHAPE_FROZEN; RUBY_ASSERT(rb_shape_id(vm->frozen_root_shape) == FROZEN_ROOT_SHAPE_ID); diff --git a/vm_callinfo.h b/vm_callinfo.h index e5b04c0709e883..f10cd9a000fcb6 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -286,8 +286,7 @@ struct rb_callcache { union { struct { - const attr_index_t index; - shape_id_t dest_shape_id; + uintptr_t value; // Shape ID in upper bits, index in lower bits } attr; const enum method_missing_reason method_missing_reason; /* used by method_missing */ VALUE v; @@ -307,9 +306,7 @@ vm_cc_attr_index_initialize(const struct rb_callcache *cc, shape_id_t shape_id) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(cc != vm_cc_empty()); - IMEMO_SET_CACHED_SHAPE_ID((VALUE)cc, shape_id); - *(attr_index_t *)&cc->aux_.attr.index = 0; - *(shape_id_t *)&cc->aux_.attr.dest_shape_id = shape_id; + *(uintptr_t *)&cc->aux_.attr.value = (uintptr_t)(shape_id) << SHAPE_FLAG_SHIFT; } static inline const struct rb_callcache * @@ -374,29 +371,7 @@ static inline attr_index_t vm_cc_attr_index(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - return cc->aux_.attr.index - 1; -} - -static inline bool -vm_cc_attr_index_p(const struct rb_callcache *cc) -{ - VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - return cc->aux_.attr.index != 0; -} - -static inline shape_id_t -vm_cc_attr_index_source_shape_id(const struct rb_callcache *cc) -{ - VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - - return IMEMO_CACHED_SHAPE_ID((VALUE)cc); -} - -static inline shape_id_t -vm_cc_attr_shape_id(const struct rb_callcache *cc) -{ - VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - return vm_cc_attr_index_source_shape_id(cc); + return (attr_index_t)((cc->aux_.attr.value & SHAPE_FLAG_MASK) - 1); } static inline shape_id_t @@ -404,37 +379,31 @@ vm_cc_attr_index_dest_shape_id(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - return cc->aux_.attr.dest_shape_id; -} - -static inline attr_index_t -vm_ic_attr_index(const struct iseq_inline_iv_cache_entry *ic) -{ - return ic->attr_index - 1; -} - -static inline bool -vm_ic_attr_index_p(const struct iseq_inline_iv_cache_entry *ic) -{ - return ic->attr_index > 0; + return cc->aux_.attr.value >> SHAPE_FLAG_SHIFT; } -static inline shape_id_t -vm_ic_attr_shape_id(const struct iseq_inline_iv_cache_entry *ic) +static inline void +vm_cc_atomic_shape_and_index(const struct rb_callcache *cc, shape_id_t * shape_id, attr_index_t * index) { - return ic->source_shape_id; + uintptr_t cache_value = cc->aux_.attr.value; // Atomically read 64 bits + *shape_id = (shape_id_t)(cache_value >> SHAPE_FLAG_SHIFT); + *index = (attr_index_t)(cache_value & SHAPE_FLAG_MASK) - 1; + return; } -static inline shape_id_t -vm_ic_attr_index_source_shape_id(const struct iseq_inline_iv_cache_entry *ic) +static inline void +vm_ic_atomic_shape_and_index(const struct iseq_inline_iv_cache_entry *ic, shape_id_t * shape_id, attr_index_t * index) { - return ic->source_shape_id; + uintptr_t cache_value = ic->value; // Atomically read 64 bits + *shape_id = (shape_id_t)(cache_value >> SHAPE_FLAG_SHIFT); + *index = (attr_index_t)(cache_value & SHAPE_FLAG_MASK) - 1; + return; } static inline shape_id_t vm_ic_attr_index_dest_shape_id(const struct iseq_inline_iv_cache_entry *ic) { - return ic->dest_shape_id; + return (shape_id_t)(ic->value >> SHAPE_FLAG_SHIFT); } static inline unsigned int @@ -479,29 +448,23 @@ vm_cc_call_set(const struct rb_callcache *cc, vm_call_handler call) } static inline void -vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id_t source_shape_id, shape_id_t dest_shape_id) +vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id_t dest_shape_id) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(cc != vm_cc_empty()); - IMEMO_SET_CACHED_SHAPE_ID((VALUE)cc, source_shape_id); - *(attr_index_t *)&cc->aux_.attr.index = (index + 1); - *(shape_id_t *)&cc->aux_.attr.dest_shape_id = dest_shape_id; + *(uintptr_t *)&cc->aux_.attr.value = (index + 1) | ((uintptr_t)(dest_shape_id) << SHAPE_FLAG_SHIFT); } static inline void -vm_ic_attr_index_set(const rb_iseq_t *iseq, const struct iseq_inline_iv_cache_entry *ic, attr_index_t index, shape_id_t source_shape_id, shape_id_t dest_shape_id) +vm_ic_attr_index_set(const rb_iseq_t *iseq, const struct iseq_inline_iv_cache_entry *ic, attr_index_t index, shape_id_t dest_shape_id) { - *(shape_id_t *)&ic->source_shape_id = source_shape_id; - *(shape_id_t *)&ic->dest_shape_id = dest_shape_id; - *(attr_index_t *)&ic->attr_index = index + 1; + *(uintptr_t *)&ic->value = ((uintptr_t)dest_shape_id << SHAPE_FLAG_SHIFT) | (index + 1); } static inline void vm_ic_attr_index_initialize(const struct iseq_inline_iv_cache_entry *ic, shape_id_t shape_id) { - *(shape_id_t *)&ic->source_shape_id = shape_id; - *(shape_id_t *)&ic->dest_shape_id = shape_id; - *(attr_index_t *)&ic->attr_index = 0; + *(uintptr_t *)&ic->value = (uintptr_t)shape_id << SHAPE_FLAG_SHIFT; } static inline void diff --git a/vm_core.h b/vm_core.h index 4dd873c1f87df4..aed15114e5bb2d 100644 --- a/vm_core.h +++ b/vm_core.h @@ -273,9 +273,7 @@ struct iseq_inline_constant_cache { }; struct iseq_inline_iv_cache_entry { - shape_id_t source_shape_id; - shape_id_t dest_shape_id; - attr_index_t attr_index; + uintptr_t value; // attr_index in lower bits, dest_shape_id in upper bits }; struct iseq_inline_cvar_cache_entry { diff --git a/vm_insnhelper.c b/vm_insnhelper.c index b8cb8c1fdd873f..2b4eda775a3699 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -52,7 +52,7 @@ ruby_vm_special_exception_copy(VALUE exc) VALUE e = rb_obj_alloc(rb_class_real(RBASIC_CLASS(exc))); rb_shape_t * shape = rb_shape_get_shape(exc); if (rb_shape_frozen_shape_p(shape)) { - shape = shape->parent; + shape = rb_shape_get_shape_by_id(shape->parent_id); } rb_shape_set_shape(e, shape); rb_obj_copy_ivar(e, exc); @@ -1097,11 +1097,11 @@ fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, in { if (is_attr) { if (vm_cc_markable(cc)) { - vm_cc_attr_index_set(cc, index, shape_id, shape_id); + vm_cc_attr_index_set(cc, index, shape_id); } } else { - vm_ic_attr_index_set(iseq, ic, index, shape_id, shape_id); + vm_ic_attr_index_set(iseq, ic, index, shape_id); } } @@ -1110,6 +1110,8 @@ fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, in #define ractor_object_incidental_shareable_p(obj, val) \ ractor_incidental_shareable_p(rb_ractor_shareable_p(obj), val) +#define ATTR_INDEX_NOT_SET (attr_index_t)-1 + ALWAYS_INLINE(static VALUE vm_getivar(VALUE, ID, const rb_iseq_t *, IVC, const struct rb_callcache *, int)); static inline VALUE vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr) @@ -1155,31 +1157,22 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } shape_id_t cached_id; + attr_index_t index; if (is_attr) { - cached_id = vm_cc_attr_shape_id(cc); + vm_cc_atomic_shape_and_index(cc, &cached_id, &index); } else { - cached_id = vm_ic_attr_shape_id(ic); + vm_ic_atomic_shape_and_index(ic, &cached_id, &index); } - attr_index_t index; - - if (LIKELY(cached_id == shape_id)) { - RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); - - if (is_attr && vm_cc_attr_index_p(cc)) { - index = vm_cc_attr_index(cc); - } - else if (!is_attr && vm_ic_attr_index_p(ic)) { - index = vm_ic_attr_index(ic); - } - else { + if(LIKELY(cached_id == shape_id)) { + if (index == ATTR_INDEX_NOT_SET) { return Qnil; } val = ivar_list[index]; - VM_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT && rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); + RUBY_ASSERT(val != Qundef); } else { // cache miss case #if RUBY_DEBUG @@ -1199,7 +1192,6 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } #endif - attr_index_t index; rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id); if (rb_shape_get_iv_index(shape, id, &index)) { @@ -1209,6 +1201,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call // We fetched the ivar list above val = ivar_list[index]; + RUBY_ASSERT(val != Qundef); } else { if (is_attr) { @@ -1242,16 +1235,16 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } static void -populate_cache(attr_index_t index, shape_id_t shape_id, shape_id_t next_shape_id, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, bool is_attr) +populate_cache(attr_index_t index, shape_id_t next_shape_id, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, bool is_attr) { // Cache population code if (is_attr) { if (vm_cc_markable(cc)) { - vm_cc_attr_index_set(cc, index, shape_id, next_shape_id); + vm_cc_attr_index_set(cc, index, next_shape_id); } } else { - vm_ic_attr_index_set(iseq, ic, index, shape_id, next_shape_id); + vm_ic_attr_index_set(iseq, ic, index, next_shape_id); } } @@ -1272,12 +1265,12 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, uint32_t num_iv = ROBJECT_NUMIV(obj); rb_shape_t* shape = rb_shape_get_shape(obj); - shape_id_t current_shape_id = ROBJECT_SHAPE_ID(obj); - shape_id_t next_shape_id = current_shape_id; + shape_id_t next_shape_id = ROBJECT_SHAPE_ID(obj); rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); if (shape != next_shape) { + RUBY_ASSERT(next_shape->parent_id == rb_shape_id(shape)); rb_shape_set_shape(obj, next_shape); next_shape_id = ROBJECT_SHAPE_ID(obj); } @@ -1287,7 +1280,7 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, rb_raise(rb_eArgError, "too many instance variables"); } - populate_cache(index, current_shape_id, next_shape_id, id, iseq, ic, cc, is_attr); + populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr); } else { rb_bug("Didn't find instance variable %s\n", rb_id2name(id)); @@ -1295,6 +1288,7 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, // Ensure the IV buffer is wide enough to store the IV if (UNLIKELY(index >= num_iv)) { + RUBY_ASSERT(index == num_iv); rb_init_iv_list(obj); } @@ -1309,7 +1303,6 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, break; default: { - shape_id_t shape_id = rb_shape_get_shape_id(obj); rb_ivar_set(obj, id, val); shape_id_t next_shape_id = rb_shape_get_shape_id(obj); rb_shape_t *next_shape = rb_shape_get_shape_by_id(next_shape_id); @@ -1320,7 +1313,7 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, rb_raise(rb_eArgError, "too many instance variables"); } - populate_cache(index, shape_id, next_shape_id, id, iseq, ic, cc, is_attr); + populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr); } else { rb_bug("didn't find the id\n"); @@ -1346,9 +1339,9 @@ vm_setivar_slowpath_attr(VALUE obj, ID id, VALUE val, const struct rb_callcache return vm_setivar_slowpath(obj, id, val, NULL, NULL, cc, true); } -NOINLINE(static VALUE vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index)); +NOINLINE(static VALUE vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_index_t index)); static VALUE -vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index) +vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_index_t index) { #if SHAPE_IN_BASIC_FLAGS shape_id_t shape_id = RBASIC_SHAPE_ID(obj); @@ -1356,73 +1349,87 @@ vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shap shape_id_t shape_id = rb_generic_shape_id(obj); #endif + struct gen_ivtbl *ivtbl = 0; + // Cache hit case - if (shape_id == source_shape_id) { + if (shape_id == dest_shape_id) { RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); - struct gen_ivtbl *ivtbl = 0; - if (dest_shape_id != shape_id) { - ivtbl = rb_ensure_generic_iv_list_size(obj, index + 1); + // Just get the IV table + rb_gen_ivtbl_get(obj, 0, &ivtbl); + } + else if (dest_shape_id != INVALID_SHAPE_ID) { + rb_shape_t * dest_shape = rb_shape_get_shape_by_id(dest_shape_id); + shape_id_t source_shape_id = dest_shape->parent_id; + + if (shape_id == source_shape_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) { + ivtbl = rb_ensure_generic_iv_list_size(obj, index + 1); #if SHAPE_IN_BASIC_FLAGS - RBASIC_SET_SHAPE_ID(obj, dest_shape_id); + RBASIC_SET_SHAPE_ID(obj, dest_shape_id); #else - ivtbl->shape_id = dest_shape_id; + ivtbl->shape_id = dest_shape_id; #endif } else { - // Just get the IV table - rb_gen_ivtbl_get(obj, 0, &ivtbl); + return Qundef; } + } + else { + return Qundef; + } - VALUE *ptr = ivtbl->ivptr; - - RB_OBJ_WRITE(obj, &ptr[index], val); + VALUE *ptr = ivtbl->ivptr; - RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); + RB_OBJ_WRITE(obj, &ptr[index], val); - return val; - } + RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); - return Qundef; + return val; } static inline VALUE -vm_setivar(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index) +vm_setivar(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_index_t index) { #if OPT_IC_FOR_IVAR switch (BUILTIN_TYPE(obj)) { case T_OBJECT: { VM_ASSERT(!rb_ractor_shareable_p(obj) || rb_obj_frozen_p(obj)); - // If object's shape id is the same as the source - // then do the shape transition and write the ivar - // If object's shape id is the same as the dest - // then write the ivar + shape_id_t shape_id = ROBJECT_SHAPE_ID(obj); - // Do we have a cache hit *and* is the CC intitialized - if (shape_id == source_shape_id) { + if (LIKELY(shape_id == dest_shape_id)) { RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); - VM_ASSERT(!rb_ractor_shareable_p(obj)); - - if (dest_shape_id != shape_id) { + } + else if (dest_shape_id != INVALID_SHAPE_ID) { + rb_shape_t *dest_shape = rb_shape_get_shape_by_id(dest_shape_id); + shape_id_t source_shape_id = dest_shape->parent_id; + if (shape_id == source_shape_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) { + RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { rb_init_iv_list(obj); } + ROBJECT_SET_SHAPE_ID(obj, dest_shape_id); - } - RUBY_ASSERT(index < ROBJECT_NUMIV(obj)); + RUBY_ASSERT(rb_shape_get_next(rb_shape_get_shape_by_id(source_shape_id), obj, id) == dest_shape); + RUBY_ASSERT(index < ROBJECT_NUMIV(obj)); - VALUE *ptr = ROBJECT_IVPTR(obj); + } + else { + break; + } + } else { + break; + } - RB_OBJ_WRITE(obj, &ptr[index], val); + VALUE *ptr = ROBJECT_IVPTR(obj); - RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); + RB_OBJ_WRITE(obj, &ptr[index], val); - return val; - } + RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); + return val; } break; case T_CLASS: @@ -1528,17 +1535,18 @@ vm_getinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, IVC ic) static inline void vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IVC ic) { - shape_id_t source_shape_id = vm_ic_attr_index_source_shape_id(ic); - attr_index_t index = vm_ic_attr_index(ic); - shape_id_t dest_shape_id = vm_ic_attr_index_dest_shape_id(ic); - if (UNLIKELY(vm_setivar(obj, id, val, source_shape_id, dest_shape_id, index) == Qundef)) { + shape_id_t dest_shape_id; + attr_index_t index; + vm_ic_atomic_shape_and_index(ic, &dest_shape_id, &index); + + if (UNLIKELY(vm_setivar(obj, id, val, dest_shape_id, index) == Qundef)) { switch (BUILTIN_TYPE(obj)) { case T_OBJECT: case T_CLASS: case T_MODULE: break; default: - if (vm_setivar_default(obj, id, val, source_shape_id, dest_shape_id, index) != Qundef) { + if (vm_setivar_default(obj, id, val, dest_shape_id, index) != Qundef) { return; } } @@ -3254,12 +3262,11 @@ vm_call_attrset_direct(rb_execution_context_t *ec, rb_control_frame_t *cfp, cons RB_DEBUG_COUNTER_INC(ccf_attrset); VALUE val = *(cfp->sp - 1); cfp->sp -= 2; - shape_id_t source_shape_id = vm_cc_attr_index_source_shape_id(cc); attr_index_t index = vm_cc_attr_index(cc); shape_id_t dest_shape_id = vm_cc_attr_index_dest_shape_id(cc); ID id = vm_cc_cme(cc)->def->body.attr.id; rb_check_frozen_internal(obj); - VALUE res = vm_setivar(obj, id, val, source_shape_id, dest_shape_id, index); + VALUE res = vm_setivar(obj, id, val, dest_shape_id, index); if (res == Qundef) { switch (BUILTIN_TYPE(obj)) { case T_OBJECT: @@ -3268,7 +3275,7 @@ vm_call_attrset_direct(rb_execution_context_t *ec, rb_control_frame_t *cfp, cons break; default: { - res = vm_setivar_default(obj, id, val, source_shape_id, dest_shape_id, index); + res = vm_setivar_default(obj, id, val, dest_shape_id, index); if (res != Qundef) { return res; } @@ -3894,8 +3901,7 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st .call_ = cc->call_, .aux_ = { .attr = { - .index = 0, - .dest_shape_id = INVALID_SHAPE_ID, + .value = INVALID_SHAPE_ID << SHAPE_FLAG_SHIFT, } }, }); diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index f453f133d92f52..56877c1721e2b8 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1943,7 +1943,7 @@ fn gen_set_ivar( rb_vm_set_ivar_id as *const u8, vec![ recv_opnd, - ivar_index.into(), + Opnd::UImm(ivar_name.into()), val_opnd, ], ); diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index a00c44f05a2e3c..cd29f883e5fe67 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -406,11 +406,11 @@ pub type attr_index_t = u32; pub type shape_id_t = u32; #[repr(C)] pub struct rb_shape { - pub parent: *mut rb_shape, pub edges: *mut rb_id_table, pub edge_name: ID, pub iv_count: attr_index_t, pub type_: u8, + pub parent_id: shape_id_t, } pub type rb_shape_t = rb_shape; extern "C" { @@ -775,10 +775,9 @@ pub struct iseq_inline_constant_cache { pub segments: *const ID, } #[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct iseq_inline_iv_cache_entry { - pub source_shape_id: shape_id_t, - pub dest_shape_id: shape_id_t, - pub attr_index: attr_index_t, + pub value: usize, } #[repr(C)] #[derive(Debug, Copy, Clone)] From 467992ee35b59577ddb53f1323b19af3f3e3d134 Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Tue, 11 Oct 2022 16:37:05 -0400 Subject: [PATCH 006/139] Implement optimize send in yjit (#6488) * Implement optimize send in yjit This successfully makes all our benchmarks exit way less for optimize send reasons. It makes some benchmarks faster, but not by as much as I'd like. I think this implementation works, but there are definitely more optimial arrangements. For example, what if we compiled send to a jump table? That seems like perhaps the most optimal we could do, but not obvious (to me) how to implement give our current setup. Co-authored-by: Alan Wu * Attempt at fixing the issues raised by @XrXr * fix allowlist * returns 0 instead of nil when not found * remove comment about encoding exception * Fix up c changes * Update assert Co-authored-by: Alan Wu * get rid of unneeded code and fix the flags * Apply suggestions from code review Co-authored-by: Alan Wu * rename and fix typo Co-authored-by: Alan Wu --- symbol.c | 22 ++++ yjit.c | 2 + yjit/bindgen/src/main.rs | 1 + yjit/src/codegen.rs | 217 ++++++++++++++++++++++++++++++--- yjit/src/core.rs | 15 +++ yjit/src/cruby.rs | 5 + yjit/src/cruby_bindings.inc.rs | 3 + yjit/src/stats.rs | 14 ++- 8 files changed, 262 insertions(+), 17 deletions(-) diff --git a/symbol.c b/symbol.c index 6aceab72c1434d..adcc1275e3e30c 100644 --- a/symbol.c +++ b/symbol.c @@ -1112,6 +1112,28 @@ rb_check_id(volatile VALUE *namep) return lookup_str_id(name); } +// Used by yjit for handling .send without throwing exceptions +ID +rb_get_symbol_id(VALUE name) +{ + if (STATIC_SYM_P(name)) { + return STATIC_SYM2ID(name); + } + else if (DYNAMIC_SYM_P(name)) { + if (SYMBOL_PINNED_P(name)) { + return RSYMBOL(name)->id; + } + else { + return 0; + } + } + else { + RUBY_ASSERT_ALWAYS(RB_TYPE_P(name, T_STRING)); + return lookup_str_id(name); + } +} + + VALUE rb_check_symbol(volatile VALUE *namep) { diff --git a/yjit.c b/yjit.c index cc64cccac39913..838956f7b47e69 100644 --- a/yjit.c +++ b/yjit.c @@ -509,6 +509,8 @@ rb_get_cme_def_body_attr_id(const rb_callable_method_entry_t *cme) return cme->def->body.attr.id; } +ID rb_get_symbol_id(VALUE namep); + enum method_optimized_type rb_get_cme_def_body_optimized_type(const rb_callable_method_entry_t *cme) { diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 7ae9f06adc4a6d..b945e9b1060ab4 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -332,6 +332,7 @@ fn main() { .allowlist_function("rb_get_cfp_ep_level") .allowlist_function("rb_get_cme_def_type") .allowlist_function("rb_get_cme_def_body_attr_id") + .allowlist_function("rb_get_symbol_id") .allowlist_function("rb_get_cme_def_body_optimized_type") .allowlist_function("rb_get_cme_def_body_optimized_index") .allowlist_function("rb_get_cme_def_body_cfunc") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 56877c1721e2b8..8b0409649f3bcd 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1918,6 +1918,9 @@ pub const OPT_AREF_MAX_CHAIN_DEPTH: i32 = 2; // up to 5 different classes pub const SEND_MAX_DEPTH: i32 = 5; +// up to 20 different methods for send +pub const SEND_MAX_CHAIN_DEPTH: i32 = 20; + // Codegen for setting an instance variable. // Preconditions: // - receiver is in REG0 @@ -1929,7 +1932,15 @@ fn gen_set_ivar( asm: &mut Assembler, recv: VALUE, ivar_name: ID, + flags: u32, + argc: i32, ) -> CodegenStatus { + + // This is a .send call and we need to adjust the stack + if flags & VM_CALL_OPT_SEND != 0 { + handle_opt_send_shift_stack(asm, argc as i32, ctx); + } + // Save the PC and SP because the callee may allocate // Note that this modifies REG_SP, which is why we do it first jit_prepare_routine_call(jit, ctx, asm); @@ -4148,8 +4159,9 @@ fn gen_send_cfunc( ci: *const rb_callinfo, cme: *const rb_callable_method_entry_t, block: Option, - argc: i32, recv_known_klass: *const VALUE, + flags: u32, + argc: i32, ) -> CodegenStatus { let cfunc = unsafe { get_cme_def_body_cfunc(cme) }; let cfunc_argc = unsafe { get_mct_argc(cfunc) }; @@ -4158,8 +4170,6 @@ fn gen_send_cfunc( // Create a side-exit to fall back to the interpreter let side_exit = get_side_exit(jit, ocb, ctx); - let flags = unsafe { vm_ci_flag(ci) }; - // If the function expects a Ruby array of arguments if cfunc_argc < 0 && cfunc_argc != -1 { gen_counter_incr!(asm, send_cfunc_ruby_array_varg); @@ -4228,6 +4238,11 @@ fn gen_send_cfunc( return CantCompile; } + // This is a .send call and we need to adjust the stack + if flags & VM_CALL_OPT_SEND != 0 { + handle_opt_send_shift_stack(asm, argc as i32, ctx); + } + // Points to the receiver operand on the stack let recv = ctx.stack_opnd(argc); @@ -4440,6 +4455,7 @@ fn gen_send_bmethod( ci: *const rb_callinfo, cme: *const rb_callable_method_entry_t, block: Option, + flags: u32, argc: i32, ) -> CodegenStatus { let procv = unsafe { rb_get_def_bmethod_proc((*cme).def) }; @@ -4469,7 +4485,7 @@ fn gen_send_bmethod( } let frame_type = VM_FRAME_MAGIC_BLOCK | VM_FRAME_FLAG_BMETHOD | VM_FRAME_FLAG_LAMBDA; - gen_send_iseq(jit, ctx, asm, ocb, iseq, ci, frame_type, Some(capture.ep), cme, block, argc) + gen_send_iseq(jit, ctx, asm, ocb, iseq, ci, frame_type, Some(capture.ep), cme, block, flags, argc) } fn gen_send_iseq( @@ -4483,11 +4499,11 @@ fn gen_send_iseq( prev_ep: Option<*const VALUE>, cme: *const rb_callable_method_entry_t, block: Option, + flags: u32, argc: i32, ) -> CodegenStatus { let mut argc = argc; - let flags = unsafe { vm_ci_flag(ci) }; // Create a side-exit to fall back to the interpreter let side_exit = get_side_exit(jit, ocb, ctx); @@ -4711,6 +4727,13 @@ fn gen_send_iseq( Some(leaf_builtin_raw) }; if let (None, Some(builtin_info)) = (block, leaf_builtin) { + + // this is a .send call not currently supported for builtins + if flags & VM_CALL_OPT_SEND != 0 { + gen_counter_incr!(asm, send_send_builtin); + return CantCompile; + } + let builtin_argc = unsafe { (*builtin_info).argc }; if builtin_argc + 1 < (C_ARG_OPNDS.len() as i32) { asm.comment("inlined leaf builtin"); @@ -4758,6 +4781,11 @@ fn gen_send_iseq( push_splat_args(required_args, ctx, asm, ocb, side_exit) } + // This is a .send call and we need to adjust the stack + if flags & VM_CALL_OPT_SEND != 0 { + handle_opt_send_shift_stack(asm, argc as i32, ctx); + } + if doing_kw_call { // Here we're calling a method with keyword arguments and specifying // keyword arguments at this call site. @@ -5012,7 +5040,10 @@ fn gen_struct_aref( cme: *const rb_callable_method_entry_t, comptime_recv: VALUE, _comptime_recv_klass: VALUE, + flags: u32, + argc: i32, ) -> CodegenStatus { + if unsafe { vm_ci_argc(ci) } != 0 { return CantCompile; } @@ -5034,6 +5065,11 @@ fn gen_struct_aref( } } + // This is a .send call and we need to adjust the stack + if flags & VM_CALL_OPT_SEND != 0 { + handle_opt_send_shift_stack(asm, argc as i32, ctx); + } + // All structs from the same Struct class should have the same // length. So if our comptime_recv is embedded all runtime // structs of the same class should be as well, and the same is @@ -5067,11 +5103,18 @@ fn gen_struct_aset( cme: *const rb_callable_method_entry_t, comptime_recv: VALUE, _comptime_recv_klass: VALUE, + flags: u32, + argc: i32, ) -> CodegenStatus { if unsafe { vm_ci_argc(ci) } != 1 { return CantCompile; } + // This is a .send call and we need to adjust the stack + if flags & VM_CALL_OPT_SEND != 0 { + handle_opt_send_shift_stack(asm, argc, ctx); + } + let off: i32 = unsafe { get_cme_def_body_optimized_index(cme) } .try_into() .unwrap(); @@ -5113,9 +5156,9 @@ fn gen_send_general( // see vm_call_method(). let ci = unsafe { get_call_data_ci(cd) }; // info about the call site - let argc: i32 = unsafe { vm_ci_argc(ci) }.try_into().unwrap(); - let mid = unsafe { vm_ci_mid(ci) }; - let flags = unsafe { vm_ci_flag(ci) }; + let mut argc: i32 = unsafe { vm_ci_argc(ci) }.try_into().unwrap(); + let mut mid = unsafe { vm_ci_mid(ci) }; + let mut flags = unsafe { vm_ci_flag(ci) }; // Don't JIT calls with keyword splat if flags & VM_CALL_KW_SPLAT != 0 { @@ -5205,7 +5248,7 @@ fn gen_send_general( VM_METHOD_TYPE_ISEQ => { let iseq = unsafe { get_def_iseq_ptr((*cme).def) }; let frame_type = VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL; - return gen_send_iseq(jit, ctx, asm, ocb, iseq, ci, frame_type, None, cme, block, argc); + return gen_send_iseq(jit, ctx, asm, ocb, iseq, ci, frame_type, None, cme, block, flags, argc); } VM_METHOD_TYPE_CFUNC => { return gen_send_cfunc( @@ -5216,8 +5259,9 @@ fn gen_send_general( ci, cme, block, - argc, &comptime_recv_klass, + flags, + argc, ); } VM_METHOD_TYPE_IVAR => { @@ -5227,6 +5271,12 @@ fn gen_send_general( return CantCompile; } + // This is a .send call not supported right now for getters + if flags & VM_CALL_OPT_SEND != 0 { + gen_counter_incr!(asm, send_send_getter); + return CantCompile; + } + if c_method_tracing_currently_enabled(jit) { // Can't generate code for firing c_call and c_return events // :attr-tracing: @@ -5270,12 +5320,12 @@ fn gen_send_general( return CantCompile; } else { let ivar_name = unsafe { get_cme_def_body_attr_id(cme) }; - return gen_set_ivar(jit, ctx, asm, comptime_recv, ivar_name); + return gen_set_ivar(jit, ctx, asm, comptime_recv, ivar_name, flags, argc); } } // Block method, e.g. define_method(:foo) { :my_block } VM_METHOD_TYPE_BMETHOD => { - return gen_send_bmethod(jit, ctx, asm, ocb, ci, cme, block, argc); + return gen_send_bmethod(jit, ctx, asm, ocb, ci, cme, block, flags, argc); } VM_METHOD_TYPE_ZSUPER => { gen_counter_incr!(asm, send_zsuper_method); @@ -5296,11 +5346,116 @@ fn gen_send_general( } // Send family of methods, e.g. call/apply VM_METHOD_TYPE_OPTIMIZED => { + let opt_type = unsafe { get_cme_def_body_optimized_type(cme) }; match opt_type { OPTIMIZED_METHOD_TYPE_SEND => { - gen_counter_incr!(asm, send_optimized_method_send); - return CantCompile; + + // This is for method calls like `foo.send(:bar)` + // The `send` method does not get its own stack frame. + // instead we look up the method and call it, + // doing some stack shifting based on the VM_CALL_OPT_SEND flag + + let starting_context = *ctx; + + if argc == 0 { + gen_counter_incr!(asm, send_send_wrong_args); + return CantCompile; + } + + argc -= 1; + + let compile_time_name = jit_peek_at_stack(jit, ctx, argc as isize); + + if !compile_time_name.string_p() && !compile_time_name.static_sym_p() { + gen_counter_incr!(asm, send_send_chain_not_string_or_sym); + return CantCompile; + } + + mid = unsafe { rb_get_symbol_id(compile_time_name) }; + if mid == 0 { + gen_counter_incr!(asm, send_send_null_mid); + return CantCompile; + } + + cme = unsafe { rb_callable_method_entry(comptime_recv_klass, mid) }; + if cme.is_null() { + gen_counter_incr!(asm, send_send_null_cme); + return CantCompile; + } + + // We aren't going to handle `send(send(:foo))`. We would need to + // do some stack manipulation here or keep track of how many levels + // deep we need to stack manipulate + // Because of how exits currently work, we can't do stack manipulation + // until we will no longer side exit. + let def_type = unsafe { get_cme_def_type(cme) }; + if let VM_METHOD_TYPE_OPTIMIZED = def_type { + let opt_type = unsafe { get_cme_def_body_optimized_type(cme) }; + if let OPTIMIZED_METHOD_TYPE_SEND = opt_type { + gen_counter_incr!(asm, send_send_nested); + return CantCompile; + } + } + + flags |= VM_CALL_FCALL | VM_CALL_OPT_SEND; + + assume_method_lookup_stable(jit, ocb, comptime_recv_klass, cme); + + let (known_class, type_mismatch_exit) = { + if compile_time_name.string_p() { + ( + unsafe { rb_cString }, + counted_exit!(ocb, side_exit, send_send_chain_not_string), + + ) + } else { + ( + unsafe { rb_cSymbol }, + counted_exit!(ocb, side_exit, send_send_chain_not_sym), + ) + } + }; + + jit_guard_known_klass( + jit, + ctx, + asm, + ocb, + known_class, + ctx.stack_opnd(argc), + StackOpnd(argc as u16), + compile_time_name, + 2, // We have string or symbol, so max depth is 2 + type_mismatch_exit + ); + + // Need to do this here so we don't have too many live + // values for the register allocator. + let name_opnd = asm.load(ctx.stack_opnd(argc)); + + let symbol_id_opnd = asm.ccall(rb_get_symbol_id as *const u8, vec![name_opnd]); + + asm.comment("chain_guard_send"); + let chain_exit = counted_exit!(ocb, side_exit, send_send_chain); + asm.cmp(symbol_id_opnd, 0.into()); + asm.jbe(chain_exit.into()); + + asm.cmp(symbol_id_opnd, mid.into()); + jit_chain_guard( + JCC_JNE, + jit, + &starting_context, + asm, + ocb, + SEND_MAX_CHAIN_DEPTH as i32, + chain_exit, + ); + + // We have changed the argc, flags, mid, and cme, so we need to re-enter the match + // and compile whatever method we found from send. + continue; + } OPTIMIZED_METHOD_TYPE_CALL => { gen_counter_incr!(asm, send_optimized_method_call); @@ -5320,6 +5475,8 @@ fn gen_send_general( cme, comptime_recv, comptime_recv_klass, + flags, + argc, ); } OPTIMIZED_METHOD_TYPE_STRUCT_ASET => { @@ -5332,6 +5489,8 @@ fn gen_send_general( cme, comptime_recv, comptime_recv_klass, + flags, + argc, ); } _ => { @@ -5354,6 +5513,32 @@ fn gen_send_general( } } + +/// Shifts the stack for send in order to remove the name of the method +/// Comment below borrow from vm_call_opt_send in vm_insnhelper.c +/// E.g. when argc == 2 +/// | | | | TOPN +/// +------+ | | +/// | arg1 | ---+ | | 0 +/// +------+ | +------+ +/// | arg0 | -+ +-> | arg1 | 1 +/// +------+ | +------+ +/// | sym | +---> | arg0 | 2 +/// +------+ +------+ +/// | recv | | recv | 3 +///--+------+--------+------+------ +/// +/// We do this for our compiletime context and the actual stack +fn handle_opt_send_shift_stack(asm: &mut Assembler, argc: i32, ctx: &mut Context) { + asm.comment("shift_stack"); + for j in (0..argc).rev() { + let opnd = ctx.stack_opnd(j); + let opnd2 = ctx.stack_opnd(j + 1); + asm.mov(opnd2, opnd); + } + ctx.shift_stack(argc as usize); +} + fn gen_opt_send_without_block( jit: &mut JITState, ctx: &mut Context, @@ -5515,10 +5700,10 @@ fn gen_invokesuper( VM_METHOD_TYPE_ISEQ => { let iseq = unsafe { get_def_iseq_ptr((*cme).def) }; let frame_type = VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL; - gen_send_iseq(jit, ctx, asm, ocb, iseq, ci, frame_type, None, cme, block, argc) + gen_send_iseq(jit, ctx, asm, ocb, iseq, ci, frame_type, None, cme, block, ci_flags, argc) } VM_METHOD_TYPE_CFUNC => { - gen_send_cfunc(jit, ctx, asm, ocb, ci, cme, block, argc, ptr::null()) + gen_send_cfunc(jit, ctx, asm, ocb, ci, cme, block, ptr::null(), ci_flags, argc) } _ => unreachable!(), } diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 66c1df083fb856..ec9384c4036150 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -1061,6 +1061,21 @@ impl Context { return top; } + pub fn shift_stack(&mut self, argc: usize) { + assert!(argc < self.stack_size.into()); + + let method_name_index = (self.stack_size - argc as u16 - 1) as usize; + + for i in method_name_index..(self.stack_size - 1) as usize { + + if i + 1 < MAX_TEMP_TYPES { + self.temp_types[i] = self.temp_types[i + 1]; + self.temp_mapping[i] = self.temp_mapping[i + 1]; + } + } + self.stack_pop(1); + } + /// Get an operand pointing to a slot on the temp stack pub fn stack_opnd(&self, idx: i32) -> Opnd { // SP points just above the topmost value diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 4307937707a837..5ddc31a06d4c0a 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -352,6 +352,10 @@ impl VALUE { self == Qnil } + pub fn string_p(self) -> bool { + self.class_of() == unsafe { rb_cString } + } + /// Read the flags bits from the RBasic object, then return a Ruby type enum (e.g. RUBY_T_ARRAY) pub fn builtin_type(self) -> ruby_value_type { (self.builtin_flags() & (RUBY_T_MASK as usize)) as ruby_value_type @@ -629,6 +633,7 @@ mod manual_defs { pub const VM_CALL_KW_SPLAT: u32 = 1 << VM_CALL_KW_SPLAT_bit; pub const VM_CALL_TAILCALL: u32 = 1 << VM_CALL_TAILCALL_bit; pub const VM_CALL_ZSUPER : u32 = 1 << VM_CALL_ZSUPER_bit; + pub const VM_CALL_OPT_SEND : u32 = 1 << VM_CALL_OPT_SEND_bit; // From internal/struct.h - in anonymous enum, so we can't easily import it pub const RSTRUCT_EMBED_LEN_MASK: usize = (RUBY_FL_USER2 | RUBY_FL_USER1) as usize; diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index cd29f883e5fe67..db14f9ca025d19 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1362,6 +1362,9 @@ extern "C" { extern "C" { pub fn rb_get_cme_def_body_attr_id(cme: *const rb_callable_method_entry_t) -> ID; } +extern "C" { + pub fn rb_get_symbol_id(namep: VALUE) -> ID; +} extern "C" { pub fn rb_get_cme_def_body_optimized_type( cme: *const rb_callable_method_entry_t, diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index 74319ec4edb088..bf968ee5638bfe 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -171,7 +171,6 @@ make_counters! { send_zsuper_method, send_undef_method, send_optimized_method, - send_optimized_method_send, send_optimized_method_call, send_optimized_method_block_call, send_missing_method, @@ -197,6 +196,19 @@ make_counters! { send_args_splat_non_iseq, send_args_splat_cfunc, send_iseq_ruby2_keywords, + send_send_not_imm, + send_send_wrong_args, + send_send_null_mid, + send_send_null_cme, + send_send_nested, + send_send_chain, + send_send_chain_string, + send_send_chain_not_string, + send_send_chain_not_sym, + send_send_chain_not_string_or_sym, + send_send_getter, + send_send_builtin, + send_bmethod_ractor, send_bmethod_block_arg, From ced1d172804b6dfe39aa31a323ffab80a25223b9 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Wed, 12 Oct 2022 12:59:05 +1300 Subject: [PATCH 007/139] Improvements to IO::Buffer implementation and documentation. (#6525) --- common.mk | 5 + cont.c | 37 ++- include/ruby/fiber/scheduler.h | 22 +- include/ruby/io/buffer.h | 10 +- io_buffer.c | 435 ++++++++++++++++++++++----------- scheduler.c | 28 +-- test/fiber/scheduler.rb | 81 +++--- test/ruby/test_io_buffer.rb | 22 +- vm_core.h | 2 +- 9 files changed, 418 insertions(+), 224 deletions(-) diff --git a/common.mk b/common.mk index 0fe52175662d36..464f64cb764f7b 100644 --- a/common.mk +++ b/common.mk @@ -7717,12 +7717,17 @@ io.$(OBJEXT): {$(VPATH)}vm_core.h io.$(OBJEXT): {$(VPATH)}vm_opts.h io_buffer.$(OBJEXT): $(hdrdir)/ruby/ruby.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/array.h +io_buffer.$(OBJEXT): $(top_srcdir)/internal/bignum.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/bits.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/compilers.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/error.h +io_buffer.$(OBJEXT): $(top_srcdir)/internal/fixnum.h +io_buffer.$(OBJEXT): $(top_srcdir)/internal/numeric.h +io_buffer.$(OBJEXT): $(top_srcdir)/internal/serial.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/static_assert.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/string.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/thread.h +io_buffer.$(OBJEXT): $(top_srcdir)/internal/vm.h io_buffer.$(OBJEXT): {$(VPATH)}assert.h io_buffer.$(OBJEXT): {$(VPATH)}backward/2/assume.h io_buffer.$(OBJEXT): {$(VPATH)}backward/2/attributes.h diff --git a/cont.c b/cont.c index 499e1e79109990..39f0fc8171a4a1 100644 --- a/cont.c +++ b/cont.c @@ -2410,20 +2410,34 @@ rb_fiber_transfer(VALUE fiber_value, int argc, const VALUE *argv) VALUE rb_fiber_blocking_p(VALUE fiber) { - return RBOOL(fiber_ptr(fiber)->blocking != 0); + return RBOOL(fiber_ptr(fiber)->blocking); } static VALUE -fiber_blocking_yield(VALUE fiber) +fiber_blocking_yield(VALUE fiber_value) { - fiber_ptr(fiber)->blocking += 1; - return rb_yield(fiber); + rb_fiber_t *fiber = fiber_ptr(fiber_value); + rb_thread_t * volatile th = fiber->cont.saved_ec.thread_ptr; + + // fiber->blocking is `unsigned int : 1`, so we use it as a boolean: + fiber->blocking = 1; + + // Once the fiber is blocking, and current, we increment the thread blocking state: + th->blocking += 1; + + return rb_yield(fiber_value); } static VALUE -fiber_blocking_ensure(VALUE fiber) +fiber_blocking_ensure(VALUE fiber_value) { - fiber_ptr(fiber)->blocking -= 1; + rb_fiber_t *fiber = fiber_ptr(fiber_value); + rb_thread_t * volatile th = fiber->cont.saved_ec.thread_ptr; + + // We are no longer blocking: + fiber->blocking = 0; + th->blocking -= 1; + return Qnil; } @@ -2440,8 +2454,15 @@ fiber_blocking_ensure(VALUE fiber) VALUE rb_fiber_blocking(VALUE class) { - VALUE fiber = rb_fiber_current(); - return rb_ensure(fiber_blocking_yield, fiber, fiber_blocking_ensure, fiber); + VALUE fiber_value = rb_fiber_current(); + rb_fiber_t *fiber = fiber_ptr(fiber_value); + + // If we are already blocking, this is essentially a no-op: + if (fiber->blocking) { + return rb_yield(fiber_value); + } else { + return rb_ensure(fiber_blocking_yield, fiber_value, fiber_blocking_ensure, fiber_value); + } } /* diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h index d38651da5c9109..37985e12852397 100644 --- a/include/ruby/fiber/scheduler.h +++ b/include/ruby/fiber/scheduler.h @@ -23,6 +23,8 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() +#define RUBY_FIBER_SCHEDULER_VERSION 2 + struct timeval; /** @@ -248,10 +250,11 @@ VALUE rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io); * @param[out] io An io object to read from. * @param[out] buffer Return buffer. * @param[in] length Requested number of bytes to read. + * @param[in] offset The offset in the buffer to read to. * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`. * @return otherwise What `scheduler.io_read` returns `[-errno, size]`. */ -VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length); +VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset); /** * Nonblocking write to the passed IO. @@ -260,36 +263,39 @@ VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t * @param[out] io An io object to write to. * @param[in] buffer What to write. * @param[in] length Number of bytes to write. + * @param[in] offset The offset in the buffer to write from. * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`. * @return otherwise What `scheduler.io_write` returns `[-errno, size]`. */ -VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length); +VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset); /** * Nonblocking read from the passed IO at the specified offset. * * @param[in] scheduler Target scheduler. * @param[out] io An io object to read from. - * @param[out] buffer Return buffer. + * @param[in] from The offset in the given IO to read the data from. + * @param[out] buffer The buffer to read the data to. * @param[in] length Requested number of bytes to read. - * @param[in] offset The offset in the given IO to read the data from. + * @param[in] offset The offset in the buffer to read to. * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`. * @return otherwise What `scheduler.io_read` returns. */ -VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset); +VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset); /** * Nonblocking write to the passed IO at the specified offset. * * @param[in] scheduler Target scheduler. * @param[out] io An io object to write to. - * @param[in] buffer What to write. + * @param[in] from The offset in the given IO to write the data to. + * @param[in] buffer The buffer to write the data from. * @param[in] length Number of bytes to write. - * @param[in] offset The offset in the given IO to write the data to. + * @param[in] offset The offset in the buffer to write from. * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`. * @return otherwise What `scheduler.io_write` returns. */ -VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset); +VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset); /** * Nonblocking read from the passed IO using a native buffer. diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h index 16b23ec6290871..dd92db5bbe647d 100644 --- a/include/ruby/io/buffer.h +++ b/include/ruby/io/buffer.h @@ -21,6 +21,8 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() // WARNING: This entire interface is experimental and may change in the future! #define RB_IO_BUFFER_EXPERIMENTAL 1 +#define RUBY_IO_BUFFER_VERSION 2 + RUBY_EXTERN VALUE rb_cIOBuffer; RUBY_EXTERN size_t RUBY_IO_BUFFER_PAGE_SIZE; RUBY_EXTERN size_t RUBY_IO_BUFFER_DEFAULT_SIZE; @@ -81,10 +83,10 @@ void rb_io_buffer_resize(VALUE self, size_t size); void rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length); // The length is the minimum required length. -VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length); -VALUE rb_io_buffer_pread(VALUE self, VALUE io, size_t length, rb_off_t offset); -VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length); -VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset); +VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset); +VALUE rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset); +VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset); +VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset); RBIMPL_SYMBOL_EXPORT_END() diff --git a/io_buffer.c b/io_buffer.c index 4326d21defcb7e..bc5fac8118c3cc 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -14,6 +14,7 @@ #include "internal/array.h" #include "internal/bits.h" #include "internal/error.h" +#include "internal/numeric.h" #include "internal/string.h" #include "internal/thread.h" @@ -439,27 +440,29 @@ rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags * mapping, you need to open a file in read-write mode, and explicitly pass * +flags+ argument without IO::Buffer::IMMUTABLE. * - * File.write('test.txt', 'test') + * Example: * - * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY) - * # => # + * File.write('test.txt', 'test') * - * buffer.readonly? # => true + * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY) + * # => # * - * buffer.get_string - * # => "test" + * buffer.readonly? # => true * - * buffer.set_string('b', 0) - * # `set_string': Buffer is not writable! (IO::Buffer::AccessError) + * buffer.get_string + * # => "test" * - * # create read/write mapping: length 4 bytes, offset 0, flags 0 - * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0) - * buffer.set_string('b', 0) - * # => 1 + * buffer.set_string('b', 0) + * # `set_string': Buffer is not writable! (IO::Buffer::AccessError) * - * # Check it - * File.read('test.txt') - * # => "best" + * # create read/write mapping: length 4 bytes, offset 0, flags 0 + * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0) + * buffer.set_string('b', 0) + * # => 1 + * + * # Check it + * File.read('test.txt') + * # => "best" * * Note that some operating systems may not have cache coherency between mapped * buffers and file reads. @@ -467,9 +470,7 @@ rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags static VALUE io_buffer_map(int argc, VALUE *argv, VALUE klass) { - if (argc < 1 || argc > 4) { - rb_error_arity(argc, 2, 4); - } + rb_check_arity(argc, 1, 4); // We might like to handle a string path? VALUE io = argv[0]; @@ -534,14 +535,14 @@ io_flags_for_size(size_t size) * * buffer = IO::Buffer.new(4) * # => - * # # - * # 0x00000000 00 00 00 00 .... + * # # + * # 0x00000000 00 00 00 00 .... * * buffer.get_string(0, 1) # => "\x00" * * buffer.set_string("test") * buffer - * # => + * # => * # # * # 0x00000000 74 65 73 74 test */ @@ -550,9 +551,7 @@ rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self) { io_buffer_experimental(); - if (argc < 0 || argc > 2) { - rb_error_arity(argc, 0, 2); - } + rb_check_arity(argc, 0, 2); struct rb_io_buffer *data = NULL; TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); @@ -890,6 +889,8 @@ rb_io_buffer_mapped_p(VALUE self) * Locking is not thread safe, but is a semantic used to ensure buffers don't * move while being used by a system call. * + * Example: + * * buffer.locked do * buffer.write(io) # theoretical system call interface * end @@ -978,6 +979,14 @@ rb_io_buffer_try_unlock(VALUE self) * can enter the lock. Also, locked buffer can't be changed with #resize or * #free. * + * The following operations acquire a lock: #resize, #free. + * + * Locking is not thread safe. It is designed as a safety net around + * non-blocking system calls. You can only share a buffer between threads with + * appropriate synchronisation techniques. + * + * Example: + * * buffer = IO::Buffer.new(4) * buffer.locked? #=> false * @@ -993,12 +1002,6 @@ rb_io_buffer_try_unlock(VALUE self) * buffer.set_string("test", 0) * end * end - * - * The following operations acquire a lock: #resize, #free. - * - * Locking is not thread safe. It is designed as a safety net around - * non-blocking system calls. You can only share a buffer between threads with - * appropriate synchronisation techniques. */ VALUE rb_io_buffer_locked(VALUE self) @@ -1029,20 +1032,22 @@ rb_io_buffer_locked(VALUE self) * * After the buffer is freed, no further operations can't be performed on it. * - * buffer = IO::Buffer.for('test') - * buffer.free - * # => # + * You can resize a freed buffer to re-allocate it. * - * buffer.get_value(:U8, 0) - * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError) + * Example: * - * buffer.get_string - * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError) + * buffer = IO::Buffer.for('test') + * buffer.free + * # => # * - * buffer.null? - * # => true + * buffer.get_value(:U8, 0) + * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError) * - * You can resize a freed buffer to re-allocate it. + * buffer.get_string + * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError) + * + * buffer.null? + * # => true */ VALUE rb_io_buffer_free(VALUE self) @@ -1068,7 +1073,7 @@ io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length } /* - * call-seq: slice(offset, length) -> io_buffer + * call-seq: slice([offset = 0, [length]]) -> io_buffer * * Produce another IO::Buffer which is a slice (or view into) the current one * starting at +offset+ bytes and going for +length+ bytes. @@ -1076,45 +1081,56 @@ io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length * The slicing happens without copying of memory, and the slice keeps being * associated with the original buffer's source (string, or file), if any. * - * Raises RuntimeError if the offset+length is out of the current + * If the offset is not given, it will be zero. If the offset is negative, it + * will raise an ArgumentError. + * + * If the length is not given, the slice will be as long as the original + * buffer minus the specified offset. If the length is negative, it will raise + * an ArgumentError. + * + * Raises RuntimeError if the offset+length is out of the current * buffer's bounds. * - * string = 'test' - * buffer = IO::Buffer.for(string) + * Example: * - * slice = buffer.slice(1, 2) - * # => - * # # - * # 0x00000000 65 73 es + * string = 'test' + * buffer = IO::Buffer.for(string) * - * # Put "o" into 0s position of the slice - * slice.set_string('o', 0) - * slice - * # => - * # # - * # 0x00000000 6f 73 os + * slice = buffer.slice + * # => + * # # + * # 0x00000000 74 65 73 74 test * + * buffer.slice(2) + * # => + * # # + * # 0x00000000 73 74 st + * + * slice = buffer.slice(1, 2) + * # => + * # # + * # 0x00000000 65 73 es + * + * # Put "o" into 0s position of the slice + * slice.set_string('o', 0) + * slice + * # => + * # # + * # 0x00000000 6f 73 os * - * # it is also visible at position 1 of the original buffer - * buffer - * # => - * # # - * # 0x00000000 74 6f 73 74 tost + * # it is also visible at position 1 of the original buffer + * buffer + * # => + * # # + * # 0x00000000 74 6f 73 74 tost * - * # ...and original string - * string - * # => tost + * # ...and original string + * string + * # => tost */ -VALUE -rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length) +static VALUE +rb_io_buffer_slice(struct rb_io_buffer *data, VALUE self, size_t offset, size_t length) { - // TODO fail on negative offets/lengths. - size_t offset = NUM2SIZET(_offset); - size_t length = NUM2SIZET(_length); - - struct rb_io_buffer *data = NULL; - TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); - io_buffer_validate_range(data, offset, length); VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self)); @@ -1133,6 +1149,37 @@ rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length) return instance; } +static VALUE +io_buffer_slice(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 2); + + struct rb_io_buffer *data = NULL; + TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); + + size_t offset = 0, length = 0; + + if (argc > 0) { + if (rb_int_negative_p(argv[0])) { + rb_raise(rb_eArgError, "Offset can't be negative!"); + } + + offset = NUM2SIZET(argv[0]); + } + + if (argc > 1) { + if (rb_int_negative_p(argv[1])) { + rb_raise(rb_eArgError, "Length can't be negative!"); + } + + length = NUM2SIZET(argv[1]); + } else { + length = data->size - offset; + } + + return rb_io_buffer_slice(data, self, offset, length); +} + int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size) { @@ -1154,7 +1201,7 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size) return 0; } -inline static void +static inline void io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size) { if (data->flags & RB_IO_BUFFER_READONLY) { @@ -1215,17 +1262,19 @@ rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size) * * Transfers ownership to a new buffer, deallocating the current one. * - * buffer = IO::Buffer.new('test') - * other = buffer.transfer - * other - * # => - * # # - * # 0x00000000 74 65 73 74 test - * buffer - * # => - * # # - * buffer.null? - * # => true + * Example: + * + * buffer = IO::Buffer.new('test') + * other = buffer.transfer + * other + * # => + * # # + * # 0x00000000 74 65 73 74 test + * buffer + * # => + * # # + * buffer.null? + * # => true */ VALUE rb_io_buffer_transfer(VALUE self) @@ -1339,7 +1388,7 @@ rb_io_buffer_resize(VALUE self, size_t size) * buffer = IO::Buffer.new(4) * buffer.set_string("test", 0) * buffer.resize(8) # resize to 8 bytes - * # => + * # => * # # * # 0x00000000 74 65 73 74 00 00 00 00 test.... * @@ -1811,7 +1860,7 @@ io_buffer_each_byte(int argc, VALUE *argv, VALUE self) return self; } -inline static void +static inline void rb_io_buffer_set_value(const void* base, size_t size, ID data_type, size_t *offset, VALUE value) { #define IO_BUFFER_SET_VALUE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;} @@ -1849,13 +1898,15 @@ rb_io_buffer_set_value(const void* base, size_t size, ID data_type, size_t *offs * symbols described in #get_value. * * buffer = IO::Buffer.new(8) - * # => + * # => * # # * # 0x00000000 00 00 00 00 00 00 00 00 + * * buffer.set_value(:U8, 1, 111) * # => 1 + * * buffer - * # => + * # => * # # * # 0x00000000 00 6f 00 00 00 00 00 00 .o...... * @@ -1863,11 +1914,12 @@ rb_io_buffer_set_value(const void* base, size_t size, ID data_type, size_t *offs * * buffer = IO::Buffer.new(8) * buffer.set_value(:U32, 0, 2.5) + * * buffer - * # => - * # # - * # 0x00000000 00 00 00 02 00 00 00 00 - * # ^^ the same as if we'd pass just integer 2 + * # => + * # # + * # 0x00000000 00 00 00 02 00 00 00 00 + * # ^^ the same as if we'd pass just integer 2 */ static VALUE io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value) @@ -1895,7 +1947,7 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value) * buffer = IO::Buffer.new(8) * buffer.set_values([:U8, :U16], 0, [1, 2]) * buffer - * # => + * # => * # # * # 0x00000000 01 00 02 00 00 00 00 00 ........ */ @@ -2028,7 +2080,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source) * at +offset+ using +memcpy+. For copying String instances, see #set_string. * * buffer = IO::Buffer.new(32) - * # => + * # => * # # * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * @@ -2036,7 +2088,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source) * buffer.copy(IO::Buffer.for("test"), 8) * # => 4 -- size of data copied * buffer - * # => + * # => * # # * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test.... * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * @@ -2077,7 +2129,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source) static VALUE io_buffer_copy(int argc, VALUE *argv, VALUE self) { - if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4); + rb_check_arity(argc, 1, 4); struct rb_io_buffer *data = NULL; TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); @@ -2097,18 +2149,18 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self) * Read a chunk or all of the buffer into a string, in the specified * +encoding+. If no encoding is provided +Encoding::BINARY+ is used. * - * buffer = IO::Buffer.for('test') - * buffer.get_string - * # => "test" - * buffer.get_string(2) - * # => "st" - * buffer.get_string(2, 1) - * # => "s" + * buffer = IO::Buffer.for('test') + * buffer.get_string + * # => "test" + * buffer.get_string(2) + * # => "st" + * buffer.get_string(2, 1) + * # => "s" */ static VALUE io_buffer_get_string(int argc, VALUE *argv, VALUE self) { - if (argc > 3) rb_error_arity(argc, 0, 3); + rb_check_arity(argc, 0, 3); struct rb_io_buffer *data = NULL; TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); @@ -2167,7 +2219,7 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self) static VALUE io_buffer_set_string(int argc, VALUE *argv, VALUE self) { - if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4); + rb_check_arity(argc, 1, 4); struct rb_io_buffer *data = NULL; TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); @@ -2229,7 +2281,7 @@ rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length) static VALUE io_buffer_clear(int argc, VALUE *argv, VALUE self) { - if (argc > 3) rb_error_arity(argc, 0, 3); + rb_check_arity(argc, 0, 3); struct rb_io_buffer *data = NULL; TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); @@ -2297,11 +2349,11 @@ io_buffer_read_internal(void *_argument) } VALUE -rb_io_buffer_read(VALUE self, VALUE io, size_t length) +rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset) { VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { - VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length); + VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, SIZET2NUM(length), SIZET2NUM(offset)); if (result != Qundef) { return result; @@ -2311,7 +2363,7 @@ rb_io_buffer_read(VALUE self, VALUE io, size_t length) struct rb_io_buffer *data = NULL; TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); - io_buffer_validate_range(data, 0, length); + io_buffer_validate_range(data, offset, length); int descriptor = rb_io_descriptor(io); @@ -2319,6 +2371,8 @@ rb_io_buffer_read(VALUE self, VALUE io, size_t length) size_t size; io_buffer_get_bytes_for_writing(data, &base, &size); + base = (unsigned char*)base + offset; + struct io_buffer_read_internal_argument argument = { .descriptor = descriptor, .base = base, @@ -2328,10 +2382,55 @@ rb_io_buffer_read(VALUE self, VALUE io, size_t length) return rb_thread_io_blocking_region(io_buffer_read_internal, &argument, descriptor); } +/* + * call-seq: read(io, [length, [offset]]) -> self + * + * Read at most +length+ bytes from +io+ into the buffer, starting at + * +offset+. + * + * If +length+ is not given, read until the end of the buffer. + * + * If +offset+ is not given, read from the beginning of the buffer. + * + * If +length+ is 0, read nothing. + * + * Example: + * + * buffer = IO::Buffer.for('test') + * # => + * # + * # 0x00000000 74 65 73 74 test + * buffer.read(File.open('/dev/urandom', 'rb'), 4) + * # => + * # + * # 0x00000000 2a 0e 0e 0e *... + */ static VALUE -io_buffer_read(VALUE self, VALUE io, VALUE length) +io_buffer_read(int argc, VALUE *argv, VALUE self) { - return rb_io_buffer_read(self, io, RB_NUM2SIZE(length)); + rb_check_arity(argc, 2, 3); + + VALUE io = argv[0]; + + size_t length; + if (argc >= 2) { + if (rb_int_negative_p(argv[1])) { + rb_raise(rb_eArgError, "Length can't be negative!"); + } + + length = NUM2SIZET(argv[1]); + } + + size_t offset = 0; + if (argc >= 3) { + if (rb_int_negative_p(argv[2])) { + rb_raise(rb_eArgError, "Offset can't be negative!"); + } + + offset = NUM2SIZET(argv[2]); + } + + return rb_io_buffer_read(self, io, length, offset); } struct io_buffer_pread_internal_argument { @@ -2367,11 +2466,11 @@ io_buffer_pread_internal(void *_argument) } VALUE -rb_io_buffer_pread(VALUE self, VALUE io, size_t length, rb_off_t offset) +rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset) { VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { - VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, self, length, offset); + VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, OFFT2NUM(from), self, SIZET2NUM(length), SIZET2NUM(offset)); if (result != Qundef) { return result; @@ -2393,16 +2492,36 @@ rb_io_buffer_pread(VALUE self, VALUE io, size_t length, rb_off_t offset) .descriptor = descriptor, .base = base, .size = length, - .offset = offset, + .offset = from, }; return rb_thread_io_blocking_region(io_buffer_pread_internal, &argument, descriptor); } static VALUE -io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset) +io_buffer_pread(int argc, VALUE *argv, VALUE self) { - return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset)); + rb_check_arity(argc, 3, 4); + + VALUE io = argv[0]; + rb_off_t from = NUM2OFFT(argv[1]); + + size_t length; + if (rb_int_negative_p(argv[2])) { + rb_raise(rb_eArgError, "Length can't be negative!"); + } + length = NUM2SIZET(argv[2]); + + size_t offset = 0; + if (argc >= 4) { + if (rb_int_negative_p(argv[3])) { + rb_raise(rb_eArgError, "Offset can't be negative!"); + } + + offset = NUM2SIZET(argv[3]); + } + + return rb_io_buffer_pread(self, io, from, length, offset); } struct io_buffer_write_internal_argument { @@ -2420,11 +2539,11 @@ io_buffer_write_internal(void *_argument) } VALUE -rb_io_buffer_write(VALUE self, VALUE io, size_t length) +rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset) { VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { - VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length); + VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, SIZET2NUM(length), SIZET2NUM(offset)); if (result != Qundef) { return result; @@ -2434,7 +2553,7 @@ rb_io_buffer_write(VALUE self, VALUE io, size_t length) struct rb_io_buffer *data = NULL; TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data); - io_buffer_validate_range(data, 0, length); + io_buffer_validate_range(data, offset, length); int descriptor = rb_io_descriptor(io); @@ -2442,6 +2561,8 @@ rb_io_buffer_write(VALUE self, VALUE io, size_t length) size_t size; io_buffer_get_bytes_for_reading(data, &base, &size); + base = (unsigned char *)base + offset; + struct io_buffer_write_internal_argument argument = { .descriptor = descriptor, .base = base, @@ -2452,9 +2573,31 @@ rb_io_buffer_write(VALUE self, VALUE io, size_t length) } static VALUE -io_buffer_write(VALUE self, VALUE io, VALUE length) +io_buffer_write(int argc, VALUE *argv, VALUE self) { - return rb_io_buffer_write(self, io, RB_NUM2SIZE(length)); + rb_check_arity(argc, 2, 3); + + VALUE io = argv[0]; + + size_t length; + if (argc >= 2) { + if (rb_int_negative_p(argv[1])) { + rb_raise(rb_eArgError, "Length can't be negative!"); + } + + length = NUM2SIZET(argv[1]); + } + + size_t offset = 0; + if (argc >= 3) { + if (rb_int_negative_p(argv[2])) { + rb_raise(rb_eArgError, "Offset can't be negative!"); + } + + offset = NUM2SIZET(argv[2]); + } + + return rb_io_buffer_write(self, io, length, offset); } struct io_buffer_pwrite_internal_argument { @@ -2490,11 +2633,11 @@ io_buffer_pwrite_internal(void *_argument) } VALUE -rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset) +rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset) { VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { - VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, self, length, OFFT2NUM(offset)); + VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, OFFT2NUM(from), self, SIZET2NUM(length), SIZET2NUM(offset)); if (result != Qundef) { return result; @@ -2516,16 +2659,36 @@ rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset) .descriptor = descriptor, .base = base, .size = length, - .offset = offset, + .offset = from, }; return rb_thread_io_blocking_region(io_buffer_pwrite_internal, &argument, descriptor); } static VALUE -io_buffer_pwrite(VALUE self, VALUE io, VALUE length, VALUE offset) +io_buffer_pwrite(int argc, VALUE *argv, VALUE self) { - return rb_io_buffer_pwrite(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset)); + rb_check_arity(argc, 3, 4); + + VALUE io = argv[0]; + rb_off_t from = NUM2OFFT(argv[1]); + + size_t length; + if (rb_int_negative_p(argv[2])) { + rb_raise(rb_eArgError, "Length can't be negative!"); + } + length = NUM2SIZET(argv[2]); + + size_t offset = 0; + if (argc >= 4) { + if (rb_int_negative_p(argv[3])) { + rb_raise(rb_eArgError, "Offset can't be negative!"); + } + + offset = NUM2SIZET(argv[3]); + } + + return rb_io_buffer_pwrite(self, io, from, length, offset); } static inline void @@ -2910,11 +3073,11 @@ io_buffer_not_inplace(VALUE self) * Empty buffer: * * buffer = IO::Buffer.new(8) # create empty 8-byte buffer - * # => + * # => * # # * # ... * buffer - * # => + * # => * # * # 0x00000000 00 00 00 00 00 00 00 00 * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2 @@ -2926,11 +3089,11 @@ io_buffer_not_inplace(VALUE self) * * string = 'data' * buffer = IO::Buffer.for(string) - * # => + * # => * # # * # ... * buffer - * # => + * # => * # # * # 0x00000000 64 61 74 61 data * @@ -2939,7 +3102,7 @@ io_buffer_not_inplace(VALUE self) * buffer.set_string('---', 1) # write content, starting from offset 1 * # => 3 * buffer - * # => + * # => * # # * # 0x00000000 64 2d 2d 2d d--- * string # original string changed, too @@ -2950,7 +3113,7 @@ io_buffer_not_inplace(VALUE self) * File.write('test.txt', 'test data') * # => 9 * buffer = IO::Buffer.map(File.open('test.txt')) - * # => + * # => * # # * # ... * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5 @@ -3037,7 +3200,7 @@ Init_IO_Buffer(void) rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0); // Manipulation: - rb_define_method(rb_cIOBuffer, "slice", rb_io_buffer_slice, 2); + rb_define_method(rb_cIOBuffer, "slice", io_buffer_slice, -1); rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1); rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1); rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1); @@ -3098,8 +3261,8 @@ Init_IO_Buffer(void) rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0); // IO operations: - rb_define_method(rb_cIOBuffer, "read", io_buffer_read, 2); - rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, 3); - rb_define_method(rb_cIOBuffer, "write", io_buffer_write, 2); - rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, 3); + rb_define_method(rb_cIOBuffer, "read", io_buffer_read, -1); + rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, -1); + rb_define_method(rb_cIOBuffer, "write", io_buffer_write, -1); + rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, -1); } diff --git a/scheduler.c b/scheduler.c index 675a0a6768efcd..785ad06f198328 100644 --- a/scheduler.c +++ b/scheduler.c @@ -232,43 +232,43 @@ rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io) } VALUE -rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length) +rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset) { VALUE arguments[] = { - io, buffer, SIZET2NUM(length) + io, buffer, SIZET2NUM(length), SIZET2NUM(offset) }; - return rb_check_funcall(scheduler, id_io_read, 3, arguments); + return rb_check_funcall(scheduler, id_io_read, 4, arguments); } VALUE -rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset) +rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset) { VALUE arguments[] = { - io, buffer, SIZET2NUM(length), OFFT2NUM(offset) + io, buffer, OFFT2NUM(from), SIZET2NUM(length), SIZET2NUM(offset) }; - return rb_check_funcall(scheduler, id_io_pread, 4, arguments); + return rb_check_funcall(scheduler, id_io_pread, 5, arguments); } VALUE -rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length) +rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset) { VALUE arguments[] = { - io, buffer, SIZET2NUM(length) + io, buffer, SIZET2NUM(length), SIZET2NUM(offset) }; - return rb_check_funcall(scheduler, id_io_write, 3, arguments); + return rb_check_funcall(scheduler, id_io_write, 4, arguments); } VALUE -rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset) +rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset) { VALUE arguments[] = { - io, buffer, SIZET2NUM(length), OFFT2NUM(offset) + io, buffer, OFFT2NUM(from), SIZET2NUM(length), SIZET2NUM(offset) }; - return rb_check_funcall(scheduler, id_io_pwrite, 4, arguments); + return rb_check_funcall(scheduler, id_io_pwrite, 5, arguments); } VALUE @@ -276,7 +276,7 @@ rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *base, size_t { VALUE buffer = rb_io_buffer_new(base, size, RB_IO_BUFFER_LOCKED); - VALUE result = rb_fiber_scheduler_io_read(scheduler, io, buffer, length); + VALUE result = rb_fiber_scheduler_io_read(scheduler, io, buffer, length, 0); rb_io_buffer_unlock(buffer); rb_io_buffer_free(buffer); @@ -289,7 +289,7 @@ rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *base, { VALUE buffer = rb_io_buffer_new((void*)base, size, RB_IO_BUFFER_LOCKED|RB_IO_BUFFER_READONLY); - VALUE result = rb_fiber_scheduler_io_write(scheduler, io, buffer, length); + VALUE result = rb_fiber_scheduler_io_write(scheduler, io, buffer, length, 0); rb_io_buffer_unlock(buffer); rb_io_buffer_free(buffer); diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index 96b22856d1a3d5..ceed6063388b80 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -263,81 +263,68 @@ def address_resolve(hostname) end class IOBufferScheduler < Scheduler - EAGAIN = Errno::EAGAIN::Errno + EAGAIN = -Errno::EAGAIN::Errno - def io_read(io, buffer, length) - offset = 0 + def io_read(io, buffer, length, offset) + total = 0 + io.nonblock = true - while true + while length >= 0 maximum_size = buffer.size - offset - result = blocking{io.read_nonblock(maximum_size, exception: false)} - # blocking{pp read: maximum_size, result: result, length: length} + result = blocking{buffer.read(io, maximum_size, offset)} - case result - when :wait_readable + if result > 0 + total += result + offset += result + break if result >= length + elsif result == 0 + break + elsif result == EAGAIN if length > 0 self.io_wait(io, IO::READABLE, nil) else - return -EAGAIN - end - when :wait_writable - if length > 0 - self.io_wait(io, IO::WRITABLE, nil) - else - return -EAGAIN + return result end - else - break unless result - - buffer.set_string(result, offset) - - size = result.bytesize - offset += size - break if size >= length - length -= size + elsif result < 0 + return result end end - return offset + return total end - def io_write(io, buffer, length) - offset = 0 + def io_write(io, buffer, length, offset) + total = 0 + io.nonblock = true - while true + while length >= 0 maximum_size = buffer.size - offset - chunk = buffer.get_string(offset, maximum_size) - result = blocking{io.write_nonblock(chunk, exception: false)} - - # blocking{pp write: maximum_size, result: result, length: length} + result = blocking{buffer.write(io, maximum_size, offset)} - case result - when :wait_readable - if length > 0 - self.io_wait(io, IO::READABLE, nil) - else - return -EAGAIN - end - when :wait_writable + if result > 0 + total += result + offset += result + break if result >= length + elsif result == 0 + break + elsif result == EAGAIN if length > 0 self.io_wait(io, IO::WRITABLE, nil) else - return -EAGAIN + return result end - else - offset += result - break if result >= length - length -= result + elsif result < 0 + return result end end - return offset + return total end def blocking(&block) - Fiber.new(blocking: true, &block).resume + Fiber.blocking(&block) end end diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 88b0a0280ae3c6..95ed98e1f4d9a2 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -169,16 +169,26 @@ def test_slice assert_equal("Hello World", buffer.get_string(8, 11)) end - def test_slice_bounds + def test_slice_arguments + buffer = IO::Buffer.for("Hello World") + + slice = buffer.slice + assert_equal "Hello World", slice.get_string + + slice = buffer.slice(2) + assert_equal("llo World", slice.get_string) + end + + def test_slice_bounds_error buffer = IO::Buffer.new(128) assert_raise ArgumentError do buffer.slice(128, 10) end - # assert_raise RuntimeError do - # pp buffer.slice(-10, 10) - # end + assert_raise ArgumentError do + buffer.slice(-10, 10) + end end def test_locked @@ -351,7 +361,7 @@ def test_pread io.seek(0) buffer = IO::Buffer.new(128) - buffer.pread(io, 5, 6) + buffer.pread(io, 6, 5) assert_equal "World", buffer.get_string(0, 5) assert_equal 0, io.tell @@ -364,7 +374,7 @@ def test_pwrite buffer = IO::Buffer.new(128) buffer.set_string("World") - buffer.pwrite(io, 5, 6) + buffer.pwrite(io, 6, 5) assert_equal 0, io.tell diff --git a/vm_core.h b/vm_core.h index aed15114e5bb2d..9aee3452102e79 100644 --- a/vm_core.h +++ b/vm_core.h @@ -1099,7 +1099,7 @@ typedef struct rb_thread_struct { rb_fiber_t *root_fiber; VALUE scheduler; - unsigned blocking; + unsigned int blocking; /* misc */ VALUE name; From 4e29ca0c4093133838eda852879b23ed4fad56b5 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sat, 8 Oct 2022 01:54:35 +0900 Subject: [PATCH 008/139] Add :ssl_min_version and :ssl_max_version options Replace :ssl_version option with these two new options. These provide access to OpenSSL::SSL::SSLContext#{min,max}_version=, which is the recommended way to specify SSL/TLS protocol versions. --- lib/open-uri.rb | 21 ++++++++++++++++++--- test/open-uri/test_ssl.rb | 37 ++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/lib/open-uri.rb b/lib/open-uri.rb index 2f7371039220cd..93e8cfcdb78aeb 100644 --- a/lib/open-uri.rb +++ b/lib/open-uri.rb @@ -99,7 +99,8 @@ module OpenURI :open_timeout => true, :ssl_ca_cert => nil, :ssl_verify_mode => nil, - :ssl_version => nil, + :ssl_min_version => nil, + :ssl_max_version => nil, :ftp_active_mode => false, :redirect => true, :encoding => nil, @@ -299,8 +300,8 @@ def OpenURI.open_http(buf, target, proxy, options) # :nodoc: require 'net/https' http.use_ssl = true http.verify_mode = options[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER - http.ssl_version = options[:ssl_version] if options[:ssl_version] && - OpenSSL::SSL::SSLContext::METHODS.include?(options[:ssl_version]) + http.min_version = options[:ssl_min_version] + http.max_version = options[:ssl_max_version] store = OpenSSL::X509::Store.new if options[:ssl_ca_cert] Array(options[:ssl_ca_cert]).each do |cert| @@ -702,6 +703,20 @@ module OpenRead # # :ssl_verify_mode is used to specify openssl verify mode. # + # [:ssl_min_version] + # Synopsis: + # :ssl_min_version=>:TLS1_2 + # + # :ssl_min_version option specifies the minimum allowed SSL/TLS protocol + # version. See also OpenSSL::SSL::SSLContext#min_version=. + # + # [:ssl_max_version] + # Synopsis: + # :ssl_max_version=>:TLS1_2 + # + # :ssl_max_version option specifies the maximum allowed SSL/TLS protocol + # version. See also OpenSSL::SSL::SSLContext#max_version=. + # # [:ftp_active_mode] # Synopsis: # :ftp_active_mode=>bool diff --git a/test/open-uri/test_ssl.rb b/test/open-uri/test_ssl.rb index 2d6149e6543fc4..3f94cab40fe82f 100644 --- a/test/open-uri/test_ssl.rb +++ b/test/open-uri/test_ssl.rb @@ -92,38 +92,37 @@ def test_validation_noverify } end - def test_validation_ssl_version - with_https {|srv, dr, url| - setup_validation(srv, dr) - URI.open("#{url}/data", :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE, :ssl_version => :TLSv1_2) {|f| - assert_equal("200", f.status[0]) - assert_equal("ddd", f.read) + def test_validation_failure + unless /mswin|mingw/ =~ RUBY_PLATFORM + # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by + # WEBrick + log_tester = lambda {|server_log| + assert_equal(1, server_log.length) + assert_match(/ERROR OpenSSL::SSL::SSLError:/, server_log[0]) } + end + with_https(log_tester) {|srv, dr, url, server_thread, server_log| + setup_validation(srv, dr) + assert_raise(OpenSSL::SSL::SSLError) { URI.open("#{url}/data") {} } } end - def test_validate_bad_ssl_version_silently + def test_ssl_min_version with_https {|srv, dr, url| setup_validation(srv, dr) - URI.open("#{url}/data", :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE, :ssl_version => :TLS_no_such_version) {|f| + URI.open("#{url}/data", :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE, :ssl_min_version => :TLS1_2) {|f| assert_equal("200", f.status[0]) assert_equal("ddd", f.read) } } end - def test_validation_failure - unless /mswin|mingw/ =~ RUBY_PLATFORM - # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by - # WEBrick - log_tester = lambda {|server_log| - assert_equal(1, server_log.length) - assert_match(/ERROR OpenSSL::SSL::SSLError:/, server_log[0]) - } - end - with_https(log_tester) {|srv, dr, url, server_thread, server_log| + def test_bad_ssl_version + with_https(nil) {|srv, dr, url| setup_validation(srv, dr) - assert_raise(OpenSSL::SSL::SSLError) { URI.open("#{url}/data") {} } + assert_raise(ArgumentError) { + URI.open("#{url}/data", :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE, :ssl_min_version => :TLS_no_such_version) {} + } } end From 04d291a490517eb32ea02df636fc8f1f1ed57873 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Wed, 12 Oct 2022 15:56:35 +1300 Subject: [PATCH 009/139] Simplify implementation of scheduler `io_read` and `io_write`. (#6527) --- test/fiber/scheduler.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index ceed6063388b80..322564fe6d5536 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -269,15 +269,14 @@ def io_read(io, buffer, length, offset) total = 0 io.nonblock = true - while length >= 0 + while true maximum_size = buffer.size - offset - result = blocking{buffer.read(io, maximum_size, offset)} if result > 0 total += result offset += result - break if result >= length + break if total >= length elsif result == 0 break elsif result == EAGAIN @@ -298,15 +297,14 @@ def io_write(io, buffer, length, offset) total = 0 io.nonblock = true - while length >= 0 + while true maximum_size = buffer.size - offset - result = blocking{buffer.write(io, maximum_size, offset)} if result > 0 total += result offset += result - break if result >= length + break if total >= length elsif result == 0 break elsif result == EAGAIN From c67e49688642b5a650f53765dd11c1202d1ea57f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 11:38:44 +0900 Subject: [PATCH 010/139] sync_default_gems: Should match with the beginning of the strings `git status -z` result is NUL-separated, and can contain newline characters. --- tool/sync_default_gems.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 1e8c086dcf1409..530ec6e7b3b124 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -478,13 +478,13 @@ def sync_default_gems_with_commits(gem, ranges, edit: nil) skipped = true elsif /^CONFLICT/ =~ result result = pipe_readlines(%W"git status --porcelain -z") - result.map! {|line| line[/^.U (.*)/, 1]} + result.map! {|line| line[/\A.U (.*)/, 1]} result.compact! ignore, conflict = result.partition {|name| IGNORE_FILE_PATTERN =~ name} unless ignore.empty? system(*%W"git reset HEAD --", *ignore) File.unlink(*ignore) - ignore = pipe_readlines(%W"git status --porcelain -z" + ignore).map! {|line| line[/^.. (.*)/, 1]} + ignore = pipe_readlines(%W"git status --porcelain -z" + ignore).map! {|line| line[/\A.. (.*)/, 1]} system(*%W"git checkout HEAD --", *ignore) unless ignore.empty? end unless conflict.empty? From df588440ee01e91d87cb815ff85dd6c785dc7827 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 11:50:25 +0900 Subject: [PATCH 011/139] sync_default_gems: Replace the external URIs to docs with rdoc-ref --- tool/sync_default_gems.rb | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 530ec6e7b3b124..3aabd3739c50d2 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -84,6 +84,30 @@ def pipe_readlines(args, rs: "\0", chomp: true) end end +def replace_rdoc_ref(file) + src = File.binread(file) + src.gsub!(%r[\[\Khttps://docs\.ruby-lang\.org/en/master/(([A-Z]\w+(?:/[A-Z]\w+)*)|\w+_rdoc)\.html(\#\S+)?(?=\])]) do + name, mod, label = $1, $2, $3 + mod &&= mod.gsub('/', '::') + if label && (m = label.match(/\A\#(?:method-([ci])|(?:(?:class|module)-#{mod}-)?label)-([-+\w]+)\z/)) + scope, label = m[1], m[2] + scope = scope ? scope.tr('ci', '.#') : '@' + end + "rdoc-ref:#{mod || name.chomp("_rdoc") + ".rdoc"}#{scope}#{label}" + end or return false + File.rename(file, file + "~") + File.binwrite(file, src) + return true +end + +def replace_rdoc_ref_all + result = pipe_readlines(%W"git status porcelain -z -- *.c *.rb *.rdoc") + result.map! {|line| line[/\A.M (.*)/, 1]} + result.compact! + result = pipe_readlines(%W"git grep -z -l -F [https://docs.ruby-lang.org/en/master/ --" + result) + result.inject(false) {|changed, file| changed | replace_rdoc_ref(file)} +end + # We usually don't use this. Please consider using #sync_default_gems_with_commits instead. def sync_default_gems(gem) repo = REPOSITORIES[gem.to_sym] @@ -382,6 +406,7 @@ def sync_default_gems(gem) else sync_lib gem, upstream end + replace_rdoc_ref_all end IGNORE_FILE_PATTERN = @@ -508,6 +533,10 @@ def sync_default_gems_with_commits(gem, ranges, edit: nil) next end + if replace_rdoc_ref_all + `git commit --amend --no-edit` + end + puts "Update commit message: #{sha}" IO.popen(%W[git filter-branch -f --msg-filter #{[filter, repo, sha].join(' ')} -- HEAD~1..HEAD], &:read) From 2b5d4fe28dc548f782be8d71de8b472000e6b38e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 11:52:46 +0900 Subject: [PATCH 012/139] sync_default_gems: Add rdoc-ref command to test --- tool/sync_default_gems.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 3aabd3739c50d2..8b7fae2db3d95d 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -642,6 +642,17 @@ def update_default_gems(gem, release: false) abort unless ARGV.size == 2 message_filter(*ARGV) exit +when "rdoc-ref" + ARGV.shift + pattern = ARGV.empty? ? %w[*.c *.rb *.rdoc] : ARGV + result = pipe_readlines(%W"git grep -z -l -F [https://docs.ruby-lang.org/en/master/ --" + pattern) + result.inject(false) do |changed, file| + if replace_rdoc_ref(file) + puts "replaced rdoc-ref in #{file}" + changed = true + end + changed + end when nil, "-h", "--help" puts <<-HELP \e[1mSync with upstream code of default libraries\e[0m From 3539da64fc42d6eb76f1d4c3ccd219c3259ecd8b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 11:54:16 +0900 Subject: [PATCH 013/139] [DOC] Replace the external URIs to docs with rdoc-ref --- ext/date/date_core.c | 46 ++++++++++++++++++++++---------------------- lib/csv/table.rb | 2 +- lib/fileutils.rb | 36 +++++++++++++++++----------------- lib/logger.rb | 8 ++++---- lib/pstore.rb | 8 ++++---- lib/set.rb | 4 ++-- lib/tempfile.rb | 18 ++++++++--------- 7 files changed, 61 insertions(+), 61 deletions(-) diff --git a/ext/date/date_core.c b/ext/date/date_core.c index 83d493c794842a..96653e0a7879e2 100644 --- a/ext/date/date_core.c +++ b/ext/date/date_core.c @@ -4377,7 +4377,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, * Date._strptime('2001-02-03', '%Y-%m-%d') # => {:year=>2001, :mon=>2, :mday=>3} * * For other formats, see - * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]. + * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]. * (Unlike Date.strftime, does not support flags and width.) * * See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html]. @@ -4406,7 +4406,7 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass) * Date.strptime('sat3feb01', '%a%d%b%y') # => # * * For other formats, see - * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]. + * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]. * (Unlike Date.strftime, does not support flags and width.) * * See argument {start}[rdoc-ref:calendars.rdoc@Argument+start]. @@ -4506,7 +4506,7 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass) * This method recognizes many forms in +string+, * but it is not a validator. * For formats, see - * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings] + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings] * * If +string+ does not specify a valid date, * the result is unpredictable; @@ -4541,7 +4541,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass) * This method recognizes many forms in +string+, * but it is not a validator. * For formats, see - * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings] + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings] * If +string+ does not specify a valid date, * the result is unpredictable; * consider using Date._strptime instead. @@ -4606,7 +4606,7 @@ VALUE date__jisx0301(VALUE); * Date._iso8601(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should contain - * an {ISO 8601 formatted date}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications]: + * an {ISO 8601 formatted date}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications]: * * d = Date.new(2001, 2, 3) * s = d.iso8601 # => "2001-02-03" @@ -4633,7 +4633,7 @@ date_s__iso8601(int argc, VALUE *argv, VALUE klass) * * Returns a new \Date object with values parsed from +string+, * which should contain - * an {ISO 8601 formatted date}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications]: + * an {ISO 8601 formatted date}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications]: * * d = Date.new(2001, 2, 3) * s = d.iso8601 # => "2001-02-03" @@ -4676,7 +4676,7 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass) * Date._rfc3339(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should be a valid - * {RFC 3339 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+3339+Format]: + * {RFC 3339 format}[rdoc-ref:strftime_formatting.rdoc@RFC+3339+Format]: * * d = Date.new(2001, 2, 3) * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00" @@ -4704,7 +4704,7 @@ date_s__rfc3339(int argc, VALUE *argv, VALUE klass) * * Returns a new \Date object with values parsed from +string+, * which should be a valid - * {RFC 3339 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+3339+Format]: + * {RFC 3339 format}[rdoc-ref:strftime_formatting.rdoc@RFC+3339+Format]: * * d = Date.new(2001, 2, 3) * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00" @@ -4816,7 +4816,7 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass) * Date._rfc2822(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should be a valid - * {RFC 2822 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+2822+Format]: + * {RFC 2822 date format}[rdoc-ref:strftime_formatting.rdoc@RFC+2822+Format]: * * d = Date.new(2001, 2, 3) * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" @@ -4846,7 +4846,7 @@ date_s__rfc2822(int argc, VALUE *argv, VALUE klass) * * Returns a new \Date object with values parsed from +string+, * which should be a valid - * {RFC 2822 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+2822+Format]: + * {RFC 2822 date format}[rdoc-ref:strftime_formatting.rdoc@RFC+2822+Format]: * * d = Date.new(2001, 2, 3) * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" @@ -4890,7 +4890,7 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass) * Date._httpdate(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should be a valid - * {HTTP date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-HTTP+Format]: + * {HTTP date format}[rdoc-ref:strftime_formatting.rdoc@HTTP+Format]: * * d = Date.new(2001, 2, 3) * s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" @@ -4916,7 +4916,7 @@ date_s__httpdate(int argc, VALUE *argv, VALUE klass) * * Returns a new \Date object with values parsed from +string+, * which should be a valid - * {HTTP date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-HTTP+Format]: + * {HTTP date format}[rdoc-ref:strftime_formatting.rdoc@HTTP+Format]: * * d = Date.new(2001, 2, 3) s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" @@ -4958,7 +4958,7 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass) * Date._jisx0301(string, limit: 128) -> hash * * Returns a hash of values parsed from +string+, which should be a valid - * {JIS X 0301 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-JIS+X+0301+Format]: + * {JIS X 0301 date format}[rdoc-ref:strftime_formatting.rdoc@JIS+X+0301+Format]: * * d = Date.new(2001, 2, 3) * s = d.jisx0301 # => "H13.02.03" @@ -4984,7 +4984,7 @@ date_s__jisx0301(int argc, VALUE *argv, VALUE klass) * Date.jisx0301(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date * * Returns a new \Date object with values parsed from +string+, - * which should be a valid {JIS X 0301 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-JIS+X+0301+Format]: + * which should be a valid {JIS X 0301 format}[rdoc-ref:strftime_formatting.rdoc@JIS+X+0301+Format]: * * d = Date.new(2001, 2, 3) * s = d.jisx0301 # => "H13.02.03" @@ -6971,7 +6971,7 @@ static VALUE strftimev(const char *, VALUE, * to_s -> string * * Returns a string representation of the date in +self+ - * in {ISO 8601 extended date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications] + * in {ISO 8601 extended date format}[rdoc-ref:strftime_formatting.rdoc@ISO+8601+Format+Specifications] * ('%Y-%m-%d'): * * Date.new(2001, 2, 3).to_s # => "2001-02-03" @@ -7252,7 +7252,7 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self, * Date.new(2001, 2, 3).strftime # => "2001-02-03" * * For other formats, see - * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]. + * {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]. * */ static VALUE @@ -7284,7 +7284,7 @@ strftimev(const char *fmt, VALUE self, * asctime -> string * * Equivalent to #strftime with argument '%a %b %e %T %Y' - * (or its {shorthand form}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Shorthand+Conversion+Specifiers] + * (or its {shorthand form}[rdoc-ref:strftime_formatting.rdoc@Shorthand+Conversion+Specifiers] * '%c'): * * Date.new(2001, 2, 3).asctime # => "Sat Feb 3 00:00:00 2001" @@ -7304,7 +7304,7 @@ d_lite_asctime(VALUE self) * iso8601 -> string * * Equivalent to #strftime with argument '%Y-%m-%d' - * (or its {shorthand form}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Shorthand+Conversion+Specifiers] + * (or its {shorthand form}[rdoc-ref:strftime_formatting.rdoc@Shorthand+Conversion+Specifiers] * '%F'); * * Date.new(2001, 2, 3).iso8601 # => "2001-02-03" @@ -7322,7 +7322,7 @@ d_lite_iso8601(VALUE self) * rfc3339 -> string * * Equivalent to #strftime with argument '%FT%T%:z'; - * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]: + * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]: * * Date.new(2001, 2, 3).rfc3339 # => "2001-02-03T00:00:00+00:00" * @@ -7338,7 +7338,7 @@ d_lite_rfc3339(VALUE self) * rfc2822 -> string * * Equivalent to #strftime with argument '%a, %-d %b %Y %T %z'; - * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]: + * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]: * * Date.new(2001, 2, 3).rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" * @@ -7355,7 +7355,7 @@ d_lite_rfc2822(VALUE self) * httpdate -> string * * Equivalent to #strftime with argument '%a, %d %b %Y %T GMT'; - * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]: + * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]: * * Date.new(2001, 2, 3).httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" * @@ -9392,7 +9392,7 @@ Init_date_core(void) * calendar dates. * * Consider using - * {class Time}[https://docs.ruby-lang.org/en/master/Time.html] + * {class Time}[rdoc-ref:Time] * instead of class \Date if: * * - You need both dates and times; \Date handles only dates. @@ -9444,7 +9444,7 @@ Init_date_core(void) * Date.strptime('fri31dec99', '%a%d%b%y') # => # * * See also the specialized methods in - * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings] + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc@Specialized+Format+Strings] * * == Argument +limit+ * diff --git a/lib/csv/table.rb b/lib/csv/table.rb index 1ce0dd6daf62c6..0b62ae89ae75d4 100644 --- a/lib/csv/table.rb +++ b/lib/csv/table.rb @@ -172,7 +172,7 @@ def headers # # Raises an exception if the access mode is :row # and +n+ is not an - # {Integer-convertible object}[https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-Integer-Convertible+Objects]. + # {Integer-convertible object}[rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects]. # table.by_row! # => # # # Raises TypeError (no implicit conversion of String into Integer): # table['Name'] diff --git a/lib/fileutils.rb b/lib/fileutils.rb index 74bb904e28143a..745170a121c1bf 100644 --- a/lib/fileutils.rb +++ b/lib/fileutils.rb @@ -12,8 +12,8 @@ # # First, what’s elsewhere. \Module \FileUtils: # -# - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html]. -# - Supplements {class File}[https://docs.ruby-lang.org/en/master/File.html] +# - Inherits from {class Object}[rdoc-ref:Object]. +# - Supplements {class File}[rdoc-ref:File] # (but is not included or extended there). # # Here, module \FileUtils provides methods that are useful for: @@ -162,8 +162,8 @@ # by applying a special pre-process: # # - If the target path points to a directory, this method uses methods -# {File#chown}[https://docs.ruby-lang.org/en/master/File.html#method-i-chown] -# and {File#chmod}[https://docs.ruby-lang.org/en/master/File.html#method-i-chmod] +# {File#chown}[rdoc-ref:File#chown] +# and {File#chmod}[rdoc-ref:File#chmod] # in removing directories. # - The owner of the target directory should be either the current process # or the super user (root). @@ -291,7 +291,7 @@ def remove_trailing_slash(dir) #:nodoc: # # With no keyword arguments, creates a directory at each +path+ in +list+ # by calling: Dir.mkdir(path, mode); - # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]: + # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]: # # FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"] # FileUtils.mkdir('tmp4') # => ["tmp4"] @@ -299,7 +299,7 @@ def remove_trailing_slash(dir) #:nodoc: # Keyword arguments: # # - mode: mode - also calls File.chmod(mode, path); - # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod]. + # see {File.chmod}[rdoc-ref:File.chmod]. # - noop: true - does not create directories. # - verbose: true - prints an equivalent command: # @@ -339,7 +339,7 @@ def mkdir(list, mode: nil, noop: nil, verbose: nil) # With no keyword arguments, creates a directory at each +path+ in +list+, # along with any needed ancestor directories, # by calling: Dir.mkdir(path, mode); - # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]: + # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]: # # FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"] # FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"] @@ -347,7 +347,7 @@ def mkdir(list, mode: nil, noop: nil, verbose: nil) # Keyword arguments: # # - mode: mode - also calls File.chmod(mode, path); - # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod]. + # see {File.chmod}[rdoc-ref:File.chmod]. # - noop: true - does not create directories. # - verbose: true - prints an equivalent command: # @@ -417,7 +417,7 @@ def fu_mkdir(path, mode) #:nodoc: # # With no keyword arguments, removes the directory at each +path+ in +list+, # by calling: Dir.rmdir(path); - # see {Dir.rmdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-rmdir]: + # see {Dir.rmdir}[rdoc-ref:Dir.rmdir]: # # FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"] # FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"] @@ -1044,7 +1044,7 @@ def copy_file(src, dest, preserve = false, dereference = true) module_function :copy_file # Copies \IO stream +src+ to \IO stream +dest+ via - # {IO.copy_stream}[https://docs.ruby-lang.org/en/master/IO.html#method-c-copy_stream]. + # {IO.copy_stream}[rdoc-ref:IO.copy_stream]. # # Related: {methods for copying}[rdoc-ref:FileUtils@Copying]. # @@ -1560,14 +1560,14 @@ def compare_stream(a, b) # Keyword arguments: # # - group: group - changes the group if not +nil+, - # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown]. + # using {File.chown}[rdoc-ref:File.chown]. # - mode: permissions - changes the permissions. - # using {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod]. + # using {File.chmod}[rdoc-ref:File.chmod]. # - noop: true - does not copy entries; returns +nil+. # - owner: owner - changes the owner if not +nil+, - # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown]. + # using {File.chown}[rdoc-ref:File.chown]. # - preserve: true - preserve timestamps - # using {File.utime}[https://docs.ruby-lang.org/en/master/File.html#method-c-utime]. + # using {File.utime}[rdoc-ref:File.utime]. # - verbose: true - prints an equivalent command: # # FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true) @@ -1704,9 +1704,9 @@ def mode_to_s(mode) #:nodoc: # returns +list+ if it is an array, [list] otherwise: # # - Modifies each entry that is a regular file using - # {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod]. + # {File.chmod}[rdoc-ref:File.chmod]. # - Modifies each entry that is a symbolic link using - # {File.lchmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchmod]. + # {File.lchmod}[rdoc-ref:File.lchmod]. # # Argument +list+ or its elements # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]. @@ -1806,9 +1806,9 @@ def chmod_R(mode, list, noop: nil, verbose: nil, force: nil) # returns +list+ if it is an array, [list] otherwise: # # - Modifies each entry that is a regular file using - # {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown]. + # {File.chown}[rdoc-ref:File.chown]. # - Modifies each entry that is a symbolic link using - # {File.lchown}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchown]. + # {File.lchown}[rdoc-ref:File.lchown]. # # Argument +list+ or its elements # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]. diff --git a/lib/logger.rb b/lib/logger.rb index cafebc510a7cc0..7e4dacc911f7d4 100644 --- a/lib/logger.rb +++ b/lib/logger.rb @@ -147,7 +147,7 @@ # when the entry is created. # # The logged timestamp is formatted by method -# {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime] +# {Time#strftime}[rdoc-ref:Time#strftime] # using this format string: # # '%Y-%m-%dT%H:%M:%S.%6N' @@ -365,7 +365,7 @@ # You can set a different format using create-time option # +shift_period_suffix+; # see details and suggestions at -# {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime]. +# {Time#strftime}[rdoc-ref:Time#strftime]. # class Logger _, name, rev = %w$Id$ @@ -425,7 +425,7 @@ def level=(severity) # Argument +datetime_format+ should be either of these: # # - A string suitable for use as a format for method - # {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime]. + # {Time#strftime}[rdoc-ref:Time#strftime]. # - +nil+: the logger uses '%Y-%m-%dT%H:%M:%S.%6N'. # def datetime_format=(datetime_format) @@ -453,7 +453,7 @@ def datetime_format # The proc should return a string containing the formatted entry. # # This custom formatter uses - # {String#dump}[https://docs.ruby-lang.org/en/master/String.html#method-i-dump] + # {String#dump}[rdoc-ref:String#dump] # to escape the message string: # # logger = Logger.new($stdout, progname: 'mung') diff --git a/lib/pstore.rb b/lib/pstore.rb index 8d7137aa39e7fb..99be1d48496337 100644 --- a/lib/pstore.rb +++ b/lib/pstore.rb @@ -71,7 +71,7 @@ # when the store is created (see PStore.new). # The objects are stored and retrieved using # module Marshal, which means that certain objects cannot be added to the store; -# see {Marshal::dump}[https://docs.ruby-lang.org/en/master/Marshal.html#method-c-dump]. +# see {Marshal::dump}[rdoc-ref:Marshal.dump]. # # == Entries # @@ -79,11 +79,11 @@ # Each entry has a key and a value, just as in a hash: # # - Key: as in a hash, the key can be (almost) any object; -# see {Hash Keys}[https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Hash+Keys]. +# see {Hash Keys}[rdoc-ref:Hash@Hash+Keys]. # You may find it convenient to keep it simple by using only # symbols or strings as keys. # - Value: the value may be any object that can be marshalled by \Marshal -# (see {Marshal::dump}[https://docs.ruby-lang.org/en/master/Marshal.html#method-c-dump]) +# (see {Marshal::dump}[rdoc-ref:Marshal.dump]) # and in fact may be a collection # (e.g., an array, a hash, a set, a range, etc). # That collection may in turn contain nested objects, @@ -194,7 +194,7 @@ # end # # And recall that you can use -# {dig methods}[https://docs.ruby-lang.org/en/master/dig_methods_rdoc.html] +# {dig methods}[rdoc-ref:dig_methods.rdoc] # in a returned hierarchy of objects. # # == Working with the Store diff --git a/lib/set.rb b/lib/set.rb index 0490654183a450..df1e68d0817364 100644 --- a/lib/set.rb +++ b/lib/set.rb @@ -66,8 +66,8 @@ # # First, what's elsewhere. \Class \Set: # -# - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html#class-Object-label-What-27s+Here]. -# - Includes {module Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-What-27s+Here], +# - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here]. +# - Includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here], # which provides dozens of additional methods. # # In particular, class \Set does not have many methods of its own diff --git a/lib/tempfile.rb b/lib/tempfile.rb index 8a6bcc3e14f47d..c3263ed3c68201 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -104,7 +104,7 @@ class Tempfile < DelegateClass(File) # - Directory is the system temporary directory (system-dependent). # - Generated filename is unique in that directory. # - Permissions are 0600; - # see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions]. + # see {File Permissions}[rdoc-ref:File@File+Permissions]. # - Mode is 'w+' (read/write mode, positioned at the end). # # The underlying file is removed when the \Tempfile object dies @@ -136,12 +136,12 @@ class Tempfile < DelegateClass(File) # Tempfile.new('foo', '.') # => # # # Keyword arguments +mode+ and +options+ are passed directly to method - # {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]: + # {File.open}[rdoc-ref:File.open]: # # - The value given with +mode+ must be an integer, # and may be expressed as the logical OR of constants defined in - # {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html]. - # - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options]. + # {File::Constants}[rdoc-ref:File::Constants]. + # - For +options+, see {Open Options}[rdoc-ref:IO@Open+Options]. # # Related: Tempfile.create. # @@ -344,11 +344,11 @@ def open(*args, **kw) # # With no block given and no arguments, creates and returns file whose: # -# - Class is {File}[https://docs.ruby-lang.org/en/master/File.html] (not \Tempfile). +# - Class is {File}[rdoc-ref:File] (not \Tempfile). # - Directory is the system temporary directory (system-dependent). # - Generated filename is unique in that directory. # - Permissions are 0600; -# see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions]. +# see {File Permissions}[rdoc-ref:File@File+Permissions]. # - Mode is 'w+' (read/write mode, positioned at the end). # # With no block, the file is not removed automatically, @@ -380,12 +380,12 @@ def open(*args, **kw) # Tempfile.create('foo', '.') # => # # # Keyword arguments +mode+ and +options+ are passed directly to method -# {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]: +# {File.open}[rdoc-ref:File.open]: # # - The value given with +mode+ must be an integer, # and may be expressed as the logical OR of constants defined in -# {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html]. -# - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options]. +# {File::Constants}[rdoc-ref:File::Constants]. +# - For +options+, see {Open Options}[rdoc-ref:IO@Open+Options]. # # With a block given, creates the file as above, passes it to the block, # and returns the block's value; From b57ecc3eb88db40517f43ed44c007e2ddf5a8b2f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 13:24:53 +0900 Subject: [PATCH 014/139] sync_default_gems: Replace the URIs to be redirected The reference generated by using RDoc without the proper `--page-dir` option (or `.rdoc_options`) file may contain `/doc/`. Since these URIs are redirected by the server now, replace such URIs with the corresponding rdoc-refs too. --- tool/sync_default_gems.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 8b7fae2db3d95d..d585f5f14aeae7 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -86,7 +86,7 @@ def pipe_readlines(args, rs: "\0", chomp: true) def replace_rdoc_ref(file) src = File.binread(file) - src.gsub!(%r[\[\Khttps://docs\.ruby-lang\.org/en/master/(([A-Z]\w+(?:/[A-Z]\w+)*)|\w+_rdoc)\.html(\#\S+)?(?=\])]) do + src.gsub!(%r[\[\Khttps://docs\.ruby-lang\.org/en/master(?:/doc)?/(([A-Z]\w+(?:/[A-Z]\w+)*)|\w+_rdoc)\.html(\#\S+)?(?=\])]) do name, mod, label = $1, $2, $3 mod &&= mod.gsub('/', '::') if label && (m = label.match(/\A\#(?:method-([ci])|(?:(?:class|module)-#{mod}-)?label)-([-+\w]+)\z/)) From 0360fca4ad9cfcffbcc5e5a016308563267a5fcb Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 13:30:25 +0900 Subject: [PATCH 015/139] [DOC] Replace the external URIs to docs with rdoc-ref --- lib/csv/row.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/csv/row.rb b/lib/csv/row.rb index 0f465ea2a31dac..7f2e7e780784a2 100644 --- a/lib/csv/row.rb +++ b/lib/csv/row.rb @@ -570,7 +570,7 @@ def to_csv(**options) # by +index_or_header+ and +specifiers+. # # The nested objects may be instances of various classes. - # See {Dig Methods}[https://docs.ruby-lang.org/en/master/doc/dig_methods_rdoc.html]. + # See {Dig Methods}[rdoc-ref:dig_methods.rdoc]. # # Examples: # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" From ed01bacf2778a5e3bd813ded5f01d7ccc85b289d Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 19:53:40 +0900 Subject: [PATCH 016/139] [ruby/psych] Abandon when libyaml is not found https://github.com/ruby/psych/commit/0b89eda398 --- ext/psych/extconf.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ext/psych/extconf.rb b/ext/psych/extconf.rb index 24173567b405d9..afae08603de440 100644 --- a/ext/psych/extconf.rb +++ b/ext/psych/extconf.rb @@ -7,14 +7,6 @@ end yaml_source = with_config("libyaml-source-dir") -unless yaml_source # default to pre-installed libyaml - pkg_config('yaml-0.1') - dir_config('libyaml') - unless find_header('yaml.h') && find_library('yaml', 'yaml_get_version') - yaml_source = true # fallback to the bundled source if exists - end -end - if yaml_source yaml_source = yaml_source.gsub(/\$\((\w+)\)|\$\{(\w+)\}/) {ENV[$1||$2]} yaml_source = yaml_source.chomp("/") @@ -44,6 +36,11 @@ libyaml = "libyaml.#$LIBEXT" $cleanfiles << libyaml $LOCAL_LIBS.prepend("$(LIBYAML) ") +else # default to pre-installed libyaml + pkg_config('yaml-0.1') + dir_config('libyaml') + find_header('yaml.h') or abort "yaml.h not found" + find_library('yaml', 'yaml_get_version') or "libyaml not found" end create_makefile 'psych' do |mk| From 66a650ec41a583ec58003737b23b42ec8f069920 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 20:17:46 +0900 Subject: [PATCH 017/139] [ruby/psych] Fix missing `abort` call https://github.com/ruby/psych/commit/de2b98c7b7 Co-authored-by: Olle Jonsson --- ext/psych/extconf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/psych/extconf.rb b/ext/psych/extconf.rb index afae08603de440..41daf8c2387369 100644 --- a/ext/psych/extconf.rb +++ b/ext/psych/extconf.rb @@ -40,7 +40,7 @@ pkg_config('yaml-0.1') dir_config('libyaml') find_header('yaml.h') or abort "yaml.h not found" - find_library('yaml', 'yaml_get_version') or "libyaml not found" + find_library('yaml', 'yaml_get_version') or abort "libyaml not found" end create_makefile 'psych' do |mk| From 7a9f865a1d855109c7990b5fee21f92cc951ce60 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Wed, 12 Oct 2022 19:02:11 +0900 Subject: [PATCH 018/139] Do not read cached_id from callcache on stack The inline cache is initialized by vm_cc_attr_index_set only when vm_cc_markable(cc). However, vm_getivar attempted to read the cache even if the cc is not vm_cc_markable. This caused a condition that depends on uninitialized value. Here is an output of valgrind: ``` ==10483== Conditional jump or move depends on uninitialised value(s) ==10483== at 0x4C1D60: vm_getivar (vm_insnhelper.c:1171) ==10483== by 0x4C1D60: vm_call_ivar (vm_insnhelper.c:3257) ==10483== by 0x4E8E48: vm_call_symbol (vm_insnhelper.c:3481) ==10483== by 0x4EAD8C: vm_sendish (vm_insnhelper.c:5035) ==10483== by 0x4C62B2: vm_exec_core (insns.def:820) ==10483== by 0x4DD519: rb_vm_exec (vm.c:0) ==10483== by 0x4F00B3: invoke_block (vm.c:1417) ==10483== by 0x4F00B3: invoke_iseq_block_from_c (vm.c:1473) ==10483== by 0x4F00B3: invoke_block_from_c_bh (vm.c:1491) ==10483== by 0x4D42B6: rb_yield (vm_eval.c:0) ==10483== by 0x259128: rb_ary_each (array.c:2733) ==10483== by 0x4E8730: vm_call_cfunc_with_frame (vm_insnhelper.c:3227) ==10483== by 0x4EAD8C: vm_sendish (vm_insnhelper.c:5035) ==10483== by 0x4C6254: vm_exec_core (insns.def:801) ==10483== by 0x4DD519: rb_vm_exec (vm.c:0) ==10483== ``` In fact, the CI on FreeBSD 12 started failing since ad63b668e22e21c352b852f3119ae98a7acf99f1. ``` gmake[1]: Entering directory '/usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby' /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:924:in `complete': undefined method `complete' for nil:NilClass (NoMethodError) from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1816:in `block in visit' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1815:in `reverse_each' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1815:in `visit' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1847:in `block in complete' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1846:in `catch' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1846:in `complete' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1640:in `block in parse_in_order' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1632:in `catch' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1632:in `parse_in_order' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1626:in `order!' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1732:in `permute!' from /usr/home/chkbuild/chkbuild/tmp/build/20221011T163003Z/ruby/lib/optparse.rb:1757:in `parse!' from ./ext/extmk.rb:359:in `parse_args' from ./ext/extmk.rb:396:in `
' ``` This change adds a guard to read the cache only when vm_cc_markable(cc). It might be better to initialize the cache as INVALID_SHAPE_ID when the cc is not vm_cc_markable. --- vm_callinfo.h | 1 + vm_insnhelper.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/vm_callinfo.h b/vm_callinfo.h index f10cd9a000fcb6..3fb0fa8ca79ba4 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -385,6 +385,7 @@ vm_cc_attr_index_dest_shape_id(const struct rb_callcache *cc) static inline void vm_cc_atomic_shape_and_index(const struct rb_callcache *cc, shape_id_t * shape_id, attr_index_t * index) { + VM_ASSERT(vm_cc_markable(cc)); uintptr_t cache_value = cc->aux_.attr.value; // Atomically read 64 bits *shape_id = (shape_id_t)(cache_value >> SHAPE_FLAG_SHIFT); *index = (attr_index_t)(cache_value & SHAPE_FLAG_MASK) - 1; diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 2b4eda775a3699..9b9220a4e32696 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1160,7 +1160,13 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call attr_index_t index; if (is_attr) { - vm_cc_atomic_shape_and_index(cc, &cached_id, &index); + if (vm_cc_markable(cc)) { + vm_cc_atomic_shape_and_index(cc, &cached_id, &index); + } + else { + cached_id = INVALID_SHAPE_ID; + index = ATTR_INDEX_NOT_SET; + } } else { vm_ic_atomic_shape_and_index(ic, &cached_id, &index); From 9cfc45d6e52cf4ffde0375f8d3f2271c36e9e91c Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Wed, 12 Oct 2022 21:48:28 +0900 Subject: [PATCH 019/139] Suppress warnings in test/ruby/test_ast.rb The tests for error tolerance printed some warnings. This change suppresses them. --- test/ruby/test_ast.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index b91880be6d8e96..aaf626e801f6b4 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -568,6 +568,7 @@ def test_e_option end def test_error_tolerant + verbose_bak, $VERBOSE = $VERBOSE, false node = RubyVM::AbstractSyntaxTree.parse(<<~STR, error_tolerant: true) class A def m @@ -579,6 +580,8 @@ def m assert_nil($!) assert_equal(:SCOPE, node.type) + ensure + $VERBOSE = verbose_bak end def test_error_tolerant_end_is_short_for_method_define @@ -965,7 +968,12 @@ def m end def assert_error_tolerant(src, expected) - node = RubyVM::AbstractSyntaxTree.parse(src, error_tolerant: true) + begin + verbose_bak, $VERBOSE = $VERBOSE, false + node = RubyVM::AbstractSyntaxTree.parse(src, error_tolerant: true) + ensure + $VERBOSE = verbose_bak + end assert_nil($!) str = "" PP.pp(node, str, 80) From 504e388525ca7a1eeac64d08731f036b2ffc7b07 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Wed, 12 Oct 2022 21:51:23 +0900 Subject: [PATCH 020/139] Suppress a "warning: method redefined" in test/ruby/test_method.rb --- test/ruby/test_method.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index b1eee7381a69d9..d2c7b6e1dd5c4c 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -1314,6 +1314,7 @@ def foo m2 = c2.instance_method(:foo) c1.class_exec do + remove_method :foo def foo [:bar2] end From 70bc8cc6c219667dcae85bbf78bc0bc00e05c76e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 18:27:23 +0900 Subject: [PATCH 021/139] Adjust indents [ci skip] --- shape.c | 126 ++++++++++++++----------- vm_insnhelper.c | 239 ++++++++++++++++++++++++------------------------ 2 files changed, 197 insertions(+), 168 deletions(-) diff --git a/shape.c b/shape.c index 0f48fa1a865baf..80d135b897e73b 100644 --- a/shape.c +++ b/shape.c @@ -10,7 +10,8 @@ * Shape getters */ static rb_shape_t* -rb_shape_get_root_shape(void) { +rb_shape_get_root_shape(void) +{ return GET_VM()->root_shape; } @@ -21,12 +22,14 @@ rb_shape_id(rb_shape_t * shape) } static rb_shape_t* -rb_shape_get_frozen_root_shape(void) { +rb_shape_get_frozen_root_shape(void) +{ return GET_VM()->frozen_root_shape; } bool -rb_shape_root_shape_p(rb_shape_t* shape) { +rb_shape_root_shape_p(rb_shape_t* shape) +{ return shape == rb_shape_get_root_shape(); } @@ -90,7 +93,8 @@ rb_shape_get_shape(VALUE obj) } static rb_shape_t * -rb_shape_lookup_id(rb_shape_t* shape, ID id, enum shape_type shape_type) { +rb_shape_lookup_id(rb_shape_t* shape, ID id, enum shape_type shape_type) +{ while (shape->parent_id != INVALID_SHAPE_ID) { if (shape->edge_name == id) { // If the shape type is different, we don't @@ -136,25 +140,25 @@ get_next_shape_internal(rb_shape_t* shape, ID id, VALUE obj, enum shape_type sha new_shape->type = (uint8_t)shape_type; - switch(shape_type) { - case SHAPE_IVAR: - new_shape->iv_count = rb_shape_get_shape_by_id(new_shape->parent_id)->iv_count + 1; + switch (shape_type) { + case SHAPE_IVAR: + new_shape->iv_count = rb_shape_get_shape_by_id(new_shape->parent_id)->iv_count + 1; - // Check if we should update max_iv_count on the object's class - if (BUILTIN_TYPE(obj) == T_OBJECT) { - VALUE klass = rb_obj_class(obj); - if (new_shape->iv_count > RCLASS_EXT(klass)->max_iv_count) { - RCLASS_EXT(klass)->max_iv_count = new_shape->iv_count; - } + // Check if we should update max_iv_count on the object's class + if (BUILTIN_TYPE(obj) == T_OBJECT) { + VALUE klass = rb_obj_class(obj); + if (new_shape->iv_count > RCLASS_EXT(klass)->max_iv_count) { + RCLASS_EXT(klass)->max_iv_count = new_shape->iv_count; } - break; - case SHAPE_IVAR_UNDEF: - case SHAPE_FROZEN: - new_shape->iv_count = rb_shape_get_shape_by_id(new_shape->parent_id)->iv_count; - break; - case SHAPE_ROOT: - rb_bug("Unreachable"); - break; + } + break; + case SHAPE_IVAR_UNDEF: + case SHAPE_FROZEN: + new_shape->iv_count = rb_shape_get_shape_by_id(new_shape->parent_id)->iv_count; + break; + case SHAPE_ROOT: + rb_bug("Unreachable"); + break; } rb_id_table_insert(shape->edges, id, (VALUE)new_shape); @@ -199,13 +203,13 @@ rb_shape_transition_shape_frozen(VALUE obj) rb_shape_t* next_shape; if (shape == rb_shape_get_root_shape()) { - switch(BUILTIN_TYPE(obj)) { - case T_OBJECT: - case T_CLASS: - case T_MODULE: - break; - default: - return; + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + return; } next_shape = rb_shape_get_frozen_root_shape(); } @@ -239,22 +243,23 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id) } bool -rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) { +rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) +{ while (shape->parent_id != INVALID_SHAPE_ID) { if (shape->edge_name == id) { enum shape_type shape_type; shape_type = (enum shape_type)shape->type; - switch(shape_type) { - case SHAPE_IVAR: - RUBY_ASSERT(shape->iv_count > 0); - *value = shape->iv_count - 1; - return true; - case SHAPE_IVAR_UNDEF: - case SHAPE_ROOT: - return false; - case SHAPE_FROZEN: - rb_bug("Ivar should not exist on frozen transition\n"); + switch (shape_type) { + case SHAPE_IVAR: + RUBY_ASSERT(shape->iv_count > 0); + *value = shape->iv_count - 1; + return true; + case SHAPE_IVAR_UNDEF: + case SHAPE_ROOT: + return false; + case SHAPE_FROZEN: + rb_bug("Ivar should not exist on frozen transition\n"); } } shape = rb_shape_get_shape_by_id(shape->parent_id); @@ -313,14 +318,16 @@ static const rb_data_type_t shape_data_type = { }; static VALUE -rb_wrapped_shape_id(VALUE self) { +rb_wrapped_shape_id(VALUE self) +{ rb_shape_t * shape; TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); return INT2NUM(rb_shape_id(shape)); } static VALUE -rb_shape_type(VALUE self) { +rb_shape_type(VALUE self) +{ rb_shape_t * shape; TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); return INT2NUM(shape->type); @@ -339,16 +346,20 @@ rb_shape_parent_id(VALUE self) } } -static VALUE parse_key(ID key) { +static VALUE +parse_key(ID key) +{ if ((key & RUBY_ID_INTERNAL) == RUBY_ID_INTERNAL) { return LONG2NUM(key); - } else { + } + else { return ID2SYM(key); } } static VALUE -rb_shape_t_to_rb_cShape(rb_shape_t *shape) { +rb_shape_t_to_rb_cShape(rb_shape_t *shape) +{ union { const rb_shape_t *in; void *out; } deconst; VALUE res; deconst.in = shape; @@ -357,7 +368,8 @@ rb_shape_t_to_rb_cShape(rb_shape_t *shape) { return res; } -static enum rb_id_table_iterator_result rb_edges_to_hash(ID key, VALUE value, void *ref) +static enum rb_id_table_iterator_result +rb_edges_to_hash(ID key, VALUE value, void *ref) { rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_shape_t_to_rb_cShape((rb_shape_t*)value)); return ID_TABLE_CONTINUE; @@ -428,15 +440,21 @@ rb_shape_parent(VALUE self) } } -VALUE rb_shape_debug_shape(VALUE self, VALUE obj) { +VALUE +rb_shape_debug_shape(VALUE self, VALUE obj) +{ return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj)); } -VALUE rb_shape_root_shape(VALUE self) { +VALUE +rb_shape_root_shape(VALUE self) +{ return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape()); } -VALUE rb_shape_frozen_root_shape(VALUE self) { +VALUE +rb_shape_frozen_root_shape(VALUE self) +{ return rb_shape_t_to_rb_cShape(rb_shape_get_frozen_root_shape()); } @@ -456,7 +474,9 @@ static VALUE edges(struct rb_id_table* edges) return hash; } -VALUE rb_obj_shape(rb_shape_t* shape) { +VALUE +rb_obj_shape(rb_shape_t* shape) +{ VALUE rb_shape = rb_hash_new(); rb_hash_aset(rb_shape, ID2SYM(rb_intern("id")), INT2NUM(rb_shape_id(shape))); @@ -473,11 +493,15 @@ VALUE rb_obj_shape(rb_shape_t* shape) { return rb_shape; } -static VALUE shape_transition_tree(VALUE self) { +static VALUE +shape_transition_tree(VALUE self) +{ return rb_obj_shape(rb_shape_get_root_shape()); } -static VALUE next_shape_id(VALUE self) { +static VALUE +next_shape_id(VALUE self) +{ return INT2NUM(GET_VM()->next_shape_id); } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 9b9220a4e32696..0a62b0d86c8b4c 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1130,30 +1130,31 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call #endif switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - ivar_list = ROBJECT_IVPTR(obj); - VM_ASSERT(rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); + case T_OBJECT: + ivar_list = ROBJECT_IVPTR(obj); + VM_ASSERT(rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); #if !SHAPE_IN_BASIC_FLAGS - shape_id = ROBJECT_SHAPE_ID(obj); + shape_id = ROBJECT_SHAPE_ID(obj); #endif - break; - case T_CLASS: - case T_MODULE: - { - goto general_path; - } - default: - if (FL_TEST_RAW(obj, FL_EXIVAR)) { - struct gen_ivtbl *ivtbl; - rb_gen_ivtbl_get(obj, id, &ivtbl); + break; + case T_CLASS: + case T_MODULE: + { + goto general_path; + } + default: + if (FL_TEST_RAW(obj, FL_EXIVAR)) { + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); #if !SHAPE_IN_BASIC_FLAGS - shape_id = ivtbl->shape_id; + shape_id = ivtbl->shape_id; #endif - ivar_list = ivtbl->ivptr; - } else { - return Qnil; - } + ivar_list = ivtbl->ivptr; + } + else { + return Qnil; + } } shape_id_t cached_id; @@ -1172,7 +1173,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call vm_ic_atomic_shape_and_index(ic, &cached_id, &index); } - if(LIKELY(cached_id == shape_id)) { + if (LIKELY(cached_id == shape_id)) { if (index == ATTR_INDEX_NOT_SET) { return Qnil; } @@ -1185,14 +1186,16 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call if (is_attr) { if (cached_id != INVALID_SHAPE_ID) { RB_DEBUG_COUNTER_INC(ivar_get_cc_miss_set); - } else { + } + else { RB_DEBUG_COUNTER_INC(ivar_get_cc_miss_unset); } } else { if (cached_id != INVALID_SHAPE_ID) { RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_set); - } else { + } + else { RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_unset); } } @@ -1264,69 +1267,69 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, #if OPT_IC_FOR_IVAR switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - { - rb_check_frozen_internal(obj); - - attr_index_t index; - - uint32_t num_iv = ROBJECT_NUMIV(obj); - rb_shape_t* shape = rb_shape_get_shape(obj); - shape_id_t next_shape_id = ROBJECT_SHAPE_ID(obj); + { + rb_check_frozen_internal(obj); - rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); + attr_index_t index; - if (shape != next_shape) { - RUBY_ASSERT(next_shape->parent_id == rb_shape_id(shape)); - rb_shape_set_shape(obj, next_shape); - next_shape_id = ROBJECT_SHAPE_ID(obj); - } + uint32_t num_iv = ROBJECT_NUMIV(obj); + rb_shape_t* shape = rb_shape_get_shape(obj); + shape_id_t next_shape_id = ROBJECT_SHAPE_ID(obj); - if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree - if (index >= MAX_IVARS) { - rb_raise(rb_eArgError, "too many instance variables"); - } + rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); - populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr); - } - else { - rb_bug("Didn't find instance variable %s\n", rb_id2name(id)); - } + if (shape != next_shape) { + RUBY_ASSERT(next_shape->parent_id == rb_shape_id(shape)); + rb_shape_set_shape(obj, next_shape); + next_shape_id = ROBJECT_SHAPE_ID(obj); + } - // Ensure the IV buffer is wide enough to store the IV - if (UNLIKELY(index >= num_iv)) { - RUBY_ASSERT(index == num_iv); - rb_init_iv_list(obj); + if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree + if (index >= MAX_IVARS) { + rb_raise(rb_eArgError, "too many instance variables"); } - VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); - RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); + populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr); + } + else { + rb_bug("Didn't find instance variable %s\n", rb_id2name(id)); + } - return val; + // Ensure the IV buffer is wide enough to store the IV + if (UNLIKELY(index >= num_iv)) { + RUBY_ASSERT(index == num_iv); + rb_init_iv_list(obj); } + + VALUE *ptr = ROBJECT_IVPTR(obj); + RB_OBJ_WRITE(obj, &ptr[index], val); + RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); + + return val; + } case T_CLASS: case T_MODULE: - break; + break; default: - { - rb_ivar_set(obj, id, val); - shape_id_t next_shape_id = rb_shape_get_shape_id(obj); - rb_shape_t *next_shape = rb_shape_get_shape_by_id(next_shape_id); - attr_index_t index; - - if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree - if (index >= MAX_IVARS) { - rb_raise(rb_eArgError, "too many instance variables"); - } - - populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr); - } - else { - rb_bug("didn't find the id\n"); + { + rb_ivar_set(obj, id, val); + shape_id_t next_shape_id = rb_shape_get_shape_id(obj); + rb_shape_t *next_shape = rb_shape_get_shape_by_id(next_shape_id); + attr_index_t index; + + if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree + if (index >= MAX_IVARS) { + rb_raise(rb_eArgError, "too many instance variables"); } - return val; + populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr); + } + else { + rb_bug("didn't find the id\n"); } + + return val; + } } #endif RB_DEBUG_COUNTER_INC(ivar_set_ic_miss); @@ -1399,45 +1402,46 @@ vm_setivar(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_index_t i #if OPT_IC_FOR_IVAR switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - { - VM_ASSERT(!rb_ractor_shareable_p(obj) || rb_obj_frozen_p(obj)); + { + VM_ASSERT(!rb_ractor_shareable_p(obj) || rb_obj_frozen_p(obj)); - shape_id_t shape_id = ROBJECT_SHAPE_ID(obj); + shape_id_t shape_id = ROBJECT_SHAPE_ID(obj); - if (LIKELY(shape_id == dest_shape_id)) { + if (LIKELY(shape_id == dest_shape_id)) { + RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); + VM_ASSERT(!rb_ractor_shareable_p(obj)); + } + else if (dest_shape_id != INVALID_SHAPE_ID) { + rb_shape_t *dest_shape = rb_shape_get_shape_by_id(dest_shape_id); + shape_id_t source_shape_id = dest_shape->parent_id; + if (shape_id == source_shape_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) { RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); - VM_ASSERT(!rb_ractor_shareable_p(obj)); - } - else if (dest_shape_id != INVALID_SHAPE_ID) { - rb_shape_t *dest_shape = rb_shape_get_shape_by_id(dest_shape_id); - shape_id_t source_shape_id = dest_shape->parent_id; - if (shape_id == source_shape_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) { - RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - rb_init_iv_list(obj); - } + if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { + rb_init_iv_list(obj); + } - ROBJECT_SET_SHAPE_ID(obj, dest_shape_id); + ROBJECT_SET_SHAPE_ID(obj, dest_shape_id); - RUBY_ASSERT(rb_shape_get_next(rb_shape_get_shape_by_id(source_shape_id), obj, id) == dest_shape); - RUBY_ASSERT(index < ROBJECT_NUMIV(obj)); + RUBY_ASSERT(rb_shape_get_next(rb_shape_get_shape_by_id(source_shape_id), obj, id) == dest_shape); + RUBY_ASSERT(index < ROBJECT_NUMIV(obj)); - } - else { - break; - } - } else { + } + else { break; } + } + else { + break; + } - VALUE *ptr = ROBJECT_IVPTR(obj); + VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); + RB_OBJ_WRITE(obj, &ptr[index], val); - RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); - return val; - } - break; + RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); + return val; + } + break; case T_CLASS: case T_MODULE: RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_noobject); @@ -1547,14 +1551,14 @@ vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IVC i if (UNLIKELY(vm_setivar(obj, id, val, dest_shape_id, index) == Qundef)) { switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - case T_CLASS: - case T_MODULE: - break; - default: - if (vm_setivar_default(obj, id, val, dest_shape_id, index) != Qundef) { - return; - } + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + if (vm_setivar_default(obj, id, val, dest_shape_id, index) != Qundef) { + return; + } } vm_setivar_slowpath_ivar(obj, id, val, iseq, ic); } @@ -3275,17 +3279,17 @@ vm_call_attrset_direct(rb_execution_context_t *ec, rb_control_frame_t *cfp, cons VALUE res = vm_setivar(obj, id, val, dest_shape_id, index); if (res == Qundef) { switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - case T_CLASS: - case T_MODULE: - break; - default: - { - res = vm_setivar_default(obj, id, val, dest_shape_id, index); - if (res != Qundef) { - return res; - } + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + { + res = vm_setivar_default(obj, id, val, dest_shape_id, index); + if (res != Qundef) { + return res; } + } } res = vm_setivar_slowpath_attr(obj, id, val, cc); } @@ -3895,7 +3899,8 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st VM_CALL_METHOD_ATTR(v, vm_call_attrset_direct(ec, cfp, cc, calling->recv), CC_SET_FASTPATH(cc, vm_call_attrset, !(vm_ci_flag(ci) & aset_mask))); - } else { + } + else { cc = &((struct rb_callcache) { .flags = T_IMEMO | (imemo_callcache << FL_USHIFT) | From 80da7250c5d7c862e3c1e1431683a1f1211a4d9c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 22:03:35 +0900 Subject: [PATCH 022/139] file2lastrev.rb: Refactor VCS directory search Search VCS directory after other options are in effective, i.e., `--srcdir=nonexitent --suppress_not_found` options, as well as the reverse order case, should print the current date only and exit successfully. --- tool/file2lastrev.rb | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tool/file2lastrev.rb b/tool/file2lastrev.rb index 09cb9599327029..94c2b2a9d6c480 100755 --- a/tool/file2lastrev.rb +++ b/tool/file2lastrev.rb @@ -26,19 +26,11 @@ def self.output=(output) OptionParser.new {|opts| opts.banner << " paths..." vcs_options = VCS.define_options(opts) - new_vcs = proc do |path| - begin - vcs = VCS.detect(path, vcs_options, opts.new) - rescue VCS::NotFoundError => e - abort "#{File.basename(Program)}: #{e.message}" unless @suppress_not_found - opts.remove - nil - end - end + srcdir = nil opts.new opts.on("--srcdir=PATH", "use PATH as source directory") do |path| - abort "#{File.basename(Program)}: srcdir is already set" if vcs - new_vcs[path] + abort "#{File.basename(Program)}: srcdir is already set" if srcdir + srcdir = path end opts.on("--changed", "changed rev") do self.output = :changed @@ -60,10 +52,11 @@ def self.output=(output) @suppress_not_found = true end opts.order! rescue abort "#{File.basename(Program)}: #{$!}\n#{opts}" - if vcs - vcs.set_options(vcs_options) # options after --srcdir - elsif new_vcs["."] - else @suppress_not_found + begin + vcs = VCS.detect(srcdir || ".", vcs_options, opts.new) + rescue VCS::NotFoundError => e + abort "#{File.basename(Program)}: #{e.message}" unless @suppress_not_found + opts.remove (vcs = VCS::Null.new(nil)).set_options(vcs_options) end } From b55e3b842a8cf4349914b05cebf00ab53024ae69 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Oct 2022 19:38:29 +0900 Subject: [PATCH 023/139] Initialize shape attr index also in non-markable CC --- vm_callinfo.h | 14 +++++++++----- vm_insnhelper.c | 24 +++++------------------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/vm_callinfo.h b/vm_callinfo.h index 3fb0fa8ca79ba4..e59f25ca59398d 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -301,12 +301,12 @@ extern const struct rb_callcache *rb_vm_empty_cc_for_super(void); #define vm_cc_empty() rb_vm_empty_cc() +static inline void vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id_t dest_shape_id); + static inline void vm_cc_attr_index_initialize(const struct rb_callcache *cc, shape_id_t shape_id) { - VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - VM_ASSERT(cc != vm_cc_empty()); - *(uintptr_t *)&cc->aux_.attr.value = (uintptr_t)(shape_id) << SHAPE_FLAG_SHIFT; + vm_cc_attr_index_set(cc, (attr_index_t)-1, shape_id); } static inline const struct rb_callcache * @@ -385,7 +385,6 @@ vm_cc_attr_index_dest_shape_id(const struct rb_callcache *cc) static inline void vm_cc_atomic_shape_and_index(const struct rb_callcache *cc, shape_id_t * shape_id, attr_index_t * index) { - VM_ASSERT(vm_cc_markable(cc)); uintptr_t cache_value = cc->aux_.attr.value; // Atomically read 64 bits *shape_id = (shape_id_t)(cache_value >> SHAPE_FLAG_SHIFT); *index = (attr_index_t)(cache_value & SHAPE_FLAG_MASK) - 1; @@ -451,9 +450,14 @@ vm_cc_call_set(const struct rb_callcache *cc, vm_call_handler call) static inline void vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id_t dest_shape_id) { + uintptr_t *attr_value = (uintptr_t *)&cc->aux_.attr.value; + if (!vm_cc_markable(cc)) { + *attr_value = (uintptr_t)INVALID_SHAPE_ID << SHAPE_FLAG_SHIFT; + return; + } VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(cc != vm_cc_empty()); - *(uintptr_t *)&cc->aux_.attr.value = (index + 1) | ((uintptr_t)(dest_shape_id) << SHAPE_FLAG_SHIFT); + *attr_value = (index + 1) | ((uintptr_t)(dest_shape_id) << SHAPE_FLAG_SHIFT); } static inline void diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 0a62b0d86c8b4c..3f1337c36c3614 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1096,9 +1096,7 @@ static inline void fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, attr_index_t index, shape_id_t shape_id) { if (is_attr) { - if (vm_cc_markable(cc)) { - vm_cc_attr_index_set(cc, index, shape_id); - } + vm_cc_attr_index_set(cc, index, shape_id); } else { vm_ic_attr_index_set(iseq, ic, index, shape_id); @@ -1161,13 +1159,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call attr_index_t index; if (is_attr) { - if (vm_cc_markable(cc)) { - vm_cc_atomic_shape_and_index(cc, &cached_id, &index); - } - else { - cached_id = INVALID_SHAPE_ID; - index = ATTR_INDEX_NOT_SET; - } + vm_cc_atomic_shape_and_index(cc, &cached_id, &index); } else { vm_ic_atomic_shape_and_index(ic, &cached_id, &index); @@ -1214,9 +1206,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } else { if (is_attr) { - if (vm_cc_markable(cc)) { - vm_cc_attr_index_initialize(cc, shape_id); - } + vm_cc_attr_index_initialize(cc, shape_id); } else { vm_ic_attr_index_initialize(ic, shape_id); @@ -1248,9 +1238,7 @@ populate_cache(attr_index_t index, shape_id_t next_shape_id, ID id, const rb_ise { // Cache population code if (is_attr) { - if (vm_cc_markable(cc)) { - vm_cc_attr_index_set(cc, index, next_shape_id); - } + vm_cc_attr_index_set(cc, index, next_shape_id); } else { vm_ic_attr_index_set(iseq, ic, index, next_shape_id); @@ -3927,9 +3915,7 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st CALLER_SETUP_ARG(cfp, calling, ci); CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); rb_check_arity(calling->argc, 0, 0); - if (vm_cc_markable(cc)) { - vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); - } + vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); const unsigned int ivar_mask = (VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT); VM_CALL_METHOD_ATTR(v, vm_call_ivar(ec, cfp, calling), From 107531583c8e8b2d706a6a27f46d429e387efff7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 12 Oct 2022 08:54:02 -0700 Subject: [PATCH 024/139] Unwrap shape id as unsigned int Shape IDs are unsigned. This commit unwraps the shape id as an unsigned int, which will automatically raise an argument error and also eliminate a compilation warning. --- shape.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shape.c b/shape.c index 80d135b897e73b..f21b9a4eced20b 100644 --- a/shape.c +++ b/shape.c @@ -514,8 +514,8 @@ rb_shape_flags_mask(void) static VALUE rb_shape_find_by_id(VALUE mod, VALUE id) { - shape_id_t shape_id = NUM2INT(id); - if (shape_id < 0 || shape_id >= GET_VM()->next_shape_id) { + shape_id_t shape_id = NUM2UINT(id); + if (shape_id >= GET_VM()->next_shape_id) { rb_raise(rb_eArgError, "Shape ID %d is out of bounds\n", shape_id); } return rb_shape_t_to_rb_cShape(rb_shape_get_shape_by_id(shape_id)); From 166140aa289e6edfd9b023b87c9e6d4434bb29bc Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 12 Oct 2022 12:50:50 -0700 Subject: [PATCH 025/139] YJIT: Set RUST_BACKTRACE=1 in every Cirrus script (#6526) * YJIT: Set RUST_BACKTRACE=1 in every Cirrus script * YJIT: Print $CIRRUS_ENV correctly It looks like $CIRRUS_ENV is cleaned up at every script. --- .cirrus.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 854a3df98201fb..cd1654857148a2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -39,9 +39,8 @@ task: # the `make` environment variable used in compilers.yml causes some rubygems # tests to fail. # https://github.com/rubygems/rubygems/issues/4921 - - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> $CIRRUS_ENV - print_env_script: - - echo "GNUMAKEFLAGS=$GNUMAKEFLAGS" + - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> "$CIRRUS_ENV" + - cat "$CIRRUS_ENV" # Arm containers are executed in AWS's EKS, and it's not yet supporting IPv6 # See https://github.com/aws/containers-roadmap/issues/835 disable_ipv6_script: sudo ./tool/disable_ipv6.sh @@ -99,9 +98,9 @@ yjit_task: # the `make` environment variable used in compilers.yml causes some rubygems # tests to fail. # https://github.com/rubygems/rubygems/issues/4921 - - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> $CIRRUS_ENV - print_env_script: - - echo "GNUMAKEFLAGS=$GNUMAKEFLAGS" + - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> "$CIRRUS_ENV" + - echo RUST_BACKTRACE=1 >> "$CIRRUS_ENV" + - cat "$CIRRUS_ENV" # Arm containers are executed in AWS's EKS, and it's not yet supporting IPv6 # See https://github.com/aws/containers-roadmap/issues/835 disable_ipv6_script: sudo ./tool/disable_ipv6.sh @@ -124,9 +123,9 @@ yjit_task: else echo "only running bindgen on clang image" fi - boot_miniruby_script: RUST_BACKTRACE=1 ./miniruby --yjit-call-threshold=1 -e0 - test_dump_insns_script: RUST_BACKTRACE=1 ./miniruby --yjit-call-threshold=1 --yjit-dump-insns -e0 - output_stats_script: RUST_BACKTRACE=1 ./miniruby --yjit-call-threshold=1 --yjit-stats -e0 + boot_miniruby_script: ./miniruby --yjit-call-threshold=1 -e0 + test_dump_insns_script: ./miniruby --yjit-call-threshold=1 --yjit-dump-insns -e0 + output_stats_script: ./miniruby --yjit-call-threshold=1 --yjit-stats -e0 full_build_script: source $HOME/.cargo/env && make cargo_test_script: source $HOME/.cargo/env && cd yjit && cargo test make_test_script: source $HOME/.cargo/env && make test RUN_OPTS="--yjit-call-threshold=1 --yjit-verify-ctx" From dd5118f8524c425894d4716b787837ad7380bb0d Mon Sep 17 00:00:00 2001 From: "NARUSE, Yui" Date: Sun, 13 Jan 2019 08:58:00 +0900 Subject: [PATCH 026/139] URI.parse should set empty string in host instead of nil --- lib/uri/rfc3986_parser.rb | 3 +-- test/uri/test_generic.rb | 9 ++++++++- test/uri/test_ldap.rb | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index 3e07de4805c374..68661d1185ccf4 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -2,8 +2,7 @@ module URI class RFC3986_Parser # :nodoc: # URI defined in RFC3986 - # this regexp is modified not to host is not empty string - RFC3986_URI = /\A(?(?[A-Za-z][+\-.0-9A-Za-z]*):(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?\g(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ + RFC3986_URI = /\A(?(?[A-Za-z][+\-.0-9A-Za-z]*):(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?\g(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ RFC3986_relative_ref = /\A(?(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?(?(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ attr_reader :regexp diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb index fdb405e396991e..3897c3d6eed38a 100644 --- a/test/uri/test_generic.rb +++ b/test/uri/test_generic.rb @@ -24,7 +24,8 @@ def test_to_s assert_equal "file:///foo", URI("file:///foo").to_s assert_equal "postgres:///foo", URI("postgres:///foo").to_s - assert_equal "http:/foo", URI("http:///foo").to_s + assert_equal "http:///foo", URI("http:///foo").to_s + assert_equal "http:/foo", URI("http:/foo").to_s end def test_parse @@ -157,6 +158,12 @@ def test_parse assert_equal(nil, url.user) assert_equal(nil, url.password) assert_equal(nil, url.userinfo) + + # sec-156615 + url = URI.parse('http:////example.com') + # must be empty string to identify as path-abempty, not path-absolute + assert_equal('', url.host) + assert_equal('http:////example.com', url.to_s) end def test_parse_scheme_with_symbols diff --git a/test/uri/test_ldap.rb b/test/uri/test_ldap.rb index 64845e487ac3e3..2625b241030965 100644 --- a/test/uri/test_ldap.rb +++ b/test/uri/test_ldap.rb @@ -39,7 +39,7 @@ def test_parse # from RFC2255, section 6. { 'ldap:///o=University%20of%20Michigan,c=US' => - ['ldap', nil, URI::LDAP::DEFAULT_PORT, + ['ldap', '', URI::LDAP::DEFAULT_PORT, 'o=University%20of%20Michigan,c=US', nil, nil, nil, nil], @@ -74,12 +74,12 @@ def test_parse nil, '(int=%5c00%5c00%5c00%5c04)', nil, nil], 'ldap:///??sub??bindname=cn=Manager%2co=Foo' => - ['ldap', nil, URI::LDAP::DEFAULT_PORT, + ['ldap', '', URI::LDAP::DEFAULT_PORT, '', nil, 'sub', nil, 'bindname=cn=Manager%2co=Foo'], 'ldap:///??sub??!bindname=cn=Manager%2co=Foo' => - ['ldap', nil, URI::LDAP::DEFAULT_PORT, + ['ldap', '', URI::LDAP::DEFAULT_PORT, '', nil, 'sub', nil, '!bindname=cn=Manager%2co=Foo'], }.each do |url2, ary| From 751ffb276f658518c6fe06461a9d3d1c136c7d5d Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 13 Oct 2022 12:10:10 +0900 Subject: [PATCH 027/139] FreeBSD make uses the target under srcdir [ci skip] --- tool/ruby_vm/helpers/dumper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 tool/ruby_vm/helpers/dumper.rb diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb old mode 100644 new mode 100755 index c083dffa7abf53..150a81f04bb1b0 --- a/tool/ruby_vm/helpers/dumper.rb +++ b/tool/ruby_vm/helpers/dumper.rb @@ -28,7 +28,7 @@ def new_erb spec path = Pathname.new(__FILE__) path = (path.relative_path_from(Pathname.pwd) rescue path).dirname path += '../views' - path += Pathname.pwd.join(spec).expand_path.to_s.sub("#{@base}/", '') + path += Pathname.pwd.join(File.basename(spec)).expand_path.to_s.sub("#{@base}/", '') src = path.expand_path.read mode: 'rt:utf-8:utf-8' rescue Errno::ENOENT raise "don't know how to generate #{path}" From c50623f0934ec0b94550fa59a2b0ffcb1b0f858f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 13 Oct 2022 12:24:59 +0900 Subject: [PATCH 028/139] Revert "FreeBSD make uses the target under srcdir [ci skip]" This reverts commit 751ffb276f658518c6fe06461a9d3d1c136c7d5d, which caused build failures on other platforms. --- tool/ruby_vm/helpers/dumper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 tool/ruby_vm/helpers/dumper.rb diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb old mode 100755 new mode 100644 index 150a81f04bb1b0..c083dffa7abf53 --- a/tool/ruby_vm/helpers/dumper.rb +++ b/tool/ruby_vm/helpers/dumper.rb @@ -28,7 +28,7 @@ def new_erb spec path = Pathname.new(__FILE__) path = (path.relative_path_from(Pathname.pwd) rescue path).dirname path += '../views' - path += Pathname.pwd.join(File.basename(spec)).expand_path.to_s.sub("#{@base}/", '') + path += Pathname.pwd.join(spec).expand_path.to_s.sub("#{@base}/", '') src = path.expand_path.read mode: 'rt:utf-8:utf-8' rescue Errno::ENOENT raise "don't know how to generate #{path}" From 1cda4146224e857f6258241bbb30d9358ce2d6c7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 13 Oct 2022 11:41:43 +0900 Subject: [PATCH 029/139] Raise ArgumentError with empty host url again. Fixup dd5118f8524c425894d4716b787837ad7380bb0d Co-authored-by: Koichi Sasada --- lib/net/http/generic_request.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb index 313de6ac92337c..d56835c76ffb80 100644 --- a/lib/net/http/generic_request.rb +++ b/lib/net/http/generic_request.rb @@ -15,7 +15,8 @@ def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) if URI === uri_or_path then raise ArgumentError, "not an HTTP URI" unless URI::HTTP === uri_or_path - raise ArgumentError, "no host component for URI" unless uri_or_path.hostname + hostname = uri_or_path.hostname + raise ArgumentError, "no host component for URI" unless (hostname && hostname.length > 0) @uri = uri_or_path.dup host = @uri.hostname.dup host << ":".freeze << @uri.port.to_s if @uri.port != @uri.default_port From b734832883e3644af2527bd463de220a0b5794bc Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 13 Oct 2022 13:42:22 +0900 Subject: [PATCH 030/139] Skip utime example with Intel C Compiler suite --- spec/ruby/core/process/times_spec.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spec/ruby/core/process/times_spec.rb b/spec/ruby/core/process/times_spec.rb index b47189a7e7f4d1..d114061eb67d2c 100644 --- a/spec/ruby/core/process/times_spec.rb +++ b/spec/ruby/core/process/times_spec.rb @@ -5,12 +5,16 @@ Process.times.should be_kind_of(Process::Tms) end - it "returns current cpu times" do - t = Process.times - user = t.utime + # TODO: Intel C Compiler does not work this example + # http://rubyci.s3.amazonaws.com/icc-x64/ruby-master/log/20221013T030005Z.fail.html.gz + unless RbConfig::CONFIG['CC'].include?("icx") + it "returns current cpu times" do + t = Process.times + user = t.utime - 1 until Process.times.utime > user - Process.times.utime.should > user + 1 until Process.times.utime > user + Process.times.utime.should > user + end end platform_is_not :windows do From 765ee822b54026e6cafa107d93475c2883e2bad8 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 13 Oct 2022 19:04:06 +1300 Subject: [PATCH 031/139] Add missing `f.resume` to fiber test. (#6539) --- test/fiber/test_scheduler.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb index 1d5881e2334148..5a24bff04fe4be 100644 --- a/test/fiber/test_scheduler.rb +++ b/test/fiber/test_scheduler.rb @@ -36,6 +36,7 @@ def test_fiber_blocking assert fiber.blocking? end end + f.resume end def test_closed_at_thread_exit From 4b1504ae0a5118153f8b47d7bd0fff7e61553d17 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 13 Oct 2022 16:24:47 +0900 Subject: [PATCH 032/139] [ruby/uri] Fix splitting relative URI https://github.com/ruby/uri/commit/ffbab83de6 --- lib/uri/rfc3986_parser.rb | 2 +- test/uri/test_parser.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index 68661d1185ccf4..f3816d9ae5ea2f 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -3,7 +3,7 @@ module URI class RFC3986_Parser # :nodoc: # URI defined in RFC3986 RFC3986_URI = /\A(?(?[A-Za-z][+\-.0-9A-Za-z]*):(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?\g(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ - RFC3986_relative_ref = /\A(?(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?(?(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ + RFC3986_relative_ref = /\A(?(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?(?(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ attr_reader :regexp def initialize diff --git a/test/uri/test_parser.rb b/test/uri/test_parser.rb index f8e9299d098b5a..72fb5901d963f6 100644 --- a/test/uri/test_parser.rb +++ b/test/uri/test_parser.rb @@ -72,4 +72,11 @@ def test_unescape assert_equal("\u3042", p1.unescape('%e3%81%82'.force_encoding(Encoding::US_ASCII))) assert_equal("\xe3\x83\x90\xe3\x83\x90", p1.unescape("\xe3\x83\x90%e3%83%90")) end + + def test_split + assert_equal(["http", nil, "example.com", nil, nil, "", nil, nil, nil], URI.split("http://example.com")) + assert_equal(["http", nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("http://[0::0]")) + assert_equal([nil, nil, "example.com", nil, nil, "", nil, nil, nil], URI.split("//example.com")) + assert_equal([nil, nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("//[0::0]")) + end end From 2cc3963a00868ef6ff84a8b3bccca778592b3c2d Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Thu, 13 Oct 2022 19:23:46 +0900 Subject: [PATCH 033/139] Prevent wrong integer expansion `(attr_index + 1)` leads to wrong integer expansion on 32-bit machines (including Solaris 10 CI) because `attr_index_t` is uint16_t. http://rubyci.s3.amazonaws.com/solaris10-gcc/ruby-master/log/20221013T080004Z.fail.html.gz ``` 1) Failure: TestRDocClassModule#test_marshal_load_version_2 [/export/home/users/chkbuild/cb-gcc/tmp/build/20221013T080004Z/ruby/test/rdoc/test_rdoc_class_module.rb:493]: <[doc: [doc (file.rb): [para: "this is a comment"]]]> expected but was <[doc: [doc (file.rb): [para: "this is a comment"]]]>. 2) Failure: TestRDocStats#test_report_method_line [/export/home/users/chkbuild/cb-gcc/tmp/build/20221013T080004Z/ruby/test/rdoc/test_rdoc_stats.rb:460]: Expected /\#\ in\ file\ file\.rb:4/ to match "The following items are not documented:\n" + "\n" + " class C # is documented\n" + "\n" + " # in file file.rb\n" + " def m1; end\n" + "\n" + " end\n" + "\n" + "\n". 3) Failure: TestRDocStats#test_report_attr_line [/export/home/users/chkbuild/cb-gcc/tmp/build/20221013T080004Z/ruby/test/rdoc/test_rdoc_stats.rb:91]: Expected /\#\ in\ file\ file\.rb:3/ to match "The following items are not documented:\n" + "\n" + " class C # is documented\n" + "\n" + " attr_accessor :a # in file file.rb\n" + "\n" + " end\n" + "\n" + "\n". ``` --- vm_callinfo.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm_callinfo.h b/vm_callinfo.h index e59f25ca59398d..20027188813f09 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -457,13 +457,13 @@ vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id } VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(cc != vm_cc_empty()); - *attr_value = (index + 1) | ((uintptr_t)(dest_shape_id) << SHAPE_FLAG_SHIFT); + *attr_value = (attr_index_t)(index + 1) | ((uintptr_t)(dest_shape_id) << SHAPE_FLAG_SHIFT); } static inline void vm_ic_attr_index_set(const rb_iseq_t *iseq, const struct iseq_inline_iv_cache_entry *ic, attr_index_t index, shape_id_t dest_shape_id) { - *(uintptr_t *)&ic->value = ((uintptr_t)dest_shape_id << SHAPE_FLAG_SHIFT) | (index + 1); + *(uintptr_t *)&ic->value = ((uintptr_t)dest_shape_id << SHAPE_FLAG_SHIFT) | (attr_index_t)(index + 1); } static inline void From 1b0c9d0e3d6dc3630072dc16c143e0d79886b7a0 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 12 Oct 2022 16:19:55 -0400 Subject: [PATCH 034/139] YJIT: No need to fill to get UDF on ARM64 On ARM64, all zeros is already undefined, so we don't need to do extra work to fill new memory with undefined instructions. --- yjit/src/virtualmem.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/yjit/src/virtualmem.rs b/yjit/src/virtualmem.rs index 8d34e521b9c5d1..4d7c061ec7f406 100644 --- a/yjit/src/virtualmem.rs +++ b/yjit/src/virtualmem.rs @@ -141,10 +141,16 @@ impl VirtualMemory { if !alloc.mark_writable(mapped_region_end.cast(), alloc_size_u32) { return Err(FailedPageMapping); } - // Fill new memory with PUSH DS (0x1E) so that executing uninitialized memory - // will fault with #UD in 64-bit mode. On Linux it becomes SIGILL and use the - // usual Ruby crash reporter. - std::slice::from_raw_parts_mut(mapped_region_end, alloc_size).fill(0x1E); + if cfg!(target_arch = "x86_64") { + // Fill new memory with PUSH DS (0x1E) so that executing uninitialized memory + // will fault with #UD in 64-bit mode. On Linux it becomes SIGILL and use the + // usual Ruby crash reporter. + std::slice::from_raw_parts_mut(mapped_region_end, alloc_size).fill(0x1E); + } else if cfg!(target_arch = "aarch64") { + // In aarch64, all zeros encodes UDF, so it's already what we want. + } else { + unreachable!("unknown arch"); + } } self.mapped_region_bytes = self.mapped_region_bytes + alloc_size; @@ -309,6 +315,7 @@ pub mod tests { } #[test] + #[cfg(target_arch = "x86_64")] fn new_memory_is_initialized() { let mut virt = new_dummy_virt_mem(); From e5058b58c22e19d559b1122d94af5af3931aa416 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 12 Oct 2022 14:37:02 -0700 Subject: [PATCH 035/139] Only expose Ruby Shape API if VM_CHECK_MODE is enabled --- lib/mjit/compiler.rb | 2 +- mjit_c.rb | 25 +++++++++++++++++++++++-- shape.c | 22 +++++++++++++--------- test/ruby/test_shapes.rb | 2 +- tool/mjit/bindgen.rb | 2 ++ 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 0d2910bf4ec2d5..55fcee6b877fcf 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -356,7 +356,7 @@ def compile_ivar(insn_name, stack_size, pos, status, operands, body) source_shape_id = if dest_shape_id == C.INVALID_SHAPE_ID dest_shape_id else - RubyVM::Shape.find_by_id(dest_shape_id).parent_id + C.rb_shape_get_shape_by_id(dest_shape_id).parent_id end src = +'' diff --git a/mjit_c.rb b/mjit_c.rb index 1858f86e4d4afb..4a68ec12ae5c4b 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -6,11 +6,11 @@ module RubyVM::MJIT class << C def SHAPE_BITS - RubyVM::Shape::SHAPE_BITS + Primitive.cexpr! 'UINT2NUM(SHAPE_BITS)' end def SHAPE_FLAG_SHIFT - RubyVM::Shape::SHAPE_FLAG_SHIFT + Primitive.cexpr! 'UINT2NUM(SHAPE_FLAG_SHIFT)' end def ROBJECT_EMBED_LEN_MAX @@ -29,6 +29,12 @@ def has_cache_for_send(cc, insn) Primitive.has_cache_for_send(cc.to_i, insn) end + def rb_shape_get_shape_by_id(shape_id) + _shape_id = shape_id.to_i + shape_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_shape_get_shape_by_id((shape_id_t)NUM2UINT(_shape_id)))' + rb_shape_t.new(shape_addr) + end + def rb_iseq_check(iseq) _iseq_addr = iseq.to_i iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseq_check((rb_iseq_t *)NUM2PTR(_iseq_addr)))' @@ -595,6 +601,21 @@ def C.rb_serial_t @rb_serial_t ||= CType::Immediate.parse("unsigned long long") end + def C.rb_shape + @rb_shape ||= CType::Struct.new( + "rb_shape", Primitive.cexpr!("SIZEOF(struct rb_shape)"), + edges: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), edges)")], + edge_name: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), edge_name)")], + iv_count: [self.attr_index_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), iv_count)")], + type: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), type)")], + parent_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), parent_id)")], + ) + end + + def C.rb_shape_t + @rb_shape_t ||= self.rb_shape + end + def C.VALUE @VALUE ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(VALUE)"), Primitive.cexpr!("SIGNED_TYPE_P(VALUE)")) end diff --git a/shape.c b/shape.c index f21b9a4eced20b..1b0f1a5dc9511c 100644 --- a/shape.c +++ b/shape.c @@ -306,6 +306,13 @@ rb_shape_set_shape(VALUE obj, rb_shape_t* shape) rb_shape_set_shape_id(obj, rb_shape_id(shape)); } +VALUE +rb_shape_flags_mask(void) +{ + return SHAPE_FLAG_MASK; +} + +#if VM_CHECK_MODE > 0 VALUE rb_cShape; /* @@ -440,19 +447,19 @@ rb_shape_parent(VALUE self) } } -VALUE +static VALUE rb_shape_debug_shape(VALUE self, VALUE obj) { return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj)); } -VALUE +static VALUE rb_shape_root_shape(VALUE self) { return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape()); } -VALUE +static VALUE rb_shape_frozen_root_shape(VALUE self) { return rb_shape_t_to_rb_cShape(rb_shape_get_frozen_root_shape()); @@ -505,12 +512,6 @@ next_shape_id(VALUE self) return INT2NUM(GET_VM()->next_shape_id); } -VALUE -rb_shape_flags_mask(void) -{ - return SHAPE_FLAG_MASK; -} - static VALUE rb_shape_find_by_id(VALUE mod, VALUE id) { @@ -520,10 +521,12 @@ rb_shape_find_by_id(VALUE mod, VALUE id) } return rb_shape_t_to_rb_cShape(rb_shape_get_shape_by_id(shape_id)); } +#endif void Init_shape(void) { +#if VM_CHECK_MODE > 0 rb_cShape = rb_define_class_under(rb_cRubyVM, "Shape", rb_cObject); rb_undef_alloc_func(rb_cShape); @@ -548,4 +551,5 @@ Init_shape(void) rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1); rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0); rb_define_singleton_method(rb_cShape, "frozen_root_shape", rb_shape_frozen_root_shape, 0); +#endif } diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 7142c30cd557d4..0da296189dd6fc 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -179,4 +179,4 @@ def test_out_of_bounds_shape RubyVM::Shape.find_by_id(-1) end end -end +end if defined?(RubyVM::Shape) diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 8c21d424495c0c..77b81814e3e68d 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -380,6 +380,8 @@ def push_target(target) rb_mjit_compile_info rb_mjit_unit rb_serial_t + rb_shape + rb_shape_t ], dynamic_types: %w[ VALUE From 93a87f4963703a709bf974c48f76a5503f31f53f Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Thu, 13 Oct 2022 18:17:13 -0400 Subject: [PATCH 036/139] Make op_ext an optional for code clarity (#6542) --- yjit/src/asm/x86_64/mod.rs | 92 +++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs index 42d97b7e80b338..9cc3bf3770ad93 100644 --- a/yjit/src/asm/x86_64/mod.rs +++ b/yjit/src/asm/x86_64/mod.rs @@ -161,6 +161,14 @@ impl X86Opnd { _ => unreachable!() } } + + pub fn is_some(&self) -> bool { + match self { + X86Opnd::None => false, + _ => true + } + } + } // Instruction pointer @@ -382,7 +390,7 @@ fn write_opcode(cb: &mut CodeBlock, opcode: u8, reg: X86Reg) { } /// Encode an RM instruction -fn write_rm(cb: &mut CodeBlock, sz_pref: bool, rex_w: bool, r_opnd: X86Opnd, rm_opnd: X86Opnd, op_ext: u8, bytes: &[u8]) { +fn write_rm(cb: &mut CodeBlock, sz_pref: bool, rex_w: bool, r_opnd: X86Opnd, rm_opnd: X86Opnd, op_ext: Option, bytes: &[u8]) { let op_len = bytes.len(); assert!(op_len > 0 && op_len <= 3); assert!(matches!(r_opnd, X86Opnd::Reg(_) | X86Opnd::None), "Can only encode an RM instruction with a register or a none"); @@ -439,7 +447,7 @@ fn write_rm(cb: &mut CodeBlock, sz_pref: bool, rex_w: bool, r_opnd: X86Opnd, rm_ // MODRM.rm (3 bits) assert!( - !(op_ext != 0xff && !matches!(r_opnd, X86Opnd::None)), + !(op_ext.is_some() && r_opnd.is_some()), "opcode extension and register operand present" ); @@ -460,8 +468,8 @@ fn write_rm(cb: &mut CodeBlock, sz_pref: bool, rex_w: bool, r_opnd: X86Opnd, rm_ // Encode the reg field let reg: u8; - if op_ext != 0xff { - reg = op_ext; + if let Some(val) = op_ext { + reg = val; } else { reg = match r_opnd { X86Opnd::Reg(reg) => reg.reg_no & 7, @@ -522,7 +530,7 @@ fn write_rm(cb: &mut CodeBlock, sz_pref: bool, rex_w: bool, r_opnd: X86Opnd, rm_ } // Encode a mul-like single-operand RM instruction -fn write_rm_unary(cb: &mut CodeBlock, op_mem_reg_8: u8, op_mem_reg_pref: u8, op_ext: u8, opnd: X86Opnd) { +fn write_rm_unary(cb: &mut CodeBlock, op_mem_reg_8: u8, op_mem_reg_pref: u8, op_ext: Option, opnd: X86Opnd) { assert!(matches!(opnd, X86Opnd::Reg(_) | X86Opnd::Mem(_))); let opnd_size = opnd.num_bits(); @@ -538,7 +546,7 @@ fn write_rm_unary(cb: &mut CodeBlock, op_mem_reg_8: u8, op_mem_reg_pref: u8, op_ } // Encode an add-like RM instruction with multiple possible encodings -fn write_rm_multi(cb: &mut CodeBlock, op_mem_reg8: u8, op_mem_reg_pref: u8, op_reg_mem8: u8, op_reg_mem_pref: u8, op_mem_imm8: u8, op_mem_imm_sml: u8, op_mem_imm_lrg: u8, op_ext_imm: u8, opnd0: X86Opnd, opnd1: X86Opnd) { +fn write_rm_multi(cb: &mut CodeBlock, op_mem_reg8: u8, op_mem_reg_pref: u8, op_reg_mem8: u8, op_reg_mem_pref: u8, op_mem_imm8: u8, op_mem_imm_sml: u8, op_mem_imm_lrg: u8, op_ext_imm: Option, opnd0: X86Opnd, opnd1: X86Opnd) { assert!(matches!(opnd0, X86Opnd::Reg(_) | X86Opnd::Mem(_))); // Check the size of opnd0 @@ -561,17 +569,17 @@ fn write_rm_multi(cb: &mut CodeBlock, op_mem_reg8: u8, op_mem_reg_pref: u8, op_r // R/M + Reg (X86Opnd::Mem(_), X86Opnd::Reg(_)) | (X86Opnd::Reg(_), X86Opnd::Reg(_)) => { if opnd_size == 8 { - write_rm(cb, false, false, opnd1, opnd0, 0xff, &[op_mem_reg8]); + write_rm(cb, false, false, opnd1, opnd0, None, &[op_mem_reg8]); } else { - write_rm(cb, sz_pref, rex_w, opnd1, opnd0, 0xff, &[op_mem_reg_pref]); + write_rm(cb, sz_pref, rex_w, opnd1, opnd0, None, &[op_mem_reg_pref]); } }, // Reg + R/M/IPRel (X86Opnd::Reg(_), X86Opnd::Mem(_) | X86Opnd::IPRel(_)) => { if opnd_size == 8 { - write_rm(cb, false, false, opnd0, opnd1, 0xff, &[op_reg_mem8]); + write_rm(cb, false, false, opnd0, opnd1, None, &[op_reg_mem8]); } else { - write_rm(cb, sz_pref, rex_w, opnd0, opnd1, 0xff, &[op_reg_mem_pref]); + write_rm(cb, sz_pref, rex_w, opnd0, opnd1, None, &[op_reg_mem_pref]); } }, // R/M + Imm @@ -640,7 +648,7 @@ pub fn add(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0x80, // opMemImm8 0x83, // opMemImmSml 0x81, // opMemImmLrg - 0x00, // opExtImm + Some(0x00), // opExtImm opnd0, opnd1 ); @@ -657,7 +665,7 @@ pub fn and(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0x80, // opMemImm8 0x83, // opMemImmSml 0x81, // opMemImmLrg - 0x04, // opExtImm + Some(0x04), // opExtImm opnd0, opnd1 ); @@ -705,7 +713,7 @@ pub fn call_label(cb: &mut CodeBlock, label_idx: usize) { /// call - Indirect call with an R/M operand pub fn call(cb: &mut CodeBlock, opnd: X86Opnd) { - write_rm(cb, false, false, X86Opnd::None, opnd, 2, &[0xff]); + write_rm(cb, false, false, X86Opnd::None, opnd, Some(2), &[0xff]); } /// Encode a conditional move instruction @@ -721,7 +729,7 @@ fn write_cmov(cb: &mut CodeBlock, opcode1: u8, dst: X86Opnd, src: X86Opnd) { let sz_pref = reg.num_bits == 16; let rex_w = reg.num_bits == 64; - write_rm(cb, sz_pref, rex_w, dst, src, 0xff, &[0x0f, opcode1]); + write_rm(cb, sz_pref, rex_w, dst, src, None, &[0x0f, opcode1]); } else { unreachable!() } @@ -770,7 +778,7 @@ pub fn cmp(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0x80, // opMemImm8 0x83, // opMemImmSml 0x81, // opMemImmLrg - 0x07, // opExtImm + Some(0x07), // opExtImm opnd0, opnd1 ); @@ -900,7 +908,7 @@ pub fn jmp_ptr (cb: &mut CodeBlock, ptr: CodePtr) { write_jcc_ptr(cb, 0xFF, 0xE9 /// jmp - Indirect jump near to an R/M operand. pub fn jmp_rm(cb: &mut CodeBlock, opnd: X86Opnd) { - write_rm(cb, false, false, X86Opnd::None, opnd, 4, &[0xff]); + write_rm(cb, false, false, X86Opnd::None, opnd, Some(4), &[0xff]); } // jmp - Jump with relative 32-bit offset @@ -913,7 +921,7 @@ pub fn jmp32(cb: &mut CodeBlock, offset: i32) { pub fn lea(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) { if let X86Opnd::Reg(reg) = dst { assert!(reg.num_bits == 64); - write_rm(cb, false, true, dst, src, 0xff, &[0x8d]); + write_rm(cb, false, true, dst, src, None, &[0x8d]); } else { unreachable!(); } @@ -981,9 +989,9 @@ pub fn mov(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) { assert!(imm.num_bits <= mem.num_bits); if mem.num_bits == 8 { - write_rm(cb, false, false, X86Opnd::None, dst, 0xff, &[0xc6]); + write_rm(cb, false, false, X86Opnd::None, dst, None, &[0xc6]); } else { - write_rm(cb, mem.num_bits == 16, mem.num_bits == 64, X86Opnd::None, dst, 0, &[0xc7]); + write_rm(cb, mem.num_bits == 16, mem.num_bits == 64, X86Opnd::None, dst, Some(0), &[0xc7]); } let output_num_bits:u32 = if mem.num_bits > 32 { 32 } else { mem.num_bits.into() }; @@ -995,10 +1003,10 @@ pub fn mov(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) { assert!(uimm.num_bits <= mem.num_bits); if mem.num_bits == 8 { - write_rm(cb, false, false, X86Opnd::None, dst, 0xff, &[0xc6]); + write_rm(cb, false, false, X86Opnd::None, dst, None, &[0xc6]); } else { - write_rm(cb, mem.num_bits == 16, mem.num_bits == 64, X86Opnd::None, dst, 0, &[0xc7]); + write_rm(cb, mem.num_bits == 16, mem.num_bits == 64, X86Opnd::None, dst, Some(0), &[0xc7]); } let output_num_bits = if mem.num_bits > 32 { 32 } else { mem.num_bits.into() }; @@ -1018,7 +1026,7 @@ pub fn mov(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) { 0xC6, // opMemImm8 0xFF, // opMemImmSml (not available) 0xFF, // opMemImmLrg - 0xFF, // opExtImm + None, // opExtImm dst, src ); @@ -1036,9 +1044,9 @@ pub fn movsx(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) { assert!(src_num_bits < dst_num_bits); match src_num_bits { - 8 => write_rm(cb, dst_num_bits == 16, dst_num_bits == 64, dst, src, 0xff, &[0x0f, 0xbe]), - 16 => write_rm(cb, dst_num_bits == 16, dst_num_bits == 64, dst, src, 0xff, &[0x0f, 0xbf]), - 32 => write_rm(cb, false, true, dst, src, 0xff, &[0x63]), + 8 => write_rm(cb, dst_num_bits == 16, dst_num_bits == 64, dst, src, None, &[0x0f, 0xbe]), + 16 => write_rm(cb, dst_num_bits == 16, dst_num_bits == 64, dst, src, None, &[0x0f, 0xbf]), + 32 => write_rm(cb, false, true, dst, src, None, &[0x63]), _ => unreachable!() }; } else { @@ -1116,7 +1124,7 @@ pub fn not(cb: &mut CodeBlock, opnd: X86Opnd) { cb, 0xf6, // opMemReg8 0xf7, // opMemRegPref - 0x02, // opExt + Some(0x02), // opExt opnd ); } @@ -1132,7 +1140,7 @@ pub fn or(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0x80, // opMemImm8 0x83, // opMemImmSml 0x81, // opMemImmLrg - 0x01, // opExtImm + Some(0x01), // opExtImm opnd0, opnd1 ); @@ -1152,7 +1160,7 @@ pub fn pop(cb: &mut CodeBlock, opnd: X86Opnd) { X86Opnd::Mem(mem) => { assert!(mem.num_bits == 64); - write_rm(cb, false, false, X86Opnd::None, opnd, 0, &[0x8f]); + write_rm(cb, false, false, X86Opnd::None, opnd, Some(0), &[0x8f]); }, _ => unreachable!() }; @@ -1174,7 +1182,7 @@ pub fn push(cb: &mut CodeBlock, opnd: X86Opnd) { write_opcode(cb, 0x50, reg); }, X86Opnd::Mem(_mem) => { - write_rm(cb, false, false, X86Opnd::None, opnd, 6, &[0xff]); + write_rm(cb, false, false, X86Opnd::None, opnd, Some(6), &[0xff]); }, _ => unreachable!() } @@ -1191,7 +1199,7 @@ pub fn ret(cb: &mut CodeBlock) { } // Encode a single-operand shift instruction -fn write_shift(cb: &mut CodeBlock, op_mem_one_pref: u8, _op_mem_cl_pref: u8, op_mem_imm_pref: u8, op_ext: u8, opnd0: X86Opnd, opnd1: X86Opnd) { +fn write_shift(cb: &mut CodeBlock, op_mem_one_pref: u8, _op_mem_cl_pref: u8, op_mem_imm_pref: u8, op_ext: Option, opnd0: X86Opnd, opnd1: X86Opnd) { assert!(matches!(opnd0, X86Opnd::Reg(_) | X86Opnd::Mem(_))); // Check the size of opnd0 @@ -1221,7 +1229,7 @@ pub fn sal(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0xD1, // opMemOnePref, 0xD3, // opMemClPref, 0xC1, // opMemImmPref, - 0x04, + Some(0x04), opnd0, opnd1 ); @@ -1234,7 +1242,7 @@ pub fn sar(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0xD1, // opMemOnePref, 0xD3, // opMemClPref, 0xC1, // opMemImmPref, - 0x07, + Some(0x07), opnd0, opnd1 ); @@ -1247,7 +1255,7 @@ pub fn shl(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0xD1, // opMemOnePref, 0xD3, // opMemClPref, 0xC1, // opMemImmPref, - 0x04, + Some(0x04), opnd0, opnd1 ); @@ -1260,7 +1268,7 @@ pub fn shr(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0xD1, // opMemOnePref, 0xD3, // opMemClPref, 0xC1, // opMemImmPref, - 0x05, + Some(0x05), opnd0, opnd1 ); @@ -1277,7 +1285,7 @@ pub fn sub(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0x80, // opMemImm8 0x83, // opMemImmSml 0x81, // opMemImmLrg - 0x05, // opExtImm + Some(0x05), // opExtImm opnd0, opnd1 ); @@ -1314,10 +1322,10 @@ pub fn test(cb: &mut CodeBlock, rm_opnd: X86Opnd, test_opnd: X86Opnd) { let rm_resized = resize_opnd(rm_opnd, uimm.num_bits); if uimm.num_bits == 8 { - write_rm(cb, false, false, X86Opnd::None, rm_resized, 0x00, &[0xf6]); + write_rm(cb, false, false, X86Opnd::None, rm_resized, Some(0x00), &[0xf6]); cb.write_int(uimm.value, uimm.num_bits.into()); } else { - write_rm(cb, uimm.num_bits == 16, false, X86Opnd::None, rm_resized, 0x00, &[0xf7]); + write_rm(cb, uimm.num_bits == 16, false, X86Opnd::None, rm_resized, Some(0x00), &[0xf7]); cb.write_int(uimm.value, uimm.num_bits.into()); } }, @@ -1326,16 +1334,16 @@ pub fn test(cb: &mut CodeBlock, rm_opnd: X86Opnd, test_opnd: X86Opnd) { assert!(imm.num_bits <= 32); assert!(rm_num_bits == 64); - write_rm(cb, false, true, X86Opnd::None, rm_opnd, 0x00, &[0xf7]); + write_rm(cb, false, true, X86Opnd::None, rm_opnd, Some(0x00), &[0xf7]); cb.write_int(imm.value as u64, 32); }, X86Opnd::Reg(reg) => { assert!(reg.num_bits == rm_num_bits); if rm_num_bits == 8 { - write_rm(cb, false, false, test_opnd, rm_opnd, 0xff, &[0x84]); + write_rm(cb, false, false, test_opnd, rm_opnd, None, &[0x84]); } else { - write_rm(cb, rm_num_bits == 16, rm_num_bits == 64, test_opnd, rm_opnd, 0xff, &[0x85]); + write_rm(cb, rm_num_bits == 16, rm_num_bits == 64, test_opnd, rm_opnd, None, &[0x85]); } }, _ => unreachable!() @@ -1361,7 +1369,7 @@ pub fn xchg(cb: &mut CodeBlock, rm_opnd: X86Opnd, r_opnd: X86Opnd) { // Write the opcode and register number cb.write_byte(0x90 + (r_reg.reg_no & 7)); } else { - write_rm(cb, false, true, r_opnd, rm_opnd, 0xff, &[0x87]); + write_rm(cb, false, true, r_opnd, rm_opnd, None, &[0x87]); } } else { unreachable!(); @@ -1379,7 +1387,7 @@ pub fn xor(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) { 0x80, // opMemImm8 0x83, // opMemImmSml 0x81, // opMemImmLrg - 0x06, // opExtImm + Some(0x06), // opExtImm opnd0, opnd1 ); From 3c0b4ef1a2a972d5b0b723b82538fc8f40d85f32 Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Thu, 13 Oct 2022 18:20:04 -0400 Subject: [PATCH 037/139] fixes more clippy warnings (#6543) * fixes more clippy warnings * Fix x86 c_callable to have doc_strings --- yjit/src/asm/mod.rs | 4 +-- yjit/src/backend/arm64/mod.rs | 5 +++- yjit/src/backend/ir.rs | 2 +- yjit/src/backend/tests.rs | 54 +++++++---------------------------- yjit/src/codegen.rs | 17 ++++------- yjit/src/core.rs | 31 +++++++++----------- yjit/src/cruby.rs | 1 + yjit/src/disasm.rs | 13 ++++----- yjit/src/utils.rs | 12 ++++++-- 9 files changed, 52 insertions(+), 87 deletions(-) diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs index 2bc83ec0596ed4..8356201ba6a2b8 100644 --- a/yjit/src/asm/mod.rs +++ b/yjit/src/asm/mod.rs @@ -300,7 +300,7 @@ impl CodeBlock { } /// Produce hex string output from the bytes in a code block -impl<'a> fmt::LowerHex for CodeBlock { +impl fmt::LowerHex for CodeBlock { fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { for pos in 0..self.write_pos { let byte = unsafe { self.mem_block.start_ptr().raw_ptr().add(pos).read() }; @@ -393,7 +393,7 @@ mod tests assert_eq!(uimm_num_bits(((u16::MAX as u32) + 1).into()), 32); assert_eq!(uimm_num_bits(u32::MAX.into()), 32); - assert_eq!(uimm_num_bits(((u32::MAX as u64) + 1)), 64); + assert_eq!(uimm_num_bits((u32::MAX as u64) + 1), 64); assert_eq!(uimm_num_bits(u64::MAX), 64); } } diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index ee4f96c0d8c4da..0180737d4d640b 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -71,7 +71,7 @@ impl Assembler // A special scratch register for intermediate processing. // This register is caller-saved (so we don't have to save it before using it) const SCRATCH0: A64Opnd = A64Opnd::Reg(X16_REG); - const SCRATCH1: A64Opnd = A64Opnd::Reg(X17_REG); + const SCRATCH1: A64Opnd = A64Opnd::Reg(X17_REG); /// Get the list of registers from which we will allocate on this platform /// These are caller-saved registers @@ -281,6 +281,9 @@ impl Assembler }; } + // We are replacing instructions here so we know they are already + // being used. It is okay not to use their output here. + #[allow(unused_must_use)] match insn { Insn::Add { left, right, .. } => { match (left, right) { diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index dfdc1deb0d8f85..ba7e3721884340 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -220,7 +220,7 @@ impl From for Opnd { impl From for Opnd { fn from(value: u64) -> Self { - Opnd::UImm(value.try_into().unwrap()) + Opnd::UImm(value) } } diff --git a/yjit/src/backend/tests.rs b/yjit/src/backend/tests.rs index 440b66d69acacd..1df726c4686e81 100644 --- a/yjit/src/backend/tests.rs +++ b/yjit/src/backend/tests.rs @@ -1,47 +1,14 @@ #![cfg(test)] - use crate::asm::{CodeBlock}; -use crate::virtualmem::{CodePtr}; use crate::backend::ir::*; use crate::cruby::*; -use crate::core::*; use crate::utils::c_callable; -use InsnOpnd::*; - -// Test that this function type checks -fn gen_dup( - ctx: &mut Context, - asm: &mut Assembler, -) { - let dup_val = ctx.stack_pop(0); - let (mapping, tmp_type) = ctx.get_opnd_mapping(StackOpnd(0)); - - let loc0 = ctx.stack_push_mapping((mapping, tmp_type)); - asm.mov(loc0, dup_val); -} - -fn guard_object_is_heap( - asm: &mut Assembler, - object_opnd: Opnd, - ctx: &mut Context, - side_exit: CodePtr, -) { - asm.comment("guard object is heap"); - - // Test that the object is not an immediate - asm.test(object_opnd, Opnd::UImm(RUBY_IMMEDIATE_MASK as u64)); - asm.jnz(Target::CodePtr(side_exit)); - - // Test that the object is not false or nil - asm.cmp(object_opnd, Opnd::UImm(Qnil.into())); - asm.jbe(Target::CodePtr(side_exit)); -} #[test] fn test_add() { let mut asm = Assembler::new(); let out = asm.add(SP, Opnd::UImm(1)); - asm.add(out, Opnd::UImm(2)); + let _ = asm.add(out, Opnd::UImm(2)); } #[test] @@ -52,21 +19,21 @@ fn test_alloc_regs() { let out1 = asm.add(EC, Opnd::UImm(1)); // Pad some instructions in to make sure it can handle that. - asm.add(EC, Opnd::UImm(2)); + let _ = asm.add(EC, Opnd::UImm(2)); // Get the second output we're going to reuse. let out2 = asm.add(EC, Opnd::UImm(3)); // Pad another instruction. - asm.add(EC, Opnd::UImm(4)); + let _ = asm.add(EC, Opnd::UImm(4)); // Reuse both the previously captured outputs. - asm.add(out1, out2); + let _ = asm.add(out1, out2); // Now get a third output to make sure that the pool has registers to // allocate now that the previous ones have been returned. let out3 = asm.add(EC, Opnd::UImm(5)); - asm.add(out3, Opnd::UImm(6)); + let _ = asm.add(out3, Opnd::UImm(6)); // Here we're going to allocate the registers. let result = asm.alloc_regs(Assembler::get_alloc_regs()); @@ -198,7 +165,7 @@ fn test_base_insn_out() fn test_c_call() { c_callable! { - fn dummy_c_fun(v0: usize, v1: usize) {} + fn dummy_c_fun(_v0: usize, _v1: usize) {} } let (mut asm, mut cb) = setup_asm(); @@ -305,11 +272,12 @@ fn test_bake_string() { #[test] fn test_draining_iterator() { + let mut asm = Assembler::new(); - asm.load(Opnd::None); + let _ = asm.load(Opnd::None); asm.store(Opnd::None, Opnd::None); - asm.add(Opnd::None, Opnd::None); + let _ = asm.add(Opnd::None, Opnd::None); let mut iter = asm.into_draining_iter(); @@ -327,11 +295,11 @@ fn test_draining_iterator() { fn test_lookback_iterator() { let mut asm = Assembler::new(); - asm.load(Opnd::None); + let _ = asm.load(Opnd::None); asm.store(Opnd::None, Opnd::None); asm.store(Opnd::None, Opnd::None); - let mut iter = asm.into_lookback_iter(); + let iter = asm.into_lookback_iter(); while let Some((index, insn)) = iter.next_unmapped() { if index > 0 { diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 8b0409649f3bcd..f8379a71596984 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -189,13 +189,6 @@ fn jit_peek_at_block_handler(jit: &JITState, level: u32) -> VALUE { } } -// Add a comment at the current position in the code block -fn add_comment(cb: &mut CodeBlock, comment_str: &str) { - if cfg!(feature = "asm_comments") { - cb.add_comment(comment_str); - } -} - /// Increment a profiling counter with counter_name #[cfg(not(feature = "stats"))] macro_rules! gen_counter_incr { @@ -6804,7 +6797,7 @@ mod tests { #[test] fn test_gen_check_ints() { - let (_, _ctx, mut asm, mut cb, mut ocb) = setup_codegen(); + let (_, _ctx, mut asm, _cb, mut ocb) = setup_codegen(); let side_exit = ocb.unwrap().get_write_ptr(); gen_check_ints(&mut asm, side_exit); } @@ -6822,7 +6815,7 @@ mod tests { #[test] fn test_gen_pop() { - let (mut jit, _, mut asm, mut cb, mut ocb) = setup_codegen(); + let (mut jit, _, mut asm, _cb, mut ocb) = setup_codegen(); let mut context = Context::new_with_stack_size(1); let status = gen_pop(&mut jit, &mut context, &mut asm, &mut ocb); @@ -6872,7 +6865,7 @@ mod tests { #[test] fn test_gen_swap() { - let (mut jit, mut context, mut asm, mut cb, mut ocb) = setup_codegen(); + let (mut jit, mut context, mut asm, _cb, mut ocb) = setup_codegen(); context.stack_push(Type::Fixnum); context.stack_push(Type::Flonum); @@ -6940,7 +6933,7 @@ mod tests { #[test] fn test_int2fix() { - let (mut jit, mut context, mut asm, mut cb, mut ocb) = setup_codegen(); + let (mut jit, mut context, mut asm, _cb, mut ocb) = setup_codegen(); jit.opcode = YARVINSN_putobject_INT2FIX_0_.as_usize(); let status = gen_putobject_int2fix(&mut jit, &mut context, &mut asm, &mut ocb); @@ -7029,7 +7022,7 @@ mod tests { #[test] fn test_gen_leave() { - let (mut jit, mut context, mut asm, mut cb, mut ocb) = setup_codegen(); + let (mut jit, mut context, mut asm, _cb, mut ocb) = setup_codegen(); // Push return value context.stack_push(Type::Fixnum); gen_leave(&mut jit, &mut context, &mut asm, &mut ocb); diff --git a/yjit/src/core.rs b/yjit/src/core.rs index ec9384c4036150..c8078bb6e358ee 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -585,10 +585,8 @@ pub extern "C" fn rb_yjit_iseq_mark(payload: *mut c_void) { // Mark outgoing branch entries for branch in &block.outgoing { let branch = branch.borrow(); - for target in &branch.targets { - if let Some(target) = target { - unsafe { rb_gc_mark_movable(target.iseq.into()) }; - } + for target in branch.targets.iter().flatten() { + unsafe { rb_gc_mark_movable(target.iseq.into()) }; } } @@ -643,10 +641,8 @@ pub extern "C" fn rb_yjit_iseq_update_references(payload: *mut c_void) { // Update outgoing branch entries for branch in &block.outgoing { let mut branch = branch.borrow_mut(); - for target in &mut branch.targets { - if let Some(target) = target { - target.iseq = unsafe { rb_gc_location(target.iseq.into()) }.as_iseq(); - } + for target in branch.targets.iter_mut().flatten() { + target.iseq = unsafe { rb_gc_location(target.iseq.into()) }.as_iseq(); } } @@ -1605,9 +1601,10 @@ fn make_branch_entry(block: &BlockRef, src_ctx: &Context, gen_fn: BranchGenFn) - return branchref; } -/// Generated code calls this function with the SysV calling convention. -/// See [get_branch_target]. + c_callable! { + /// Generated code calls this function with the SysV calling convention. + /// See [get_branch_target]. fn branch_stub_hit( branch_ptr: *const c_void, target_idx: u32, @@ -2018,14 +2015,12 @@ fn free_block(blockref: &BlockRef) { let out_branch = out_branchref.borrow(); // For each successor block - for succ in &out_branch.blocks { - if let Some(succ) = succ { - // Remove outgoing branch from the successor's incoming list - let mut succ_block = succ.borrow_mut(); - succ_block - .incoming - .retain(|succ_incoming| !Rc::ptr_eq(succ_incoming, out_branchref)); - } + for succ in out_branch.blocks.iter().flatten() { + // Remove outgoing branch from the successor's incoming list + let mut succ_block = succ.borrow_mut(); + succ_block + .incoming + .retain(|succ_incoming| !Rc::ptr_eq(succ_incoming, out_branchref)); } } diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 5ddc31a06d4c0a..f31390fc574a3b 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -96,6 +96,7 @@ pub type size_t = u64; pub type RedefinitionFlag = u32; #[allow(dead_code)] +#[allow(clippy::useless_transmute)] mod autogened { use super::*; // Textually include output from rust-bindgen as suggested by its user guide. diff --git a/yjit/src/disasm.rs b/yjit/src/disasm.rs index c236d9055d98b5..10a89bafd04e76 100644 --- a/yjit/src/disasm.rs +++ b/yjit/src/disasm.rs @@ -70,11 +70,8 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St total_code_size += blockref.borrow().code_size(); } - out.push_str(&format!("NUM BLOCK VERSIONS: {}\n", block_list.len())); - out.push_str(&format!( - "TOTAL INLINE CODE SIZE: {} bytes\n", - total_code_size - )); + writeln!(out, "NUM BLOCK VERSIONS: {}", block_list.len()).unwrap(); + writeln!(out, "TOTAL INLINE CODE SIZE: {} bytes", total_code_size).unwrap(); // For each block, sorted by increasing start address for block_idx in 0..block_list.len() { @@ -95,7 +92,7 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St end_idx, code_size ); - out.push_str(&format!("== {:=<60}\n", block_ident)); + writeln!(out, "== {:=<60}", block_ident).unwrap(); // Disassemble the instructions out.push_str(&disasm_addr_range(global_cb, start_addr, code_size)); @@ -109,7 +106,7 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St // Log the size of the gap between the blocks if nonzero if gap_size > 0 { - out.push_str(&format!("... {} byte gap ...\n", gap_size)); + writeln!(out, "... {} byte gap ...", gap_size).unwrap(); } } } @@ -141,7 +138,7 @@ pub fn disasm_addr_range(cb: &CodeBlock, start_addr: *const u8, code_size: usize .detail(true) .build() .unwrap(); - cs.set_skipdata(true); + cs.set_skipdata(true).unwrap(); // Disassemble the instructions let code_slice = unsafe { std::slice::from_raw_parts(start_addr, code_size) }; diff --git a/yjit/src/utils.rs b/yjit/src/utils.rs index bea57e4fc24a5d..cabebb7dccda4c 100644 --- a/yjit/src/utils.rs +++ b/yjit/src/utils.rs @@ -122,12 +122,20 @@ yjit_print_iseq(const rb_iseq_t *iseq) #[cfg(target_arch = "aarch64")] macro_rules! c_callable { - (fn $f:ident $args:tt $(-> $ret:ty)? $body:block) => { extern "C" fn $f $args $(-> $ret)? $body }; + ($(#[$outer:meta])* + fn $f:ident $args:tt $(-> $ret:ty)? $body:block) => { + $(#[$outer])* + extern "C" fn $f $args $(-> $ret)? $body + }; } #[cfg(target_arch = "x86_64")] macro_rules! c_callable { - (fn $f:ident $args:tt $(-> $ret:ty)? $body:block) => { extern "sysv64" fn $f $args $(-> $ret)? $body }; + ($(#[$outer:meta])* + fn $f:ident $args:tt $(-> $ret:ty)? $body:block) => { + $(#[$outer])* + extern "sysv64" fn $f $args $(-> $ret)? $body + }; } pub(crate) use c_callable; From 5e554d30ba747ad1b59335efaa63c7e3c94bfad0 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 13 Oct 2022 16:28:16 -0700 Subject: [PATCH 038/139] Upgrade zlib_version on AppVeyor They removed https://zlib.net/zlib1212.zip because https://zlib.net/zlib1213.zip was released :thinking_face: Fix CI failures like: https://ci.appveyor.com/project/ruby/ruby/builds/45064876/job/bb9biogolh0u2595 --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 1ea5e592104a31..5fb678c3c6467f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -19,7 +19,7 @@ skip_commits: - '**/.document' environment: ruby_version: "24-%Platform%" - zlib_version: "1.2.12" + zlib_version: "1.2.13" matrix: - build: vs vs: 120 From ee8bcbf40578c0c4e60063a3e0c86439a6891131 Mon Sep 17 00:00:00 2001 From: S-H-GAMELINKS Date: Thu, 13 Oct 2022 00:09:17 +0900 Subject: [PATCH 039/139] Reuse ins_methods_type_i function --- class.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/class.c b/class.c index 3a83a7f052e0f2..7a32951e601ffc 100644 --- a/class.c +++ b/class.c @@ -1639,10 +1639,7 @@ ins_methods_pub_i(st_data_t name, st_data_t type, st_data_t ary) static int ins_methods_undef_i(st_data_t name, st_data_t type, st_data_t ary) { - if ((rb_method_visibility_t)type == METHOD_VISI_UNDEF) { - ins_methods_push(name, ary); - } - return ST_CONTINUE; + return ins_methods_type_i(name, type, ary, METHOD_VISI_UNDEF); } struct method_entry_arg { From 5ccb625fbbd1e774636a9fdbe0bf1c3d38e085d5 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 4 Oct 2022 18:10:41 +0900 Subject: [PATCH 040/139] Use `roomof` macro for rounding up divisions --- array.c | 2 +- bignum.c | 4 ++-- gc.c | 2 +- internal/numeric.h | 8 ++++++-- numeric.c | 2 +- regcomp.c | 2 +- regenc.h | 7 +++---- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/array.c b/array.c index 73e8a3c9ce4c6d..a33c43bdbf258e 100644 --- a/array.c +++ b/array.c @@ -1389,7 +1389,7 @@ ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step) } long ustep = (step < 0) ? -step : step; - len = (len + ustep - 1) / ustep; + len = roomof(len, ustep); long i; long j = offset + ((step > 0) ? 0 : (orig_len - 1)); diff --git a/bignum.c b/bignum.c index 9901a807b1936d..4f8349dd17d457 100644 --- a/bignum.c +++ b/bignum.c @@ -977,7 +977,7 @@ integer_unpack_num_bdigits_small(size_t numwords, size_t wordsize, size_t nails, { /* nlp_bits stands for number of leading padding bits */ size_t num_bits = (wordsize * CHAR_BIT - nails) * numwords; - size_t num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG; + size_t num_bdigits = roomof(num_bits, BITSPERDIG); *nlp_bits_ret = (int)(num_bdigits * BITSPERDIG - num_bits); return num_bdigits; } @@ -987,7 +987,7 @@ integer_unpack_num_bdigits_generic(size_t numwords, size_t wordsize, size_t nail { /* BITSPERDIG = SIZEOF_BDIGIT * CHAR_BIT */ /* num_bits = (wordsize * CHAR_BIT - nails) * numwords */ - /* num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG */ + /* num_bdigits = roomof(num_bits, BITSPERDIG) */ /* num_bits = CHAR_BIT * (wordsize * numwords) - nails * numwords = CHAR_BIT * num_bytes1 - nails * numwords */ size_t num_bytes1 = wordsize * numwords; diff --git a/gc.c b/gc.c index a5e37b72e23c54..52d5609c3ea895 100644 --- a/gc.c +++ b/gc.c @@ -870,7 +870,7 @@ typedef struct rb_objspace { #define BASE_SLOT_SIZE sizeof(RVALUE) -#define CEILDIV(i, mod) (((i) + (mod) - 1)/(mod)) +#define CEILDIV(i, mod) roomof(i, mod) enum { HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG), HEAP_PAGE_ALIGN_MASK = (~(~0UL << HEAP_PAGE_ALIGN_LOG)), diff --git a/internal/numeric.h b/internal/numeric.h index 19069cb3bc7ee1..89bc54b307ff6a 100644 --- a/internal/numeric.h +++ b/internal/numeric.h @@ -35,12 +35,16 @@ enum ruby_num_rounding_mode { RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT, }; +/* same as internal.h */ +#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0]))) +#define roomof(x, y) (((x) + (y) - 1) / (y)) +#define type_roomof(x, y) roomof(sizeof(x), sizeof(y)) + #if SIZEOF_DOUBLE <= SIZEOF_VALUE typedef double rb_float_value_type; #else typedef struct { - VALUE values[(SIZEOF_DOUBLE + SIZEOF_VALUE - 1) / SIZEOF_VALUE]; - /* roomof() needs internal.h, and the order of some macros may matter */ + VALUE values[roomof(SIZEOF_DOUBLE, SIZEOF_VALUE)]; } rb_float_value_type; #endif diff --git a/numeric.c b/numeric.c index 4f927f00fb655b..8607d697942a3a 100644 --- a/numeric.c +++ b/numeric.c @@ -1055,7 +1055,7 @@ flo_to_s(VALUE flt) { enum {decimal_mant = DBL_MANT_DIG-DBL_DIG}; enum {float_dig = DBL_DIG+1}; - char buf[float_dig + (decimal_mant + CHAR_BIT - 1) / CHAR_BIT + 10]; + char buf[float_dig + roomof(decimal_mant, CHAR_BIT) + 10]; double value = RFLOAT_VALUE(flt); VALUE s; char *p, *e; diff --git a/regcomp.c b/regcomp.c index 94640639d8b87f..be85d85f93d476 100644 --- a/regcomp.c +++ b/regcomp.c @@ -341,7 +341,7 @@ static int select_str_opcode(int mb_len, OnigDistance byte_len, int ignore_case) { int op; - OnigDistance str_len = (byte_len + mb_len - 1) / mb_len; + OnigDistance str_len = roomof(byte_len, mb_len); if (ignore_case) { switch (str_len) { diff --git a/regenc.h b/regenc.h index 8c4ff0483b380e..1c409010545530 100644 --- a/regenc.h +++ b/regenc.h @@ -125,10 +125,9 @@ typedef struct { #define POSIX_BRACKET_ENTRY_INIT(name, ctype) \ {(short int )(sizeof(name) - 1), name, (ctype)} -#ifndef numberof -# define numberof(array) (int )(sizeof(array) / sizeof((array)[0])) -#endif - +#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0]))) +#define roomof(x, y) (((x) + (y) - 1) / (y)) +#define type_roomof(x, y) roomof(sizeof(x), sizeof(y)) #define USE_CRNL_AS_LINE_TERMINATOR #define USE_UNICODE_PROPERTIES From 7b7e5153e81288fe57ae64f2e1db228435156aeb Mon Sep 17 00:00:00 2001 From: manga_osyo Date: Mon, 10 Oct 2022 12:30:35 +0900 Subject: [PATCH 041/139] [ruby/pp] [Feature #19045] Add support Data#pretty_print https://github.com/ruby/pp/commit/343a20d721 --- lib/pp.rb | 20 ++++++++++++++++++++ test/test_pp.rb | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/pp.rb b/lib/pp.rb index f43356a3dfaaab..81551aa116d7dc 100644 --- a/lib/pp.rb +++ b/lib/pp.rb @@ -416,6 +416,26 @@ def pretty_print_cycle(q) # :nodoc: end end +class Data # :nodoc: + def pretty_print(q) # :nodoc: + q.group(1, sprintf("#') { + q.seplist(PP.mcall(self, Data, :members), lambda { q.text "," }) {|member| + q.breakable + q.text member.to_s + q.text '=' + q.group(1) { + q.breakable '' + q.pp public_send(member) + } + } + } + end + + def pretty_print_cycle(q) # :nodoc: + q.text sprintf("#", PP.mcall(self, Kernel, :class).name) + end +end if "3.2" <= RUBY_VERSION + class Range # :nodoc: def pretty_print(q) # :nodoc: q.pp self.begin diff --git a/test/test_pp.rb b/test/test_pp.rb index 9cef555d79f0d8..4fcb8df4b0241e 100644 --- a/test/test_pp.rb +++ b/test/test_pp.rb @@ -152,6 +152,15 @@ def test_struct assert_equal("#{a.inspect}\n", PP.pp(a, ''.dup)) end + if "3.2" <= RUBY_VERSION + D = Data.define(:aaa, :bbb) + def test_data + a = D.new("aaa", "bbb") + assert_equal("#\n", PP.pp(a, ''.dup, 20)) + assert_equal("#{a.inspect}\n", PP.pp(a, ''.dup)) + end + end + def test_object a = Object.new a.instance_eval {@a = a} From d4162053410782a449e0921ee7222e7ce3deca6f Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 15 Oct 2022 01:45:51 +1300 Subject: [PATCH 042/139] Copy `IO#timeout` on `IO#dup`. (#6546) --- io.c | 1 + test/ruby/test_io.rb | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/io.c b/io.c index 99513573d78b30..2fe092b84e49a9 100644 --- a/io.c +++ b/io.c @@ -8407,6 +8407,7 @@ rb_io_init_copy(VALUE dest, VALUE io) fptr->encs = orig->encs; fptr->pid = orig->pid; fptr->lineno = orig->lineno; + fptr->timeout = orig->timeout; if (!NIL_P(orig->pathv)) fptr->pathv = orig->pathv; fptr_copy_finalizer(fptr, orig); diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 0c8beb2f955c9a..989c4c89913a9b 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -1441,6 +1441,14 @@ def test_dup_many End end + def test_dup_timeout + with_pipe do |r, w| + r.timeout = 0.1 + r2 = r.dup + assert_equal(0.1, r2.timeout) + end + end + def test_inspect with_pipe do |r, w| assert_match(/^#$/, r.inspect) From 60610031009e60bdfe5775b0316df251ee36a973 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Tue, 11 Oct 2022 12:02:15 -0500 Subject: [PATCH 043/139] [ruby/delegate] Fix DelegateClass block "method redefined" warning This commit prevents "method redefined" warnings when overriding methods within a `DelegateClass` block, such as in the following example: ```ruby Base = Class.new do def foo "foo" end end Overridden = DelegateClass(Base) do def foo super + "!" end end ``` Fixes https://bugs.ruby-lang.org/issues/19047. https://github.com/ruby/delegate/commit/214fae86de --- lib/delegate.rb | 2 ++ test/test_delegate.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/delegate.rb b/lib/delegate.rb index 70d4e4ad1d04cd..af95c866f3e795 100644 --- a/lib/delegate.rb +++ b/lib/delegate.rb @@ -412,10 +412,12 @@ def __setobj__(obj) # :nodoc: end protected_instance_methods.each do |method| define_method(method, Delegator.delegating_block(method)) + alias_method(method, method) protected method end public_instance_methods.each do |method| define_method(method, Delegator.delegating_block(method)) + alias_method(method, method) end end klass.define_singleton_method :public_instance_methods do |all=true| diff --git a/test/test_delegate.rb b/test/test_delegate.rb index 57480b18ea674c..431d134f0f258a 100644 --- a/test/test_delegate.rb +++ b/test/test_delegate.rb @@ -29,6 +29,18 @@ def test_delegate_class_block assert_equal(1, klass.new([1]).foo) end + def test_delegate_class_block_with_override + warning = EnvUtil.verbose_warning do + klass = DelegateClass(Array) do + def first + super.inspect + end + end + assert_equal("1", klass.new([1]).first) + end + assert_empty(warning) + end + def test_systemcallerror_eq e = SystemCallError.new(0) assert((SimpleDelegator.new(e) == e) == (e == SimpleDelegator.new(e)), "[ruby-dev:34808]") From ee6cc2502664ac46edc61868d8954b626bb48e53 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 15 Oct 2022 01:03:47 +0900 Subject: [PATCH 044/139] Remove wrong dollar --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d20d70b8d1860a..058bfd3aa85e9c 100644 --- a/configure.ac +++ b/configure.ac @@ -3070,7 +3070,7 @@ AS_IF([test "$rb_cv_dlopen" = yes], [ "-undefined dynamic_lookup" \ ; do test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`" - RUBY_TRY_LDFLAGS([$flag], [], [$flag=]) + RUBY_TRY_LDFLAGS([$flag], [], [flag=]) AS_IF([test x"$flag" = x], [continue]) AC_MSG_CHECKING([whether $flag is accepted for bundle]) From 7e24ebc649b9b12e5fc704d7fc7563aeaf589e03 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 14 Oct 2022 09:16:21 -0700 Subject: [PATCH 045/139] Disable msystem: MINGW64 job on GitHub Actions (#6545) --- .github/workflows/mingw.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 020295baa12189..42c35e890cbec4 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -37,10 +37,7 @@ jobs: strategy: matrix: include: - - msystem: "MINGW64" - base_ruby: 2.6 - test_task: "check" - test-all-opts: "--name=!/TestObjSpace#test_reachable_objects_during_iteration/" + # To mitigate flakiness of MinGW CI, we test only one runtime that newer MSYS2 uses. - msystem: "UCRT64" base_ruby: head test_task: "check" From 7e81dd94073d699f6f0c930072cd43e5e387784e Mon Sep 17 00:00:00 2001 From: "Eileen M. Uchitelle" Date: Fri, 14 Oct 2022 12:43:18 -0400 Subject: [PATCH 046/139] Update yjit docs (#6548) * The list of supported architectures was updated in https://github.com/ruby/ruby/commit/5ef048e5b1c3dd61adf782ace570bb0a1f9bb12f but the first paragraph wasn't updated. * `--yjit-trace-exits` was missing from the command-line options * Fixes some spacing issues * Updates call threshold default to 10, verified in the code that's correct. * Add code ticks around method names. * Fix namespace of stats example --- doc/yjit/yjit.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md index c2521eba426bf9..d4a15d0c7795e7 100644 --- a/doc/yjit/yjit.md +++ b/doc/yjit/yjit.md @@ -13,8 +13,7 @@ YJIT - Yet Another Ruby JIT YJIT is a lightweight, minimalistic Ruby JIT built inside CRuby. It lazily compiles code using a Basic Block Versioning (BBV) architecture. The target use case is that of servers running Ruby on Rails, an area where MJIT has not yet managed to deliver speedups. -To simplify development, we currently support only macOS and Linux on x86-64, but an ARM64 backend -is part of future plans. +YJIT is currently supported for macOS and Linux on x86-64 and arm64/aarch64 CPUs. This project is open source and falls under the same license as CRuby. If you wish to learn more about the approach taken, here are some conference talks and publications: @@ -105,10 +104,12 @@ make -j install ``` Typically configure will choose the default C compiler. To specify the C compiler, use + ``` # Choosing a specific c compiler export CC=/path/to/my/chosen/c/compiler ``` + before running `./configure`. You can test that YJIT works correctly by running: @@ -141,15 +142,15 @@ You can dump statistics about compilation and execution by running YJIT with the The machine code generated for a given method can be printed by adding `puts RubyVM::YJIT.disasm(method(:method_name))` to a Ruby script. Note that no code will be generated if the method is not compiled. - ### Command-Line Options YJIT supports all command-line options supported by upstream CRuby, but also adds a few YJIT-specific options: - `--yjit`: enable YJIT (disabled by default) -- `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function (default 2) +- `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function (default 10) - `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 256 MiB) - `--yjit-stats`: produce statistics after the execution of a program (must compile with `cppflags=-DRUBY_DEBUG` to use this) +- `--yjit-trace-exits`: produce a Marshal dump of backtraces from specific exits. Automatically enables `--yjit-stats` (must compile with `cppflags=-DRUBY_DEBUG` to use this) - `--yjit-max-versions=N`: maximum number of versions to generate per basic block (default 4) - `--yjit-greedy-versioning`: greedy versioning mode (disabled by default, may increase code size) @@ -176,7 +177,7 @@ You can also compile YJIT in debug mode and use the `--yjit-stats` command-line ### Memory Statistics -YJIT, including in production configuration, keeps track of the size of generated code. If you check YJIT.runtime_stats you can see them: +YJIT, including in production configuration, keeps track of the size of generated code. If you check `YJIT.runtime_stats` you can see them: ``` $ RUBYOPT="--yjit" irb @@ -188,11 +189,11 @@ These are the size in bytes of generated inlined code and generated outlined cod ### Other Statistics -If you compile Ruby with RUBY_DEBUG and/or YJIT_STATS defined and run with "--yjit --yjit-stats", YJIT will track and return performance statistics in RubyVM::YJIT.runtime_stats. +If you compile Ruby with `RUBY_DEBUG` and/or `YJIT_STATS` defined and run with `--yjit --yjit-stats`, YJIT will track and return performance statistics in `RubyVM::YJIT.runtime_stats`. ``` $ RUBYOPT="--yjit --yjit-stats" irb -irb(main):001:0> YJIT.runtime_stats +irb(main):001:0> RubyVM::YJIT.runtime_stats => {:inline_code_size=>340745, :outlined_code_size=>297664, From fb99227ca1ee9d8540d251c8b61c3e6433211714 Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Fri, 14 Oct 2022 13:04:53 -0400 Subject: [PATCH 047/139] More clippy fixes (#6547) --- yjit/src/asm/arm64/arg/shifted_imm.rs | 18 ++++--- yjit/src/backend/tests.rs | 17 +++++-- yjit/src/backend/x86_64/mod.rs | 24 +++++---- yjit/src/codegen.rs | 71 ++++++++++++++------------- 4 files changed, 78 insertions(+), 52 deletions(-) diff --git a/yjit/src/asm/arm64/arg/shifted_imm.rs b/yjit/src/asm/arm64/arg/shifted_imm.rs index 0dd7af25b5994e..4602ac64ab9495 100644 --- a/yjit/src/asm/arm64/arg/shifted_imm.rs +++ b/yjit/src/asm/arm64/arg/shifted_imm.rs @@ -46,18 +46,24 @@ mod tests { #[test] fn test_no_shift() { - let value = 256; - let result = ShiftedImmediate::try_from(value); + let expected_value = 256; + let result = ShiftedImmediate::try_from(expected_value); - assert!(matches!(result, Ok(ShiftedImmediate { shift: Shift::LSL0, value }))); + match result { + Ok(ShiftedImmediate { shift: Shift::LSL0, value }) => assert_eq!(value as u64, expected_value), + _ => panic!("Unexpected shift value") + } } #[test] fn test_maximum_no_shift() { - let value = (1 << 12) - 1; - let result = ShiftedImmediate::try_from(value); + let expected_value = (1 << 12) - 1; + let result = ShiftedImmediate::try_from(expected_value); - assert!(matches!(result, Ok(ShiftedImmediate { shift: Shift::LSL0, value }))); + match result { + Ok(ShiftedImmediate { shift: Shift::LSL0, value }) => assert_eq!(value as u64, expected_value), + _ => panic!("Unexpected shift value") + } } #[test] diff --git a/yjit/src/backend/tests.rs b/yjit/src/backend/tests.rs index 1df726c4686e81..1bad8642a247dd 100644 --- a/yjit/src/backend/tests.rs +++ b/yjit/src/backend/tests.rs @@ -44,9 +44,20 @@ fn test_alloc_regs() { let reg0 = regs[0]; let reg1 = regs[1]; - assert!(matches!(result.insns[0].out_opnd(), Some(Opnd::Reg(reg0)))); - assert!(matches!(result.insns[2].out_opnd(), Some(Opnd::Reg(reg1)))); - assert!(matches!(result.insns[5].out_opnd(), Some(Opnd::Reg(reg0)))); + match result.insns[0].out_opnd() { + Some(Opnd::Reg(value)) => assert_eq!(value, ®0), + val => panic!("Unexpected register value {:?}", val), + } + + match result.insns[2].out_opnd() { + Some(Opnd::Reg(value)) => assert_eq!(value, ®1), + val => panic!("Unexpected register value {:?}", val), + } + + match result.insns[5].out_opnd() { + Some(Opnd::Reg(value)) => assert_eq!(value, ®0), + val => panic!("Unexpected register value {:?}", val), + } } fn setup_asm() -> (Assembler, CodeBlock) { diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index 2f770c2eac7923..f6bd8227276a8f 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -152,6 +152,9 @@ impl Assembler } } + // We are replacing instructions here so we know they are already + // being used. It is okay not to use their output here. + #[allow(unused_must_use)] match &mut insn { Insn::Add { left, right, out } | Insn::Sub { left, right, out } | @@ -660,6 +663,7 @@ impl Assembler // we feed to the backend could get lowered into other // instructions. So it's possible that some of our backend // instructions can never make it to the emit stage. + #[allow(unreachable_patterns)] _ => panic!("unsupported instruction passed to x86 backend: {:?}", insn) }; } @@ -700,7 +704,7 @@ mod tests { fn test_emit_add_lt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.add(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); + let _ = asm.add(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c04881c0ff000000"); @@ -710,7 +714,7 @@ mod tests { fn test_emit_add_gt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.add(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); + let _ = asm.add(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c01d8"); @@ -720,7 +724,7 @@ mod tests { fn test_emit_and_lt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.and(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); + let _ = asm.and(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c04881e0ff000000"); @@ -730,7 +734,7 @@ mod tests { fn test_emit_and_gt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.and(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); + let _ = asm.and(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c21d8"); @@ -760,7 +764,7 @@ mod tests { fn test_emit_or_lt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.or(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); + let _ = asm.or(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c04881c8ff000000"); @@ -770,7 +774,7 @@ mod tests { fn test_emit_or_gt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.or(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); + let _ = asm.or(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c09d8"); @@ -780,7 +784,7 @@ mod tests { fn test_emit_sub_lt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.sub(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); + let _ = asm.sub(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c04881e8ff000000"); @@ -790,7 +794,7 @@ mod tests { fn test_emit_sub_gt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.sub(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); + let _ = asm.sub(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c29d8"); @@ -820,7 +824,7 @@ mod tests { fn test_emit_xor_lt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.xor(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); + let _ = asm.xor(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c04881f0ff000000"); @@ -830,7 +834,7 @@ mod tests { fn test_emit_xor_gt_32_bits() { let (mut asm, mut cb) = setup_asm(); - asm.xor(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); + let _ = asm.xor(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c31d8"); diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index f8379a71596984..88b51674cf3351 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1923,7 +1923,7 @@ fn gen_set_ivar( jit: &mut JITState, ctx: &mut Context, asm: &mut Assembler, - recv: VALUE, + _recv: VALUE, ivar_name: ID, flags: u32, argc: i32, @@ -1947,7 +1947,7 @@ fn gen_set_ivar( rb_vm_set_ivar_id as *const u8, vec![ recv_opnd, - Opnd::UImm(ivar_name.into()), + Opnd::UImm(ivar_name), val_opnd, ], ); @@ -2077,41 +2077,46 @@ fn gen_get_ivar( side_exit, ); - // If there is no IVAR index, then the ivar was undefined - // when we entered the compiler. That means we can just return - // nil for this shape + iv name - if ivar_index.is_none() { - let out_opnd = ctx.stack_push(Type::Nil); - asm.mov(out_opnd, Qnil.into()); - } else if embed_test_result { - // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h - - // Load the variable - let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index.unwrap() * SIZEOF_VALUE) as i32; - let ivar_opnd = Opnd::mem(64, recv, offs); - - // Push the ivar on the stack - let out_opnd = ctx.stack_push(Type::Unknown); - asm.mov(out_opnd, ivar_opnd); - } else { - // Compile time value is *not* embedded. - - if USE_RVARGC == 0 { - // Check that the extended table is big enough - // Check that the slot is inside the extended table (num_slots > index) - let num_slots = Opnd::mem(32, recv, ROBJECT_OFFSET_NUMIV); - asm.cmp(num_slots, Opnd::UImm(ivar_index.unwrap() as u64)); - asm.jbe(counted_exit!(ocb, side_exit, getivar_idx_out_of_range).into()); + match ivar_index { + // If there is no IVAR index, then the ivar was undefined + // when we entered the compiler. That means we can just return + // nil for this shape + iv name + None => { + let out_opnd = ctx.stack_push(Type::Nil); + asm.mov(out_opnd, Qnil.into()); } + Some(ivar_index) => { + if embed_test_result { + // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h + + // Load the variable + let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index * SIZEOF_VALUE) as i32; + let ivar_opnd = Opnd::mem(64, recv, offs); + + // Push the ivar on the stack + let out_opnd = ctx.stack_push(Type::Unknown); + asm.mov(out_opnd, ivar_opnd); + } else { + // Compile time value is *not* embedded. + + if USE_RVARGC == 0 { + // Check that the extended table is big enough + // Check that the slot is inside the extended table (num_slots > index) + let num_slots = Opnd::mem(32, recv, ROBJECT_OFFSET_NUMIV); + asm.cmp(num_slots, Opnd::UImm(ivar_index as u64)); + asm.jbe(counted_exit!(ocb, side_exit, getivar_idx_out_of_range).into()); + } - // Get a pointer to the extended table - let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_IVPTR)); + // Get a pointer to the extended table + let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_IVPTR)); - // Read the ivar from the extended table - let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index.unwrap()) as i32); + // Read the ivar from the extended table + let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index) as i32); - let out_opnd = ctx.stack_push(Type::Unknown); - asm.mov(out_opnd, ivar_opnd); + let out_opnd = ctx.stack_push(Type::Unknown); + asm.mov(out_opnd, ivar_opnd); + } + } } // Jump to next instruction. This allows guard chains to share the same successor. From 9a5684bf7f92ae71a55ede82ec18c31c161694ec Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Tue, 4 Oct 2022 15:27:13 -0400 Subject: [PATCH 048/139] Add test for ractor race condition on ivar sets --- bootstraptest/test_ractor.rb | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index b29db7ab0eb8de..6373349e427d61 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1579,4 +1579,43 @@ def foo((x), (y)); ->{ super }; end end } +assert_equal "ok", %q{ + module M + def foo + @foo + end + end + + class A + include M + + def initialize + 100.times { |i| instance_variable_set(:"@var_#{i}", "bad: #{i}") } + @foo = 2 + end + end + + class B + include M + + def initialize + @foo = 1 + end + end + + Ractor.new do + b = B.new + 100_000.times do + raise unless b.foo == 1 + end + end + + a = A.new + 100_000.times do + raise unless a.foo == 2 + end + + "ok" +} + end # if !ENV['GITHUB_WORKFLOW'] From cbd3d655745564e3c33a29a5625ac30b4d69fb29 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 14 Oct 2022 11:59:31 -0700 Subject: [PATCH 049/139] Add a test for transition order We only cache the destination shape id, but that can lead to false cache hits. This patch tests that we correctly handle false cache hits --- test/ruby/test_shapes.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 0da296189dd6fc..0f947bcd479433 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -3,6 +3,20 @@ # These test the functionality of object shapes class TestShapes < Test::Unit::TestCase + class ShapeOrder + def initialize + @b = :b # 5 => 6 + end + + def set_b + @b = :b # 5 => 6 + end + + def set_c + @c = :c # 5 => 7 + end + end + class Example def initialize @a = 1 @@ -37,6 +51,17 @@ def refute_shape_equal(shape1, shape2) refute_equal(shape1.id, shape2.id) end + def test_shape_order + bar = ShapeOrder.new # 0 => 1 + bar.set_c # 1 => 2 + bar.set_b # 2 => 2 + + foo = ShapeOrder.new # 0 => 1 + shape_id = RubyVM::Shape.of(foo).id + foo.set_b # should not transition + assert_equal shape_id, RubyVM::Shape.of(foo).id + end + def test_iv_index example = RemoveAndAdd.new shape = RubyVM::Shape.of(example) From 53e0e5e8df8648e23278e4811e634671de9e1af1 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 14 Oct 2022 12:45:00 -0700 Subject: [PATCH 050/139] YJIT: Avoid creating payloads for non-JITed ISEQs (#6549) * YJIT: Count freed ISEQs * YJIT: Avoid creating payloads for non-JITed ISEQs --- yjit.rb | 1 + yjit/src/core.rs | 72 +++++++++++++++++++++++++++--------------- yjit/src/disasm.rs | 4 +-- yjit/src/invariants.rs | 2 +- yjit/src/stats.rs | 1 + 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/yjit.rb b/yjit.rb index 226f2a81345428..b80861dbfbb6fb 100644 --- a/yjit.rb +++ b/yjit.rb @@ -214,6 +214,7 @@ def _print_stats $stderr.puts "compilation_failure: " + ("%10d" % compilation_failure) if compilation_failure != 0 $stderr.puts "compiled_iseq_count: " + ("%10d" % stats[:compiled_iseq_count]) $stderr.puts "compiled_block_count: " + ("%10d" % stats[:compiled_block_count]) + $stderr.puts "freed_iseq_count: " + ("%10d" % stats[:freed_iseq_count]) $stderr.puts "invalidation_count: " + ("%10d" % stats[:invalidation_count]) $stderr.puts "constant_state_bumps: " + ("%10d" % stats[:constant_state_bumps]) $stderr.puts "inline_code_size: " + ("%10d" % stats[:inline_code_size]) diff --git a/yjit/src/core.rs b/yjit/src/core.rs index c8078bb6e358ee..1805a2bb13300f 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -491,14 +491,14 @@ impl IseqPayload { /// Get the payload for an iseq. For safety it's up to the caller to ensure the returned `&mut` /// upholds aliasing rules and that the argument is a valid iseq. -pub unsafe fn load_iseq_payload(iseq: IseqPtr) -> Option<&'static mut IseqPayload> { - let payload = rb_iseq_get_yjit_payload(iseq); +pub fn get_iseq_payload(iseq: IseqPtr) -> Option<&'static mut IseqPayload> { + let payload = unsafe { rb_iseq_get_yjit_payload(iseq) }; let payload: *mut IseqPayload = payload.cast(); - payload.as_mut() + unsafe { payload.as_mut() } } /// Get the payload object associated with an iseq. Create one if none exists. -fn get_iseq_payload(iseq: IseqPtr) -> &'static mut IseqPayload { +fn get_or_create_iseq_payload(iseq: IseqPtr) -> &'static mut IseqPayload { type VoidPtr = *mut c_void; let payload_non_null = unsafe { @@ -546,6 +546,9 @@ pub extern "C" fn rb_yjit_iseq_free(payload: *mut c_void) { // SAFETY: We got the pointer from Box::into_raw(). let payload = unsafe { Box::from_raw(payload) }; + // Increment the freed iseq count + incr_counter!(freed_iseq_count); + // Remove all blocks in the payload from global invariants table. for versions in &payload.version_map { for block in versions { @@ -679,8 +682,19 @@ pub extern "C" fn rb_yjit_iseq_update_references(payload: *mut c_void) { } /// Get all blocks for a particular place in an iseq. -fn get_version_list(blockid: BlockId) -> &'static mut VersionList { - let payload = get_iseq_payload(blockid.iseq); +fn get_version_list(blockid: BlockId) -> Option<&'static mut VersionList> { + let insn_idx = blockid.idx.as_usize(); + match get_iseq_payload(blockid.iseq) { + Some(payload) if insn_idx < payload.version_map.len() => { + Some(payload.version_map.get_mut(insn_idx).unwrap()) + }, + _ => None + } +} + +/// Get or create all blocks for a particular place in an iseq. +fn get_or_create_version_list(blockid: BlockId) -> &'static mut VersionList { + let payload = get_or_create_iseq_payload(blockid.iseq); let insn_idx = blockid.idx.as_usize(); // Expand the version map as necessary @@ -695,32 +709,34 @@ fn get_version_list(blockid: BlockId) -> &'static mut VersionList { /// Take all of the blocks for a particular place in an iseq pub fn take_version_list(blockid: BlockId) -> VersionList { - let payload = get_iseq_payload(blockid.iseq); let insn_idx = blockid.idx.as_usize(); - - if insn_idx >= payload.version_map.len() { - VersionList::default() - } else { - mem::take(&mut payload.version_map[insn_idx]) + match get_iseq_payload(blockid.iseq) { + Some(payload) if insn_idx < payload.version_map.len() => { + mem::take(&mut payload.version_map[insn_idx]) + }, + _ => VersionList::default(), } } /// Count the number of block versions matching a given blockid fn get_num_versions(blockid: BlockId) -> usize { let insn_idx = blockid.idx.as_usize(); - let payload = get_iseq_payload(blockid.iseq); - - payload - .version_map - .get(insn_idx) - .map(|versions| versions.len()) - .unwrap_or(0) + match get_iseq_payload(blockid.iseq) { + Some(payload) => { + payload + .version_map + .get(insn_idx) + .map(|versions| versions.len()) + .unwrap_or(0) + } + None => 0, + } } -/// Get a list of block versions generated for an iseq +/// Get or create a list of block versions generated for an iseq /// This is used for disassembly (see disasm.rs) -pub fn get_iseq_block_list(iseq: IseqPtr) -> Vec { - let payload = get_iseq_payload(iseq); +pub fn get_or_create_iseq_block_list(iseq: IseqPtr) -> Vec { + let payload = get_or_create_iseq_payload(iseq); let mut blocks = Vec::::new(); @@ -741,7 +757,10 @@ pub fn get_iseq_block_list(iseq: IseqPtr) -> Vec { /// Retrieve a basic block version for an (iseq, idx) tuple /// This will return None if no version is found fn find_block_version(blockid: BlockId, ctx: &Context) -> Option { - let versions = get_version_list(blockid); + let versions = match get_version_list(blockid) { + Some(versions) => versions, + None => return None, + }; // Best match found let mut best_version: Option = None; @@ -802,7 +821,7 @@ fn add_block_version(blockref: &BlockRef, cb: &CodeBlock) { // Function entry blocks must have stack size 0 assert!(!(block.blockid.idx == 0 && block.ctx.stack_size > 0)); - let version_list = get_version_list(block.blockid); + let version_list = get_or_create_version_list(block.blockid); version_list.push(blockref.clone()); @@ -830,7 +849,10 @@ fn add_block_version(blockref: &BlockRef, cb: &CodeBlock) { /// Remove a block version from the version map of its parent ISEQ fn remove_block_version(blockref: &BlockRef) { let block = blockref.borrow(); - let version_list = get_version_list(block.blockid); + let version_list = match get_version_list(block.blockid) { + Some(version_list) => version_list, + None => return, + }; // Retain the versions that are not this one version_list.retain(|other| blockref != other); diff --git a/yjit/src/disasm.rs b/yjit/src/disasm.rs index 10a89bafd04e76..9e45dffd60e834 100644 --- a/yjit/src/disasm.rs +++ b/yjit/src/disasm.rs @@ -42,7 +42,7 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St let mut out = String::from(""); // Get a list of block versions generated for this iseq - let mut block_list = get_iseq_block_list(iseq); + let mut block_list = get_or_create_iseq_block_list(iseq); // Get a list of codeblocks relevant to this iseq let global_cb = crate::codegen::CodegenGlobals::get_inline_cb(); @@ -206,7 +206,7 @@ fn insns_compiled(iseq: IseqPtr) -> Vec<(String, u32)> { let mut insn_vec = Vec::new(); // Get a list of block versions generated for this iseq - let block_list = get_iseq_block_list(iseq); + let block_list = get_or_create_iseq_block_list(iseq); // For each block associated with this iseq for blockref in &block_list { diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs index c7c0701e7447a5..07de3374c8ea7a 100644 --- a/yjit/src/invariants.rs +++ b/yjit/src/invariants.rs @@ -535,7 +535,7 @@ pub extern "C" fn rb_yjit_tracing_invalidate_all() { unsafe { rb_yjit_for_each_iseq(Some(invalidate_all_blocks_for_tracing)) }; extern "C" fn invalidate_all_blocks_for_tracing(iseq: IseqPtr) { - if let Some(payload) = unsafe { load_iseq_payload(iseq) } { + if let Some(payload) = unsafe { get_iseq_payload(iseq) } { // C comment: // Leaking the blocks for now since we might have situations where // a different ractor is waiting for the VM lock in branch_stub_hit(). diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index bf968ee5638bfe..0ad77fc5df1230 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -252,6 +252,7 @@ make_counters! { compiled_iseq_count, compiled_block_count, compilation_failure, + freed_iseq_count, exit_from_branch_stub, From 1acc1a5c6d5d01b2822d7aa4356208095481724b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 14 Oct 2022 16:11:37 -0700 Subject: [PATCH 051/139] YJIT doesn't need rb_obj_ensure_iv_index_mapping We should make this function static and remove it from YJIT bindings. --- internal/variable.h | 1 - variable.c | 2 +- yjit/bindgen/src/main.rs | 1 - yjit/src/cruby_bindings.inc.rs | 3 --- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/variable.h b/internal/variable.h index 47037a33923265..bb24f5129ff6a3 100644 --- a/internal/variable.h +++ b/internal/variable.h @@ -35,7 +35,6 @@ void rb_gvar_ractor_local(const char *name); static inline bool ROBJ_TRANSIENT_P(VALUE obj); static inline void ROBJ_TRANSIENT_SET(VALUE obj); static inline void ROBJ_TRANSIENT_UNSET(VALUE obj); -uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id); struct gen_ivtbl; int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl); diff --git a/variable.c b/variable.c index 1f532f2154eaab..4b8c87bd1f62bc 100644 --- a/variable.c +++ b/variable.c @@ -1449,7 +1449,7 @@ rb_init_iv_list(VALUE obj) // @note May raise when there are too many instance variables. // @note YJIT uses this function at compile time to simplify the work needed to // access the variable at runtime. -uint32_t +static uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id) { RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index b945e9b1060ab4..7bdfdade7709ad 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -304,7 +304,6 @@ fn main() { // From internal/variable.h .allowlist_function("rb_gvar_(get|set)") - .allowlist_function("rb_obj_ensure_iv_index_mapping") // From include/ruby/internal/intern/variable.h .allowlist_function("rb_attr_get") diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index db14f9ca025d19..e12ac9eef167b3 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1015,9 +1015,6 @@ extern "C" { extern "C" { pub fn rb_hash_resurrect(hash: VALUE) -> VALUE; } -extern "C" { - pub fn rb_obj_ensure_iv_index_mapping(obj: VALUE, id: ID) -> u32; -} extern "C" { pub fn rb_gvar_get(arg1: ID) -> VALUE; } From 8a420670a29a7c78c7201f678eb26528621bf39f Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 15 Oct 2022 19:59:04 +1300 Subject: [PATCH 052/139] Introduce `Fiber::Scheduler#io_select` hook for non-blocking `IO.select`. (#6559) --- NEWS.md | 4 ++++ include/ruby/fiber/scheduler.h | 22 +++++++++++++++++++++- io.c | 9 +++++++++ scheduler.c | 21 +++++++++++++++++++++ test/fiber/scheduler.rb | 7 +++++++ test/fiber/test_io.rb | 22 ++++++++++++++++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index a355d91d7d43d2..730462d85daf23 100644 --- a/NEWS.md +++ b/NEWS.md @@ -102,6 +102,9 @@ Note that each entry is kept to a minimum, see links for details. Note: We're only listing outstanding class updates. +* Fiber::Scheduler + * Introduce `Fiber::Scheduler#io_select` for non-blocking `IO.select`. [[Feature #19060]] + * IO * Introduce `IO#timeout=` and `IO#timeout` which can cause `IO::TimeoutError` to be raised if a blocking operation exceeds the @@ -354,3 +357,4 @@ The following deprecated APIs are removed. [Feature #16122]: https://bugs.ruby-lang.org/issues/16122 [Feature #18630]: https://bugs.ruby-lang.org/issues/18630 [Feature #18589]: https://bugs.ruby-lang.org/issues/18589 +[Feature #19060]: https://bugs.ruby-lang.org/issues/19060 diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h index 37985e12852397..d6a033d32259c3 100644 --- a/include/ruby/fiber/scheduler.h +++ b/include/ruby/fiber/scheduler.h @@ -244,7 +244,27 @@ VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io); VALUE rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io); /** - * Nonblocking read from the passed IO. + * Non-blocking version of `IO.select`. + * + * It's possible that this will be emulated using a thread, so you should not + * rely on it for high performance. + * + * @param[in] scheduler Target scheduler. + * @param[in] readables An array of readable objects. + * @param[in] writables An array of writable objects. + * @param[in] exceptables An array of objects that might encounter exceptional conditions. + * @param[in] timeout Numeric timeout or nil. + * @return What `scheduler.io_select` returns, normally a 3-tuple of arrays of ready objects. + */ +VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout); + +/** + * Non-blocking version of `IO.select`, `argv` variant. + */ +VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv); + +/** + * Non-blocking read from the passed IO. * * @param[in] scheduler Target scheduler. * @param[out] io An io object to read from. diff --git a/io.c b/io.c index 2fe092b84e49a9..a1b3f276111c8c 100644 --- a/io.c +++ b/io.c @@ -10851,6 +10851,15 @@ rb_io_advise(int argc, VALUE *argv, VALUE io) static VALUE rb_f_select(int argc, VALUE *argv, VALUE obj) { + VALUE scheduler = rb_fiber_scheduler_current(); + if (scheduler != Qnil) { + // It's optionally supported. + VALUE result = rb_fiber_scheduler_io_selectv(scheduler, argc, argv); + if (result != Qundef) { + return result; + } + } + VALUE timeout; struct select_args args; struct timeval timerec; diff --git a/scheduler.c b/scheduler.c index 785ad06f198328..09fc921c886bd2 100644 --- a/scheduler.c +++ b/scheduler.c @@ -28,6 +28,7 @@ static ID id_process_wait; static ID id_io_read, id_io_pread; static ID id_io_write, id_io_pwrite; static ID id_io_wait; +static ID id_io_select; static ID id_io_close; static ID id_address_resolve; @@ -51,6 +52,7 @@ Init_Fiber_Scheduler(void) id_io_pwrite = rb_intern_const("io_pwrite"); id_io_wait = rb_intern_const("io_wait"); + id_io_select = rb_intern_const("io_select"); id_io_close = rb_intern_const("io_close"); id_address_resolve = rb_intern_const("address_resolve"); @@ -231,6 +233,25 @@ rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io) return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_WRITABLE), rb_io_timeout(io)); } +VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout) +{ + VALUE arguments[] = { + readables, writables, exceptables, timeout + }; + + return rb_fiber_scheduler_io_selectv(scheduler, 4, arguments); +} + +VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv) +{ + // I wondered about extracting argv, and checking if there is only a single + // IO instance, and instead calling `io_wait`. However, it would require a + // decent amount of work and it would be hard to preserve the exact + // semantics of IO.select. + + return rb_check_funcall(scheduler, id_io_select, argc, argv); +} + VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset) { diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index 322564fe6d5536..3fd41ef6f161c5 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -197,6 +197,13 @@ def io_wait(io, events, duration) @writable.delete(io) end + def io_select(...) + # Emulate the operation using a non-blocking thread: + Thread.new do + IO.select(...) + end.value + end + # Used for Kernel#sleep and Thread::Mutex#sleep def kernel_sleep(duration = nil) # $stderr.puts [__method__, duration, Fiber.current].inspect diff --git a/test/fiber/test_io.rb b/test/fiber/test_io.rb index 4252641cde14ef..821a169e44af72 100644 --- a/test/fiber/test_io.rb +++ b/test/fiber/test_io.rb @@ -172,4 +172,26 @@ def test_read_write_blocking assert_predicate(i, :closed?) assert_predicate(o, :closed?) end + + def test_io_select + omit "UNIXSocket is not defined!" unless defined?(UNIXSocket) + + UNIXSocket.pair do |r, w| + result = nil + + thread = Thread.new do + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + Fiber.schedule do + w.write("Hello World") + result = IO.select([r], [w]) + end + end + + thread.join + + assert_equal [[r], [w], []], result + end + end end From 52fcb3a72446189448e96a465d8c91c469ac0ed2 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 15 Oct 2022 20:04:58 +1300 Subject: [PATCH 053/139] Add missing `#close` call to `test_dup_timeout` test. (#6560) --- test/ruby/test_io.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 989c4c89913a9b..6313e111791977 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -1446,6 +1446,8 @@ def test_dup_timeout r.timeout = 0.1 r2 = r.dup assert_equal(0.1, r2.timeout) + ensure + r2&.close end end From 7fcad1fa03c21b9a9916a12b816ec886a5b68920 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 15 Oct 2022 21:43:45 +1300 Subject: [PATCH 054/139] Update `Fiber::Scheduler` documentation. (#6562) --- cont.c | 340 +-------------------------------- include/ruby/fiber/scheduler.h | 34 ++-- io.c | 4 +- scheduler.c | 310 +++++++++++++++++++++++++++++- 4 files changed, 331 insertions(+), 357 deletions(-) diff --git a/cont.c b/cont.c index 39f0fc8171a4a1..b6d26d716ef8b1 100644 --- a/cont.c +++ b/cont.c @@ -1991,7 +1991,7 @@ rb_fiber_s_schedule_kw(int argc, VALUE* argv, int kw_splat) VALUE fiber = Qnil; if (scheduler != Qnil) { - fiber = rb_funcall_passing_block_kw(scheduler, rb_intern("fiber"), argc, argv, kw_splat); + fiber = rb_fiber_scheduler_fiber(scheduler, argc, argv, kw_splat); } else { rb_raise(rb_eRuntimeError, "No scheduler is available!"); @@ -3000,329 +3000,6 @@ rb_fiber_pool_initialize(int argc, VALUE* argv, VALUE self) * fiber.resume #=> FiberError: dead fiber called */ -/* - * Document-class: Fiber::SchedulerInterface - * - * This is not an existing class, but documentation of the interface that Scheduler - * object should comply to in order to be used as argument to Fiber.scheduler and handle non-blocking - * fibers. See also the "Non-blocking fibers" section in Fiber class docs for explanations - * of some concepts. - * - * Scheduler's behavior and usage are expected to be as follows: - * - * * When the execution in the non-blocking Fiber reaches some blocking operation (like - * sleep, wait for a process, or a non-ready I/O), it calls some of the scheduler's - * hook methods, listed below. - * * Scheduler somehow registers what the current fiber is waiting on, and yields control - * to other fibers with Fiber.yield (so the fiber would be suspended while expecting its - * wait to end, and other fibers in the same thread can perform) - * * At the end of the current thread execution, the scheduler's method #close is called - * * The scheduler runs into a wait loop, checking all the blocked fibers (which it has - * registered on hook calls) and resuming them when the awaited resource is ready - * (e.g. I/O ready or sleep time elapsed). - * - * A typical implementation would probably rely for this closing loop on a gem like - * EventMachine[https://github.com/eventmachine/eventmachine] or - * Async[https://github.com/socketry/async]. - * - * This way concurrent execution will be achieved transparently for every - * individual Fiber's code. - * - * Hook methods are: - * - * * #io_wait, #io_read, and #io_write - * * #process_wait - * * #kernel_sleep - * * #timeout_after - * * #address_resolve - * * #block and #unblock - * * (the list is expanded as Ruby developers make more methods having non-blocking calls) - * - * When not specified otherwise, the hook implementations are mandatory: if they are not - * implemented, the methods trying to call hook will fail. To provide backward compatibility, - * in the future hooks will be optional (if they are not implemented, due to the scheduler - * being created for the older Ruby version, the code which needs this hook will not fail, - * and will just behave in a blocking fashion). - * - * It is also strongly recommended that the scheduler implements the #fiber method, which is - * delegated to by Fiber.schedule. - * - * Sample _toy_ implementation of the scheduler can be found in Ruby's code, in - * test/fiber/scheduler.rb - * - */ - -#if 0 /* for RDoc */ -/* - * - * Document-method: Fiber::SchedulerInterface#close - * - * Called when the current thread exits. The scheduler is expected to implement this - * method in order to allow all waiting fibers to finalize their execution. - * - * The suggested pattern is to implement the main event loop in the #close method. - * - */ -static VALUE -rb_fiber_scheduler_interface_close(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#process_wait - * call-seq: process_wait(pid, flags) - * - * Invoked by Process::Status.wait in order to wait for a specified process. - * See that method description for arguments description. - * - * Suggested minimal implementation: - * - * Thread.new do - * Process::Status.wait(pid, flags) - * end.value - * - * This hook is optional: if it is not present in the current scheduler, - * Process::Status.wait will behave as a blocking method. - * - * Expected to return a Process::Status instance. - */ -static VALUE -rb_fiber_scheduler_interface_process_wait(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#io_wait - * call-seq: io_wait(io, events, timeout) - * - * Invoked by IO#wait, IO#wait_readable, IO#wait_writable to ask whether the - * specified descriptor is ready for specified events within - * the specified +timeout+. - * - * +events+ is a bit mask of IO::READABLE, IO::WRITABLE, and - * IO::PRIORITY. - * - * Suggested implementation should register which Fiber is waiting for which - * resources and immediately calling Fiber.yield to pass control to other - * fibers. Then, in the #close method, the scheduler might dispatch all the - * I/O resources to fibers waiting for it. - * - * Expected to return the subset of events that are ready immediately. - * - */ -static VALUE -rb_fiber_scheduler_interface_io_wait(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#io_read - * call-seq: io_read(io, buffer, length) -> read length or -errno - * - * Invoked by IO#read to read +length+ bytes from +io+ into a specified - * +buffer+ (see IO::Buffer). - * - * The +length+ argument is the "minimum length to be read". - * If the IO buffer size is 8KiB, but the +length+ is +1024+ (1KiB), up to - * 8KiB might be read, but at least 1KiB will be. - * Generally, the only case where less data than +length+ will be read is if - * there is an error reading the data. - * - * Specifying a +length+ of 0 is valid and means try reading at least once - * and return any available data. - * - * Suggested implementation should try to read from +io+ in a non-blocking - * manner and call #io_wait if the +io+ is not ready (which will yield control - * to other fibers). - * - * See IO::Buffer for an interface available to return data. - * - * Expected to return number of bytes read, or, in case of an error, -errno - * (negated number corresponding to system's error code). - * - * The method should be considered _experimental_. - */ -static VALUE -rb_fiber_scheduler_interface_io_read(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#io_write - * call-seq: io_write(io, buffer, length) -> written length or -errno - * - * Invoked by IO#write to write +length+ bytes to +io+ from - * from a specified +buffer+ (see IO::Buffer). - * - * The +length+ argument is the "(minimum) length to be written". - * If the IO buffer size is 8KiB, but the +length+ specified is 1024 (1KiB), - * at most 8KiB will be written, but at least 1KiB will be. - * Generally, the only case where less data than +length+ will be written is if - * there is an error writing the data. - * - * Specifying a +length+ of 0 is valid and means try writing at least once, - * as much data as possible. - * - * Suggested implementation should try to write to +io+ in a non-blocking - * manner and call #io_wait if the +io+ is not ready (which will yield control - * to other fibers). - * - * See IO::Buffer for an interface available to get data from buffer efficiently. - * - * Expected to return number of bytes written, or, in case of an error, -errno - * (negated number corresponding to system's error code). - * - * The method should be considered _experimental_. - */ -static VALUE -rb_fiber_scheduler_interface_io_write(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#kernel_sleep - * call-seq: kernel_sleep(duration = nil) - * - * Invoked by Kernel#sleep and Mutex#sleep and is expected to provide - * an implementation of sleeping in a non-blocking way. Implementation might - * register the current fiber in some list of "which fiber wait until what - * moment", call Fiber.yield to pass control, and then in #close resume - * the fibers whose wait period has elapsed. - * - */ -static VALUE -rb_fiber_scheduler_interface_kernel_sleep(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#address_resolve - * call-seq: address_resolve(hostname) -> array_of_strings or nil - * - * Invoked by any method that performs a non-reverse DNS lookup. The most - * notable method is Addrinfo.getaddrinfo, but there are many other. - * - * The method is expected to return an array of strings corresponding to ip - * addresses the +hostname+ is resolved to, or +nil+ if it can not be resolved. - * - * Fairly exhaustive list of all possible call-sites: - * - * - Addrinfo.getaddrinfo - * - Addrinfo.tcp - * - Addrinfo.udp - * - Addrinfo.ip - * - Addrinfo.new - * - Addrinfo.marshal_load - * - SOCKSSocket.new - * - TCPServer.new - * - TCPSocket.new - * - IPSocket.getaddress - * - TCPSocket.gethostbyname - * - UDPSocket#connect - * - UDPSocket#bind - * - UDPSocket#send - * - Socket.getaddrinfo - * - Socket.gethostbyname - * - Socket.pack_sockaddr_in - * - Socket.sockaddr_in - * - Socket.unpack_sockaddr_in - */ -static VALUE -rb_fiber_scheduler_interface_address_resolve(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#timeout_after - * call-seq: timeout_after(duration, exception_class, *exception_arguments, &block) -> result of block - * - * Invoked by Timeout.timeout to execute the given +block+ within the given - * +duration+. It can also be invoked directly by the scheduler or user code. - * - * Attempt to limit the execution time of a given +block+ to the given - * +duration+ if possible. When a non-blocking operation causes the +block+'s - * execution time to exceed the specified +duration+, that non-blocking - * operation should be interrupted by raising the specified +exception_class+ - * constructed with the given +exception_arguments+. - * - * General execution timeouts are often considered risky. This implementation - * will only interrupt non-blocking operations. This is by design because it's - * expected that non-blocking operations can fail for a variety of - * unpredictable reasons, so applications should already be robust in handling - * these conditions and by implication timeouts. - * - * However, as a result of this design, if the +block+ does not invoke any - * non-blocking operations, it will be impossible to interrupt it. If you - * desire to provide predictable points for timeouts, consider adding - * +sleep(0)+. - * - * If the block is executed successfully, its result will be returned. - * - * The exception will typically be raised using Fiber#raise. - */ -static VALUE -rb_fiber_scheduler_interface_timeout_after(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#block - * call-seq: block(blocker, timeout = nil) - * - * Invoked by methods like Thread.join, and by Mutex, to signify that current - * Fiber is blocked until further notice (e.g. #unblock) or until +timeout+ has - * elapsed. - * - * +blocker+ is what we are waiting on, informational only (for debugging and - * logging). There are no guarantee about its value. - * - * Expected to return boolean, specifying whether the blocking operation was - * successful or not. - */ -static VALUE -rb_fiber_scheduler_interface_block(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#unblock - * call-seq: unblock(blocker, fiber) - * - * Invoked to wake up Fiber previously blocked with #block (for example, Mutex#lock - * calls #block and Mutex#unlock calls #unblock). The scheduler should use - * the +fiber+ parameter to understand which fiber is unblocked. - * - * +blocker+ is what was awaited for, but it is informational only (for debugging - * and logging), and it is not guaranteed to be the same value as the +blocker+ for - * #block. - * - */ -static VALUE -rb_fiber_scheduler_interface_unblock(VALUE self) -{ -} - -/* - * Document-method: SchedulerInterface#fiber - * call-seq: fiber(&block) - * - * Implementation of the Fiber.schedule. The method is expected to immediately - * run the given block of code in a separate non-blocking fiber, and to return that Fiber. - * - * Minimal suggested implementation is: - * - * def fiber(&block) - * fiber = Fiber.new(blocking: false, &block) - * fiber.resume - * fiber - * end - */ -static VALUE -rb_fiber_scheduler_interface_fiber(VALUE self) -{ -} -#endif - void Init_Cont(void) { @@ -3374,21 +3051,6 @@ Init_Cont(void) rb_define_singleton_method(rb_cFiber, "schedule", rb_fiber_s_schedule, -1); -#if 0 /* for RDoc */ - rb_cFiberScheduler = rb_define_class_under(rb_cFiber, "SchedulerInterface", rb_cObject); - rb_define_method(rb_cFiberScheduler, "close", rb_fiber_scheduler_interface_close, 0); - rb_define_method(rb_cFiberScheduler, "process_wait", rb_fiber_scheduler_interface_process_wait, 0); - rb_define_method(rb_cFiberScheduler, "io_wait", rb_fiber_scheduler_interface_io_wait, 0); - rb_define_method(rb_cFiberScheduler, "io_read", rb_fiber_scheduler_interface_io_read, 0); - rb_define_method(rb_cFiberScheduler, "io_write", rb_fiber_scheduler_interface_io_write, 0); - rb_define_method(rb_cFiberScheduler, "kernel_sleep", rb_fiber_scheduler_interface_kernel_sleep, 0); - rb_define_method(rb_cFiberScheduler, "address_resolve", rb_fiber_scheduler_interface_address_resolve, 0); - rb_define_method(rb_cFiberScheduler, "timeout_after", rb_fiber_scheduler_interface_timeout_after, 0); - rb_define_method(rb_cFiberScheduler, "block", rb_fiber_scheduler_interface_block, 0); - rb_define_method(rb_cFiberScheduler, "unblock", rb_fiber_scheduler_interface_unblock, 0); - rb_define_method(rb_cFiberScheduler, "fiber", rb_fiber_scheduler_interface_fiber, 0); -#endif - #ifdef RB_EXPERIMENTAL_FIBER_POOL rb_cFiberPool = rb_define_class_under(rb_cFiber, "Pool", rb_cObject); rb_define_alloc_func(rb_cFiberPool, fiber_pool_alloc); diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h index d6a033d32259c3..250b39b6df5782 100644 --- a/include/ruby/fiber/scheduler.h +++ b/include/ruby/fiber/scheduler.h @@ -146,7 +146,7 @@ VALUE rb_fiber_scheduler_make_timeout(struct timeval *timeout); VALUE rb_fiber_scheduler_close(VALUE scheduler); /** - * Nonblocking `sleep`. Depending on scheduler implementation, this for + * Non-blocking `sleep`. Depending on scheduler implementation, this for * instance switches to another fiber etc. * * @param[in] scheduler Target scheduler. @@ -174,7 +174,7 @@ int rb_fiber_scheduler_supports_process_wait(VALUE scheduler); #endif /** - * Nonblocking `waitpid`. Depending on scheduler implementation, this for + * Non-blocking `waitpid`. Depending on scheduler implementation, this for * instance switches to another fiber etc. * * @param[in] scheduler Target scheduler. @@ -185,7 +185,7 @@ int rb_fiber_scheduler_supports_process_wait(VALUE scheduler); VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags); /** - * Nonblocking wait for the passed "blocker", which is for instance + * Non-blocking wait for the passed "blocker", which is for instance * `Thread.join` or `Mutex.lock`. Depending on scheduler implementation, this * for instance switches to another fiber etc. * @@ -207,8 +207,8 @@ VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout); VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber); /** - * Nonblocking version of rb_io_wait(). Depending on scheduler implementation, - * this for instance switches to another fiber etc. + * Non-blocking version of rb_io_wait(). Depending on scheduler + * implementation, this for instance switches to another fiber etc. * * The "events" here is a Ruby level integer, which is an OR-ed value of * `IO::READABLE`, `IO::WRITABLE`, and `IO::PRIORITY`. @@ -222,7 +222,7 @@ VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber); VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout); /** - * Nonblocking wait until the passed IO is ready for reading. This is a + * Non-blocking wait until the passed IO is ready for reading. This is a * special case of rb_fiber_scheduler_io_wait(), where the interest is * `IO::READABLE` and timeout is never. * @@ -233,7 +233,7 @@ VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io); /** - * Nonblocking wait until the passed IO is ready for writing. This is a + * Non-blocking wait until the passed IO is ready for writing. This is a * special case of rb_fiber_scheduler_io_wait(), where the interest is * `IO::WRITABLE` and timeout is never. * @@ -277,7 +277,7 @@ VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv); VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset); /** - * Nonblocking write to the passed IO. + * Non-blocking write to the passed IO. * * @param[in] scheduler Target scheduler. * @param[out] io An io object to write to. @@ -290,7 +290,7 @@ VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset); /** - * Nonblocking read from the passed IO at the specified offset. + * Non-blocking read from the passed IO at the specified offset. * * @param[in] scheduler Target scheduler. * @param[out] io An io object to read from. @@ -304,7 +304,7 @@ VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_ VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset); /** - * Nonblocking write to the passed IO at the specified offset. + * Non-blocking write to the passed IO at the specified offset. * * @param[in] scheduler Target scheduler. * @param[out] io An io object to write to. @@ -318,7 +318,7 @@ VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALU VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset); /** - * Nonblocking read from the passed IO using a native buffer. + * Non-blocking read from the passed IO using a native buffer. * * @param[in] scheduler Target scheduler. * @param[out] io An io object to read from. @@ -331,7 +331,7 @@ VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VAL VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *buffer, size_t size, size_t length); /** - * Nonblocking write to the passed IO using a native buffer. + * Non-blocking write to the passed IO using a native buffer. * * @param[in] scheduler Target scheduler. * @param[out] io An io object to write to. @@ -344,7 +344,7 @@ VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *buffer, VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *buffer, size_t size, size_t length); /** - * Nonblocking close the given IO. + * Non-blocking close the given IO. * * @param[in] scheduler Target scheduler. * @param[in] io An io object to close. @@ -354,7 +354,7 @@ VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void * VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io); /** - * Nonblocking DNS lookup. + * Non-blocking DNS lookup. * * @param[in] scheduler Target scheduler. * @param[in] hostname A host name to query. @@ -363,6 +363,12 @@ VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io); */ VALUE rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname); +/** + * Create and schedule a non-blocking fiber. + * + */ +VALUE rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat); + RBIMPL_SYMBOL_EXPORT_END() #endif /* RUBY_FIBER_SCHEDULER_H */ diff --git a/io.c b/io.c index a1b3f276111c8c..a7da551a6a0a98 100644 --- a/io.c +++ b/io.c @@ -10855,9 +10855,7 @@ rb_f_select(int argc, VALUE *argv, VALUE obj) if (scheduler != Qnil) { // It's optionally supported. VALUE result = rb_fiber_scheduler_io_selectv(scheduler, argc, argv); - if (result != Qundef) { - return result; - } + if (result != Qundef) return result; } VALUE timeout; diff --git a/scheduler.c b/scheduler.c index 09fc921c886bd2..611182741c664b 100644 --- a/scheduler.c +++ b/scheduler.c @@ -33,6 +33,58 @@ static ID id_io_close; static ID id_address_resolve; +static ID id_fiber_schedule; + +/* + * Document-class: Fiber::Scheduler + * + * This is not an existing class, but documentation of the interface that Scheduler + * object should comply to in order to be used as argument to Fiber.scheduler and handle non-blocking + * fibers. See also the "Non-blocking fibers" section in Fiber class docs for explanations + * of some concepts. + * + * Scheduler's behavior and usage are expected to be as follows: + * + * * When the execution in the non-blocking Fiber reaches some blocking operation (like + * sleep, wait for a process, or a non-ready I/O), it calls some of the scheduler's + * hook methods, listed below. + * * Scheduler somehow registers what the current fiber is waiting on, and yields control + * to other fibers with Fiber.yield (so the fiber would be suspended while expecting its + * wait to end, and other fibers in the same thread can perform) + * * At the end of the current thread execution, the scheduler's method #scheduler_close is called + * * The scheduler runs into a wait loop, checking all the blocked fibers (which it has + * registered on hook calls) and resuming them when the awaited resource is ready + * (e.g. I/O ready or sleep time elapsed). + * + * This way concurrent execution will be achieved transparently for every + * individual Fiber's code. + * + * Scheduler implementations are provided by gems, like + * Async[https://github.com/socketry/async]. + * + * Hook methods are: + * + * * #io_wait, #io_read, #io_write, #io_pread, #io_pwrite, and #io_select, #io_close + * * #process_wait + * * #kernel_sleep + * * #timeout_after + * * #address_resolve + * * #block and #unblock + * * (the list is expanded as Ruby developers make more methods having non-blocking calls) + * + * When not specified otherwise, the hook implementations are mandatory: if they are not + * implemented, the methods trying to call hook will fail. To provide backward compatibility, + * in the future hooks will be optional (if they are not implemented, due to the scheduler + * being created for the older Ruby version, the code which needs this hook will not fail, + * and will just behave in a blocking fashion). + * + * It is also strongly recommended that the scheduler implements the #fiber method, which is + * delegated to by Fiber.schedule. + * + * Sample _toy_ implementation of the scheduler can be found in Ruby's code, in + * test/fiber/scheduler.rb + * + */ void Init_Fiber_Scheduler(void) { @@ -56,6 +108,23 @@ Init_Fiber_Scheduler(void) id_io_close = rb_intern_const("io_close"); id_address_resolve = rb_intern_const("address_resolve"); + + id_fiber_schedule = rb_intern_const("fiber"); + +#if 0 /* for RDoc */ + rb_cFiberScheduler = rb_define_class_under(rb_cFiber, "Scheduler", rb_cObject); + rb_define_method(rb_cFiberScheduler, "close", rb_fiber_scheduler_close, 0); + rb_define_method(rb_cFiberScheduler, "process_wait", rb_fiber_scheduler_process_wait, 2); + rb_define_method(rb_cFiberScheduler, "io_wait", rb_fiber_scheduler_io_wait, 3); + rb_define_method(rb_cFiberScheduler, "io_read", rb_fiber_scheduler_io_read, 4); + rb_define_method(rb_cFiberScheduler, "io_write", rb_fiber_scheduler_io_write, 4); + rb_define_method(rb_cFiberScheduler, "kernel_sleep", rb_fiber_scheduler_kernel_sleep, 1); + rb_define_method(rb_cFiberScheduler, "address_resolve", rb_fiber_scheduler_address_resolve, 1); + rb_define_method(rb_cFiberScheduler, "timeout_after", rb_fiber_scheduler_timeout_after, 3); + rb_define_method(rb_cFiberScheduler, "block", rb_fiber_scheduler_block, 2); + rb_define_method(rb_cFiberScheduler, "unblock", rb_fiber_scheduler_unblock, 2); + rb_define_method(rb_cFiberScheduler, "fiber", rb_fiber_scheduler, -2); +#endif } VALUE @@ -101,7 +170,10 @@ rb_fiber_scheduler_set(VALUE scheduler) verify_interface(scheduler); } - // We invoke Scheduler#close when setting it to something else, to ensure the previous scheduler runs to completion before changing the scheduler. That way, we do not need to consider interactions, e.g., of a Fiber from the previous scheduler with the new scheduler. + // We invoke Scheduler#close when setting it to something else, to ensure + // the previous scheduler runs to completion before changing the scheduler. + // That way, we do not need to consider interactions, e.g., of a Fiber from + // the previous scheduler with the new scheduler. if (thread->scheduler != Qnil) { rb_fiber_scheduler_close(thread->scheduler); } @@ -135,6 +207,16 @@ VALUE rb_fiber_scheduler_current_for_thread(VALUE thread) return rb_fiber_scheduler_current_for_threadptr(rb_thread_ptr(thread)); } +/* + * + * Document-method: Fiber::Scheduler#close + * + * Called when the current thread exits. The scheduler is expected to implement this + * method in order to allow all waiting fibers to finalize their execution. + * + * The suggested pattern is to implement the main event loop in the #close method. + * + */ VALUE rb_fiber_scheduler_close(VALUE scheduler) { @@ -142,6 +224,12 @@ rb_fiber_scheduler_close(VALUE scheduler) VALUE result; + // The reason for calling `scheduler_close` before calling `close` is for + // legacy schedulers which implement `close` and expect the user to call + // it. Subsequently, that method would call `Fiber.set_scheduler(nil)` + // which should call `scheduler_close`. If it were to call `close`, it + // would create an infinite loop. + result = rb_check_funcall(scheduler, id_scheduler_close, 0, NULL); if (result != Qundef) return result; @@ -161,6 +249,17 @@ rb_fiber_scheduler_make_timeout(struct timeval *timeout) return Qnil; } +/* + * Document-method: Fiber::Scheduler#kernel_sleep + * call-seq: kernel_sleep(duration = nil) + * + * Invoked by Kernel#sleep and Mutex#sleep and is expected to provide + * an implementation of sleeping in a non-blocking way. Implementation might + * register the current fiber in some list of "which fiber wait until what + * moment", call Fiber.yield to pass control, and then in #close resume + * the fibers whose wait period has elapsed. + * + */ VALUE rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE timeout) { @@ -174,6 +273,34 @@ rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv) } #if 0 +/* + * Document-method: Fiber::Scheduler#timeout_after + * call-seq: timeout_after(duration, exception_class, *exception_arguments, &block) -> result of block + * + * Invoked by Timeout.timeout to execute the given +block+ within the given + * +duration+. It can also be invoked directly by the scheduler or user code. + * + * Attempt to limit the execution time of a given +block+ to the given + * +duration+ if possible. When a non-blocking operation causes the +block+'s + * execution time to exceed the specified +duration+, that non-blocking + * operation should be interrupted by raising the specified +exception_class+ + * constructed with the given +exception_arguments+. + * + * General execution timeouts are often considered risky. This implementation + * will only interrupt non-blocking operations. This is by design because it's + * expected that non-blocking operations can fail for a variety of + * unpredictable reasons, so applications should already be robust in handling + * these conditions and by implication timeouts. + * + * However, as a result of this design, if the +block+ does not invoke any + * non-blocking operations, it will be impossible to interrupt it. If you + * desire to provide predictable points for timeouts, consider adding + * +sleep(0)+. + * + * If the block is executed successfully, its result will be returned. + * + * The exception will typically be raised using Fiber#raise. + */ VALUE rb_fiber_scheduler_timeout_after(VALUE scheduler, VALUE timeout, VALUE exception, VALUE message) { @@ -191,6 +318,24 @@ rb_fiber_scheduler_timeout_afterv(VALUE scheduler, int argc, VALUE * argv) } #endif +/* + * Document-method: Fiber::Scheduler#process_wait + * call-seq: process_wait(pid, flags) + * + * Invoked by Process::Status.wait in order to wait for a specified process. + * See that method description for arguments description. + * + * Suggested minimal implementation: + * + * Thread.new do + * Process::Status.wait(pid, flags) + * end.value + * + * This hook is optional: if it is not present in the current scheduler, + * Process::Status.wait will behave as a blocking method. + * + * Expected to return a Process::Status instance. + */ VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags) { @@ -201,12 +346,39 @@ rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags) return rb_check_funcall(scheduler, id_process_wait, 2, arguments); } +/* + * Document-method: Fiber::Scheduler#block + * call-seq: block(blocker, timeout = nil) + * + * Invoked by methods like Thread.join, and by Mutex, to signify that current + * Fiber is blocked until further notice (e.g. #unblock) or until +timeout+ has + * elapsed. + * + * +blocker+ is what we are waiting on, informational only (for debugging and + * logging). There are no guarantee about its value. + * + * Expected to return boolean, specifying whether the blocking operation was + * successful or not. + */ VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout) { return rb_funcall(scheduler, id_block, 2, blocker, timeout); } +/* + * Document-method: Fiber::Scheduler#unblock + * call-seq: unblock(blocker, fiber) + * + * Invoked to wake up Fiber previously blocked with #block (for example, Mutex#lock + * calls #block and Mutex#unlock calls #unblock). The scheduler should use + * the +fiber+ parameter to understand which fiber is unblocked. + * + * +blocker+ is what was awaited for, but it is informational only (for debugging + * and logging), and it is not guaranteed to be the same value as the +blocker+ for + * #block. + * + */ VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber) { @@ -215,6 +387,25 @@ rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber) return rb_funcall(scheduler, id_unblock, 2, blocker, fiber); } +/* + * Document-method: Fiber::Scheduler#io_wait + * call-seq: io_wait(io, events, timeout) + * + * Invoked by IO#wait, IO#wait_readable, IO#wait_writable to ask whether the + * specified descriptor is ready for specified events within + * the specified +timeout+. + * + * +events+ is a bit mask of IO::READABLE, IO::WRITABLE, and + * IO::PRIORITY. + * + * Suggested implementation should register which Fiber is waiting for which + * resources and immediately calling Fiber.yield to pass control to other + * fibers. Then, in the #close method, the scheduler might dispatch all the + * I/O resources to fibers waiting for it. + * + * Expected to return the subset of events that are ready immediately. + * + */ VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout) { @@ -233,6 +424,16 @@ rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io) return rb_fiber_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_WRITABLE), rb_io_timeout(io)); } +/* + * Document-method: Fiber::Scheduler#io_select + * call-seq: io_select(readables, writables, exceptables, timeout) + * + * Invoked by IO.select to ask whether the specified descriptors are ready for + * specified events within the specified +timeout+. + * + * Expected to return the 3-tuple of Array of IOs that are ready. + * + */ VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout) { VALUE arguments[] = { @@ -252,6 +453,33 @@ VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv) return rb_check_funcall(scheduler, id_io_select, argc, argv); } +/* + * Document-method: Fiber::Scheduler#io_read + * call-seq: io_read(io, buffer, length) -> read length or -errno + * + * Invoked by IO#read to read +length+ bytes from +io+ into a specified + * +buffer+ (see IO::Buffer). + * + * The +length+ argument is the "minimum length to be read". + * If the IO buffer size is 8KiB, but the +length+ is +1024+ (1KiB), up to + * 8KiB might be read, but at least 1KiB will be. + * Generally, the only case where less data than +length+ will be read is if + * there is an error reading the data. + * + * Specifying a +length+ of 0 is valid and means try reading at least once + * and return any available data. + * + * Suggested implementation should try to read from +io+ in a non-blocking + * manner and call #io_wait if the +io+ is not ready (which will yield control + * to other fibers). + * + * See IO::Buffer for an interface available to return data. + * + * Expected to return number of bytes read, or, in case of an error, -errno + * (negated number corresponding to system's error code). + * + * The method should be considered _experimental_. + */ VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset) { @@ -272,6 +500,33 @@ rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buff return rb_check_funcall(scheduler, id_io_pread, 5, arguments); } +/* + * Document-method: Scheduler#io_write + * call-seq: io_write(io, buffer, length) -> written length or -errno + * + * Invoked by IO#write to write +length+ bytes to +io+ from + * from a specified +buffer+ (see IO::Buffer). + * + * The +length+ argument is the "(minimum) length to be written". + * If the IO buffer size is 8KiB, but the +length+ specified is 1024 (1KiB), + * at most 8KiB will be written, but at least 1KiB will be. + * Generally, the only case where less data than +length+ will be written is if + * there is an error writing the data. + * + * Specifying a +length+ of 0 is valid and means try writing at least once, + * as much data as possible. + * + * Suggested implementation should try to write to +io+ in a non-blocking + * manner and call #io_wait if the +io+ is not ready (which will yield control + * to other fibers). + * + * See IO::Buffer for an interface available to get data from buffer efficiently. + * + * Expected to return number of bytes written, or, in case of an error, -errno + * (negated number corresponding to system's error code). + * + * The method should be considered _experimental_. + */ VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset) { @@ -326,6 +581,38 @@ rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io) return rb_check_funcall(scheduler, id_io_close, 1, arguments); } +/* + * Document-method: Fiber::Scheduler#address_resolve + * call-seq: address_resolve(hostname) -> array_of_strings or nil + * + * Invoked by any method that performs a non-reverse DNS lookup. The most + * notable method is Addrinfo.getaddrinfo, but there are many other. + * + * The method is expected to return an array of strings corresponding to ip + * addresses the +hostname+ is resolved to, or +nil+ if it can not be resolved. + * + * Fairly exhaustive list of all possible call-sites: + * + * - Addrinfo.getaddrinfo + * - Addrinfo.tcp + * - Addrinfo.udp + * - Addrinfo.ip + * - Addrinfo.new + * - Addrinfo.marshal_load + * - SOCKSSocket.new + * - TCPServer.new + * - TCPSocket.new + * - IPSocket.getaddress + * - TCPSocket.gethostbyname + * - UDPSocket#connect + * - UDPSocket#bind + * - UDPSocket#send + * - Socket.getaddrinfo + * - Socket.gethostbyname + * - Socket.pack_sockaddr_in + * - Socket.sockaddr_in + * - Socket.unpack_sockaddr_in + */ VALUE rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname) { @@ -335,3 +622,24 @@ rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname) return rb_check_funcall(scheduler, id_address_resolve, 1, arguments); } + +/* + * Document-method: Fiber::Scheduler#fiber + * call-seq: fiber(&block) + * + * Implementation of the Fiber.schedule. The method is expected to immediately + * run the given block of code in a separate non-blocking fiber, and to return that Fiber. + * + * Minimal suggested implementation is: + * + * def fiber(&block) + * fiber = Fiber.new(blocking: false, &block) + * fiber.resume + * fiber + * end + */ +VALUE +rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat) +{ + return rb_funcall_passing_block_kw(scheduler, id_fiber_schedule, argc, argv, kw_splat); +} From 1e3fa634f7c90bc5eab5bc7280b673333070ca84 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 16 Oct 2022 00:20:58 +0900 Subject: [PATCH 055/139] Explicitly cast to uint32_t and suppress warnings by VC ``` ../src/variable.c(1440): warning C4244: 'initializing': conversion from 'double' to 'uint32_t', possible loss of data 242 ../src/variable.c(1470): warning C4244: 'initializing': conversion from 'double' to 'uint32_t', possible loss of data 243 ``` TODO: check for `newsize` overflow --- variable.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/variable.c b/variable.c index 4b8c87bd1f62bc..8c802082c28fc9 100644 --- a/variable.c +++ b/variable.c @@ -1434,10 +1434,11 @@ rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize) return ivtbl; } +// @note May raise when there are too many instance variables. void rb_init_iv_list(VALUE obj) { - uint32_t newsize = rb_shape_get_shape(obj)->iv_count * 2.0; + uint32_t newsize = (uint32_t)(rb_shape_get_shape(obj)->iv_count * 2.0); uint32_t len = ROBJECT_NUMIV(obj); rb_ensure_iv_list_size(obj, len, newsize < len ? len : newsize); } @@ -1467,7 +1468,7 @@ rb_obj_ensure_iv_index_mapping(VALUE obj, ID id) uint32_t len = ROBJECT_NUMIV(obj); if (len <= index) { - uint32_t newsize = (shape->iv_count + 1) * 1.25; + uint32_t newsize = (uint32_t)((shape->iv_count + 1) * 1.25); rb_ensure_iv_list_size(obj, len, newsize); } RUBY_ASSERT(index <= ROBJECT_NUMIV(obj)); From d56301c00afff7115e01f54d57e464d26b8d1177 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 15 Oct 2022 23:55:25 +0900 Subject: [PATCH 056/139] No longer download zlib source Although zlib package in vcpkg is still 1.2.12, it should be no problem in ruby tests. --- .appveyor.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 5fb678c3c6467f..0d2c5b16074824 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -19,7 +19,6 @@ skip_commits: - '**/.document' environment: ruby_version: "24-%Platform%" - zlib_version: "1.2.13" matrix: - build: vs vs: 120 @@ -69,9 +68,6 @@ for: - mkdir \usr\local\bin - mkdir \usr\local\include - mkdir \usr\local\lib - - SET ZLIB_ZIP=.downloaded-cache\zlib%zlib_version:.=%.zip - - if not exist %ZLIB_ZIP% curl -fsSL -o %ZLIB_ZIP% --retry 10 https://zlib.net/zlib%zlib_version:.=%.zip - - 7z x -aos -o%APPVEYOR_BUILD_FOLDER%\ext\zlib %ZLIB_ZIP% - for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I - for %%I in (c:\Tools\vcpkg\installed\%Platform%-windows\bin\*.dll) do ( if not %%~nI == readline mklink \usr\local\bin\%%~nxI %%I From 8d20632df849833d8a98f3d6f5e61f32eefa0fa6 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 15 Oct 2022 10:53:08 -0500 Subject: [PATCH 057/139] [DOC] Packed data (#6520) New page for packed data --- doc/packed_data.rdoc | 586 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 doc/packed_data.rdoc diff --git a/doc/packed_data.rdoc b/doc/packed_data.rdoc new file mode 100644 index 00000000000000..252fae3c85cbaf --- /dev/null +++ b/doc/packed_data.rdoc @@ -0,0 +1,586 @@ +== Packed Data + +Certain Ruby core methods deal with packing and unpacking data: + +- \Method Array#pack: + Formats each element in array +self+ into a binary string; + returns that string. +- \Method String#unpack: + Extracts data from string +self+, + forming objects that become the elements of a new array; + returns that array. +- \Method String#unpack1: + Does the same, but returns only the first extracted object. + +Each of these methods accepts a string +template+, +consisting of zero or more _directive_ characters, +each followed by zero or more _modifier_ characters. + +Examples (directive 'C' specifies 'unsigned character'): + + [65].pack('C') # => "A" # One element, one directive. + [65, 66].pack('CC') # => "AB" # Two elements, two directives. + [65, 66].pack('C') # => "A" # Extra element is ignored. + [65].pack('') # => "" # No directives. + [65].pack('CC') # Extra directive raises ArgumentError. + + 'A'.unpack('C') # => [65] # One character, one directive. + 'AB'.unpack('CC') # => [65, 66] # Two characters, two directives. + 'AB'.unpack('C') # => [65] # Extra character is ignored. + 'A'.unpack('CC') # => [65, nil] # Extra directive generates nil. + 'AB'.unpack('') # => [] # No directives. + +The string +template+ may contain any mixture of valid directives +(directive 'c' specifies 'signed character'): + + [65, -1].pack('cC') # => "A\xFF" + "A\xFF".unpack('cC') # => [65, 255] + +The string +template+ may contain whitespace (which is ignored) +and comments, each of which begins with character '#' +and continues up to and including the next following newline: + + [0,1].pack(" C #foo \n C ") # => "\x00\x01" + "\0\1".unpack(" C #foo \n C ") # => [0, 1] + +Any directive may be followed by either of these modifiers: + +- '*' - The directive is to be applied as many times as needed: + + [65, 66].pack('C*') # => "AB" + 'AB'.unpack('C*') # => [65, 66] + +- Integer +count+ - The directive is to be applied +count+ times: + + [65, 66].pack('C2') # => "AB" + [65, 66].pack('C3') # Raises ArgumentError. + 'AB'.unpack('C2') # => [65, 66] + 'AB'.unpack('C3') # => [65, 66, nil] + + Note: Directives in %w[A a Z m] use +count+ differently; + see {String Directives}[rdoc-ref:packed_data.rdoc@String+Directives]. + +=== Packing \Method + +\Method Array#pack accepts optional keyword argument ++buffer+ that specifies the target string (instead of a new string): + + [65, 66].pack('C*', buffer: 'foo') # => "fooAB" + +The method can accept a block: + + # Packed string is passed to the block. + [65, 66].pack('C*') {|s| p s } # => "AB" + +=== Unpacking Methods + +Methods String#unpack and String#unpack1 each accept +an optional keyword argument +offset+ that specifies an offset +into the string: + + 'ABC'.unpack('C*', offset: 1) # => [66, 67] + 'ABC'.unpack1('C*', offset: 1) # => 66 + +Both methods can accept a block: + + # Each unpacked object is passed to the block. + ret = [] + "ABCD".unpack("C*") {|c| ret << c } + ret # => [65, 66, 67, 68] + + # The single unpacked object is passed to the block. + 'AB'.unpack1('C*') {|ele| p ele } # => 65 + +=== \Integer Directives + +Each integer directive specifies the packing or unpacking +for one element in the input or output array. + +==== 8-Bit \Integer Directives + +- 'c' - 8-bit signed integer + (like C signed char): + + [0, 1, 255].pack('c*') # => "\x00\x01\xFF" + s = [0, 1, -1].pack('c*') # => "\x00\x01\xFF" + s.unpack('c*') # => [0, 1, -1] + +- 'C' - 8-bit signed integer + (like C unsigned char): + + [0, 1, 255].pack('C*') # => "\x00\x01\xFF" + s = [0, 1, -1].pack('C*') # => "\x00\x01\xFF" + s.unpack('C*') # => [0, 1, 255] + +==== 16-Bit \Integer Directives + +- 's' - 16-bit signed integer, native-endian + (like C int16_t): + + [513, -514].pack('s*') # => "\x01\x02\xFE\xFD" + s = [513, 65022].pack('s*') # => "\x01\x02\xFE\xFD" + s.unpack('s*') # => [513, -514] + +- 'S' - 16-bit unsigned integer, native-endian + (like C uint16_t): + + [513, -514].pack('S*') # => "\x01\x02\xFE\xFD" + s = [513, 65022].pack('S*') # => "\x01\x02\xFE\xFD" + s.unpack('S*') # => [513, 65022] + +- 'n' - 16-bit network integer, big-endian: + + s = [0, 1, -1, 32767, -32768, 65535].pack('n*') + # => "\x00\x00\x00\x01\xFF\xFF\x7F\xFF\x80\x00\xFF\xFF" + s.unpack('n*') + # => [0, 1, 65535, 32767, 32768, 65535] + +- 'v' - 16-bit VAX integer, little-endian: + + s = [0, 1, -1, 32767, -32768, 65535].pack('v*') + # => "\x00\x00\x01\x00\xFF\xFF\xFF\x7F\x00\x80\xFF\xFF" + s.unpack('v*') + # => [0, 1, 65535, 32767, 32768, 65535] + +==== 32-Bit \Integer Directives + +- 'l' - 32-bit signed integer, native-endian + (like C int32_t): + + s = [67305985, -50462977].pack('l*') + # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC" + s.unpack('l*') + # => [67305985, -50462977] + +- 'L' - 32-bit unsigned integer, native-endian + (like C uint32_t): + + s = [67305985, 4244504319].pack('L*') + # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC" + s.unpack('L*') + # => [67305985, 4244504319] + +- 'N' - 32-bit network integer, big-endian: + + s = [0,1,-1].pack('N*') + # => "\x00\x00\x00\x00\x00\x00\x00\x01\xFF\xFF\xFF\xFF" + s.unpack('N*') + # => [0, 1, 4294967295] + +- 'V' - 32-bit VAX integer, little-endian: + + s = [0,1,-1].pack('V*') + # => "\x00\x00\x00\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF" + s.unpack('v*') + # => [0, 0, 1, 0, 65535, 65535] + +==== 64-Bit \Integer Directives + +- 'q' - 64-bit signed integer, native-endian + (like C int64_t): + + s = [578437695752307201, -506097522914230529].pack('q*') + # => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8" + s.unpack('q*') + # => [578437695752307201, -506097522914230529] + +- 'Q' - 64-bit unsigned integer, native-endian + (like C uint64_t): + + s = [578437695752307201, 17940646550795321087].pack('Q*') + # => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8" + s.unpack('Q*') + # => [578437695752307201, 17940646550795321087] + +==== Platform-Dependent \Integer Directives + +- 'i' - Platform-dependent width signed integer, + native-endian (like C int): + + s = [67305985, -50462977].pack('i*') + # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC" + s.unpack('i*') + # => [67305985, -50462977] + +- 'I' - Platform-dependent width unsigned integer, + native-endian (like C unsigned int): + + s = [67305985, -50462977].pack('I*') + # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC" + s.unpack('I*') + # => [67305985, 4244504319] + +==== Pointer Directives + +- 'j' - 64-bit pointer-width signed integer, + native-endian (like C intptr_t): + + s = [67305985, -50462977].pack('j*') + # => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\xFF\xFF\xFF\xFF" + s.unpack('j*') + # => [67305985, -50462977] + +- 'j' - 64-bit pointer-width unsigned integer, + native-endian (like C uintptr_t): + + s = [67305985, 4244504319].pack('J*') + # => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\x00\x00\x00\x00" + s.unpack('J*') + # => [67305985, 4244504319] + +==== Other \Integer Directives +: +- 'U' - UTF-8 character: + + s = [4194304].pack('U*') + # => "\xF8\x90\x80\x80\x80" + s.unpack('U*') + # => [4194304] + +- 'w' - BER-encoded integer + (see {BER enocding}[https://en.wikipedia.org/wiki/X.690#BER_encoding]): + + s = [1073741823].pack('w*') + # => "\x83\xFF\xFF\xFF\x7F" + s.unpack('w*') + # => [1073741823] + +==== Modifiers for \Integer Directives + +For directives in +'i', +'I', +'s', +'S', +'l', +'L', +'q', +'Q', +'j', and +'J', +these modifiers may be suffixed: + +- '!' or '_' - Underlying platform’s native size. +- '>' - Big-endian. +- '<' - Little-endian. + +=== \Float Directives + +Each float directive specifies the packing or unpacking +for one element in the input or output array. + +==== Single-Precision \Float Directives + +- 'F' or 'f' - Native format: + + s = [3.0].pack('F') # => "\x00\x00@@" + s.unpack('F') # => [3.0] + +- 'e' - Little-endian: + + s = [3.0].pack('e') # => "\x00\x00@@" + s.unpack('e') # => [3.0] + +- 'g' - Big-endian: + + s = [3.0].pack('g') # => "@@\x00\x00" + s.unpack('g') # => [3.0] + +==== Double-Precision \Float Directives + +- 'D' or 'd' - Native format: + + s = [3.0].pack('D') # => "\x00\x00\x00\x00\x00\x00\b@" + s.unpack('D') # => [3.0] + +- 'E' - Little-endian: + + s = [3.0].pack('E') # => "\x00\x00\x00\x00\x00\x00\b@" + s.unpack('E') # => [3.0] + +- 'G' - Big-endian: + + s = [3.0].pack('G') # => "@\b\x00\x00\x00\x00\x00\x00" + s.unpack('G') # => [3.0] + +A float directive may be infinity or not-a-number: + + inf = 1.0/0.0 # => Infinity + [inf].pack('f') # => "\x00\x00\x80\x7F" + "\x00\x00\x80\x7F".unpack('f') # => [Infinity] + + nan = inf/inf # => NaN + [nan].pack('f') # => "\x00\x00\xC0\x7F" + "\x00\x00\xC0\x7F".unpack('f') # => [NaN] + +=== \String Directives + +Each string directive specifies the packing or unpacking +for one byte in the input or output string. + +==== Binary \String Directives + +- 'A' - Arbitrary binary string (space padded; count is width); + +nil+ is treated as the empty string: + + ['foo'].pack('A') # => "f" + ['foo'].pack('A*') # => "foo" + ['foo'].pack('A2') # => "fo" + ['foo'].pack('A4') # => "foo " + [nil].pack('A') # => " " + [nil].pack('A*') # => "" + [nil].pack('A2') # => " " + [nil].pack('A4') # => " " + + "foo\0".unpack('A') # => ["f"] + "foo\0".unpack('A4') # => ["foo"] + "foo\0bar".unpack('A10') # => ["foo\x00bar"] # Reads past "\0". + "foo ".unpack('A') # => ["f"] + "foo ".unpack('A4') # => ["foo"] + "foo".unpack('A4') # => ["foo"] + + russian = "\u{442 435 441 442}" # => "тест" + russian.size # => 4 + russian.bytesize # => 8 + [russian].pack('A') # => "\xD1" + [russian].pack('A*') # => "\xD1\x82\xD0\xB5\xD1\x81\xD1\x82" + russian.unpack('A') # => ["\xD1"] + russian.unpack('A2') # => ["\xD1\x82"] + russian.unpack('A4') # => ["\xD1\x82\xD0\xB5"] + russian.unpack('A*') # => ["\xD1\x82\xD0\xB5\xD1\x81\xD1\x82"] + +- 'a' - Arbitrary binary string (null padded; count is width): + + ["foo"].pack('a') # => "f" + ["foo"].pack('a*') # => "foo" + ["foo"].pack('a2') # => "fo" + ["foo\0"].pack('a4') # => "foo\x00" + [nil].pack('a') # => "\x00" + [nil].pack('a*') # => "" + [nil].pack('a2') # => "\x00\x00" + [nil].pack('a4') # => "\x00\x00\x00\x00" + + "foo\0".unpack('a') # => ["f"] + "foo\0".unpack('a4') # => ["foo\x00"] + "foo ".unpack('a4') # => ["foo "] + "foo".unpack('a4') # => ["foo"] + "foo\0bar".unpack('a4') # => ["foo\x00"] # Reads past "\0". + +- 'Z' - Same as 'a', + except that null is added or ignored with '*': + + ["foo"].pack('Z*') # => "foo\x00" + [nil].pack('Z*') # => "\x00" + + "foo\0".unpack('Z*') # => ["foo"] + "foo".unpack('Z*') # => ["foo"] + "foo\0bar".unpack('Z*') # => ["foo"] # Does not read past "\0". + +==== Bit \String Directives + +- 'B' - Bit string (high byte first): + + ['11111111' + '00000000'].pack('B*') # => "\xFF\x00" + ['10000000' + '01000000'].pack('B*') # => "\x80@" + + ['1'].pack('B0') # => "" + ['1'].pack('B1') # => "\x80" + ['1'].pack('B2') # => "\x80\x00" + ['1'].pack('B3') # => "\x80\x00" + ['1'].pack('B4') # => "\x80\x00\x00" + ['1'].pack('B5') # => "\x80\x00\x00" + ['1'].pack('B6') # => "\x80\x00\x00\x00" + + "\xff\x00".unpack("B*") # => ["1111111100000000"] + "\x01\x02".unpack("B*") # => ["0000000100000010"] + + "".unpack("B0") # => [""] + "\x80".unpack("B1") # => ["1"] + "\x80".unpack("B2") # => ["10"] + "\x80".unpack("B3") # => ["100"] + +- 'b' - Bit string (low byte first): + + ['11111111' + '00000000'].pack('b*') # => "\xFF\x00" + ['10000000' + '01000000'].pack('b*') # => "\x01\x02" + + ['1'].pack('b0') # => "" + ['1'].pack('b1') # => "\x01" + ['1'].pack('b2') # => "\x01\x00" + ['1'].pack('b3') # => "\x01\x00" + ['1'].pack('b4') # => "\x01\x00\x00" + ['1'].pack('b5') # => "\x01\x00\x00" + ['1'].pack('b6') # => "\x01\x00\x00\x00" + + "\xff\x00".unpack("b*") # => ["1111111100000000"] + "\x01\x02".unpack("b*") # => ["1000000001000000"] + + "".unpack("b0") # => [""] + "\x01".unpack("b1") # => ["1"] + "\x01".unpack("b2") # => ["10"] + "\x01".unpack("b3") # => ["100"] + +==== Hex \String Directives + +- 'H' - Hex string (high nibble first): + + ['10ef'].pack('H*') # => "\x10\xEF" + ['10ef'].pack('H0') # => "" + ['10ef'].pack('H3') # => "\x10\xE0" + ['10ef'].pack('H5') # => "\x10\xEF\x00" + + ['fff'].pack('H3') # => "\xFF\xF0" + ['fff'].pack('H4') # => "\xFF\xF0" + ['fff'].pack('H5') # => "\xFF\xF0\x00" + ['fff'].pack('H6') # => "\xFF\xF0\x00" + ['fff'].pack('H7') # => "\xFF\xF0\x00\x00" + ['fff'].pack('H8') # => "\xFF\xF0\x00\x00" + + "\x10\xef".unpack('H*') # => ["10ef"] + "\x10\xef".unpack('H0') # => [""] + "\x10\xef".unpack('H1') # => ["1"] + "\x10\xef".unpack('H2') # => ["10"] + "\x10\xef".unpack('H3') # => ["10e"] + "\x10\xef".unpack('H4') # => ["10ef"] + "\x10\xef".unpack('H5') # => ["10ef"] + +- 'h' - Hex string (low nibble first): + + ['10ef'].pack('h*') # => "\x01\xFE" + ['10ef'].pack('h0') # => "" + ['10ef'].pack('h3') # => "\x01\x0E" + ['10ef'].pack('h5') # => "\x01\xFE\x00" + + ['fff'].pack('h3') # => "\xFF\x0F" + ['fff'].pack('h4') # => "\xFF\x0F" + ['fff'].pack('h5') # => "\xFF\x0F\x00" + ['fff'].pack('h6') # => "\xFF\x0F\x00" + ['fff'].pack('h7') # => "\xFF\x0F\x00\x00" + ['fff'].pack('h8') # => "\xFF\x0F\x00\x00" + + "\x01\xfe".unpack('h*') # => ["10ef"] + "\x01\xfe".unpack('h0') # => [""] + "\x01\xfe".unpack('h1') # => ["1"] + "\x01\xfe".unpack('h2') # => ["10"] + "\x01\xfe".unpack('h3') # => ["10e"] + "\x01\xfe".unpack('h4') # => ["10ef"] + "\x01\xfe".unpack('h5') # => ["10ef"] + +==== Pointer \String Directives + +- 'P' - Pointer to a structure (fixed-length string): + + s = ['abc'].pack('P') # => "\xE0O\x7F\xE5\xA1\x01\x00\x00" + s.unpack('P*') # => ["abc"] + ".".unpack("P") # => [] + ("\0" * 8).unpack("P") # => [nil] + [nil].pack("P") # => "\x00\x00\x00\x00\x00\x00\x00\x00" + +- 'p' - Pointer to a null-terminated string: + + s = ['abc'].pack('p') # => "(\xE4u\xE5\xA1\x01\x00\x00" + s.unpack('p*') # => ["abc"] + ".".unpack("p") # => [] + ("\0" * 8).unpack("p") # => [nil] + [nil].pack("p") # => "\x00\x00\x00\x00\x00\x00\x00\x00" + +==== Other \String Directives + +- 'M' - Quoted printable, MIME encoding; + text mode, but input must use LF and output LF; + (see {RFC 2045}[https://www.ietf.org/rfc/rfc2045.txt]): + + ["a b c\td \ne"].pack('M') # => "a b c\td =\n\ne=\n" + ["\0"].pack('M') # => "=00=\n" + + ["a"*1023].pack('M') == ("a"*73+"=\n")*14+"a=\n" # => true + ("a"*73+"=\na=\n").unpack('M') == ["a"*74] # => true + (("a"*73+"=\n")*14+"a=\n").unpack('M') == ["a"*1023] # => true + + "a b c\td =\n\ne=\n".unpack('M') # => ["a b c\td \ne"] + "=00=\n".unpack('M') # => ["\x00"] + + "pre=31=32=33after".unpack('M') # => ["pre123after"] + "pre=\nafter".unpack('M') # => ["preafter"] + "pre=\r\nafter".unpack('M') # => ["preafter"] + "pre=".unpack('M') # => ["pre="] + "pre=\r".unpack('M') # => ["pre=\r"] + "pre=hoge".unpack('M') # => ["pre=hoge"] + "pre==31after".unpack('M') # => ["pre==31after"] + "pre===31after".unpack('M') # => ["pre===31after"] + +- 'm' - Base64 encoded string; + count specifies input bytes between each newline, + rounded down to nearest multiple of 3; + if count is zero, no newlines are added; + (see {RFC 4648}[https://www.ietf.org/rfc/rfc4648.txt]): + + [""].pack('m') # => "" + ["\0"].pack('m') # => "AA==\n" + ["\0\0"].pack('m') # => "AAA=\n" + ["\0\0\0"].pack('m') # => "AAAA\n" + ["\377"].pack('m') # => "/w==\n" + ["\377\377"].pack('m') # => "//8=\n" + ["\377\377\377"].pack('m') # => "////\n" + + "".unpack('m') # => [""] + "AA==\n".unpack('m') # => ["\x00"] + "AAA=\n".unpack('m') # => ["\x00\x00"] + "AAAA\n".unpack('m') # => ["\x00\x00\x00"] + "/w==\n".unpack('m') # => ["\xFF"] + "//8=\n".unpack('m') # => ["\xFF\xFF"] + "////\n".unpack('m') # => ["\xFF\xFF\xFF"] + "A\n".unpack('m') # => [""] + "AA\n".unpack('m') # => ["\x00"] + "AA=\n".unpack('m') # => ["\x00"] + "AAA\n".unpack('m') # => ["\x00\x00"] + + [""].pack('m0') # => "" + ["\0"].pack('m0') # => "AA==" + ["\0\0"].pack('m0') # => "AAA=" + ["\0\0\0"].pack('m0') # => "AAAA" + ["\377"].pack('m0') # => "/w==" + ["\377\377"].pack('m0') # => "//8=" + ["\377\377\377"].pack('m0') # => "////" + + "".unpack('m0') # => [""] + "AA==".unpack('m0') # => ["\x00"] + "AAA=".unpack('m0') # => ["\x00\x00"] + "AAAA".unpack('m0') # => ["\x00\x00\x00"] + "/w==".unpack('m0') # => ["\xFF"] + "//8=".unpack('m0') # => ["\xFF\xFF"] + "////".unpack('m0') # => ["\xFF\xFF\xFF"] + +- 'u' - UU-encoded string: + + [0].pack("U") # => "\u0000" + [0x3fffffff].pack("U") # => "\xFC\xBF\xBF\xBF\xBF\xBF" + [0x40000000].pack("U") # => "\xFD\x80\x80\x80\x80\x80" + [0x7fffffff].pack("U") # => "\xFD\xBF\xBF\xBF\xBF\xBF" + +=== Offset Directives + +- '@' - Begin packing at the given byte offset; + for packing, null fill if necessary: + + [1, 2].pack("C@0C") # => "\x02" + [1, 2].pack("C@1C") # => "\x01\x02" + [1, 2].pack("C@5C") # => "\x01\x00\x00\x00\x00\x02" + + "\x01\x00\x00\x02".unpack("C@3C") # => [1, 2] + "\x00".unpack("@1C") # => [nil] + +- 'X' - Back up a byte: + + [0, 1, 2].pack("CCXC") # => "\x00\x02" + [0, 1, 2].pack("CCX2C") # => "\x02" + "\x00\x02".unpack("CCXC") # => [0, 2, 2] + +=== Null Byte Direcive + +- 'x' - Null byte: + + [].pack("x0") # => "" + [].pack("x") # => "\x00" + [].pack("x8") # => "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x02".unpack("CxC") # => [0, 2] From f0654b1027d2f24cbb6e3cfb0c5946b70f06739b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 15 Oct 2022 09:37:44 -0700 Subject: [PATCH 058/139] More precisely iterate over Object instance variables Shapes provides us with an (almost) exact count of instance variables. We only need to check for Qundef when an IV has been "undefined" Prefer to use ROBJECT_IV_COUNT when iterating IVs --- gc.c | 4 ++-- ractor.c | 4 ++-- shape.h | 9 +++++++++ variable.c | 35 ++++++++++++----------------------- vm_insnhelper.c | 6 ++++-- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/gc.c b/gc.c index 52d5609c3ea895..7373bcfd67fafd 100644 --- a/gc.c +++ b/gc.c @@ -7270,7 +7270,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) { const VALUE * const ptr = ROBJECT_IVPTR(obj); - uint32_t i, len = ROBJECT_NUMIV(obj); + uint32_t i, len = ROBJECT_IV_COUNT(obj); for (i = 0; i < len; i++) { gc_mark(objspace, ptr[i]); } @@ -10019,7 +10019,7 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v) } #endif - for (uint32_t i = 0; i < numiv; i++) { + for (uint32_t i = 0; i < ROBJECT_IV_COUNT(v); i++) { UPDATE_IF_MOVED(objspace, ptr[i]); } } diff --git a/ractor.c b/ractor.c index 3bd6c04af00b35..7fea312ab0e31b 100644 --- a/ractor.c +++ b/ractor.c @@ -2312,7 +2312,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data) case T_OBJECT: { - uint32_t len = ROBJECT_NUMIV(obj); + uint32_t len = ROBJECT_IV_COUNT(obj); VALUE *ptr = ROBJECT_IVPTR(obj); for (uint32_t i=0; imove) rb_obj_transient_heap_evacuate(obj, TRUE); #endif - uint32_t len = ROBJECT_NUMIV(obj); + uint32_t len = ROBJECT_IV_COUNT(obj); VALUE *ptr = ROBJECT_IVPTR(obj); for (uint32_t i=0; iiv_count; + RUBY_ASSERT(ivc <= ROBJECT_NUMIV(obj)); + return ivc; +} + rb_shape_t * rb_shape_alloc(ID edge_name, rb_shape_t * parent); rb_shape_t * rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id); diff --git a/variable.c b/variable.c index 8c802082c28fc9..8d329d7900e23d 100644 --- a/variable.c +++ b/variable.c @@ -1443,43 +1443,32 @@ rb_init_iv_list(VALUE obj) rb_ensure_iv_list_size(obj, len, newsize < len ? len : newsize); } -// Return the instance variable index for a given name and T_OBJECT object. The -// mapping between name and index lives on `rb_obj_class(obj)` and is created -// if not already present. -// -// @note May raise when there are too many instance variables. -// @note YJIT uses this function at compile time to simplify the work needed to -// access the variable at runtime. -static uint32_t -rb_obj_ensure_iv_index_mapping(VALUE obj, ID id) +static VALUE +obj_ivar_set(VALUE obj, ID id, VALUE val) { - RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); attr_index_t index; - // Ensure there is a transition for IVAR +id+ - rb_shape_transition_shape(obj, id, rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))); - // Get the current shape rb_shape_t * shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); if (!rb_shape_get_iv_index(shape, id, &index)) { - rb_bug("unreachable. Shape was not found for id: %s", rb_id2name(id)); + shape = rb_shape_get_next(shape, obj, id); + index = shape->iv_count - 1; } uint32_t len = ROBJECT_NUMIV(obj); + + // Reallocating can kick off GC. We can't set the new shape + // on this object until the buffer has been allocated, otherwise + // GC could read off the end of the buffer. if (len <= index) { - uint32_t newsize = (uint32_t)((shape->iv_count + 1) * 1.25); + uint32_t newsize = (uint32_t)((len + 1) * 1.25); rb_ensure_iv_list_size(obj, len, newsize); } - RUBY_ASSERT(index <= ROBJECT_NUMIV(obj)); - return index; -} -static VALUE -obj_ivar_set(VALUE obj, ID id, VALUE val) -{ - attr_index_t index = rb_obj_ensure_iv_index_mapping(obj, id); RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], val); + rb_shape_set_shape(obj, shape); + return val; } @@ -1768,7 +1757,7 @@ rb_ivar_count(VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: if (rb_shape_get_shape(obj)->iv_count > 0) { - st_index_t i, count, num = ROBJECT_NUMIV(obj); + st_index_t i, count, num = ROBJECT_IV_COUNT(obj); const VALUE *const ivptr = ROBJECT_IVPTR(obj); for (i = count = 0; i < num; ++i) { if (ivptr[i] != Qundef) { diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 3f1337c36c3614..8b19c6c10dfe2c 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1268,8 +1268,7 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, if (shape != next_shape) { RUBY_ASSERT(next_shape->parent_id == rb_shape_id(shape)); - rb_shape_set_shape(obj, next_shape); - next_shape_id = ROBJECT_SHAPE_ID(obj); + next_shape_id = rb_shape_id(next_shape); } if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree @@ -1289,6 +1288,9 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, rb_init_iv_list(obj); } + if (shape != next_shape) { + rb_shape_set_shape(obj, next_shape); + } VALUE *ptr = ROBJECT_IVPTR(obj); RB_OBJ_WRITE(obj, &ptr[index], val); RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); From 15488fc1c6f66d2ebb7410c48d76e9588921db63 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 16 Oct 2022 18:04:15 +0900 Subject: [PATCH 059/139] Update bundled gems list at 2022-10-16 --- NEWS.md | 1 + gems/bundled_gems | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 730462d85daf23..0d0308c0a0debc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -234,6 +234,7 @@ Note: We're only listing outstanding class updates. * timeout 0.3.0 * The following bundled gems are updated. * minitest 5.16.3 + * power_assert 2.0.2 * test-unit 3.5.5 * net-ftp 0.2.0 * net-imap 0.3.1 diff --git a/gems/bundled_gems b/gems/bundled_gems index 3b8ca4791db205..18c7eb8b1fce2a 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -1,6 +1,6 @@ # gem-name version-to-bundle repository-url [optional-commit-hash-to-test-or-defaults-to-v-version] minitest 5.16.3 https://github.com/seattlerb/minitest -power_assert 2.0.1 https://github.com/ruby/power_assert +power_assert 2.0.2 https://github.com/ruby/power_assert rake 13.0.6 https://github.com/ruby/rake test-unit 3.5.5 https://github.com/test-unit/test-unit rexml 3.2.5 https://github.com/ruby/rexml From ba9c0d0b9fe7ce61fa1162011ced8dbe3e9716c7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 16 Oct 2022 18:07:41 +0900 Subject: [PATCH 060/139] Allow run bundled_gems manually [ci skip] --- .github/workflows/bundled_gems.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml index f6f8b9a45b14cf..471f32b25f018d 100644 --- a/.github/workflows/bundled_gems.yml +++ b/.github/workflows/bundled_gems.yml @@ -11,6 +11,7 @@ on: - 'gems/bundled_gems' schedule: - cron: '45 6 * * *' + workflow_dispatch: jobs: update: From e4191ac1ec7940b37bfeaf09c000cf46d2db5200 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Sun, 16 Oct 2022 21:40:42 +0900 Subject: [PATCH 061/139] Change a tab to spaces --- test/coverage/test_coverage.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb index 211faa0cbc215e..1a21235d0a983d 100644 --- a/test/coverage/test_coverage.rb +++ b/test/coverage/test_coverage.rb @@ -207,19 +207,19 @@ def test_coverage_optimized_branch def test_coverage_ensure_if_return result = { :branches => { - [:if, 0, 3, 1, 6, 4] => { - [:then, 1, 3, 6, 3, 6] => 0, - [:else, 2, 5, 3, 5, 9] => 1, - }, + [:if, 0, 3, 2, 6, 5] => { + [:then, 1, 3, 7, 3, 7] => 0, + [:else, 2, 5, 4, 5, 10] => 1, + }, }, } assert_coverage(<<~"end;", { branches: true }, result) def flush ensure - if $! - else - return - end + if $! + else + return + end end flush end; From 7cf37a57221c085aaf29e260060ea0e709af1e93 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 17 Oct 2022 12:00:39 +0900 Subject: [PATCH 062/139] [DOC] Add the polar form in String#to_c --- complex.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/complex.c b/complex.c index 07e5914d54ca26..18d61b206f29a1 100644 --- a/complex.c +++ b/complex.c @@ -2026,6 +2026,12 @@ string_to_c_strict(VALUE self, int raise) * '1/2+3/4i'.to_c #=> ((1/2)+(3/4)*i) * 'ruby'.to_c #=> (0+0i) * + * Polar form: + * include Math + * "1.0@0".to_c #=> (1+0.0i) + * "1.0@#{PI/2}".to_c #=> (0.0+1i) + * "1.0@#{PI}".to_c #=> (-1+0.0i) + * * See Kernel.Complex. */ static VALUE From 0d7292ec734d49def118959d6a80588e7c0f7a39 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 16 Oct 2022 22:02:47 -0700 Subject: [PATCH 063/139] Leave only the oldest Visual Studio in AppVeyor (#6561) --- .appveyor.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 0d2c5b16074824..9d3cb3f6137bb5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,13 +20,11 @@ skip_commits: environment: ruby_version: "24-%Platform%" matrix: + # Test only the oldest supported version because AppVeyor is unstable, its concurrency + # is limited, and compatibility issues that happen only in newer versions are rare. + # You may test some other stuff on GitHub Actions instead. - build: vs vs: 120 - ssl: OpenSSL - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - GEMS_FOR_TEST: "" - - build: vs - vs: 140 ssl: OpenSSL-v111 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 GEMS_FOR_TEST: "" From fb6a9656a114cc9246b657cb4fa63313ef266fe0 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 16 Oct 2022 22:44:15 -0700 Subject: [PATCH 064/139] Do not run CodeQL on dependabot PRs --- .github/workflows/codeql-analysis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f9fa0a74490162..d812517c9f79b9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,8 @@ jobs: # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-latest - if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} + # CodeQL fails to run pull requests from dependabot due to missing write access to upload results. + if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') && github.event.head_commit.pusher.name != 'dependabot[bot]' }} env: enable_install_doc: no From bfc6c1f1cb7a2dd381688999d635e3532f35bfef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 16 Oct 2022 22:45:02 -0700 Subject: [PATCH 065/139] Bump necojackarc/auto-request-review from 0.7.0 to 0.8.0 (#6571) Bumps [necojackarc/auto-request-review](https://github.com/necojackarc/auto-request-review) from 0.7.0 to 0.8.0. - [Release notes](https://github.com/necojackarc/auto-request-review/releases) - [Commits](https://github.com/necojackarc/auto-request-review/compare/e08cdffa277d50854744de3f76230260e61c67f4...b5e81876454003a4ccb9b89cb205c67d77d7035b) --- updated-dependencies: - dependency-name: necojackarc/auto-request-review dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/auto_request_review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml index 7e163de6978893..d499a84d5a6b1e 100644 --- a/.github/workflows/auto_request_review.yml +++ b/.github/workflows/auto_request_review.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Request review based on files changes and/or groups the author belongs to - uses: necojackarc/auto-request-review@e08cdffa277d50854744de3f76230260e61c67f4 # v0.7.0, checking sha + uses: necojackarc/auto-request-review@b5e81876454003a4ccb9b89cb205c67d77d7035b # v0.7.0, checking sha with: # scope: public_repo token: ${{ secrets.MATZBOT_GITHUB_TOKEN }} From b7de04d161726fbb277eaa95fb0d658dbb6a9536 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 16 Oct 2022 22:47:49 -0700 Subject: [PATCH 066/139] Disable dependabot for auto-request-review for now because you have to manually update the version tag comment. It feels unsafe to trust third party git tags when you need to pass MATZBOT_GITHUB_TOKEN to it. Git commit sha alone isn't human-readable and I'm reluctant to remove the comment either. It doesn't seem worth the effort to review changes for every release of this action. --- .github/dependabot.yml | 3 +++ .github/workflows/auto_request_review.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b18fd293573676..97adcabffe111f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,6 @@ updates: directory: '/' schedule: interval: 'weekly' + ignore: + # It doesn't update the version comment for us + - dependency-name: 'necojackarc/auto-request-review' diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml index d499a84d5a6b1e..8275927fd337a2 100644 --- a/.github/workflows/auto_request_review.yml +++ b/.github/workflows/auto_request_review.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Request review based on files changes and/or groups the author belongs to - uses: necojackarc/auto-request-review@b5e81876454003a4ccb9b89cb205c67d77d7035b # v0.7.0, checking sha + uses: necojackarc/auto-request-review@b5e81876454003a4ccb9b89cb205c67d77d7035b # v0.8.0, checking sha with: # scope: public_repo token: ${{ secrets.MATZBOT_GITHUB_TOKEN }} From 63234edf6791e787f8ea4d439df431dcc2f0f527 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Mon, 17 Oct 2022 16:17:27 +0900 Subject: [PATCH 067/139] openssl: use the old rb_ary_tmp_new() alias openssl has to support older versions of Ruby. Undo the change in ext/openssl/ossl_pkey_ec.c by commit efb91ff19b73 ("Rename rb_ary_tmp_new to rb_ary_hidden_new", 2022-07-25). --- ext/openssl/ossl_pkey_ec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index 08972e92a135cb..dee215447dacd5 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -1462,7 +1462,7 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) "use #mul(bn) form instead"); num = RARRAY_LEN(arg1); - bns_tmp = rb_ary_hidden_new(num); + bns_tmp = rb_ary_tmp_new(num); bignums = ALLOCV_N(const BIGNUM *, tmp_b, num); for (i = 0; i < num; i++) { VALUE item = RARRAY_AREF(arg1, i); From a211b32180b1ab0532f6978c32e907fe1c0c218a Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Sun, 26 Jun 2022 21:34:42 -0400 Subject: [PATCH 068/139] [ruby/openssl] Check for OpenSSL functions in headers While building with a custom build of OpenSSL, I noticed in mkmf.log that all the feature detection checks are done using a program lacking an OpenSSL header include. `mkmf` retries using a fallback program when this fails, but that means all the `have_func` calls compile twice when compiling once should suffice. Example log without this commit: have_func: checking for X509_STORE_CTX_get0_cert()... -------------------- yes DYLD_FALLBACK_LIBRARY_PATH=.:../.. "clang -o conftest ... conftest.c:14:57: error: use of undeclared identifier 'X509_STORE_CTX_get0_cert' int t(void) { void ((*volatile p)()); p = (void ((*)()))X509_STORE_CTX_get0_cert; return !p; } ^ 1 error generated. checked program was: /* begin */ 1: #include "ruby.h" 2: 3: /*top*/ 4: extern int t(void); 5: int main(int argc, char **argv) 6: { 7: if (argc > 1000000) { 8: int (* volatile tp)(void)=(int (*)(void))&t; 9: printf("%d", (*tp)()); 10: } 11: 12: return !!argv[argc]; 13: } 14: int t(void) { void ((*volatile p)()); p = (void ((*)()))X509_STORE_CTX_get0_cert; return !p; } /* end */ DYLD_FALLBACK_LIBRARY_PATH=.:../.. "clang -o conftest ... checked program was: /* begin */ 1: #include "ruby.h" 2: 3: /*top*/ 4: extern int t(void); 5: int main(int argc, char **argv) 6: { 7: if (argc > 1000000) { 8: int (* volatile tp)(void)=(int (*)(void))&t; 9: printf("%d", (*tp)()); 10: } 11: 12: return !!argv[argc]; 13: } 14: extern void X509_STORE_CTX_get0_cert(); 15: int t(void) { X509_STORE_CTX_get0_cert(); return 0; } /* end */ The second compilation succeeds. Specify the header for each checked function. https://github.com/ruby/openssl/commit/34ae7d92d0 --- ext/openssl/extconf.rb | 97 ++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index cc2b1f8ba2383b..4a9ae5598d4b36 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -120,8 +120,13 @@ def find_openssl_library end Logging::message "=== Checking for OpenSSL features... ===\n" +evp_h = "openssl/evp.h".freeze +x509_h = "openssl/x509.h".freeze +ts_h = "openssl/ts.h".freeze +ssl_h = "openssl/ssl.h".freeze + # compile options -have_func("RAND_egd") +have_func("RAND_egd", "openssl/rand.h") engines = %w{dynamic 4758cca aep atalla chil cswift nuron sureware ubsec padlock capi gmp gost cryptodev} engines.each { |name| @@ -132,56 +137,56 @@ def find_openssl_library if !have_struct_member("SSL", "ctx", "openssl/ssl.h") || is_libressl $defs.push("-DHAVE_OPAQUE_OPENSSL") end -have_func("EVP_MD_CTX_new") -have_func("EVP_MD_CTX_free") -have_func("EVP_MD_CTX_pkey_ctx") -have_func("X509_STORE_get_ex_data") -have_func("X509_STORE_set_ex_data") -have_func("X509_STORE_get_ex_new_index") -have_func("X509_CRL_get0_signature") -have_func("X509_REQ_get0_signature") -have_func("X509_REVOKED_get0_serialNumber") -have_func("X509_REVOKED_get0_revocationDate") -have_func("X509_get0_tbs_sigalg") -have_func("X509_STORE_CTX_get0_untrusted") -have_func("X509_STORE_CTX_get0_cert") -have_func("X509_STORE_CTX_get0_chain") -have_func("OCSP_SINGLERESP_get0_id") -have_func("SSL_CTX_get_ciphers") -have_func("X509_up_ref") -have_func("X509_CRL_up_ref") -have_func("X509_STORE_up_ref") -have_func("SSL_SESSION_up_ref") -have_func("EVP_PKEY_up_ref") -have_func("SSL_CTX_set_min_proto_version(NULL, 0)", "openssl/ssl.h") -have_func("SSL_CTX_get_security_level") -have_func("X509_get0_notBefore") -have_func("SSL_SESSION_get_protocol_version") -have_func("TS_STATUS_INFO_get0_status") -have_func("TS_STATUS_INFO_get0_text") -have_func("TS_STATUS_INFO_get0_failure_info") -have_func("TS_VERIFY_CTS_set_certs(NULL, NULL)", "openssl/ts.h") -have_func("TS_VERIFY_CTX_set_store") -have_func("TS_VERIFY_CTX_add_flags") -have_func("TS_RESP_CTX_set_time_cb") -have_func("EVP_PBE_scrypt") -have_func("SSL_CTX_set_post_handshake_auth") +have_func("EVP_MD_CTX_new", evp_h) +have_func("EVP_MD_CTX_free", evp_h) +have_func("EVP_MD_CTX_pkey_ctx", evp_h) +have_func("X509_STORE_get_ex_data", x509_h) +have_func("X509_STORE_set_ex_data", x509_h) +have_func("X509_STORE_get_ex_new_index", x509_h) +have_func("X509_CRL_get0_signature", x509_h) +have_func("X509_REQ_get0_signature", x509_h) +have_func("X509_REVOKED_get0_serialNumber", x509_h) +have_func("X509_REVOKED_get0_revocationDate", x509_h) +have_func("X509_get0_tbs_sigalg", x509_h) +have_func("X509_STORE_CTX_get0_untrusted", x509_h) +have_func("X509_STORE_CTX_get0_cert", x509_h) +have_func("X509_STORE_CTX_get0_chain", x509_h) +have_func("OCSP_SINGLERESP_get0_id", "openssl/ocsp.h") +have_func("SSL_CTX_get_ciphers", ssl_h) +have_func("X509_up_ref", x509_h) +have_func("X509_CRL_up_ref", x509_h) +have_func("X509_STORE_up_ref", x509_h) +have_func("SSL_SESSION_up_ref", ssl_h) +have_func("EVP_PKEY_up_ref", evp_h) +have_func("SSL_CTX_set_min_proto_version(NULL, 0)", ssl_h) +have_func("SSL_CTX_get_security_level", ssl_h) +have_func("X509_get0_notBefore", x509_h) +have_func("SSL_SESSION_get_protocol_version", ssl_h) +have_func("TS_STATUS_INFO_get0_status", ts_h) +have_func("TS_STATUS_INFO_get0_text", ts_h) +have_func("TS_STATUS_INFO_get0_failure_info", ts_h) +have_func("TS_VERIFY_CTS_set_certs(NULL, NULL)", ts_h) +have_func("TS_VERIFY_CTX_set_store", ts_h) +have_func("TS_VERIFY_CTX_add_flags", ts_h) +have_func("TS_RESP_CTX_set_time_cb", ts_h) +have_func("EVP_PBE_scrypt", evp_h) +have_func("SSL_CTX_set_post_handshake_auth", ssl_h) # added in 1.1.1 -have_func("EVP_PKEY_check") -have_func("SSL_CTX_set_ciphersuites") +have_func("EVP_PKEY_check", evp_h) +have_func("SSL_CTX_set_ciphersuites", ssl_h) # added in 3.0.0 openssl_3 = -have_func("SSL_set0_tmp_dh_pkey") -have_func("ERR_get_error_all") -have_func("TS_VERIFY_CTX_set_certs(NULL, NULL)", "openssl/ts.h") -have_func("SSL_CTX_load_verify_file") -have_func("BN_check_prime") -have_func("EVP_MD_CTX_get0_md") -have_func("EVP_MD_CTX_get_pkey_ctx") -have_func("EVP_PKEY_eq") -have_func("EVP_PKEY_dup") +have_func("SSL_set0_tmp_dh_pkey", ssl_h) +have_func("ERR_get_error_all", "openssl/err.h") +have_func("TS_VERIFY_CTX_set_certs(NULL, NULL)", ts_h) +have_func("SSL_CTX_load_verify_file", ssl_h) +have_func("BN_check_prime", "openssl/bn.h") +have_func("EVP_MD_CTX_get0_md", evp_h) +have_func("EVP_MD_CTX_get_pkey_ctx", evp_h) +have_func("EVP_PKEY_eq", evp_h) +have_func("EVP_PKEY_dup", evp_h) Logging::message "=== Checking done. ===\n" From 5dae78b9d36be7e2adfb5f9cad78bb10d641e40a Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Mon, 11 Jul 2022 16:55:39 -0400 Subject: [PATCH 069/139] [ruby/openssl] Pass arguments to check macro presence X509_STORE_get_ex_new_index() is a macro, so passing just its name to have_func() doesn't detect it. Pass an example call instead. https://github.com/ruby/openssl/commit/8d264d3e60 Co-authored-by: Nobuyoshi Nakada --- ext/openssl/extconf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 4a9ae5598d4b36..f8be3e9e039058 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -142,7 +142,7 @@ def find_openssl_library have_func("EVP_MD_CTX_pkey_ctx", evp_h) have_func("X509_STORE_get_ex_data", x509_h) have_func("X509_STORE_set_ex_data", x509_h) -have_func("X509_STORE_get_ex_new_index", x509_h) +have_func("X509_STORE_get_ex_new_index(0, NULL, NULL, NULL, NULL)", x509_h) have_func("X509_CRL_get0_signature", x509_h) have_func("X509_REQ_get0_signature", x509_h) have_func("X509_REVOKED_get0_serialNumber", x509_h) From c865e8d161f40c6e9cce411c92e313a91b14124e Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Mon, 25 Jul 2022 21:43:47 -0500 Subject: [PATCH 070/139] [ruby/openssl] [CI] TestHMAC#test_dup - remove 'pend' for OpenSSL 3 https://github.com/ruby/openssl/commit/626b0434a6 --- test/openssl/test_hmac.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/openssl/test_hmac.rb b/test/openssl/test_hmac.rb index 47cb3718dff547..2f53a813e191ec 100644 --- a/test/openssl/test_hmac.rb +++ b/test/openssl/test_hmac.rb @@ -21,7 +21,6 @@ def test_hmac end def test_dup - pend "HMAC#initialize_copy is currently broken on OpenSSL 3.0.0" if openssl?(3, 0, 0) h1 = OpenSSL::HMAC.new("KEY", "MD5") h1.update("DATA") h = h1.dup From a98096349ec7280edabf3822d2c6932ac6e63634 Mon Sep 17 00:00:00 2001 From: Nobuhiro IMAI Date: Fri, 5 Aug 2022 18:42:06 +0900 Subject: [PATCH 071/139] [ruby/openssl] Check if the option is an Hash in `pkey_ctx_apply_options0()` causes SEGV if it is an Array or something like that. https://github.com/ruby/openssl/commit/ef23525210 --- ext/openssl/ossl_pkey.c | 1 + test/openssl/test_pkey_rsa.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 24d0da46833256..0dafa6dc71b646 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -200,6 +200,7 @@ static VALUE pkey_ctx_apply_options0(VALUE args_v) { VALUE *args = (VALUE *)args_v; + Check_Type(args[1], T_HASH); rb_block_call(args[1], rb_intern("each"), 0, NULL, pkey_ctx_apply_options_i, args[0]); diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb index 4bb39ed4a6411a..fa84b76f4b1c6d 100644 --- a/test/openssl/test_pkey_rsa.rb +++ b/test/openssl/test_pkey_rsa.rb @@ -108,6 +108,11 @@ def test_sign_verify_options salt_length: 20, mgf1_hash: "SHA1") # Defaults to PKCS #1 v1.5 padding => verification failure assert_equal false, key.verify("SHA256", sig_pss, data) + + # option type check + assert_raise_with_message(TypeError, /expected Hash/) { + key.sign("SHA256", data, ["x"]) + } end def test_sign_verify_raw From 79543b9a530d85f0487583d96ad412f5e7683ffa Mon Sep 17 00:00:00 2001 From: madblobfish <-> Date: Wed, 3 Aug 2022 23:24:28 +0200 Subject: [PATCH 072/139] [ruby/openssl] ssl: enable generating keying material from SSL sessions Add OpenSSL::SSL::SSLSocket#export_keying_material to support RFC 5705 https://github.com/ruby/openssl/commit/65530b887e --- ext/openssl/ossl_ssl.c | 44 ++++++++++++++++++++++++++++++++++++++++ test/openssl/test_ssl.rb | 13 ++++++++++++ 2 files changed, 57 insertions(+) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 319ba5840e67ac..593a6aa7580711 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -2433,6 +2433,49 @@ ossl_ssl_alpn_protocol(VALUE self) return rb_str_new((const char *) out, outlen); } +/* + * call-seq: + * session.export_keying_material(label, length) -> String + * + * Enables use of shared session key material in accordance with RFC 5705. + */ +static VALUE +ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self) +{ + SSL *ssl; + VALUE str; + VALUE label; + VALUE length; + VALUE context; + unsigned char *p; + size_t len; + int use_ctx = 0; + unsigned char *ctx; + size_t ctx_len = 0; + int ret; + + rb_scan_args(argc, argv, "21", &label, &length, &context); + StringValue(label); + + GetSSL(self, ssl); + + len = (size_t)NUM2LONG(length); + str = rb_str_new(0, len); + p = (unsigned char *)RSTRING_PTR(str); + if (!NIL_P(context)) { + use_ctx = 1; + StringValue(context); + ctx = (unsigned char *)RSTRING_PTR(context); + ctx_len = RSTRING_LEN(context); + } + ret = SSL_export_keying_material(ssl, p, len, (char *)RSTRING_PTR(label), + RSTRING_LENINT(label), ctx, ctx_len, use_ctx); + if (ret == 0 || ret == -1) { + ossl_raise(eSSLError, "SSL_export_keying_material"); + } + return str; +} + /* * call-seq: * ssl.tmp_key => PKey or nil @@ -2860,6 +2903,7 @@ Init_ossl_ssl(void) rb_define_method(cSSLSocket, "peer_finished_message", ossl_ssl_get_peer_finished, 0); rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0); rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0); + rb_define_method(cSSLSocket, "export_keying_material", ossl_ssl_export_keying_material, -1); # ifndef OPENSSL_NO_NEXTPROTONEG rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0); # endif diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 5679ae77d781d3..9f5a27ea9fcdb5 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -1817,6 +1817,19 @@ def test_fileno sock2.close end + def test_export_keying_material + start_server do |port| + cli_ctx = OpenSSL::SSL::SSLContext.new + server_connect(port, cli_ctx) do |ssl| + assert_instance_of(String, ssl.export_keying_material('ttls keying material', 64)) + assert_operator(64, :==, ssl.export_keying_material('ttls keying material', 64).b.length) + assert_operator(8, :==, ssl.export_keying_material('ttls keying material', 8).b.length) + assert_operator(5, :==, ssl.export_keying_material('test', 5, 'context').b.length) + ssl.puts "abc"; ssl.gets # workaround to make tests work on windows + end + end + end + private def start_server_version(version, ctx_proc = nil, From bee383d9fe84eb29ec12a8c392fcbf7c646575b8 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 2 Sep 2022 13:55:19 +0900 Subject: [PATCH 073/139] [ruby/openssl] x509*: fix error queue leak in #extensions= and #attributes= methods X509at_delete_attr() in OpenSSL master puts an error queue entry if there is no attribute left to delete. We must either clear the error queue, or try not to call it when the list is already empty. https://github.com/ruby/openssl/commit/a0c878481f --- ext/openssl/ossl_x509cert.c | 6 +++--- ext/openssl/ossl_x509crl.c | 6 +++--- ext/openssl/ossl_x509req.c | 6 +++--- ext/openssl/ossl_x509revoked.c | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index 996f184170b446..9443541645fbe4 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -642,12 +642,12 @@ ossl_x509_set_extensions(VALUE self, VALUE ary) OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext); } GetX509(self, x509); - while ((ext = X509_delete_ext(x509, 0))) - X509_EXTENSION_free(ext); + for (i = X509_get_ext_count(x509); i > 0; i--) + X509_EXTENSION_free(X509_delete_ext(x509, 0)); for (i=0; i 0; i--) + X509_EXTENSION_free(X509_CRL_delete_ext(crl, 0)); for (i=0; i 0; i--) + X509_ATTRIBUTE_free(X509_REQ_delete_attr(req, 0)); for (i=0;i 0; i--) + X509_EXTENSION_free(X509_REVOKED_delete_ext(rev, 0)); for (i=0; i Date: Thu, 1 Sep 2022 15:59:52 +0900 Subject: [PATCH 074/139] [ruby/openssl] hmac: use EVP_PKEY_new_raw_private_key() if available Current OpenSSL 3.0.x release has a regression with zero-length MAC keys. While this issue should be fixed in a future release of OpenSSL, we can use EVP_PKEY_new_raw_private_key() in place of the problematic EVP_PKEY_new_mac_key() to avoid the issue. OpenSSL 3.0's man page recommends using it regardless: > EVP_PKEY_new_mac_key() works in the same way as > EVP_PKEY_new_raw_private_key(). New applications should use > EVP_PKEY_new_raw_private_key() instead. Fixes https://github.com/ruby/openssl/issues/369#issuecomment-1224912710 https://github.com/ruby/openssl/commit/4293f18b1f --- ext/openssl/extconf.rb | 1 + ext/openssl/ossl_hmac.c | 8 ++++++++ test/openssl/test_hmac.rb | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index f8be3e9e039058..fd96533569454a 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -174,6 +174,7 @@ def find_openssl_library # added in 1.1.1 have_func("EVP_PKEY_check", evp_h) +have_func("EVP_PKEY_new_raw_private_key", evp_h) have_func("SSL_CTX_set_ciphersuites", ssl_h) # added in 3.0.0 diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index bfe3a74b126b00..1a5f471a27d3f3 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -97,11 +97,19 @@ ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest) GetHMAC(self, ctx); StringValue(key); +#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, + (unsigned char *)RSTRING_PTR(key), + RSTRING_LENINT(key)); + if (!pkey) + ossl_raise(eHMACError, "EVP_PKEY_new_raw_private_key"); +#else pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, (unsigned char *)RSTRING_PTR(key), RSTRING_LENINT(key)); if (!pkey) ossl_raise(eHMACError, "EVP_PKEY_new_mac_key"); +#endif if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest), NULL, pkey) != 1) { EVP_PKEY_free(pkey); diff --git a/test/openssl/test_hmac.rb b/test/openssl/test_hmac.rb index 2f53a813e191ec..3cb707448a5f37 100644 --- a/test/openssl/test_hmac.rb +++ b/test/openssl/test_hmac.rb @@ -62,6 +62,14 @@ def test_singleton_methods b64digest = OpenSSL::HMAC.base64digest("MD5", key, "Hi There") assert_equal "kpRyejY4uxwT9I74FYv8nQ==", b64digest end + + def test_zero_length_key + # Empty string as the key + hexdigest = OpenSSL::HMAC.hexdigest("SHA256", "\0"*32, "test") + assert_equal "43b0cef99265f9e34c10ea9d3501926d27b39f57c6d674561d8ba236e7a819fb", hexdigest + hexdigest = OpenSSL::HMAC.hexdigest("SHA256", "", "test") + assert_equal "43b0cef99265f9e34c10ea9d3501926d27b39f57c6d674561d8ba236e7a819fb", hexdigest + end end end From 10f93a8bd787658996f08b13a0e564eaf3f41489 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Tue, 4 Jan 2022 22:11:00 +0900 Subject: [PATCH 075/139] [ruby/openssl] pkey/dsa: let PKey::DSA.generate choose appropriate q size DSA parameters generation via EVP_PKEY_paramgen() will not automatically adjust the size of q value but uses 224 bits by default unless specified explicitly. This behavior is different from the now-deprecated DSA_generate_parameters_ex(), which PKey::DSA.generate used to call. Fixes https://github.com/ruby/openssl/issues/483 Fixes: https://github.com/ruby/openssl/commit/1800a8d5ebaf ("pkey/dsa: use high level EVP interface to generate parameters and keys", 2020-05-17) https://github.com/ruby/openssl/commit/0105975a0b --- ext/openssl/lib/openssl/pkey.rb | 8 ++++++++ test/openssl/test_pkey_dsa.rb | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb index c3e06290910de1..d51f066b8938ad 100644 --- a/ext/openssl/lib/openssl/pkey.rb +++ b/ext/openssl/lib/openssl/pkey.rb @@ -167,8 +167,16 @@ class << self # +size+:: # The desired key size in bits. def generate(size, &blk) + # FIPS 186-4 specifies four (L,N) pairs: (1024,160), (2048,224), + # (2048,256), and (3072,256). + # + # q size is derived here with compatibility with + # DSA_generator_parameters_ex() which previous versions of ruby/openssl + # used to call. + qsize = size >= 2048 ? 256 : 160 dsaparams = OpenSSL::PKey.generate_parameters("DSA", { "dsa_paramgen_bits" => size, + "dsa_paramgen_q_bits" => qsize, }, &blk) OpenSSL::PKey.generate_key(dsaparams) end diff --git a/test/openssl/test_pkey_dsa.rb b/test/openssl/test_pkey_dsa.rb index 726b7dbf7e6114..de6aa63e23b2c7 100644 --- a/test/openssl/test_pkey_dsa.rb +++ b/test/openssl/test_pkey_dsa.rb @@ -28,6 +28,25 @@ def test_new_break end end + def test_generate + # DSA.generate used to call DSA_generate_parameters_ex(), which adjusts the + # size of q according to the size of p + key1024 = OpenSSL::PKey::DSA.generate(1024) + assert_predicate key1024, :private? + assert_equal 1024, key1024.p.num_bits + assert_equal 160, key1024.q.num_bits + + key2048 = OpenSSL::PKey::DSA.generate(2048) + assert_equal 2048, key2048.p.num_bits + assert_equal 256, key2048.q.num_bits + + if ENV["OSSL_TEST_ALL"] == "1" # slow + key3072 = OpenSSL::PKey::DSA.generate(3072) + assert_equal 3072, key3072.p.num_bits + assert_equal 256, key3072.q.num_bits + end + end + def test_sign_verify dsa512 = Fixtures.pkey("dsa512") data = "Sign me!" From 4fb2845c7b71d94f01a224020e4eb91c99f99d66 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 2 Sep 2022 18:14:57 +0900 Subject: [PATCH 076/139] [ruby/openssl] pkey: clear error queue before each OSSL_DECODER_from_bio() call Fix potential error queue leak. https://github.com/ruby/openssl/commit/3992b6f208 --- ext/openssl/ossl_pkey.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 0dafa6dc71b646..0effb3e96a1e08 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -99,17 +99,20 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass) /* First check DER */ if (OSSL_DECODER_from_bio(dctx, bio) == 1) goto out; + OSSL_BIO_reset(bio); /* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */ - OSSL_BIO_reset(bio); if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1) goto out; - while (OSSL_DECODER_from_bio(dctx, bio) != 1) { - if (BIO_eof(bio)) + while (1) { + if (OSSL_DECODER_from_bio(dctx, bio) == 1) goto out; + if (BIO_eof(bio)) + break; pos2 = BIO_tell(bio); if (pos2 < 0 || pos2 <= pos) - goto out; + break; + ossl_clear_error(); pos = pos2; } From 0677b2fb87fa4bdff64e650e5df0fd7bf684bd2e Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 2 Sep 2022 22:40:54 +0900 Subject: [PATCH 077/139] [ruby/openssl] pkey: restore support for decoding "openssl ecparam -genkey" output Scan through the input for a private key, then fallback to generic decoder. OpenSSL 3.0's OSSL_DECODER supports encoded key parameters. The PEM header "-----BEGIN EC PARAMETERS-----" is used by one of such encoding formats. While this is useful for OpenSSL::PKey::PKey, an edge case has been discovered. The openssl CLI command line "openssl ecparam -genkey" prints two PEM blocks in a row, one for EC parameters and another for the private key. Feeding the whole output into OSSL_DECODER results in only the first PEM block, the key parameters, being decoded. Previously, ruby/openssl did not support decoding key parameters and it would decode the private key PEM block instead. While the new behavior is technically correct, "openssl ecparam -genkey" is so widely used that ruby/openssl does not want to break existing applications. Fixes https://github.com/ruby/openssl/pull/535 https://github.com/ruby/openssl/commit/d486c82833 --- ext/openssl/ossl_pkey.c | 36 ++++++++++++++++++++++++++++++++++++ test/openssl/test_pkey_ec.rb | 23 +++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 0effb3e96a1e08..ec39e8bd771093 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -104,6 +104,42 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass) /* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */ if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1) goto out; + /* + * First check for private key formats. This is to keep compatibility with + * ruby/openssl < 3.0 which decoded the following as a private key. + * + * $ openssl ecparam -name prime256v1 -genkey -outform PEM + * -----BEGIN EC PARAMETERS----- + * BggqhkjOPQMBBw== + * -----END EC PARAMETERS----- + * -----BEGIN EC PRIVATE KEY----- + * MHcCAQEEIAG8ugBbA5MHkqnZ9ujQF93OyUfL9tk8sxqM5Wv5tKg5oAoGCCqGSM49 + * AwEHoUQDQgAEVcjhJfkwqh5C7kGuhAf8XaAjVuG5ADwb5ayg/cJijCgs+GcXeedj + * 86avKpGH84DXUlB23C/kPt+6fXYlitUmXQ== + * -----END EC PRIVATE KEY----- + * + * While the first PEM block is a proper encoding of ECParameters, thus + * OSSL_DECODER_from_bio() would pick it up, ruby/openssl used to return + * the latter instead. Existing applications expect this behavior. + * + * Note that normally, the input is supposed to contain a single decodable + * PEM block only, so this special handling should not create a new problem. + */ + OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR); + while (1) { + if (OSSL_DECODER_from_bio(dctx, bio) == 1) + goto out; + if (BIO_eof(bio)) + break; + pos2 = BIO_tell(bio); + if (pos2 < 0 || pos2 <= pos) + break; + ossl_clear_error(); + pos = pos2; + } + + OSSL_BIO_reset(bio); + OSSL_DECODER_CTX_set_selection(dctx, 0); while (1) { if (OSSL_DECODER_from_bio(dctx, bio) == 1) goto out; diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb index ffe5a94e5b1b2a..23c6c4d42157b1 100644 --- a/test/openssl/test_pkey_ec.rb +++ b/test/openssl/test_pkey_ec.rb @@ -199,6 +199,29 @@ def test_ECPrivateKey assert_equal pem, p256.export end + def test_ECPrivateKey_with_parameters + p256 = Fixtures.pkey("p256") + + # The format used by "openssl ecparam -name prime256v1 -genkey -outform PEM" + # + # "EC PARAMETERS" block should be ignored if it is followed by an + # "EC PRIVATE KEY" block + in_pem = <<~EOF + -----BEGIN EC PARAMETERS----- + BggqhkjOPQMBBw== + -----END EC PARAMETERS----- + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49 + AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt + CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg== + -----END EC PRIVATE KEY----- + EOF + + key = OpenSSL::PKey::EC.new(in_pem) + assert_same_ec p256, key + assert_equal p256.to_der, key.to_der + end + def test_ECPrivateKey_encrypted p256 = Fixtures.pkey("p256") # key = abcdef From b69d41e1c433f9109b7c9237cde6ed8b4884cc06 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 2 Sep 2022 23:05:28 +0900 Subject: [PATCH 078/139] [ruby/openssl] pkey/ec: check existence of public key component before exporting i2d_PUBKEY_bio() against an EC_KEY without the public key component trggers a null dereference. This is a regression introduced by commit https://github.com/ruby/openssl/commit/56f0d34d63fb ("pkey: refactor #export/#to_pem and #to_der", 2017-06-14). Fixes https://github.com/ruby/openssl/pull/527#issuecomment-1220504524 Fixes https://github.com/ruby/openssl/issues/369#issuecomment-1221554057 https://github.com/ruby/openssl/commit/f6ee0fa4de --- ext/openssl/ossl_pkey_ec.c | 4 ++++ test/openssl/test_pkey_ec.rb | 2 ++ 2 files changed, 6 insertions(+) diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index dee215447dacd5..06d59c2a4f69d1 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -414,6 +414,8 @@ ossl_ec_key_export(int argc, VALUE *argv, VALUE self) EC_KEY *ec; GetEC(self, ec); + if (EC_KEY_get0_public_key(ec) == NULL) + ossl_raise(eECError, "can't export - no public key set"); if (EC_KEY_get0_private_key(ec)) return ossl_pkey_export_traditional(argc, argv, self, 0); else @@ -432,6 +434,8 @@ ossl_ec_key_to_der(VALUE self) EC_KEY *ec; GetEC(self, ec); + if (EC_KEY_get0_public_key(ec) == NULL) + ossl_raise(eECError, "can't export - no public key set"); if (EC_KEY_get0_private_key(ec)) return ossl_pkey_export_traditional(0, NULL, self, 1); else diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb index 23c6c4d42157b1..9a4818de8ede49 100644 --- a/test/openssl/test_pkey_ec.rb +++ b/test/openssl/test_pkey_ec.rb @@ -61,8 +61,10 @@ def test_generate def test_generate_key ec = OpenSSL::PKey::EC.new("prime256v1") assert_equal false, ec.private? + assert_raise(OpenSSL::PKey::ECError) { ec.to_der } ec.generate_key! assert_equal true, ec.private? + assert_nothing_raised { ec.to_der } end if !openssl?(3, 0, 0) def test_marshal From 33d30a8675e926e7ad07ff2b135da0c819af33ca Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 8 Sep 2022 17:58:27 +0900 Subject: [PATCH 079/139] [ruby/openssl] Ruby/OpenSSL 2.1.4 https://github.com/ruby/openssl/commit/5316241e61 --- ext/openssl/History.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ext/openssl/History.md b/ext/openssl/History.md index 479ec3b4a25b11..7e5622961287e4 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -194,6 +194,16 @@ Notable changes [[GitHub #297]](https://github.com/ruby/openssl/pull/297) +Version 2.1.4 +============= + +Bug fixes +--------- + +* Do not use pkg-config if --with-openssl-dir option is specified. + [[GitHub #486]](https://github.com/ruby/openssl/pull/486) + + Version 2.1.3 ============= From 15a966a674a0c6813131057ee1540f021071ad3c Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 8 Sep 2022 18:07:20 +0900 Subject: [PATCH 080/139] [ruby/openssl] Ruby/OpenSSL 2.2.2 https://github.com/ruby/openssl/commit/de8a644bc4 --- ext/openssl/History.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/openssl/History.md b/ext/openssl/History.md index 7e5622961287e4..f54e0984e3e3e3 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -100,6 +100,12 @@ Notable changes [[GitHub #342]](https://github.com/ruby/openssl/issues/342) +Version 2.2.2 +============= + +Merged changes in 2.1.4. + + Version 2.2.1 ============= From cd83f5b961dfb5401d601c3b08ff3db9f02a5793 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 8 Sep 2022 18:34:26 +0900 Subject: [PATCH 081/139] [ruby/openssl] Ruby/OpenSSL 3.0.1 https://github.com/ruby/openssl/commit/e5bbd015dc --- ext/openssl/History.md | 24 ++++++++++++++++++++++++ ext/openssl/lib/openssl/version.rb | 2 +- ext/openssl/openssl.gemspec | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ext/openssl/History.md b/ext/openssl/History.md index f54e0984e3e3e3..a4f6bd7fd6123f 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -1,3 +1,27 @@ +Version 3.0.1 +============= + +Merged changes in 2.1.4 and 2.2.2. Additionally, the following issues are fixed +by this release. + +Bug fixes +--------- + +* Add missing type check in OpenSSL::PKey::PKey#sign's optional parameters. + [[GitHub #531]](https://github.com/ruby/openssl/pull/531) +* Work around OpenSSL 3.0's HMAC issues with a zero-length key. + [[GitHub #538]](https://github.com/ruby/openssl/pull/538) +* Fix a regression in OpenSSL::PKey::DSA.generate's default of 'q' size. + [[GitHub #483]](https://github.com/ruby/openssl/issues/483) + [[GitHub #539]](https://github.com/ruby/openssl/pull/539) +* Restore OpenSSL::PKey.read's ability to decode "openssl ecparam -genkey" + output when linked against OpenSSL 3.0. + [[GitHub #535]](https://github.com/ruby/openssl/pull/535) + [[GitHub #540]](https://github.com/ruby/openssl/pull/540) +* Restore error checks in OpenSSL::PKey::EC#{to_der,to_pem}. + [[GitHub #541]](https://github.com/ruby/openssl/pull/541) + + Version 3.0.0 ============= diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb index 5e60604353ef85..b9e8444d4d1c1d 100644 --- a/ext/openssl/lib/openssl/version.rb +++ b/ext/openssl/lib/openssl/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module OpenSSL - VERSION = "3.0.0" + VERSION = "3.0.1" end diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index c6cd818336908a..1c13505b974ac5 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "3.0.0" + spec.version = "3.0.1" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.} From 04bf83d6f7c9999ea82e472e09a42286026194f9 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 9 Sep 2022 00:28:55 +0900 Subject: [PATCH 082/139] [ruby/openssl] bump version number to 3.1.0.pre https://github.com/ruby/openssl/commit/fceb978a5d --- ext/openssl/lib/openssl/version.rb | 2 +- ext/openssl/openssl.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb index b9e8444d4d1c1d..80597c074366bd 100644 --- a/ext/openssl/lib/openssl/version.rb +++ b/ext/openssl/lib/openssl/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module OpenSSL - VERSION = "3.0.1" + VERSION = "3.1.0.pre" end diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index 1c13505b974ac5..d3d8be05db2acb 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "3.0.1" + spec.version = "3.1.0.pre" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.} From e4b1627983ca27afcae9a2c502915a747a9e1656 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 9 Sep 2022 16:03:42 +0900 Subject: [PATCH 083/139] =?UTF-8?q?[ruby/openssl]=20ssl:=20fix=20"warning:?= =?UTF-8?q?=20=E2=80=98ctx=E2=80=99=20may=20be=20used=20uninitialized"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code was introduced by https://github.com/ruby/openssl/commit/65530b887e54 ("ssl: enable generating keying material from SSL sessions", 2022-08-03). This is harmless, but we should avoid it. https://github.com/ruby/openssl/commit/f5b82e814b --- ext/openssl/ossl_ssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 593a6aa7580711..aa4c9470285213 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -2450,7 +2450,7 @@ ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self) unsigned char *p; size_t len; int use_ctx = 0; - unsigned char *ctx; + unsigned char *ctx = NULL; size_t ctx_len = 0; int ret; From 17998ad3bb5864db38ba9e709ed7209da6189f0f Mon Sep 17 00:00:00 2001 From: Christophe De La Fuente Date: Mon, 29 Aug 2022 20:15:54 +0200 Subject: [PATCH 084/139] [ruby/openssl] Add support to SSL_CTX_set_keylog_callback - This callback is invoked when TLS key material is generated or received, in order to allow applications to store this keying material for debugging purposes. - It is invoked with an `SSLSocket` and a string containing the key material in the format used by NSS for its SSLKEYLOGFILE debugging output. - This commit adds the Ruby binding `keylog_cb` and the related tests - It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements `SSL_CTX_set_keylog_callback()` from v3.4.2, it does nothing (see https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6) https://github.com/ruby/openssl/commit/3b63232cf1 --- ext/openssl/ossl_ssl.c | 86 +++++++++++++++++++++++++++++++++++++++- test/openssl/test_ssl.rb | 48 ++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index aa4c9470285213..a202109b7af18a 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -49,7 +49,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb, id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols, id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, - id_i_verify_hostname; + id_i_verify_hostname, id_i_keylog_cb; static ID id_i_io, id_i_context, id_i_hostname; static int ossl_ssl_ex_vcb_idx; @@ -441,6 +441,54 @@ ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess) return 0; } +#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER) +/* + * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements + * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see + * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6). + */ + +struct ossl_call_keylog_cb_args { + VALUE ssl_obj; + const char * line; +}; + +static VALUE +ossl_call_keylog_cb(VALUE args_v) +{ + VALUE sslctx_obj, cb, line_v; + struct ossl_call_keylog_cb_args *args = (struct ossl_call_keylog_cb_args *) args_v; + + sslctx_obj = rb_attr_get(args->ssl_obj, id_i_context); + + cb = rb_attr_get(sslctx_obj, id_i_keylog_cb); + if (NIL_P(cb)) return Qnil; + + line_v = rb_str_new_cstr(args->line); + + return rb_funcall(cb, id_call, 2, args->ssl_obj, line_v); +} + +static void +ossl_sslctx_keylog_cb(const SSL *ssl, const char *line) +{ + VALUE ssl_obj; + struct ossl_call_keylog_cb_args args; + int state = 0; + + OSSL_Debug("SSL keylog callback entered"); + + ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); + args.ssl_obj = ssl_obj; + args.line = line; + + rb_protect(ossl_call_keylog_cb, (VALUE)&args, &state); + if (state) { + rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state)); + } +} +#endif + static VALUE ossl_call_session_remove_cb(VALUE ary) { @@ -911,6 +959,18 @@ ossl_sslctx_setup(VALUE self) OSSL_Debug("SSL TLSEXT servername callback added"); } +#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER) + /* + * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements + * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see + * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6). + */ + if (RTEST(rb_attr_get(self, id_i_keylog_cb))) { + SSL_CTX_set_keylog_callback(ctx, ossl_sslctx_keylog_cb); + OSSL_Debug("SSL keylog callback added"); + } +#endif + return Qtrue; } @@ -2783,6 +2843,29 @@ Init_ossl_ssl(void) */ rb_attr(cSSLContext, rb_intern_const("alpn_select_cb"), 1, 1, Qfalse); + /* + * A callback invoked when TLS key material is generated or received, in + * order to allow applications to store this keying material for debugging + * purposes. + * + * The callback is invoked with an SSLSocket and a string containing the + * key material in the format used by NSS for its SSLKEYLOGFILE debugging + * output. + * + * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements + * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see + * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6). + * + * === Example + * + * context.keylog_cb = proc do |_sock, line| + * File.open('ssl_keylog_file', "a") do |f| + * f.write("#{line}\n") + * end + * end + */ + rb_attr(cSSLContext, rb_intern_const("keylog_cb"), 1, 1, Qfalse); + rb_define_alias(cSSLContext, "ssl_timeout", "timeout"); rb_define_alias(cSSLContext, "ssl_timeout=", "timeout="); rb_define_private_method(cSSLContext, "set_minmax_proto_version", @@ -3064,6 +3147,7 @@ Init_ossl_ssl(void) DefIVarID(alpn_select_cb); DefIVarID(servername_cb); DefIVarID(verify_hostname); + DefIVarID(keylog_cb); DefIVarID(io); DefIVarID(context); diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 9f5a27ea9fcdb5..945cc7c48e7cc1 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -804,6 +804,54 @@ def socketpair end end + def test_keylog_cb + pend "Keylog callback is not supported" if !openssl?(1, 1, 1) || libressl? + + prefix = 'CLIENT_RANDOM' + context = OpenSSL::SSL::SSLContext.new + context.min_version = context.max_version = OpenSSL::SSL::TLS1_2_VERSION + + cb_called = false + context.keylog_cb = proc do |_sock, line| + cb_called = true + assert_equal(prefix, line.split.first) + end + + start_server do |port| + server_connect(port, context) do |ssl| + ssl.puts "abc" + assert_equal("abc\n", ssl.gets) + assert_equal(true, cb_called) + end + end + + if tls13_supported? + prefixes = [ + 'SERVER_HANDSHAKE_TRAFFIC_SECRET', + 'EXPORTER_SECRET', + 'SERVER_TRAFFIC_SECRET_0', + 'CLIENT_HANDSHAKE_TRAFFIC_SECRET', + 'CLIENT_TRAFFIC_SECRET_0', + ] + context = OpenSSL::SSL::SSLContext.new + context.min_version = context.max_version = OpenSSL::SSL::TLS1_3_VERSION + cb_called = false + context.keylog_cb = proc do |_sock, line| + cb_called = true + assert_not_nil(prefixes.delete(line.split.first)) + end + + start_server do |port| + server_connect(port, context) do |ssl| + ssl.puts "abc" + assert_equal("abc\n", ssl.gets) + assert_equal(true, cb_called) + end + assert_equal(0, prefixes.size) + end + end + end + def test_tlsext_hostname fooctx = OpenSSL::SSL::SSLContext.new fooctx.cert = @cli_cert From aecc470a3354bbf61e5cd2e65f4aeb4f3825a9be Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 7 Oct 2022 23:05:17 +1300 Subject: [PATCH 085/139] [ruby/openssl] Use default `IO#timeout` if possible. https://github.com/ruby/openssl/commit/471340f612 --- ext/openssl/ossl_ssl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index a202109b7af18a..478ff869a43cbe 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1701,15 +1701,16 @@ no_exception_p(VALUE opts) return 0; } -#ifndef RB_IO_TIMEOUT_DEFAULT -#define RB_IO_TIMEOUT_DEFAULT Qnil +// Provided by Ruby 3.2.0 and later in order to support the default IO#timeout. +#ifndef RUBY_IO_TIMEOUT_DEFAULT +#define RUBY_IO_TIMEOUT_DEFAULT Qnil #endif static void io_wait_writable(rb_io_t *fptr) { #ifdef HAVE_RB_IO_MAYBE_WAIT - rb_io_maybe_wait_writable(errno, fptr->self, RB_IO_TIMEOUT_DEFAULT); + rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT); #else rb_io_wait_writable(fptr->fd); #endif @@ -1719,7 +1720,7 @@ static void io_wait_readable(rb_io_t *fptr) { #ifdef HAVE_RB_IO_MAYBE_WAIT - rb_io_maybe_wait_readable(errno, fptr->self, RB_IO_TIMEOUT_DEFAULT); + rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT); #else rb_io_wait_readable(fptr->fd); #endif From 6166fa612cf798930863c7cca23dc96f46ab1c37 Mon Sep 17 00:00:00 2001 From: Bart de Water <496367+bdewater@users.noreply.github.com> Date: Sat, 8 Oct 2022 07:53:57 -0400 Subject: [PATCH 086/139] [ruby/openssl] Call out insecure PKCS #1 v1.5 default padding for RSA https://github.com/ruby/openssl/commit/fd5eaa6dfc --- ext/openssl/lib/openssl/pkey.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb index d51f066b8938ad..0414658a106134 100644 --- a/ext/openssl/lib/openssl/pkey.rb +++ b/ext/openssl/lib/openssl/pkey.rb @@ -363,7 +363,8 @@ def new(*args, &blk) # :nodoc: # rsa.private_encrypt(string, padding) -> String # # Encrypt +string+ with the private key. +padding+ defaults to - # PKCS1_PADDING. The encrypted string output can be decrypted using + # PKCS1_PADDING, which is known to be insecure but is kept for backwards + # compatibility. The encrypted string output can be decrypted using # #public_decrypt. # # Deprecated in version 3.0. @@ -386,7 +387,8 @@ def private_encrypt(string, padding = PKCS1_PADDING) # rsa.public_decrypt(string, padding) -> String # # Decrypt +string+, which has been encrypted with the private key, with the - # public key. +padding+ defaults to PKCS1_PADDING. + # public key. +padding+ defaults to PKCS1_PADDING which is known to be + # insecure but is kept for backwards compatibility. # # Deprecated in version 3.0. # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and @@ -407,7 +409,8 @@ def public_decrypt(string, padding = PKCS1_PADDING) # rsa.public_encrypt(string, padding) -> String # # Encrypt +string+ with the public key. +padding+ defaults to - # PKCS1_PADDING. The encrypted string output can be decrypted using + # PKCS1_PADDING, which is known to be insecure but is kept for backwards + # compatibility. The encrypted string output can be decrypted using # #private_decrypt. # # Deprecated in version 3.0. @@ -428,7 +431,8 @@ def public_encrypt(data, padding = PKCS1_PADDING) # rsa.private_decrypt(string, padding) -> String # # Decrypt +string+, which has been encrypted with the public key, with the - # private key. +padding+ defaults to PKCS1_PADDING. + # private key. +padding+ defaults to PKCS1_PADDING, which is known to be + # insecure but is kept for backwards compatibility. # # Deprecated in version 3.0. # Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead. From 85cee293570e10f11170d54c7fae7ac681a52193 Mon Sep 17 00:00:00 2001 From: git Date: Mon, 17 Oct 2022 07:43:51 +0000 Subject: [PATCH 087/139] Update default gems list at 6166fa612cf798930863c7cca23dc9 [ci skip] --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 0d0308c0a0debc..81506f93b77078 100644 --- a/NEWS.md +++ b/NEWS.md @@ -224,6 +224,7 @@ Note: We're only listing outstanding class updates. * logger 1.5.1 * net-http 0.2.2 * net-protocol 0.1.3 + * openssl 3.1.0.pre * ostruct 0.5.5 * psych 5.0.0.dev * reline 0.3.1 From f08fcd0e803bb07912794b8a2366973946d2af2f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 17 Oct 2022 14:46:10 +0900 Subject: [PATCH 088/139] Fix possible use of undefined macros on very old macOS [ci skip] --- dln.c | 8 ++++---- error.c | 9 +++++++-- thread_pthread.c | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dln.c b/dln.c index b14ba63c44c67c..bf87251bb6b4d1 100644 --- a/dln.c +++ b/dln.c @@ -298,15 +298,15 @@ COMPILER_WARNING_POP /* assume others than old Mac OS X have no problem */ # define dln_disable_dlclose() false -#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 -/* targeting newer versions only */ -# define dln_disable_dlclose() false - #elif !defined(MAC_OS_X_VERSION_10_11) || \ (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11) /* targeting older versions only */ # define dln_disable_dlclose() true +#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 +/* targeting newer versions only */ +# define dln_disable_dlclose() false + #else /* support both versions, and check at runtime */ # include diff --git a/error.c b/error.c index b37d139b7cba8a..ceea1c55d1f6c7 100644 --- a/error.c +++ b/error.c @@ -666,6 +666,11 @@ bug_important_message(FILE *out, const char *const msg, size_t len) fwrite(p, 1, endmsg - p, out); } +#undef CRASH_REPORTER_MAY_BE_CREATED +#if defined(__APPLE__) && \ + (!defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6) +# define CRASH_REPORTER_MAY_BE_CREATED +#endif static void preface_dump(FILE *out) { @@ -674,7 +679,7 @@ preface_dump(FILE *out) "-- Crash Report log information " "--------------------------------------------\n" " See Crash Report log file in one of the following locations:\n" -# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 +# ifdef CRASH_REPORTER_MAY_BE_CREATED " * ~/Library/Logs/CrashReporter\n" " * /Library/Logs/CrashReporter\n" # endif @@ -699,7 +704,7 @@ postscript_dump(FILE *out) "[IMPORTANT]" /*" ------------------------------------------------"*/ "\n""Don't forget to include the Crash Report log file under\n" -# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 +# ifdef CRASH_REPORTER_MAY_BE_CREATED "CrashReporter or " # endif "DiagnosticReports directory in bug reports.\n" diff --git a/thread_pthread.c b/thread_pthread.c index c29c7e195806e7..aa5435aa99a5fa 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -1861,7 +1861,8 @@ native_thread_native_thread_id(rb_thread_t *target_th) return INT2FIX(tid); #elif defined(__APPLE__) uint64_t tid; -# if ((MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6) || \ +# if (!defined(MAC_OS_X_VERSION_10_6) || \ + (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6) || \ defined(__POWERPC__) /* never defined for PowerPC platforms */) const bool no_pthread_threadid_np = true; # define NO_PTHREAD_MACH_THREAD_NP 1 From b584c106d9f432431094136be67f11853bd3eb9c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 17 Oct 2022 15:33:28 +0900 Subject: [PATCH 089/139] Fix debug messages [ci skip] --- random.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/random.c b/random.c index 9476de0d4afc03..013e9f66e3d7c8 100644 --- a/random.c +++ b/random.c @@ -500,15 +500,18 @@ static int fill_random_bytes_syscall(void *seed, size_t size, int unused) { #if USE_COMMON_RANDOM - int failed = CCRandomGenerateBytes(seed, size) != kCCSuccess; + CCRNGStatus status = CCRandomGenerateBytes(seed, size); + int failed = status != kCCSuccess; #else - int failed = SecRandomCopyBytes(kSecRandomDefault, size, seed) != errSecSuccess; + int status = SecRandomCopyBytes(kSecRandomDefault, size, seed); + int failed = status != errSecSuccess; #endif if (failed) { # if 0 # if USE_COMMON_RANDOM /* How to get the error message? */ + fprintf(stderr, "CCRandomGenerateBytes failed: %d\n", status); # else CFStringRef s = SecCopyErrorMessageString(status, NULL); const char *m = s ? CFStringGetCStringPtr(s, kCFStringEncodingUTF8) : NULL; From f6bf5e2c5f07a0e99a6f3dbc29035f911a4e477f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 17 Oct 2022 15:34:00 +0900 Subject: [PATCH 090/139] Respect `USE_COMMON_RANDOM` macro by a command line option [ci skip] --- random.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/random.c b/random.c index 013e9f66e3d7c8..f8879cfb88a836 100644 --- a/random.c +++ b/random.c @@ -487,13 +487,17 @@ fill_random_bytes_urandom(void *seed, size_t size) #if 0 #elif defined MAC_OS_X_VERSION_10_7 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 -# if defined MAC_OS_X_VERSION_10_10 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10 +# if defined(USE_COMMON_RANDOM) +# elif defined MAC_OS_X_VERSION_10_10 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10 +# define USE_COMMON_RANDOM 1 +# else +# define USE_COMMON_RANDOM 0 +# endif +# if USE_COMMON_RANDOM # include /* for old Xcode */ # include -# define USE_COMMON_RANDOM 1 # else # include -# define USE_COMMON_RANDOM 0 # endif static int From e037731c9f840b0c74eb68a808a109fdef3c6d5e Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Mon, 17 Oct 2022 07:30:30 -0600 Subject: [PATCH 091/139] [ruby/openssl] define BIGNUM_2cr macro for BN function that takes context and returns a BN https://github.com/ruby/openssl/commit/4d0971c51c --- ext/openssl/ossl_bn.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index 56fa0ec30214f0..7eb1bc79a5a7a9 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -577,22 +577,25 @@ BIGNUM_2c(gcd) */ BIGNUM_2c(mod_sqr) +#define BIGNUM_2cr(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_##func(NULL, bn1, bn2, ossl_bn_ctx))) \ + ossl_raise(eBNError, NULL); \ + SetBN(obj, result); \ + return obj; \ + } + /* * call-seq: * bn.mod_inverse(bn2) => aBN */ -static VALUE -ossl_bn_mod_inverse(VALUE self, VALUE other) -{ - BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; - VALUE obj; - GetBN(self, bn1); - obj = NewBN(rb_obj_class(self)); - if (!(result = BN_mod_inverse(NULL, bn1, bn2, ossl_bn_ctx))) - ossl_raise(eBNError, "BN_mod_inverse"); - SetBN(obj, result); - return obj; -} +BIGNUM_2cr(mod_inverse) /* * call-seq: From 149cb049f1d7f5fd2edf5ab467c33037ec993a47 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Mon, 17 Oct 2022 07:31:40 -0600 Subject: [PATCH 092/139] [ruby/openssl] add BN#mod_sqrt https://github.com/ruby/openssl/commit/4619ab3e76 --- ext/openssl/ossl_bn.c | 8 ++++++++ test/openssl/test_bn.rb | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index 7eb1bc79a5a7a9..06647b5b627294 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -591,6 +591,13 @@ BIGNUM_2c(mod_sqr) return obj; \ } +/* + * Document-method: OpenSSL::BN#mod_sqrt + * call-seq: + * bn.mod_sqrt(bn2) => aBN + */ +BIGNUM_2cr(mod_sqrt) + /* * call-seq: * bn.mod_inverse(bn2) => aBN @@ -1237,6 +1244,7 @@ Init_ossl_bn(void) rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 2); rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 2); rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1); + rb_define_method(cBN, "mod_sqrt", ossl_bn_mod_sqrt, 1); rb_define_method(cBN, "**", ossl_bn_exp, 1); rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 2); rb_define_method(cBN, "gcd", ossl_bn_gcd, 1); diff --git a/test/openssl/test_bn.rb b/test/openssl/test_bn.rb index 346602dada21ef..77af14091e3a55 100644 --- a/test/openssl/test_bn.rb +++ b/test/openssl/test_bn.rb @@ -174,6 +174,12 @@ def test_mod_sqr assert_equal(0, 59.to_bn.mod_sqr(59)) end + def test_mod_sqrt + assert_equal(3, 4.to_bn.mod_sqrt(5)) + assert_equal(0, 5.to_bn.mod_sqrt(5)) + assert_raise(OpenSSL::BNError) { 3.to_bn.mod_sqrt(5) } + end + def test_mod_inverse assert_equal(2, 3.to_bn.mod_inverse(5)) assert_raise(OpenSSL::BNError) { 3.to_bn.mod_inverse(6) } From 7db29de008bbf06ce1dc2a1e9fd9c9684b31e325 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Mon, 17 Oct 2022 07:58:04 -0600 Subject: [PATCH 093/139] [ruby/openssl] add document-method for BN#mod_inverse https://github.com/ruby/openssl/commit/5befde7519 --- ext/openssl/ossl_bn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index 06647b5b627294..bf2bac36798373 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -599,6 +599,7 @@ BIGNUM_2c(mod_sqr) BIGNUM_2cr(mod_sqrt) /* + * Document-method: OpenSSL::BN#mod_inverse * call-seq: * bn.mod_inverse(bn2) => aBN */ From 60defe0a68a40d1b3225cf6b971ea195e19ae2e2 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 6 Oct 2022 15:53:16 +0200 Subject: [PATCH 094/139] thread_sync.c: Clarify and document the behavior of timeout == 0 [Feature #18982] Instead of introducing an `exception: false` argument to have `non_block` return nil rather than raise, we can clearly document that a timeout of 0 immediately returns. The code is refactored a bit to avoid doing a time calculation in such case. --- spec/ruby/shared/queue/deque.rb | 7 +++++ spec/ruby/shared/sizedqueue/enque.rb | 8 ++++- thread_sync.c | 45 +++++++++++++++------------- thread_sync.rb | 6 ++-- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/spec/ruby/shared/queue/deque.rb b/spec/ruby/shared/queue/deque.rb index ed32bd29c88b61..616e56ec8a1a37 100644 --- a/spec/ruby/shared/queue/deque.rb +++ b/spec/ruby/shared/queue/deque.rb @@ -87,6 +87,13 @@ t.join end + it "immediately returns nil if no item is available and the timeout is 0" do + q = @object.call + q << 1 + q.send(@method, timeout: 0).should == 1 + q.send(@method, timeout: 0).should == nil + end + it "raise TypeError if timeout is not a valid numeric" do q = @object.call -> { q.send(@method, timeout: "1") }.should raise_error( diff --git a/spec/ruby/shared/sizedqueue/enque.rb b/spec/ruby/shared/sizedqueue/enque.rb index 126470594a43c4..059f1025a75b90 100644 --- a/spec/ruby/shared/sizedqueue/enque.rb +++ b/spec/ruby/shared/sizedqueue/enque.rb @@ -73,7 +73,13 @@ t.join end - it "returns nil if no item is available in time" do + it "returns nil if no space is avialable and timeout is 0" do + q = @object.call(1) + q.send(@method, 1, timeout: 0).should == q + q.send(@method, 2, timeout: 0).should == nil + end + + it "returns nil if no space is available in time" do q = @object.call(1) q << 1 t = Thread.new { diff --git a/thread_sync.c b/thread_sync.c index 4ae404ec055bb7..b2ee052aa57f86 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -1029,13 +1029,19 @@ static VALUE queue_do_pop(VALUE self, struct rb_queue *q, int should_block, VALUE timeout) { check_array(self, q->que); - rb_hrtime_t end = queue_timeout2hrtime(timeout); + if (RARRAY_LEN(q->que) == 0) { + if (!should_block) { + rb_raise(rb_eThreadError, "queue empty"); + } + + if (RTEST(rb_equal(INT2FIX(0), timeout))) { + return Qnil; + } + } + rb_hrtime_t end = queue_timeout2hrtime(timeout); while (RARRAY_LEN(q->que) == 0) { - if (!should_block) { - rb_raise(rb_eThreadError, "queue empty"); - } - else if (queue_closed_p(self)) { + if (queue_closed_p(self)) { return queue_closed_result(self, q); } else { @@ -1232,16 +1238,22 @@ rb_szqueue_max_set(VALUE self, VALUE vmax) static VALUE rb_szqueue_push(rb_execution_context_t *ec, VALUE self, VALUE object, VALUE non_block, VALUE timeout) { - rb_hrtime_t end = queue_timeout2hrtime(timeout); - bool timed_out = false; struct rb_szqueue *sq = szqueue_ptr(self); + if (queue_length(self, &sq->q) >= sq->max) { + if (RTEST(non_block)) { + rb_raise(rb_eThreadError, "queue full"); + } + + if (RTEST(rb_equal(INT2FIX(0), timeout))) { + return Qnil; + } + } + + rb_hrtime_t end = queue_timeout2hrtime(timeout); while (queue_length(self, &sq->q) >= sq->max) { - if (RTEST(non_block)) { - rb_raise(rb_eThreadError, "queue full"); - } - else if (queue_closed_p(self)) { - break; + if (queue_closed_p(self)) { + raise_closed_queue_error(self); } else { rb_execution_context_t *ec = GET_EC(); @@ -1262,18 +1274,11 @@ rb_szqueue_push(rb_execution_context_t *ec, VALUE self, VALUE object, VALUE non_ }; rb_ensure(queue_sleep, (VALUE)&queue_sleep_arg, szqueue_sleep_done, (VALUE)&queue_waiter); if (!NIL_P(timeout) && rb_hrtime_now() >= end) { - timed_out = true; - break; + return Qnil; } } } - if (queue_closed_p(self)) { - raise_closed_queue_error(self); - } - - if (timed_out) return Qnil; - return queue_do_push(self, &sq->q, object); } diff --git a/thread_sync.rb b/thread_sync.rb index 7e4c341ad0c65e..f8fa69900b39d0 100644 --- a/thread_sync.rb +++ b/thread_sync.rb @@ -10,7 +10,7 @@ class Queue # +ThreadError+ is raised. # # If +timeout+ seconds have passed and no data is available +nil+ is - # returned. + # returned. If +timeout+ is +0+ it returns immediately. def pop(non_block = false, timeout: nil) if non_block && timeout raise ArgumentError, "can't set a timeout if non_block is enabled" @@ -32,7 +32,7 @@ class SizedQueue # suspended, and +ThreadError+ is raised. # # If +timeout+ seconds have passed and no data is available +nil+ is - # returned. + # returned. If +timeout+ is +0+ it returns immediately. def pop(non_block = false, timeout: nil) if non_block && timeout raise ArgumentError, "can't set a timeout if non_block is enabled" @@ -54,7 +54,7 @@ def pop(non_block = false, timeout: nil) # thread isn't suspended, and +ThreadError+ is raised. # # If +timeout+ seconds have passed and no space is available +nil+ is - # returned. + # returned. If +timeout+ is +0+ it returns immediately. # Otherwise it returns +self+. def push(object, non_block = false, timeout: nil) if non_block && timeout From 637144b83432781e7e070ca74445b172f8af109e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 18 Oct 2022 00:23:23 +0900 Subject: [PATCH 095/139] Adjust indents [ci skip] --- thread_sync.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/thread_sync.c b/thread_sync.c index b2ee052aa57f86..388853446861ad 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -1030,13 +1030,13 @@ queue_do_pop(VALUE self, struct rb_queue *q, int should_block, VALUE timeout) { check_array(self, q->que); if (RARRAY_LEN(q->que) == 0) { - if (!should_block) { - rb_raise(rb_eThreadError, "queue empty"); - } + if (!should_block) { + rb_raise(rb_eThreadError, "queue empty"); + } - if (RTEST(rb_equal(INT2FIX(0), timeout))) { - return Qnil; - } + if (RTEST(rb_equal(INT2FIX(0), timeout))) { + return Qnil; + } } rb_hrtime_t end = queue_timeout2hrtime(timeout); @@ -1241,13 +1241,13 @@ rb_szqueue_push(rb_execution_context_t *ec, VALUE self, VALUE object, VALUE non_ struct rb_szqueue *sq = szqueue_ptr(self); if (queue_length(self, &sq->q) >= sq->max) { - if (RTEST(non_block)) { - rb_raise(rb_eThreadError, "queue full"); - } + if (RTEST(non_block)) { + rb_raise(rb_eThreadError, "queue full"); + } - if (RTEST(rb_equal(INT2FIX(0), timeout))) { - return Qnil; - } + if (RTEST(rb_equal(INT2FIX(0), timeout))) { + return Qnil; + } } rb_hrtime_t end = queue_timeout2hrtime(timeout); From ad3d210beab9d162b96e2601aa3ad896785b831b Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Fri, 14 Oct 2022 17:20:41 -0400 Subject: [PATCH 096/139] YJIT: call free_block to cleanup block when out of memory The commented out instance of free_block() is left over from the port. The addition in gen_single_block() was a place we missed. The new block is allocated in the same function and could have invariants associated with it even though there is no space to hold all the code. --- yjit/src/codegen.rs | 1 + yjit/src/core.rs | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 88b51674cf3351..5f6d97834a9c7e 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -877,6 +877,7 @@ pub fn gen_single_block( // If code for the block doesn't fit, fail if cb.has_dropped_bytes() || ocb.unwrap().has_dropped_bytes() { + free_block(&blockref); return Err(()); } diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 1805a2bb13300f..3cecf31a85d06c 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -1438,11 +1438,9 @@ fn gen_block_series_body( if result.is_err() { // Remove previously compiled block // versions from the version map + mem::drop(last_branch); // end borrow for blockref in &batch { - // FIXME: should be deallocating resources here too - // e.g. invariants, etc. - //free_block(blockref) - + free_block(blockref); remove_block_version(blockref); } @@ -2010,7 +2008,7 @@ pub fn defer_compilation( } // Remove all references to a block then free it. -fn free_block(blockref: &BlockRef) { +pub fn free_block(blockref: &BlockRef) { use crate::invariants::*; block_assumptions_free(blockref); From 07a93b1e378bf2ea356b0561e5e89e60d30fc684 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Tue, 18 Oct 2022 00:26:36 +0800 Subject: [PATCH 097/139] YJIT: Do not call `mprotect` when `mem_size` is zero (#6563) This allows x86_64 based YJIT to run on Docker Desktop on Apple silicon (arm64) Mac because it will avoid a subtle behavior difference in `mprotect` system call between the Linux kernel and `qemu-x86_64` user space emulator. --- yjit.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yjit.c b/yjit.c index 838956f7b47e69..a53e2ca7099de7 100644 --- a/yjit.c +++ b/yjit.c @@ -74,6 +74,11 @@ rb_yjit_mark_writable(void *mem_block, uint32_t mem_size) void rb_yjit_mark_executable(void *mem_block, uint32_t mem_size) { + // Do not call mprotect when mem_size is zero. Some platforms may return + // an error for it. https://github.com/Shopify/ruby/issues/450 + if (mem_size == 0) { + return; + } if (mprotect(mem_block, mem_size, PROT_READ | PROT_EXEC)) { rb_bug("Couldn't make JIT page (%p, %lu bytes) executable, errno: %s\n", mem_block, (unsigned long)mem_size, strerror(errno)); From e7c71c6c9271b0c29f210769159090e17128e740 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 17 Oct 2022 09:27:59 -0700 Subject: [PATCH 098/139] Make mjit_cont sharable with YJIT (#6556) * Make mjit_cont sharable with YJIT * Update dependencies * Update YJIT binding --- common.mk | 20 +++++ cont.c | 131 +++++++++++++++++++++++++++++---- eval.c | 4 +- internal/cont.h | 5 +- iseq.h | 1 + mjit.c | 103 +------------------------- mjit.h | 4 - ruby.c | 7 ++ yjit.c | 6 +- yjit/src/cruby_bindings.inc.rs | 4 +- 10 files changed, 159 insertions(+), 126 deletions(-) diff --git a/common.mk b/common.mk index 464f64cb764f7b..934939ea4d3004 100644 --- a/common.mk +++ b/common.mk @@ -3564,6 +3564,7 @@ cont.$(OBJEXT): {$(VPATH)}internal/value_type.h cont.$(OBJEXT): {$(VPATH)}internal/variable.h cont.$(OBJEXT): {$(VPATH)}internal/warning_push.h cont.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +cont.$(OBJEXT): {$(VPATH)}iseq.h cont.$(OBJEXT): {$(VPATH)}method.h cont.$(OBJEXT): {$(VPATH)}missing.h cont.$(OBJEXT): {$(VPATH)}mjit.h @@ -3580,6 +3581,8 @@ cont.$(OBJEXT): {$(VPATH)}thread_native.h cont.$(OBJEXT): {$(VPATH)}vm_core.h cont.$(OBJEXT): {$(VPATH)}vm_debug.h cont.$(OBJEXT): {$(VPATH)}vm_opts.h +cont.$(OBJEXT): {$(VPATH)}vm_sync.h +cont.$(OBJEXT): {$(VPATH)}yjit.h debug.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h debug.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h debug.$(OBJEXT): $(CCAN_DIR)/list/list.h @@ -6262,6 +6265,7 @@ eval.$(OBJEXT): $(hdrdir)/ruby/ruby.h eval.$(OBJEXT): $(top_srcdir)/internal/array.h eval.$(OBJEXT): $(top_srcdir)/internal/class.h eval.$(OBJEXT): $(top_srcdir)/internal/compilers.h +eval.$(OBJEXT): $(top_srcdir)/internal/cont.h eval.$(OBJEXT): $(top_srcdir)/internal/error.h eval.$(OBJEXT): $(top_srcdir)/internal/eval.h eval.$(OBJEXT): $(top_srcdir)/internal/gc.h @@ -6902,6 +6906,7 @@ gc.$(OBJEXT): {$(VPATH)}internal/variable.h gc.$(OBJEXT): {$(VPATH)}internal/warning_push.h gc.$(OBJEXT): {$(VPATH)}internal/xmalloc.h gc.$(OBJEXT): {$(VPATH)}io.h +gc.$(OBJEXT): {$(VPATH)}iseq.h gc.$(OBJEXT): {$(VPATH)}method.h gc.$(OBJEXT): {$(VPATH)}missing.h gc.$(OBJEXT): {$(VPATH)}mjit.h @@ -7124,6 +7129,10 @@ goruby.$(OBJEXT): {$(VPATH)}thread_native.h goruby.$(OBJEXT): {$(VPATH)}vm_core.h goruby.$(OBJEXT): {$(VPATH)}vm_debug.h goruby.$(OBJEXT): {$(VPATH)}vm_opts.h +hash.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +hash.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +hash.$(OBJEXT): $(CCAN_DIR)/list/list.h +hash.$(OBJEXT): $(CCAN_DIR)/str/str.h hash.$(OBJEXT): $(hdrdir)/ruby/ruby.h hash.$(OBJEXT): $(top_srcdir)/internal/array.h hash.$(OBJEXT): $(top_srcdir)/internal/bignum.h @@ -7134,6 +7143,7 @@ hash.$(OBJEXT): $(top_srcdir)/internal/cont.h hash.$(OBJEXT): $(top_srcdir)/internal/error.h hash.$(OBJEXT): $(top_srcdir)/internal/gc.h hash.$(OBJEXT): $(top_srcdir)/internal/hash.h +hash.$(OBJEXT): $(top_srcdir)/internal/imemo.h hash.$(OBJEXT): $(top_srcdir)/internal/object.h hash.$(OBJEXT): $(top_srcdir)/internal/proc.h hash.$(OBJEXT): $(top_srcdir)/internal/serial.h @@ -7146,6 +7156,7 @@ hash.$(OBJEXT): $(top_srcdir)/internal/variable.h hash.$(OBJEXT): $(top_srcdir)/internal/vm.h hash.$(OBJEXT): $(top_srcdir)/internal/warnings.h hash.$(OBJEXT): {$(VPATH)}assert.h +hash.$(OBJEXT): {$(VPATH)}atomic.h hash.$(OBJEXT): {$(VPATH)}backward/2/assume.h hash.$(OBJEXT): {$(VPATH)}backward/2/attributes.h hash.$(OBJEXT): {$(VPATH)}backward/2/bool.h @@ -7314,21 +7325,28 @@ hash.$(OBJEXT): {$(VPATH)}internal/value_type.h hash.$(OBJEXT): {$(VPATH)}internal/variable.h hash.$(OBJEXT): {$(VPATH)}internal/warning_push.h hash.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +hash.$(OBJEXT): {$(VPATH)}iseq.h +hash.$(OBJEXT): {$(VPATH)}method.h hash.$(OBJEXT): {$(VPATH)}missing.h +hash.$(OBJEXT): {$(VPATH)}node.h hash.$(OBJEXT): {$(VPATH)}onigmo.h hash.$(OBJEXT): {$(VPATH)}oniguruma.h hash.$(OBJEXT): {$(VPATH)}probes.dmyh hash.$(OBJEXT): {$(VPATH)}probes.h hash.$(OBJEXT): {$(VPATH)}ractor.h hash.$(OBJEXT): {$(VPATH)}ruby_assert.h +hash.$(OBJEXT): {$(VPATH)}ruby_atomic.h hash.$(OBJEXT): {$(VPATH)}shape.h hash.$(OBJEXT): {$(VPATH)}st.h hash.$(OBJEXT): {$(VPATH)}subst.h hash.$(OBJEXT): {$(VPATH)}symbol.h +hash.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h hash.$(OBJEXT): {$(VPATH)}thread_native.h hash.$(OBJEXT): {$(VPATH)}transient_heap.h hash.$(OBJEXT): {$(VPATH)}util.h +hash.$(OBJEXT): {$(VPATH)}vm_core.h hash.$(OBJEXT): {$(VPATH)}vm_debug.h +hash.$(OBJEXT): {$(VPATH)}vm_opts.h hash.$(OBJEXT): {$(VPATH)}vm_sync.h inits.$(OBJEXT): $(hdrdir)/ruby.h inits.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -13408,6 +13426,7 @@ ruby.$(OBJEXT): $(top_srcdir)/internal/array.h ruby.$(OBJEXT): $(top_srcdir)/internal/class.h ruby.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h ruby.$(OBJEXT): $(top_srcdir)/internal/compilers.h +ruby.$(OBJEXT): $(top_srcdir)/internal/cont.h ruby.$(OBJEXT): $(top_srcdir)/internal/error.h ruby.$(OBJEXT): $(top_srcdir)/internal/file.h ruby.$(OBJEXT): $(top_srcdir)/internal/gc.h @@ -13598,6 +13617,7 @@ ruby.$(OBJEXT): {$(VPATH)}internal/variable.h ruby.$(OBJEXT): {$(VPATH)}internal/warning_push.h ruby.$(OBJEXT): {$(VPATH)}internal/xmalloc.h ruby.$(OBJEXT): {$(VPATH)}io.h +ruby.$(OBJEXT): {$(VPATH)}iseq.h ruby.$(OBJEXT): {$(VPATH)}method.h ruby.$(OBJEXT): {$(VPATH)}missing.h ruby.$(OBJEXT): {$(VPATH)}mjit.h diff --git a/cont.c b/cont.c index b6d26d716ef8b1..1ce60811d229fd 100644 --- a/cont.c +++ b/cont.c @@ -34,7 +34,9 @@ extern int madvise(caddr_t, size_t, int); #include "internal/warnings.h" #include "ruby/fiber/scheduler.h" #include "mjit.h" +#include "yjit.h" #include "vm_core.h" +#include "vm_sync.h" #include "id_table.h" #include "ractor_core.h" @@ -67,6 +69,8 @@ static VALUE rb_cFiberPool; #define FIBER_POOL_ALLOCATION_FREE #endif +#define jit_cont_enabled mjit_enabled // To be used by YJIT later + enum context_type { CONTINUATION_CONTEXT = 0, FIBER_CONTEXT = 1 @@ -195,6 +199,15 @@ struct fiber_pool { size_t vm_stack_size; }; +// Continuation contexts used by JITs +struct rb_jit_cont { + rb_execution_context_t *ec; // continuation ec + struct rb_jit_cont *prev, *next; // used to form lists +}; + +// Doubly linked list for enumerating all on-stack ISEQs. +static struct rb_jit_cont *first_jit_cont; + typedef struct rb_context_struct { enum context_type type; int argc; @@ -212,8 +225,7 @@ typedef struct rb_context_struct { rb_execution_context_t saved_ec; rb_jmpbuf_t jmpbuf; rb_ensure_entry_t *ensure_array; - /* Pointer to MJIT info about the continuation. */ - struct mjit_cont *mjit_cont; + struct rb_jit_cont *jit_cont; // Continuation contexts for JITs } rb_context_t; @@ -1000,6 +1012,8 @@ fiber_is_root_p(const rb_fiber_t *fiber) } #endif +static void jit_cont_free(struct rb_jit_cont *cont); + static void cont_free(void *ptr) { @@ -1020,9 +1034,9 @@ cont_free(void *ptr) RUBY_FREE_UNLESS_NULL(cont->saved_vm_stack.ptr); - if (mjit_enabled) { - VM_ASSERT(cont->mjit_cont != NULL); - mjit_cont_free(cont->mjit_cont); + if (jit_cont_enabled) { + VM_ASSERT(cont->jit_cont != NULL); + jit_cont_free(cont->jit_cont); } /* free rb_cont_t or rb_fiber_t */ ruby_xfree(ptr); @@ -1187,12 +1201,98 @@ cont_save_thread(rb_context_t *cont, rb_thread_t *th) sec->machine.stack_end = NULL; } +// Register a new continuation with execution context `ec`. Return JIT info about +// the continuation. +static struct rb_jit_cont * +jit_cont_new(rb_execution_context_t *ec) +{ + struct rb_jit_cont *cont; + + // We need to use calloc instead of something like ZALLOC to avoid triggering GC here. + // When this function is called from rb_thread_alloc through rb_threadptr_root_fiber_setup, + // the thread is still being prepared and marking it causes SEGV. + cont = calloc(1, sizeof(struct rb_jit_cont)); + if (cont == NULL) + rb_memerror(); + cont->ec = ec; + + RB_VM_LOCK_ENTER(); + if (first_jit_cont == NULL) { + cont->next = cont->prev = NULL; + } + else { + cont->prev = NULL; + cont->next = first_jit_cont; + first_jit_cont->prev = cont; + } + first_jit_cont = cont; + RB_VM_LOCK_LEAVE(); + + return cont; +} + +// Unregister continuation `cont`. +static void +jit_cont_free(struct rb_jit_cont *cont) +{ + RB_VM_LOCK_ENTER(); + if (cont == first_jit_cont) { + first_jit_cont = cont->next; + if (first_jit_cont != NULL) + first_jit_cont->prev = NULL; + } + else { + cont->prev->next = cont->next; + if (cont->next != NULL) + cont->next->prev = cont->prev; + } + RB_VM_LOCK_LEAVE(); + + free(cont); +} + +// Call a given callback against all on-stack ISEQs. +void +rb_jit_cont_each_iseq(rb_iseq_callback callback) +{ + struct rb_jit_cont *cont; + for (cont = first_jit_cont; cont != NULL; cont = cont->next) { + if (cont->ec->vm_stack == NULL) + continue; + + const rb_control_frame_t *cfp; + for (cfp = RUBY_VM_END_CONTROL_FRAME(cont->ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) { + const rb_iseq_t *iseq; + if (cfp->pc && (iseq = cfp->iseq) != NULL && imemo_type((VALUE)iseq) == imemo_iseq) { + callback(iseq); + } + + if (cfp == cont->ec->cfp) + break; // reached the most recent cfp + } + } +} + +// Finish working with continuation info. +void +rb_jit_cont_finish(void) +{ + if (!jit_cont_enabled) + return; + + struct rb_jit_cont *cont, *next; + for (cont = first_jit_cont; cont != NULL; cont = next) { + next = cont->next; + xfree(cont); + } +} + static void -cont_init_mjit_cont(rb_context_t *cont) +cont_init_jit_cont(rb_context_t *cont) { - VM_ASSERT(cont->mjit_cont == NULL); - if (mjit_enabled) { - cont->mjit_cont = mjit_cont_new(&(cont->saved_ec)); + VM_ASSERT(cont->jit_cont == NULL); + if (jit_cont_enabled) { + cont->jit_cont = jit_cont_new(&(cont->saved_ec)); } } @@ -1211,7 +1311,7 @@ cont_init(rb_context_t *cont, rb_thread_t *th) cont->saved_ec.local_storage = NULL; cont->saved_ec.local_storage_recursive_hash = Qnil; cont->saved_ec.local_storage_recursive_hash_for_trace = Qnil; - cont_init_mjit_cont(cont); + cont_init_jit_cont(cont); } static rb_context_t * @@ -1242,9 +1342,9 @@ rb_fiberptr_blocking(struct rb_fiber_struct *fiber) // This is used for root_fiber because other fibers call cont_init_mjit_cont through cont_new. void -rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber) +rb_fiber_init_jit_cont(struct rb_fiber_struct *fiber) { - cont_init_mjit_cont(&fiber->cont); + cont_init_jit_cont(&fiber->cont); } #if 0 @@ -2187,9 +2287,10 @@ rb_threadptr_root_fiber_setup(rb_thread_t *th) fiber->blocking = 1; fiber_status_set(fiber, FIBER_RESUMED); /* skip CREATED */ th->ec = &fiber->cont.saved_ec; - // This skips mjit_cont_new for the initial thread because mjit_enabled is always false - // at this point. mjit_init calls rb_fiber_init_mjit_cont again for this root_fiber. - rb_fiber_init_mjit_cont(fiber); + // This skips jit_cont_new for the initial thread because rb_yjit_enabled_p() and + // mjit_enabled are false at this point. ruby_opt_init will call rb_fiber_init_jit_cont + // again for this root_fiber. + rb_fiber_init_jit_cont(fiber); } void diff --git a/eval.c b/eval.c index 9567f801501d86..f5c9c0f087075e 100644 --- a/eval.c +++ b/eval.c @@ -21,6 +21,7 @@ #include "gc.h" #include "internal.h" #include "internal/class.h" +#include "internal/cont.h" #include "internal/error.h" #include "internal/eval.h" #include "internal/hash.h" @@ -251,7 +252,8 @@ rb_ec_cleanup(rb_execution_context_t *ec, int ex0) } } - mjit_finish(true); // We still need ISeqs here. + mjit_finish(true); // We still need ISeqs here, so it's before rb_ec_finalize(). + rb_jit_cont_finish(); rb_ec_finalize(ec); diff --git a/internal/cont.h b/internal/cont.h index abffc97104422f..0b669f0ad58ada 100644 --- a/internal/cont.h +++ b/internal/cont.h @@ -9,6 +9,7 @@ * @brief Internal header for Fiber. */ #include "ruby/ruby.h" /* for VALUE */ +#include "iseq.h" struct rb_thread_struct; /* in vm_core.h */ struct rb_fiber_struct; /* in cont.c */ @@ -17,7 +18,9 @@ struct rb_execution_context_struct; /* in vm_core.c */ /* cont.c */ void rb_fiber_reset_root_local_storage(struct rb_thread_struct *); void ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(VALUE), VALUE (*rollback_func)(VALUE)); -void rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber); +void rb_fiber_init_jit_cont(struct rb_fiber_struct *fiber); +void rb_jit_cont_each_iseq(rb_iseq_callback callback); +void rb_jit_cont_finish(void); VALUE rb_fiberptr_self(struct rb_fiber_struct *fiber); unsigned int rb_fiberptr_blocking(struct rb_fiber_struct *fiber); diff --git a/iseq.h b/iseq.h index 98cf71d52cd90b..d771874c2fd7f0 100644 --- a/iseq.h +++ b/iseq.h @@ -31,6 +31,7 @@ RUBY_EXTERN const int ruby_api_version[]; typedef struct rb_iseq_struct rb_iseq_t; #define rb_iseq_t rb_iseq_t #endif +typedef void (*rb_iseq_callback)(const rb_iseq_t *); extern const ID rb_iseq_shared_exc_local_tbl[]; diff --git a/mjit.c b/mjit.c index 80743a150bdba3..22da5fd45fb501 100644 --- a/mjit.c +++ b/mjit.c @@ -951,42 +951,19 @@ mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const // Set up field `used_code_p` for unit iseqs whose iseq on the stack of ec. static void -mark_ec_units(rb_execution_context_t *ec) +mark_iseq_units(const rb_iseq_t *iseq) { - const rb_control_frame_t *cfp; - - if (ec->vm_stack == NULL) - return; - for (cfp = RUBY_VM_END_CONTROL_FRAME(ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) { - const rb_iseq_t *iseq; - if (cfp->pc && (iseq = cfp->iseq) != NULL - && imemo_type((VALUE) iseq) == imemo_iseq - && (ISEQ_BODY(iseq)->jit_unit) != NULL) { - ISEQ_BODY(iseq)->jit_unit->used_code_p = true; - } - - if (cfp == ec->cfp) - break; // reached the most recent cfp + if (ISEQ_BODY(iseq)->jit_unit != NULL) { + ISEQ_BODY(iseq)->jit_unit->used_code_p = true; } } -// MJIT info related to an existing continutaion. -struct mjit_cont { - rb_execution_context_t *ec; // continuation ec - struct mjit_cont *prev, *next; // used to form lists -}; - -// Double linked list of registered continuations. This is used to detect -// units which are in use in unload_units. -static struct mjit_cont *first_cont; - // Unload JIT code of some units to satisfy the maximum permitted // number of units with a loaded code. static void unload_units(void) { struct rb_mjit_unit *unit = 0, *next; - struct mjit_cont *cont; int units_num = active_units.length; // For now, we don't unload units when ISeq is GCed. We should @@ -1005,9 +982,7 @@ unload_units(void) } // All threads have a root_fiber which has a mjit_cont. Other normal fibers also // have a mjit_cont. Thus we can check ISeqs in use by scanning ec of mjit_conts. - for (cont = first_cont; cont != NULL; cont = cont->next) { - mark_ec_units(cont->ec); - } + rb_jit_cont_each_iseq(mark_iseq_units); // TODO: check stale_units and unload unused ones! (note that the unit is not associated to ISeq anymore) // Unload units whose total_calls is smaller than any total_calls in unit_queue. @@ -1163,68 +1138,6 @@ free_list(struct rb_mjit_unit_list *list, bool close_handle_p) list->length = 0; } -// Register a new continuation with execution context `ec`. Return MJIT info about -// the continuation. -struct mjit_cont * -mjit_cont_new(rb_execution_context_t *ec) -{ - struct mjit_cont *cont; - - // We need to use calloc instead of something like ZALLOC to avoid triggering GC here. - // When this function is called from rb_thread_alloc through rb_threadptr_root_fiber_setup, - // the thread is still being prepared and marking it causes SEGV. - cont = calloc(1, sizeof(struct mjit_cont)); - if (cont == NULL) - rb_memerror(); - cont->ec = ec; - - CRITICAL_SECTION_START(3, "in mjit_cont_new"); - if (first_cont == NULL) { - cont->next = cont->prev = NULL; - } - else { - cont->prev = NULL; - cont->next = first_cont; - first_cont->prev = cont; - } - first_cont = cont; - CRITICAL_SECTION_FINISH(3, "in mjit_cont_new"); - - return cont; -} - -// Unregister continuation `cont`. -void -mjit_cont_free(struct mjit_cont *cont) -{ - CRITICAL_SECTION_START(3, "in mjit_cont_new"); - if (cont == first_cont) { - first_cont = cont->next; - if (first_cont != NULL) - first_cont->prev = NULL; - } - else { - cont->prev->next = cont->next; - if (cont->next != NULL) - cont->next->prev = cont->prev; - } - CRITICAL_SECTION_FINISH(3, "in mjit_cont_new"); - - free(cont); -} - -// Finish work with continuation info. -static void -finish_conts(void) -{ - struct mjit_cont *cont, *next; - - for (cont = first_cont; cont != NULL; cont = next) { - next = cont->next; - xfree(cont); - } -} - static void mjit_wait(struct rb_iseq_constant_body *body); // Check the unit queue and start mjit_compile if nothing is in progress. @@ -1889,13 +1802,6 @@ mjit_init(const struct mjit_options *opts) rb_native_cond_initialize(&mjit_worker_wakeup); rb_native_cond_initialize(&mjit_gc_wakeup); - // Make sure the saved_ec of the initial thread's root_fiber is scanned by mark_ec_units. - // - // rb_threadptr_root_fiber_setup for the initial thread is called before mjit_init, - // meaning mjit_cont_new is skipped for the root_fiber. Therefore we need to call - // rb_fiber_init_mjit_cont again with mjit_enabled=true to set the root_fiber's mjit_cont. - rb_fiber_init_mjit_cont(GET_EC()->fiber_ptr); - // If --mjit=pause is given, lazily start MJIT when RubyVM::MJIT.resume is called. // You can use it to control MJIT warmup, or to customize the JIT implementation. if (!mjit_opts.pause) { @@ -2052,7 +1958,6 @@ mjit_finish(bool close_handle_p) free_list(&active_units, close_handle_p); free_list(&compact_units, close_handle_p); free_list(&stale_units, close_handle_p); - finish_conts(); mjit_enabled = false; verbose(1, "Successful MJIT finish"); diff --git a/mjit.h b/mjit.h index ed696b3ff7cd9e..7211e460ab42fb 100644 --- a/mjit.h +++ b/mjit.h @@ -101,8 +101,6 @@ extern void mjit_init(const struct mjit_options *opts); extern void mjit_free_iseq(const rb_iseq_t *iseq); extern void mjit_update_references(const rb_iseq_t *iseq); extern void mjit_mark(void); -extern struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec); -extern void mjit_cont_free(struct mjit_cont *cont); extern void mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body); extern void mjit_notify_waitpid(int exit_code); @@ -120,8 +118,6 @@ void mjit_finish(bool close_handle_p); # else // USE_MJIT static inline void mjit_cancel_all(const char *reason){} -static inline struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec){return NULL;} -static inline void mjit_cont_free(struct mjit_cont *cont){} static inline void mjit_free_iseq(const rb_iseq_t *iseq){} static inline void mjit_mark(void){} static inline VALUE jit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ } diff --git a/ruby.c b/ruby.c index 752498f66c0ead..0a3248f95fd87b 100644 --- a/ruby.c +++ b/ruby.c @@ -44,6 +44,7 @@ #include "eval_intern.h" #include "internal.h" #include "internal/cmdlineopt.h" +#include "internal/cont.h" #include "internal/error.h" #include "internal/file.h" #include "internal/inits.h" @@ -1618,6 +1619,12 @@ ruby_opt_init(ruby_cmdline_options_t *opt) rb_call_builtin_inits(); ruby_init_prelude(); + // Make sure the saved_ec of the initial thread's root_fiber is scanned by rb_jit_cont_each_ec. + // + // rb_threadptr_root_fiber_setup for the initial thread is called before rb_yjit_enabled_p() + // or mjit_enabled becomes true, meaning jit_cont_new is skipped for the root_fiber. + // Therefore we need to call this again here to set the root_fiber's jit_cont. + rb_fiber_init_jit_cont(GET_EC()->fiber_ptr); #if USE_MJIT // mjit_init is safe only after rb_call_builtin_inits defines RubyVM::MJIT::Compiler if (opt->mjit.on) diff --git a/yjit.c b/yjit.c index a53e2ca7099de7..2a523f9787bee1 100644 --- a/yjit.c +++ b/yjit.c @@ -894,13 +894,11 @@ rb_assert_cme_handle(VALUE handle) RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_ment)); } -typedef void (*iseq_callback)(const rb_iseq_t *); - // Heap-walking callback for rb_yjit_for_each_iseq(). static int for_each_iseq_i(void *vstart, void *vend, size_t stride, void *data) { - const iseq_callback callback = (iseq_callback)data; + const rb_iseq_callback callback = (rb_iseq_callback)data; VALUE v = (VALUE)vstart; for (; v != (VALUE)vend; v += stride) { void *ptr = asan_poisoned_object_p(v); @@ -919,7 +917,7 @@ for_each_iseq_i(void *vstart, void *vend, size_t stride, void *data) // Iterate through the whole GC heap and invoke a callback for each iseq. // Used for global code invalidation. void -rb_yjit_for_each_iseq(iseq_callback callback) +rb_yjit_for_each_iseq(rb_iseq_callback callback) { rb_objspace_each_objects(for_each_iseq_i, (void *)callback); } diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index e12ac9eef167b3..db124c930392f9 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1244,6 +1244,7 @@ pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 200; pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 201; pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 202; pub type ruby_vminsn_type = u32; +pub type rb_iseq_callback = ::std::option::Option; extern "C" { pub fn rb_vm_insn_addr2opcode(addr: *const ::std::os::raw::c_void) -> ::std::os::raw::c_int; } @@ -1538,9 +1539,8 @@ extern "C" { extern "C" { pub fn rb_assert_cme_handle(handle: VALUE); } -pub type iseq_callback = ::std::option::Option; extern "C" { - pub fn rb_yjit_for_each_iseq(callback: iseq_callback); + pub fn rb_yjit_for_each_iseq(callback: rb_iseq_callback); } extern "C" { pub fn rb_yjit_obj_written( From 64c52c428285e7930aed62740cc9c54ee483178e Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 17 Oct 2022 10:45:59 -0700 Subject: [PATCH 099/139] YJIT: Interleave inline and outlined code blocks (#6460) Co-authored-by: Alan Wu Co-authored-by: Maxime Chevalier-Boisvert --- yjit/src/asm/mod.rs | 240 +++++++++++++++++++++++++++------ yjit/src/backend/arm64/mod.rs | 103 ++++++++------ yjit/src/backend/ir.rs | 17 +-- yjit/src/backend/tests.rs | 6 +- yjit/src/backend/x86_64/mod.rs | 36 +++-- yjit/src/codegen.rs | 80 ++++------- yjit/src/core.rs | 12 +- yjit/src/options.rs | 36 ++++- yjit/src/utils.rs | 9 +- 9 files changed, 378 insertions(+), 161 deletions(-) diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs index 8356201ba6a2b8..1ab813964c8f5b 100644 --- a/yjit/src/asm/mod.rs +++ b/yjit/src/asm/mod.rs @@ -1,9 +1,20 @@ +use std::cell::RefCell; +use std::cmp; use std::fmt; use std::mem; +use std::rc::Rc; +#[cfg(target_arch = "x86_64")] +use crate::backend::x86_64::JMP_PTR_BYTES; +#[cfg(target_arch = "aarch64")] +use crate::backend::arm64::JMP_PTR_BYTES; +use crate::backend::ir::Assembler; +use crate::backend::ir::Target; +use crate::virtualmem::WriteError; #[cfg(feature = "asm_comments")] use std::collections::BTreeMap; +use crate::codegen::CodegenGlobals; use crate::virtualmem::{VirtualMem, CodePtr}; // Lots of manual vertical alignment in there that rustfmt doesn't handle well. @@ -17,7 +28,8 @@ pub mod arm64; // /// Reference to an ASM label -struct LabelRef { +#[derive(Clone)] +pub struct LabelRef { // Position in the code block where the label reference exists pos: usize, @@ -36,7 +48,7 @@ struct LabelRef { /// Block of memory into which instructions can be assembled pub struct CodeBlock { // Memory for storing the encoded instructions - mem_block: VirtualMem, + mem_block: Rc>, // Memory block size mem_size: usize, @@ -44,6 +56,12 @@ pub struct CodeBlock { // Current writing position write_pos: usize, + // Size of a code page (inlined + outlined) + page_size: usize, + + // Size reserved for writing a jump to the next page + page_end_reserve: usize, + // Table of registered label addresses label_addrs: Vec, @@ -58,7 +76,6 @@ pub struct CodeBlock { asm_comments: BTreeMap>, // True for OutlinedCb - #[cfg(feature = "disasm")] pub outlined: bool, // Set if the CodeBlock is unable to output some instructions, @@ -67,27 +84,158 @@ pub struct CodeBlock { dropped_bytes: bool, } +/// Set of CodeBlock label states. Used for recovering the previous state. +pub struct LabelState { + label_addrs: Vec, + label_names: Vec, + label_refs: Vec, +} + impl CodeBlock { /// Make a new CodeBlock - pub fn new(mem_block: VirtualMem, outlined: bool) -> Self { - Self { - mem_size: mem_block.virtual_region_size(), + pub fn new(mem_block: Rc>, page_size: usize, outlined: bool) -> Self { + let mem_size = mem_block.borrow().virtual_region_size(); + let mut cb = Self { mem_block, + mem_size, write_pos: 0, + page_size, + page_end_reserve: JMP_PTR_BYTES, label_addrs: Vec::new(), label_names: Vec::new(), label_refs: Vec::new(), #[cfg(feature = "asm_comments")] asm_comments: BTreeMap::new(), - #[cfg(feature = "disasm")] outlined, dropped_bytes: false, + }; + cb.write_pos = cb.page_start(); + cb + } + + /// Move the CodeBlock to the next page. If it's on the furthest page, + /// move the other CodeBlock to the next page as well. + pub fn next_page(&mut self, base_ptr: CodePtr, jmp_ptr: F) -> bool { + let old_write_ptr = self.get_write_ptr(); + self.set_write_ptr(base_ptr); + self.without_page_end_reserve(|cb| assert!(cb.has_capacity(JMP_PTR_BYTES))); + + // Move self to the next page + let next_page_idx = self.write_pos / self.page_size + 1; + if !self.set_page(next_page_idx, &jmp_ptr) { + self.set_write_ptr(old_write_ptr); // rollback if there are no more pages + return false; + } + + // Move the other CodeBlock to the same page if it'S on the furthest page + self.other_cb().unwrap().set_page(next_page_idx, &jmp_ptr); + + return !self.dropped_bytes; + } + + /// Move the CodeBlock to page_idx only if it's not going backwards. + fn set_page(&mut self, page_idx: usize, jmp_ptr: &F) -> bool { + // Do not move the CodeBlock if page_idx points to an old position so that this + // CodeBlock will not overwrite existing code. + // + // Let's say this is the current situation: + // cb: [page1, page2, page3 (write_pos)], ocb: [page1, page2, page3 (write_pos)] + // + // When cb needs to patch page1, this will be temporarily changed to: + // cb: [page1 (write_pos), page2, page3], ocb: [page1, page2, page3 (write_pos)] + // + // While patching page1, cb may need to jump to page2. What set_page currently does is: + // cb: [page1, page2 (write_pos), page3], ocb: [page1, page2, page3 (write_pos)] + // instead of: + // cb: [page1, page2 (write_pos), page3], ocb: [page1, page2 (write_pos), page3] + // because moving ocb's write_pos from page3 to the beginning of page2 will let ocb's + // write_pos point to existing code in page2, which might let ocb overwrite it later. + // + // We could remember the last write_pos in page2 and let set_page use that position, + // but you need to waste some space for keeping write_pos for every single page. + // It doesn't seem necessary for performance either. So we're currently not doing it. + let mut dst_pos = self.page_size * page_idx + self.page_start(); + if self.page_size * page_idx < self.mem_size && self.write_pos < dst_pos { + // Reset dropped_bytes + self.dropped_bytes = false; + + // Convert dst_pos to dst_ptr + let src_pos = self.write_pos; + self.write_pos = dst_pos; + let dst_ptr = self.get_write_ptr(); + self.write_pos = src_pos; + + // Generate jmp_ptr from src_pos to dst_pos + self.without_page_end_reserve(|cb| { + cb.add_comment("jump to next page"); + jmp_ptr(cb, dst_ptr); + assert!(!cb.has_dropped_bytes()); + }); + + // Start the next code from dst_pos + self.write_pos = dst_pos; } + !self.dropped_bytes + } + + /// write_pos of the current page start + pub fn page_start_pos(&self) -> usize { + self.get_write_pos() / self.page_size * self.page_size + self.page_start() + } + + /// Offset of each page where CodeBlock should start writing + pub fn page_start(&self) -> usize { + let mut start = if self.inline() { + 0 + } else { + self.page_size / 2 + }; + if cfg!(debug_assertions) && !cfg!(test) { + // Leave illegal instructions at the beginning of each page to assert + // we're not accidentally crossing page boundaries. + start += JMP_PTR_BYTES; + } + start + } + + /// Offset of each page where CodeBlock should stop writing (exclusive) + pub fn page_end(&self) -> usize { + let page_end = if self.inline() { + self.page_size / 2 + } else { + self.page_size + }; + page_end - self.page_end_reserve // reserve space to jump to the next page + } + + /// Call a given function with page_end_reserve = 0 + pub fn without_page_end_reserve(&mut self, block: F) { + let old_page_end_reserve = self.page_end_reserve; + self.page_end_reserve = 0; + block(self); + self.page_end_reserve = old_page_end_reserve; + } + + /// Return the address ranges of a given address range that this CodeBlock can write. + pub fn writable_addrs(&self, start_ptr: CodePtr, end_ptr: CodePtr) -> Vec<(usize, usize)> { + let mut addrs = vec![]; + let mut start = start_ptr.raw_ptr() as usize; + let codeblock_end = self.get_ptr(self.get_mem_size()).raw_ptr() as usize; + let end = std::cmp::min(end_ptr.raw_ptr() as usize, codeblock_end); + while start < end { + let current_page = start / self.page_size * self.page_size; + let page_end = std::cmp::min(end, current_page + self.page_end()) as usize; + addrs.push((start, page_end)); + start = current_page + self.page_size + self.page_start(); + } + addrs } /// Check if this code block has sufficient remaining capacity pub fn has_capacity(&self, num_bytes: usize) -> bool { - self.write_pos + num_bytes < self.mem_size + let page_offset = self.write_pos % self.page_size; + let capacity = self.page_end().saturating_sub(page_offset); + num_bytes <= capacity } /// Add an assembly comment if the feature is on. @@ -121,8 +269,8 @@ impl CodeBlock { self.write_pos } - pub fn get_mem(&mut self) -> &mut VirtualMem { - &mut self.mem_block + pub fn write_mem(&self, write_ptr: CodePtr, byte: u8) -> Result<(), WriteError> { + self.mem_block.borrow_mut().write_byte(write_ptr, byte) } // Set the current write position @@ -134,49 +282,31 @@ impl CodeBlock { self.write_pos = pos; } - // Align the current write pointer to a multiple of bytes - pub fn align_pos(&mut self, multiple: u32) { - // Compute the alignment boundary that is lower or equal - // Do everything with usize - let multiple: usize = multiple.try_into().unwrap(); - let pos = self.get_write_ptr().raw_ptr() as usize; - let remainder = pos % multiple; - let prev_aligned = pos - remainder; - - if prev_aligned == pos { - // Already aligned so do nothing - } else { - // Align by advancing - let pad = multiple - remainder; - self.set_pos(self.get_write_pos() + pad); - } - } - // Set the current write position from a pointer pub fn set_write_ptr(&mut self, code_ptr: CodePtr) { - let pos = code_ptr.into_usize() - self.mem_block.start_ptr().into_usize(); + let pos = code_ptr.into_usize() - self.mem_block.borrow().start_ptr().into_usize(); self.set_pos(pos); } /// Get a (possibly dangling) direct pointer into the executable memory block pub fn get_ptr(&self, offset: usize) -> CodePtr { - self.mem_block.start_ptr().add_bytes(offset) + self.mem_block.borrow().start_ptr().add_bytes(offset) } /// Get a (possibly dangling) direct pointer to the current write position - pub fn get_write_ptr(&mut self) -> CodePtr { + pub fn get_write_ptr(&self) -> CodePtr { self.get_ptr(self.write_pos) } /// Write a single byte at the current position. pub fn write_byte(&mut self, byte: u8) { let write_ptr = self.get_write_ptr(); - - if self.mem_block.write_byte(write_ptr, byte).is_ok() { - self.write_pos += 1; - } else { + if !self.has_capacity(1) || self.mem_block.borrow_mut().write_byte(write_ptr, byte).is_err() { self.dropped_bytes = true; } + + // Always advance write_pos since arm64 PadEntryExit needs this to stop the loop. + self.write_pos += 1; } /// Write multiple bytes starting from the current position. @@ -242,6 +372,9 @@ impl CodeBlock { self.label_refs.push(LabelRef { pos: self.write_pos, label_idx, num_bytes, encode }); // Move past however many bytes the instruction takes up + if !self.has_capacity(num_bytes) { + self.dropped_bytes = true; // retry emitting the Insn after next_page + } self.write_pos += num_bytes; } @@ -274,14 +407,43 @@ impl CodeBlock { assert!(self.label_refs.is_empty()); } + pub fn clear_labels(&mut self) { + self.label_addrs.clear(); + self.label_names.clear(); + self.label_refs.clear(); + } + + pub fn get_label_state(&self) -> LabelState { + LabelState { + label_addrs: self.label_addrs.clone(), + label_names: self.label_names.clone(), + label_refs: self.label_refs.clone(), + } + } + + pub fn set_label_state(&mut self, state: LabelState) { + self.label_addrs = state.label_addrs; + self.label_names = state.label_names; + self.label_refs = state.label_refs; + } + pub fn mark_all_executable(&mut self) { - self.mem_block.mark_all_executable(); + self.mem_block.borrow_mut().mark_all_executable(); } - #[cfg(feature = "disasm")] pub fn inline(&self) -> bool { !self.outlined } + + pub fn other_cb(&self) -> Option<&'static mut Self> { + if !CodegenGlobals::has_instance() { + None + } else if self.inline() { + Some(CodegenGlobals::get_outlined_cb().unwrap()) + } else { + Some(CodegenGlobals::get_inline_cb()) + } + } } #[cfg(test)] @@ -295,7 +457,7 @@ impl CodeBlock { let mem_start: *const u8 = alloc.mem_start(); let virt_mem = VirtualMem::new(alloc, 1, mem_start as *mut u8, mem_size); - Self::new(virt_mem, false) + Self::new(Rc::new(RefCell::new(virt_mem)), 16 * 1024, false) } } @@ -303,7 +465,7 @@ impl CodeBlock { impl fmt::LowerHex for CodeBlock { fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { for pos in 0..self.write_pos { - let byte = unsafe { self.mem_block.start_ptr().raw_ptr().add(pos).read() }; + let byte = unsafe { self.mem_block.borrow().start_ptr().raw_ptr().add(pos).read() }; fmtr.write_fmt(format_args!("{:02x}", byte))?; } Ok(()) diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 0180737d4d640b..5df072ed387ad7 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -4,7 +4,7 @@ use crate::asm::{CodeBlock}; use crate::asm::arm64::*; -use crate::codegen::{JITState}; +use crate::codegen::{JITState, CodegenGlobals}; use crate::cruby::*; use crate::backend::ir::*; use crate::virtualmem::CodePtr; @@ -36,6 +36,9 @@ pub const _C_RET_OPND: Opnd = Opnd::Reg(X0_REG); pub const C_SP_REG: A64Opnd = X31; pub const C_SP_STEP: i32 = 16; +// The number of bytes that are generated by emit_jmp_ptr +pub const JMP_PTR_BYTES: usize = 20; + /// Map Opnd to A64Opnd impl From for A64Opnd { fn from(opnd: Opnd) -> Self { @@ -567,7 +570,7 @@ impl Assembler /// Emit the required instructions to load the given value into the /// given register. Our goal here is to use as few instructions as /// possible to get this value into the register. - fn emit_load_value(cb: &mut CodeBlock, rd: A64Opnd, value: u64) -> i32 { + fn emit_load_value(cb: &mut CodeBlock, rd: A64Opnd, value: u64) -> usize { let mut current = value; if current <= 0xffff { @@ -680,6 +683,31 @@ impl Assembler ldr_post(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP)); } + fn emit_jmp_ptr(cb: &mut CodeBlock, dst_ptr: CodePtr) { + let src_addr = cb.get_write_ptr().into_i64(); + let dst_addr = dst_ptr.into_i64(); + + // If the offset is short enough, then we'll use the + // branch instruction. Otherwise, we'll move the + // destination into a register and use the branch + // register instruction. + let num_insns = if b_offset_fits_bits((dst_addr - src_addr) / 4) { + b(cb, InstructionOffset::from_bytes((dst_addr - src_addr) as i32)); + 1 + } else { + let num_insns = emit_load_value(cb, Assembler::SCRATCH0, dst_addr as u64); + br(cb, Assembler::SCRATCH0); + num_insns + 1 + }; + + // Make sure it's always a consistent number of + // instructions in case it gets patched and has to + // use the other branch. + for _ in num_insns..(JMP_PTR_BYTES / 4) { + nop(cb); + } + } + // dbg!(&self.insns); // List of GC offsets @@ -687,7 +715,13 @@ impl Assembler // For each instruction let start_write_pos = cb.get_write_pos(); - for insn in &self.insns { + let mut insn_idx: usize = 0; + while let Some(insn) = self.insns.get(insn_idx) { + let src_ptr = cb.get_write_ptr(); + let had_dropped_bytes = cb.has_dropped_bytes(); + let old_label_state = cb.get_label_state(); + let mut insn_gc_offsets: Vec = Vec::new(); + match insn { Insn::Comment(text) => { if cfg!(feature = "asm_comments") { @@ -796,7 +830,7 @@ impl Assembler cb.write_bytes(&value.as_u64().to_le_bytes()); let ptr_offset: u32 = (cb.get_write_pos() as u32) - (SIZEOF_VALUE as u32); - gc_offsets.push(ptr_offset); + insn_gc_offsets.push(ptr_offset); }, Opnd::None => { unreachable!("Attempted to load from None operand"); @@ -904,28 +938,7 @@ impl Assembler Insn::Jmp(target) => { match target { Target::CodePtr(dst_ptr) => { - let src_addr = cb.get_write_ptr().into_i64(); - let dst_addr = dst_ptr.into_i64(); - - // If the offset is short enough, then we'll use the - // branch instruction. Otherwise, we'll move the - // destination into a register and use the branch - // register instruction. - let num_insns = if b_offset_fits_bits((dst_addr - src_addr) / 4) { - b(cb, InstructionOffset::from_bytes((dst_addr - src_addr) as i32)); - 0 - } else { - let num_insns = emit_load_value(cb, Self::SCRATCH0, dst_addr as u64); - br(cb, Self::SCRATCH0); - num_insns - }; - - // Make sure it's always a consistent number of - // instructions in case it gets patched and has to - // use the other branch. - for _ in num_insns..4 { - nop(cb); - } + emit_jmp_ptr(cb, *dst_ptr); }, Target::Label(label_idx) => { // Here we're going to save enough space for @@ -997,13 +1010,21 @@ impl Assembler csel(cb, out.into(), truthy.into(), falsy.into(), Condition::GE); } Insn::LiveReg { .. } => (), // just a reg alloc signal, no code - Insn::PadEntryExit => { - let jmp_len = 5 * 4; // Op::Jmp may emit 5 instructions - while (cb.get_write_pos() - start_write_pos) < jmp_len { + Insn::PadInvalPatch => { + while (cb.get_write_pos().saturating_sub(std::cmp::max(start_write_pos, cb.page_start_pos()))) < JMP_PTR_BYTES { nop(cb); } } }; + + // On failure, jump to the next page and retry the current insn + if !had_dropped_bytes && cb.has_dropped_bytes() && cb.next_page(src_ptr, emit_jmp_ptr) { + // Reset cb states before retrying the current Insn + cb.set_label_state(old_label_state); + } else { + insn_idx += 1; + gc_offsets.append(&mut insn_gc_offsets); + } } gc_offsets @@ -1020,21 +1041,23 @@ impl Assembler assert!(label_idx == idx); } - let start_write_pos = cb.get_write_pos(); + let start_ptr = cb.get_write_ptr(); let gc_offsets = asm.arm64_emit(cb); - if !cb.has_dropped_bytes() { + if cb.has_dropped_bytes() { + cb.clear_labels(); + } else { cb.link_labels(); - } - // Invalidate icache for newly written out region so we don't run stale code. - #[cfg(not(test))] - { - let start = cb.get_ptr(start_write_pos).raw_ptr(); - let write_ptr = cb.get_write_ptr().raw_ptr(); - let codeblock_end = cb.get_ptr(cb.get_mem_size()).raw_ptr(); - let end = std::cmp::min(write_ptr, codeblock_end); - unsafe { rb_yjit_icache_invalidate(start as _, end as _) }; + // Invalidate icache for newly written out region so we don't run stale code. + // It should invalidate only the code ranges of the current cb because the code + // ranges of the other cb might have a memory region that is still PROT_NONE. + #[cfg(not(test))] + cb.without_page_end_reserve(|cb| { + for (start, end) in cb.writable_addrs(start_ptr, cb.get_write_ptr()) { + unsafe { rb_yjit_icache_invalidate(start as _, end as _) }; + } + }); } gc_offsets diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index ba7e3721884340..e11235aec964b4 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -5,6 +5,7 @@ use std::cell::Cell; use std::fmt; use std::convert::From; +use std::io::Write; use std::mem::take; use crate::cruby::{VALUE}; use crate::virtualmem::{CodePtr}; @@ -433,9 +434,9 @@ pub enum Insn { // binary OR operation. Or { left: Opnd, right: Opnd, out: Opnd }, - /// Pad nop instructions to accomodate Op::Jmp in case the block is - /// invalidated. - PadEntryExit, + /// Pad nop instructions to accomodate Op::Jmp in case the block or the insn + /// is invalidated. + PadInvalPatch, // Mark a position in the generated code PosMarker(PosMarkerFn), @@ -521,7 +522,7 @@ impl Insn { Insn::Mov { .. } => "Mov", Insn::Not { .. } => "Not", Insn::Or { .. } => "Or", - Insn::PadEntryExit => "PadEntryExit", + Insn::PadInvalPatch => "PadEntryExit", Insn::PosMarker(_) => "PosMarker", Insn::RShift { .. } => "RShift", Insn::Store { .. } => "Store", @@ -658,7 +659,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> { Insn::Jz(_) | Insn::Label(_) | Insn::LeaLabel { .. } | - Insn::PadEntryExit | + Insn::PadInvalPatch | Insn::PosMarker(_) => None, Insn::CPopInto(opnd) | Insn::CPush(opnd) | @@ -755,7 +756,7 @@ impl<'a> InsnOpndMutIterator<'a> { Insn::Jz(_) | Insn::Label(_) | Insn::LeaLabel { .. } | - Insn::PadEntryExit | + Insn::PadInvalPatch | Insn::PosMarker(_) => None, Insn::CPopInto(opnd) | Insn::CPush(opnd) | @@ -1474,8 +1475,8 @@ impl Assembler { out } - pub fn pad_entry_exit(&mut self) { - self.push_insn(Insn::PadEntryExit); + pub fn pad_inval_patch(&mut self) { + self.push_insn(Insn::PadInvalPatch); } //pub fn pos_marker(&mut self, marker_fn: F) diff --git a/yjit/src/backend/tests.rs b/yjit/src/backend/tests.rs index 1bad8642a247dd..3098c7e3b09ef5 100644 --- a/yjit/src/backend/tests.rs +++ b/yjit/src/backend/tests.rs @@ -231,7 +231,7 @@ fn test_jcc_ptr() { let (mut asm, mut cb) = setup_asm(); - let side_exit = Target::CodePtr((5 as *mut u8).into()); + let side_exit = Target::CodePtr(((cb.get_write_ptr().raw_ptr() as usize + 4) as *mut u8).into()); let not_mask = asm.not(Opnd::mem(32, EC, RUBY_OFFSET_EC_INTERRUPT_MASK)); asm.test( Opnd::mem(32, EC, RUBY_OFFSET_EC_INTERRUPT_FLAG), @@ -248,7 +248,7 @@ fn test_jmp_ptr() { let (mut asm, mut cb) = setup_asm(); - let stub = Target::CodePtr((5 as *mut u8).into()); + let stub = Target::CodePtr(((cb.get_write_ptr().raw_ptr() as usize + 4) as *mut u8).into()); asm.jmp(stub); asm.compile_with_num_regs(&mut cb, 0); @@ -259,7 +259,7 @@ fn test_jo() { let (mut asm, mut cb) = setup_asm(); - let side_exit = Target::CodePtr((5 as *mut u8).into()); + let side_exit = Target::CodePtr(((cb.get_write_ptr().raw_ptr() as usize + 4) as *mut u8).into()); let arg1 = Opnd::mem(64, SP, 0); let arg0 = Opnd::mem(64, SP, 8); diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index f6bd8227276a8f..c8aa1a0ed507d3 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -9,6 +9,7 @@ use crate::asm::x86_64::*; use crate::codegen::{JITState}; use crate::cruby::*; use crate::backend::ir::*; +use crate::codegen::CodegenGlobals; // Use the x86 register type for this platform pub type Reg = X86Reg; @@ -32,6 +33,9 @@ pub const _C_ARG_OPNDS: [Opnd; 6] = [ pub const C_RET_REG: Reg = RAX_REG; pub const _C_RET_OPND: Opnd = Opnd::Reg(RAX_REG); +// The number of bytes that are generated by jmp_ptr +pub const JMP_PTR_BYTES: usize = 6; + /// Map Opnd to X86Opnd impl From for X86Opnd { fn from(opnd: Opnd) -> Self { @@ -375,7 +379,13 @@ impl Assembler // For each instruction let start_write_pos = cb.get_write_pos(); - for insn in &self.insns { + let mut insns_idx: usize = 0; + while let Some(insn) = self.insns.get(insns_idx) { + let src_ptr = cb.get_write_ptr(); + let had_dropped_bytes = cb.has_dropped_bytes(); + let old_label_state = cb.get_label_state(); + let mut insn_gc_offsets: Vec = Vec::new(); + match insn { Insn::Comment(text) => { if cfg!(feature = "asm_comments") { @@ -461,7 +471,7 @@ impl Assembler if !val.special_const_p() { // The pointer immediate is encoded as the last part of the mov written out let ptr_offset: u32 = (cb.get_write_pos() as u32) - (SIZEOF_VALUE as u32); - gc_offsets.push(ptr_offset); + insn_gc_offsets.push(ptr_offset); } } }, @@ -651,11 +661,10 @@ impl Assembler emit_csel(cb, *truthy, *falsy, *out, cmovl); } Insn::LiveReg { .. } => (), // just a reg alloc signal, no code - Insn::PadEntryExit => { - // We assume that our Op::Jmp usage that gets invalidated is <= 5 - let code_size: u32 = (cb.get_write_pos() - start_write_pos).try_into().unwrap(); - if code_size < 5 { - nop(cb, 5 - code_size); + Insn::PadInvalPatch => { + let code_size = cb.get_write_pos().saturating_sub(std::cmp::max(start_write_pos, cb.page_start_pos())); + if code_size < JMP_PTR_BYTES { + nop(cb, (JMP_PTR_BYTES - code_size) as u32); } } @@ -666,6 +675,15 @@ impl Assembler #[allow(unreachable_patterns)] _ => panic!("unsupported instruction passed to x86 backend: {:?}", insn) }; + + // On failure, jump to the next page and retry the current insn + if !had_dropped_bytes && cb.has_dropped_bytes() && cb.next_page(src_ptr, jmp_ptr) { + // Reset cb states before retrying the current Insn + cb.set_label_state(old_label_state); + } else { + insns_idx += 1; + gc_offsets.append(&mut insn_gc_offsets); + } } gc_offsets @@ -684,7 +702,9 @@ impl Assembler let gc_offsets = asm.x86_emit(cb); - if !cb.has_dropped_bytes() { + if cb.has_dropped_bytes() { + cb.clear_labels(); + } else { cb.link_labels(); } diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 5f6d97834a9c7e..626916b24086b3 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -13,13 +13,15 @@ use crate::utils::*; use CodegenStatus::*; use InsnOpnd::*; - +use std::cell::RefCell; +use std::cell::RefMut; use std::cmp; use std::collections::HashMap; use std::ffi::CStr; use std::mem::{self, size_of}; use std::os::raw::c_uint; use std::ptr; +use std::rc::Rc; use std::slice; pub use crate::virtualmem::CodePtr; @@ -296,6 +298,7 @@ fn jit_prepare_routine_call( /// Record the current codeblock write position for rewriting into a jump into /// the outlined block later. Used to implement global code invalidation. fn record_global_inval_patch(asm: &mut Assembler, outline_block_target_pos: CodePtr) { + asm.pad_inval_patch(); asm.pos_marker(move |code_ptr| { CodegenGlobals::push_global_inval_patch(code_ptr, outline_block_target_pos); }); @@ -606,19 +609,6 @@ fn gen_pc_guard(asm: &mut Assembler, iseq: IseqPtr, insn_idx: u32) { /// Compile an interpreter entry block to be inserted into an iseq /// Returns None if compilation fails. pub fn gen_entry_prologue(cb: &mut CodeBlock, iseq: IseqPtr, insn_idx: u32) -> Option { - const MAX_PROLOGUE_SIZE: usize = 1024; - - // Check if we have enough executable memory - if !cb.has_capacity(MAX_PROLOGUE_SIZE) { - return None; - } - - let old_write_pos = cb.get_write_pos(); - - // TODO: figure out if this is actually beneficial for performance - // Align the current write position to cache line boundaries - cb.align_pos(64); - let code_ptr = cb.get_write_ptr(); let mut asm = Assembler::new(); @@ -660,10 +650,11 @@ pub fn gen_entry_prologue(cb: &mut CodeBlock, iseq: IseqPtr, insn_idx: u32) -> O asm.compile(cb); - // Verify MAX_PROLOGUE_SIZE - assert!(cb.get_write_pos() - old_write_pos <= MAX_PROLOGUE_SIZE); - - return Some(code_ptr); + if (cb.has_dropped_bytes()) { + None + } else { + Some(code_ptr) + } } // Generate code to check for interrupts and take a side-exit. @@ -853,7 +844,7 @@ pub fn gen_single_block( { let mut block = jit.block.borrow_mut(); if block.entry_exit.is_some() { - asm.pad_entry_exit(); + asm.pad_inval_patch(); } // Compile code into the code block @@ -6544,29 +6535,13 @@ static mut CODEGEN_GLOBALS: Option = None; impl CodegenGlobals { /// Initialize the codegen globals pub fn init() { - // Executable memory size in MiB - let mem_size = get_option!(exec_mem_size) * 1024 * 1024; + // Executable memory and code page size in bytes + let mem_size = get_option!(exec_mem_size); + let code_page_size = get_option!(code_page_size); #[cfg(not(test))] let (mut cb, mut ocb) = { - // TODO(alan): we can error more gracefully when the user gives - // --yjit-exec-mem=absurdly-large-number - // - // 2 GiB. It's likely a bug if we generate this much code. - const MAX_BUFFER_SIZE: usize = 2 * 1024 * 1024 * 1024; - assert!(mem_size <= MAX_BUFFER_SIZE); - let mem_size_u32 = mem_size as u32; - let half_size = mem_size / 2; - - let page_size = unsafe { rb_yjit_get_page_size() }; - let assert_page_aligned = |ptr| assert_eq!( - 0, - ptr as usize % page_size.as_usize(), - "Start of virtual address block should be page-aligned", - ); - - let virt_block: *mut u8 = unsafe { rb_yjit_reserve_addr_space(mem_size_u32) }; - let second_half = virt_block.wrapping_add(half_size); + let virt_block: *mut u8 = unsafe { rb_yjit_reserve_addr_space(mem_size as u32) }; // Memory protection syscalls need page-aligned addresses, so check it here. Assuming // `virt_block` is page-aligned, `second_half` should be page-aligned as long as the @@ -6575,26 +6550,25 @@ impl CodegenGlobals { // // Basically, we don't support x86-64 2MiB and 1GiB pages. ARMv8 can do up to 64KiB // (2¹⁶ bytes) pages, which should be fine. 4KiB pages seem to be the most popular though. - assert_page_aligned(virt_block); - assert_page_aligned(second_half); + let page_size = unsafe { rb_yjit_get_page_size() }; + assert_eq!( + virt_block as usize % page_size.as_usize(), 0, + "Start of virtual address block should be page-aligned", + ); + assert_eq!(code_page_size % page_size.as_usize(), 0, "code_page_size was not page-aligned"); use crate::virtualmem::*; - let first_half = VirtualMem::new( + let mem_block = VirtualMem::new( SystemAllocator {}, page_size, virt_block, - half_size - ); - let second_half = VirtualMem::new( - SystemAllocator {}, - page_size, - second_half, - half_size + mem_size, ); + let mem_block = Rc::new(RefCell::new(mem_block)); - let cb = CodeBlock::new(first_half, false); - let ocb = OutlinedCb::wrap(CodeBlock::new(second_half, true)); + let cb = CodeBlock::new(mem_block.clone(), code_page_size, false); + let ocb = OutlinedCb::wrap(CodeBlock::new(mem_block, code_page_size, true)); (cb, ocb) }; @@ -6702,6 +6676,10 @@ impl CodegenGlobals { unsafe { CODEGEN_GLOBALS.as_mut().unwrap() } } + pub fn has_instance() -> bool { + unsafe { CODEGEN_GLOBALS.as_mut().is_some() } + } + /// Get a mutable reference to the inline code block pub fn get_inline_cb() -> &'static mut CodeBlock { &mut CodegenGlobals::get_instance().inline_cb diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 3cecf31a85d06c..53cb31beb146dc 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -665,7 +665,7 @@ pub extern "C" fn rb_yjit_iseq_update_references(payload: *mut c_void) { if new_addr != object { for (byte_idx, &byte) in new_addr.as_u64().to_le_bytes().iter().enumerate() { let byte_code_ptr = value_code_ptr.add_bytes(byte_idx); - cb.get_mem().write_byte(byte_code_ptr, byte) + cb.write_mem(byte_code_ptr, byte) .expect("patching existing code should be within bounds"); } } @@ -1916,7 +1916,9 @@ pub fn gen_branch( // Call the branch generation function asm.mark_branch_start(&branchref); - gen_fn(asm, branch.dst_addrs[0].unwrap(), branch.dst_addrs[1], BranchShape::Default); + if let Some(dst_addr) = branch.dst_addrs[0] { + gen_fn(asm, dst_addr, branch.dst_addrs[1], BranchShape::Default); + } asm.mark_branch_end(&branchref); } @@ -1955,6 +1957,7 @@ pub fn gen_direct_jump(jit: &JITState, ctx: &Context, target0: BlockId, asm: &mu branch.shape = BranchShape::Default; // Call the branch generation function + asm.comment("gen_direct_jmp: existing block"); asm.mark_branch_start(&branchref); gen_jump_branch(asm, branch.dst_addrs[0].unwrap(), None, BranchShape::Default); asm.mark_branch_end(&branchref); @@ -1965,6 +1968,7 @@ pub fn gen_direct_jump(jit: &JITState, ctx: &Context, target0: BlockId, asm: &mu branch.shape = BranchShape::Next0; // The branch is effectively empty (a noop) + asm.comment("gen_direct_jmp: fallthrough"); asm.mark_branch_start(&branchref); asm.mark_branch_end(&branchref); } @@ -2003,7 +2007,9 @@ pub fn defer_compilation( // Call the branch generation function asm.mark_branch_start(&branch_rc); - gen_jump_branch(asm, branch.dst_addrs[0].unwrap(), None, BranchShape::Default); + if let Some(dst_addr) = branch.dst_addrs[0] { + gen_jump_branch(asm, dst_addr, None, BranchShape::Default); + } asm.mark_branch_end(&branch_rc); } diff --git a/yjit/src/options.rs b/yjit/src/options.rs index f73dca67de21bb..d2b43ecb262401 100644 --- a/yjit/src/options.rs +++ b/yjit/src/options.rs @@ -4,9 +4,14 @@ use std::ffi::CStr; #[derive(Clone, PartialEq, Eq, Debug)] #[repr(C)] pub struct Options { - // Size of the executable memory block to allocate in MiB + // Size of the executable memory block to allocate in bytes + // Note that the command line argument is expressed in MiB and not bytes pub exec_mem_size: usize, + // Size of each executable memory code page in bytes + // Note that the command line argument is expressed in KiB and not bytes + pub code_page_size: usize, + // Number of method calls after which to start generating code // Threshold==1 means compile on first execution pub call_threshold: usize, @@ -48,7 +53,8 @@ pub struct Options { // Initialize the options to default values pub static mut OPTIONS: Options = Options { - exec_mem_size: 256, + exec_mem_size: 256 * 1024 * 1024, + code_page_size: 16 * 1024, call_threshold: 10, greedy_versioning: false, no_type_prop: false, @@ -118,8 +124,30 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { match (opt_name, opt_val) { ("", "") => (), // Simply --yjit - ("exec-mem-size", _) => match opt_val.parse() { - Ok(n) => unsafe { OPTIONS.exec_mem_size = n }, + ("exec-mem-size", _) => match opt_val.parse::() { + Ok(n) => { + if n == 0 || n > 2 * 1024 * 1024 { + return None + } + + // Convert from MiB to bytes internally for convenience + unsafe { OPTIONS.exec_mem_size = n * 1024 * 1024 } + } + Err(_) => { + return None; + } + }, + + ("code-page-size", _) => match opt_val.parse::() { + Ok(n) => { + // Enforce bounds checks and that n is divisible by 4KiB + if n < 4 || n > 256 || n % 4 != 0 { + return None + } + + // Convert from KiB to bytes internally for convenience + unsafe { OPTIONS.code_page_size = n * 1024 } + } Err(_) => { return None; } diff --git a/yjit/src/utils.rs b/yjit/src/utils.rs index cabebb7dccda4c..b156c9d5eda721 100644 --- a/yjit/src/utils.rs +++ b/yjit/src/utils.rs @@ -74,14 +74,13 @@ pub(crate) use offset_of; // This should work fine on ASCII strings and anything else // that is considered legal UTF-8, including embedded nulls. fn ruby_str_to_rust(v: VALUE) -> String { - // Make sure the CRuby encoding is UTF-8 compatible - let encoding = unsafe { rb_ENCODING_GET(v) } as u32; - assert!(encoding == RUBY_ENCINDEX_ASCII_8BIT || encoding == RUBY_ENCINDEX_UTF_8 || encoding == RUBY_ENCINDEX_US_ASCII); - let str_ptr = unsafe { rb_RSTRING_PTR(v) } as *mut u8; let str_len: usize = unsafe { rb_RSTRING_LEN(v) }.try_into().unwrap(); let str_slice: &[u8] = unsafe { slice::from_raw_parts(str_ptr, str_len) }; - String::from_utf8(str_slice.to_vec()).unwrap() // does utf8 validation + match String::from_utf8(str_slice.to_vec()) { + Ok(utf8) => utf8, + Err(_) => String::new(), + } } // Location is the file defining the method, colon, method name. From f11765aef0cafb6fadf98216c94507f2390a0a70 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 17 Oct 2022 10:47:22 -0700 Subject: [PATCH 100/139] YJIT: Allow --yjit-dump-disasm to dump into a file (#6552) * YJIT: Allow --yjit-dump-disasm to dump into a file * YJIT: Move IO implementation to disasm.rs * YJIT: More consistent naming --- yjit/src/backend/ir.rs | 11 ++++------- yjit/src/codegen.rs | 4 ++-- yjit/src/disasm.rs | 22 ++++++++++++++++++++-- yjit/src/options.rs | 32 ++++++++++++++------------------ 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index e11235aec964b4..df0600b2fff70c 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -1100,13 +1100,10 @@ impl Assembler let gc_offsets = self.compile_with_regs(cb, alloc_regs); #[cfg(feature = "disasm")] - if get_option!(dump_disasm) == DumpDisasm::All || (get_option!(dump_disasm) == DumpDisasm::Inline && cb.inline()) { - use crate::disasm::disasm_addr_range; - let last_ptr = cb.get_write_ptr(); - let disasm = disasm_addr_range(cb, start_addr, last_ptr.raw_ptr() as usize - start_addr as usize); - if disasm.len() > 0 { - println!("{disasm}"); - } + if let Some(dump_disasm) = get_option_ref!(dump_disasm) { + use crate::disasm::dump_disasm_addr_range; + let end_addr = cb.get_write_ptr().raw_ptr(); + dump_disasm_addr_range(cb, start_addr, end_addr, dump_disasm) } gc_offsets } diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 626916b24086b3..ae01149081789f 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -612,7 +612,7 @@ pub fn gen_entry_prologue(cb: &mut CodeBlock, iseq: IseqPtr, insn_idx: u32) -> O let code_ptr = cb.get_write_ptr(); let mut asm = Assembler::new(); - if get_option!(dump_disasm).is_enabled() { + if get_option_ref!(dump_disasm).is_some() { asm.comment(&format!("YJIT entry: {}", iseq_get_location(iseq))); } else { asm.comment("YJIT entry"); @@ -742,7 +742,7 @@ pub fn gen_single_block( let mut asm = Assembler::new(); #[cfg(feature = "disasm")] - if get_option!(dump_disasm).is_enabled() { + if get_option_ref!(dump_disasm).is_some() { asm.comment(&format!("Block: {} (ISEQ offset: {})", iseq_get_location(blockid.iseq), blockid.idx)); } diff --git a/yjit/src/disasm.rs b/yjit/src/disasm.rs index 9e45dffd60e834..21ab6b5507e9be 100644 --- a/yjit/src/disasm.rs +++ b/yjit/src/disasm.rs @@ -2,6 +2,7 @@ use crate::core::*; use crate::cruby::*; use crate::yjit::yjit_enabled_p; use crate::asm::CodeBlock; +use crate::options::DumpDisasm; use std::fmt::Write; @@ -95,7 +96,7 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St writeln!(out, "== {:=<60}", block_ident).unwrap(); // Disassemble the instructions - out.push_str(&disasm_addr_range(global_cb, start_addr, code_size)); + out.push_str(&disasm_addr_range(global_cb, start_addr, (start_addr as usize + code_size) as *const u8)); // If this is not the last block if block_idx < block_list.len() - 1 { @@ -115,9 +116,25 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St return out; } +#[cfg(feature = "disasm")] +pub fn dump_disasm_addr_range(cb: &CodeBlock, start_addr: *const u8, end_addr: *const u8, dump_disasm: &DumpDisasm) { + use std::fs::File; + use std::io::Write; + + let disasm = disasm_addr_range(cb, start_addr, end_addr); + if disasm.len() > 0 { + match dump_disasm { + DumpDisasm::Stdout => println!("{disasm}"), + DumpDisasm::File(path) => { + let mut f = File::options().append(true).create(true).open(path).unwrap(); + f.write_all(disasm.as_bytes()).unwrap(); + } + }; + } +} #[cfg(feature = "disasm")] -pub fn disasm_addr_range(cb: &CodeBlock, start_addr: *const u8, code_size: usize) -> String { +pub fn disasm_addr_range(cb: &CodeBlock, start_addr: *const u8, end_addr: *const u8) -> String { let mut out = String::from(""); // Initialize capstone @@ -141,6 +158,7 @@ pub fn disasm_addr_range(cb: &CodeBlock, start_addr: *const u8, code_size: usize cs.set_skipdata(true).unwrap(); // Disassemble the instructions + let code_size = end_addr as usize - start_addr as usize; let code_slice = unsafe { std::slice::from_raw_parts(start_addr, code_size) }; let insns = cs.disasm_all(code_slice, start_addr as u64).unwrap(); diff --git a/yjit/src/options.rs b/yjit/src/options.rs index d2b43ecb262401..303ae4980f5fbb 100644 --- a/yjit/src/options.rs +++ b/yjit/src/options.rs @@ -36,7 +36,7 @@ pub struct Options { pub dump_insns: bool, /// Dump all compiled instructions of target cbs. - pub dump_disasm: DumpDisasm, + pub dump_disasm: Option, /// Print when specific ISEQ items are compiled or invalidated pub dump_iseq_disasm: Option, @@ -62,26 +62,18 @@ pub static mut OPTIONS: Options = Options { gen_stats: false, gen_trace_exits: false, dump_insns: false, - dump_disasm: DumpDisasm::None, + dump_disasm: None, verify_ctx: false, global_constant_state: false, dump_iseq_disasm: None, }; -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub enum DumpDisasm { - // Dump only inline cb - Inline, - // Dump both inline and outlined cbs - All, - // Dont dump anything - None, -} - -impl DumpDisasm { - pub fn is_enabled(&self) -> bool { - *self != DumpDisasm::None - } + // Dump to stdout + Stdout, + // Dump to "yjit_{pid}.log" file under the specified directory + File(String), } /// Macro to get an option value by name @@ -168,9 +160,13 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { }, ("dump-disasm", _) => match opt_val.to_string().as_str() { - "all" => unsafe { OPTIONS.dump_disasm = DumpDisasm::All }, - "" => unsafe { OPTIONS.dump_disasm = DumpDisasm::Inline }, - _ => return None, + "" => unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::Stdout) }, + directory => { + let pid = std::process::id(); + let path = format!("{directory}/yjit_{pid}.log"); + println!("YJIT disasm dump: {path}"); + unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::File(path)) } + } }, ("dump-iseq-disasm", _) => unsafe { From 1c83469fc6e6bde8781e09ce2844231feed9b170 Mon Sep 17 00:00:00 2001 From: st0012 Date: Fri, 7 Oct 2022 23:19:03 +0100 Subject: [PATCH 101/139] [ruby/irb] Remove unnecessary coloring support check https://github.com/ruby/irb/commit/ddd7dbe2c5 --- test/irb/test_color.rb | 47 +++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb index 8de03a5e8e509e..34af4e2beafc42 100644 --- a/test/irb/test_color.rb +++ b/test/irb/test_color.rb @@ -140,23 +140,19 @@ def test_colorize_code end tests.each do |code, result| - if colorize_code_supported? - assert_equal_with_term(result, code, complete: true) - assert_equal_with_term(result, code, complete: false) + assert_equal_with_term(result, code, complete: true) + assert_equal_with_term(result, code, complete: false) - assert_equal_with_term(code, code, complete: true, tty: false) - assert_equal_with_term(code, code, complete: false, tty: false) + assert_equal_with_term(code, code, complete: true, tty: false) + assert_equal_with_term(code, code, complete: false, tty: false) - assert_equal_with_term(code, code, complete: true, colorable: false) + assert_equal_with_term(code, code, complete: true, colorable: false) - assert_equal_with_term(code, code, complete: false, colorable: false) + assert_equal_with_term(code, code, complete: false, colorable: false) - assert_equal_with_term(result, code, complete: true, tty: false, colorable: true) + assert_equal_with_term(result, code, complete: true, tty: false, colorable: true) - assert_equal_with_term(result, code, complete: false, tty: false, colorable: true) - else - assert_equal_with_term(code, code) - end + assert_equal_with_term(result, code, complete: false, tty: false, colorable: true) end end @@ -186,26 +182,22 @@ def test_colorize_code_complete_false "'foo' + 'bar" => "#{RED}#{BOLD}'#{CLEAR}#{RED}foo#{CLEAR}#{RED}#{BOLD}'#{CLEAR} + #{RED}#{BOLD}'#{CLEAR}#{RED}bar#{CLEAR}", "('foo" => "(#{RED}#{BOLD}'#{CLEAR}#{RED}foo#{CLEAR}", }.each do |code, result| - if colorize_code_supported? - assert_equal_with_term(result, code, complete: false) + assert_equal_with_term(result, code, complete: false) - assert_equal_with_term(code, code, complete: false, tty: false) + assert_equal_with_term(code, code, complete: false, tty: false) - assert_equal_with_term(code, code, complete: false, colorable: false) + assert_equal_with_term(code, code, complete: false, colorable: false) - assert_equal_with_term(result, code, complete: false, tty: false, colorable: true) + assert_equal_with_term(result, code, complete: false, tty: false, colorable: true) - unless complete_option_supported? - assert_equal_with_term(result, code, complete: true) + unless complete_option_supported? + assert_equal_with_term(result, code, complete: true) - assert_equal_with_term(code, code, complete: true, tty: false) + assert_equal_with_term(code, code, complete: true, tty: false) - assert_equal_with_term(code, code, complete: true, colorable: false) + assert_equal_with_term(code, code, complete: true, colorable: false) - assert_equal_with_term(result, code, complete: true, tty: false, colorable: true) - end - else - assert_equal_with_term(code, code) + assert_equal_with_term(result, code, complete: true, tty: false, colorable: true) end end end @@ -232,11 +224,6 @@ def test_inspect_colorable private - # `#colorize_code` is supported only for Ruby 2.5+. It just returns the original code in 2.4-. - def colorize_code_supported? - Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0') - end - # `complete: true` is the same as `complete: false` in Ruby 2.6- def complete_option_supported? Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7.0') From 1865ed4ab938a9979b3a310800a37c25e468c564 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 17 Oct 2022 14:32:44 -0700 Subject: [PATCH 102/139] Fix an outdated notification message https://github.com/ruby/ruby/commit/0d7292ec734d49def118959d6a80588e7c0f7a39 --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 9d3cb3f6137bb5..a2cdb09ac7b018 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -118,7 +118,7 @@ notifications: {{^isPullRequest}} { "ci": "AppVeyor CI", - "env": "Visual Studio 2013 / 2015", + "env": "Visual Studio 2013", "url": "{{buildUrl}}", "commit": "{{commitId}}", "branch": "{{branch}}" From b54c8ba8fc1a03446fa9ecfd7995ba8fafd20a3e Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Mon, 17 Oct 2022 14:31:24 -0400 Subject: [PATCH 103/139] Simplified rb_obj_copy_ivar implementation --- object.c | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/object.c b/object.c index 929968c4cbf13e..d0c0e34f82ae96 100644 --- a/object.c +++ b/object.c @@ -268,37 +268,21 @@ rb_obj_singleton_class(VALUE obj) MJIT_FUNC_EXPORTED void rb_obj_copy_ivar(VALUE dest, VALUE obj) { - VALUE *dest_buf = ROBJECT_IVPTR(dest); - VALUE *src_buf = ROBJECT_IVPTR(obj); uint32_t dest_len = ROBJECT_NUMIV(dest); uint32_t src_len = ROBJECT_NUMIV(obj); - uint32_t max_len = dest_len < src_len ? src_len : dest_len; - rb_ensure_iv_list_size(dest, dest_len, max_len); - - dest_len = ROBJECT_NUMIV(dest); - uint32_t min_len = dest_len > src_len ? src_len : dest_len; - - if (RBASIC(obj)->flags & ROBJECT_EMBED) { - src_buf = ROBJECT(obj)->as.ary; - - // embedded -> embedded - if (RBASIC(dest)->flags & ROBJECT_EMBED) { - dest_buf = ROBJECT(dest)->as.ary; - } - // embedded -> extended - else { - dest_buf = ROBJECT(dest)->as.heap.ivptr; - } + if (dest_len < src_len) { + rb_ensure_iv_list_size(dest, dest_len, src_len); + RUBY_ASSERT(!(RBASIC(dest)->flags & ROBJECT_EMBED)); } - // extended -> extended else { - RUBY_ASSERT(!(RBASIC(dest)->flags & ROBJECT_EMBED)); - dest_buf = ROBJECT(dest)->as.heap.ivptr; - src_buf = ROBJECT(obj)->as.heap.ivptr; + RUBY_ASSERT((RBASIC(dest)->flags & ROBJECT_EMBED)); } - MEMCPY(dest_buf, src_buf, VALUE, min_len); + VALUE * dest_buf = ROBJECT_IVPTR(dest); + VALUE * src_buf = ROBJECT_IVPTR(obj); + + MEMCPY(dest_buf, src_buf, VALUE, ROBJECT_IV_COUNT(obj)); } static void From 995bdd69de0ce0ab5cec93c5818cc28912657458 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 17 Oct 2022 15:37:56 -0700 Subject: [PATCH 104/139] Try --x-use-aria2 for better retries (#6574) to possibly deal with failures like https://ci.appveyor.com/project/ruby/ruby/builds/45097615. I'm not sure how vcpkg retries things, but at least aria2c seems to attempt downloads 5 times by default. https://aria2.github.io/manual/en/html/aria2c.html Looking at the CI log, vcpkg might be already trying it 5 times, but let me give it a try too. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index a2cdb09ac7b018..4eca17a210ded3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -46,7 +46,7 @@ for: - git pull -q - .\bootstrap-vcpkg.bat - cd %APPVEYOR_BUILD_FOLDER% - - vcpkg --triplet %Platform%-windows install libffi libyaml readline zlib + - vcpkg --triplet %Platform%-windows install --x-use-aria2 libffi libyaml readline zlib - CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat - SET vcvars - '"%vcvars%" %Platform:x64=amd64%' From 329d5424a479bb08e75bd750c51a5382e382731c Mon Sep 17 00:00:00 2001 From: Hiroshi Shirosaki Date: Tue, 18 Oct 2022 09:18:03 +0900 Subject: [PATCH 105/139] [Bug #19042] Fix Dir.glob brace with '/' Dir.glob brace pattern with '/' after '**' does not match paths in recursive expansion process. We expand braces with '/' before expanding a recursive. Co-authored-by: Nobuyoshi Nakada --- dir.c | 2 +- test/ruby/test_dir.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/dir.c b/dir.c index a3ea5eea50a6b3..a35aace6f1d3c6 100644 --- a/dir.c +++ b/dir.c @@ -2305,7 +2305,7 @@ glob_helper( #endif break; case BRACE: - if (!recursive) { + if (!recursive || strchr(p->str, '/')) { brace = 1; } break; diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb index 514c6e5921ed56..ebbed754457841 100644 --- a/test/ruby/test_dir.rb +++ b/test/ruby/test_dir.rb @@ -259,6 +259,20 @@ def test_glob_starts_with_brace end end + def test_glob_recursive_with_brace + Dir.chdir(@root) do + bug19042 = '[ruby-core:110220] [Bug #19042]' + %w"c/dir_a c/dir_b c/dir_b/dir".each do |d| + Dir.mkdir(d) + end + expected = %w"c/dir_a/file c/dir_b/dir/file" + expected.each do |f| + File.write(f, "") + end + assert_equal(expected, Dir.glob("**/{dir_a,dir_b/dir}/file"), bug19042) + end + end + def test_glob_order Dir.chdir(@root) do assert_equal(["#{@root}/a", "#{@root}/b"], Dir.glob("#{@root}/[ba]")) From ab3b1b23811c44f56f3dff318b08a9112cba3582 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 18 Oct 2022 11:30:56 +0900 Subject: [PATCH 106/139] sync_default_gems.rb: fix links to GitHub issues - Substitute `GH-xxxx` which does not contain `#` too. - Split each substitutions. --- tool/sync_default_gems.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index d585f5f14aeae7..a5f8c7fd095094 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -422,13 +422,16 @@ def message_filter(repo, sha) log = STDIN.read log.delete!("\r") url = "https://github.com/#{repo}" - print "[#{repo}] ", log.gsub(/\b(?:(?i:fix(?:e[sd])?) +|GH-)\K#(?=\d+\b)|\(\K#(?=\d+\))/) { + log.gsub!(/\b(?:(?i:fix(?:e[sd])?) +)\K#(?=\d+\b)|\bGH-#?(?=\d+\b)|\(\K#(?=\d+\))/) { "#{url}/pull/" - }.gsub(%r{(? Date: Mon, 17 Oct 2022 21:16:01 -0700 Subject: [PATCH 107/139] ruby/ruby-commit-hook has been renamed [ci skip] to ruby/git.ruby-lang.org --- tool/gen-mailmap.rb | 2 +- tool/sync_default_gems.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/gen-mailmap.rb b/tool/gen-mailmap.rb index 27b7abf8de0929..df1884520c0c60 100755 --- a/tool/gen-mailmap.rb +++ b/tool/gen-mailmap.rb @@ -3,7 +3,7 @@ require "open-uri" require "yaml" -EMAIL_YML_URL = "https://cdn.jsdelivr.net/gh/ruby/ruby-commit-hook/config/email.yml" +EMAIL_YML_URL = "https://cdn.jsdelivr.net/gh/ruby/git.ruby-lang.org/config/email.yml" email_yml = URI(EMAIL_YML_URL).read.sub(/\A(?:#.*\n)+/, "").gsub(/^# +(.+)$/) { $1 + ": []" } diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index a5f8c7fd095094..9fcbeb7d22e82c 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -434,7 +434,7 @@ def message_filter(repo, sha) print "[#{repo}] ", log end -# NOTE: This method is also used by ruby-commit-hook/bin/update-default-gem.sh +# NOTE: This method is also used by GitHub ruby/git.ruby-lang.org's bin/update-default-gem.sh # @param gem [String] A gem name, also used as a git remote name. REPOSITORIES converts it to the appropriate GitHub repository. # @param ranges [Array] "before..after". Note that it will NOT sync "before" (but commits after that). # @param edit [TrueClass] Set true if you want to resolve conflicts. Obviously, update-default-gem.sh doesn't use this. From 344e6c915f41d99df024c7e90403baca0d5213a5 Mon Sep 17 00:00:00 2001 From: tomoya ishida Date: Tue, 18 Oct 2022 14:30:29 +0900 Subject: [PATCH 108/139] [ruby/irb] Fix code terminated check with heredoc and backtick (https://github.com/ruby/irb/pull/390) * Fix backtick method def method call handled as backtick open * Fix handling heredoc in check_string_literal * Sort result of lexer.parse by pos in ruby<2.7. It's not sorted when the given code includes heredoc. * Update lib/irb/ruby-lex.rb Co-authored-by: Stan Lo * Update lib/irb/ruby-lex.rb Co-authored-by: Stan Lo * Add check_string_literal test for heredoc code that does not end with newline https://github.com/ruby/irb/commit/44bc712460 Co-authored-by: Stan Lo --- lib/irb/ruby-lex.rb | 22 ++++++++++++++------ test/irb/test_ruby_lex.rb | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb index 8f629331dba3b8..cb6d669a727abc 100644 --- a/lib/irb/ruby-lex.rb +++ b/lib/irb/ruby-lex.rb @@ -162,7 +162,7 @@ def self.ripper_lex_without_warning(code, context: nil) end end else - lexer.parse.reject { |it| it.pos.first == 0 } + lexer.parse.reject { |it| it.pos.first == 0 }.sort_by(&:pos) end end ensure @@ -706,6 +706,7 @@ def check_string_literal(tokens) i = 0 start_token = [] end_type = [] + pending_heredocs = [] while i < tokens.size t = tokens[i] case t.event @@ -729,18 +730,27 @@ def check_string_literal(tokens) end end when :on_backtick - start_token << t - end_type << :on_tstring_end + if t.state.allbits?(Ripper::EXPR_BEG) + start_token << t + end_type << :on_tstring_end + end when :on_qwords_beg, :on_words_beg, :on_qsymbols_beg, :on_symbols_beg start_token << t end_type << :on_tstring_end when :on_heredoc_beg - start_token << t - end_type << :on_heredoc_end + pending_heredocs << t + end + + if pending_heredocs.any? && t.tok.include?("\n") + pending_heredocs.reverse_each do |t| + start_token << t + end_type << :on_heredoc_end + end + pending_heredocs = [] end i += 1 end - start_token.last.nil? ? nil : start_token.last + pending_heredocs.first || start_token.last end def process_literal_type(tokens = @tokens) diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb index 2c94a36a5d91d9..beda53fc8997f0 100644 --- a/test/irb/test_ruby_lex.rb +++ b/test/irb/test_ruby_lex.rb @@ -170,6 +170,40 @@ def test_endless_range_at_end_of_line assert_dynamic_prompt(lines, expected_prompt_list) end + def test_heredoc_with_embexpr + input_with_prompt = [ + PromptRow.new('001:0:":* ', %q(< ', %q(])), + PromptRow.new('012:0: :* ', %q()), + ] + + lines = input_with_prompt.map(&:content) + expected_prompt_list = input_with_prompt.map(&:prompt) + assert_dynamic_prompt(lines, expected_prompt_list) + end + + def test_backtick_method + input_with_prompt = [ + PromptRow.new('001:0: :> ', %q(self.`(arg))), + PromptRow.new('002:0: :* ', %q()), + PromptRow.new('003:0: :> ', %q(def `(); end)), + PromptRow.new('004:0: :* ', %q()), + ] + + lines = input_with_prompt.map(&:content) + expected_prompt_list = input_with_prompt.map(&:prompt) + assert_dynamic_prompt(lines, expected_prompt_list) + end + def test_incomplete_coding_magic_comment input_with_correct_indents = [ Row.new(%q(#coding:u), nil, 0), @@ -632,5 +666,13 @@ def test_unterminated_code assert_empty(error_tokens, 'Error tokens must be ignored if there is corresponding non-error token') end end + + def test_unterminated_heredoc_string_literal + ['< Date: Tue, 18 Oct 2022 14:44:04 +0900 Subject: [PATCH 109/139] [ruby/irb] Always use local variables in current context to parse code (https://github.com/ruby/irb/pull/397) * Use local_variables for colorize, code_block_open check, nesting_level and assignment_expression check * Check if expression is an assignment BEFORE evaluating it. evaluate might define new localvars and change result of assignment_expression? * Add local_variables dependent code test * pend local variable dependent test on truffleruby code_block_open is not working on truffleruby * Always pass context to RubyLex#lex * Rename local_variable_assign_code generator method name * Add assignment expression truncate test * Add Context#local_variables and make generate_local_variables_assign_code more simple * Update lib/irb/input-method.rb Co-authored-by: Stan Lo * Add a comment why assignment expression check should be done before evaluate https://github.com/ruby/irb/commit/c8b3877281 Co-authored-by: Stan Lo Co-authored-by: Takashi Kokubun --- lib/irb.rb | 13 +++++++--- lib/irb/color.rb | 13 +++++++--- lib/irb/context.rb | 4 +++ lib/irb/input-method.rb | 3 ++- lib/irb/ruby-lex.rb | 33 ++++++++++++------------ test/irb/test_color.rb | 11 ++++++++ test/irb/test_context.rb | 10 +++++++ test/irb/test_ruby_lex.rb | 31 +++++++++++++++++++--- test/irb/yamatanooroti/test_rendering.rb | 19 ++++++++++++++ 9 files changed, 109 insertions(+), 28 deletions(-) diff --git a/lib/irb.rb b/lib/irb.rb index 0c145069c06668..749f3ee167efc0 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -506,13 +506,15 @@ def eval_input @scanner.set_auto_indent(@context) if @context.auto_indent_mode - @scanner.each_top_level_statement do |line, line_no| + @scanner.each_top_level_statement(@context) do |line, line_no| signal_status(:IN_EVAL) do begin line.untaint if RUBY_VERSION < '2.7' if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty? IRB.set_measure_callback end + # Assignment expression check should be done before @context.evaluate to handle code like `a /2#/ if false; a = 1` + is_assignment = assignment_expression?(line) if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty? result = nil last_proc = proc{ result = @context.evaluate(line, line_no, exception: exc) } @@ -529,7 +531,7 @@ def eval_input @context.evaluate(line, line_no, exception: exc) end if @context.echo? - if assignment_expression?(line) + if is_assignment if @context.echo_on_assignment? output_value(@context.echo_on_assignment? == :truncate) end @@ -827,9 +829,12 @@ def assignment_expression?(line) # array of parsed expressions. The first element of each expression is the # expression's type. verbose, $VERBOSE = $VERBOSE, nil - result = ASSIGNMENT_NODE_TYPES.include?(Ripper.sexp(line)&.dig(1,-1,0)) + code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{line}" + # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part. + node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0) + ASSIGNMENT_NODE_TYPES.include?(node_type) + ensure $VERBOSE = verbose - result end ATTR_TTY = "\e[%sm" diff --git a/lib/irb/color.rb b/lib/irb/color.rb index 7071696cb29c7c..34912420e42b65 100644 --- a/lib/irb/color.rb +++ b/lib/irb/color.rb @@ -123,13 +123,15 @@ def colorize(text, seq, colorable: colorable?) # If `complete` is false (code is incomplete), this does not warn compile_error. # This option is needed to avoid warning a user when the compile_error is happening # because the input is not wrong but just incomplete. - def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?) + def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: []) return code unless colorable symbol_state = SymbolState.new colored = +'' + lvars_code = RubyLex.generate_local_variables_assign_code(local_variables) + code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code - scan(code, allow_last_error: !complete) do |token, str, expr| + scan(code_with_lvars, allow_last_error: !complete) do |token, str, expr| # handle uncolorable code if token.nil? colored << Reline::Unicode.escape_for_print(str) @@ -152,7 +154,12 @@ def colorize_code(code, complete: true, ignore_error: false, colorable: colorabl end end end - colored + + if lvars_code + colored.sub(/\A.+\n/, '') + else + colored + end end private diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 5e07f5dfb0ef87..d238da9350b0ef 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -518,5 +518,9 @@ def inspect # :nodoc: end alias __to_s__ to_s alias to_s inspect + + def local_variables # :nodoc: + workspace.binding.local_variables + end end end diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index aa5cb5adb97e01..b0110dd09b295c 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -286,7 +286,8 @@ def initialize if IRB.conf[:USE_COLORIZE] proc do |output, complete: | next unless IRB::Color.colorable? - IRB::Color.colorize_code(output, complete: complete) + lvars = IRB.CurrentContext&.local_variables || [] + IRB::Color.colorize_code(output, complete: complete, local_variables: lvars) end else proc do |output| diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb index cb6d669a727abc..54ea2a9e7b390a 100644 --- a/lib/irb/ruby-lex.rb +++ b/lib/irb/ruby-lex.rb @@ -136,16 +136,18 @@ def set_prompt(p = nil, &block) :on_param_error ] + def self.generate_local_variables_assign_code(local_variables) + "#{local_variables.join('=')}=nil;" unless local_variables.empty? + end + def self.ripper_lex_without_warning(code, context: nil) verbose, $VERBOSE = $VERBOSE, nil - if context - lvars = context.workspace&.binding&.local_variables - if lvars && !lvars.empty? - code = "#{lvars.join('=')}=nil\n#{code}" - line_no = 0 - else - line_no = 1 - end + lvars_code = generate_local_variables_assign_code(context&.local_variables || []) + if lvars_code + code = "#{lvars_code}\n#{code}" + line_no = 0 + else + line_no = 1 end compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no| @@ -214,6 +216,8 @@ def check_state(code, tokens = nil, context: nil) ltype = process_literal_type(tokens) indent = process_nesting_level(tokens) continue = process_continue(tokens) + lvars_code = self.class.generate_local_variables_assign_code(context&.local_variables || []) + code = "#{lvars_code}\n#{code}" if lvars_code code_block_open = check_code_block(code, tokens) [ltype, indent, continue, code_block_open] end @@ -233,13 +237,13 @@ def initialize_input @code_block_open = false end - def each_top_level_statement + def each_top_level_statement(context) initialize_input catch(:TERM_INPUT) do loop do begin prompt - unless l = lex + unless l = lex(context) throw :TERM_INPUT if @line == '' else @line_no += l.count("\n") @@ -269,18 +273,15 @@ def each_top_level_statement end end - def lex + def lex(context) line = @input.call if @io.respond_to?(:check_termination) return line # multiline end code = @line + (line.nil? ? '' : line) code.gsub!(/\s*\z/, '').concat("\n") - @tokens = self.class.ripper_lex_without_warning(code) - @continue = process_continue - @code_block_open = check_code_block(code) - @indent = process_nesting_level - @ltype = process_literal_type + @tokens = self.class.ripper_lex_without_warning(code, context: context) + @ltype, @indent, @continue, @code_block_open = check_state(code, @tokens, context: context) line end diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb index 34af4e2beafc42..02c79e344376f4 100644 --- a/test/irb/test_color.rb +++ b/test/irb/test_color.rb @@ -156,6 +156,17 @@ def test_colorize_code end end + def test_colorize_code_with_local_variables + code = "a /(b +1)/i" + result_without_lvars = "a #{RED}#{BOLD}/#{CLEAR}#{RED}(b +1)#{CLEAR}#{RED}#{BOLD}/i#{CLEAR}" + result_with_lvar = "a /(b #{BLUE}#{BOLD}+1#{CLEAR})/i" + result_with_lvars = "a /(b +#{BLUE}#{BOLD}1#{CLEAR})/i" + + assert_equal_with_term(result_without_lvars, code) + assert_equal_with_term(result_with_lvar, code, local_variables: ['a']) + assert_equal_with_term(result_with_lvars, code, local_variables: ['a', 'b']) + end + def test_colorize_code_complete_true unless complete_option_supported? pend '`complete: true` is the same as `complete: false` in Ruby 2.6-' diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index b3fc49e7e33c1b..998cdd8591d0f7 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -225,6 +225,16 @@ def test_assignment_expression end end + def test_assignment_expression_with_local_variable + input = TestInputMethod.new + irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) + code = "a /1;x=1#/" + refute(irb.assignment_expression?(code), "#{code}: should not be an assignment expression") + irb.context.workspace.binding.eval('a = 1') + assert(irb.assignment_expression?(code), "#{code}: should be an assignment expression") + refute(irb.assignment_expression?(""), "empty code should not be an assignment expression") + end + def test_echo_on_assignment input = TestInputMethod.new([ "a = 1\n", diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb index beda53fc8997f0..1388d0896204c8 100644 --- a/test/irb/test_ruby_lex.rb +++ b/test/irb/test_ruby_lex.rb @@ -34,13 +34,27 @@ def assert_indenting(lines, correct_space_count, add_new_line) ruby_lex.set_auto_indent(context) end - def assert_nesting_level(lines, expected) + def assert_nesting_level(lines, expected, local_variables: []) + ruby_lex = ruby_lex_for_lines(lines, local_variables: local_variables) + error_message = "Calculated the wrong number of nesting level for:\n #{lines.join("\n")}" + assert_equal(expected, ruby_lex.instance_variable_get(:@indent), error_message) + end + + def assert_code_block_open(lines, expected, local_variables: []) + ruby_lex = ruby_lex_for_lines(lines, local_variables: local_variables) + error_message = "Wrong result of code_block_open for:\n #{lines.join("\n")}" + assert_equal(expected, ruby_lex.instance_variable_get(:@code_block_open), error_message) + end + + def ruby_lex_for_lines(lines, local_variables: []) ruby_lex = RubyLex.new() io = proc{ lines.join("\n") } ruby_lex.set_input(io, io) - ruby_lex.lex - error_message = "Calculated the wrong number of nesting level for:\n #{lines.join("\n")}" - assert_equal(expected, ruby_lex.instance_variable_get(:@indent), error_message) + unless local_variables.empty? + context = OpenStruct.new(local_variables: local_variables) + end + ruby_lex.lex(context) + ruby_lex end def test_auto_indent @@ -514,6 +528,15 @@ def test_do_corresponding_to_loop end end + def test_local_variables_dependent_code + pend if RUBY_ENGINE == 'truffleruby' + lines = ["a /1#/ do", "2"] + assert_nesting_level(lines, 1) + assert_code_block_open(lines, true) + assert_nesting_level(lines, 0, local_variables: ['a']) + assert_code_block_open(lines, false, local_variables: ['a']) + end + def test_heredoc_with_indent input_with_correct_indents = [ Row.new(%q(<<~Q), 0, 0, 0), diff --git a/test/irb/yamatanooroti/test_rendering.rb b/test/irb/yamatanooroti/test_rendering.rb index 7ed98b11c1ac19..f9a130b7d44a60 100644 --- a/test/irb/yamatanooroti/test_rendering.rb +++ b/test/irb/yamatanooroti/test_rendering.rb @@ -216,6 +216,25 @@ def test_autocomplete_with_showdoc_in_gaps_on_narrow_screen_left EOC end + def test_assignment_expression_truncate + write_irbrc <<~'LINES' + puts 'start IRB' + LINES + start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB') + # Assignment expression code that turns into non-assignment expression after evaluation + code = "a /'/i if false; a=1; x=1000.times.to_a#'.size" + write(code + "\n") + close + assert_screen(<<~EOC) + start IRB + irb(main):001:0> #{code} + => + [0, + ... + irb(main):002:0> + EOC + end + private def write_irbrc(content) File.open(@irbrc_file, 'w') do |f| f.write content From 931bcd1b6315de5407a73019482bd3919f16820d Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 17 Oct 2022 23:00:23 -0700 Subject: [PATCH 110/139] [ruby/irb] Assert lvars_code doesn't include \n Removing /\A.+\n/ could have an unexpected impact, depending on how RubyLex.generate_local_variables_assign_code is implemented. It feels like a too much assumption and the intention isn't immediately clear, so I added these changes. https://github.com/ruby/irb/commit/ccc07a35ce --- lib/irb/color.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/irb/color.rb b/lib/irb/color.rb index 34912420e42b65..6378e14856aa22 100644 --- a/lib/irb/color.rb +++ b/lib/irb/color.rb @@ -156,10 +156,10 @@ def colorize_code(code, complete: true, ignore_error: false, colorable: colorabl end if lvars_code - colored.sub(/\A.+\n/, '') - else - colored + raise "#{lvars_code.dump} should have no \\n" if lvars_code.include?("\n") + colored.sub!(/\A.+\n/, '') # delete_prefix lvars_code with colors end + colored end private From 4205190cb24c350f0fbe5471881c25e8ecece96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 29 Sep 2022 07:16:32 +0200 Subject: [PATCH 111/139] [rubygems/rubygems] Remove dead code https://github.com/rubygems/rubygems/commit/999b644708 --- lib/bundler/resolver.rb | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 161a3c0518c639..104e149f6b9cfb 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -264,18 +264,7 @@ def verify_gemfile_dependencies_are_found!(requirements) next requirement unless search_for(requirement).empty? next unless requirement.current_platform? - if (base = @base[name]) && !base.empty? - version = base.first.version - message = "You have requested:\n" \ - " #{name} #{requirement.requirement}\n\n" \ - "The bundle currently has #{name} locked at #{version}.\n" \ - "Try running `bundle update #{name}`\n\n" \ - "If you are updating multiple gems in your Gemfile at once,\n" \ - "try passing them all to `bundle update`" - else - message = gem_not_found_message(name, requirement, source_for(name)) - end - raise GemNotFound, message + raise GemNotFound, gem_not_found_message(name, requirement, source_for(name)) end.compact! end From da1981fca450b5dd28d252cd42d59480c7d062b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 29 Sep 2022 07:30:43 +0200 Subject: [PATCH 112/139] [rubygems/rubygems] Remove unused parameter to `SharedHelpers.pretty_dependency` https://github.com/rubygems/rubygems/commit/665051d085 --- lib/bundler/definition.rb | 4 ++-- lib/bundler/injector.rb | 2 +- lib/bundler/shared_helpers.rb | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 21db42f7726ef2..6bb42ab02cf8e1 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -578,8 +578,8 @@ def change_reason ].select(&:first).map(&:last).join(", ") end - def pretty_dep(dep, source = false) - SharedHelpers.pretty_dependency(dep, source) + def pretty_dep(dep) + SharedHelpers.pretty_dependency(dep) end # Check if the specs of the given source changed diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index 82d5bd588012c8..81465cec19f616 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -70,7 +70,7 @@ def remove(gemfile_path, lockfile_path) show_warning("No gems were removed from the gemfile.") if deps.empty? - deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep, false)} was removed." } + deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep)} was removed." } end # Invalidate the cached Bundler.definition. diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index 8c4e26f074ec4c..899eb68e0a86ce 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -163,7 +163,7 @@ def ensure_same_dependencies(spec, old_deps, new_deps) "\nEither installing with `--full-index` or running `bundle update #{spec.name}` should fix the problem." end - def pretty_dependency(dep, print_source = false) + def pretty_dependency(dep) msg = String.new(dep.name) msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default @@ -172,7 +172,6 @@ def pretty_dependency(dep, print_source = false) msg << " " << platform_string if !platform_string.empty? && platform_string != Gem::Platform::RUBY end - msg << " from the `#{dep.source}` source" if print_source && dep.source msg end From 0b6f2af3ba7bda0a81bc1eb1bd5b067c8d9e3e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 6 Jul 2022 13:47:11 +0200 Subject: [PATCH 113/139] [rubygems/rubygems] Avoid unnecessary dependency expansion https://github.com/rubygems/rubygems/commit/426748ed06 --- lib/bundler/definition.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 6bb42ab02cf8e1..485d8a1616b261 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -139,7 +139,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti if @unlock[:conservative] @unlock[:gems] ||= @dependencies.map(&:name) else - eager_unlock = expand_dependencies(@unlock[:gems] || [], true) + eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") } @unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq end @@ -795,7 +795,6 @@ def metadata_dependencies def expand_dependencies(dependencies, remote = false) deps = [] dependencies.each do |dep| - dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name) next unless remote || dep.current_platform? target_platforms = dep.gem_platforms(remote ? @platforms : [generic_local_platform]) deps += expand_dependency_with_platforms(dep, target_platforms) From 60670b163f32cae27629c852f95870656d2ee72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 4 Jul 2022 14:47:53 +0200 Subject: [PATCH 114/139] [rubygems/rubygems] Remove another unnecessary dependency expansion https://github.com/rubygems/rubygems/commit/b4a0fcd2d2 --- lib/bundler/definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 485d8a1616b261..a289babcb3448a 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -484,7 +484,7 @@ def expanded_dependencies end def filter_specs(specs, deps) - SpecSet.new(specs).for(expand_dependencies(deps, true), false, platforms) + SpecSet.new(specs).for(deps, false, platforms) end def materialize(dependencies) From 8c4bd1e58d426ff014a3958ac197d40b4e367f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 6 Jul 2022 22:21:32 +0200 Subject: [PATCH 115/139] [rubygems/rubygems] Remove another `expand_dependencies` instance https://github.com/rubygems/rubygems/commit/33769ddb07 --- lib/bundler/definition.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index a289babcb3448a..6537ba1de855b6 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -224,7 +224,7 @@ def requested_dependencies def current_dependencies dependencies.select do |d| - d.should_include? && !d.gem_platforms(@platforms).empty? + d.should_include? && !d.gem_platforms([generic_local_platform]).empty? end end @@ -248,10 +248,9 @@ def specs_for(groups) def dependencies_for(groups) groups.map!(&:to_sym) - deps = current_dependencies.reject do |d| + current_dependencies.reject do |d| (d.groups & groups).empty? end - expand_dependencies(deps) end # Resolve all the dependencies specified in Gemfile. It ensures that From cef7f6b2241565f51bb724c19b89b8320825b98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 6 Jul 2022 22:29:13 +0200 Subject: [PATCH 116/139] [rubygems/rubygems] Remove now unnecessary parameter to `expand_dependencies` We just call it once for resolution, so we can simplify things. https://github.com/rubygems/rubygems/commit/99c144fbe3 --- lib/bundler/definition.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 6537ba1de855b6..1cf0bbbdb4f8c0 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -479,7 +479,7 @@ def resolver end def expanded_dependencies - @expanded_dependencies ||= expand_dependencies(dependencies + metadata_dependencies, true) + @expanded_dependencies ||= expand_dependencies(dependencies + metadata_dependencies) end def filter_specs(specs, deps) @@ -791,11 +791,10 @@ def metadata_dependencies ] end - def expand_dependencies(dependencies, remote = false) + def expand_dependencies(dependencies) deps = [] dependencies.each do |dep| - next unless remote || dep.current_platform? - target_platforms = dep.gem_platforms(remote ? @platforms : [generic_local_platform]) + target_platforms = dep.gem_platforms(@platforms) deps += expand_dependency_with_platforms(dep, target_platforms) end deps From 71dede387e089023292e66c7f39305e13400c8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Tue, 23 Aug 2022 08:36:30 +0200 Subject: [PATCH 117/139] [rubygems/rubygems] Remove one more `expand_dependencies` call https://github.com/rubygems/rubygems/commit/996fd81871 --- lib/bundler/definition.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 1cf0bbbdb4f8c0..340b1b68dda392 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -473,7 +473,7 @@ def unlocking? def resolver @resolver ||= begin last_resolve = converge_locked_specs - remove_ruby_from_platforms_if_necessary!(dependencies) + remove_ruby_from_platforms_if_necessary!(current_dependencies) Resolver.new(source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve(last_resolve), platforms) end end @@ -877,7 +877,7 @@ def remove_ruby_from_platforms_if_necessary!(dependencies) Bundler.local_platform == Gem::Platform::RUBY || !platforms.include?(Gem::Platform::RUBY) || (@new_platform && platforms.last == Gem::Platform::RUBY) || - !@originally_locked_specs.incomplete_ruby_specs?(expand_dependencies(dependencies)) + !@originally_locked_specs.incomplete_ruby_specs?(dependencies) remove_platform(Gem::Platform::RUBY) add_current_platform From b99010f9f2aa9e8ea100f4cdbe1e2f1a5601fd5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 4 Jul 2022 18:37:59 +0200 Subject: [PATCH 118/139] [rubygems/rubygems] Use `flat_map` https://github.com/rubygems/rubygems/commit/b31308fb4c --- lib/bundler/definition.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 340b1b68dda392..374462ab49bc24 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -792,12 +792,10 @@ def metadata_dependencies end def expand_dependencies(dependencies) - deps = [] - dependencies.each do |dep| + dependencies.flat_map do |dep| target_platforms = dep.gem_platforms(@platforms) - deps += expand_dependency_with_platforms(dep, target_platforms) + expand_dependency_with_platforms(dep, target_platforms) end - deps end def expand_dependency_with_platforms(dep, platforms) From 67de00053a26db9d976efa393d121479b5315f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 4 Jul 2022 19:04:00 +0200 Subject: [PATCH 119/139] [rubygems/rubygems] Inline helper method https://github.com/rubygems/rubygems/commit/e60459d6b6 --- lib/bundler/definition.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 374462ab49bc24..a3042e06a33ffb 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -793,14 +793,9 @@ def metadata_dependencies def expand_dependencies(dependencies) dependencies.flat_map do |dep| - target_platforms = dep.gem_platforms(@platforms) - expand_dependency_with_platforms(dep, target_platforms) - end - end - - def expand_dependency_with_platforms(dep, platforms) - platforms.map do |p| - DepProxy.get_proxy(dep, p) + dep.gem_platforms(@platforms).map do |p| + DepProxy.get_proxy(dep, p) + end end end From 804ae4ea12d05e66aaa680feea83af587ac53745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 12 Sep 2022 18:38:33 +0200 Subject: [PATCH 120/139] [rubygems/rubygems] Simplify SpecGroup creation https://github.com/rubygems/rubygems/commit/788e46e152 --- lib/bundler/resolver.rb | 6 +++--- lib/bundler/resolver/spec_group.rb | 17 +++++++---------- .../bundler/gem_version_promoter_spec.rb | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 104e149f6b9cfb..80ad39e8968046 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -136,15 +136,15 @@ def search_for(dependency_proxy) current_specs[current_platform] = select_best_platform_match(specs, current_platform) end - spec_group_ruby = SpecGroup.create_for(specs_by_platform, [Gem::Platform::RUBY], Gem::Platform::RUBY) - if spec_group_ruby + if specs_by_platform[Gem::Platform::RUBY].any? + spec_group_ruby = SpecGroup.create_for(specs_by_platform, [Gem::Platform::RUBY]) spec_group_ruby.force_ruby_platform = dependency.force_ruby_platform groups << spec_group_ruby end next groups if @resolving_only_for_ruby || dependency.force_ruby_platform - spec_group = SpecGroup.create_for(specs_by_platform, @platforms, platform) + spec_group = SpecGroup.create_for(specs_by_platform, @platforms) groups << spec_group groups diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 4e5b0082d3d316..dfe15a5052ce84 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -6,20 +6,17 @@ class SpecGroup attr_accessor :name, :version, :source attr_accessor :activated_platforms, :force_ruby_platform - def self.create_for(specs, all_platforms, specific_platform) - specific_platform_specs = specs[specific_platform] - return unless specific_platform_specs.any? - + def self.create_for(specs, all_platforms) platforms = all_platforms.select {|p| specs[p].any? } - new(specific_platform_specs.first, specs, platforms) + new(specs, platforms) end - def initialize(exemplary_spec, specs, relevant_platforms) - @exemplary_spec = exemplary_spec - @name = exemplary_spec.name - @version = exemplary_spec.version - @source = exemplary_spec.source + def initialize(specs, relevant_platforms) + @exemplary_spec = specs[relevant_platforms.first].first + @name = @exemplary_spec.name + @version = @exemplary_spec.version + @source = @exemplary_spec.source @activated_platforms = relevant_platforms @dependencies = Hash.new do |dependencies, platforms| diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb index 99e008bfbc9b65..82c325097f84ff 100644 --- a/spec/bundler/bundler/gem_version_promoter_spec.rb +++ b/spec/bundler/bundler/gem_version_promoter_spec.rb @@ -28,7 +28,7 @@ def keep_locked(options) def build_spec_groups(name, versions) versions.map do |v| - Bundler::Resolver::SpecGroup.create_for({ Gem::Platform::RUBY => build_spec(name, v) }, [Gem::Platform::RUBY], Gem::Platform::RUBY) + Bundler::Resolver::SpecGroup.create_for({ Gem::Platform::RUBY => build_spec(name, v) }, [Gem::Platform::RUBY]) end end From 56ad79694ba18789cb26a85dd693d51d96a092c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 12 Sep 2022 18:46:01 +0200 Subject: [PATCH 121/139] [rubygems/rubygems] Simplify fetching spec group dependencies https://github.com/rubygems/rubygems/commit/62c2edd255 --- lib/bundler/resolver/spec_group.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index dfe15a5052ce84..2c00af3b84b945 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -19,9 +19,6 @@ def initialize(specs, relevant_platforms) @source = @exemplary_spec.source @activated_platforms = relevant_platforms - @dependencies = Hash.new do |dependencies, platforms| - dependencies[platforms] = dependencies_for(platforms) - end @specs = specs end @@ -45,7 +42,7 @@ def to_s end def dependencies_for_activated_platforms - @dependencies[activated_platforms] + @dependencies_for_activated_platforms ||= dependencies_for(activated_platforms) end def ==(other) From e956caea88b67cc1a1b42b95327190c63d45888a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 5 Oct 2022 14:16:57 +0200 Subject: [PATCH 122/139] [rubygems/rubygems] Extract matcher for slow perf spec https://github.com/rubygems/rubygems/commit/1c0eb63c6a --- spec/bundler/realworld/slow_perf_spec.rb | 8 +------- spec/bundler/support/matchers.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb index 9f11821bf58f08..467e740cb496d3 100644 --- a/spec/bundler/realworld/slow_perf_spec.rb +++ b/spec/bundler/realworld/slow_perf_spec.rb @@ -11,12 +11,6 @@ gem "mongoid", ">= 0.10.2" G - start_time = Time.now - - bundle "lock" - - duration = Time.now - start_time - - expect(duration.to_f).to be < 18 # seconds + expect { bundle "lock" }.to take_less_than(18) # seconds end end diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb index ce6b2166191e9f..8b22e7dbc3dbff 100644 --- a/spec/bundler/support/matchers.rb +++ b/spec/bundler/support/matchers.rb @@ -97,6 +97,18 @@ def self.define_compound_matcher(matcher, preconditions, &declarations) end end + RSpec::Matchers.define :take_less_than do |seconds| + match do |actual| + start_time = Time.now + + actual.call + + (Time.now - start_time).to_f < seconds + end + + supports_block_expectations + end + define_compound_matcher :read_as, [exist] do |file_contents| diffable From 5fa18a7b0cb1ffbc0c11d151c074c9aa0ea6ee41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 5 Oct 2022 15:40:03 +0200 Subject: [PATCH 123/139] [rubygems/rubygems] Fix bad spec wording https://github.com/rubygems/rubygems/commit/06faad1e05 --- spec/bundler/install/gems/resolving_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index 9f4da23162671f..07389aa990774e 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -305,7 +305,7 @@ end end - context "in a transitive dependencies in a lockfile" do + context "with transitive dependencies in a lockfile" do before do build_repo2 do build_gem "rubocop", "1.28.2" do |s| From 74dbb0d09a694c40b8406ce35ca5d4278185c3f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Tue, 11 Oct 2022 01:33:09 +0200 Subject: [PATCH 124/139] [rubygems/rubygems] Remove unnecessary generic I didn't realize how the `Bundler::GemHelpers.generic` method works when I added this. It already matches this and other java platforms properly. https://github.com/rubygems/rubygems/commit/5f0f0c678c --- lib/bundler/gem_helpers.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index 0d50d8687b90b5..2e6d788f9c6f0a 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -5,7 +5,6 @@ module GemHelpers GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant GENERICS = [ [Gem::Platform.new("java"), Gem::Platform.new("java")], - [Gem::Platform.new("universal-java"), Gem::Platform.new("java")], [Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")], [Gem::Platform.new("mswin64"), Gem::Platform.new("mswin64")], [Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")], From 4d3dbbe77e273262757483d402ee1157a48453e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Tue, 11 Oct 2022 01:34:12 +0200 Subject: [PATCH 125/139] [rubygems/rubygems] Remove unnecessary receiver https://github.com/rubygems/rubygems/commit/ba9d9b1890 --- lib/bundler/lazy_specification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index ec141cfa27da29..3630de56720854 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -77,7 +77,7 @@ def materialize_for_installation source.local! candidates = if source.is_a?(Source::Path) || !ruby_platform_materializes_to_ruby_platform? - target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : Bundler.local_platform + target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : local_platform source.specs.search(Dependency.new(name, version)).select do |spec| MatchPlatform.platforms_match?(spec.platform, target_platform) From cb63a3f4c1d4b3e6245674e8f759adeda7c7abe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Tue, 11 Oct 2022 01:36:04 +0200 Subject: [PATCH 126/139] [rubygems/rubygems] Materialize platforms strictly on Windows too https://github.com/rubygems/rubygems/commit/ab11545f80 --- lib/bundler/lazy_specification.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 3630de56720854..7100d822b4f0ec 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -151,7 +151,8 @@ def method_missing(method, *args, &blk) # # For backwards compatibility with existing lockfiles, if the most specific - # locked platform is RUBY, we keep the previous behaviour of resolving the + # locked platform is not a specific platform like x86_64-linux or + # universal-java-11, then we keep the previous behaviour of resolving the # best platform variant at materiliazation time. For previous bundler # versions (before 2.2.0) this was always the case (except when the lockfile # only included non-ruby platforms), but we're also keeping this behaviour @@ -159,7 +160,9 @@ def method_missing(method, *args, &blk) # explicitly add a more specific platform. # def ruby_platform_materializes_to_ruby_platform? - !Bundler.most_specific_locked_platform?(generic_local_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform] + generic_platform = generic_local_platform == Gem::Platform::JAVA ? Gem::Platform::JAVA : Gem::Platform::RUBY + + !Bundler.most_specific_locked_platform?(generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform] end end end From cbddc913d94dde97714eeab1eac8570ff6674201 Mon Sep 17 00:00:00 2001 From: Jarek Prokop Date: Fri, 9 Sep 2022 15:18:25 +0200 Subject: [PATCH 127/139] [rubygems/rubygems] Set Subject Key Identifier in test to correspond to the new certificate. https://github.com/rubygems/rubygems/commit/3a607f43d1 --- test/rubygems/test_gem_security.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/rubygems/test_gem_security.rb b/test/rubygems/test_gem_security.rb index f0b9824aabb6af..518a508c8647bb 100644 --- a/test/rubygems/test_gem_security.rb +++ b/test/rubygems/test_gem_security.rb @@ -51,7 +51,7 @@ def test_class_create_cert key_ident = cert.extensions.find {|ext| ext.oid == "subjectKeyIdentifier" } assert_equal 59, key_ident.value.length - assert_equal "5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B", + assert_equal "B1:1A:54:09:67:45:60:02:02:D7:CE:F4:1D:60:4A:89:DF:E7:58:D9", key_ident.value assert_equal "", cert.issuer.to_s @@ -99,7 +99,7 @@ def test_class_create_cert_email key_ident = cert.extensions.find {|ext| ext.oid == "subjectKeyIdentifier" } assert_equal 59, key_ident.value.length - assert_equal "5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B", + assert_equal "B1:1A:54:09:67:45:60:02:02:D7:CE:F4:1D:60:4A:89:DF:E7:58:D9", key_ident.value end @@ -230,7 +230,7 @@ def test_class_sign key_ident = signed.extensions.find {|ext| ext.oid == "subjectKeyIdentifier" } assert_equal 59, key_ident.value.length - assert_equal "5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B", + assert_equal "B1:1A:54:09:67:45:60:02:02:D7:CE:F4:1D:60:4A:89:DF:E7:58:D9", key_ident.value assert signed.verify key @@ -272,7 +272,7 @@ def test_class_sign_AltName key_ident = signed.extensions.find {|ext| ext.oid == "subjectKeyIdentifier" } assert_equal 59, key_ident.value.length - assert_equal "5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B", + assert_equal "B1:1A:54:09:67:45:60:02:02:D7:CE:F4:1D:60:4A:89:DF:E7:58:D9", key_ident.value assert signed.verify PUBLIC_KEY From b7debaa8015c7a3dba832954ecc93334f6853f37 Mon Sep 17 00:00:00 2001 From: Jarek Prokop Date: Fri, 9 Sep 2022 15:22:31 +0200 Subject: [PATCH 128/139] [rubygems/rubygems] TestGemSecurity#test_class_re_sign: Correct signature algorithm. https://github.com/rubygems/rubygems/commit/e2d533591c --- test/rubygems/test_gem_security.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rubygems/test_gem_security.rb b/test/rubygems/test_gem_security.rb index 518a508c8647bb..0aecccdb273f33 100644 --- a/test/rubygems/test_gem_security.rb +++ b/test/rubygems/test_gem_security.rb @@ -151,7 +151,7 @@ def test_class_email_to_name end def test_class_re_sign - assert_equal "sha1WithRSAEncryption", EXPIRED_CERT.signature_algorithm + assert_equal "sha256WithRSAEncryption", EXPIRED_CERT.signature_algorithm re_signed = Gem::Security.re_sign EXPIRED_CERT, PRIVATE_KEY, 60 assert_in_delta Time.now, re_signed.not_before, 10 From 2ab7bb8969ca76d49a6c2043f423a3646b20d1f7 Mon Sep 17 00:00:00 2001 From: Jarek Prokop Date: Fri, 9 Sep 2022 15:25:09 +0200 Subject: [PATCH 129/139] [rubygems/rubygems] TestGemSecuritySigner#test_sign: Correct the expectation for new cert. https://github.com/rubygems/rubygems/commit/bed360410d --- test/rubygems/test_gem_security_signer.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/rubygems/test_gem_security_signer.rb b/test/rubygems/test_gem_security_signer.rb index d9f320eeb01d3c..6b2a4a201a77c1 100644 --- a/test/rubygems/test_gem_security_signer.rb +++ b/test/rubygems/test_gem_security_signer.rb @@ -120,12 +120,12 @@ def test_sign signature = signer.sign "hello" expected = <<-EXPECTED -cHze2sEfRysoUMCfGVAx/7o8jxj5liJJ2ptNxe2jf3l+EZvyjdqpXo9Ndzxx -6xLp2rxLG4K2//ip4aCH5Sh7hnia+F5u6iuLBETPlklPrmw5dnuKZxolz+vM -0O1aOZsQHcVzQoESTGjkms3KZk+gn3lg0sSBbAV5/LyDYoHCEjxlcA5D+Olb -rDmRyBMOnMS+q489OZ5Hr6B2YJJ3QbUwIZNhUeNmOxIBEYTrrKkZ92qkxbRN -qhlqFP4jR6zXFeyBCOr0KpTiWBNuxBFXDsxmhGyt2BOIjD6qmKn7RSIfYg/U -toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA== +FmrCYxEXW3dgYYNMxPdS16VrdXT+d5nyXTVlRm64ZHSgMxMAaPtQJsVYv73m +DWHTzNnLhhINSpgBMLh5a4atM52yxVdkPUTgqIH+LeIPBXn8xaP5JLmfDcmI +tBpc/9DhS3v9iKCX40igAArFu7Gg3swbgQ61SP+U22LvG5nDQZQz3sudtsw3 +qKPykFVaYjrRwzvBdSdJ1PwlAsanSwcwS/GKPtmE/ykZ6X5XOx7wvCDL/zGy +B8khkB8hDKC6moCzebmUxCBmTmXD0Wjzon+bf4MOriVE3a0ySGRvpr1mKR2+ +9EaVo7pDJLEM487+xg1CAZHRhwshd6II00XEzG/jBQ== EXPECTED assert_equal expected, [signature].pack("m") From f5df47d1f3ec403d057f823375f1dfeea711caa6 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 18 Oct 2022 15:24:42 +0900 Subject: [PATCH 130/139] Merge RubyGems/Bundler master https://github.com/rubygems/rubygems/commit/6214d00b2315ed37c76b1fbc1c72f61f92ba5a65 --- lib/bundler.rb | 1 - lib/bundler/definition.rb | 10 +-- lib/bundler/dep_proxy.rb | 55 -------------- lib/bundler/endpoint_specification.rb | 4 + lib/bundler/index.rb | 1 - lib/bundler/lazy_specification.rb | 9 +-- lib/bundler/remote_specification.rb | 8 +- lib/bundler/resolver.rb | 76 ++++++------------- lib/bundler/resolver/base.rb | 2 +- lib/bundler/resolver/spec_group.rb | 52 +++++-------- spec/bundler/bundler/dep_proxy_spec.rb | 32 -------- .../bundler/gem_version_promoter_spec.rb | 2 +- .../bundler/remote_specification_spec.rb | 2 +- spec/bundler/install/gems/resolving_spec.rb | 2 +- spec/bundler/install/yanked_spec.rb | 66 ++++++++++++++++ spec/bundler/realworld/slow_perf_spec.rb | 17 +++++ spec/bundler/resolver/basic_spec.rb | 7 +- spec/bundler/support/indexes.rb | 8 +- test/rubygems/alternate_cert.pem | 28 +++---- test/rubygems/alternate_cert_32.pem | 30 ++++---- test/rubygems/alternate_key.pem | 50 ++++++------ test/rubygems/child_cert.pem | 31 ++++---- test/rubygems/child_cert_32.pem | 31 ++++---- test/rubygems/child_key.pem | 50 ++++++------ test/rubygems/encrypted_private_key.pem | 52 ++++++------- test/rubygems/expired_cert.pem | 30 ++++---- test/rubygems/future_cert.pem | 30 ++++---- test/rubygems/future_cert_32.pem | 30 ++++---- test/rubygems/grandchild_cert.pem | 31 ++++---- test/rubygems/grandchild_cert_32.pem | 31 ++++---- test/rubygems/grandchild_key.pem | 50 ++++++------ test/rubygems/invalid_issuer_cert.pem | 32 ++++---- test/rubygems/invalid_issuer_cert_32.pem | 32 ++++---- test/rubygems/invalid_key.pem | 50 ++++++------ test/rubygems/invalid_signer_cert.pem | 30 ++++---- test/rubygems/invalid_signer_cert_32.pem | 30 ++++---- test/rubygems/invalidchild_cert.pem | 31 ++++---- test/rubygems/invalidchild_cert_32.pem | 31 ++++---- test/rubygems/invalidchild_key.pem | 50 ++++++------ test/rubygems/private_key.pem | 50 ++++++------ test/rubygems/public_cert.pem | 32 ++++---- test/rubygems/public_cert_32.pem | 30 ++++---- test/rubygems/public_key.pem | 14 ++-- test/rubygems/wrong_key_cert.pem | 30 ++++---- test/rubygems/wrong_key_cert_32.pem | 30 ++++---- 45 files changed, 613 insertions(+), 687 deletions(-) delete mode 100644 lib/bundler/dep_proxy.rb delete mode 100644 spec/bundler/bundler/dep_proxy_spec.rb diff --git a/lib/bundler.rb b/lib/bundler.rb index dc88bbdcb93771..1a94e0c963e6f3 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -41,7 +41,6 @@ module Bundler autoload :Definition, File.expand_path("bundler/definition", __dir__) autoload :Dependency, File.expand_path("bundler/dependency", __dir__) - autoload :DepProxy, File.expand_path("bundler/dep_proxy", __dir__) autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__) autoload :Digest, File.expand_path("bundler/digest", __dir__) autoload :Dsl, File.expand_path("bundler/dsl", __dir__) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index a3042e06a33ffb..95be7a7e273930 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -479,7 +479,7 @@ def resolver end def expanded_dependencies - @expanded_dependencies ||= expand_dependencies(dependencies + metadata_dependencies) + @expanded_dependencies ||= dependencies + metadata_dependencies end def filter_specs(specs, deps) @@ -791,14 +791,6 @@ def metadata_dependencies ] end - def expand_dependencies(dependencies) - dependencies.flat_map do |dep| - dep.gem_platforms(@platforms).map do |p| - DepProxy.get_proxy(dep, p) - end - end - end - def source_requirements # Record the specs available in each gem's source, so that those # specs will be available later when the resolver knows where to diff --git a/lib/bundler/dep_proxy.rb b/lib/bundler/dep_proxy.rb deleted file mode 100644 index a32dc37b492d65..00000000000000 --- a/lib/bundler/dep_proxy.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class DepProxy - attr_reader :__platform, :dep - - @proxies = {} - - def self.get_proxy(dep, platform) - @proxies[[dep, platform]] ||= new(dep, platform).freeze - end - - def initialize(dep, platform) - @dep = dep - @__platform = platform - end - - private_class_method :new - - alias_method :eql?, :== - - def type - @dep.type - end - - def name - @dep.name - end - - def requirement - @dep.requirement - end - - def to_s - s = name.dup - s << " (#{requirement})" unless requirement == Gem::Requirement.default - s << " #{__platform}" unless __platform == Gem::Platform::RUBY - s - end - - def dup - raise NoMethodError.new("DepProxy cannot be duplicated") - end - - def clone - raise NoMethodError.new("DepProxy cannot be cloned") - end - - private - - def method_missing(*args, &blk) - @dep.send(*args, &blk) - end - end -end diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index 863544b1f926db..d315d1cc68de9b 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -26,6 +26,10 @@ def fetch_platform @platform end + def identifier + @__identifier ||= [name, version, platform.to_s] + end + # needed for standalone, load required_paths from local gemspec # after the gem is installed def require_paths diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index d3743adb688553..ed16c90a3a1681 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -71,7 +71,6 @@ def local_search(query) when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query) when String then specs_by_name(query) when Gem::Dependency then search_by_dependency(query) - when DepProxy then search_by_dependency(query.dep) else raise "You can't search for a #{query.inspect}." end diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 7100d822b4f0ec..f5fe2e64aec527 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -120,7 +120,7 @@ def to_s end def identifier - @__identifier ||= [name, version, platform_string] + @__identifier ||= [name, version, platform.to_s] end def git_version @@ -128,13 +128,6 @@ def git_version " #{source.revision[0..6]}" end - protected - - def platform_string - platform_string = platform.to_s - platform_string == Index::RUBY ? Index::NULL : platform_string - end - private def to_ary diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb index 601957746f3af6..34d7fd116c576f 100644 --- a/lib/bundler/remote_specification.rb +++ b/lib/bundler/remote_specification.rb @@ -29,11 +29,15 @@ def fetch_platform @platform = _remote_specification.platform end + def identifier + @__identifier ||= [name, version, @platform.to_s] + end + def full_name - if @original_platform == Gem::Platform::RUBY + if @platform == Gem::Platform::RUBY "#{@name}-#{@version}" else - "#{@name}-#{@version}-#{@original_platform}" + "#{@name}-#{@version}-#{@platform}" end end diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 80ad39e8968046..115c5cfcc4aa14 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -8,22 +8,6 @@ class Resolver include GemHelpers - # Figures out the best possible configuration of gems that satisfies - # the list of passed dependencies and any child dependencies without - # causing any gem activation errors. - # - # ==== Parameters - # *dependencies:: The list of dependencies to resolve - # - # ==== Returns - # ,nil:: If the list of dependencies can be resolved, a - # collection of gemspecs is returned. Otherwise, nil is returned. - def self.resolve(requirements, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil) - base = SpecSet.new(base) unless base.is_a?(SpecSet) - resolver = new(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) - resolver.start(requirements) - end - def initialize(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) @source_requirements = source_requirements @base = Resolver::Base.new(base, additional_base_requirements) @@ -116,41 +100,35 @@ def dependencies_for(specification) specification.dependencies_for_activated_platforms end - def search_for(dependency_proxy) - platform = dependency_proxy.__platform - dependency = dependency_proxy.dep - name = dependency.name - @search_for[dependency_proxy] ||= begin + def search_for(dependency) + @search_for[dependency] ||= begin + name = dependency.name locked_results = @base[name].select {|spec| requirement_satisfied_by?(dependency, nil, spec) } locked_requirement = base_requirements[name] results = results_for(dependency) + locked_results results = results.select {|spec| requirement_satisfied_by?(locked_requirement, nil, spec) } if locked_requirement + dep_platforms = dependency.gem_platforms(@platforms) - if results.any? - results = @gem_version_promoter.sort_versions(dependency, results) + @gem_version_promoter.sort_versions(dependency, results).group_by(&:version).reduce([]) do |groups, (_, specs)| + relevant_platforms = dep_platforms.select {|platform| specs.any? {|spec| spec.match_platform(platform) } } + next groups unless relevant_platforms.any? - results.group_by(&:version).reduce([]) do |groups, (_, specs)| - next groups unless specs.any? {|spec| spec.match_platform(platform) } - - specs_by_platform = Hash.new do |current_specs, current_platform| - current_specs[current_platform] = select_best_platform_match(specs, current_platform) - end + ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY) + if ruby_specs.any? + spec_group_ruby = SpecGroup.new(ruby_specs, [Gem::Platform::RUBY]) + spec_group_ruby.force_ruby_platform = dependency.force_ruby_platform + groups << spec_group_ruby + end - if specs_by_platform[Gem::Platform::RUBY].any? - spec_group_ruby = SpecGroup.create_for(specs_by_platform, [Gem::Platform::RUBY]) - spec_group_ruby.force_ruby_platform = dependency.force_ruby_platform - groups << spec_group_ruby - end + next groups if @resolving_only_for_ruby || dependency.force_ruby_platform - next groups if @resolving_only_for_ruby || dependency.force_ruby_platform + platform_specs = relevant_platforms.flat_map {|platform| select_best_platform_match(specs, platform) } + next groups if platform_specs == ruby_specs - spec_group = SpecGroup.create_for(specs_by_platform, @platforms) - groups << spec_group + spec_group = SpecGroup.new(platform_specs, relevant_platforms) + groups << spec_group - groups - end - else - [] + groups end end end @@ -181,10 +159,6 @@ def requirement_satisfied_by?(requirement, activated, spec) requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec) end - def dependencies_equal?(dependencies, other_dependencies) - dependencies.map(&:dep) == other_dependencies.map(&:dep) - end - def sort_dependencies(dependencies, activated, conflicts) dependencies.sort_by do |dependency| name = name_for(dependency) @@ -196,17 +170,10 @@ def sort_dependencies(dependencies, activated, conflicts) amount_constrained(dependency), conflicts[name] ? 0 : 1, vertex.payload ? 0 : search_for(dependency).count, - self.class.platform_sort_key(dependency.__platform), ] end end - def self.platform_sort_key(platform) - # Prefer specific platform to not specific platform - return ["99-LAST", "", "", ""] if Gem::Platform::RUBY == platform - ["00", *platform.to_a.map {|part| part || "" }] - end - private def base_requirements @@ -261,6 +228,7 @@ def verify_gemfile_dependencies_are_found!(requirements) requirements.map! do |requirement| name = requirement.name next requirement if name == "bundler" + next if requirement.gem_platforms(@platforms).empty? next requirement unless search_for(requirement).empty? next unless requirement.current_platform? @@ -282,7 +250,9 @@ def gem_not_found_message(name, requirement, source, extra_message = "") if specs_matching_requirement.any? specs = specs_matching_requirement matching_part = requirement_label - requirement_label = "#{requirement_label}' with platform '#{requirement.__platform}" + platforms = requirement.gem_platforms(@platforms) + platform_label = platforms.size == 1 ? "platform '#{platforms.first}" : "platforms '#{platforms.join("', '")}" + requirement_label = "#{requirement_label}' with #{platform_label}" end message = String.new("Could not find gem '#{requirement_label}'#{extra_message} in #{source}#{cache_message}.\n") diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb index 84e087b0ae9931..a8f42dc994e418 100644 --- a/lib/bundler/resolver/base.rb +++ b/lib/bundler/resolver/base.rb @@ -40,7 +40,7 @@ def build_base_requirements base_requirements = {} @base.each do |ls| dep = Dependency.new(ls.name, ls.version) - base_requirements[ls.name] = DepProxy.get_proxy(dep, ls.platform) + base_requirements[ls.name] = dep end @additional_base_requirements.each {|d| base_requirements[d.name] = d } base_requirements diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 2c00af3b84b945..ac32c3c1190a42 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -6,14 +6,8 @@ class SpecGroup attr_accessor :name, :version, :source attr_accessor :activated_platforms, :force_ruby_platform - def self.create_for(specs, all_platforms) - platforms = all_platforms.select {|p| specs[p].any? } - - new(specs, platforms) - end - def initialize(specs, relevant_platforms) - @exemplary_spec = specs[relevant_platforms.first].first + @exemplary_spec = specs.first @name = @exemplary_spec.name @version = @exemplary_spec.version @source = @exemplary_spec.source @@ -23,17 +17,12 @@ def initialize(specs, relevant_platforms) end def to_specs - activated_platforms.map do |p| - specs = @specs[p] - next unless specs.any? - - specs.map do |s| - lazy_spec = LazySpecification.new(name, version, s.platform, source) - lazy_spec.force_ruby_platform = force_ruby_platform - lazy_spec.dependencies.replace s.dependencies - lazy_spec - end - end.flatten.compact.uniq + @specs.map do |s| + lazy_spec = LazySpecification.new(name, version, s.platform, source) + lazy_spec.force_ruby_platform = force_ruby_platform + lazy_spec.dependencies.replace s.dependencies + lazy_spec + end end def to_s @@ -42,7 +31,9 @@ def to_s end def dependencies_for_activated_platforms - @dependencies_for_activated_platforms ||= dependencies_for(activated_platforms) + @dependencies_for_activated_platforms ||= @specs.map do |spec| + __dependencies(spec) + metadata_dependencies(spec) + end.flatten.uniq end def ==(other) @@ -73,35 +64,28 @@ def sorted_activated_platforms private - def dependencies_for(platforms) - platforms.map do |platform| - __dependencies(platform) + metadata_dependencies(platform) - end.flatten - end - - def __dependencies(platform) + def __dependencies(spec) dependencies = [] - @specs[platform].first.dependencies.each do |dep| + spec.dependencies.each do |dep| next if dep.type == :development - dependencies << DepProxy.get_proxy(Dependency.new(dep.name, dep.requirement), platform) + dependencies << Dependency.new(dep.name, dep.requirement) end dependencies end - def metadata_dependencies(platform) - spec = @specs[platform].first + def metadata_dependencies(spec) return [] if spec.is_a?(LazySpecification) [ - metadata_dependency("Ruby", spec.required_ruby_version, platform), - metadata_dependency("RubyGems", spec.required_rubygems_version, platform), + metadata_dependency("Ruby", spec.required_ruby_version), + metadata_dependency("RubyGems", spec.required_rubygems_version), ].compact end - def metadata_dependency(name, requirement, platform) + def metadata_dependency(name, requirement) return if requirement.nil? || requirement.none? - DepProxy.get_proxy(Dependency.new("#{name}\0", requirement), platform) + Dependency.new("#{name}\0", requirement) end end end diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb deleted file mode 100644 index 8d02a337250d3b..00000000000000 --- a/spec/bundler/bundler/dep_proxy_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe Bundler::DepProxy do - let(:dep) { Bundler::Dependency.new("rake", ">= 0") } - subject { described_class.get_proxy(dep, Gem::Platform::RUBY) } - let(:same) { subject } - let(:other) { described_class.get_proxy(dep, Gem::Platform::RUBY) } - let(:different) { described_class.get_proxy(dep, Gem::Platform::JAVA) } - - describe "#eql?" do - it { expect(subject.eql?(same)).to be true } - it { expect(subject.eql?(other)).to be true } - it { expect(subject.eql?(different)).to be false } - it { expect(subject.eql?(nil)).to be false } - it { expect(subject.eql?("foobar")).to be false } - end - - describe "must use factory methods" do - it { expect { described_class.new(dep, Gem::Platform::RUBY) }.to raise_error NoMethodError } - it { expect { subject.dup }.to raise_error NoMethodError } - it { expect { subject.clone }.to raise_error NoMethodError } - end - - describe "frozen" do - if Gem.ruby_version >= Gem::Version.new("2.5.0") - error = Object.const_get("FrozenError") - else - error = RuntimeError - end - it { expect { subject.instance_variable_set(:@__platform, {}) }.to raise_error error } - end -end diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb index 82c325097f84ff..54c559c1d8591e 100644 --- a/spec/bundler/bundler/gem_version_promoter_spec.rb +++ b/spec/bundler/bundler/gem_version_promoter_spec.rb @@ -28,7 +28,7 @@ def keep_locked(options) def build_spec_groups(name, versions) versions.map do |v| - Bundler::Resolver::SpecGroup.create_for({ Gem::Platform::RUBY => build_spec(name, v) }, [Gem::Platform::RUBY]) + Bundler::Resolver::SpecGroup.new(build_spec(name, v), [Gem::Platform::RUBY]) end end diff --git a/spec/bundler/bundler/remote_specification_spec.rb b/spec/bundler/bundler/remote_specification_spec.rb index 8115e026d8f588..921a47a2d3b849 100644 --- a/spec/bundler/bundler/remote_specification_spec.rb +++ b/spec/bundler/bundler/remote_specification_spec.rb @@ -45,7 +45,7 @@ let(:platform) { "jruby" } it "should return the spec name, version, and platform" do - expect(subject.full_name).to eq("foo-1.0.0-jruby") + expect(subject.full_name).to eq("foo-1.0.0-java") end end end diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index 07389aa990774e..7a5307720f227c 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -187,7 +187,7 @@ bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" } - activated_groups = "net_b (1.0) (ruby), net_b (1.0) (#{specific_local_platform})" + activated_groups = "net_b (1.0) (ruby)" expect(out).to include(" net_b"). and include("BUNDLER: Starting resolution"). diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb index 09a5ba0be1fa60..78b701e4887c49 100644 --- a/spec/bundler/install/yanked_spec.rb +++ b/spec/bundler/install/yanked_spec.rb @@ -30,6 +30,72 @@ expect(err).to include("Your bundle is locked to foo (10.0.0)") end + context "when a re-resolve is necessary, and a yanked version is considered by the resolver" do + before do + skip "Materialization on Windows is not yet strict, so the example does not detect the gem has been yanked" if Gem.win_platform? + + build_repo4 do + build_gem "foo", "1.0.0", "1.0.1" + build_gem "actiontext", "6.1.7" do |s| + s.add_dependency "nokogiri", ">= 1.8" + end + build_gem "actiontext", "6.1.6" do |s| + s.add_dependency "nokogiri", ">= 1.8" + end + build_gem "actiontext", "6.1.7" do |s| + s.add_dependency "nokogiri", ">= 1.8" + end + build_gem "nokogiri", "1.13.8" + end + + gemfile <<~G + source "#{source_uri}" + gem "foo", "1.0.1" + gem "actiontext", "6.1.6" + G + + lockfile <<~L + GEM + remote: #{source_uri}/ + specs: + actiontext (6.1.6) + nokogiri (>= 1.8) + foo (1.0.0) + nokogiri (1.13.8-#{Bundler.local_platform}) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + actiontext (= 6.1.6) + foo (= 1.0.0) + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + context "and the old index is used" do + let(:source_uri) { file_uri_for(gem_repo4) } + + it "reports the yanked gem properly" do + bundle "install", :raise_on_error => false + + expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})") + end + end + + context "and the compact index API is used" do + let(:source_uri) { "https://gem.repo4" } + + it "reports the yanked gem properly" do + bundle "install", :artifice => "compact_index", :raise_on_error => false + + expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})") + end + end + end + it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do bundle "config set force_ruby_platform true" diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb index 467e740cb496d3..3ef537be0a5071 100644 --- a/spec/bundler/realworld/slow_perf_spec.rb +++ b/spec/bundler/realworld/slow_perf_spec.rb @@ -13,4 +13,21 @@ expect { bundle "lock" }.to take_less_than(18) # seconds end + + it "resolves quickly (case 2)" do + gemfile <<-G + source "https://rubygems.org" + + gem 'metasploit-erd' + gem 'rails-erd' + gem 'yard' + + gem 'coveralls' + gem 'rails' + gem 'simplecov' + gem 'rspec-rails' + G + + expect { bundle "lock" }.to take_less_than(18) # seconds + end end diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb index ee62dc3577aba8..b583fb871501ef 100644 --- a/spec/bundler/resolver/basic_spec.rb +++ b/spec/bundler/resolver/basic_spec.rb @@ -174,12 +174,7 @@ dep "foo" dep "Ruby\0", "1.8.7" - deps = [] - @deps.each do |d| - deps << Bundler::DepProxy.get_proxy(d, "ruby") - end - - should_resolve_and_include %w[foo-1.0.0 bar-1.0.0], [[]] + should_resolve_and_include %w[foo-1.0.0 bar-1.0.0] end context "conservative" do diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb index c496679ee63562..e20059429c5ee1 100644 --- a/spec/bundler/support/indexes.rb +++ b/spec/bundler/support/indexes.rb @@ -16,21 +16,17 @@ def platform(*args) def resolve(args = []) @platforms ||= ["ruby"] - deps = [] default_source = instance_double("Bundler::Source::Rubygems", :specs => @index, :to_s => "locally install gems") source_requirements = { :default => default_source } @deps.each do |d| source_requirements[d.name] = d.source = default_source - @platforms.each do |p| - deps << Bundler::DepProxy.get_proxy(d, p) - end end - args[0] ||= [] # base + args[0] ||= Bundler::SpecSet.new([]) # base args[0].each {|ls| ls.source = default_source } args[1] ||= Bundler::GemVersionPromoter.new # gem_version_promoter args[2] ||= [] # additional_base_requirements args[3] ||= @platforms # platforms - Bundler::Resolver.resolve(deps, source_requirements, *args) + Bundler::Resolver.new(source_requirements, *args).start(@deps) end def should_not_resolve diff --git a/test/rubygems/alternate_cert.pem b/test/rubygems/alternate_cert.pem index 54a34441b1ad28..55303190f5d81d 100644 --- a/test/rubygems/alternate_cert.pem +++ b/test/rubygems/alternate_cert.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDFjCCAf6gAwIBAgIBBDANBgkqhkiG9w0BAQUFADAtMRIwEAYDVQQDDAlhbHRl +MIIDFjCCAf6gAwIBAgIBBDANBgkqhkiG9w0BAQsFADAtMRIwEAYDVQQDDAlhbHRl cm5hdGUxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTEyMDEwMTAwMDAwMFoY Dzk5OTkxMjMxMjM1OTU5WjAtMRIwEAYDVQQDDAlhbHRlcm5hdGUxFzAVBgoJkiaJ k/IsZAEZFgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -vZQipBa1xH3M9OonkrUYhGZeX9UHAcJhe6jJbUr/uHXkh1Tu2ERWNnblm85upqBf -jyZEnKer7uBcwwkkvmisVgC8uBECymsBxuEIw0rfiKYEnLu0B6SiWFYz3dYPS92b -BK7Vks2/kNyXUmLLGoZ3id2K0eK5C/AJ0j+p84OqPnVhylsjrZmXfIZrh7lkHhgC -IrzPefjE3pOloi/tz6fh2ktb0FYKQMfweT3Ba2TMeflG13PEOW80AD5w0THxDutG -G0zPNCDyDEoT7UU1a3B3RMHYuUxEk1GUEYWq9L6a6SMpZISWTSpCp0Ww1QB55PON -iCCn+o6vcIy46jI71dATAQIDAQABoz8wPTAcBgNVHREEFTATgRFhbHRlcm5hdGVA -ZXhhbXBsZTAdBgNVHQ4EFgQUyvn/FwcZnA7AkzPjmoooB4/tKgcwDQYJKoZIhvcN -AQEFBQADggEBAHilxCg0dmOn3hxPjWMf/tAvG/S25iAIZZPuWo71DSSsn/zPRWDZ -OkDNL7syJ7S2jjrWWCIyLxhj89ZI7Oyd3elOB2zd4RsDij1y9Gv0ZPqNoTp0Repk -aPtRRLEwk9j2C37Tv+qA2PnTLp8MA0DVkb1/yuSd03b2K/AZEHT8Jtf3WC3RqGSK -A1+M8CvPSSgPY7oveFFerpqAzfC4tlgyPZjSqBjZucEIlxBD2lA/3JQ8Ys8+0705 -j2jGMl5r1Y22nl0A0+cHGtPX3irtR8bcEAO+rpEfpHNF2APaYsCT7Frk1CtuAHYB -mEwqWPQKU5ZJOV4uu69Hw5Po2bfgyjKV+N8= +pebGm7NOnx+DtWG1xQsJBfTfwNlZvfzY61nlZccrhU6vx0AnYNiDZAG3J/gFQmYZ +9gJ98rzEwfLMCGq9R/TZM+lAEaLhzYZCu3X4QdhKxr1xZ/SFC+1f8KVuH4tLXORW +30DwayPhNxnrOvup4pWLiYuXUSZpV9CGMvPSUCW2odhMkBMKqaTTPjxoXJIcgacy +prkNgIq48cSvqWG/e/HrMRtkqvFbD5ta00uO1mlpajYYw1RRftEwktFo8vQgDBo9 +NT/EqoE72tffaDnLq6rQrVtw4Kr9fy775DjNAWXyiCBjnJgOQSXCGPsM/AEdFrh/ +LKQQv2M/M7WNevnEUgsEIwIDAQABoz8wPTAcBgNVHREEFTATgRFhbHRlcm5hdGVA +ZXhhbXBsZTAdBgNVHQ4EFgQUYPwS8g98+4Tq/8gcEK1iilkGXH4wDQYJKoZIhvcN +AQELBQADggEBABSKUFmTk53+yrVFT3TvX5iGgXudNdACQqcnknAg66Q8+wMA8WT1 +M2oZJIH4SWSKUGMYubpYuc53tTtMnR594LPidyNbxo8KXMYoNfEkZCk6hh0eKVdx +zPJSZ4fOQ4mKFCd7HrycOr4bxuGPTVQERYJ45vZnhvVJDIRMgshnQuivP3VBwXkQ +gKLTCh2ew2ZJgPi1dfqdNMMSw7k4OQtQVhwbAkHgwL1TUShAO9lHzxFHlQgssfR0 +f6c89eB035Vn9s21StjerTOlC9+v4hOO7QhvbsCcUs2wWiE1BWo1QqnVBCjGKyVE +fISkJd1Sn5j+Vx/NJ7EfZcOGGQMdxHC+c90= -----END CERTIFICATE----- diff --git a/test/rubygems/alternate_cert_32.pem b/test/rubygems/alternate_cert_32.pem index adeffda24aa2b2..e727189470e0da 100644 --- a/test/rubygems/alternate_cert_32.pem +++ b/test/rubygems/alternate_cert_32.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDFDCCAfygAwIBAgIBBTANBgkqhkiG9w0BAQUFADAtMRIwEAYDVQQDDAlhbHRl +MIIDFDCCAfygAwIBAgIBBTANBgkqhkiG9w0BAQsFADAtMRIwEAYDVQQDDAlhbHRl cm5hdGUxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTEyMDEwMTAwMDAwMFoX DTM4MDExOTAzMTQwN1owLTESMBAGA1UEAwwJYWx0ZXJuYXRlMRcwFQYKCZImiZPy -LGQBGRYHZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2U -IqQWtcR9zPTqJ5K1GIRmXl/VBwHCYXuoyW1K/7h15IdU7thEVjZ25ZvObqagX48m -RJynq+7gXMMJJL5orFYAvLgRAsprAcbhCMNK34imBJy7tAekolhWM93WD0vdmwSu -1ZLNv5Dcl1JiyxqGd4nditHiuQvwCdI/qfODqj51YcpbI62Zl3yGa4e5ZB4YAiK8 -z3n4xN6TpaIv7c+n4dpLW9BWCkDH8Hk9wWtkzHn5RtdzxDlvNAA+cNEx8Q7rRhtM -zzQg8gxKE+1FNWtwd0TB2LlMRJNRlBGFqvS+mukjKWSElk0qQqdFsNUAeeTzjYgg -p/qOr3CMuOoyO9XQEwECAwEAAaM/MD0wHAYDVR0RBBUwE4ERYWx0ZXJuYXRlQGV4 -YW1wbGUwHQYDVR0OBBYEFMr5/xcHGZwOwJMz45qKKAeP7SoHMA0GCSqGSIb3DQEB -BQUAA4IBAQA1Vs3lcPpqnbsdtFDgrzApZuNgtyCRbbSrshq37dem9wSI4aFjAPLx -QGgf3c+XZczK9FkR3VUHcK8yZFrCKpv9giZDvzCUOMB/HSBAzNiNbhgUC1S0THir -xFriDITPoY7mrdJlX41Ssqk6tIKZsYP63UVghy7f9wxqXJvyfJZSB9UeM+0baQVL -tGTKXmvzLw1Pc/LHTrt7jcZT9UbBsxNNy0Wk9FPPePCUUlegRjInd/sNevywzL/T -1DL0BefqF6iyWcu86Udo+eli1JDzeUsfHOL7oqJGlWhlZHRDJ1M89n5KwPg8SCx5 -TpemV2Wy0nRTzITnmggexlMibSJ0iOvC +LGQBGRYHZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXm +xpuzTp8fg7VhtcULCQX038DZWb382OtZ5WXHK4VOr8dAJ2DYg2QBtyf4BUJmGfYC +ffK8xMHyzAhqvUf02TPpQBGi4c2GQrt1+EHYSsa9cWf0hQvtX/Clbh+LS1zkVt9A +8Gsj4TcZ6zr7qeKVi4mLl1EmaVfQhjLz0lAltqHYTJATCqmk0z48aFySHIGnMqa5 +DYCKuPHEr6lhv3vx6zEbZKrxWw+bWtNLjtZpaWo2GMNUUX7RMJLRaPL0IAwaPTU/ +xKqBO9rX32g5y6uq0K1bcOCq/X8u++Q4zQFl8oggY5yYDkElwhj7DPwBHRa4fyyk +EL9jPzO1jXr5xFILBCMCAwEAAaM/MD0wHAYDVR0RBBUwE4ERYWx0ZXJuYXRlQGV4 +YW1wbGUwHQYDVR0OBBYEFGD8EvIPfPuE6v/IHBCtYopZBlx+MA0GCSqGSIb3DQEB +CwUAA4IBAQBJeq9kniAdddOY2r9MhRYb8/rzWL1mcCgbIzkwXCTeOyVt0nWZZwAy +arEy8ofkXt1O3Bm+59+dPORnG5WJeBGVmGDHo9HxDjm4dgTramYqgtxbthmB/pu+ +QhJ5DQg59odomoRRG7dkqacd8GMdcJVYcb3OzV3Fe5v2gtvnVPZ711APtjZ7sZUR +4XBA+ok95QFeSUYo4WIOCHh16pPtZ9ium5SZ7ChVVNj5rthr+sS+rQKjEdjG510w +UOkg8rUjEvXPGjM80/DnOvzyRJvbcCWBWWHquft3wqbjTomnQtqHne2SwRFEyfpd +JLEuY9QtEUCUy7obccN39+nT9jUOyg3B -----END CERTIFICATE----- diff --git a/test/rubygems/alternate_key.pem b/test/rubygems/alternate_key.pem index 14ca734aba9fa8..ae587b34258050 100644 --- a/test/rubygems/alternate_key.pem +++ b/test/rubygems/alternate_key.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/test/rubygems/child_cert.pem b/test/rubygems/child_cert.pem index 9293cfc966d320..26c916cf96ad64 100644 --- a/test/rubygems/child_cert.pem +++ b/test/rubygems/child_cert.pem @@ -1,20 +1,19 @@ -----BEGIN CERTIFICATE----- -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 -----END CERTIFICATE----- diff --git a/test/rubygems/child_cert_32.pem b/test/rubygems/child_cert_32.pem index 3484803a2090ad..4e2fcf10df3f2c 100644 --- a/test/rubygems/child_cert_32.pem +++ b/test/rubygems/child_cert_32.pem @@ -1,20 +1,19 @@ -----BEGIN CERTIFICATE----- -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 -----END CERTIFICATE----- diff --git a/test/rubygems/child_key.pem b/test/rubygems/child_key.pem index c56d0699c81f8b..61fdfb628e3ce0 100644 --- a/test/rubygems/child_key.pem +++ b/test/rubygems/child_key.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/test/rubygems/encrypted_private_key.pem b/test/rubygems/encrypted_private_key.pem index d9667689a62992..2d0022a6c005f4 100644 --- a/test/rubygems/encrypted_private_key.pem +++ b/test/rubygems/encrypted_private_key.pem @@ -1,30 +1,30 @@ -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED -DEK-Info: AES-256-CBC,CB6FD0B173EF450C6EE21A01DD785C1D +DEK-Info: AES-256-CBC,4851D8D265711252BA5EAD983C957412 -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 -----END RSA PRIVATE KEY----- diff --git a/test/rubygems/expired_cert.pem b/test/rubygems/expired_cert.pem index c76fe4c58f9543..12b8836607861a 100644 --- a/test/rubygems/expired_cert.pem +++ b/test/rubygems/expired_cert.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDCzCCAfOgAwIBAgIBBjANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv +MIIDCzCCAfOgAwIBAgIBBjANBgkqhkiG9w0BAQsFADAqMQ8wDQYDVQQDDAZub2Jv ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTcwMDEwMTAwMDAwMFoXDTcw MDEwMTAwMDAwMFowKjEPMA0GA1UEAwwGbm9ib2R5MRcwFQYKCZImiZPyLGQBGRYH -ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdlNYcvFB6x -gpT8QcnMybVelGLUKyQL/hQPTK9J5FyqB8hEDLt2ruMwKsHU6fu/mgG+sLvHF1nq -WhZADhn6rwCfNP03025vktOZ6iJfU60vytY/f3gkiGNkP21l0hz0+Ms15f+52gk+ -sFXqwGBbDteI0x57UsHi+KAN67QuPDnthYDtwhXIA5pcdx2wH+NW8F82HEZvm1hc -pA75BDVk4vPxnpDfvPOKSYn9dWghmUtaPmqyVvs8XkDxHDfY54/D3ziPehP+zQzE -g8C07Sq/z6vLSOQ3uaYn0nBDuNE6XP3ijJ4Zvs2eJEdOMyS1H3CsnkWiePCrLtKd -w8d/F+D5bocCAwEAAaM8MDowGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwHQYD -VR0OBBYEFF9DbvaajkUl6SLjfTdepNU2AoUbMA0GCSqGSIb3DQEBBQUAA4IBAQAP -Z8vKQ1OjQCHx8MJ0gy6WN2CIjh0Uqf3fSfza3nd+K8jlJPkF7znEBZ345iCSmNwn -EPrz/OrqiId2OHW2OCJv3c9NEb7hgaTswGfdq9LmqMRVeQy57FWh2lRdjVZbquWy -IvM72hjVLqnMVisWbDxf32YghnmT37SzZKsHSXU0xUx7rVSIShnoMjLWQtIQDoqI -3isMAeKals2NNJOy0TGKJ9SspIDl/IYm9sEO1HKiuxubrdN9q+wEQdizt59sXkol -4AYga+5TN+KtsSRclAGjfKYfuRrQ85IbVsRhQIYX1ZOJtMseEhBRnIeySKV4xWsi -kXAaxX5wIyoM+S1vaKcu +ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFC3H6Q26GO +bllXTiJBeALUbouzzxaGinwZeYYhhXKWEcfA0D8k+22w/JyndeEYc29JxwjFNwxM +/DyyQplptzbM1Q5BCcLNQaQhmpOryosjWuYWW1JUDGTG3nEg0qRCrtjwVoAU++Ko +NkQboM0wFQJxkBns0J1sr+O5Ml73YzKKtdVWWvg89osxyb4wRxkEoa9a/b9/aiK7 +WoeQhF00cRKoa/eoIN8usTjzKsYudHUtEQWibD7IxWdY3nMntGf1jyLYi40lVUbL +FFytWtlZGMVKpMVIOMMCQ+ex+ExGQaJrQF5QEqPq1L/BvXgTZkv5P6CS3YjBdVC2 +/ntfos3gMhsCAwEAAaM8MDowGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwHQYD +VR0OBBYEFLEaVAlnRWACAtfO9B1gSonf51jZMA0GCSqGSIb3DQEBCwUAA4IBAQDI +x70p19RI73Y9AEfBd0Wmk7KsZQyP55kjTAdaM37crhmClkUQRrk/Y2a0pXOhsrNI +0YO7t44fJKwMu++1dxh/bllKbuOkca6ApxTb2IfhOejx3IXXWzp+C1F9pmG9q5QW +0FDt//0SFZJIqDq9N+zFiA2z0MM1L7X/r29uUgAqkU7rsytKavHXru9EGQrrkxwh +5dM5VI+pnJgk9HYpBDPwiFVd0yFelebqdlv/nnW11tVBMTkMloVYyhei5yZbWKFk +f0Aj5g94+S107oRMyzq5e6lxQcJBhmKfOXqGaz2wue3/IHnwio9dMArayug9GLEr +ycy6PubJImhM/dboDIdO -----END CERTIFICATE----- diff --git a/test/rubygems/future_cert.pem b/test/rubygems/future_cert.pem index 05d3f9ebe3778a..06e08bb4c6288f 100644 --- a/test/rubygems/future_cert.pem +++ b/test/rubygems/future_cert.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDDzCCAfegAwIBAgIBCDANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv +MIIDDzCCAfegAwIBAgIBCDANBgkqhkiG9w0BAQsFADAqMQ8wDQYDVQQDDAZub2Jv ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCIYDzk5OTkxMjMxMjM1OTU5WhgP OTk5OTEyMzEyMzU5NTlaMCoxDzANBgNVBAMMBm5vYm9keTEXMBUGCgmSJomT8ixk -ARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnZTWH -LxQesYKU/EHJzMm1XpRi1CskC/4UD0yvSeRcqgfIRAy7dq7jMCrB1On7v5oBvrC7 -xxdZ6loWQA4Z+q8AnzT9N9Nub5LTmeoiX1OtL8rWP394JIhjZD9tZdIc9PjLNeX/ -udoJPrBV6sBgWw7XiNMee1LB4vigDeu0Ljw57YWA7cIVyAOaXHcdsB/jVvBfNhxG -b5tYXKQO+QQ1ZOLz8Z6Q37zzikmJ/XVoIZlLWj5qslb7PF5A8Rw32OePw984j3oT -/s0MxIPAtO0qv8+ry0jkN7mmJ9JwQ7jROlz94oyeGb7NniRHTjMktR9wrJ5Fonjw -qy7SncPHfxfg+W6HAgMBAAGjPDA6MBkGA1UdEQQSMBCBDm5vYm9keUBleGFtcGxl -MB0GA1UdDgQWBBRfQ272mo5FJeki4303XqTVNgKFGzANBgkqhkiG9w0BAQUFAAOC -AQEAM/0YoOjHEs1zdE6oG10XKdDcYn192AYhkMaITrfJUJiVUYehmcC+fa7aV5Hp -CYasFm5SawiyHwcI92GROaM97gJy7Tjpg8zcpkXg/vUA/8ItzD0UYphs5efZ9B3R -zOfxQDasTZJgg0uCxw23Bil7Anf5/KipuiU92Cc3fjpI6jWOuezGWqSqC2KGJLM+ -S59/oLqQNR67j7WBH3lHe3qbUehpNPkSrTdcLEbvspZM2udrRN9QYUUwBxD9dMlB -eC4hADgWEh5uPZO5sM2+qUdyIRa+fDUpcbTEhLQy6NceHuCNLK230DdC2xszrtNY -yNFy3B5BHgxfZeycaC4pMED0gA== +ARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRQtx+ +kNuhjm5ZV04iQXgC1G6Ls88Whop8GXmGIYVylhHHwNA/JPttsPycp3XhGHNvSccI +xTcMTPw8skKZabc2zNUOQQnCzUGkIZqTq8qLI1rmFltSVAxkxt5xINKkQq7Y8FaA +FPviqDZEG6DNMBUCcZAZ7NCdbK/juTJe92MyirXVVlr4PPaLMcm+MEcZBKGvWv2/ +f2oiu1qHkIRdNHESqGv3qCDfLrE48yrGLnR1LREFomw+yMVnWN5zJ7Rn9Y8i2IuN +JVVGyxRcrVrZWRjFSqTFSDjDAkPnsfhMRkGia0BeUBKj6tS/wb14E2ZL+T+gkt2I +wXVQtv57X6LN4DIbAgMBAAGjPDA6MBkGA1UdEQQSMBCBDm5vYm9keUBleGFtcGxl +MB0GA1UdDgQWBBSxGlQJZ0VgAgLXzvQdYEqJ3+dY2TANBgkqhkiG9w0BAQsFAAOC +AQEAATJNbSaVk8fM1uhqQnlBiQ/0TzchHADy+WmsbIbOiUAc8c29sjpCC4x0o5Gk +Zn18+BsXUjfyF6zyY4wPABZp9SCDIxvLxP2QVmbCtY2TJeKV95c4mWEnrFcCqYZ/ +je13m5iLnAu1WqVjSbFn1KamTf25pfBd9mqgQVwrdJMGoviJC18LyKJTZs7aV+Bu +tsF9fVMIP8p1Xu0Es/hBXFRwths41seqT0dYFszhwTCBC9tt85TTixWmEXpdyrov +vLFULJg4ijn9YZeKMViC6V3zgHUl9ZnMlWdFpVZ6cf7kA/1acW1ESEZnARLQhvMF +a1gof1D2Mj5k3AtQJ2TtPXytxw== -----END CERTIFICATE----- diff --git a/test/rubygems/future_cert_32.pem b/test/rubygems/future_cert_32.pem index aa74bdffbca223..c0231101414f93 100644 --- a/test/rubygems/future_cert_32.pem +++ b/test/rubygems/future_cert_32.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDCzCCAfOgAwIBAgIBCTANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv +MIIDCzCCAfOgAwIBAgIBCTANBgkqhkiG9w0BAQsFADAqMQ8wDQYDVQQDDAZub2Jv ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTM4MDExOTAzMTQwN1oXDTM4 MDExOTAzMTQwN1owKjEPMA0GA1UEAwwGbm9ib2R5MRcwFQYKCZImiZPyLGQBGRYH -ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdlNYcvFB6x -gpT8QcnMybVelGLUKyQL/hQPTK9J5FyqB8hEDLt2ruMwKsHU6fu/mgG+sLvHF1nq -WhZADhn6rwCfNP03025vktOZ6iJfU60vytY/f3gkiGNkP21l0hz0+Ms15f+52gk+ -sFXqwGBbDteI0x57UsHi+KAN67QuPDnthYDtwhXIA5pcdx2wH+NW8F82HEZvm1hc -pA75BDVk4vPxnpDfvPOKSYn9dWghmUtaPmqyVvs8XkDxHDfY54/D3ziPehP+zQzE -g8C07Sq/z6vLSOQ3uaYn0nBDuNE6XP3ijJ4Zvs2eJEdOMyS1H3CsnkWiePCrLtKd -w8d/F+D5bocCAwEAAaM8MDowGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwHQYD -VR0OBBYEFF9DbvaajkUl6SLjfTdepNU2AoUbMA0GCSqGSIb3DQEBBQUAA4IBAQA6 -lip2nqmXyhz1uBDAxlNAv9nN2JPicWpRIvQ4KLNwmZKnagPWfleZ4TbLqsn723w8 -lD2VqFNX/Vj1XNuEJg8pXME+qxbMgtWxGsXC0z6k2Q3rT81QTdhXJ7nqdoe2i8y1 -423Fft2L6Dcgmx2USJwZsNy53pK9smxI9NipuRtL4W34PHHpaFsC2646daxZ2F8M -No3R9C4CtSFJDrM0XZoFiAnarbqoGCJs2q0NtcdV8D5m6xGeNShWJMLNbVx4DgsT -E90gVxVqPaqm5ytAIfdPWVUsyJBoD15jDVH5AZtkBmFRNoz60KPt3HpiRPspKWCd -tVabH2JRC0wDYRwEEMKB +ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFC3H6Q26GO +bllXTiJBeALUbouzzxaGinwZeYYhhXKWEcfA0D8k+22w/JyndeEYc29JxwjFNwxM +/DyyQplptzbM1Q5BCcLNQaQhmpOryosjWuYWW1JUDGTG3nEg0qRCrtjwVoAU++Ko +NkQboM0wFQJxkBns0J1sr+O5Ml73YzKKtdVWWvg89osxyb4wRxkEoa9a/b9/aiK7 +WoeQhF00cRKoa/eoIN8usTjzKsYudHUtEQWibD7IxWdY3nMntGf1jyLYi40lVUbL +FFytWtlZGMVKpMVIOMMCQ+ex+ExGQaJrQF5QEqPq1L/BvXgTZkv5P6CS3YjBdVC2 +/ntfos3gMhsCAwEAAaM8MDowGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwHQYD +VR0OBBYEFLEaVAlnRWACAtfO9B1gSonf51jZMA0GCSqGSIb3DQEBCwUAA4IBAQB2 +EjAxPVbDwoYehhEJEiMnMWk8GL6oo1uVwGJgCM76Yml69FVyWrre2GcSYiv+9f7K +AzE9S2uREeJuGcSV0VR0MeXoHXwQpQyBV12gCL1eQH15oxFfTi9XbVLhh5CyTKc1 +J6lYtQVpZPlLRoJN3oBq3J8PN8hWBWuhb2VIrpHXNBXAMOZ+hJM2B+EYifH6tcI5 +YK/Bw5Tes81QU4stQTLKybJzQkct02Rm7bYIM3GX/FGMMEPZE4jhnQ5btSNYVBlY +HXh6qfZVhZ8qpAy50yrlxROLa5zJSuTQ28QWh95XLzzHiHJ4Q4qZQzhcRGUQLfM3 +X6P8rtGWzXtB++CKKQIG -----END CERTIFICATE----- diff --git a/test/rubygems/grandchild_cert.pem b/test/rubygems/grandchild_cert.pem index dbd14bce802fc8..c1d9887b60ab69 100644 --- a/test/rubygems/grandchild_cert.pem +++ b/test/rubygems/grandchild_cert.pem @@ -1,20 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDNTCCAh2gAwIBAgIBDDANBgkqhkiG9w0BAQUFADApMQ4wDAYDVQQDDAVjaGls +MIIDFDCCAfygAwIBAgIBDDANBgkqhkiG9w0BAQsFADApMQ4wDAYDVQQDDAVjaGls ZDEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwIBcNMTIwMTAxMDAwMDAwWhgPOTk5 OTEyMzEyMzU5NTlaMC4xEzARBgNVBAMMCmdyYW5kY2hpbGQxFzAVBgoJkiaJk/Is -ZAEZFgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkfgy -EbzcRpXvpNA0s75R8gOVk7ENEPX5uXUElbzHbVEmHkC/NFsWZXV0vxGFfZrrnEkT -Y2kDaMMkfZX9rriRIvsZpxyqrdX87QfQTZ1ktDoytVnd+gw9A6AXB6PR7uoPymso -f/lZYJ8BWP9fIU39nogiptFqsgkpOtKSFjJfMILkcMAeBPs2B5HV5l4oLrpJ7Ns/ -0vazCXGakTByAXNKBagJWR43gh+RUQWF6Uh04VQTQ7ENGWI83088SKAPtCCcgKxr -ROHI025S7o7vEfDEqEn+gtu+4ndaLuRp+2AmF3YK8dEDiLXrrvEvG1r4+gIB/6tS -MUfkkJtBleZrDoIAgQIDAQABo2EwXzAdBgNVHREEFjAUgRJncmFuZGNoaWxkQGV4 -YW1wbGUwHQYDVR0OBBYEFAnex9R5fpiHNpHxq91rMog/PZA6MB8GA1UdIwQYMBaA -FHzVU9N7sklKBPrHElxoZ32Fg64IMA0GCSqGSIb3DQEBBQUAA4IBAQCYT10tUm9O -2xSGVmsbYv5YVrMr1Ucuq6Y+mXuTqqXuidILXZZl3LDwaYj64AhmMUqfITybGbR3 -sbWZrz2TWI2qWo+wcYDq+k0K+ys2PBavXdbjw4nVMf/9xYb2bCHK4bxxIG6rkmCw -zxYRTx+UivRf7Hw+6ZRtkHDxX/qPfMQK1PbmvK9c2VYXGN3+fRRIJqPnknN26Hv1 -gangMI7oqcaNwCvL16E/CpN8uwdJNB1LcCgjFJP7Ora+LIEJVe4eZvmuOYkYaYH2 -7u6CglItKwNjVZ5jULUfBuJqFkRiOxkVMVjChe9QvNXz/fkhW2sH05CR+TDyh4rD -ZeON5sdo9TV8 +ZAEZFgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyh7u +lK+O7leQKBQzSSvhEur49Xu4sQJllkVbrVvkv2jSo68woESgljSS8jCSX0I9IJ5T +0kHzNRpiuOpfWKZx7E4cKeqW6nmV7D/B+Iy1QnJbMzUdnxp1Y+CMCK0JrLIsFBnx +ndVmiS/S817W3coqkmZqce4e33/O0qmCChGxGJZuAVQFGYI25SiYWPbysVEjwXoW +6zO0D1m8QZLYYCeDSRpxKgItBoJK+Up+bm/nsrkFvUf4kAZNLgik5zvflM3/Mjyx +zxgY2MO07YaULOlr0vZN6Wxnq2F3jkuZY8JbEOpQSDGSgU2wJ1iFH+GLvj7LsBuR +ScjL0FJ4s1EjqjjP8QIDAQABo0AwPjAdBgNVHREEFjAUgRJncmFuZGNoaWxkQGV4 +YW1wbGUwHQYDVR0OBBYEFEuKocg9sisZIWu12gxzse+7/sHfMA0GCSqGSIb3DQEB +CwUAA4IBAQAFaz3FUNzDYt/Cb++6U1PXsucAy3pKDj8bUW7slJFleNsSooIA9A5b +axpnHst1Lt6i/bmvbi8F8q/0iq4PtsZvusmwVhJkCinEUZ9inddSJdxpWSyzOww5 +at8ragidzREd9Fx62flRr3GflzqIufkKN8TDjYrB+n45ijnSxDcwZhu7m/tmP/e2 +FxqODaIO9sVrPAU7YbXb0m3p3Em7RK22d0tqy1dHhcRIR5/yMQB4MSEvQ7ytRD2x +aHAP7A4rDbkRwG+LQ/4L57RJ1kU3InqpOEQnCgpwu3xw/6Alti9TXS7sGhXL4Aav +OLsFS/1BLSJx/DSSK58Vg6JFxFh6X2NK -----END CERTIFICATE----- diff --git a/test/rubygems/grandchild_cert_32.pem b/test/rubygems/grandchild_cert_32.pem index 5077a43452e0f8..5d1ad45cb7b1c1 100644 --- a/test/rubygems/grandchild_cert_32.pem +++ b/test/rubygems/grandchild_cert_32.pem @@ -1,20 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDMzCCAhugAwIBAgIBDTANBgkqhkiG9w0BAQUFADApMQ4wDAYDVQQDDAVjaGls +MIIDEjCCAfqgAwIBAgIBDTANBgkqhkiG9w0BAQsFADApMQ4wDAYDVQQDDAVjaGls ZDEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwHhcNMTIwMTAxMDAwMDAwWhcNMzgw MTE5MDMxNDA3WjAuMRMwEQYDVQQDDApncmFuZGNoaWxkMRcwFQYKCZImiZPyLGQB -GRYHZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJH4MhG8 -3EaV76TQNLO+UfIDlZOxDRD1+bl1BJW8x21RJh5AvzRbFmV1dL8RhX2a65xJE2Np -A2jDJH2V/a64kSL7Gaccqq3V/O0H0E2dZLQ6MrVZ3foMPQOgFwej0e7qD8prKH/5 -WWCfAVj/XyFN/Z6IIqbRarIJKTrSkhYyXzCC5HDAHgT7NgeR1eZeKC66SezbP9L2 -swlxmpEwcgFzSgWoCVkeN4IfkVEFhelIdOFUE0OxDRliPN9PPEigD7QgnICsa0Th -yNNuUu6O7xHwxKhJ/oLbvuJ3Wi7kaftgJhd2CvHRA4i1667xLxta+PoCAf+rUjFH -5JCbQZXmaw6CAIECAwEAAaNhMF8wHQYDVR0RBBYwFIESZ3JhbmRjaGlsZEBleGFt -cGxlMB0GA1UdDgQWBBQJ3sfUeX6YhzaR8avdazKIPz2QOjAfBgNVHSMEGDAWgBR8 -1VPTe7JJSgT6xxJcaGd9hYOuCDANBgkqhkiG9w0BAQUFAAOCAQEAnIlgmwVS3BGk -cT9LxE3bbQt1WGeLLxAF5ScqkNgFm3OvIxrKPX94WRa6tjWPEeJ9rl64QMKwJ8h1 -GzNbf0DUH8nMcSJLL1J5HUqdzwysQ96HNz54RuZ3ZF8w7QD4Kv3Vvh4IIsANchSj -unVYjcDVH9lSsbqTwC+bnUxYZ1Xu530fKZ440BG1ju9vLLZu5JaSOoJWRuHNOlQF -FbYj5AIjPwMNUidzoE9sUWdRzvARwecismWkPjfM0x1AmxVMs8hA5tS2QoCEvOxt -Hl3q6k7BaC8BRENkA/XRRIqgTMoTv86OOZIMFVaTiuHyU1XUxNHKv+ybAeAlP+H7 -hM/Hqka4bQ== +GRYHZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMoe7pSv +ju5XkCgUM0kr4RLq+PV7uLECZZZFW61b5L9o0qOvMKBEoJY0kvIwkl9CPSCeU9JB +8zUaYrjqX1imcexOHCnqlup5lew/wfiMtUJyWzM1HZ8adWPgjAitCayyLBQZ8Z3V +Zokv0vNe1t3KKpJmanHuHt9/ztKpggoRsRiWbgFUBRmCNuUomFj28rFRI8F6Fusz +tA9ZvEGS2GAng0kacSoCLQaCSvlKfm5v57K5Bb1H+JAGTS4IpOc735TN/zI8sc8Y +GNjDtO2GlCzpa9L2TelsZ6thd45LmWPCWxDqUEgxkoFNsCdYhR/hi74+y7AbkUnI +y9BSeLNRI6o4z/ECAwEAAaNAMD4wHQYDVR0RBBYwFIESZ3JhbmRjaGlsZEBleGFt +cGxlMB0GA1UdDgQWBBRLiqHIPbIrGSFrtdoMc7Hvu/7B3zANBgkqhkiG9w0BAQsF +AAOCAQEAFW6StPxdeONHUmWXUAKXNncP66aaHPAk+rQ84IvuyJKqR3UrrKdCbQLQ +LpL/lQZDkjGJSXdNHUhoB4MQsDJ8TQs2BdwDqOIvQLsZxTKZaXPfc1diFP+z/5f0 +9wbWezUateJwShDpp1RltCBmFCdibPZQUCpsgAsfqsgIWNpzhqy9dI/Upg3vECVc +c4g+EsEtiNXSbjd0kWUXCGBAeTMu4paBLVtt81fuffKJfnAKRhzrfFy0PoL3Kt2T +DkMr4MWfkMOyVlrSkvjPtW1oHVnxfZgqaE91J0Ht9qiHvekti3JDqxqcKaYuIgAL +w9HK9z5uQA2KrMvqn/jiuD8lBFsnuA== -----END CERTIFICATE----- diff --git a/test/rubygems/grandchild_key.pem b/test/rubygems/grandchild_key.pem index a9b9aef6248bbc..63635844b0d3d9 100644 --- a/test/rubygems/grandchild_key.pem +++ b/test/rubygems/grandchild_key.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/test/rubygems/invalid_issuer_cert.pem b/test/rubygems/invalid_issuer_cert.pem index ea11756bb03fdb..5cb9d22a87b947 100644 --- a/test/rubygems/invalid_issuer_cert.pem +++ b/test/rubygems/invalid_issuer_cert.pem @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -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 +vpqxaQ== -----END CERTIFICATE----- diff --git a/test/rubygems/invalid_issuer_cert_32.pem b/test/rubygems/invalid_issuer_cert_32.pem index bb73772735ea8c..2b71179270653b 100644 --- a/test/rubygems/invalid_issuer_cert_32.pem +++ b/test/rubygems/invalid_issuer_cert_32.pem @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -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 +Wkw= -----END CERTIFICATE----- diff --git a/test/rubygems/invalid_key.pem b/test/rubygems/invalid_key.pem index 74bedabcda1d80..bc02147403cddc 100644 --- a/test/rubygems/invalid_key.pem +++ b/test/rubygems/invalid_key.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAzTJw5KFI4oMaEOU9nf4kBbCnsoHamz9WUuEixmXS4AfMIAY8 -0jUjjqCaVrWDsaHjb19h4nRWB5FPEj+tFY0nQdfYj2OFomoCSI6pXlGTpGQm//SV -TVVK7UgGMpGZXqpeI14FYDx24Q8113rMpftSgpxiCYbcEwO4TLrsCpZSBk6Nz1ZS -d3qL6bBZyIy42q6Xr28y8HmCjhz+uC2nVjc+EsMiuF5g/2z4HNrUw2nQVQWWs+cv -mDNlOw/8hfa7uQXcISEoJldk9fShlz+luohLY65D1Pp+3pK1LAD70iEDIJ4n1sbI -FgrvxLKJJ8QsrIYpuTvhKZECqOk3Y8lfej+CTwIDAQABAoIBAFo5I4pjoDh4jK2B -HmapqA0Yb6P9lLFOWBZ5B2FUxDPdOdOa6oNC+i9sTnBxv0YLeIUv20pG/My3B51u -ghxHxEsfLQlfKRMQqZBdqfrew5w0rTE9yagHKLrMQG1bt6P4JQxH+vUloN+0YGgu -hm005EKpoDGwKnPx3sdeKQs+rTI37fGLw40ZPSod2B7UkSna4/u/uXa1yFbONlqq -P4ozsDd1xoCaBJfwie2h93KneSPwyHHumlVgbFAJcQycuD5qproYbea9PppppQ+4 -17M9ccuQpS2j9rZWgk7+KqwLrYqzajmtHoQH4zkBYd0JQ4ywGKg307xDhppMZpq3 -uMqnXgECgYEA9iYB7vX91Q3HsRtUAd0aZ91lDIv9RXBP+PuK3OUrtChdtZx7Mih3 -uTgtfVEnP7bxgnPpwecnF4Ncginuu7TBtxjoztFjuFNzk/8Fz/J3i/H3oB0SdjhL -uqVa6P4hPak7FnWynZHivuuIe+eKYLvK0TCMbYaz6G81xi0Whtbm498CgYEA1Wja -PgscP/9YykCC6eRP+ixo6chGzSNJR+WRW9CJYpXEicPlaT5AI3nSZVXXSLvXIbq9 -NoExPu47pDzr9Gd02qYmFWUOolUa21W4s/x4d7lU+wJzS6ZNTFoC8woZagL2kZ5G -+we5ifbUz7eG+ahZODGMGA9BJVT3PI6zPdKtr5ECgYEAy0ORnypGBXUOrUMa+TsD -fjfGJTlI2dmoQLw/7K/Wijw3PizNUxs12p74eZ7VYXkKMKbVpwjiMDmK3/YOrbTT -rwaD4Z3p0iIftFwJCbJ5Y/hZez/mqfdNGgFIdFS/UHL6V060RAhfjTdlCqSmkcEh -9+M2Y4+z60JCzrcW/hxiqFMCgYEAj9ntwoSatkjZAPwbQq2ze18UGQH3N6/hZaVJ -JiqbcOijYnm52gcsFL25JLWIOG7lxMarZGIRX+oWKc8m/cf+7KOyaBmGk8XqJI7T -wf8c9RboQYqVTRj8YcsK0eis2NjGe8HE9tFuL6FCMgHz6bWg7k/3rwAZWaC8RwWp -rLKmgQECgYBXGjEvogVeYMgnpzjoaa99wvfp6FtbRx1jZ+FOSBoH5uCRDalD5Q16 -0UVnoPcnj0Hi7Hvvl6jTLesRW/LDra5Hqyxs4yuSBagEUFv/PvY0eYGZ5egGZgaa -PlVmxgk33xYXar8wGHLkstwqZY/OqT89cKvJqeLKMb0G2Re13oPVww== +MIIEowIBAAKCAQEA8RlnMh3LSbWNJtwcHBRzQwumr/4kl3AukauMPfkK4jsM0/Qy +If6Y0duXJOoxp0Hd+ie3bqRAUiMNm1zV6zVm+3iY2jWVJxCy9tUZEUNArb/F1nQ7 +QZsrfULsgnYGhr3cX7vh/s14Posqf75JYLEc0IpXzdG6ZDp2Ib9Z1RlC7yT3GOlH +LAja7YTA0MRORuEZqaPnM2Ptrylw/KJ8HHNOJn0Vm9ZVoIQ4O90isXg10i15fJgR +DANEtYP+dD1n3BvDgpAK0D9RR36s5uhyO95q7XAuJmvK6aFlHDAgplTj5poA1hMV +ve1uZQqehH1otq8q/ESeUXTmQej3FwehLl+3DwIDAQABAoIBAAQ1PSEES8XZESzH +nwakcBCQValGW8LNi+/cmSGEb7qxbNC5jIhzBkgfYjIcPkqc3TAeXQFlhNPfgGog +7YDh7o60Ck2WAtOVjdy+AmZ+kH9Cf271719+mFHi3E2N8XY3k8Q9+2dJlHAvA0pg +eq0L2k9kzWcuYmeZF50Q1xNfkf9U0qkb6tbx15NOGFhQMUZ/mMcbYifW/bQnU2YO +2vgxWFDItk0c82gpi/uo22gu0TjhcBp/ByKMgXV8xrLCW7Lsh1DvJ3LtigD0Zp5s +XtdXcF4KOo81VeUijDbByIQQJTxBlfmgdcyarAb7kV107wvDh55Xd8m68lZUrkj5 +tc3dRf0CgYEA+crRUXgtdeiHqN5gZRg6MF6S5yxFiAaKgeeKm/x/2xpMmoiyWhFv +tK1DLxRHEGiFuWc9IGgrWu2udEg8VE4j2EExvtFi/w3lSHxBArjeq7oCabnDk261 +lRCNzYb0HU5UZL3vBsHW8pCdnDfaOxlrT2+cDVN7LHd20DsIZROG7T0CgYEA9xdH +wSNsHqxcB44A7iNNvHxhU3Vi6QCIpzp8sfjc/fd7Ghe/MIn67gs5XPtT+DvQME0N +4hNEy2GmbHjVJQXqlf4VSWdFJ1RnKwL+4rnOiWq5sNrxmlXuTvOTRLimpjOsLKCR +G3dgKXiH7/qU9zjLljKOWgo7gqv3cPrPIL8H0jsCgYEAp99SRTpK8w7O/QJWLhrW +IGOuvoXWRNCAjUUatjI/ivRlvwVXN4i4VeiLWjx6deCI3k0vm3PmjzllIOjbAp1x +IYjO8bqumVKxYMGAv1+W5jogHQWnFpI0nnV9lz63GGrn9Lxgw13KI0JlafNvSoCO +ydfsPFh6UywGQXMq9SLmbtECgYAHWC3N09u23bCumM32Rh5dZ+UMsgSKoHVvYxGp +yJfpBJ4oHGSWcMOAp9zAosfQLRb3GJM9EQ2ObgygVMchHpfmdXL0h5lKnfujD6e7 +3YICG6YBV8CusbcvqZXLCSIK9qY7fVpS0q2NDgQcYfpjjtCeWkpY6szyCWKFfS8C +7iqxWQKBgBIwdnksccXP01svXJ0tMhiAKx1jKY/aJBPIpcSqxTFNe2WpgzynRpUg ++MKAO2Ed6Rec3TTefGRNKDPb5OUsYZUMHeNbIJ1Y1S2pORRffrE3TFWBUM0LkOTj +r4Dl5bcnDRL93Phbx7MAmjMO3TZWbQGfEgDuqk3sS1QzKMJnBLAO -----END RSA PRIVATE KEY----- diff --git a/test/rubygems/invalid_signer_cert.pem b/test/rubygems/invalid_signer_cert.pem index 7f8b5cc727e2ff..59364ee51b5930 100644 --- a/test/rubygems/invalid_signer_cert.pem +++ b/test/rubygems/invalid_signer_cert.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDEDCCAfigAwIBAgIBDjANBgkqhkiG9w0BAQUFADArMRAwDgYDVQQDDAdpbnZh +MIIDEDCCAfigAwIBAgIBDjANBgkqhkiG9w0BAQsFADArMRAwDgYDVQQDDAdpbnZh bGlkMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTAgFw0xMjAxMDEwMDAwMDBaGA85 OTk5MTIzMTIzNTk1OVowKzEQMA4GA1UEAwwHaW52YWxpZDEXMBUGCgmSJomT8ixk -ARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNMnDk -oUjigxoQ5T2d/iQFsKeygdqbP1ZS4SLGZdLgB8wgBjzSNSOOoJpWtYOxoeNvX2Hi -dFYHkU8SP60VjSdB19iPY4WiagJIjqleUZOkZCb/9JVNVUrtSAYykZleql4jXgVg -PHbhDzXXesyl+1KCnGIJhtwTA7hMuuwKllIGTo3PVlJ3eovpsFnIjLjarpevbzLw -eYKOHP64LadWNz4SwyK4XmD/bPgc2tTDadBVBZaz5y+YM2U7D/yF9ru5BdwhISgm -V2T19KGXP6W6iEtjrkPU+n7ekrUsAPvSIQMgnifWxsgWCu/EsoknxCyshim5O+Ep -kQKo6TdjyV96P4JPAgMBAAGjPTA7MBoGA1UdEQQTMBGBD2ludmFsaWRAZXhhbXBs -ZTAdBgNVHQ4EFgQUk9+Lsaj37L/MP/nHPYBAoVp52U4wDQYJKoZIhvcNAQEFBQAD -ggEBAGJurQiRw3Vb2LPUhoL9BHyPy8TY+ZXz7eZyzf72saiATkHTuainb9jvLTzf -9ZNYNnjxGNwR4QnDhlh8Vb8vHaYfQGGwiLhd5ZbKjkid/41Y9XmNh+oHCrmwGcS6 -vX4QmTTmPQRBZ2Ilckr+wb1fdpLYk0wW/JFMFdAzp1OO3kP23hYElqia6qeJGw4k -4fe9d56huHHptSJrlMTjHwaqXq6QRjfjF5Za1unCjxQMO2vNQC/dUYRaq/M0kwFN -6A2xHKukkgA9cyjVpFVpxhE3/iFz8OE1GZg7VVcT+Jzhhxaajubpe+ZtDN5rbln/ -NI4WP/ZM3cnxVrHG84G3AYRpkhc= +ARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDxGWcy +HctJtY0m3BwcFHNDC6av/iSXcC6Rq4w9+QriOwzT9DIh/pjR25ck6jGnQd36J7du +pEBSIw2bXNXrNWb7eJjaNZUnELL21RkRQ0Ctv8XWdDtBmyt9QuyCdgaGvdxfu+H+ +zXg+iyp/vklgsRzQilfN0bpkOnYhv1nVGULvJPcY6UcsCNrthMDQxE5G4Rmpo+cz +Y+2vKXD8onwcc04mfRWb1lWghDg73SKxeDXSLXl8mBEMA0S1g/50PWfcG8OCkArQ +P1FHfqzm6HI73mrtcC4ma8rpoWUcMCCmVOPmmgDWExW97W5lCp6EfWi2ryr8RJ5R +dOZB6PcXB6EuX7cPAgMBAAGjPTA7MBoGA1UdEQQTMBGBD2ludmFsaWRAZXhhbXBs +ZTAdBgNVHQ4EFgQUde3TNs2UED6K/5OjePyV3q1l4UgwDQYJKoZIhvcNAQELBQAD +ggEBACbQA7m+zqdAE1/GrJKcXnC/Ujr1tu6rGpe0uExTwySptgVieUGAOvzlLLrY +r2b/nGmK4bgHkeDPSVuC3F8NhgESvPTuXFSw+DU+1m+mhHxdpW30uSbbebgqV5Ea +GpckCZj1SEdsn/1SDauXCmkrs/MBuK49mYXv3jejX0Rp0STu1NxnU13yrKF0MtrL +fzZ/QF8bOZdg5DwtqMwhDPp72x58KpqAPN9GgdlsTiwOMGy4avds1g5KMGmkK8pK +6I58NEMCOuN4CxFK4oWD9xuD4j+wtHy/UYxqor4rn+UKYHfr5fWtAyRJ3gD8vMUu +C+Nl2vB8t4OVwS5FCcn0ZK18bPU= -----END CERTIFICATE----- diff --git a/test/rubygems/invalid_signer_cert_32.pem b/test/rubygems/invalid_signer_cert_32.pem index 657608e5031ac9..97feb8fd0a9cf9 100644 --- a/test/rubygems/invalid_signer_cert_32.pem +++ b/test/rubygems/invalid_signer_cert_32.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDDjCCAfagAwIBAgIBDzANBgkqhkiG9w0BAQUFADArMRAwDgYDVQQDDAdpbnZh +MIIDDjCCAfagAwIBAgIBDzANBgkqhkiG9w0BAQsFADArMRAwDgYDVQQDDAdpbnZh bGlkMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTAeFw0xMjAxMDEwMDAwMDBaFw0z ODAxMTkwMzE0MDdaMCsxEDAOBgNVBAMMB2ludmFsaWQxFzAVBgoJkiaJk/IsZAEZ -FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTJw5KFI -4oMaEOU9nf4kBbCnsoHamz9WUuEixmXS4AfMIAY80jUjjqCaVrWDsaHjb19h4nRW -B5FPEj+tFY0nQdfYj2OFomoCSI6pXlGTpGQm//SVTVVK7UgGMpGZXqpeI14FYDx2 -4Q8113rMpftSgpxiCYbcEwO4TLrsCpZSBk6Nz1ZSd3qL6bBZyIy42q6Xr28y8HmC -jhz+uC2nVjc+EsMiuF5g/2z4HNrUw2nQVQWWs+cvmDNlOw/8hfa7uQXcISEoJldk -9fShlz+luohLY65D1Pp+3pK1LAD70iEDIJ4n1sbIFgrvxLKJJ8QsrIYpuTvhKZEC -qOk3Y8lfej+CTwIDAQABoz0wOzAaBgNVHREEEzARgQ9pbnZhbGlkQGV4YW1wbGUw -HQYDVR0OBBYEFJPfi7Go9+y/zD/5xz2AQKFaedlOMA0GCSqGSIb3DQEBBQUAA4IB -AQA6WW6YyykIJmVFW8Og1R3d8fbFtQarDMGN+oZrCN3Eb9Twoy36Yr/h0bZgkCEe -arB+VCD1DfEii5luZowUDr/BlIjIR2cNIMFpmQ8ZLIpVWQz/BYHrbw7CHO5df3mg -HYTKlEoUMimOEWvCYnSgzKWCgSWU/jBQQ0bcUBk2HHpdT4BnLPe/7rs+TZmwd/Dz -r80sNXQ6vqTkQS+te2hqyh62r+WeaFx7aqnFjGtwiOqMmYPr80uYOy/rX26mmfwm -vyf8JlA4uTt795Krsc4Brc+BO0bOPfDDGhRs/2tvyHhOOqBbUxQeeV5ogiAZmA31 -ZrzbFysajMrWZ+1GV8QXOcBi +FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8RlnMh3L +SbWNJtwcHBRzQwumr/4kl3AukauMPfkK4jsM0/QyIf6Y0duXJOoxp0Hd+ie3bqRA +UiMNm1zV6zVm+3iY2jWVJxCy9tUZEUNArb/F1nQ7QZsrfULsgnYGhr3cX7vh/s14 +Posqf75JYLEc0IpXzdG6ZDp2Ib9Z1RlC7yT3GOlHLAja7YTA0MRORuEZqaPnM2Pt +rylw/KJ8HHNOJn0Vm9ZVoIQ4O90isXg10i15fJgRDANEtYP+dD1n3BvDgpAK0D9R +R36s5uhyO95q7XAuJmvK6aFlHDAgplTj5poA1hMVve1uZQqehH1otq8q/ESeUXTm +Qej3FwehLl+3DwIDAQABoz0wOzAaBgNVHREEEzARgQ9pbnZhbGlkQGV4YW1wbGUw +HQYDVR0OBBYEFHXt0zbNlBA+iv+To3j8ld6tZeFIMA0GCSqGSIb3DQEBCwUAA4IB +AQBM/EjV791vKu2KQQAhd03eUXfMlvObwEj9nGtEEnSFH5i16V1m8Y5ZY7Aw+VR5 +/xJ9J2AvFG6EXclDVmkDQpo1fUVhWM/SPKD6HJWuB/eyjA30xSj+UeLmBYjdKzHt +kK+HeEaGVQaT5j8OPOW6meGriucdBWtwbhTdyZgqvwpuZwbtPpElYXwGgSijKqHI +GG6RWzeANdG/4AIChrp8CIgFfW4sbmlIZK3BAXuSvZ9A/deYqC2DAadJIoTJ9H6w +0TgYlyTidaWtCkqRBXc3qrwpc/r3wJwpgan0kJAQ2kGc5W7gYUylpoS56sFq23YX +Fmm0pRq5UZj9jKZUIwcqAoGo -----END CERTIFICATE----- diff --git a/test/rubygems/invalidchild_cert.pem b/test/rubygems/invalidchild_cert.pem index 936dc650c9b32c..281dcce655cf41 100644 --- a/test/rubygems/invalidchild_cert.pem +++ b/test/rubygems/invalidchild_cert.pem @@ -1,20 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDOTCCAiGgAwIBAgIBEDANBgkqhkiG9w0BAQUFADApMQ4wDAYDVQQDDAVjaGls +MIIDGDCCAgCgAwIBAgIBEDANBgkqhkiG9w0BAQsFADApMQ4wDAYDVQQDDAVjaGls ZDEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwIBcNMTIwMTAxMDAwMDAwWhgPOTk5 OTEyMzEyMzU5NTlaMDAxFTATBgNVBAMMDGludmFsaWRjaGlsZDEXMBUGCgmSJomT -8ixkARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDl -rDAlDZwmP6Zxc4sSaOdSRRXJBmbQubxmWpqU8bXrTKCkvg1l/2U6weJIS52zW6Te -Zok7Uus5jywyeNSJ/MBHb7X5ytAPQsvLu/3WFwaVfHJzimQI4vtmx9+CDgZzipYp -ett7IF18He8DPiFur1xCn8pp0qOOV8mXL/8TpUgJfmaOJosqgFqorj5niqF52IwJ -vtur/gwpq2xpCtYaCUB/dFzzefLV37kt58S6jTmZnYf4kIdFKhhyTeGmDRf/wOz+ -kK/H/aKtpsYgzI//bo+bsuWNFceIdWrdCBr63cVs4ql7VN7p2xfn9ckEfwH6wFut -QLquA/6fRkgUFF8fxUiHAgMBAAGjYzBhMB8GA1UdEQQYMBaBFGludmFsaWRjaGls -ZEBleGFtcGxlMB0GA1UdDgQWBBSwAwFqdxjicsovAzZ1ZeSAWlnKcDAfBgNVHSME -GDAWgBR81VPTe7JJSgT6xxJcaGd9hYOuCDANBgkqhkiG9w0BAQUFAAOCAQEAYxom -7nHgbfqLiMtVwx8D6LM6P3sk3p5HwoHJU0zzFVfPA0iuhhC2vWyPoHJroq6d31DG -DpPpOeVQxTYyQ6CtD6jGUpE+0MjmRoavFnvqFH5lf4OKO0dQ8xUa9fALo7fmK0v0 -ueDlgsHw3mwoVNX2Xu/+jCTuiK2KGG3J+pxr6JNA1++gQEghFFJjM8rw1/mYrDW0 -CSCyx/LeNyYbt9xzeVgo83XNO5TCfwwh8+df52I6Hj5BDFhhkP8KOai4ejq2CmZ8 -by9ylCUXc/aiz9mzX/NzIAYLuRzkmrI+lxDbUJpG/hV7MCS/TZegjD+SUSu9EI1O -RVerg89R7kQUWZyJqg== +8ixkARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDs +dqLesclj2/6HnJLw4al4swiDFBA4W0KeOS3DXOa2k5+rNfGacKOQ/AkciUnzqLiC +9D49x/YLlnlJYeSMG57C7gixhLHHDviCneQwy8TlCpuhUXnfcpmHQO+X/c+lrVB7 +wHTPjl5yZYzLFtPa8QAEtJa83E2GrUd+JEOwTRErOcBucIRP5HbeR2aD7BVP1V+F +gaUhOZb/n2e53diQF7BZurB45DTWQ2L1R/m8VldQiWoTZluWqnCGeEwtpZJXt9kU +8y5rQcxI3p8Qx4q0ilLKsE7c/sw2wzEFWoZ4BeURnjxdvPgYeOPk4K3a34pFg7lZ +u1s9XvjChJVsD8HRnbQtAgMBAAGjQjBAMB8GA1UdEQQYMBaBFGludmFsaWRjaGls +ZEBleGFtcGxlMB0GA1UdDgQWBBS3tDKiI1oVuEkKtGux8TsEy/8iuDANBgkqhkiG +9w0BAQsFAAOCAQEAQsgdq4NZBnQRk+eNbwS4go3S+m5cPPWY7kG94kpQ3aC3nwR4 +wnICb/hd+kRCB2azTTBY8PA0SkHxIXWgRFjdEpIAekm3Xtag+lCC1q7jtDTNoHw4 +KOdfeBEF621FHL5vBvJmnRMH8f4sCjVZZE8RJcaWAK7uJJXi6gzUm3xz0UnylZNr +vA+Z+cM5pFnCfxxqXUyT0MRb47O/wqH4f5SybKpOxO3+vnzpSeN++zgdMVjA/Pl0 +0SB8VKNKXGLQrvY2S3GuRoh7OD1DhRPzUTQ8gwwBAlhw2i3i5t9ib0ujw9MUO3GR +dqn4Yco98R0+wFpY8feGhr5uJNDuIC10l5LHLQ== -----END CERTIFICATE----- diff --git a/test/rubygems/invalidchild_cert_32.pem b/test/rubygems/invalidchild_cert_32.pem index 07687eb156c4ae..87885157f8dacb 100644 --- a/test/rubygems/invalidchild_cert_32.pem +++ b/test/rubygems/invalidchild_cert_32.pem @@ -1,20 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDNzCCAh+gAwIBAgIBETANBgkqhkiG9w0BAQUFADApMQ4wDAYDVQQDDAVjaGls +MIIDFjCCAf6gAwIBAgIBETANBgkqhkiG9w0BAQsFADApMQ4wDAYDVQQDDAVjaGls ZDEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwHhcNMTIwMTAxMDAwMDAwWhcNMzgw MTE5MDMxNDA3WjAwMRUwEwYDVQQDDAxpbnZhbGlkY2hpbGQxFzAVBgoJkiaJk/Is -ZAEZFgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5aww -JQ2cJj+mcXOLEmjnUkUVyQZm0Lm8ZlqalPG160ygpL4NZf9lOsHiSEuds1uk3maJ -O1LrOY8sMnjUifzAR2+1+crQD0LLy7v91hcGlXxyc4pkCOL7Zsffgg4Gc4qWKXrb -eyBdfB3vAz4hbq9cQp/KadKjjlfJly//E6VICX5mjiaLKoBaqK4+Z4qhediMCb7b -q/4MKatsaQrWGglAf3Rc83ny1d+5LefEuo05mZ2H+JCHRSoYck3hpg0X/8Ds/pCv -x/2irabGIMyP/26Pm7LljRXHiHVq3Qga+t3FbOKpe1Te6dsX5/XJBH8B+sBbrUC6 -rgP+n0ZIFBRfH8VIhwIDAQABo2MwYTAfBgNVHREEGDAWgRRpbnZhbGlkY2hpbGRA -ZXhhbXBsZTAdBgNVHQ4EFgQUsAMBancY4nLKLwM2dWXkgFpZynAwHwYDVR0jBBgw -FoAUfNVT03uySUoE+scSXGhnfYWDrggwDQYJKoZIhvcNAQEFBQADggEBAGK07MzH -/TJftVEgrghvEHIna0gadQUbWfrpMWxt/Vj7jsd4LvRaZEOWObMRxhtjMvMA2q3A -qKXoP1KddPkFGfAVdUsNKTKLsaMvDceZ7aN3SGEWv5IYGXtfjUEVhWj7CmpS05li -Phw9uPQVUwkkY20G2UGInRzxCYqyeW5KZIaep49KebaXCrjrV5Xy13UxgtpbmNMj -yKPvyA5u0J8TK4gQmx1Az0gujpT1KSwfMyEDrsbIqYDVMp4kF2yLlsrdmNT9Jhwj -R+M6AMhNqpLrjsklqE6TX2pCQGo+JFxCDOKQmvv5B7AhH0od46Jlwk//aIAheQQK -4rhDHEHYLCxGhrw= +ZAEZFgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7Hai +3rHJY9v+h5yS8OGpeLMIgxQQOFtCnjktw1zmtpOfqzXxmnCjkPwJHIlJ86i4gvQ+ +Pcf2C5Z5SWHkjBuewu4IsYSxxw74gp3kMMvE5QqboVF533KZh0Dvl/3Ppa1Qe8B0 +z45ecmWMyxbT2vEABLSWvNxNhq1HfiRDsE0RKznAbnCET+R23kdmg+wVT9VfhYGl +ITmW/59nud3YkBewWbqweOQ01kNi9Uf5vFZXUIlqE2ZblqpwhnhMLaWSV7fZFPMu +a0HMSN6fEMeKtIpSyrBO3P7MNsMxBVqGeAXlEZ48Xbz4GHjj5OCt2t+KRYO5Wbtb +PV74woSVbA/B0Z20LQIDAQABo0IwQDAfBgNVHREEGDAWgRRpbnZhbGlkY2hpbGRA +ZXhhbXBsZTAdBgNVHQ4EFgQUt7QyoiNaFbhJCrRrsfE7BMv/IrgwDQYJKoZIhvcN +AQELBQADggEBAFPyjOoghY6Iba9+psCWC//rwLtDfZPQ3kO9hFtd4CGvalPtR103 +Oc1Dk5pxHSWDTdeQfOb75mqWuqs4ET7IUyw8yY+bKkOOhY1ldwaJhTyP/fkElnrL +prsLSpB3AdQceS1Ob2wpMaaGIPDzJlW0LG8cQNeU3SRTTdBjX5zjG6imHKG2yuJd +76PM0tl8ooslPABlYSPSLpd1PAARqxG2ekfrxZioCLknyhBtcRsl1aMWWBiG4BH9 +xZoJvEHwpOMTxMaAFXSkyMjZAoOzAn9qP6eLbwwzeuckIrZXxYBfGcC29VOPPAN4 +IPX0IUZ7/LlzzOE5iuVS/RIBRcD+qpEzSXs= -----END CERTIFICATE----- diff --git a/test/rubygems/invalidchild_key.pem b/test/rubygems/invalidchild_key.pem index 9706c9566ebcd5..1c9997150d8b30 100644 --- a/test/rubygems/invalidchild_key.pem +++ b/test/rubygems/invalidchild_key.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/test/rubygems/private_key.pem b/test/rubygems/private_key.pem index c6ed3fc24e6aaf..4e6ce13e65baf1 100644 --- a/test/rubygems/private_key.pem +++ b/test/rubygems/private_key.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAp2U1hy8UHrGClPxByczJtV6UYtQrJAv+FA9Mr0nkXKoHyEQM -u3au4zAqwdTp+7+aAb6wu8cXWepaFkAOGfqvAJ80/TfTbm+S05nqIl9TrS/K1j9/ -eCSIY2Q/bWXSHPT4yzXl/7naCT6wVerAYFsO14jTHntSweL4oA3rtC48Oe2FgO3C -FcgDmlx3HbAf41bwXzYcRm+bWFykDvkENWTi8/GekN+884pJif11aCGZS1o+arJW -+zxeQPEcN9jnj8PfOI96E/7NDMSDwLTtKr/Pq8tI5De5pifScEO40Tpc/eKMnhm+ -zZ4kR04zJLUfcKyeRaJ48Ksu0p3Dx38X4PluhwIDAQABAoIBAAx09qfJtBiYoxwN -LaQjzjrl/+re2RsEnXLGtLEysYDH0m5vyfbFXTxg4D2uZ38pgf9xPluq9CznyK5x -M9txEUbdkibp2Z0VRnrisE7Ag0yXCuQos4awSUoEMsgkVJ99B2qv5x7BqN0ZQiwS -nSBOhms5rmRNTxpIlrHqd0jgS/EPggnqVzNcM4/K8PJFthwEBKDmzOyiRByvz54Y -shzOnTjGtV2oGNgwpzmCXce1yO7dh2IdKnSnmeFwyU88GxEYnGh5MIFuTiyErP72 -k6iEUfiXy0hxk/iXmKs8UyD1lVnwTNWcZcpV8yw4a06Z6nkSnwQm0SSOVIo/w35V -jdVdUkECgYEA3GhZ70MD/Q47GFvz6BovwQvxhjFN+nIEbBfi7OTkuXprKdhVhjaR -nERPZpZjHWrcfgbFcvPY7/GJLTPN/VF1nhOsOZpzfAmCgBujRXrzlAGpU877ZNJA -QKPgzo+iv/RsQCIdrzF1gwHkqD2v1HRLaqb2+dVumiG4Qp3NXgasT2cCgYEAwm1U -uRDXgQKGODeLK8eSVpfMjD5umBVu7m4D3ZmipbN6sMBxGMAlsU40eQ7DBFH0AFft -s2D88JdjlwoOrbXYYpOc6iWD/QkygJfPpA9VQx92hv8KBd82gLHuXYMd0T0G3yZO -gPPioeRgl2TvgVCfjn6AYr3Ubt3rB5aBlSplE+ECgYEAiXhcf6rg1fkGSs8vddi/ -aDy2y+f8pvRuZa0QUIkDT9xW8qaH0Uo/z6ObknTCJRr9o209wdDtwdp4oMTq+dDQ -92N1zAfVd8vGpXiXgUKKognXPvqeOegZQzfzg2J7NBaTXfzpXtgOX0PTBkxTWsOe -NkslR/YjIedeMc6SxM6MsokCgYA3mTYyGevWe5dQOin1IgPp+UzICg5sNSzcx98Z -HpcRVWrPYqi00DW3J0sAF0WTVbA17O8PbbvHPTOAfKLH8Alp3xZvKr08vcWQWllJ -bA0Qvc2SOxptpXAbi0ZDvXvoWtA9PeITJCr56qnogTewPhLyl6A1HF3EOne8WsDB -nDb9YQKBgEyUDWhDBGXUfQN0fWy5ksqCCeHXQzvt6aEUstWvkkbnnarUfLAhBIqC -2B6omokICmWzvAfDt3UsRbb3QJUBxbbVsZVM7Vr+kY2cQ1Ma093I/2mXDoq3bV+j -LZM5+Uc7xSfiCi1hbVhGm96DXofudddo86W5mhXp3xhcQP1Fl4JZ +MIIEpAIBAAKCAQEA0ULcfpDboY5uWVdOIkF4AtRui7PPFoaKfBl5hiGFcpYRx8DQ +PyT7bbD8nKd14Rhzb0nHCMU3DEz8PLJCmWm3NszVDkEJws1BpCGak6vKiyNa5hZb +UlQMZMbecSDSpEKu2PBWgBT74qg2RBugzTAVAnGQGezQnWyv47kyXvdjMoq11VZa ++Dz2izHJvjBHGQShr1r9v39qIrtah5CEXTRxEqhr96gg3y6xOPMqxi50dS0RBaJs +PsjFZ1jecye0Z/WPItiLjSVVRssUXK1a2VkYxUqkxUg4wwJD57H4TEZBomtAXlAS +o+rUv8G9eBNmS/k/oJLdiMF1ULb+e1+izeAyGwIDAQABAoIBABS+vR8q+ysD9LqU +piFNPjmBl8fvtmr0QYxV9i8u6YzXhcG4wqxP3UEdl51sGIqNIvx4YuviqTdl9meK +AII28bvUCJcWKYKPWJ+N2UYkdLrgcWV39+use2IOOQUcDOXN2omVIe4mpmXs6RxN +ZN6SkrKgUXnQeUnx+Wno9S5m9gWPfrAMTr8iwBquo5JMFqJmAlfq1cIActU7SE6M +7LJZ8rbVGuGmZAyqJw/I5xvxts6NlTFBm2AyGq74wtbUD4OlFDtOTA57LmtRswmr +MK+JG5LIJIyc6qKQwmHU8vLstJEcVUyCJDbpjDkApd6GQ2FwXXode+S9C40DZKvi +hnaGfKECgYEA1ArI5tt3GdNR4CbY4e4yDHEuSdSkMVsRa9a7gdtrrjyNitBMbZNE +HZNqswijBCPZnXPtBxeytMUmlU7uFc7xx3QQvuBSJ6HOb9j0qtWVL+jl8R9XLogH +sE/m0yMPs2IowevJoQ95KRZIirLywZ8QgTsySEv9U4hx2CdKpn7IeRECgYEA/KR9 +WI1n6pxDbxcjWMaOdziIcgDBk7v05Qssb1OePiz+y+zfrBVqDjsvsokRLoElSAkR +TAV+/9hRgAu4cZJpP0FMdFe+sVkr1UfcDqvJdgumBL+xUNxRV/QFvuprLa6XbiQO +jUclJR6BPdXeVQPDUHuQBgnwXrJdRtMl9Fj8GGsCgYAhZKVo/e0OyyHczCFhy1Jk +dTqV8//7qdzff8y90aFuqiicUNuIciXLBplKIrURhNfTnRsZ/9hr8ZR29Rw3oQSg +pZ2xgcBOb4QER0WY1dQN3H7B726abF/Rm3O9kor5dB75EzoIvFgXaWP5O99RMMy3 +nWv4yMbXbeiH4wA1okfOEQKBgQDkaAnyrxUN5GyK2M5aCljurCufddOMrtb+5BUu +KNjduWw6DVNCjrGtYBEFRipEcvmzoI8EvctNntJAA1ijRQzl5TEr1dBPIiEg17C5 +itG+aVWU4YF7a1QXQkSXj/OJ/1hkeTC2xWVto6CQuPQixB4mey+AZifsVTFDQM4F +lRWFNQKBgQDQWRXaJgc4Jeur1/I1iMAcihN/o4Lra4baM07FdpFWrePJ4IimXHG+ +buws6BuytnQP56hqIQYLQSWCF192dcnHNbodXz48Ug6rKLSBalxH3pvThYLpkBSk +7OrfHzO4TTVaCH1mcPjzj9vmRog2tvrF/m6/8UAZQIBGDv2+cHk4rA== -----END RSA PRIVATE KEY----- diff --git a/test/rubygems/public_cert.pem b/test/rubygems/public_cert.pem index 4517de53c6b490..7481c26b794697 100644 --- a/test/rubygems/public_cert.pem +++ b/test/rubygems/public_cert.pem @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDLjCCAhagAwIBAgIBADANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv +MIIDLjCCAhagAwIBAgIBADANBgkqhkiG9w0BAQsFADAqMQ8wDQYDVQQDDAZub2Jv ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTEyMDEwMTAwMDAwMFoYDzk5 OTkxMjMxMjM1OTU5WjAqMQ8wDQYDVQQDDAZub2JvZHkxFzAVBgoJkiaJk/IsZAEZ -FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2U1hy8U -HrGClPxByczJtV6UYtQrJAv+FA9Mr0nkXKoHyEQMu3au4zAqwdTp+7+aAb6wu8cX -WepaFkAOGfqvAJ80/TfTbm+S05nqIl9TrS/K1j9/eCSIY2Q/bWXSHPT4yzXl/7na -CT6wVerAYFsO14jTHntSweL4oA3rtC48Oe2FgO3CFcgDmlx3HbAf41bwXzYcRm+b -WFykDvkENWTi8/GekN+884pJif11aCGZS1o+arJW+zxeQPEcN9jnj8PfOI96E/7N -DMSDwLTtKr/Pq8tI5De5pifScEO40Tpc/eKMnhm+zZ4kR04zJLUfcKyeRaJ48Ksu -0p3Dx38X4PluhwIDAQABo10wWzAZBgNVHREEEjAQgQ5ub2JvZHlAZXhhbXBsZTAd -BgNVHQ4EFgQUX0Nu9pqORSXpIuN9N16k1TYChRswDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQEFBQADggEBAJasznHQK7el5CY19uRM -QZSP2moi65jbESKA5CaSOK0erWfLL7k0W69Rr4RG8CQDXmtlVLzdQlEGkvJEfMLs -GumlIGDEsLZU/3tQ8lML2fMUKipv/fsyWoe6wUHyfsywYYT4WAxyKUtY6AepwN6Y -sJ6+qDWUFziSVgDnU2bBdqzIOw+ww1NtRGE3PEam+a/VL7l/a2DYcot5cvcc8RYR -6gyBXp4fvSGasM3iQp7sWdNV04H8m8+lYBLtsfuucgLDu45uEuvKL1tRcRXvtomp -rKB5y3B5qT/bcc+3b0tbOU6s7CBIdyzIflJI7GuIbZk6lZ+V8Yem+tWt1ArL3Hqf -Myk= +FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ULcfpDb +oY5uWVdOIkF4AtRui7PPFoaKfBl5hiGFcpYRx8DQPyT7bbD8nKd14Rhzb0nHCMU3 +DEz8PLJCmWm3NszVDkEJws1BpCGak6vKiyNa5hZbUlQMZMbecSDSpEKu2PBWgBT7 +4qg2RBugzTAVAnGQGezQnWyv47kyXvdjMoq11VZa+Dz2izHJvjBHGQShr1r9v39q +Irtah5CEXTRxEqhr96gg3y6xOPMqxi50dS0RBaJsPsjFZ1jecye0Z/WPItiLjSVV +RssUXK1a2VkYxUqkxUg4wwJD57H4TEZBomtAXlASo+rUv8G9eBNmS/k/oJLdiMF1 +ULb+e1+izeAyGwIDAQABo10wWzAZBgNVHREEEjAQgQ5ub2JvZHlAZXhhbXBsZTAd +BgNVHQ4EFgQUsRpUCWdFYAIC1870HWBKid/nWNkwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADggEBAAYrbeZLQujvqrzAa6NN +QsiGLSVoF+ZcySQH+rpoqcvbd8zLKz/vH+SoMM6db4OUgD8BKaeBb8dGhzz/lE8m +YUqW/La4xvf/ffW5yze51Rtr1bPw1Tjrk92nE630Unjf0ZZO0h8shTmq/WlXiEqg +cXxfbZiPP0KZkhn4ulf+RBbKKQGrLYvaVrCZcowSjLWzcfredYxElOpcgHCrL2pl +2ZAjCySqAUpYo8HTZwgY/DeeGS3V4fZ06ZDoG20cxi7t8R+AcNbEfaTMzG3oe5qN +A8+a3Qcf5M4yiJtXRNusMKeoRwLDkYRX9u76iOI5LfKTSqnUyhrUQcSYCTFO4y/V +P48= -----END CERTIFICATE----- diff --git a/test/rubygems/public_cert_32.pem b/test/rubygems/public_cert_32.pem index 0431b8ba1abecf..efcf7c0ed7437c 100644 --- a/test/rubygems/public_cert_32.pem +++ b/test/rubygems/public_cert_32.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -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 -----END CERTIFICATE----- diff --git a/test/rubygems/public_key.pem b/test/rubygems/public_key.pem index 7c29dcd6145d99..c233b35e7c8242 100644 --- a/test/rubygems/public_key.pem +++ b/test/rubygems/public_key.pem @@ -1,9 +1,9 @@ -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2U1hy8UHrGClPxByczJ -tV6UYtQrJAv+FA9Mr0nkXKoHyEQMu3au4zAqwdTp+7+aAb6wu8cXWepaFkAOGfqv -AJ80/TfTbm+S05nqIl9TrS/K1j9/eCSIY2Q/bWXSHPT4yzXl/7naCT6wVerAYFsO -14jTHntSweL4oA3rtC48Oe2FgO3CFcgDmlx3HbAf41bwXzYcRm+bWFykDvkENWTi -8/GekN+884pJif11aCGZS1o+arJW+zxeQPEcN9jnj8PfOI96E/7NDMSDwLTtKr/P -q8tI5De5pifScEO40Tpc/eKMnhm+zZ4kR04zJLUfcKyeRaJ48Ksu0p3Dx38X4Plu -hwIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ULcfpDboY5uWVdOIkF4 +AtRui7PPFoaKfBl5hiGFcpYRx8DQPyT7bbD8nKd14Rhzb0nHCMU3DEz8PLJCmWm3 +NszVDkEJws1BpCGak6vKiyNa5hZbUlQMZMbecSDSpEKu2PBWgBT74qg2RBugzTAV +AnGQGezQnWyv47kyXvdjMoq11VZa+Dz2izHJvjBHGQShr1r9v39qIrtah5CEXTRx +Eqhr96gg3y6xOPMqxi50dS0RBaJsPsjFZ1jecye0Z/WPItiLjSVVRssUXK1a2VkY +xUqkxUg4wwJD57H4TEZBomtAXlASo+rUv8G9eBNmS/k/oJLdiMF1ULb+e1+izeAy +GwIDAQAB -----END PUBLIC KEY----- diff --git a/test/rubygems/wrong_key_cert.pem b/test/rubygems/wrong_key_cert.pem index 23396d7c81d703..2f2cffbc61c864 100644 --- a/test/rubygems/wrong_key_cert.pem +++ b/test/rubygems/wrong_key_cert.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDDTCCAfWgAwIBAgIBEjANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv +MIIDDTCCAfWgAwIBAgIBEjANBgkqhkiG9w0BAQsFADAqMQ8wDQYDVQQDDAZub2Jv ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTEyMDEwMTAwMDAwMFoYDzk5 OTkxMjMxMjM1OTU5WjAqMQ8wDQYDVQQDDAZub2JvZHkxFzAVBgoJkiaJk/IsZAEZ -FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvZQipBa1 -xH3M9OonkrUYhGZeX9UHAcJhe6jJbUr/uHXkh1Tu2ERWNnblm85upqBfjyZEnKer -7uBcwwkkvmisVgC8uBECymsBxuEIw0rfiKYEnLu0B6SiWFYz3dYPS92bBK7Vks2/ -kNyXUmLLGoZ3id2K0eK5C/AJ0j+p84OqPnVhylsjrZmXfIZrh7lkHhgCIrzPefjE -3pOloi/tz6fh2ktb0FYKQMfweT3Ba2TMeflG13PEOW80AD5w0THxDutGG0zPNCDy -DEoT7UU1a3B3RMHYuUxEk1GUEYWq9L6a6SMpZISWTSpCp0Ww1QB55PONiCCn+o6v -cIy46jI71dATAQIDAQABozwwOjAZBgNVHREEEjAQgQ5ub2JvZHlAZXhhbXBsZTAd -BgNVHQ4EFgQUyvn/FwcZnA7AkzPjmoooB4/tKgcwDQYJKoZIhvcNAQEFBQADggEB -ABswixv3veo8S0Omyhbch3t19xAd/I4ZAx/lq6a/Ubl+X33hRbZQ7ulXja6Y5ZCs -iIkezGcpc182e7hZdHuEGGnJ1fJwxz3rbFWvs+MrtRSaCC73HbbxF1x0mQY6Kc7W -PahB2v+Dx5n3dIN6ah5p+STUHmzhKGr3bsQswtSHrFn74wt6mBu/hyCz+t6UaQ7b -rpRpzDFO1q3tZB8MeIHg57Lk2bu0LtfaHdkGDghNa3t7oZfV5A6pruiTy/EdQvFL -ZJpvDgHCyUsDmukMMjz6U4ts+nx7kZ9JoIH9K2lMJo3SGw4iZ8rB+HTfsbUdRfBj -UlyNcLqCW+FPlUHgHgNugFA= +FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApebGm7NO +nx+DtWG1xQsJBfTfwNlZvfzY61nlZccrhU6vx0AnYNiDZAG3J/gFQmYZ9gJ98rzE +wfLMCGq9R/TZM+lAEaLhzYZCu3X4QdhKxr1xZ/SFC+1f8KVuH4tLXORW30DwayPh +NxnrOvup4pWLiYuXUSZpV9CGMvPSUCW2odhMkBMKqaTTPjxoXJIcgacyprkNgIq4 +8cSvqWG/e/HrMRtkqvFbD5ta00uO1mlpajYYw1RRftEwktFo8vQgDBo9NT/EqoE7 +2tffaDnLq6rQrVtw4Kr9fy775DjNAWXyiCBjnJgOQSXCGPsM/AEdFrh/LKQQv2M/ +M7WNevnEUgsEIwIDAQABozwwOjAZBgNVHREEEjAQgQ5ub2JvZHlAZXhhbXBsZTAd +BgNVHQ4EFgQUYPwS8g98+4Tq/8gcEK1iilkGXH4wDQYJKoZIhvcNAQELBQADggEB +AJacpAT2GQQn5Rmb/LYaPhguWFHEdjHBO6rU0p7tE8QJW5pwMgim0mFtQlbuXE2L +mbHrYCyRgVyaPV9gNiFg1sLzQMmmG4Afg1N4cpdg8zLZFZho+eyYGRkH+EtrAN0Q +l8l603zovMXj8q8Q1eeurxkDHA6mDgwwMGiBQgAiPixQVOjvL+5M5PkwNw/kVzjA +0EXumGQkeZ1jUAkVjp7uWbyoVatamUcpYvo51NQQQeuqwzdo2oDYLa27axWxaqj2 +m4TKsz2WnnrSnrpq3NnflXfWcOrnXI3odNlhQoPmtZQVt6P8pNbGrx4XD/WTGkGw +i/6jlkwwjK7A7dSepfnTI4Y= -----END CERTIFICATE----- diff --git a/test/rubygems/wrong_key_cert_32.pem b/test/rubygems/wrong_key_cert_32.pem index 7a3bb9f7148418..aca829ab4487b0 100644 --- a/test/rubygems/wrong_key_cert_32.pem +++ b/test/rubygems/wrong_key_cert_32.pem @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDCzCCAfOgAwIBAgIBEzANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv +MIIDCzCCAfOgAwIBAgIBEzANBgkqhkiG9w0BAQsFADAqMQ8wDQYDVQQDDAZub2Jv ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTEyMDEwMTAwMDAwMFoXDTM4 MDExOTAzMTQwN1owKjEPMA0GA1UEAwwGbm9ib2R5MRcwFQYKCZImiZPyLGQBGRYH -ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2UIqQWtcR9 -zPTqJ5K1GIRmXl/VBwHCYXuoyW1K/7h15IdU7thEVjZ25ZvObqagX48mRJynq+7g -XMMJJL5orFYAvLgRAsprAcbhCMNK34imBJy7tAekolhWM93WD0vdmwSu1ZLNv5Dc -l1JiyxqGd4nditHiuQvwCdI/qfODqj51YcpbI62Zl3yGa4e5ZB4YAiK8z3n4xN6T -paIv7c+n4dpLW9BWCkDH8Hk9wWtkzHn5RtdzxDlvNAA+cNEx8Q7rRhtMzzQg8gxK -E+1FNWtwd0TB2LlMRJNRlBGFqvS+mukjKWSElk0qQqdFsNUAeeTzjYggp/qOr3CM -uOoyO9XQEwECAwEAAaM8MDowGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwHQYD -VR0OBBYEFMr5/xcHGZwOwJMz45qKKAeP7SoHMA0GCSqGSIb3DQEBBQUAA4IBAQCU -VkESWkNeiJ1L3MfkMl2ybP2QPWP5nlhz+NOqwOCJSmEXF86Dq2XTY/GgSu1iPp4s -pPm1RSpnjujtiV7gjCziNif/XqAxeSjU2deApvq4mknyk958N0IS8c86dUXms1DQ -Rncmb3pxxvzP1d6Is35Uwoc5KqheJWR3aDwE8ejaFfO8pq/ncUDB25bc7OyslHMJ -7Ah+2YlFjvHYuPvN8bP44UMYyAPZCCJnjIPn7m7giyQiIo6SA9aiLQ8F7+NN3SU4 -7I9ED0F7RTDxNYISr1sLZ7aNtnXkTAnZclwKohuGfZD3OGAOshLZZXIB2pIRr/Kj -6zxTef39tli6orheYQXp +ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXmxpuzTp8f +g7VhtcULCQX038DZWb382OtZ5WXHK4VOr8dAJ2DYg2QBtyf4BUJmGfYCffK8xMHy +zAhqvUf02TPpQBGi4c2GQrt1+EHYSsa9cWf0hQvtX/Clbh+LS1zkVt9A8Gsj4TcZ +6zr7qeKVi4mLl1EmaVfQhjLz0lAltqHYTJATCqmk0z48aFySHIGnMqa5DYCKuPHE +r6lhv3vx6zEbZKrxWw+bWtNLjtZpaWo2GMNUUX7RMJLRaPL0IAwaPTU/xKqBO9rX +32g5y6uq0K1bcOCq/X8u++Q4zQFl8oggY5yYDkElwhj7DPwBHRa4fyykEL9jPzO1 +jXr5xFILBCMCAwEAAaM8MDowGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwHQYD +VR0OBBYEFGD8EvIPfPuE6v/IHBCtYopZBlx+MA0GCSqGSIb3DQEBCwUAA4IBAQBE +lq/rPaPT+5uui42u4BTFayrQU9EFik9xPG6WJw+IJ/5CprECj6h90FC+GaMNeu2Z +R0l+iEfgx4xBM4DERiRVLZIlHbZzuPp0YRYH9gyJot/+0piaAcnUEvjz2Z8axoFq +lE3/q4s6Yw8xdqwxvc5kiwymv2nEm4+v5w6Pb3K/fP/QQLvDUBWrBUMHPgdYXYk2 +zvc58BBwgGDSpFRO0tw+lnqMTM1y+fbpfuvP9TY1m4y13nuw/g7KxVxYolAWVQs3 +TptnJqqzEwdFa2S9BidUIlDRRY8cuhY6R3DM6THNLjR3c1oyf04nhT3sd/ilAJdD +YjnEtcLlkjxZyLLoWjn2 -----END CERTIFICATE----- From 007946c27d7445e25569be58e255f7bb08e8bd75 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Tue, 18 Oct 2022 03:55:59 +0000 Subject: [PATCH 131/139] [rubygems/rubygems] Bundler: github DSL has used https protocol over git This behavior change was done in Bundler 2.2.0. https://github.com/rubygems/bundler/pull/7142 Signed-off-by: Takuya Noguchi https://github.com/rubygems/rubygems/commit/9510190be1 --- lib/bundler/man/bundle-add.1 | 2 +- lib/bundler/man/bundle-binstubs.1 | 2 +- lib/bundler/man/bundle-cache.1 | 2 +- lib/bundler/man/bundle-check.1 | 2 +- lib/bundler/man/bundle-clean.1 | 2 +- lib/bundler/man/bundle-config.1 | 2 +- lib/bundler/man/bundle-console.1 | 2 +- lib/bundler/man/bundle-doctor.1 | 2 +- lib/bundler/man/bundle-exec.1 | 2 +- lib/bundler/man/bundle-gem.1 | 2 +- lib/bundler/man/bundle-help.1 | 2 +- lib/bundler/man/bundle-info.1 | 2 +- lib/bundler/man/bundle-init.1 | 2 +- lib/bundler/man/bundle-inject.1 | 2 +- lib/bundler/man/bundle-install.1 | 2 +- lib/bundler/man/bundle-list.1 | 2 +- lib/bundler/man/bundle-lock.1 | 2 +- lib/bundler/man/bundle-open.1 | 2 +- lib/bundler/man/bundle-outdated.1 | 2 +- lib/bundler/man/bundle-platform.1 | 2 +- lib/bundler/man/bundle-plugin.1 | 2 +- lib/bundler/man/bundle-pristine.1 | 2 +- lib/bundler/man/bundle-remove.1 | 2 +- lib/bundler/man/bundle-show.1 | 2 +- lib/bundler/man/bundle-update.1 | 2 +- lib/bundler/man/bundle-version.1 | 2 +- lib/bundler/man/bundle-viz.1 | 2 +- lib/bundler/man/bundle.1 | 2 +- lib/bundler/man/gemfile.5 | 4 ++-- lib/bundler/man/gemfile.5.ronn | 2 +- 30 files changed, 31 insertions(+), 31 deletions(-) diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1 index fd49dd084f5711..ba4b76f7dff372 100644 --- a/lib/bundler/man/bundle-add.1 +++ b/lib/bundler/man/bundle-add.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-ADD" "1" "September 2022" "" "" +.TH "BUNDLE\-ADD" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1 index 59bb6a44470336..152aec9dd07953 100644 --- a/lib/bundler/man/bundle-binstubs.1 +++ b/lib/bundler/man/bundle-binstubs.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-BINSTUBS" "1" "September 2022" "" "" +.TH "BUNDLE\-BINSTUBS" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-binstubs\fR \- Install the binstubs of the listed gems diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1 index 4346aa00f82cbd..9eb2e1d7cc899b 100644 --- a/lib/bundler/man/bundle-cache.1 +++ b/lib/bundler/man/bundle-cache.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CACHE" "1" "September 2022" "" "" +.TH "BUNDLE\-CACHE" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1 index bb91ed90d5167b..f6aa6988c432c9 100644 --- a/lib/bundler/man/bundle-check.1 +++ b/lib/bundler/man/bundle-check.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CHECK" "1" "September 2022" "" "" +.TH "BUNDLE\-CHECK" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1 index eaa8ea35fb4465..f89ae3a1b006eb 100644 --- a/lib/bundler/man/bundle-clean.1 +++ b/lib/bundler/man/bundle-clean.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CLEAN" "1" "September 2022" "" "" +.TH "BUNDLE\-CLEAN" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1 index 1582e3c464d93d..08e08ecca9ffa8 100644 --- a/lib/bundler/man/bundle-config.1 +++ b/lib/bundler/man/bundle-config.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CONFIG" "1" "September 2022" "" "" +.TH "BUNDLE\-CONFIG" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-config\fR \- Set bundler configuration options diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1 index 6359f442313c7e..c9463e372c78b0 100644 --- a/lib/bundler/man/bundle-console.1 +++ b/lib/bundler/man/bundle-console.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CONSOLE" "1" "September 2022" "" "" +.TH "BUNDLE\-CONSOLE" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1 index 92f5c80df932c7..dc5f5cf27e3268 100644 --- a/lib/bundler/man/bundle-doctor.1 +++ b/lib/bundler/man/bundle-doctor.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-DOCTOR" "1" "September 2022" "" "" +.TH "BUNDLE\-DOCTOR" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-doctor\fR \- Checks the bundle for common problems diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1 index 158a9e0bf644cc..1b3ad113953f0e 100644 --- a/lib/bundler/man/bundle-exec.1 +++ b/lib/bundler/man/bundle-exec.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-EXEC" "1" "September 2022" "" "" +.TH "BUNDLE\-EXEC" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-exec\fR \- Execute a command in the context of the bundle diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1 index 2c36627559899a..b70cfbccd80eaf 100644 --- a/lib/bundler/man/bundle-gem.1 +++ b/lib/bundler/man/bundle-gem.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-GEM" "1" "September 2022" "" "" +.TH "BUNDLE\-GEM" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1 index ed72024e06b6a8..bf378b09504b9e 100644 --- a/lib/bundler/man/bundle-help.1 +++ b/lib/bundler/man/bundle-help.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-HELP" "1" "September 2022" "" "" +.TH "BUNDLE\-HELP" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-help\fR \- Displays detailed help for each subcommand diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1 index f1ef32b758c108..9445aece25f26b 100644 --- a/lib/bundler/man/bundle-info.1 +++ b/lib/bundler/man/bundle-info.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INFO" "1" "September 2022" "" "" +.TH "BUNDLE\-INFO" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-info\fR \- Show information for the given gem in your bundle diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1 index 83f8d75324f54d..b80652d189ab7b 100644 --- a/lib/bundler/man/bundle-init.1 +++ b/lib/bundler/man/bundle-init.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INIT" "1" "September 2022" "" "" +.TH "BUNDLE\-INIT" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-init\fR \- Generates a Gemfile into the current working directory diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1 index d675dba79b889a..bd440eee658997 100644 --- a/lib/bundler/man/bundle-inject.1 +++ b/lib/bundler/man/bundle-inject.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INJECT" "1" "September 2022" "" "" +.TH "BUNDLE\-INJECT" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1 index 858f56e673692e..34bb58b53d1c10 100644 --- a/lib/bundler/man/bundle-install.1 +++ b/lib/bundler/man/bundle-install.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INSTALL" "1" "September 2022" "" "" +.TH "BUNDLE\-INSTALL" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-install\fR \- Install the dependencies specified in your Gemfile diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1 index bf15769eaf8c25..d82093ad4ab0e5 100644 --- a/lib/bundler/man/bundle-list.1 +++ b/lib/bundler/man/bundle-list.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-LIST" "1" "September 2022" "" "" +.TH "BUNDLE\-LIST" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-list\fR \- List all the gems in the bundle diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1 index af805f34d32b17..65586c89c594a5 100644 --- a/lib/bundler/man/bundle-lock.1 +++ b/lib/bundler/man/bundle-lock.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-LOCK" "1" "September 2022" "" "" +.TH "BUNDLE\-LOCK" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-lock\fR \- Creates / Updates a lockfile without installing diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1 index f2b10b8808b754..b2327fa9f1552c 100644 --- a/lib/bundler/man/bundle-open.1 +++ b/lib/bundler/man/bundle-open.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-OPEN" "1" "September 2022" "" "" +.TH "BUNDLE\-OPEN" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-open\fR \- Opens the source directory for a gem in your bundle diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1 index 699416583857d8..896155212f3558 100644 --- a/lib/bundler/man/bundle-outdated.1 +++ b/lib/bundler/man/bundle-outdated.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-OUTDATED" "1" "September 2022" "" "" +.TH "BUNDLE\-OUTDATED" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-outdated\fR \- List installed gems with newer versions available diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1 index 848c3024cd2564..2d2450780a766c 100644 --- a/lib/bundler/man/bundle-platform.1 +++ b/lib/bundler/man/bundle-platform.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PLATFORM" "1" "September 2022" "" "" +.TH "BUNDLE\-PLATFORM" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-platform\fR \- Displays platform compatibility information diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1 index 1508b85b38036a..3a08bf8c4693fc 100644 --- a/lib/bundler/man/bundle-plugin.1 +++ b/lib/bundler/man/bundle-plugin.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PLUGIN" "1" "September 2022" "" "" +.TH "BUNDLE\-PLUGIN" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-plugin\fR \- Manage Bundler plugins diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1 index 9a3a26bbfae144..5f562a2e0773b9 100644 --- a/lib/bundler/man/bundle-pristine.1 +++ b/lib/bundler/man/bundle-pristine.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PRISTINE" "1" "September 2022" "" "" +.TH "BUNDLE\-PRISTINE" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-pristine\fR \- Restores installed gems to their pristine condition diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1 index f9d7d574d30d8a..128ac64f9fa0cc 100644 --- a/lib/bundler/man/bundle-remove.1 +++ b/lib/bundler/man/bundle-remove.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-REMOVE" "1" "September 2022" "" "" +.TH "BUNDLE\-REMOVE" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-remove\fR \- Removes gems from the Gemfile diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1 index ff860c64cc9d8a..1d747fd5f4770d 100644 --- a/lib/bundler/man/bundle-show.1 +++ b/lib/bundler/man/bundle-show.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-SHOW" "1" "September 2022" "" "" +.TH "BUNDLE\-SHOW" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1 index 608ad744367875..15e0517737ccab 100644 --- a/lib/bundler/man/bundle-update.1 +++ b/lib/bundler/man/bundle-update.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-UPDATE" "1" "September 2022" "" "" +.TH "BUNDLE\-UPDATE" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-update\fR \- Update your gems to the latest available versions diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1 index 68fc24c4483bbc..3721cf9c7a67fa 100644 --- a/lib/bundler/man/bundle-version.1 +++ b/lib/bundler/man/bundle-version.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-VERSION" "1" "September 2022" "" "" +.TH "BUNDLE\-VERSION" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-version\fR \- Prints Bundler version information diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1 index 4d108a2aea98d3..3508c09bcc079a 100644 --- a/lib/bundler/man/bundle-viz.1 +++ b/lib/bundler/man/bundle-viz.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-VIZ" "1" "September 2022" "" "" +.TH "BUNDLE\-VIZ" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1 index 1898b15647703f..c2e7e4c5c4b674 100644 --- a/lib/bundler/man/bundle.1 +++ b/lib/bundler/man/bundle.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE" "1" "September 2022" "" "" +.TH "BUNDLE" "1" "October 2022" "" "" . .SH "NAME" \fBbundle\fR \- Ruby Dependency Management diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index e793500517a93c..957ba19ff16a61 100644 --- a/lib/bundler/man/gemfile.5 +++ b/lib/bundler/man/gemfile.5 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GEMFILE" "5" "September 2022" "" "" +.TH "GEMFILE" "5" "October 2022" "" "" . .SH "NAME" \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs @@ -497,7 +497,7 @@ Are both equivalent to . .nf -gem "rails", git: "git://github\.com/rails/rails\.git" +gem "rails", git: "https://github\.com/rails/rails\.git" . .fi . diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn index 89ebcc7214d342..d27849ae6aa34f 100644 --- a/lib/bundler/man/gemfile.5.ronn +++ b/lib/bundler/man/gemfile.5.ronn @@ -369,7 +369,7 @@ same, you can omit one. Are both equivalent to - gem "rails", git: "git://github.com/rails/rails.git" + gem "rails", git: "https://github.com/rails/rails.git" Since the `github` method is a specialization of `git_source`, it accepts a `:branch` named argument. From 091e3522d709160ebb4cb58794b10b3ab08f163b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 18 Oct 2022 10:49:47 +0900 Subject: [PATCH 132/139] fiddle: use the old rb_ary_tmp_new() alias Fiddle is a gem and has the external upstream which supports older versions of Ruby. --- ext/fiddle/closure.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c index 6770162b9408a6..83fa752b6dcf83 100644 --- a/ext/fiddle/closure.c +++ b/ext/fiddle/closure.c @@ -74,7 +74,7 @@ with_gvl_callback(void *ptr) VALUE rbargs = rb_iv_get(self, "@args"); VALUE ctype = rb_iv_get(self, "@ctype"); int argc = RARRAY_LENINT(rbargs); - VALUE params = rb_ary_hidden_new(argc); + VALUE params = rb_ary_tmp_new(argc); VALUE ret; VALUE cPointer; int i, type; From 08ec6562822f0734f246741ebc9b0fa0829f8208 Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Wed, 24 Nov 2021 11:40:34 +0900 Subject: [PATCH 133/139] [ruby/fiddle] Bump version --- ext/fiddle/lib/fiddle/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb index db6504b65080f4..719dc62e37c24c 100644 --- a/ext/fiddle/lib/fiddle/version.rb +++ b/ext/fiddle/lib/fiddle/version.rb @@ -1,3 +1,3 @@ module Fiddle - VERSION = "1.1.0" + VERSION = "1.1.1" end From 93da67d46394007aa977c9345535c37878fd0db9 Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz Date: Sat, 19 Mar 2022 22:16:07 +0100 Subject: [PATCH 134/139] [ruby/fiddle] Fix filenames for glibc SO files on alpha and ia64 (https://github.com/ruby/fiddle/pull/105) Fixes [Bug #18645] https://github.com/ruby/fiddle/commit/9a5a1dab1d --- test/fiddle/helper.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/fiddle/helper.rb b/test/fiddle/helper.rb index 0ea3bf57f47073..e470f5a2764396 100644 --- a/test/fiddle/helper.rb +++ b/test/fiddle/helper.rb @@ -49,8 +49,14 @@ libm_so = libc_so else # glibc - libc_so = "libc.so.6" - libm_so = "libm.so.6" + case RUBY_PLATFORM + when /alpha-linux/, /ia64-linux/ + libc_so = "libc.so.6.1" + libm_so = "libm.so.6.1" + else + libc_so = "libc.so.6" + libm_so = "libm.so.6" + end end when /mingw/, /mswin/ require "rbconfig" From e84ea4af695de307c4e66bf0d9aa445a1016a4af Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Mon, 11 Apr 2022 08:32:05 +0900 Subject: [PATCH 135/139] [ruby/fiddle] Add support for linker script on Linux GitHub: fix https://github.com/ruby/fiddle/pull/107 Reported by nicholas a. evans. Thanks!!! https://github.com/ruby/fiddle/commit/49ea1490df --- ext/fiddle/lib/fiddle.rb | 31 ++++++++++++++++++++++++++++++- test/fiddle/test_fiddle.rb | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb index 09262c43fff3f4..6137c487c67887 100644 --- a/ext/fiddle/lib/fiddle.rb +++ b/ext/fiddle/lib/fiddle.rb @@ -58,7 +58,36 @@ def self.last_error= error # # See Fiddle::Handle.new for more. def dlopen library - Fiddle::Handle.new library + begin + Fiddle::Handle.new(library) + rescue DLError => error + case RUBY_PLATFORM + when /linux/ + case error.message + when /\A(\/.+?): (?:invalid ELF header|file too short)/ + # This may be a linker script: + # https://sourceware.org/binutils/docs/ld.html#Scripts + path = $1 + else + raise + end + else + raise + end + + File.open(path) do |input| + input.each_line do |line| + case line + when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/ + # TODO: Should we support multiple files? + return dlopen($1) + end + end + end + + # Not found + raise + end end module_function :dlopen diff --git a/test/fiddle/test_fiddle.rb b/test/fiddle/test_fiddle.rb index 8751d969203d35..2792897074dfb3 100644 --- a/test/fiddle/test_fiddle.rb +++ b/test/fiddle/test_fiddle.rb @@ -14,4 +14,38 @@ def test_windows_constant end end + def test_dlopen_linker_script_input_linux + omit("This is only for Linux") unless RUBY_PLATFORM.match?("linux") + if Dir.glob("/usr/lib/*/libncurses.so").empty? + omit("libncurses.so is needed") + end + # libncurses.so uses INPUT() on Debian GNU/Linux + # $ cat /usr/lib/x86_64-linux-gnu/libncurses.so + # INPUT(libncurses.so.6 -ltinfo) + handle = Fiddle.dlopen("libncurses.so") + begin + assert_equal("libncurses.so", + File.basename(handle.file_name, ".*")) + ensure + handle.close + end + end + + def test_dlopen_linker_script_group_linux + omit("This is only for Linux") unless RUBY_PLATFORM.match?("linux") + # libc.so uses GROUP() on Debian GNU/Linux + # $ cat /usr/lib/x86_64-linux-gnu/libc.so + # /* GNU ld script + # Use the shared library, but some functions are only in + # the static library, so try that secondarily. */ + # OUTPUT_FORMAT(elf64-x86-64) + # GROUP ( /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-x86-64.so.2 ) ) + handle = Fiddle.dlopen("libc.so") + begin + assert_equal("libc.so", + File.basename(handle.file_name, ".*")) + ensure + handle.close + end + end end if defined?(Fiddle) From dffca50bb609661f04660221834c48f36b4b21fa Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 14 Jul 2022 10:00:15 -0700 Subject: [PATCH 136/139] [ruby/fiddle] Free closures immediately (https://github.com/ruby/fiddle/pull/109) These structs don't need to be freed as part of finalization, so lets free them immediately. https://github.com/ruby/fiddle/commit/8a10ec1152 --- ext/fiddle/closure.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c index 83fa752b6dcf83..892f522a620e27 100644 --- a/ext/fiddle/closure.c +++ b/ext/fiddle/closure.c @@ -56,6 +56,8 @@ closure_memsize(const void * ptr) const rb_data_type_t closure_data_type = { "fiddle/closure", {0, dealloc, closure_memsize,}, + 0, 0, + RUBY_TYPED_FREE_IMMEDIATELY, }; struct callback_args { From 93d73cfe80455e74d7853df443dcdf5d2157cadf Mon Sep 17 00:00:00 2001 From: git Date: Tue, 18 Oct 2022 08:22:21 +0000 Subject: [PATCH 137/139] Update default gems list at dffca50bb609661f04660221834c48 [ci skip] --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 81506f93b77078..3d618c5abb230d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -215,6 +215,7 @@ Note: We're only listing outstanding class updates. * date 3.2.3 * error_highlight 0.4.0 * etc 1.4.0 + * fiddle 1.1.1 * io-console 0.5.11 * io-nonblock 0.1.1 * io-wait 0.3.0.pre From e1ca90c2a4e392073480e7c0b4bed0b9948fad94 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 18 Oct 2022 12:13:06 +0900 Subject: [PATCH 138/139] sync_default_gems.rb: fold too long subject [ci skip] Line with substituted issue references with URLs are often very long. Although Git (and GitHub) recommends folding subject lines less than 50 columns, but many commits ignore this, so fold at 68 columns for now. --- tool/sync_default_gems.rb | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 9fcbeb7d22e82c..d7151af7012872 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -422,16 +422,30 @@ def message_filter(repo, sha) log = STDIN.read log.delete!("\r") url = "https://github.com/#{repo}" - log.gsub!(/\b(?:(?i:fix(?:e[sd])?) +)\K#(?=\d+\b)|\bGH-#?(?=\d+\b)|\(\K#(?=\d+\))/) { - "#{url}/pull/" - } - log.gsub!(%r{(? 68 + subject.gsub!(/\G.{,67}[^\s.,][.,]*\K\s+/, "\n") + end + end + if log + conv[log] + log.sub!(/\s*(?=(?i:\nCo-authored-by:.*)*\Z)/) { + "\n\n" "#{url}/commit/#{sha[0,10]}\n" + } + end + print subject, "\n\n", log end # NOTE: This method is also used by GitHub ruby/git.ruby-lang.org's bin/update-default-gem.sh From 1b235dcfd99b925e4c6eb90b221beec2e82410fb Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 18 Oct 2022 17:05:56 +0900 Subject: [PATCH 139/139] Ignore manual files only commits [ci skip] --- .github/workflows/baseruby.yml | 6 ++++++ .github/workflows/check_dependencies.yml | 4 ++++ .github/workflows/codeql-analysis.yml | 4 ++++ .github/workflows/compilers.yml | 4 ++++ .github/workflows/macos.yml | 4 ++++ .github/workflows/mingw.yml | 4 ++++ .github/workflows/mjit-bindgen.yml | 4 ++++ .github/workflows/mjit.yml | 4 ++++ .github/workflows/spec_guards.yml | 4 ++++ .github/workflows/ubuntu.yml | 4 ++++ .github/workflows/wasm.yml | 4 ++++ .github/workflows/windows.yml | 4 ++++ .github/workflows/yjit-ubuntu.yml | 4 ++++ 13 files changed, 54 insertions(+) diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml index 1c314da9117e45..65d2813ac23a5c 100644 --- a/.github/workflows/baseruby.yml +++ b/.github/workflows/baseruby.yml @@ -6,11 +6,17 @@ on: - 'doc/**' - '**.md' - '**.rdoc' + - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' + - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index fab198933518ad..1753f4657409cb 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d812517c9f79b9..85b11e90e72ea2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -7,12 +7,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' schedule: - cron: '0 12 * * 4' diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 70d0e3ae6ca08b..8080963fa6303a 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -7,12 +7,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 72f28a7b615baf..04446d3f053bcd 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 42c35e890cbec4..5c8189428ca177 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/mjit-bindgen.yml b/.github/workflows/mjit-bindgen.yml index 04c9ac4a9f2ade..e1d22d91f2d92f 100644 --- a/.github/workflows/mjit-bindgen.yml +++ b/.github/workflows/mjit-bindgen.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml index b5065288c70c88..89bc0a226becff 100644 --- a/.github/workflows/mjit.yml +++ b/.github/workflows/mjit.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml index 7bffe25bb26610..6d51320c2f969e 100644 --- a/.github/workflows/spec_guards.yml +++ b/.github/workflows/spec_guards.yml @@ -7,12 +7,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index a1818182a34beb..1a5b6661fe1f8a 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 553347d727ad86..6999ea5882b219 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index ab9e35d5a303ba..6f5106e815639c 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index ae108d72a5bbbc..176181f11c5b3e 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -6,12 +6,16 @@ on: - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' pull_request: paths-ignore: - 'doc/**' - '**.md' - '**.rdoc' - '**/.document' + - '**.[1-8]' + - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}