From 24fd2f73d0835eea534225a6381551d591764002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 7 Sep 2022 07:13:04 +0200 Subject: [PATCH 001/269] Resync Bundler & RubyGems --- lib/bundler/bundler.gemspec | 14 +- lib/bundler/cli.rb | 2 +- lib/bundler/cli/console.rb | 4 +- lib/bundler/cli/gem.rb | 26 +-- lib/bundler/dependency.rb | 99 ++------- lib/bundler/endpoint_specification.rb | 2 +- lib/bundler/graph.rb | 6 +- lib/bundler/lockfile_parser.rb | 6 +- 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 | 53 +++++ lib/bundler/man/bundle-console.1.ronn | 44 ++++ 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 | 35 +++ lib/bundler/man/bundle-version.1.ronn | 24 ++ lib/bundler/man/bundle-viz.1 | 2 +- lib/bundler/man/bundle.1 | 6 +- lib/bundler/man/bundle.1.ronn | 3 + lib/bundler/man/gemfile.5 | 46 +--- lib/bundler/man/gemfile.5.ronn | 36 +-- lib/bundler/man/index.txt | 2 + lib/bundler/plugin/index.rb | 8 +- lib/bundler/source/rubygems.rb | 12 +- lib/rubygems/command_manager.rb | 4 +- lib/rubygems/commands/fetch_command.rb | 2 +- lib/rubygems/commands/install_command.rb | 6 +- lib/rubygems/commands/setup_command.rb | 2 +- lib/rubygems/commands/unpack_command.rb | 2 +- lib/rubygems/dependency_installer.rb | 48 ++-- lib/rubygems/indexer.rb | 2 +- lib/rubygems/installer.rb | 6 +- lib/rubygems/package/tar_header.rb | 22 +- .../request_set/gem_dependency_api.rb | 208 +++++++++--------- lib/rubygems/requirement.rb | 14 +- lib/rubygems/security.rb | 4 +- lib/rubygems/security/policies.rb | 80 +++---- lib/rubygems/security/trust_dir.rb | 2 +- lib/rubygems/source.rb | 4 +- lib/rubygems/specification.rb | 66 +++--- lib/rubygems/stub_specification.rb | 4 +- spec/bundler/bundler/dependency_spec.rb | 106 +++++++++ spec/bundler/commands/fund_spec.rb | 16 +- spec/bundler/commands/help_spec.rb | 4 +- spec/bundler/commands/info_spec.rb | 12 +- spec/bundler/install/gemfile/platform_spec.rb | 2 +- spec/bundler/install/gems/fund_spec.rb | 16 +- spec/bundler/realworld/dependency_api_spec.rb | 10 +- .../realworld/gemfile_source_header_spec.rb | 10 +- spec/bundler/realworld/mirror_probe_spec.rb | 10 +- spec/bundler/support/artifice/endpoint.rb | 6 +- spec/bundler/support/platforms.rb | 6 +- spec/bundler/update/gems/fund_spec.rb | 16 +- test/rubygems/test_gem.rb | 4 +- .../test_gem_commands_update_command.rb | 12 +- test/rubygems/test_gem_config_file.rb | 2 +- ...m_ext_cargo_builder_link_flag_converter.rb | 30 +-- test/rubygems/test_gem_package_tar_header.rb | 26 +-- test/rubygems/test_gem_platform.rb | 120 +++++----- test/rubygems/test_gem_remote_fetcher.rb | 8 +- test/rubygems/test_gem_request_set.rb | 4 +- test/rubygems/test_gem_resolver_api_set.rb | 24 +- .../test_gem_resolver_api_specification.rb | 38 ++-- test/rubygems/test_gem_security_policy.rb | 20 +- test/rubygems/test_gem_specification.rb | 18 +- test/rubygems/test_gem_uninstaller.rb | 2 +- 86 files changed, 814 insertions(+), 662 deletions(-) create mode 100644 lib/bundler/man/bundle-console.1 create mode 100644 lib/bundler/man/bundle-console.1.ronn create mode 100644 lib/bundler/man/bundle-version.1 create mode 100644 lib/bundler/man/bundle-version.1.ronn diff --git a/lib/bundler/bundler.gemspec b/lib/bundler/bundler.gemspec index 38c533b0c1bc2b..a9c9fac462675c 100644 --- a/lib/bundler/bundler.gemspec +++ b/lib/bundler/bundler.gemspec @@ -22,14 +22,12 @@ Gem::Specification.new do |s| s.summary = "The best way to manage your application's dependencies" s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably" - if s.respond_to?(:metadata=) - s.metadata = { - "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler", - "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md", - "homepage_uri" => "https://bundler.io/", - "source_code_uri" => "https://github.com/rubygems/rubygems/", - } - end + s.metadata = { + "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler", + "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md", + "homepage_uri" => "https://bundler.io/", + "source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler", + } s.required_ruby_version = ">= 2.3.0" s.required_rubygems_version = ">= 2.5.2" diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 4f969b17bfecb4..3ba4d0f8c42ef1 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -517,7 +517,7 @@ def console(group = nil) end end - desc "version", "Prints the bundler's version information" + desc "version", "Prints Bundler version information" def version cli_help = current_command.name == "cli_help" if cli_help || ARGV.include?("version") diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb index 97b8dc06636b1d..1eb8ea82545126 100644 --- a/lib/bundler/cli/console.rb +++ b/lib/bundler/cli/console.rb @@ -30,9 +30,9 @@ def get_console(name) def get_constant(name) const_name = { - "pry" => :Pry, + "pry" => :Pry, "ripl" => :Ripl, - "irb" => :IRB, + "irb" => :IRB, }[name] Object.const_get(const_name) rescue NameError diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb index c4c76d1b69de8d..8c8ebe8ff30ba4 100644 --- a/lib/bundler/cli/gem.rb +++ b/lib/bundler/cli/gem.rb @@ -55,20 +55,20 @@ def run end config = { - :name => name, + :name => name, :underscored_name => underscored_name, - :namespaced_path => namespaced_path, - :makefile_path => "#{underscored_name}/#{underscored_name}", - :constant_name => constant_name, - :constant_array => constant_array, - :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name, - :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email, - :test => options[:test], - :ext => options[:ext], - :exe => options[:exe], - :bundler_version => bundler_dependency_version, - :git => use_git, - :github_username => github_username.empty? ? "[USERNAME]" : github_username, + :namespaced_path => namespaced_path, + :makefile_path => "#{underscored_name}/#{underscored_name}", + :constant_name => constant_name, + :constant_array => constant_array, + :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name, + :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email, + :test => options[:test], + :ext => options[:ext], + :exe => options[:exe], + :bundler_version => bundler_dependency_version, + :git => use_git, + :github_username => github_username.empty? ? "[USERNAME]" : github_username, :required_ruby_version => required_ruby_version, :minitest_constant_name => minitest_constant_name, } diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index 7607b4695c03c7..605000ba5321ee 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -9,91 +9,22 @@ class Dependency < Gem::Dependency attr_reader :autorequire attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref, :force_ruby_platform - # rubocop:disable Naming/VariableNumber + ALL_RUBY_VERSIONS = ((18..27).to_a + (30..31).to_a).freeze PLATFORM_MAP = { - :ruby => Gem::Platform::RUBY, - :ruby_18 => Gem::Platform::RUBY, - :ruby_19 => Gem::Platform::RUBY, - :ruby_20 => Gem::Platform::RUBY, - :ruby_21 => Gem::Platform::RUBY, - :ruby_22 => Gem::Platform::RUBY, - :ruby_23 => Gem::Platform::RUBY, - :ruby_24 => Gem::Platform::RUBY, - :ruby_25 => Gem::Platform::RUBY, - :ruby_26 => Gem::Platform::RUBY, - :ruby_27 => Gem::Platform::RUBY, - :ruby_30 => Gem::Platform::RUBY, - :ruby_31 => Gem::Platform::RUBY, - :mri => Gem::Platform::RUBY, - :mri_18 => Gem::Platform::RUBY, - :mri_19 => Gem::Platform::RUBY, - :mri_20 => Gem::Platform::RUBY, - :mri_21 => Gem::Platform::RUBY, - :mri_22 => Gem::Platform::RUBY, - :mri_23 => Gem::Platform::RUBY, - :mri_24 => Gem::Platform::RUBY, - :mri_25 => Gem::Platform::RUBY, - :mri_26 => Gem::Platform::RUBY, - :mri_27 => Gem::Platform::RUBY, - :mri_30 => Gem::Platform::RUBY, - :mri_31 => Gem::Platform::RUBY, - :rbx => Gem::Platform::RUBY, - :truffleruby => Gem::Platform::RUBY, - :jruby => Gem::Platform::JAVA, - :jruby_18 => Gem::Platform::JAVA, - :jruby_19 => Gem::Platform::JAVA, - :windows => Gem::Platform::WINDOWS, - :mswin => Gem::Platform::MSWIN, - :mswin_18 => Gem::Platform::MSWIN, - :mswin_19 => Gem::Platform::MSWIN, - :mswin_20 => Gem::Platform::MSWIN, - :mswin_21 => Gem::Platform::MSWIN, - :mswin_22 => Gem::Platform::MSWIN, - :mswin_23 => Gem::Platform::MSWIN, - :mswin_24 => Gem::Platform::MSWIN, - :mswin_25 => Gem::Platform::MSWIN, - :mswin_26 => Gem::Platform::MSWIN, - :mswin_27 => Gem::Platform::MSWIN, - :mswin_30 => Gem::Platform::MSWIN, - :mswin_31 => Gem::Platform::MSWIN, - :mswin64 => Gem::Platform::MSWIN64, - :mswin64_19 => Gem::Platform::MSWIN64, - :mswin64_20 => Gem::Platform::MSWIN64, - :mswin64_21 => Gem::Platform::MSWIN64, - :mswin64_22 => Gem::Platform::MSWIN64, - :mswin64_23 => Gem::Platform::MSWIN64, - :mswin64_24 => Gem::Platform::MSWIN64, - :mswin64_25 => Gem::Platform::MSWIN64, - :mswin64_26 => Gem::Platform::MSWIN64, - :mswin64_27 => Gem::Platform::MSWIN64, - :mswin64_30 => Gem::Platform::MSWIN64, - :mswin64_31 => Gem::Platform::MSWIN64, - :mingw => Gem::Platform::MINGW, - :mingw_18 => Gem::Platform::MINGW, - :mingw_19 => Gem::Platform::MINGW, - :mingw_20 => Gem::Platform::MINGW, - :mingw_21 => Gem::Platform::MINGW, - :mingw_22 => Gem::Platform::MINGW, - :mingw_23 => Gem::Platform::MINGW, - :mingw_24 => Gem::Platform::MINGW, - :mingw_25 => Gem::Platform::MINGW, - :mingw_26 => Gem::Platform::MINGW, - :mingw_27 => Gem::Platform::MINGW, - :mingw_30 => Gem::Platform::MINGW, - :mingw_31 => Gem::Platform::MINGW, - :x64_mingw => Gem::Platform::X64_MINGW, - :x64_mingw_20 => Gem::Platform::X64_MINGW, - :x64_mingw_21 => Gem::Platform::X64_MINGW, - :x64_mingw_22 => Gem::Platform::X64_MINGW, - :x64_mingw_23 => Gem::Platform::X64_MINGW, - :x64_mingw_24 => Gem::Platform::X64_MINGW, - :x64_mingw_25 => Gem::Platform::X64_MINGW, - :x64_mingw_26 => Gem::Platform::X64_MINGW, - :x64_mingw_27 => Gem::Platform::X64_MINGW, - :x64_mingw_30 => Gem::Platform::X64_MINGW, - :x64_mingw_31 => Gem::Platform::X64_MINGW, - }.freeze - # rubocop:enable Naming/VariableNumber + :ruby => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS], + :mri => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS], + :rbx => [Gem::Platform::RUBY], + :truffleruby => [Gem::Platform::RUBY], + :jruby => [Gem::Platform::JAVA, [18, 19]], + :windows => [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS], + :mswin => [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS], + :mswin64 => [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]], + :mingw => [Gem::Platform::MINGW, ALL_RUBY_VERSIONS], + :x64_mingw => [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]], + }.each_with_object({}) do |(platform, spec), hash| + hash[platform] = spec[0] + spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] } + end.freeze def initialize(name, version, options = {}, &blk) type = options["type"] || :runtime diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index f0e7ba2595a3f0..863544b1f926db 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -12,7 +12,7 @@ def initialize(name, version, platform, spec_fetcher, dependencies, metadata = n super() @name = name @version = Gem::Version.create version - @platform = platform.nil? ? nil : Gem::Platform.new(platform) + @platform = Gem::Platform.new(platform) @spec_fetcher = spec_fetcher @dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) } diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb index 8f52e2f0f03950..3c008e63e367d7 100644 --- a/lib/bundler/graph.rb +++ b/lib/bundler/graph.rb @@ -114,10 +114,10 @@ def run @groups.each do |group| g.add_nodes( group, { - :style => "filled", + :style => "filled", :fillcolor => "#B9B9D5", - :shape => "box3d", - :fontsize => 16, + :shape => "box3d", + :fontsize => 16, }.merge(@node_options[group]) ) end diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 64fff4713db03b..871f53663cef9b 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -100,9 +100,9 @@ def may_include_redundant_platform_specific_gems? private TYPES = { - GIT => Bundler::Source::Git, - GEM => Bundler::Source::Rubygems, - PATH => Bundler::Source::Path, + GIT => Bundler::Source::Git, + GEM => Bundler::Source::Rubygems, + PATH => Bundler::Source::Path, PLUGIN => Bundler::Plugin, }.freeze diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1 index e5f1e228b574e8..fd49dd084f5711 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" "August 2022" "" "" +.TH "BUNDLE\-ADD" "1" "September 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 d1204104b1a6df..59bb6a44470336 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" "August 2022" "" "" +.TH "BUNDLE\-BINSTUBS" "1" "September 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 1393caec65c241..4346aa00f82cbd 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" "August 2022" "" "" +.TH "BUNDLE\-CACHE" "1" "September 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 71ddb5cee3ab3e..bb91ed90d5167b 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" "August 2022" "" "" +.TH "BUNDLE\-CHECK" "1" "September 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 e5dc4ccf92c76d..eaa8ea35fb4465 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" "August 2022" "" "" +.TH "BUNDLE\-CLEAN" "1" "September 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 ffc91ed4922dd0..1582e3c464d93d 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" "August 2022" "" "" +.TH "BUNDLE\-CONFIG" "1" "September 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 new file mode 100644 index 00000000000000..6359f442313c7e --- /dev/null +++ b/lib/bundler/man/bundle-console.1 @@ -0,0 +1,53 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "BUNDLE\-CONSOLE" "1" "September 2022" "" "" +. +.SH "NAME" +\fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded +. +.SH "SYNOPSIS" +\fBbundle console\fR [GROUP] +. +.SH "DESCRIPTION" +Starts an interactive Ruby console session in the context of the current bundle\. +. +.P +If no \fBGROUP\fR is specified, all gems in the \fBdefault\fR group in the Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR are preliminarily loaded\. +. +.P +If \fBGROUP\fR is specified, all gems in the given group in the Gemfile in addition to the gems in \fBdefault\fR group are loaded\. Even if the given group does not exist in the Gemfile, IRB console starts without any warning or error\. +. +.P +The environment variable \fBBUNDLE_CONSOLE\fR or \fBbundle config set console\fR can be used to change the shell from the following: +. +.IP "\(bu" 4 +\fBirb\fR (default) +. +.IP "\(bu" 4 +\fBpry\fR (https://github\.com/pry/pry) +. +.IP "\(bu" 4 +\fBripl\fR (https://github\.com/cldwalker/ripl) +. +.IP "" 0 +. +.P +\fBbundle console\fR uses irb by default\. An alternative Pry or Ripl can be used with \fBbundle console\fR by adjusting the \fBconsole\fR Bundler setting\. Also make sure that \fBpry\fR or \fBripl\fR is in your Gemfile\. +. +.SH "EXAMPLE" +. +.nf + +$ bundle config set console pry +$ bundle console +Resolving dependencies\.\.\. +[1] pry(main)> +. +.fi +. +.SH "NOTES" +This command was deprecated in Bundler 2\.1 and will be removed in 3\.0\. Use \fBbin/console\fR script, which can be generated by \fBbundle gem \fR\. +. +.SH "SEE ALSO" +Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR diff --git a/lib/bundler/man/bundle-console.1.ronn b/lib/bundler/man/bundle-console.1.ronn new file mode 100644 index 00000000000000..f9096d386aefc2 --- /dev/null +++ b/lib/bundler/man/bundle-console.1.ronn @@ -0,0 +1,44 @@ +bundle-console(1) -- Deprecated way to open an IRB session with the bundle pre-loaded +===================================================================================== + +## SYNOPSIS + +`bundle console` [GROUP] + +## DESCRIPTION + +Starts an interactive Ruby console session in the context of the current bundle. + +If no `GROUP` is specified, all gems in the `default` group in the [Gemfile(5)](https://bundler.io/man/gemfile.5.html) are +preliminarily loaded. + +If `GROUP` is specified, all gems in the given group in the Gemfile in addition +to the gems in `default` group are loaded. Even if the given group does not +exist in the Gemfile, IRB console starts without any warning or error. + +The environment variable `BUNDLE_CONSOLE` or `bundle config set console` can be used to change +the shell from the following: + +* `irb` (default) +* `pry` (https://github.com/pry/pry) +* `ripl` (https://github.com/cldwalker/ripl) + +`bundle console` uses irb by default. An alternative Pry or Ripl can be used with +`bundle console` by adjusting the `console` Bundler setting. Also make sure that +`pry` or `ripl` is in your Gemfile. + +## EXAMPLE + + $ bundle config set console pry + $ bundle console + Resolving dependencies... + [1] pry(main)> + +## NOTES + +This command was deprecated in Bundler 2.1 and will be removed in 3.0. +Use `bin/console` script, which can be generated by `bundle gem `. + +## SEE ALSO + +[Gemfile(5)](https://bundler.io/man/gemfile.5.html) diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1 index 3f18aaf569d406..92f5c80df932c7 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" "August 2022" "" "" +.TH "BUNDLE\-DOCTOR" "1" "September 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 7f6a892f6c25a0..158a9e0bf644cc 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" "August 2022" "" "" +.TH "BUNDLE\-EXEC" "1" "September 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 63d611bdd25715..2c36627559899a 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" "August 2022" "" "" +.TH "BUNDLE\-GEM" "1" "September 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 c0893425826b7e..ed72024e06b6a8 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" "August 2022" "" "" +.TH "BUNDLE\-HELP" "1" "September 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 ae9bc48539a51b..f1ef32b758c108 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" "August 2022" "" "" +.TH "BUNDLE\-INFO" "1" "September 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 0b06ce89973262..83f8d75324f54d 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" "August 2022" "" "" +.TH "BUNDLE\-INIT" "1" "September 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 4ad37ec3eb8d1e..d675dba79b889a 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" "August 2022" "" "" +.TH "BUNDLE\-INJECT" "1" "September 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 077a58f4152f6f..858f56e673692e 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" "August 2022" "" "" +.TH "BUNDLE\-INSTALL" "1" "September 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 6e89f810a116c5..bf15769eaf8c25 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" "August 2022" "" "" +.TH "BUNDLE\-LIST" "1" "September 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 f5f0ce2bdbeae3..af805f34d32b17 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" "August 2022" "" "" +.TH "BUNDLE\-LOCK" "1" "September 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 7bda0afd33efab..f2b10b8808b754 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" "August 2022" "" "" +.TH "BUNDLE\-OPEN" "1" "September 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 a2b8e2bf92193f..699416583857d8 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" "August 2022" "" "" +.TH "BUNDLE\-OUTDATED" "1" "September 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 7fda4ef0466a59..848c3024cd2564 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" "August 2022" "" "" +.TH "BUNDLE\-PLATFORM" "1" "September 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 72108063279f4c..1508b85b38036a 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" "August 2022" "" "" +.TH "BUNDLE\-PLUGIN" "1" "September 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 d635d24e2d67bb..9a3a26bbfae144 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" "August 2022" "" "" +.TH "BUNDLE\-PRISTINE" "1" "September 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 4952e8094e5cb1..f9d7d574d30d8a 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" "August 2022" "" "" +.TH "BUNDLE\-REMOVE" "1" "September 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 72ef3283ccd0fd..ff860c64cc9d8a 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" "August 2022" "" "" +.TH "BUNDLE\-SHOW" "1" "September 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 06ceef85db2be7..608ad744367875 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" "August 2022" "" "" +.TH "BUNDLE\-UPDATE" "1" "September 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 new file mode 100644 index 00000000000000..68fc24c4483bbc --- /dev/null +++ b/lib/bundler/man/bundle-version.1 @@ -0,0 +1,35 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "BUNDLE\-VERSION" "1" "September 2022" "" "" +. +.SH "NAME" +\fBbundle\-version\fR \- Prints Bundler version information +. +.SH "SYNOPSIS" +\fBbundle version\fR +. +.SH "DESCRIPTION" +Prints Bundler version information\. +. +.SH "OPTIONS" +No options\. +. +.SH "EXAMPLE" +Print the version of Bundler with build date and commit hash of the in the Git source\. +. +.IP "" 4 +. +.nf + +bundle version +. +.fi +. +.IP "" 0 +. +.P +shows \fBBundler version 2\.3\.21 (2022\-08\-24 commit d54be5fdd8)\fR for example\. +. +.P +cf\. \fBbundle \-\-version\fR shows \fBBundler version 2\.3\.21\fR\. diff --git a/lib/bundler/man/bundle-version.1.ronn b/lib/bundler/man/bundle-version.1.ronn new file mode 100644 index 00000000000000..46c6f0b30a290a --- /dev/null +++ b/lib/bundler/man/bundle-version.1.ronn @@ -0,0 +1,24 @@ +bundle-version(1) -- Prints Bundler version information +======================================================= + +## SYNOPSIS + +`bundle version` + +## DESCRIPTION + +Prints Bundler version information. + +## OPTIONS + +No options. + +## EXAMPLE + +Print the version of Bundler with build date and commit hash of the in the Git source. + + bundle version + +shows `Bundler version 2.3.21 (2022-08-24 commit d54be5fdd8)` for example. + +cf. `bundle --version` shows `Bundler version 2.3.21`. diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1 index cf63dd8f9e3b26..4d108a2aea98d3 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" "August 2022" "" "" +.TH "BUNDLE\-VIZ" "1" "September 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 d178b2f5dcb1e4..1898b15647703f 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" "August 2022" "" "" +.TH "BUNDLE" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\fR \- Ruby Dependency Management @@ -124,6 +124,10 @@ Removes gems from the Gemfile \fBbundle plugin(1)\fR \fIbundle\-plugin\.1\.html\fR Manage Bundler plugins . +.TP +\fBbundle version(1)\fR \fIbundle\-version\.1\.html\fR +Prints Bundler version information +. .SH "PLUGINS" When running a command that isn\'t listed in PRIMARY COMMANDS or UTILITIES, Bundler will try to find an executable on your path named \fBbundler\-\fR and execute it, passing down any extra arguments to it\. . diff --git a/lib/bundler/man/bundle.1.ronn b/lib/bundler/man/bundle.1.ronn index 35c0a7ef873aec..8245effabd0bb7 100644 --- a/lib/bundler/man/bundle.1.ronn +++ b/lib/bundler/man/bundle.1.ronn @@ -100,6 +100,9 @@ We divide `bundle` subcommands into primary commands and utilities: * [`bundle plugin(1)`](bundle-plugin.1.html): Manage Bundler plugins +* [`bundle version(1)`](bundle-version.1.html): + Prints Bundler version information + ## PLUGINS When running a command that isn't listed in PRIMARY COMMANDS or UTILITIES, diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index 3d3994153b1bf9..e793500517a93c 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" "August 2022" "" "" +.TH "GEMFILE" "5" "September 2022" "" "" . .SH "NAME" \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs @@ -254,19 +254,15 @@ There are a number of \fBGemfile\fR platforms: . .TP \fBruby\fR -C Ruby (MRI), Rubinius or TruffleRuby, but \fBNOT\fR Windows +C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows . .TP \fBmri\fR -Same as \fIruby\fR, but only C Ruby (MRI) +C Ruby (MRI) only, but not Windows . .TP -\fBmingw\fR -Windows 32 bit \'mingw32\' platform (aka RubyInstaller) -. -.TP -\fBx64_mingw\fR -Windows 64 bit \'mingw32\' platform (aka RubyInstaller x64) +\fBwindows\fR +Windows C Ruby (MRI), including RubyInstaller 32\-bit and 64\-bit versions . .TP \fBrbx\fR @@ -280,15 +276,8 @@ JRuby \fBtruffleruby\fR TruffleRuby . -.TP -\fBmswin\fR -Windows -. .P -You can restrict further by platform and version for all platforms \fIexcept\fR for \fBrbx\fR, \fBjruby\fR, \fBtruffleruby\fR and \fBmswin\fR\. -. -.P -To specify a version in addition to a platform, append the version number without the delimiter to the platform\. For example, to specify that a gem should only be used on platforms with Ruby 2\.3, use: +On platforms \fBruby\fR, \fBmri\fR, and \fBwindows\fR, you may additionally specify a version by appending the major and minor version numbers without a delimiter\. For example, to specify that a gem should only be used on platform \fBruby\fR version 2\.3, use: . .IP "" 4 . @@ -301,26 +290,7 @@ ruby_23 .IP "" 0 . .P -The full list of platforms and supported versions includes: -. -.TP -\fBruby\fR -1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 -. -.TP -\fBmri\fR -1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 -. -.TP -\fBmingw\fR -1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 -. -.TP -\fBx64_mingw\fR -2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 -. -.P -As with groups, you can specify one or more platforms: +As with groups (above), you may specify one or more platforms: . .IP "" 4 . @@ -328,7 +298,7 @@ As with groups, you can specify one or more platforms: gem "weakling", platforms: :jruby gem "ruby\-debug", platforms: :mri_18 -gem "nokogiri", platforms: [:mri_18, :jruby] +gem "nokogiri", platforms: [:windows_26, :jruby] . .fi . diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn index c2f9141c6553a1..89ebcc7214d342 100644 --- a/lib/bundler/man/gemfile.5.ronn +++ b/lib/bundler/man/gemfile.5.ronn @@ -190,47 +190,29 @@ platforms. There are a number of `Gemfile` platforms: * `ruby`: - C Ruby (MRI), Rubinius or TruffleRuby, but `NOT` Windows + C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows * `mri`: - Same as _ruby_, but only C Ruby (MRI) - * `mingw`: - Windows 32 bit 'mingw32' platform (aka RubyInstaller) - * `x64_mingw`: - Windows 64 bit 'mingw32' platform (aka RubyInstaller x64) + C Ruby (MRI) only, but not Windows + * `windows`: + Windows C Ruby (MRI), including RubyInstaller 32-bit and 64-bit versions * `rbx`: Rubinius * `jruby`: JRuby * `truffleruby`: TruffleRuby - * `mswin`: - Windows -You can restrict further by platform and version for all platforms *except* for -`rbx`, `jruby`, `truffleruby` and `mswin`. - -To specify a version in addition to a platform, append the version number without -the delimiter to the platform. For example, to specify that a gem should only be -used on platforms with Ruby 2.3, use: +On platforms `ruby`, `mri`, and `windows`, you may additionally specify a version +by appending the major and minor version numbers without a delimiter. For example, +to specify that a gem should only be used on platform `ruby` version 2.3, use: ruby_23 -The full list of platforms and supported versions includes: - - * `ruby`: - 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - * `mri`: - 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - * `mingw`: - 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - * `x64_mingw`: - 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - -As with groups, you can specify one or more platforms: +As with groups (above), you may specify one or more platforms: gem "weakling", platforms: :jruby gem "ruby-debug", platforms: :mri_18 - gem "nokogiri", platforms: [:mri_18, :jruby] + gem "nokogiri", platforms: [:windows_26, :jruby] All operations involving groups ([`bundle install`](bundle-install.1.html), `Bundler.setup`, `Bundler.require`) behave exactly the same as if any groups not diff --git a/lib/bundler/man/index.txt b/lib/bundler/man/index.txt index 6de81ea0271862..24f7633e66912c 100644 --- a/lib/bundler/man/index.txt +++ b/lib/bundler/man/index.txt @@ -6,6 +6,7 @@ bundle-cache(1) bundle-cache.1 bundle-check(1) bundle-check.1 bundle-clean(1) bundle-clean.1 bundle-config(1) bundle-config.1 +bundle-console(1) bundle-console.1 bundle-doctor(1) bundle-doctor.1 bundle-exec(1) bundle-exec.1 bundle-gem(1) bundle-gem.1 @@ -24,4 +25,5 @@ bundle-pristine(1) bundle-pristine.1 bundle-remove(1) bundle-remove.1 bundle-show(1) bundle-show.1 bundle-update(1) bundle-update.1 +bundle-version(1) bundle-version.1 bundle-viz(1) bundle-viz.1 diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb index 29d33be7181687..6fbd036f3811e6 100644 --- a/lib/bundler/plugin/index.rb +++ b/lib/bundler/plugin/index.rb @@ -167,11 +167,11 @@ def load_index(index_file, global = false) # to be only String key value pairs) def save_index index = { - "commands" => @commands, - "hooks" => @hooks, - "load_paths" => @load_paths, + "commands" => @commands, + "hooks" => @hooks, + "load_paths" => @load_paths, "plugin_paths" => @plugin_paths, - "sources" => @sources, + "sources" => @sources, } require_relative "../yaml_serializer" diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 396465f0cb7a7b..3b640b55a96a5c 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -169,13 +169,13 @@ def install(spec, options = {}) installer = Bundler::RubyGemsGemInstaller.at( path, - :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]], - :install_dir => install_path.to_s, - :bin_dir => bin_path.to_s, + :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]], + :install_dir => install_path.to_s, + :bin_dir => bin_path.to_s, :ignore_dependencies => true, - :wrappers => true, - :env_shebang => true, - :build_args => options[:build_args], + :wrappers => true, + :env_shebang => true, + :build_args => options[:build_args], :bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum, :bundler_extension_cache_path => extension_cache_path(spec) ) diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb index 9d49138ae71af1..8d31d85b443c6d 100644 --- a/lib/rubygems/command_manager.rb +++ b/lib/rubygems/command_manager.rb @@ -73,8 +73,8 @@ class Gem::CommandManager ].freeze ALIAS_COMMANDS = { - "i" => "install", - "login" => "signin", + "i" => "install", + "login" => "signin", "logout" => "signout", }.freeze diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb index 3a8c435d0e4711..5eb45d259c0c0b 100644 --- a/lib/rubygems/commands/fetch_command.rb +++ b/lib/rubygems/commands/fetch_command.rb @@ -10,7 +10,7 @@ class Gem::Commands::FetchCommand < Gem::Command def initialize defaults = { :suggest_alternate => true, - :version => Gem::Requirement.default, + :version => Gem::Requirement.default, } super "fetch", "Download a gem and place it in the current directory", defaults diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb index 724b4fe51dac1c..071687c63fe46a 100644 --- a/lib/rubygems/commands/install_command.rb +++ b/lib/rubygems/commands/install_command.rb @@ -21,10 +21,10 @@ class Gem::Commands::InstallCommand < Gem::Command def initialize defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ :format_executable => false, - :lock => true, + :lock => true, :suggest_alternate => true, - :version => Gem::Requirement.default, - :without_groups => [], + :version => Gem::Requirement.default, + :without_groups => [], }) defaults.merge!(install_update_options) diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index c782c3618cf480..37c9b4ada1ae94 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -576,7 +576,7 @@ def uninstall_old_gemcutter require_relative "../uninstaller" ui = Gem::Uninstaller.new("gemcutter", :all => true, :ignore => true, - :version => "< 0.4") + :version => "< 0.4") ui.uninstall rescue Gem::InstallError end diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb index a365e8541659f8..b1f939b0bc7b85 100644 --- a/lib/rubygems/commands/unpack_command.rb +++ b/lib/rubygems/commands/unpack_command.rb @@ -21,7 +21,7 @@ def initialize super "unpack", "Unpack an installed gem to the current directory", :version => Gem::Requirement.default, - :target => Dir.pwd + :target => Dir.pwd add_option("--target=DIR", "target directory for unpacking") do |value, options| diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb index aa7b66d36bbfa3..1009376b903040 100644 --- a/lib/rubygems/dependency_installer.rb +++ b/lib/rubygems/dependency_installer.rb @@ -16,16 +16,16 @@ class Gem::DependencyInstaller extend Gem::Deprecate DEFAULT_OPTIONS = { # :nodoc: - :env_shebang => false, - :document => %w[ri], - :domain => :both, # HACK dup - :force => false, - :format_executable => false, # HACK dup + :env_shebang => false, + :document => %w[ri], + :domain => :both, # HACK dup + :force => false, + :format_executable => false, # HACK dup :ignore_dependencies => false, - :prerelease => false, - :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low? - :wrappers => true, - :build_args => nil, + :prerelease => false, + :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low? + :wrappers => true, + :build_args => nil, :build_docs_in_background => false, :install_as_default => false, }.freeze @@ -230,22 +230,22 @@ def install(dep_or_name, version = Gem::Requirement.default) @installed_gems = [] options = { - :bin_dir => @bin_dir, - :build_args => @build_args, - :document => @document, - :env_shebang => @env_shebang, - :force => @force, - :format_executable => @format_executable, + :bin_dir => @bin_dir, + :build_args => @build_args, + :document => @document, + :env_shebang => @env_shebang, + :force => @force, + :format_executable => @format_executable, :ignore_dependencies => @ignore_dependencies, - :prerelease => @prerelease, - :security_policy => @security_policy, - :user_install => @user_install, - :wrappers => @wrappers, - :build_root => @build_root, - :install_as_default => @install_as_default, - :dir_mode => @dir_mode, - :data_mode => @data_mode, - :prog_mode => @prog_mode, + :prerelease => @prerelease, + :security_policy => @security_policy, + :user_install => @user_install, + :wrappers => @wrappers, + :build_root => @build_root, + :install_as_default => @install_as_default, + :dir_mode => @dir_mode, + :data_mode => @data_mode, + :prog_mode => @prog_mode, } options[:install_dir] = @install_dir if @only_install_dir diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb index 39653462e90686..d0061ff82ee23c 100644 --- a/lib/rubygems/indexer.rb +++ b/lib/rubygems/indexer.rb @@ -397,7 +397,7 @@ def update_index dst_name = File.join @dest_directory, file # REFACTOR: duped above FileUtils.mv src_name, dst_name, :verbose => verbose, - :force => true + :force => true File.utime newest_mtime, newest_mtime, dst_name end diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 531ca8716eeda1..9fbb2824c72019 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -649,9 +649,9 @@ def ensure_dependencies_met # :nodoc: def process_options # :nodoc: @options = { - :bin_dir => nil, - :env_shebang => false, - :force => false, + :bin_dir => nil, + :env_shebang => false, + :force => false, :only_install_dir => false, :post_install_message => true, }.merge options diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb index ee515a9e050f5a..590a2f03155f8d 100644 --- a/lib/rubygems/package/tar_header.rb +++ b/lib/rubygems/package/tar_header.rb @@ -103,22 +103,22 @@ def self.from(stream) fields = header.unpack UNPACK_FORMAT - new :name => fields.shift, - :mode => strict_oct(fields.shift), - :uid => oct_or_256based(fields.shift), - :gid => oct_or_256based(fields.shift), - :size => strict_oct(fields.shift), - :mtime => strict_oct(fields.shift), + new :name => fields.shift, + :mode => strict_oct(fields.shift), + :uid => oct_or_256based(fields.shift), + :gid => oct_or_256based(fields.shift), + :size => strict_oct(fields.shift), + :mtime => strict_oct(fields.shift), :checksum => strict_oct(fields.shift), :typeflag => fields.shift, :linkname => fields.shift, - :magic => fields.shift, - :version => strict_oct(fields.shift), - :uname => fields.shift, - :gname => fields.shift, + :magic => fields.shift, + :version => strict_oct(fields.shift), + :uname => fields.shift, + :gname => fields.shift, :devmajor => strict_oct(fields.shift), :devminor => strict_oct(fields.shift), - :prefix => fields.shift, + :prefix => fields.shift, :empty => empty end diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb index fe75ac5208de5c..693cd2793a986a 100644 --- a/lib/rubygems/request_set/gem_dependency_api.rb +++ b/lib/rubygems/request_set/gem_dependency_api.rb @@ -32,22 +32,22 @@ class Gem::RequestSet::GemDependencyAPI ENGINE_MAP = { # :nodoc: - :jruby => %w[jruby], - :jruby_18 => %w[jruby], - :jruby_19 => %w[jruby], - :maglev => %w[maglev], - :mri => %w[ruby], - :mri_18 => %w[ruby], - :mri_19 => %w[ruby], - :mri_20 => %w[ruby], - :mri_21 => %w[ruby], - :rbx => %w[rbx], - :truffleruby => %w[truffleruby], - :ruby => %w[ruby rbx maglev truffleruby], - :ruby_18 => %w[ruby rbx maglev truffleruby], - :ruby_19 => %w[ruby rbx maglev truffleruby], - :ruby_20 => %w[ruby rbx maglev truffleruby], - :ruby_21 => %w[ruby rbx maglev truffleruby], + :jruby => %w[jruby], + :jruby_18 => %w[jruby], + :jruby_19 => %w[jruby], + :maglev => %w[maglev], + :mri => %w[ruby], + :mri_18 => %w[ruby], + :mri_19 => %w[ruby], + :mri_20 => %w[ruby], + :mri_21 => %w[ruby], + :rbx => %w[rbx], + :truffleruby => %w[truffleruby], + :ruby => %w[ruby rbx maglev truffleruby], + :ruby_18 => %w[ruby rbx maglev truffleruby], + :ruby_19 => %w[ruby rbx maglev truffleruby], + :ruby_20 => %w[ruby rbx maglev truffleruby], + :ruby_21 => %w[ruby rbx maglev truffleruby], }.freeze mswin = Gem::Platform.new "x86-mswin32" @@ -56,37 +56,37 @@ class Gem::RequestSet::GemDependencyAPI x64_mingw = Gem::Platform.new "x64-mingw32" PLATFORM_MAP = { # :nodoc: - :jruby => Gem::Platform::RUBY, - :jruby_18 => Gem::Platform::RUBY, - :jruby_19 => Gem::Platform::RUBY, - :maglev => Gem::Platform::RUBY, - :mingw => x86_mingw, - :mingw_18 => x86_mingw, - :mingw_19 => x86_mingw, - :mingw_20 => x86_mingw, - :mingw_21 => x86_mingw, - :mri => Gem::Platform::RUBY, - :mri_18 => Gem::Platform::RUBY, - :mri_19 => Gem::Platform::RUBY, - :mri_20 => Gem::Platform::RUBY, - :mri_21 => Gem::Platform::RUBY, - :mswin => mswin, - :mswin_18 => mswin, - :mswin_19 => mswin, - :mswin_20 => mswin, - :mswin_21 => mswin, - :mswin64 => mswin64, - :mswin64_19 => mswin64, - :mswin64_20 => mswin64, - :mswin64_21 => mswin64, - :rbx => Gem::Platform::RUBY, - :ruby => Gem::Platform::RUBY, - :ruby_18 => Gem::Platform::RUBY, - :ruby_19 => Gem::Platform::RUBY, - :ruby_20 => Gem::Platform::RUBY, - :ruby_21 => Gem::Platform::RUBY, - :truffleruby => Gem::Platform::RUBY, - :x64_mingw => x64_mingw, + :jruby => Gem::Platform::RUBY, + :jruby_18 => Gem::Platform::RUBY, + :jruby_19 => Gem::Platform::RUBY, + :maglev => Gem::Platform::RUBY, + :mingw => x86_mingw, + :mingw_18 => x86_mingw, + :mingw_19 => x86_mingw, + :mingw_20 => x86_mingw, + :mingw_21 => x86_mingw, + :mri => Gem::Platform::RUBY, + :mri_18 => Gem::Platform::RUBY, + :mri_19 => Gem::Platform::RUBY, + :mri_20 => Gem::Platform::RUBY, + :mri_21 => Gem::Platform::RUBY, + :mswin => mswin, + :mswin_18 => mswin, + :mswin_19 => mswin, + :mswin_20 => mswin, + :mswin_21 => mswin, + :mswin64 => mswin64, + :mswin64_19 => mswin64, + :mswin64_20 => mswin64, + :mswin64_21 => mswin64, + :rbx => Gem::Platform::RUBY, + :ruby => Gem::Platform::RUBY, + :ruby_18 => Gem::Platform::RUBY, + :ruby_19 => Gem::Platform::RUBY, + :ruby_20 => Gem::Platform::RUBY, + :ruby_21 => Gem::Platform::RUBY, + :truffleruby => Gem::Platform::RUBY, + :x64_mingw => x64_mingw, :x64_mingw_20 => x64_mingw, :x64_mingw_21 => x64_mingw, }.freeze @@ -98,68 +98,68 @@ class Gem::RequestSet::GemDependencyAPI tilde_gt_2_1_0 = Gem::Requirement.new "~> 2.1.0" VERSION_MAP = { # :nodoc: - :jruby => gt_eq_0, - :jruby_18 => tilde_gt_1_8_0, - :jruby_19 => tilde_gt_1_9_0, - :maglev => gt_eq_0, - :mingw => gt_eq_0, - :mingw_18 => tilde_gt_1_8_0, - :mingw_19 => tilde_gt_1_9_0, - :mingw_20 => tilde_gt_2_0_0, - :mingw_21 => tilde_gt_2_1_0, - :mri => gt_eq_0, - :mri_18 => tilde_gt_1_8_0, - :mri_19 => tilde_gt_1_9_0, - :mri_20 => tilde_gt_2_0_0, - :mri_21 => tilde_gt_2_1_0, - :mswin => gt_eq_0, - :mswin_18 => tilde_gt_1_8_0, - :mswin_19 => tilde_gt_1_9_0, - :mswin_20 => tilde_gt_2_0_0, - :mswin_21 => tilde_gt_2_1_0, - :mswin64 => gt_eq_0, - :mswin64_19 => tilde_gt_1_9_0, - :mswin64_20 => tilde_gt_2_0_0, - :mswin64_21 => tilde_gt_2_1_0, - :rbx => gt_eq_0, - :ruby => gt_eq_0, - :ruby_18 => tilde_gt_1_8_0, - :ruby_19 => tilde_gt_1_9_0, - :ruby_20 => tilde_gt_2_0_0, - :ruby_21 => tilde_gt_2_1_0, - :truffleruby => gt_eq_0, - :x64_mingw => gt_eq_0, + :jruby => gt_eq_0, + :jruby_18 => tilde_gt_1_8_0, + :jruby_19 => tilde_gt_1_9_0, + :maglev => gt_eq_0, + :mingw => gt_eq_0, + :mingw_18 => tilde_gt_1_8_0, + :mingw_19 => tilde_gt_1_9_0, + :mingw_20 => tilde_gt_2_0_0, + :mingw_21 => tilde_gt_2_1_0, + :mri => gt_eq_0, + :mri_18 => tilde_gt_1_8_0, + :mri_19 => tilde_gt_1_9_0, + :mri_20 => tilde_gt_2_0_0, + :mri_21 => tilde_gt_2_1_0, + :mswin => gt_eq_0, + :mswin_18 => tilde_gt_1_8_0, + :mswin_19 => tilde_gt_1_9_0, + :mswin_20 => tilde_gt_2_0_0, + :mswin_21 => tilde_gt_2_1_0, + :mswin64 => gt_eq_0, + :mswin64_19 => tilde_gt_1_9_0, + :mswin64_20 => tilde_gt_2_0_0, + :mswin64_21 => tilde_gt_2_1_0, + :rbx => gt_eq_0, + :ruby => gt_eq_0, + :ruby_18 => tilde_gt_1_8_0, + :ruby_19 => tilde_gt_1_9_0, + :ruby_20 => tilde_gt_2_0_0, + :ruby_21 => tilde_gt_2_1_0, + :truffleruby => gt_eq_0, + :x64_mingw => gt_eq_0, :x64_mingw_20 => tilde_gt_2_0_0, :x64_mingw_21 => tilde_gt_2_1_0, }.freeze WINDOWS = { # :nodoc: - :mingw => :only, - :mingw_18 => :only, - :mingw_19 => :only, - :mingw_20 => :only, - :mingw_21 => :only, - :mri => :never, - :mri_18 => :never, - :mri_19 => :never, - :mri_20 => :never, - :mri_21 => :never, - :mswin => :only, - :mswin_18 => :only, - :mswin_19 => :only, - :mswin_20 => :only, - :mswin_21 => :only, - :mswin64 => :only, - :mswin64_19 => :only, - :mswin64_20 => :only, - :mswin64_21 => :only, - :rbx => :never, - :ruby => :never, - :ruby_18 => :never, - :ruby_19 => :never, - :ruby_20 => :never, - :ruby_21 => :never, - :x64_mingw => :only, + :mingw => :only, + :mingw_18 => :only, + :mingw_19 => :only, + :mingw_20 => :only, + :mingw_21 => :only, + :mri => :never, + :mri_18 => :never, + :mri_19 => :never, + :mri_20 => :never, + :mri_21 => :never, + :mswin => :only, + :mswin_18 => :only, + :mswin_19 => :only, + :mswin_20 => :only, + :mswin_21 => :only, + :mswin64 => :only, + :mswin64_19 => :only, + :mswin64_20 => :only, + :mswin64_21 => :only, + :rbx => :never, + :ruby => :never, + :ruby_18 => :never, + :ruby_19 => :never, + :ruby_20 => :never, + :ruby_21 => :never, + :x64_mingw => :only, :x64_mingw_20 => :only, :x64_mingw_21 => :only, }.freeze diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 4f19b8c5b0362d..64f9ac3465a8a9 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -10,13 +10,13 @@ class Gem::Requirement OPS = { #:nodoc: - "=" => lambda {|v, r| v == r }, - "!=" => lambda {|v, r| v != r }, - ">" => lambda {|v, r| v > r }, - "<" => lambda {|v, r| v < r }, - ">=" => lambda {|v, r| v >= r }, - "<=" => lambda {|v, r| v <= r }, - "~>" => lambda {|v, r| v >= r && v.release < r.bump }, + "=" => lambda {|v, r| v == r }, + "!=" => lambda {|v, r| v != r }, + ">" => lambda {|v, r| v > r }, + "<" => lambda {|v, r| v < r }, + ">=" => lambda {|v, r| v >= r }, + "<=" => lambda {|v, r| v <= r }, + "~>" => lambda {|v, r| v >= r && v.release < r.bump }, }.freeze SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc: diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb index dd16283a982296..2ba9562fd48fbb 100644 --- a/lib/rubygems/security.rb +++ b/lib/rubygems/security.rb @@ -376,8 +376,8 @@ class Exception < Gem::Exception; end # * The certificate contains a subject key identifier EXTENSIONS = { - "basicConstraints" => "CA:FALSE", - "keyUsage" => + "basicConstraints" => "CA:FALSE", + "keyUsage" => "keyEncipherment,dataEncipherment,digitalSignature", "subjectKeyIdentifier" => "hash", }.freeze diff --git a/lib/rubygems/security/policies.rb b/lib/rubygems/security/policies.rb index b3f9070394d44a..d28005223e9b8d 100644 --- a/lib/rubygems/security/policies.rb +++ b/lib/rubygems/security/policies.rb @@ -6,12 +6,12 @@ module Gem::Security NoSecurity = Policy.new( "No Security", - :verify_data => false, - :verify_signer => false, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + :verify_data => false, + :verify_signer => false, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false ) ## @@ -24,12 +24,12 @@ module Gem::Security AlmostNoSecurity = Policy.new( "Almost No Security", - :verify_data => true, - :verify_signer => false, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + :verify_data => true, + :verify_signer => false, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false ) ## @@ -41,12 +41,12 @@ module Gem::Security LowSecurity = Policy.new( "Low Security", - :verify_data => true, - :verify_signer => true, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + :verify_data => true, + :verify_signer => true, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false ) ## @@ -60,12 +60,12 @@ module Gem::Security MediumSecurity = Policy.new( "Medium Security", - :verify_data => true, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => true, - :only_signed => false + :verify_data => true, + :verify_signer => true, + :verify_chain => true, + :verify_root => true, + :only_trusted => true, + :only_signed => false ) ## @@ -79,12 +79,12 @@ module Gem::Security HighSecurity = Policy.new( "High Security", - :verify_data => true, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => true, - :only_signed => true + :verify_data => true, + :verify_signer => true, + :verify_chain => true, + :verify_root => true, + :only_trusted => true, + :only_signed => true ) ## @@ -92,23 +92,23 @@ module Gem::Security SigningPolicy = Policy.new( "Signing Policy", - :verify_data => false, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => false, - :only_signed => false + :verify_data => false, + :verify_signer => true, + :verify_chain => true, + :verify_root => true, + :only_trusted => false, + :only_signed => false ) ## # Hash of configured security policies Policies = { - "NoSecurity" => NoSecurity, + "NoSecurity" => NoSecurity, "AlmostNoSecurity" => AlmostNoSecurity, - "LowSecurity" => LowSecurity, - "MediumSecurity" => MediumSecurity, - "HighSecurity" => HighSecurity, + "LowSecurity" => LowSecurity, + "MediumSecurity" => MediumSecurity, + "HighSecurity" => HighSecurity, # SigningPolicy is not intended for use by `gem -P` so do not list it }.freeze diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb index a6882c66e7f185..df59680d845100 100644 --- a/lib/rubygems/security/trust_dir.rb +++ b/lib/rubygems/security/trust_dir.rb @@ -8,7 +8,7 @@ class Gem::Security::TrustDir # Default permissions for the trust directory and its contents DEFAULT_PERMISSIONS = { - :trust_dir => 0700, + :trust_dir => 0700, :trusted_cert => 0600, }.freeze diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb index fc72a1038aa68a..aa0cbc1641e59e 100644 --- a/lib/rubygems/source.rb +++ b/lib/rubygems/source.rb @@ -12,8 +12,8 @@ class Gem::Source include Gem::Text FILES = { # :nodoc: - :released => "specs", - :latest => "latest_specs", + :released => "specs", + :latest => "latest_specs", :prerelease => "prerelease_specs", }.freeze diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index af07cd36e25e81..5175db2d84403a 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -75,7 +75,7 @@ class Gem::Specification < Gem::BasicSpecification SPECIFICATION_VERSION_HISTORY = { # :nodoc: -1 => ["(RubyGems versions up to and including 0.7 did not have versioned specifications)"], - 1 => [ + 1 => [ 'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"', '"test_file=x" is a shortcut for "test_files=[x]"', ], @@ -93,10 +93,10 @@ class Gem::Specification < Gem::BasicSpecification MARSHAL_FIELDS = { # :nodoc: -1 => 16, - 1 => 16, - 2 => 16, - 3 => 17, - 4 => 18, + 1 => 16, + 2 => 16, + 3 => 17, + 4 => 18, }.freeze today = Time.now.utc @@ -124,35 +124,35 @@ class Gem::Specification < Gem::BasicSpecification # Map of attribute names to default values. @@default_value = { - :authors => [], - :autorequire => nil, - :bindir => "bin", - :cert_chain => [], - :date => nil, - :dependencies => [], - :description => nil, - :email => nil, - :executables => [], - :extensions => [], - :extra_rdoc_files => [], - :files => [], - :homepage => nil, - :licenses => [], - :metadata => {}, - :name => nil, - :platform => Gem::Platform::RUBY, - :post_install_message => nil, - :rdoc_options => [], - :require_paths => ["lib"], - :required_ruby_version => Gem::Requirement.default, + :authors => [], + :autorequire => nil, + :bindir => "bin", + :cert_chain => [], + :date => nil, + :dependencies => [], + :description => nil, + :email => nil, + :executables => [], + :extensions => [], + :extra_rdoc_files => [], + :files => [], + :homepage => nil, + :licenses => [], + :metadata => {}, + :name => nil, + :platform => Gem::Platform::RUBY, + :post_install_message => nil, + :rdoc_options => [], + :require_paths => ["lib"], + :required_ruby_version => Gem::Requirement.default, :required_rubygems_version => Gem::Requirement.default, - :requirements => [], - :rubygems_version => Gem::VERSION, - :signing_key => nil, - :specification_version => CURRENT_SPECIFICATION_VERSION, - :summary => nil, - :test_files => [], - :version => nil, + :requirements => [], + :rubygems_version => Gem::VERSION, + :signing_key => nil, + :specification_version => CURRENT_SPECIFICATION_VERSION, + :summary => nil, + :test_files => [], + :version => nil, }.freeze # rubocop:disable Style/MutableConstant diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb index 33b4f45b0a0717..512ca9143d92d2 100644 --- a/lib/rubygems/stub_specification.rb +++ b/lib/rubygems/stub_specification.rb @@ -19,9 +19,9 @@ class StubLine # :nodoc: all # These are common require paths. REQUIRE_PATHS = { # :nodoc: - "lib" => "lib".freeze, + "lib" => "lib".freeze, "test" => "test".freeze, - "ext" => "ext".freeze, + "ext" => "ext".freeze, }.freeze # These are common require path lists. This hash is used to optimize diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb index f4701529968dd8..9b9e2ddf0a8bd3 100644 --- a/spec/bundler/bundler/dependency_spec.rb +++ b/spec/bundler/bundler/dependency_spec.rb @@ -34,4 +34,110 @@ end end end + + describe "PLATFORM_MAP" do + subject { described_class::PLATFORM_MAP } + + # rubocop:disable Naming/VariableNumber + let(:platforms) do + { :ruby => Gem::Platform::RUBY, + :ruby_18 => Gem::Platform::RUBY, + :ruby_19 => Gem::Platform::RUBY, + :ruby_20 => Gem::Platform::RUBY, + :ruby_21 => Gem::Platform::RUBY, + :ruby_22 => Gem::Platform::RUBY, + :ruby_23 => Gem::Platform::RUBY, + :ruby_24 => Gem::Platform::RUBY, + :ruby_25 => Gem::Platform::RUBY, + :ruby_26 => Gem::Platform::RUBY, + :ruby_27 => Gem::Platform::RUBY, + :ruby_30 => Gem::Platform::RUBY, + :ruby_31 => Gem::Platform::RUBY, + :mri => Gem::Platform::RUBY, + :mri_18 => Gem::Platform::RUBY, + :mri_19 => Gem::Platform::RUBY, + :mri_20 => Gem::Platform::RUBY, + :mri_21 => Gem::Platform::RUBY, + :mri_22 => Gem::Platform::RUBY, + :mri_23 => Gem::Platform::RUBY, + :mri_24 => Gem::Platform::RUBY, + :mri_25 => Gem::Platform::RUBY, + :mri_26 => Gem::Platform::RUBY, + :mri_27 => Gem::Platform::RUBY, + :mri_30 => Gem::Platform::RUBY, + :mri_31 => Gem::Platform::RUBY, + :rbx => Gem::Platform::RUBY, + :truffleruby => Gem::Platform::RUBY, + :jruby => Gem::Platform::JAVA, + :jruby_18 => Gem::Platform::JAVA, + :jruby_19 => Gem::Platform::JAVA, + :windows => Gem::Platform::WINDOWS, + :windows_18 => Gem::Platform::WINDOWS, + :windows_19 => Gem::Platform::WINDOWS, + :windows_20 => Gem::Platform::WINDOWS, + :windows_21 => Gem::Platform::WINDOWS, + :windows_22 => Gem::Platform::WINDOWS, + :windows_23 => Gem::Platform::WINDOWS, + :windows_24 => Gem::Platform::WINDOWS, + :windows_25 => Gem::Platform::WINDOWS, + :windows_26 => Gem::Platform::WINDOWS, + :windows_27 => Gem::Platform::WINDOWS, + :windows_30 => Gem::Platform::WINDOWS, + :windows_31 => Gem::Platform::WINDOWS, + :mswin => Gem::Platform::MSWIN, + :mswin_18 => Gem::Platform::MSWIN, + :mswin_19 => Gem::Platform::MSWIN, + :mswin_20 => Gem::Platform::MSWIN, + :mswin_21 => Gem::Platform::MSWIN, + :mswin_22 => Gem::Platform::MSWIN, + :mswin_23 => Gem::Platform::MSWIN, + :mswin_24 => Gem::Platform::MSWIN, + :mswin_25 => Gem::Platform::MSWIN, + :mswin_26 => Gem::Platform::MSWIN, + :mswin_27 => Gem::Platform::MSWIN, + :mswin_30 => Gem::Platform::MSWIN, + :mswin_31 => Gem::Platform::MSWIN, + :mswin64 => Gem::Platform::MSWIN64, + :mswin64_19 => Gem::Platform::MSWIN64, + :mswin64_20 => Gem::Platform::MSWIN64, + :mswin64_21 => Gem::Platform::MSWIN64, + :mswin64_22 => Gem::Platform::MSWIN64, + :mswin64_23 => Gem::Platform::MSWIN64, + :mswin64_24 => Gem::Platform::MSWIN64, + :mswin64_25 => Gem::Platform::MSWIN64, + :mswin64_26 => Gem::Platform::MSWIN64, + :mswin64_27 => Gem::Platform::MSWIN64, + :mswin64_30 => Gem::Platform::MSWIN64, + :mswin64_31 => Gem::Platform::MSWIN64, + :mingw => Gem::Platform::MINGW, + :mingw_18 => Gem::Platform::MINGW, + :mingw_19 => Gem::Platform::MINGW, + :mingw_20 => Gem::Platform::MINGW, + :mingw_21 => Gem::Platform::MINGW, + :mingw_22 => Gem::Platform::MINGW, + :mingw_23 => Gem::Platform::MINGW, + :mingw_24 => Gem::Platform::MINGW, + :mingw_25 => Gem::Platform::MINGW, + :mingw_26 => Gem::Platform::MINGW, + :mingw_27 => Gem::Platform::MINGW, + :mingw_30 => Gem::Platform::MINGW, + :mingw_31 => Gem::Platform::MINGW, + :x64_mingw => Gem::Platform::X64_MINGW, + :x64_mingw_20 => Gem::Platform::X64_MINGW, + :x64_mingw_21 => Gem::Platform::X64_MINGW, + :x64_mingw_22 => Gem::Platform::X64_MINGW, + :x64_mingw_23 => Gem::Platform::X64_MINGW, + :x64_mingw_24 => Gem::Platform::X64_MINGW, + :x64_mingw_25 => Gem::Platform::X64_MINGW, + :x64_mingw_26 => Gem::Platform::X64_MINGW, + :x64_mingw_27 => Gem::Platform::X64_MINGW, + :x64_mingw_30 => Gem::Platform::X64_MINGW, + :x64_mingw_31 => Gem::Platform::X64_MINGW } + end + # rubocop:enable Naming/VariableNumber + + it "includes all platforms" do + expect(subject).to eq(platforms) + end + end end diff --git a/spec/bundler/commands/fund_spec.rb b/spec/bundler/commands/fund_spec.rb index 5a0c5411dabcf4..5415b88eeb00e8 100644 --- a/spec/bundler/commands/fund_spec.rb +++ b/spec/bundler/commands/fund_spec.rb @@ -5,20 +5,20 @@ build_repo2 do build_gem "has_funding_and_other_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end build_gem "has_funding", "1.2.3" do |s| s.metadata = { - "funding_uri" => "https://example.com/has_funding/funding", + "funding_uri" => "https://example.com/has_funding/funding", } end diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb index f72763900efc28..409c49f9e165d0 100644 --- a/spec/bundler/commands/help_spec.rb +++ b/spec/bundler/commands/help_spec.rb @@ -23,8 +23,8 @@ end it "still outputs the old help for commands that do not have man pages yet" do - bundle "help version" - expect(out).to include("Prints the bundler's version information") + bundle "help fund" + expect(out).to include("Lists information about gems seeking funding assistance") end it "looks for a binary and executes it with --help option if it's named bundler-" do diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb index e4b970eb34c15e..2e17ee6dd83a18 100644 --- a/spec/bundler/commands/info_spec.rb +++ b/spec/bundler/commands/info_spec.rb @@ -6,13 +6,13 @@ build_repo2 do build_gem "has_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end end diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb index 62e6bda4cd9449..1bae0bb3715d40 100644 --- a/spec/bundler/install/gemfile/platform_spec.rb +++ b/spec/bundler/install/gemfile/platform_spec.rb @@ -475,7 +475,7 @@ gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby] + gem "rack", :platform => [:windows, :mingw, :mswin, :x64_mingw, :jruby] G bundle "install" diff --git a/spec/bundler/install/gems/fund_spec.rb b/spec/bundler/install/gems/fund_spec.rb index 436454c1f404e8..9aadc9ed250e16 100644 --- a/spec/bundler/install/gems/fund_spec.rb +++ b/spec/bundler/install/gems/fund_spec.rb @@ -6,20 +6,20 @@ build_repo2 do build_gem "has_funding_and_other_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end build_gem "has_funding", "1.2.3" do |s| s.metadata = { - "funding_uri" => "https://example.com/has_funding/funding", + "funding_uri" => "https://example.com/has_funding/funding", } end diff --git a/spec/bundler/realworld/dependency_api_spec.rb b/spec/bundler/realworld/dependency_api_spec.rb index 08c6acf1903960..14f99bd262f805 100644 --- a/spec/bundler/realworld/dependency_api_spec.rb +++ b/spec/bundler/realworld/dependency_api_spec.rb @@ -13,12 +13,12 @@ require_relative "../support/artifice/endpoint_timeout" @t = Thread.new do - server = Rack::Server.start(:app => EndpointTimeout, - :Host => "0.0.0.0", - :Port => port, - :server => "webrick", + server = Rack::Server.start(:app => EndpointTimeout, + :Host => "0.0.0.0", + :Port => port, + :server => "webrick", :AccessLog => [], - :Logger => Spec::SilentLogger.new) + :Logger => Spec::SilentLogger.new) server.start end @t.run diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb index ada2fc92ee5d6e..60c0055a621106 100644 --- a/spec/bundler/realworld/gemfile_source_header_spec.rb +++ b/spec/bundler/realworld/gemfile_source_header_spec.rb @@ -40,12 +40,12 @@ def setup_server require_relative "../support/artifice/endpoint_mirror_source" @t = Thread.new do - Rack::Server.start(:app => EndpointMirrorSource, - :Host => "0.0.0.0", - :Port => @port, - :server => "webrick", + Rack::Server.start(:app => EndpointMirrorSource, + :Host => "0.0.0.0", + :Port => @port, + :server => "webrick", :AccessLog => [], - :Logger => Spec::SilentLogger.new) + :Logger => Spec::SilentLogger.new) end.run wait_for_server("127.0.0.1", @port) diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb index 241424d4d6e139..f2ce477c107393 100644 --- a/spec/bundler/realworld/mirror_probe_spec.rb +++ b/spec/bundler/realworld/mirror_probe_spec.rb @@ -113,12 +113,12 @@ def setup_server require_relative "../support/artifice/endpoint" @server_thread = Thread.new do - Rack::Server.start(:app => Endpoint, - :Host => host, - :Port => @server_port, - :server => "webrick", + Rack::Server.start(:app => Endpoint, + :Host => host, + :Port => @server_port, + :server => "webrick", :AccessLog => [], - :Logger => Spec::SilentLogger.new) + :Logger => Spec::SilentLogger.new) end.run wait_for_server(host, @server_port) diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb index c00113b28f3c96..b0e3f750ead324 100644 --- a/spec/bundler/support/artifice/endpoint.rb +++ b/spec/bundler/support/artifice/endpoint.rb @@ -70,9 +70,9 @@ def dependencies_for(gem_names, gem_repo = default_gem_repo) spec = load_spec(name, version, platform, gem_repo) next unless gem_names.include?(spec.name) { - :name => spec.name, - :number => spec.version.version, - :platform => spec.platform.to_s, + :name => spec.name, + :number => spec.version.version, + :platform => spec.platform.to_s, :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep| [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")] end, diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb index d3aefe004ac3d8..3776901ce3c0fd 100644 --- a/spec/bundler/support/platforms.rb +++ b/spec/bundler/support/platforms.rb @@ -68,14 +68,14 @@ def local_tag if RUBY_PLATFORM == "java" :jruby elsif ["x64-mingw32", "x64-mingw-ucrt"].include?(RUBY_PLATFORM) - :x64_mingw + :windows else :ruby end end def not_local_tag - [:jruby, :x64_mingw, :ruby].find {|tag| tag != local_tag } + [:jruby, :windows, :ruby].find {|tag| tag != local_tag } end def local_ruby_engine @@ -88,7 +88,7 @@ def local_engine_version def not_local_engine_version case not_local_tag - when :ruby, :x64_mingw + when :ruby, :windows not_local_ruby_version when :jruby "1.6.1" diff --git a/spec/bundler/update/gems/fund_spec.rb b/spec/bundler/update/gems/fund_spec.rb index 0dfe63d36d8b0c..d80f4018f3ad97 100644 --- a/spec/bundler/update/gems/fund_spec.rb +++ b/spec/bundler/update/gems/fund_spec.rb @@ -5,20 +5,20 @@ build_repo2 do build_gem "has_funding_and_other_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end build_gem "has_funding", "1.2.3" do |s| s.metadata = { - "funding_uri" => "https://example.com/has_funding/funding", + "funding_uri" => "https://example.com/has_funding/funding", } end end diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index bbb3e6dd0a1ed1..9070b9342ed210 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -1357,8 +1357,8 @@ def test_self_use_paths_with_nils def test_setting_paths_does_not_warn_about_unknown_keys stdout, stderr = capture_output do - Gem.paths = { "foo" => [], - "bar" => Object.new, + Gem.paths = { "foo" => [], + "bar" => Object.new, "GEM_HOME" => Gem.paths.home, "GEM_PATH" => "foo" } end diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb index 58bc7576da8c12..1b70eac9eb959a 100644 --- a/test/rubygems/test_gem_commands_update_command.rb +++ b/test/rubygems/test_gem_commands_update_command.rb @@ -669,10 +669,10 @@ def test_handle_options_system @cmd.handle_options %w[--system] expected = { - :args => [], + :args => [], :document => %w[ri], - :force => false, - :system => true, + :force => false, + :system => true, } assert_equal expected, @cmd.options @@ -688,10 +688,10 @@ def test_handle_options_system_specific @cmd.handle_options %w[--system 1.3.7] expected = { - :args => [], + :args => [], :document => %w[ri], - :force => false, - :system => "1.3.7", + :force => false, + :system => "1.3.7", } assert_equal expected, @cmd.options diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb index e23773a133c9d1..14adddf1c4097d 100644 --- a/test/rubygems/test_gem_config_file.rb +++ b/test/rubygems/test_gem_config_file.rb @@ -330,7 +330,7 @@ def test_load_api_keys util_config_file assert_equal({ :rubygems => "701229f217cdf23b1344c7b4b54ca97", - :other => "a5fdbb6ba150cbb83aad2bb2fede64c" }, @cfg.api_keys) + :other => "a5fdbb6ba150cbb83aad2bb2fede64c" }, @cfg.api_keys) end def test_load_api_keys_bad_permission diff --git a/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb b/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb index 88581ea4d8039a..92bd893a1820e1 100644 --- a/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb +++ b/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb @@ -5,22 +5,22 @@ class TestGemExtCargoBuilderLinkFlagConverter < Gem::TestCase CASES = { - test_search_path_basic: ["-L/usr/local/lib", ["-L", "native=/usr/local/lib"]], - test_search_path_space: ["-L /usr/local/lib", ["-L", "native=/usr/local/lib"]], - test_search_path_space_in_path: ["-L/usr/local/my\ lib", ["-L", "native=/usr/local/my\ lib"]], - test_simple_lib: ["-lfoo", ["-l", "foo"]], - test_lib_with_nonascii: ["-lws2_32", ["-l", "ws2_32"]], - test_simple_lib_space: ["-l foo", ["-l", "foo"]], - test_verbose_lib_space: ["--library=foo", ["-l", "foo"]], - test_libstatic_with_colon: ["-l:libssp.a", ["-l", "static=ssp"]], - test_libstatic_with_colon_space: ["-l :libssp.a", ["-l", "static=ssp"]], + test_search_path_basic: ["-L/usr/local/lib", ["-L", "native=/usr/local/lib"]], + test_search_path_space: ["-L /usr/local/lib", ["-L", "native=/usr/local/lib"]], + test_search_path_space_in_path: ["-L/usr/local/my\ lib", ["-L", "native=/usr/local/my\ lib"]], + test_simple_lib: ["-lfoo", ["-l", "foo"]], + test_lib_with_nonascii: ["-lws2_32", ["-l", "ws2_32"]], + test_simple_lib_space: ["-l foo", ["-l", "foo"]], + test_verbose_lib_space: ["--library=foo", ["-l", "foo"]], + test_libstatic_with_colon: ["-l:libssp.a", ["-l", "static=ssp"]], + test_libstatic_with_colon_space: ["-l :libssp.a", ["-l", "static=ssp"]], test_unconventional_lib_with_colon: ["-l:ssp.a", ["-C", "link_arg=-l:ssp.a"]], - test_dylib_with_colon_space: ["-l :libssp.dylib", ["-l", "dylib=ssp"]], - test_so_with_colon_space: ["-l :libssp.so", ["-l", "dylib=ssp"]], - test_dll_with_colon_space: ["-l :libssp.dll", ["-l", "dylib=ssp"]], - test_framework: ["-F/some/path", ["-l", "framework=/some/path"]], - test_framework_space: ["-F /some/path", ["-l", "framework=/some/path"]], - test_non_lib_dash_l: ["test_rubygems_20220413-976-lemgf9/prefix", ["-C", "link_arg=test_rubygems_20220413-976-lemgf9/prefix"]], + test_dylib_with_colon_space: ["-l :libssp.dylib", ["-l", "dylib=ssp"]], + test_so_with_colon_space: ["-l :libssp.so", ["-l", "dylib=ssp"]], + test_dll_with_colon_space: ["-l :libssp.dll", ["-l", "dylib=ssp"]], + test_framework: ["-F/some/path", ["-l", "framework=/some/path"]], + test_framework_space: ["-F /some/path", ["-l", "framework=/some/path"]], + test_non_lib_dash_l: ["test_rubygems_20220413-976-lemgf9/prefix", ["-C", "link_arg=test_rubygems_20220413-976-lemgf9/prefix"]], }.freeze CASES.each do |test_name, (arg, expected)| diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb index 3ff4f0b30b214a..ccb0db739ed622 100644 --- a/test/rubygems/test_gem_package_tar_header.rb +++ b/test/rubygems/test_gem_package_tar_header.rb @@ -7,19 +7,19 @@ def setup super header = { - :name => "x", - :mode => 0644, - :uid => 1000, - :gid => 10000, - :size => 100, - :mtime => 12345, + :name => "x", + :mode => 0644, + :uid => 1000, + :gid => 10000, + :size => 100, + :mtime => 12345, :typeflag => "0", :linkname => "link", - :uname => "user", - :gname => "group", + :uname => "user", + :gname => "group", :devmajor => 1, :devminor => 2, - :prefix => "y", + :prefix => "y", } @tar_header = Gem::Package::TarHeader.new header @@ -76,10 +76,10 @@ def test_initialize_bad def test_initialize_typeflag header = { - :mode => "", - :name => "", - :prefix => "", - :size => "", + :mode => "", + :name => "", + :prefix => "", + :size => "", :typeflag => "", } diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index 576f150219cd3a..f683204a2fc209 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -85,66 +85,66 @@ def test_self_new def test_initialize test_cases = { - "amd64-freebsd6" => ["amd64", "freebsd", "6"], - "hppa2.0w-hpux11.31" => ["hppa2.0w", "hpux", "11"], - "java" => [nil, "java", nil], - "jruby" => [nil, "java", nil], - "universal-dotnet" => ["universal", "dotnet", nil], - "universal-dotnet2.0" => ["universal", "dotnet", "2.0"], - "universal-dotnet4.0" => ["universal", "dotnet", "4.0"], - "powerpc-aix5.3.0.0" => ["powerpc", "aix", "5"], - "powerpc-darwin7" => ["powerpc", "darwin", "7"], - "powerpc-darwin8" => ["powerpc", "darwin", "8"], - "powerpc-linux" => ["powerpc", "linux", nil], - "powerpc64-linux" => ["powerpc64", "linux", nil], - "sparc-solaris2.10" => ["sparc", "solaris", "2.10"], - "sparc-solaris2.8" => ["sparc", "solaris", "2.8"], - "sparc-solaris2.9" => ["sparc", "solaris", "2.9"], - "universal-darwin8" => ["universal", "darwin", "8"], - "universal-darwin9" => ["universal", "darwin", "9"], - "universal-macruby" => ["universal", "macruby", nil], - "i386-cygwin" => ["x86", "cygwin", nil], - "i686-darwin" => ["x86", "darwin", nil], - "i686-darwin8.4.1" => ["x86", "darwin", "8"], - "i386-freebsd4.11" => ["x86", "freebsd", "4"], - "i386-freebsd5" => ["x86", "freebsd", "5"], - "i386-freebsd6" => ["x86", "freebsd", "6"], - "i386-freebsd7" => ["x86", "freebsd", "7"], - "i386-freebsd" => ["x86", "freebsd", nil], - "universal-freebsd" => ["universal", "freebsd", nil], - "i386-java1.5" => ["x86", "java", "1.5"], - "x86-java1.6" => ["x86", "java", "1.6"], - "i386-java1.6" => ["x86", "java", "1.6"], - "i686-linux" => ["x86", "linux", nil], - "i586-linux" => ["x86", "linux", nil], - "i486-linux" => ["x86", "linux", nil], - "i386-linux" => ["x86", "linux", nil], - "i586-linux-gnu" => ["x86", "linux", "gnu"], - "i386-linux-gnu" => ["x86", "linux", "gnu"], - "i386-mingw32" => ["x86", "mingw32", nil], - "x64-mingw-ucrt" => ["x64", "mingw", "ucrt"], - "i386-mswin32" => ["x86", "mswin32", nil], - "i386-mswin32_80" => ["x86", "mswin32", "80"], - "i386-mswin32-80" => ["x86", "mswin32", "80"], - "x86-mswin32" => ["x86", "mswin32", nil], - "x86-mswin32_60" => ["x86", "mswin32", "60"], - "x86-mswin32-60" => ["x86", "mswin32", "60"], - "i386-netbsdelf" => ["x86", "netbsdelf", nil], - "i386-openbsd4.0" => ["x86", "openbsd", "4.0"], - "i386-solaris2.10" => ["x86", "solaris", "2.10"], - "i386-solaris2.8" => ["x86", "solaris", "2.8"], - "mswin32" => ["x86", "mswin32", nil], - "x86_64-linux" => ["x86_64", "linux", nil], - "x86_64-linux-gnu" => ["x86_64", "linux", "gnu"], - "x86_64-linux-musl" => ["x86_64", "linux", "musl"], - "x86_64-linux-uclibc" => ["x86_64", "linux", "uclibc"], - "arm-linux-eabi" => ["arm", "linux", "eabi"], - "arm-linux-gnueabi" => ["arm", "linux", "gnueabi"], - "arm-linux-musleabi" => ["arm", "linux", "musleabi"], - "arm-linux-uclibceabi" => ["arm", "linux", "uclibceabi"], - "x86_64-openbsd3.9" => ["x86_64", "openbsd", "3.9"], - "x86_64-openbsd4.0" => ["x86_64", "openbsd", "4.0"], - "x86_64-openbsd" => ["x86_64", "openbsd", nil], + "amd64-freebsd6" => ["amd64", "freebsd", "6"], + "hppa2.0w-hpux11.31" => ["hppa2.0w", "hpux", "11"], + "java" => [nil, "java", nil], + "jruby" => [nil, "java", nil], + "universal-dotnet" => ["universal", "dotnet", nil], + "universal-dotnet2.0" => ["universal", "dotnet", "2.0"], + "universal-dotnet4.0" => ["universal", "dotnet", "4.0"], + "powerpc-aix5.3.0.0" => ["powerpc", "aix", "5"], + "powerpc-darwin7" => ["powerpc", "darwin", "7"], + "powerpc-darwin8" => ["powerpc", "darwin", "8"], + "powerpc-linux" => ["powerpc", "linux", nil], + "powerpc64-linux" => ["powerpc64", "linux", nil], + "sparc-solaris2.10" => ["sparc", "solaris", "2.10"], + "sparc-solaris2.8" => ["sparc", "solaris", "2.8"], + "sparc-solaris2.9" => ["sparc", "solaris", "2.9"], + "universal-darwin8" => ["universal", "darwin", "8"], + "universal-darwin9" => ["universal", "darwin", "9"], + "universal-macruby" => ["universal", "macruby", nil], + "i386-cygwin" => ["x86", "cygwin", nil], + "i686-darwin" => ["x86", "darwin", nil], + "i686-darwin8.4.1" => ["x86", "darwin", "8"], + "i386-freebsd4.11" => ["x86", "freebsd", "4"], + "i386-freebsd5" => ["x86", "freebsd", "5"], + "i386-freebsd6" => ["x86", "freebsd", "6"], + "i386-freebsd7" => ["x86", "freebsd", "7"], + "i386-freebsd" => ["x86", "freebsd", nil], + "universal-freebsd" => ["universal", "freebsd", nil], + "i386-java1.5" => ["x86", "java", "1.5"], + "x86-java1.6" => ["x86", "java", "1.6"], + "i386-java1.6" => ["x86", "java", "1.6"], + "i686-linux" => ["x86", "linux", nil], + "i586-linux" => ["x86", "linux", nil], + "i486-linux" => ["x86", "linux", nil], + "i386-linux" => ["x86", "linux", nil], + "i586-linux-gnu" => ["x86", "linux", "gnu"], + "i386-linux-gnu" => ["x86", "linux", "gnu"], + "i386-mingw32" => ["x86", "mingw32", nil], + "x64-mingw-ucrt" => ["x64", "mingw", "ucrt"], + "i386-mswin32" => ["x86", "mswin32", nil], + "i386-mswin32_80" => ["x86", "mswin32", "80"], + "i386-mswin32-80" => ["x86", "mswin32", "80"], + "x86-mswin32" => ["x86", "mswin32", nil], + "x86-mswin32_60" => ["x86", "mswin32", "60"], + "x86-mswin32-60" => ["x86", "mswin32", "60"], + "i386-netbsdelf" => ["x86", "netbsdelf", nil], + "i386-openbsd4.0" => ["x86", "openbsd", "4.0"], + "i386-solaris2.10" => ["x86", "solaris", "2.10"], + "i386-solaris2.8" => ["x86", "solaris", "2.8"], + "mswin32" => ["x86", "mswin32", nil], + "x86_64-linux" => ["x86_64", "linux", nil], + "x86_64-linux-gnu" => ["x86_64", "linux", "gnu"], + "x86_64-linux-musl" => ["x86_64", "linux", "musl"], + "x86_64-linux-uclibc" => ["x86_64", "linux", "uclibc"], + "arm-linux-eabi" => ["arm", "linux", "eabi"], + "arm-linux-gnueabi" => ["arm", "linux", "gnueabi"], + "arm-linux-musleabi" => ["arm", "linux", "musleabi"], + "arm-linux-uclibceabi" => ["arm", "linux", "uclibceabi"], + "x86_64-openbsd3.9" => ["x86_64", "openbsd", "3.9"], + "x86_64-openbsd4.0" => ["x86_64", "openbsd", "4.0"], + "x86_64-openbsd" => ["x86_64", "openbsd", nil], } test_cases.each do |arch, expected| diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index d8b55a5f7aa389..5f54230c77f901 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -1174,10 +1174,10 @@ def start_ssl_server(config = {}) def start_server(data) null_logger = NilLog.new s = WEBrick::HTTPServer.new( - :Port => 0, - :DocumentRoot => nil, - :Logger => null_logger, - :AccessLog => null_logger + :Port => 0, + :DocumentRoot => nil, + :Logger => null_logger, + :AccessLog => null_logger ) s.mount_proc("/kill") {|req, res| s.shutdown } s.mount_proc("/yaml") do |req, res| diff --git a/test/rubygems/test_gem_request_set.rb b/test/rubygems/test_gem_request_set.rb index 6d14321126e346..0a27400adeadfe 100644 --- a/test/rubygems/test_gem_request_set.rb +++ b/test/rubygems/test_gem_request_set.rb @@ -110,7 +110,7 @@ def test_install_from_gemdeps_install_dir end options = { - :gemdeps => "gem.deps.rb", + :gemdeps => "gem.deps.rb", :install_dir => "#{@gemhome}2", } @@ -575,7 +575,7 @@ def test_install_into_development_shallow rs.resolve options = { - :development => true, + :development => true, :development_shallow => true, } diff --git a/test/rubygems/test_gem_resolver_api_set.rb b/test/rubygems/test_gem_resolver_api_set.rb index 5c13311b1b3868..17c6c49a701bcd 100644 --- a/test/rubygems/test_gem_resolver_api_set.rb +++ b/test/rubygems/test_gem_resolver_api_set.rb @@ -36,9 +36,9 @@ def test_find_all spec_fetcher data = [ - { :name => "a", - :number => "1", - :platform => "ruby", + { :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [] }, ] @@ -59,13 +59,13 @@ def test_find_all_prereleases spec_fetcher data = [ - { :name => "a", - :number => "1", - :platform => "ruby", + { :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [] }, - { :name => "a", - :number => "2.a", - :platform => "ruby", + { :name => "a", + :number => "2.a", + :platform => "ruby", :dependencies => [] }, ] @@ -88,9 +88,9 @@ def test_find_all_cache spec_fetcher data = [ - { :name => "a", - :number => "1", - :platform => "ruby", + { :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [] }, ] diff --git a/test/rubygems/test_gem_resolver_api_specification.rb b/test/rubygems/test_gem_resolver_api_specification.rb index 98981e53e94527..96e2f3c8c67815 100644 --- a/test/rubygems/test_gem_resolver_api_specification.rb +++ b/test/rubygems/test_gem_resolver_api_specification.rb @@ -5,8 +5,8 @@ class TestGemResolverAPISpecification < Gem::TestCase def test_initialize set = Gem::Resolver::APISet.new data = { - :name => "rails", - :number => "3.0.3", + :name => "rails", + :number => "3.0.3", :platform => Gem::Platform.local.to_s, :dependencies => [ ["bundler", "~> 1.0"], @@ -44,8 +44,8 @@ def test_fetch_development_dependencies set = Gem::Resolver::APISet.new repo data = { - :name => "rails", - :number => "3.0.3", + :name => "rails", + :number => "3.0.3", :platform => "ruby", :dependencies => [ ["bundler", "~> 1.0"], @@ -71,8 +71,8 @@ def test_fetch_development_dependencies def test_installable_platform_eh set = Gem::Resolver::APISet.new data = { - :name => "a", - :number => "1", + :name => "a", + :number => "1", :platform => "ruby", :dependencies => [], } @@ -82,8 +82,8 @@ def test_installable_platform_eh assert a_spec.installable_platform? data = { - :name => "b", - :number => "1", + :name => "b", + :number => "1", :platform => "cpu-other_platform-1", :dependencies => [], } @@ -93,8 +93,8 @@ def test_installable_platform_eh refute b_spec.installable_platform? data = { - :name => "c", - :number => "1", + :name => "c", + :number => "1", :platform => Gem::Platform.local.to_s, :dependencies => [], } @@ -107,9 +107,9 @@ def test_installable_platform_eh def test_source set = Gem::Resolver::APISet.new data = { - :name => "a", - :number => "1", - :platform => "ruby", + :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [], } @@ -126,9 +126,9 @@ def test_spec dep_uri = URI(@gem_repo) + "info" set = Gem::Resolver::APISet.new dep_uri data = { - :name => "a", - :number => "1", - :platform => "ruby", + :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [], } @@ -150,9 +150,9 @@ def test_spec_jruby_platform dep_uri = URI(@gem_repo) + "info" set = Gem::Resolver::APISet.new dep_uri data = { - :name => "j", - :number => "1", - :platform => "jruby", + :name => "j", + :number => "1", + :platform => "jruby", :dependencies => [], } diff --git a/test/rubygems/test_gem_security_policy.rb b/test/rubygems/test_gem_security_policy.rb index 6680238245c7e4..9217f5a818a8e2 100644 --- a/test/rubygems/test_gem_security_policy.rb +++ b/test/rubygems/test_gem_security_policy.rb @@ -43,22 +43,22 @@ def setup @chain = Gem::Security::Policy.new( "Chain", - :verify_data => true, + :verify_data => true, :verify_signer => true, - :verify_chain => true, - :verify_root => false, - :only_trusted => false, - :only_signed => false + :verify_chain => true, + :verify_root => false, + :only_trusted => false, + :only_signed => false ) @root = Gem::Security::Policy.new( "Root", - :verify_data => true, + :verify_data => true, :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => false, - :only_signed => false + :verify_chain => true, + :verify_root => true, + :only_trusted => false, + :only_signed => false ) end diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 8ce8293f33a742..24a726146685c6 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -1866,9 +1866,9 @@ def test_full_name def test_full_name_windows test_cases = { - "i386-mswin32" => "a-1-x86-mswin32-60", - "i386-mswin32_80" => "a-1-x86-mswin32-80", - "i386-mingw32" => "a-1-x86-mingw32", + "i386-mswin32" => "a-1-x86-mswin32-60", + "i386-mswin32_80" => "a-1-x86-mswin32-80", + "i386-mingw32" => "a-1-x86-mingw32", } test_cases.each do |arch, expected| @@ -1969,10 +1969,10 @@ def test_platform_equals assert_equal Gem::Platform::RUBY, @a1.platform test_cases = { - "i386-mswin32" => ["x86", "mswin32", "60"], + "i386-mswin32" => ["x86", "mswin32", "60"], "i386-mswin32_80" => ["x86", "mswin32", "80"], - "i386-mingw32" => ["x86", "mingw32", nil ], - "x86-darwin8" => ["x86", "darwin", "8" ], + "i386-mingw32" => ["x86", "mingw32", nil ], + "x86-darwin8" => ["x86", "darwin", "8" ], } test_cases.each do |arch, expected| @@ -3449,10 +3449,10 @@ def test_metadata_validates_ok @m1 = quick_gem "m", "1" do |s| s.files = %w[lib/code.rb] s.metadata = { - "one" => "two", - "home" => "three", + "one" => "two", + "home" => "three", "homepage_uri" => "https://example.com/user/repo", - "funding_uri" => "https://example.com/donate", + "funding_uri" => "https://example.com/donate", } end diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index e81a9be0fff3ab..5fcf0d481de91d 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -372,7 +372,7 @@ def test_uninstall_user_install @user_spec = Gem::Specification.find_by_name "b" uninstaller = Gem::Uninstaller.new(@user_spec.name, - :executables => true, + :executables => true, :user_install => true) gem_dir = File.join @user_spec.gem_dir From b1efdcee6e27501b56d8e90a945d3301e06b48df Mon Sep 17 00:00:00 2001 From: git Date: Thu, 8 Sep 2022 11:25:32 +0900 Subject: [PATCH 002/269] * 2022-09-08 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index 513a862e648b85..b47594cfcffcdd 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 7 +#define RUBY_RELEASE_DAY 8 #include "ruby/version.h" #include "ruby/internal/abi.h" From 332d29df5342996ce153e65c3096f1b9027afe01 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 8 Sep 2022 11:49:21 +0900 Subject: [PATCH 003/269] [DOC] non-positive `base` in `Kernel#Integer` and `String#to_i` --- object.c | 31 +++++++++++++++++++++++++++---- string.c | 18 ++++++++++++++---- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/object.c b/object.c index eb54d849675cc9..2328b20757aae2 100644 --- a/object.c +++ b/object.c @@ -3175,6 +3175,11 @@ rb_opts_exception_p(VALUE opts, int default_value) * using +to_int+ first and +to_i+ second; * see below for exceptions. * + * With a non-zero +base+, +object+ must be a string or convertible + * to a string. + * + * ==== numeric objects + * * With integer argument +object+ given, returns +object+: * * Integer(1) # => 1 @@ -3186,6 +3191,8 @@ rb_opts_exception_p(VALUE opts, int default_value) * Integer(1.9) # => 1 # Rounds toward zero. * Integer(-1.9) # => -1 # Rounds toward zero. * + * ==== string objects + * * With string argument +object+ and zero +base+ given, * returns +object+ converted to an integer in base 10: * @@ -3193,32 +3200,48 @@ rb_opts_exception_p(VALUE opts, int default_value) * Integer('-100') # => -100 * * With +base+ zero, string +object+ may contain leading characters - * to specify the actual base: + * to specify the actual base (radix indicator): * * Integer('0100') # => 64 # Leading '0' specifies base 8. * Integer('0b100') # => 4 # Leading '0b', specifies base 2. * Integer('0x100') # => 256 # Leading '0x' specifies base 16. * - * With a non-zero +base+ (in range 2..36) given - * (in which case +object+ must be a string), - * returns +object+ converted to an integer in the given base: + * With a positive +base+ (in range 2..36) given, returns +object+ + * converted to an integer in the given base: * * Integer('100', 2) # => 4 * Integer('100', 8) # => 64 * Integer('-100', 16) # => -256 * + * With a negative +base+ (in range -36..-2) given, returns +object+ + * converted to an integer in the radix indicator if exists or + * +-base+: + * + * Integer('0x100', -2) # => 256 + * Integer('100', -2) # => 4 + * Integer('0b100', -8) # => 4 + * Integer('100', -8) # => 64 + * Integer('0o100', -10) # => 64 + * Integer('100', -10) # => 100 + * + * +base+ -1 is equal the -10 case. + * * When converting strings, surrounding whitespace and embedded underscores * are allowed and ignored: * * Integer(' 100 ') # => 100 * Integer('-1_0_0', 16) # => -256 * + * ==== other classes + * * Examples with +object+ of various other classes: * * Integer(Rational(9, 10)) # => 0 # Rounds toward zero. * Integer(Complex(2, 0)) # => 2 # Imaginary part must be zero. * Integer(Time.now) # => 1650974042 * + * ==== keywords + * * With optional keyword argument +exception+ given as +true+ (the default): * * - Raises TypeError if +object+ does not respond to +to_int+ or +to_i+. diff --git a/string.c b/string.c index 951aeca6dd90ca..a2bc7410fabbf6 100644 --- a/string.c +++ b/string.c @@ -6513,11 +6513,21 @@ rb_str_include(VALUE str, VALUE arg) * to_i(base = 10) -> integer * * Returns the result of interpreting leading characters in +self+ - * as an integer in the given +base+ (which must be in (2..36)): + * as an integer in the given +base+ (which must be in (0, 2..36)): * * '123456'.to_i # => 123456 * '123def'.to_i(16) # => 1195503 * + * With +base+ zero, string +object+ may contain leading characters + * to specify the actual base: + * + * '123def'.to_i(0) # => 123 + * '0123def'.to_i(0) # => 83 + * '0b123def'.to_i(0) # => 1 + * '0o123def'.to_i(0) # => 83 + * '0d123def'.to_i(0) # => 123 + * '0x123def'.to_i(0) # => 1195503 + * * Characters past a leading valid number (in the given +base+) are ignored: * * '12.345'.to_i # => 12 @@ -10083,9 +10093,9 @@ rb_str_hex(VALUE str) * returns zero if there is no such leading substring: * * '123'.oct # => 83 - '-377'.oct # => -255 - '0377non-numeric'.oct # => 255 - 'non-numeric'.oct # => 0 + * '-377'.oct # => -255 + * '0377non-numeric'.oct # => 255 + * 'non-numeric'.oct # => 0 * * If +self+ starts with 0, radix indicators are honored; * see Kernel#Integer. From 26c4230404b62475813c50561751487735d6bbdd Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 6 Sep 2022 18:57:31 +0900 Subject: [PATCH 004/269] [MSWin] Disable rubygems of BASERUBY --- win32/setup.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/setup.mak b/win32/setup.mak index 17e321b98427a1..70565973c06f18 100644 --- a/win32/setup.mak +++ b/win32/setup.mak @@ -68,7 +68,7 @@ MJIT_SUPPORT = $(MJIT_SUPPORT) !if defined(BASERUBY) @echo BASERUBY = $(BASERUBY:/=\)>> $(MAKEFILE) !else - @for %I in (ruby.exe) do @echo BASERUBY = %~s$$PATH:I>> $(MAKEFILE) + @for %I in (ruby.exe) do @echo BASERUBY = %~s$$PATH:I --disable=gems>> $(MAKEFILE) !endif @type << >> $(MAKEFILE) $(BANG)if "$$(BASERUBY)" == "" From e76a550876356093a0e48bd540ae80daafedcba0 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 6 Sep 2022 20:22:21 +0900 Subject: [PATCH 005/269] [MSWin] Fix for non-Windows (cygwin/msys) BASERUBY --- win32/mkexports.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/win32/mkexports.rb b/win32/mkexports.rb index 70b7f3f22864cc..6808a38ea00189 100755 --- a/win32/mkexports.rb +++ b/win32/mkexports.rb @@ -113,6 +113,7 @@ def each_export(objs) end case filetype when /OBJECT/, /LIBRARY/ + l.chomp! next if /^[[:xdigit:]]+ 0+ UNDEF / =~ l next unless /External/ =~ l next if /(?:_local_stdio_printf_options|v(f|sn?)printf(_s)?_l)\Z/ =~ l From 70e6be2b055d78d3c475368d744c423564597f6e Mon Sep 17 00:00:00 2001 From: rm155 Date: Mon, 19 Jul 2021 21:48:33 -0400 Subject: [PATCH 006/269] [ruby/ipaddr] Improve Ractor-compliance https://github.com/ruby/ipaddr/commit/73461724e5 --- lib/ipaddr.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index 459b8478a0d072..7a99069fafe48c 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -47,7 +47,7 @@ class IPAddr # 128 bit mask for IPv6 IN6MASK = 0xffffffffffffffffffffffffffffffff # Format string for IPv6 - IN6FORMAT = (["%.4x"] * 8).join(':') + IN6FORMAT = (["%.4x"] * 8).join(':').freeze # Regexp _internally_ used for parsing IPv4 address. RE_IPV4ADDRLIKE = %r{ @@ -736,7 +736,7 @@ def _to_string(addr) unless Socket.const_defined? :AF_INET6 class Socket < BasicSocket # IPv6 protocol family - AF_INET6 = Object.new + AF_INET6 = Object.new.freeze end class << IPSocket From 78af05ba0f29d1fc290dc24a6a5fb645386e7d62 Mon Sep 17 00:00:00 2001 From: rm155 Date: Tue, 27 Jul 2021 12:47:31 -0400 Subject: [PATCH 007/269] [ruby/forwardable] Freeze VERSION and FORWARDABLE_VERSION to improve Ractor-compliance https://github.com/ruby/forwardable/commit/c91f41f4fa --- lib/forwardable.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/forwardable.rb b/lib/forwardable.rb index c9c4128f9f59ef..9318639f016a4b 100644 --- a/lib/forwardable.rb +++ b/lib/forwardable.rb @@ -113,7 +113,9 @@ module Forwardable # Version of +forwardable.rb+ VERSION = "1.3.2" + VERSION.freeze FORWARDABLE_VERSION = VERSION + FORWARDABLE_VERSION.freeze @debug = nil class << self From 7400628cb054a9a9651d69411a100fc9d518099f Mon Sep 17 00:00:00 2001 From: Xin Li Date: Thu, 8 Sep 2022 01:37:37 -0700 Subject: [PATCH 008/269] [Bug #18997] Don't define ruby_qsort when POSIX qsort_r is available The current code would define ruby_qsort as a wrapper of qsort_s when it is available. When both qsort_s and POSIX (GNU) qsort_r are available, we should call qsort_r directly instead, and the qsort_s wrapper is redundant. --- util.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/util.c b/util.c index c7fb0998a8d512..74f2d11b94fd25 100644 --- a/util.c +++ b/util.c @@ -218,6 +218,7 @@ ruby_strtoul(const char *str, char **endptr, int base) typedef int (cmpfunc_t)(const void*, const void*, void*); +#if !defined HAVE_GNU_QSORT_R #if defined HAVE_QSORT_S && defined RUBY_MSVCRT_VERSION /* In contrast to its name, Visual Studio qsort_s is incompatible with * C11 in the order of the comparison function's arguments, and same @@ -263,7 +264,7 @@ ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void qsort_s(base, nel, size, cmp, d); } # define HAVE_GNU_QSORT_R 1 -#elif !defined HAVE_GNU_QSORT_R +#else /* mm.c */ #define mmtype long @@ -530,7 +531,8 @@ ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void else goto nxt; /* need not to sort both sides */ } } -#endif /* HAVE_GNU_QSORT_R */ +#endif +#endif /* !HAVE_GNU_QSORT_R */ char * ruby_strdup(const char *str) From a977c663123f7256f51201ed8390dc84adf63cf6 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 8 Sep 2022 15:25:05 +0900 Subject: [PATCH 009/269] Generate token ID indexes in id.def Separate the logic accross the tables from the template view for id.h. --- defs/id.def | 4 +++- template/id.c.tmpl | 2 +- template/id.h.tmpl | 10 +++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/defs/id.def b/defs/id.def index 097e34e405614d..94af02b12fbca4 100644 --- a/defs/id.def +++ b/defs/id.def @@ -194,13 +194,14 @@ predefined.split(/^/).each_with_index do |line, num| end << token predefined_ids[token] = name end +index = 127 token_ops.split(/^/).each do |line| next if /^#/ =~ line line.sub!(/\s+#.*/, '') id, op, token = line.split next unless id and op token ||= (id unless /\A\W\z/ =~ op) - token_op_ids << [id, op, token] + token_op_ids << [id, op, token, (index += 1 if token)] end { "LOCAL" => local_ids, @@ -212,4 +213,5 @@ end :preserved => preserved_ids, :predefined => predefined_ids, :token_op => token_op_ids, + :last_token => index, } diff --git a/template/id.c.tmpl b/template/id.c.tmpl index 4f30875c044769..5b9e8797303341 100644 --- a/template/id.c.tmpl +++ b/template/id.c.tmpl @@ -12,7 +12,7 @@ **********************************************************************/ <% defs = File.join(File.dirname(File.dirname(erb.filename)), "defs/id.def") -ids = eval(File.read(defs), binding, defs) +ids = eval(File.read(defs), nil, defs) ops = ids[:token_op].uniq {|id, op, token| token && op} %> % ops.each do |_id, _op, token| diff --git a/template/id.h.tmpl b/template/id.h.tmpl index 687cbbbe4072f2..9c588305eb830c 100644 --- a/template/id.h.tmpl +++ b/template/id.h.tmpl @@ -11,10 +11,8 @@ **********************************************************************/ <% -op_id_offset = 128 - defs = File.join(File.dirname(File.dirname(erb.filename)), "defs/id.def") -ids = eval(File.read(defs), binding, defs) +ids = eval(File.read(defs), nil, defs) types = ids.keys.grep(/^[A-Z]/) %> #ifndef RUBY_ID_H @@ -49,11 +47,9 @@ enum ruby_id_types { #define symIFUNC ID2SYM(idIFUNC) #define symCFUNC ID2SYM(idCFUNC) -% index = op_id_offset -% ids[:token_op].each do |_id, _op, token| +% ids[:token_op].each do |_id, _op, token, index| % next unless token #define RUBY_TOKEN_<%=token%> <%=index%> -% index += 1 % end #define RUBY_TOKEN(t) RUBY_TOKEN_##t @@ -66,7 +62,7 @@ enum ruby_method_ids { % ids[:token_op].uniq {|_, op| op}.each do |id, op, token| id<%=id%> = <%=token ? "RUBY_TOKEN(#{token})" : "'#{op}'"%>, % end - tPRESERVED_ID_BEGIN = <%=index-1%>, + tPRESERVED_ID_BEGIN = <%=ids[:last_token]%>, % ids[:preserved].each do |token| id<%=token%>, % end From bcf82b7c26a88a2521715a38990f0ab081001190 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 8 Sep 2022 15:36:04 +0900 Subject: [PATCH 010/269] Process token IDs from id.def without id.h Fixes id.h error during updating ripper.c by `make after-update`. While it used to update id.h in the build directory, but was trying to update ripper.c in the source directory. In principle, files in the source directory can or should not depend on files in the build directory. --- common.mk | 4 ++-- ext/ripper/depend | 5 ++--- tool/id2token.rb | 11 ++++------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/common.mk b/common.mk index f86bf3ff787e24..1385db9a4976e5 100644 --- a/common.mk +++ b/common.mk @@ -933,7 +933,7 @@ PHONY: {$(srcdir)}.y.c: $(ECHO) generating $@ - $(Q)$(BASERUBY) $(tooldir)/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ --vpath=$(VPATH) id.h $(SRC_FILE) > parse.tmp.y + $(Q)$(BASERUBY) $(tooldir)/id2token.rb $(SRC_FILE) > parse.tmp.y $(Q)$(BASERUBY) $(tooldir)/pure_parser.rb parse.tmp.y $(YACC) $(Q)$(RM) parse.tmp.y.bak $(Q)$(YACC) -d $(YFLAGS) -o y.tab.c parse.tmp.y @@ -1219,7 +1219,7 @@ revision.$(HAVE_BASERUBY:yes=tmp):: $(srcdir)/version.h $(tooldir)/file2lastrev. $(REVISION_H): revision.tmp $(Q)$(IFCHANGE) "--timestamp=$@" "$(srcdir)/revision.h" revision.tmp -$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y id.h $(srcdir)/ext/ripper/depend +$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y $(srcdir)/defs/id.def $(srcdir)/ext/ripper/depend $(ECHO) generating $@ $(Q) $(CHDIR) $(@D) && \ sed -e 's/{\$$([^(){}]*)[^{}]*}//g' -e /AUTOGENERATED/q depend | \ diff --git a/ext/ripper/depend b/ext/ripper/depend index 80a6d62346c7d7..15c557a8efa052 100644 --- a/ext/ripper/depend +++ b/ext/ripper/depend @@ -17,10 +17,9 @@ ripper.o: ripper.c all: check static: check -ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y {$(VPATH)}id.h +ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y $(top_srcdir)/defs/id.def $(ECHO) extracting $@ from $(top_srcdir)/parse.y - $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ \ - --vpath=$(VPATH)$(PATH_SEPARATOR)$(top_srcdir) id.h $(top_srcdir)/parse.y > ripper.tmp.y + $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb $(top_srcdir)/parse.y > ripper.tmp.y $(Q) $(RUBY) $(top_srcdir)/tool/pure_parser.rb ripper.tmp.y $(BISON) $(Q) $(RM) ripper.tmp.y.bak $(Q) $(RUBY) $(srcdir)/tools/preproc.rb ripper.tmp.y --output=$@ diff --git a/tool/id2token.rb b/tool/id2token.rb index d12ea9c08e5b7a..cf730958422389 100755 --- a/tool/id2token.rb +++ b/tool/id2token.rb @@ -5,18 +5,15 @@ BEGIN { require 'optparse' - require_relative 'lib/vpath' - vpath = VPath.new - header = nil opt = OptionParser.new do |o| - vpath.def_options(o) - header = o.order!(ARGV).shift + o.order!(ARGV) end or abort opt.opt_s TOKENS = {} - h = vpath.read(header) rescue abort("#{header} not found in #{vpath.inspect}") - h.scan(/^#define\s+RUBY_TOKEN_(\w+)\s+(\d+)/) do |token, id| + defs = File.join(File.dirname(File.dirname(__FILE__)), "defs/id.def") + ids = eval(File.read(defs), nil, defs) + ids[:token_op].each do |_id, _op, token, id| TOKENS[token] = id end From e0cd466ae71cd483b73d28128df1a02dd52fcb92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 8 Sep 2022 12:10:34 +0200 Subject: [PATCH 011/269] [rubygems/rubygems] Fix unused variable warning https://github.com/rubygems/rubygems/commit/ca8d47e53a --- spec/bundler/bundler/endpoint_specification_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb index 81fcd76e0135c3..7dd6925007bb0e 100644 --- a/spec/bundler/bundler/endpoint_specification_spec.rb +++ b/spec/bundler/bundler/endpoint_specification_spec.rb @@ -54,8 +54,6 @@ let(:required_ruby_version) { existing_value } it "should return the current value when already set on endpoint specification" do - remote_spec = double(:remote_spec, :required_ruby_version => "remote_value", :required_rubygems_version => nil) - expect(spec.required_ruby_version). eql?(existing_value) end end From cd1724bddeac5dd4c01ab0a2d527110fff9cf5f9 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 8 Sep 2022 12:43:03 +0200 Subject: [PATCH 012/269] rb_str_concat_literals: use rb_str_buf_append That's about 1.30x faster. --- benchmark/string_concat.yml | 10 ++++++++++ string.c | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/benchmark/string_concat.yml b/benchmark/string_concat.yml index b8a69ed909f2a4..e65c00cca97650 100644 --- a/benchmark/string_concat.yml +++ b/benchmark/string_concat.yml @@ -33,3 +33,13 @@ benchmark: buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK + interpolation: | + buffer = "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" diff --git a/string.c b/string.c index a2bc7410fabbf6..963652cdb967c8 100644 --- a/string.c +++ b/string.c @@ -3393,8 +3393,7 @@ rb_str_concat_literals(size_t num, const VALUE *strary) const VALUE v = strary[i]; int encidx = ENCODING_GET(v); - rb_enc_cr_str_buf_cat(str, RSTRING_PTR(v), RSTRING_LEN(v), - encidx, ENC_CODERANGE(v), NULL); + rb_str_buf_append(str, v); if (encidx != ENCINDEX_US_ASCII) { if (ENCODING_GET_INLINED(str) == ENCINDEX_US_ASCII) rb_enc_set_index(str, encidx); From b7fa78b0f3c42d698aff7d6003d839febcbb4740 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 8 Sep 2022 14:07:43 +0200 Subject: [PATCH 013/269] vm_objtostring: skip method lookup for T_STRING receivers We don't need it, and in string interpolation context that's the common case. --- vm_insnhelper.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 9ccfdff4a023a4..c68872a9bf6b62 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -4851,11 +4851,14 @@ VALUE rb_mod_name(VALUE); static VALUE vm_objtostring(const rb_iseq_t *iseq, VALUE recv, CALL_DATA cd) { + int type = TYPE(recv); + if (type == T_STRING) { + return recv; + } + const struct rb_callcache *cc = vm_search_method((VALUE)iseq, cd, recv); - switch (TYPE(recv)) { - case T_STRING: - return recv; + switch (type) { case T_SYMBOL: if (check_cfunc(vm_cc_cme(cc), rb_sym_to_s)) { // rb_sym_to_s() allocates a mutable string, but since we are only From 55fef084dac843caf155f03077773704b36e139e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 5 Sep 2022 20:17:05 +0900 Subject: [PATCH 014/269] [Win32] Remove dead code using `chsize` Already using `rb_w32_truncate` and `rb_w32_ftruncate`, and `HAVE_FTRUNCATE` has been added 14 years ago. --- file.c | 44 ++++---------------------------------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/file.c b/file.c index ced2c1e4a20a05..514dec341aaa36 100644 --- a/file.c +++ b/file.c @@ -5086,41 +5086,17 @@ rb_file_s_join(VALUE klass, VALUE args) return rb_file_join(args); } -#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE) +#if defined(HAVE_TRUNCATE) struct truncate_arg { const char *path; -#if defined(HAVE_TRUNCATE) -#define NUM2POS(n) NUM2OFFT(n) off_t pos; -#else -#define NUM2POS(n) NUM2LONG(n) - long pos; -#endif }; static void * nogvl_truncate(void *ptr) { struct truncate_arg *ta = ptr; -#ifdef HAVE_TRUNCATE return (void *)(VALUE)truncate(ta->path, ta->pos); -#else /* defined(HAVE_CHSIZE) */ - { - int tmpfd = rb_cloexec_open(ta->path, 0, 0); - - if (tmpfd < 0) - return (void *)-1; - rb_update_max_fd(tmpfd); - if (chsize(tmpfd, ta->pos) < 0) { - int e = errno; - close(tmpfd); - errno = e; - return (void *)-1; - } - close(tmpfd); - return 0; - } -#endif } /* @@ -5144,7 +5120,7 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len) struct truncate_arg ta; int r; - ta.pos = NUM2POS(len); + ta.pos = NUM2OFFT(len); FilePathValue(path); path = rb_str_encode_ospath(path); ta.path = StringValueCStr(path); @@ -5154,22 +5130,15 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len) if (r < 0) rb_sys_fail_path(path); return INT2FIX(0); -#undef NUM2POS } #else #define rb_file_s_truncate rb_f_notimplement #endif -#if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE) +#if defined(HAVE_FTRUNCATE) struct ftruncate_arg { int fd; -#if defined(HAVE_FTRUNCATE) -#define NUM2POS(n) NUM2OFFT(n) off_t pos; -#else -#define NUM2POS(n) NUM2LONG(n) - long pos; -#endif }; static VALUE @@ -5177,11 +5146,7 @@ nogvl_ftruncate(void *ptr) { struct ftruncate_arg *fa = ptr; -#ifdef HAVE_FTRUNCATE return (VALUE)ftruncate(fa->fd, fa->pos); -#else /* defined(HAVE_CHSIZE) */ - return (VALUE)chsize(fa->fd, fa->pos); -#endif } /* @@ -5204,7 +5169,7 @@ rb_file_truncate(VALUE obj, VALUE len) rb_io_t *fptr; struct ftruncate_arg fa; - fa.pos = NUM2POS(len); + fa.pos = NUM2OFFT(len); GetOpenFile(obj, fptr); if (!(fptr->mode & FMODE_WRITABLE)) { rb_raise(rb_eIOError, "not opened for writing"); @@ -5215,7 +5180,6 @@ rb_file_truncate(VALUE obj, VALUE len) rb_sys_fail_path(fptr->pathv); } return INT2FIX(0); -#undef NUM2POS } #else #define rb_file_truncate rb_f_notimplement From e2ccb316b437104cd1734c378970d34f5305966d Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 5 Sep 2022 20:23:27 +0900 Subject: [PATCH 015/269] [Bug #5317] Use `rb_off_t` instead of `off_t` Get rid of the conflict with system-provided small `off_t`. --- file.c | 8 +- include/ruby/fiber/scheduler.h | 4 +- include/ruby/internal/intern/file.h | 2 +- include/ruby/io/buffer.h | 6 +- include/ruby/win32.h | 11 +- io.c | 156 +++++++++++++--------------- io_buffer.c | 28 ++--- scheduler.c | 4 +- win32/Makefile.sub | 4 + win32/win32.c | 16 +-- 10 files changed, 117 insertions(+), 122 deletions(-) diff --git a/file.c b/file.c index 514dec341aaa36..eb5851de7d66bb 100644 --- a/file.c +++ b/file.c @@ -2517,7 +2517,7 @@ rb_file_birthtime(VALUE obj) * */ -off_t +rb_off_t rb_file_size(VALUE file) { if (RB_TYPE_P(file, T_FILE)) { @@ -5089,7 +5089,7 @@ rb_file_s_join(VALUE klass, VALUE args) #if defined(HAVE_TRUNCATE) struct truncate_arg { const char *path; - off_t pos; + rb_off_t pos; }; static void * @@ -5138,7 +5138,7 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len) #if defined(HAVE_FTRUNCATE) struct ftruncate_arg { int fd; - off_t pos; + rb_off_t pos; }; static VALUE @@ -6093,7 +6093,7 @@ rb_stat_z(VALUE obj) static VALUE rb_stat_s(VALUE obj) { - off_t size = get_stat(obj)->st_size; + rb_off_t size = get_stat(obj)->st_size; if (size == 0) return Qnil; return OFFT2NUM(size); diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h index 9f67bd5bf6d194..d38651da5c9109 100644 --- a/include/ruby/fiber/scheduler.h +++ b/include/ruby/fiber/scheduler.h @@ -276,7 +276,7 @@ VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_ * @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, off_t offset); +VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset); /** * Nonblocking write to the passed IO at the specified offset. @@ -289,7 +289,7 @@ VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_ * @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, off_t offset); +VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset); /** * Nonblocking read from the passed IO using a native buffer. diff --git a/include/ruby/internal/intern/file.h b/include/ruby/internal/intern/file.h index 2dc60c7ba78e36..79820fdc61657c 100644 --- a/include/ruby/internal/intern/file.h +++ b/include/ruby/internal/intern/file.h @@ -206,7 +206,7 @@ int rb_is_absolute_path(const char *path); * unpredictable. POSIX's `` states that "the use of * this field is unspecified" then. */ -off_t rb_file_size(VALUE file); +rb_off_t rb_file_size(VALUE file); RBIMPL_SYMBOL_EXPORT_END() diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h index 907fec20bb47b9..bb83fe0be6d9b7 100644 --- a/include/ruby/io/buffer.h +++ b/include/ruby/io/buffer.h @@ -65,7 +65,7 @@ enum rb_io_buffer_endian { }; VALUE rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags); -VALUE rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags); +VALUE rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags); VALUE rb_io_buffer_lock(VALUE self); VALUE rb_io_buffer_unlock(VALUE self); @@ -82,9 +82,9 @@ 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, off_t offset); +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, off_t offset); +VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset); RBIMPL_SYMBOL_EXPORT_END() diff --git a/include/ruby/win32.h b/include/ruby/win32.h index ea460a5e46cd40..93e6183ed990a2 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -195,7 +195,6 @@ struct stati128 { long st_ctimensec; }; -#define off_t __int64 #define stat stati128 #undef SIZEOF_STRUCT_STAT_ST_INO #define SIZEOF_STRUCT_STAT_ST_INO sizeof(unsigned __int64) @@ -401,9 +400,9 @@ scalb(double a, long b) #define SUFFIX -extern int rb_w32_ftruncate(int fd, off_t length); -extern int rb_w32_truncate(const char *path, off_t length); -extern int rb_w32_utruncate(const char *path, off_t length); +extern int rb_w32_ftruncate(int fd, rb_off_t length); +extern int rb_w32_truncate(const char *path, rb_off_t length); +extern int rb_w32_utruncate(const char *path, rb_off_t length); #undef HAVE_FTRUNCATE #define HAVE_FTRUNCATE 1 @@ -722,7 +721,7 @@ int rb_w32_fclose(FILE*); int rb_w32_pipe(int[2]); ssize_t rb_w32_read(int, void *, size_t); ssize_t rb_w32_write(int, const void *, size_t); -off_t rb_w32_lseek(int, off_t, int); +rb_off_t rb_w32_lseek(int, rb_off_t, int); int rb_w32_uutime(const char *, const struct utimbuf *); int rb_w32_uutimes(const char *, const struct timeval *); int rb_w32_uutimensat(int /* must be AT_FDCWD */, const char *, const struct timespec *, int /* must be 0 */); @@ -815,7 +814,7 @@ double rb_w32_pow(double x, double y); #define MAP_ANON 0x1000 #define MAP_ANONYMOUS MAP_ANON -extern void *rb_w32_mmap(void *, size_t, int, int, int, off_t); +extern void *rb_w32_mmap(void *, size_t, int, int, int, rb_off_t); extern int rb_w32_munmap(void *, size_t); extern int rb_w32_mprotect(void *, size_t, int); diff --git a/io.c b/io.c index 1eb48dd19b22d0..30165e16169e70 100644 --- a/io.c +++ b/io.c @@ -75,10 +75,6 @@ #include #endif -#if !HAVE_OFF_T && !defined(off_t) -# define off_t long -#endif - #ifdef HAVE_SYS_TIME_H # include #endif @@ -149,10 +145,6 @@ #define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) #endif -#if SIZEOF_OFF_T > SIZEOF_LONG && !defined(HAVE_LONG_LONG) -# error off_t is bigger than long, but you have no long long... -#endif - #ifndef PIPE_BUF # ifdef _POSIX_PIPE_BUF # define PIPE_BUF _POSIX_PIPE_BUF @@ -620,7 +612,7 @@ raise_on_write(rb_io_t *fptr, int e, VALUE errinfo) static void io_unread(rb_io_t *fptr) { - off_t r, pos; + rb_off_t r, pos; ssize_t read_size; long i; long newlines = 0; @@ -859,7 +851,7 @@ rb_io_s_try_convert(VALUE dummy, VALUE io) static void io_unread(rb_io_t *fptr) { - off_t r; + rb_off_t r; rb_io_check_closed(fptr); if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX) return; @@ -2224,7 +2216,7 @@ static VALUE rb_io_tell(VALUE io) { rb_io_t *fptr; - off_t pos; + rb_off_t pos; GetOpenFile(io, fptr); pos = io_tell(fptr); @@ -2237,7 +2229,7 @@ static VALUE rb_io_seek(VALUE io, VALUE offset, int whence) { rb_io_t *fptr; - off_t pos; + rb_off_t pos; pos = NUM2OFFT(offset); GetOpenFile(io, fptr); @@ -2348,7 +2340,7 @@ static VALUE rb_io_set_pos(VALUE io, VALUE offset) { rb_io_t *fptr; - off_t pos; + rb_off_t pos; pos = NUM2OFFT(offset); GetOpenFile(io, fptr); @@ -2884,8 +2876,8 @@ static long remain_size(rb_io_t *fptr) { struct stat st; - off_t siz = READ_DATA_PENDING_COUNT(fptr); - off_t pos; + rb_off_t siz = READ_DATA_PENDING_COUNT(fptr); + rb_off_t pos; if (fstat(fptr->fd, &st) == 0 && S_ISREG(st.st_mode) #if defined(__HAIKU__) @@ -5662,7 +5654,7 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io) VALUE offset, ptrname; int whence = SEEK_SET; rb_io_t *fptr; - off_t pos; + rb_off_t pos; if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) { whence = interpret_seek_whence(ptrname); @@ -5790,7 +5782,7 @@ struct prdwr_internal_arg { int fd; void *buf; size_t count; - off_t offset; + rb_off_t offset; }; #endif /* HAVE_PREAD || HAVE_PWRITE */ @@ -7984,7 +7976,7 @@ io_reopen(VALUE io, VALUE nfile) { rb_io_t *fptr, *orig; int fd, fd2; - off_t pos = 0; + rb_off_t pos = 0; nfile = rb_io_get_io(nfile); GetOpenFile(io, fptr); @@ -8204,7 +8196,7 @@ rb_io_init_copy(VALUE dest, VALUE io) rb_io_t *fptr, *orig; int fd; VALUE write_io; - off_t pos; + rb_off_t pos; io = rb_io_get_io(io); if (!OBJ_INIT_COPY(dest, io)) return dest; @@ -10345,8 +10337,8 @@ static VALUE sym_normal, sym_sequential, sym_random, struct io_advise_struct { int fd; int advice; - off_t offset; - off_t len; + rb_off_t offset; + rb_off_t len; }; static VALUE @@ -10393,7 +10385,7 @@ io_advise_sym_to_const(VALUE sym) } static VALUE -do_io_advise(rb_io_t *fptr, VALUE advice, off_t offset, off_t len) +do_io_advise(rb_io_t *fptr, VALUE advice, rb_off_t offset, rb_off_t len) { int rv; struct io_advise_struct ias; @@ -10483,7 +10475,7 @@ static VALUE rb_io_advise(int argc, VALUE *argv, VALUE io) { VALUE advice, offset, len; - off_t off, l; + rb_off_t off, l; rb_io_t *fptr; rb_scan_args(argc, argv, "12", &advice, &offset, &len); @@ -12029,15 +12021,15 @@ rb_io_s_binwrite(int argc, VALUE *argv, VALUE io) struct copy_stream_struct { VALUE src; VALUE dst; - off_t copy_length; /* (off_t)-1 if not specified */ - off_t src_offset; /* (off_t)-1 if not specified */ + rb_off_t copy_length; /* (rb_off_t)-1 if not specified */ + rb_off_t src_offset; /* (rb_off_t)-1 if not specified */ rb_io_t *src_fptr; rb_io_t *dst_fptr; unsigned close_src : 1; unsigned close_dst : 1; int error_no; - off_t total; + rb_off_t total; const char *syserr; const char *notimp; VALUE th; @@ -12202,7 +12194,7 @@ nogvl_copy_stream_wait_write(struct copy_stream_struct *stp) #ifdef USE_COPY_FILE_RANGE static ssize_t -simple_copy_file_range(int in_fd, off_t *in_offset, int out_fd, off_t *out_offset, size_t count, unsigned int flags) +simple_copy_file_range(int in_fd, rb_off_t *in_offset, int out_fd, rb_off_t *out_offset, size_t count, unsigned int flags) { #ifdef HAVE_COPY_FILE_RANGE return copy_file_range(in_fd, in_offset, out_fd, out_offset, count, flags); @@ -12215,15 +12207,15 @@ static int nogvl_copy_file_range(struct copy_stream_struct *stp) { ssize_t ss; - off_t src_size; - off_t copy_length, src_offset, *src_offset_ptr; + rb_off_t src_size; + rb_off_t copy_length, src_offset, *src_offset_ptr; if (!S_ISREG(stp->src_stat.st_mode)) return 0; src_size = stp->src_stat.st_size; src_offset = stp->src_offset; - if (src_offset >= (off_t)0) { + if (src_offset >= (rb_off_t)0) { src_offset_ptr = &src_offset; } else { @@ -12231,12 +12223,12 @@ nogvl_copy_file_range(struct copy_stream_struct *stp) } copy_length = stp->copy_length; - if (copy_length < (off_t)0) { - if (src_offset < (off_t)0) { - off_t current_offset; + if (copy_length < (rb_off_t)0) { + if (src_offset < (rb_off_t)0) { + rb_off_t current_offset; errno = 0; current_offset = lseek(stp->src_fptr->fd, 0, SEEK_CUR); - if (current_offset < (off_t)0 && errno) { + if (current_offset < (rb_off_t)0 && errno) { stp->syserr = "lseek"; stp->error_no = errno; return (int)current_offset; @@ -12251,7 +12243,7 @@ nogvl_copy_file_range(struct copy_stream_struct *stp) retry_copy_file_range: # if SIZEOF_OFF_T > SIZEOF_SIZE_T /* we are limited by the 32-bit ssize_t return value on 32-bit */ - ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length; + ss = (copy_length > (rb_off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length; # else ss = (ssize_t)copy_length; # endif @@ -12310,11 +12302,11 @@ nogvl_copy_file_range(struct copy_stream_struct *stp) static int nogvl_fcopyfile(struct copy_stream_struct *stp) { - off_t cur, ss = 0; - const off_t src_offset = stp->src_offset; + rb_off_t cur, ss = 0; + const rb_off_t src_offset = stp->src_offset; int ret; - if (stp->copy_length >= (off_t)0) { + if (stp->copy_length >= (rb_off_t)0) { /* copy_length can't be specified in fcopyfile(3) */ return 0; } @@ -12324,30 +12316,30 @@ nogvl_fcopyfile(struct copy_stream_struct *stp) if (!S_ISREG(stp->dst_stat.st_mode)) return 0; - if (lseek(stp->dst_fptr->fd, 0, SEEK_CUR) > (off_t)0) /* if dst IO was already written */ + if (lseek(stp->dst_fptr->fd, 0, SEEK_CUR) > (rb_off_t)0) /* if dst IO was already written */ return 0; if (fcntl(stp->dst_fptr->fd, F_GETFL) & O_APPEND) { /* fcopyfile(3) appends src IO to dst IO and then truncates * dst IO to src IO's original size. */ - off_t end = lseek(stp->dst_fptr->fd, 0, SEEK_END); + rb_off_t end = lseek(stp->dst_fptr->fd, 0, SEEK_END); lseek(stp->dst_fptr->fd, 0, SEEK_SET); - if (end > (off_t)0) return 0; + if (end > (rb_off_t)0) return 0; } - if (src_offset > (off_t)0) { - off_t r; + if (src_offset > (rb_off_t)0) { + rb_off_t r; /* get current offset */ errno = 0; cur = lseek(stp->src_fptr->fd, 0, SEEK_CUR); - if (cur < (off_t)0 && errno) { + if (cur < (rb_off_t)0 && errno) { stp->error_no = errno; return 1; } errno = 0; r = lseek(stp->src_fptr->fd, src_offset, SEEK_SET); - if (r < (off_t)0 && errno) { + if (r < (rb_off_t)0 && errno) { stp->error_no = errno; return 1; } @@ -12359,12 +12351,12 @@ nogvl_fcopyfile(struct copy_stream_struct *stp) if (ret == 0) { /* success */ stp->total = ss; - if (src_offset > (off_t)0) { - off_t r; + if (src_offset > (rb_off_t)0) { + rb_off_t r; errno = 0; /* reset offset */ r = lseek(stp->src_fptr->fd, cur, SEEK_SET); - if (r < (off_t)0 && errno) { + if (r < (rb_off_t)0 && errno) { stp->error_no = errno; return 1; } @@ -12395,7 +12387,7 @@ nogvl_fcopyfile(struct copy_stream_struct *stp) # endif static ssize_t -simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count) +simple_sendfile(int out_fd, int in_fd, rb_off_t *offset, rb_off_t count) { return sendfile(out_fd, in_fd, offset, (size_t)count); } @@ -12407,11 +12399,11 @@ simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count) # define USE_SENDFILE static ssize_t -simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count) +simple_sendfile(int out_fd, int in_fd, rb_off_t *offset, rb_off_t count) { int r; - off_t pos = offset ? *offset : lseek(in_fd, 0, SEEK_CUR); - off_t sbytes; + rb_off_t pos = offset ? *offset : lseek(in_fd, 0, SEEK_CUR); + rb_off_t sbytes; # ifdef __APPLE__ r = sendfile(in_fd, out_fd, pos, &count, NULL, 0); sbytes = count; @@ -12437,9 +12429,9 @@ static int nogvl_copy_stream_sendfile(struct copy_stream_struct *stp) { ssize_t ss; - off_t src_size; - off_t copy_length; - off_t src_offset; + rb_off_t src_size; + rb_off_t copy_length; + rb_off_t src_offset; int use_pread; if (!S_ISREG(stp->src_stat.st_mode)) @@ -12452,17 +12444,17 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp) #endif src_offset = stp->src_offset; - use_pread = src_offset >= (off_t)0; + use_pread = src_offset >= (rb_off_t)0; copy_length = stp->copy_length; - if (copy_length < (off_t)0) { + if (copy_length < (rb_off_t)0) { if (use_pread) copy_length = src_size - src_offset; else { - off_t cur; + rb_off_t cur; errno = 0; cur = lseek(stp->src_fptr->fd, 0, SEEK_CUR); - if (cur < (off_t)0 && errno) { + if (cur < (rb_off_t)0 && errno) { stp->syserr = "lseek"; stp->error_no = errno; return (int)cur; @@ -12474,7 +12466,7 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp) retry_sendfile: # if SIZEOF_OFF_T > SIZEOF_SIZE_T /* we are limited by the 32-bit ssize_t return value on 32-bit */ - ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length; + ss = (copy_length > (rb_off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length; # else ss = (ssize_t)copy_length; # endif @@ -12545,11 +12537,11 @@ maygvl_read(int has_gvl, rb_io_t *fptr, void *buf, size_t count) } static ssize_t -maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, size_t len, off_t offset) +maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, size_t len, rb_off_t offset) { ssize_t ss; retry_read: - if (offset < (off_t)0) { + if (offset < (rb_off_t)0) { ss = maygvl_read(has_gvl, stp->src_fptr, buf, len); } else { @@ -12582,7 +12574,7 @@ maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, return ss; #endif } - stp->syserr = offset < (off_t)0 ? "read" : "pread"; + stp->syserr = offset < (rb_off_t)0 ? "read" : "pread"; stp->error_no = errno; } return ss; @@ -12621,31 +12613,31 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp) size_t len; ssize_t ss; int ret; - off_t copy_length; + rb_off_t copy_length; + rb_off_t src_offset; int use_eof; - off_t src_offset; int use_pread; copy_length = stp->copy_length; - use_eof = copy_length < (off_t)0; + use_eof = copy_length < (rb_off_t)0; src_offset = stp->src_offset; - use_pread = src_offset >= (off_t)0; + use_pread = src_offset >= (rb_off_t)0; if (use_pread && stp->close_src) { - off_t r; + rb_off_t r; errno = 0; r = lseek(stp->src_fptr->fd, src_offset, SEEK_SET); - if (r < (off_t)0 && errno) { + if (r < (rb_off_t)0 && errno) { stp->syserr = "lseek"; stp->error_no = errno; return; } - src_offset = (off_t)-1; + src_offset = (rb_off_t)-1; use_pread = 0; } while (use_eof || 0 < copy_length) { - if (!use_eof && copy_length < (off_t)sizeof(buf)) { + if (!use_eof && copy_length < (rb_off_t)sizeof(buf)) { len = (size_t)copy_length; } else { @@ -12657,7 +12649,7 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp) src_offset += ss; } else { - ss = maygvl_copy_stream_read(0, stp, buf, len, (off_t)-1); + ss = maygvl_copy_stream_read(0, stp, buf, len, (rb_off_t)-1); } if (ss <= 0) /* EOF or error */ return; @@ -12712,8 +12704,8 @@ copy_stream_fallback_body(VALUE arg) const int buflen = 16*1024; VALUE n; VALUE buf = rb_str_buf_new(buflen); - off_t rest = stp->copy_length; - off_t off = stp->src_offset; + rb_off_t rest = stp->copy_length; + rb_off_t off = stp->src_offset; ID read_method = id_readpartial; if (!stp->src_fptr) { @@ -12725,7 +12717,7 @@ copy_stream_fallback_body(VALUE arg) while (1) { long numwrote; long l; - if (stp->copy_length < (off_t)0) { + if (stp->copy_length < (rb_off_t)0) { l = buflen; } else { @@ -12750,7 +12742,7 @@ copy_stream_fallback_body(VALUE arg) return Qnil; if (ss == 0) rb_eof_error(); - if (off >= (off_t)0) + if (off >= (rb_off_t)0) off += ss; } n = rb_io_write(stp->dst, buf); @@ -12768,7 +12760,7 @@ copy_stream_fallback_body(VALUE arg) static VALUE copy_stream_fallback(struct copy_stream_struct *stp) { - if (!stp->src_fptr && stp->src_offset >= (off_t)0) { + if (!stp->src_fptr && stp->src_offset >= (rb_off_t)0) { rb_raise(rb_eArgError, "cannot specify src_offset for non-IO"); } rb_rescue2(copy_stream_fallback_body, (VALUE)stp, @@ -12868,10 +12860,10 @@ copy_stream_body(VALUE arg) if (stp->dst_fptr) io_ascii8bit_binmode(stp->dst_fptr); - if (stp->src_offset < (off_t)0 && stp->src_fptr && stp->src_fptr->rbuf.len) { + if (stp->src_offset < (rb_off_t)0 && stp->src_fptr && stp->src_fptr->rbuf.len) { size_t len = stp->src_fptr->rbuf.len; VALUE str; - if (stp->copy_length >= (off_t)0 && stp->copy_length < (off_t)len) { + if (stp->copy_length >= (rb_off_t)0 && stp->copy_length < (rb_off_t)len) { len = (size_t)stp->copy_length; } str = rb_str_buf_new(len); @@ -12885,7 +12877,7 @@ copy_stream_body(VALUE arg) rb_io_write(dst_io, str); rb_str_resize(str, 0); stp->total += len; - if (stp->copy_length >= (off_t)0) + if (stp->copy_length >= (rb_off_t)0) stp->copy_length -= len; } @@ -12998,12 +12990,12 @@ rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io) st.dst_fptr = NULL; if (NIL_P(length)) - st.copy_length = (off_t)-1; + st.copy_length = (rb_off_t)-1; else st.copy_length = NUM2OFFT(length); if (NIL_P(src_offset)) - st.src_offset = (off_t)-1; + st.src_offset = (rb_off_t)-1; else st.src_offset = NUM2OFFT(src_offset); diff --git a/io_buffer.c b/io_buffer.c index 6ce0dd13dfba76..98f1da1fc12f53 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -64,7 +64,7 @@ io_buffer_map_memory(size_t size) } static void -io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t offset, enum rb_io_buffer_flags flags) +io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags) { #if defined(_WIN32) HANDLE file = (HANDLE)_get_osfhandle(descriptor); @@ -409,7 +409,7 @@ rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags) } VALUE -rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags) +rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags) { io_buffer_experimental(); @@ -478,7 +478,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass) size = RB_NUM2SIZE(argv[1]); } else { - off_t file_size = rb_file_size(io); + rb_off_t file_size = rb_file_size(io); // Compiler can confirm that we handled file_size < 0 case: if (file_size < 0) { @@ -494,7 +494,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass) } } - off_t offset = 0; + rb_off_t offset = 0; if (argc >= 3) { offset = NUM2OFFT(argv[2]); } @@ -2037,7 +2037,7 @@ io_buffer_read(VALUE self, VALUE io, VALUE length) } VALUE -rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset) +rb_io_buffer_pread(VALUE self, VALUE io, size_t length, rb_off_t offset) { VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { @@ -2063,16 +2063,16 @@ rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset) ssize_t result = pread(descriptor, base, size, offset); #else // This emulation is not thread safe, but the GVL means it's unlikely to be a problem. - off_t current_offset = lseek(descriptor, 0, SEEK_CUR); - if (current_offset == (off_t)-1) + rb_off_t current_offset = lseek(descriptor, 0, SEEK_CUR); + if (current_offset == (rb_off_t)-1) return rb_fiber_scheduler_io_result(-1, errno); - if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1) + if (lseek(descriptor, offset, SEEK_SET) == (rb_off_t)-1) return rb_fiber_scheduler_io_result(-1, errno); ssize_t result = read(descriptor, base, size); - if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1) + if (lseek(descriptor, current_offset, SEEK_SET) == (rb_off_t)-1) return rb_fiber_scheduler_io_result(-1, errno); #endif @@ -2120,7 +2120,7 @@ io_buffer_write(VALUE self, VALUE io, VALUE length) } VALUE -rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset) +rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset) { VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { @@ -2146,16 +2146,16 @@ rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset) ssize_t result = pwrite(descriptor, base, length, offset); #else // This emulation is not thread safe, but the GVL means it's unlikely to be a problem. - off_t current_offset = lseek(descriptor, 0, SEEK_CUR); - if (current_offset == (off_t)-1) + rb_off_t current_offset = lseek(descriptor, 0, SEEK_CUR); + if (current_offset == (rb_off_t)-1) return rb_fiber_scheduler_io_result(-1, errno); - if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1) + if (lseek(descriptor, offset, SEEK_SET) == (rb_off_t)-1) return rb_fiber_scheduler_io_result(-1, errno); ssize_t result = write(descriptor, base, length); - if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1) + if (lseek(descriptor, current_offset, SEEK_SET) == (rb_off_t)-1) return rb_fiber_scheduler_io_result(-1, errno); #endif diff --git a/scheduler.c b/scheduler.c index 06658356b176e9..e13733722cebf5 100644 --- a/scheduler.c +++ b/scheduler.c @@ -242,7 +242,7 @@ rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t lengt } VALUE -rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset) +rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset) { VALUE arguments[] = { io, buffer, SIZET2NUM(length), OFFT2NUM(offset) @@ -262,7 +262,7 @@ rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t leng } VALUE -rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset) +rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset) { VALUE arguments[] = { io, buffer, SIZET2NUM(length), OFFT2NUM(offset) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index e4b9a3cac88875..0b1f41f70ec193 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -632,6 +632,10 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub #define ULL_TO_DOUBLE(n) ((double)(unsigned long)((n)>>32) * (1I64 << 32) + (unsigned long)(n)) !endif #define HAVE_OFF_T 1 +#define rb_off_t __int64 +#define SIGNEDNESS_OF_OFF_T -1 +#define OFFT2NUM(v) LL2NUM(v) +#define NUM2OFFT(v) NUM2LL(v) #define SIZEOF_INT 4 #define SIZEOF_SHORT 2 #define SIZEOF_LONG 4 diff --git a/win32/win32.c b/win32/win32.c index edf89be4b18305..480b7a679712b7 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -5899,8 +5899,8 @@ rb_w32_lstati128(const char *path, struct stati128 *st) } /* License: Ruby's */ -off_t -rb_w32_lseek(int fd, off_t ofs, int whence) +rb_off_t +rb_w32_lseek(int fd, rb_off_t ofs, int whence) { SOCKET sock = TO_SOCKET(fd); if (is_socket(sock) || is_pipe(sock)) { @@ -5941,7 +5941,7 @@ rb_w32_uaccess(const char *path, int mode) /* License: Ruby's */ static int -rb_chsize(HANDLE h, off_t size) +rb_chsize(HANDLE h, rb_off_t size) { long upos, lpos, usize, lsize; int ret = -1; @@ -5970,7 +5970,7 @@ rb_chsize(HANDLE h, off_t size) /* License: Ruby's */ static int -w32_truncate(const char *path, off_t length, UINT cp) +w32_truncate(const char *path, rb_off_t length, UINT cp) { HANDLE h; int ret; @@ -5992,21 +5992,21 @@ w32_truncate(const char *path, off_t length, UINT cp) /* License: Ruby's */ int -rb_w32_utruncate(const char *path, off_t length) +rb_w32_utruncate(const char *path, rb_off_t length) { return w32_truncate(path, length, CP_UTF8); } /* License: Ruby's */ int -rb_w32_truncate(const char *path, off_t length) +rb_w32_truncate(const char *path, rb_off_t length) { return w32_truncate(path, length, filecp()); } /* License: Ruby's */ int -rb_w32_ftruncate(int fd, off_t length) +rb_w32_ftruncate(int fd, rb_off_t length) { HANDLE h; @@ -8214,7 +8214,7 @@ VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notim #endif void * -rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset) { void *ptr; //DWORD protect = 0; From 7a849e1903f3943351773190a1e39e0445b55204 Mon Sep 17 00:00:00 2001 From: YO4 Date: Sat, 3 Sep 2022 00:53:09 +0900 Subject: [PATCH 016/269] win32.c fix: is_readable_console UnicodeChar with lower byte == 0 has dropped accidentaly this is additional fix for #5634 --- win32/win32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/win32.c b/win32/win32.c index 480b7a679712b7..6f1ee5120615fa 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -3102,7 +3102,7 @@ is_readable_console(SOCKET sock) /* call this for console only */ RUBY_CRITICAL { if (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n) && n > 0) { if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown && - ir.Event.KeyEvent.uChar.AsciiChar) { + ir.Event.KeyEvent.uChar.UnicodeChar) { ret = 1; } else { From 81e7573a64b0544da7172966b88e797033b1eae6 Mon Sep 17 00:00:00 2001 From: YO4 Date: Sat, 3 Sep 2022 01:03:10 +0900 Subject: [PATCH 017/269] win32.c additional fix: is_readable_console classic console(conhost.exe) reports an input with ALT+NUMPAD as VK_MENU, KeyUp, and uChar!=0. additional fix for #5634 --- win32/win32.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/win32/win32.c b/win32/win32.c index 6f1ee5120615fa..24990a00d37cd0 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -3105,6 +3105,11 @@ is_readable_console(SOCKET sock) /* call this for console only */ ir.Event.KeyEvent.uChar.UnicodeChar) { ret = 1; } + else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown && + ir.Event.KeyEvent.wVirtualKeyCode == 18 /* VK_MENU */ && + ir.Event.KeyEvent.uChar.UnicodeChar) { + ret = 1; + } else { ReadConsoleInputW((HANDLE)sock, &ir, 1, &n); } From 28030f7b543299b3f5af41a3c9cf445d23820d26 Mon Sep 17 00:00:00 2001 From: YO4 Date: Wed, 7 Sep 2022 22:45:43 +0900 Subject: [PATCH 018/269] eliminate magic number --- win32/win32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/win32.c b/win32/win32.c index 24990a00d37cd0..865edc8aa68f41 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -3106,7 +3106,7 @@ is_readable_console(SOCKET sock) /* call this for console only */ ret = 1; } else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown && - ir.Event.KeyEvent.wVirtualKeyCode == 18 /* VK_MENU */ && + ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU /* ALT key */ && ir.Event.KeyEvent.uChar.UnicodeChar) { ret = 1; } From 6d93644ba1c1eb662f9186019b69da44b71be32e Mon Sep 17 00:00:00 2001 From: git Date: Fri, 9 Sep 2022 00:46:21 +0900 Subject: [PATCH 019/269] * 2022-09-09 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index b47594cfcffcdd..c60ab92249698a 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 8 +#define RUBY_RELEASE_DAY 9 #include "ruby/version.h" #include "ruby/internal/abi.h" From e4f5296f065110fa83eb450d3a861253e76e534f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 31 Mar 2022 09:39:16 +0900 Subject: [PATCH 020/269] No longer bundle external library sources --- ext/fiddle/extlibs | 13 ------------- ext/psych/extlibs | 11 ----------- 2 files changed, 24 deletions(-) delete mode 100644 ext/fiddle/extlibs delete mode 100644 ext/psych/extlibs diff --git a/ext/fiddle/extlibs b/ext/fiddle/extlibs deleted file mode 100644 index 68dac46a9504b4..00000000000000 --- a/ext/fiddle/extlibs +++ /dev/null @@ -1,13 +0,0 @@ -ver = 3.2.1 -pkg = libffi-$(ver) - -https://ftp.osuosl.org/pub/blfs/conglomeration/libffi/$(pkg).tar.gz \ - md5:83b89587607e3eb65c70d361f13bab43 \ - sha512:980ca30a8d76f963fca722432b1fe5af77d7a4e4d2eac5144fbc5374d4c596609a293440573f4294207e1bdd9fda80ad1e1cafb2ffb543df5a275bc3bd546483 \ - # - win32/$(pkg)-mswin.patch -p0 - -$(pkg)/config.guess -> /tool/config.guess -$(pkg)/config.sub -> /tool/config.sub - -! chdir: $(pkg)| autoconf || exit 0 diff --git a/ext/psych/extlibs b/ext/psych/extlibs deleted file mode 100644 index 108aad42af9ae8..00000000000000 --- a/ext/psych/extlibs +++ /dev/null @@ -1,11 +0,0 @@ -ver = 0.2.5 -pkg = yaml-$(ver) - -https://github.com/yaml/libyaml/releases/download/$(ver)/$(pkg).tar.gz \ - rmd160:cc175ed640046722fb7790de828002633407b6b9 \ - sha256:c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4 \ - sha512:dadd7d8e0d88b5ebab005e5d521d56d541580198aa497370966b98c904586e642a1cd4f3881094eb57624f218d50db77417bbfd0ffdce50340f011e35e8c4c02 \ - # - -$(pkg)/config/config.guess -> /tool/config.guess -$(pkg)/config/config.sub -> /tool/config.sub From 35cfc9a3bb0078c297eb70520216ad228f2083e1 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 8 Sep 2022 17:09:50 -0400 Subject: [PATCH 021/269] Remove as many unnecessary moves as possible (#6342) This commit does a bunch of stuff to try to eliminate as many unnecessary mov instructions as possible. First, it introduces the Insn::LoadInto instruction. Previously when we needed a value to go into a specific register (like in Insn::CCall when we're putting values into the argument registers or in Insn::CRet when we're putting a value into the return register) we would first load the value and then mov it into the correct register. This resulted in a lot of duplicated work with short live ranges since they basically immediately we unnecessary. The new instruction accepts a destination and does not interact with the register allocator at all, making it much more efficient. We then use the new instruction when we're loading values into argument registers for AArch64 or X86_64, and when we're returning a value from AArch64. Notably we don't do it when we're returning a value from X86_64 because everything can be accomplished with a single mov anyway. A couple of unnecessary movs were also present because when we called the split_load_opnd function in a lot of split passes we were loading all registers and instruction outputs. We no longer do that. This commit also makes it so that UImm(0) passes through the Insn::Store split without attempting to be loaded, which allows it can take advantage of the zero register. So now instead of mov-ing 0 into a register and then calling store, it just stores XZR. --- yjit/src/backend/arm64/mod.rs | 74 ++++++++++++++++++++++------------ yjit/src/backend/ir.rs | 10 +++++ yjit/src/backend/x86_64/mod.rs | 31 ++++++++------ 3 files changed, 76 insertions(+), 39 deletions(-) diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 3e926c93870336..32db0ab3dcdafd 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -137,6 +137,7 @@ impl Assembler /// to be split in case their displacement doesn't fit into 9 bits. fn split_load_operand(asm: &mut Assembler, opnd: Opnd) -> Opnd { match opnd { + Opnd::Reg(_) | Opnd::InsnOut { .. } => opnd, Opnd::Mem(_) => { let split_opnd = split_memory_address(asm, opnd); asm.load(split_opnd) @@ -235,7 +236,7 @@ impl Assembler // such that only the Op::Load instruction needs to handle that // case. If the values aren't heap objects then we'll treat them as // if they were just unsigned integer. - let is_load = matches!(insn, Insn::Load { .. }); + let is_load = matches!(insn, Insn::Load { .. } | Insn::LoadInto { .. }); let mut opnd_iter = insn.opnd_iter_mut(); while let Some(opnd) = opnd_iter.next() { @@ -284,9 +285,8 @@ impl Assembler Insn::CCall { opnds, target, .. } => { assert!(opnds.len() <= C_ARG_OPNDS.len()); - // For each of the operands we're going to first load them - // into a register and then move them into the correct - // argument register. + // Load each operand into the corresponding argument + // register. // Note: the iteration order is reversed to avoid corrupting x0, // which is both the return value and first argument register for (idx, opnd) in opnds.into_iter().enumerate().rev() { @@ -295,10 +295,11 @@ impl Assembler // a UImm of 0 along as the argument to the move. let value = match opnd { Opnd::UImm(0) | Opnd::Imm(0) => Opnd::UImm(0), - _ => split_load_operand(asm, opnd) + Opnd::Mem(_) => split_memory_address(asm, opnd), + _ => opnd }; - asm.mov(C_ARG_OPNDS[idx], value); + asm.load_into(C_ARG_OPNDS[idx], value); } // Now we push the CCall without any arguments so that it @@ -306,18 +307,29 @@ impl Assembler asm.ccall(target.unwrap_fun_ptr(), vec![]); }, Insn::Cmp { left, right } => { - let opnd0 = match left { - Opnd::Reg(_) | Opnd::InsnOut { .. } => left, - _ => split_load_operand(asm, left) - }; - + let opnd0 = split_load_operand(asm, left); let opnd1 = split_shifted_immediate(asm, right); asm.cmp(opnd0, opnd1); }, Insn::CRet(opnd) => { - if opnd != Opnd::Reg(C_RET_REG) { - let value = split_load_operand(asm, opnd); - asm.mov(C_RET_OPND, value); + match opnd { + // If the value is already in the return register, then + // we don't need to do anything. + Opnd::Reg(C_RET_REG) => {}, + + // If the value is a memory address, we need to first + // make sure the displacement isn't too large and then + // load it into the return register. + Opnd::Mem(_) => { + let split = split_memory_address(asm, opnd); + asm.load_into(C_RET_OPND, split); + }, + + // Otherwise we just need to load the value into the + // return register. + _ => { + asm.load_into(C_RET_OPND, opnd); + } } asm.cret(C_RET_OPND); }, @@ -375,7 +387,20 @@ impl Assembler } }, Insn::Load { opnd, .. } => { - split_load_operand(asm, opnd); + let value = match opnd { + Opnd::Mem(_) => split_memory_address(asm, opnd), + _ => opnd + }; + + asm.load(value); + }, + Insn::LoadInto { dest, opnd } => { + let value = match opnd { + Opnd::Mem(_) => split_memory_address(asm, opnd), + _ => opnd + }; + + asm.load_into(dest, value); }, Insn::LoadSExt { opnd, .. } => { match opnd { @@ -442,28 +467,24 @@ impl Assembler // The value being stored must be in a register, so if it's // not already one we'll load it first. let opnd1 = match src { - Opnd::Reg(_) | Opnd::InsnOut { .. } => src, + // If the first operand is zero, then we can just use + // the zero register. + Opnd::UImm(0) | Opnd::Imm(0) => Opnd::Reg(XZR_REG), + // Otherwise we'll check if we need to load it first. _ => split_load_operand(asm, src) }; asm.store(opnd0, opnd1); }, Insn::Sub { left, right, .. } => { - let opnd0 = match left { - Opnd::Reg(_) | Opnd::InsnOut { .. } => left, - _ => split_load_operand(asm, left) - }; - + let opnd0 = split_load_operand(asm, left); let opnd1 = split_shifted_immediate(asm, right); asm.sub(opnd0, opnd1); }, Insn::Test { left, right } => { // The value being tested must be in a register, so if it's // not already one we'll load it first. - let opnd0 = match left { - Opnd::Reg(_) | Opnd::InsnOut { .. } => left, - _ => split_load_operand(asm, left) - }; + let opnd0 = split_load_operand(asm, left); // The second value must be either a register or an // unsigned immediate that can be encoded as a bitmask @@ -710,7 +731,8 @@ impl Assembler // our IR we have the address first and the register second. stur(cb, src.into(), dest.into()); }, - Insn::Load { opnd, out } => { + Insn::Load { opnd, out } | + Insn::LoadInto { opnd, dest: out } => { match *opnd { Opnd::Reg(_) | Opnd::InsnOut { .. } => { mov(cb, out.into(), opnd.into()); diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 0b96af7f62cbdc..ee6499ff64b069 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -401,6 +401,9 @@ pub enum Insn { // A low-level instruction that loads a value into a register. Load { opnd: Opnd, out: Opnd }, + // A low-level instruction that loads a value into a specified register. + LoadInto { dest: Opnd, opnd: Opnd }, + // A low-level instruction that loads a value into a register and // sign-extends it to a 64-bit value. LoadSExt { opnd: Opnd, out: Opnd }, @@ -502,6 +505,7 @@ impl Insn { Insn::Lea { .. } => "Lea", Insn::LiveReg { .. } => "LiveReg", Insn::Load { .. } => "Load", + Insn::LoadInto { .. } => "LoadInto", Insn::LoadSExt { .. } => "LoadSExt", Insn::LShift { .. } => "LShift", Insn::Mov { .. } => "Mov", @@ -675,6 +679,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> { Insn::CSelNZ { truthy: opnd0, falsy: opnd1, .. } | Insn::CSelZ { truthy: opnd0, falsy: opnd1, .. } | Insn::IncrCounter { mem: opnd0, value: opnd1, .. } | + Insn::LoadInto { dest: opnd0, opnd: opnd1 } | Insn::LShift { opnd: opnd0, shift: opnd1, .. } | Insn::Mov { dest: opnd0, src: opnd1 } | Insn::Or { left: opnd0, right: opnd1, .. } | @@ -771,6 +776,7 @@ impl<'a> InsnOpndMutIterator<'a> { Insn::CSelNZ { truthy: opnd0, falsy: opnd1, .. } | Insn::CSelZ { truthy: opnd0, falsy: opnd1, .. } | Insn::IncrCounter { mem: opnd0, value: opnd1, .. } | + Insn::LoadInto { dest: opnd0, opnd: opnd1 } | Insn::LShift { opnd: opnd0, shift: opnd1, .. } | Insn::Mov { dest: opnd0, src: opnd1 } | Insn::Or { left: opnd0, right: opnd1, .. } | @@ -1422,6 +1428,10 @@ impl Assembler { out } + pub fn load_into(&mut self, dest: Opnd, opnd: Opnd) { + self.push_insn(Insn::LoadInto { dest, opnd }); + } + #[must_use] pub fn load_sext(&mut self, opnd: Opnd) -> Opnd { let out = self.next_opnd_out(Opnd::match_num_bits(&[opnd])); diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index 4e230822f1661a..ca07d50ffc4a5e 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -131,7 +131,7 @@ impl Assembler // VALUEs alive. This is a sort of canonicalization. let mut unmapped_opnds: Vec = vec![]; - let is_load = matches!(insn, Insn::Load { .. }); + let is_load = matches!(insn, Insn::Load { .. } | Insn::LoadInto { .. }); let mut opnd_iter = insn.opnd_iter_mut(); while let Some(opnd) = opnd_iter.next() { @@ -289,6 +289,19 @@ impl Assembler asm.not(opnd0); }, + Insn::CCall { opnds, target, .. } => { + assert!(opnds.len() <= C_ARG_OPNDS.len()); + + // Load each operand into the corresponding argument + // register. + for (idx, opnd) in opnds.into_iter().enumerate() { + asm.load_into(C_ARG_OPNDS[idx], *opnd); + } + + // Now we push the CCall without any arguments so that it + // just performs the call. + asm.ccall(target.unwrap_fun_ptr(), vec![]); + }, _ => { if insn.out_opnd().is_some() { let out_num_bits = Opnd::match_num_bits_iter(insn.opnd_iter()); @@ -421,7 +434,8 @@ impl Assembler }, // This assumes only load instructions can contain references to GC'd Value operands - Insn::Load { opnd, out } => { + Insn::Load { opnd, out } | + Insn::LoadInto { dest: out, opnd } => { mov(cb, out.into(), opnd.into()); // If the value being loaded is a heap object @@ -490,17 +504,8 @@ impl Assembler }, // C function call - Insn::CCall { opnds, target, .. } => { - // Temporary - assert!(opnds.len() <= _C_ARG_OPNDS.len()); - - // For each operand - for (idx, opnd) in opnds.iter().enumerate() { - mov(cb, X86Opnd::Reg(_C_ARG_OPNDS[idx].unwrap_reg()), opnds[idx].into()); - } - - let ptr = target.unwrap_fun_ptr(); - call_ptr(cb, RAX, ptr); + Insn::CCall { target, .. } => { + call_ptr(cb, RAX, target.unwrap_fun_ptr()); }, Insn::CRet(opnd) => { From 59eadf3d25b0f53e7570f98f1801737af642f328 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 9 Sep 2022 11:35:47 +0900 Subject: [PATCH 022/269] Ignore EACCES on Windows --- tool/ln_sr.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/ln_sr.rb b/tool/ln_sr.rb index 6ab412edde9533..81fd803355874d 100755 --- a/tool/ln_sr.rb +++ b/tool/ln_sr.rb @@ -116,7 +116,7 @@ def fu_starting_path?(path) if File.respond_to?(:symlink) begin ln_sr(src, dest, verbose: true, target_directory: target_directory, force: force, noop: noop) - rescue NotImplementedError, Errno::EPERM + rescue NotImplementedError, Errno::EPERM, Errno::EACCES else exit end From db0e0dad1171456253ebd899e7e878823923d3d8 Mon Sep 17 00:00:00 2001 From: Kazuki Tsujimoto Date: Fri, 9 Sep 2022 14:00:27 +0900 Subject: [PATCH 023/269] Fix unexpected "duplicated key name" error in paren-less one line pattern matching [Bug #18990] --- parse.y | 16 ++++++++++++---- test/ruby/test_pattern_matching.rb | 12 ++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/parse.y b/parse.y index e6fee29595e26e..38f7690ccc01d7 100644 --- a/parse.y +++ b/parse.y @@ -1774,14 +1774,18 @@ expr : command_call p->ctxt.in_kwarg = 1; $$ = push_pvtbl(p); } + { + $$ = push_pktbl(p); + } p_top_expr_body { + pop_pktbl(p, $4); pop_pvtbl(p, $3); p->ctxt.in_kwarg = $2.in_kwarg; /*%%%*/ - $$ = NEW_CASE3($1, NEW_IN($4, 0, 0, &@4), &@$); + $$ = NEW_CASE3($1, NEW_IN($5, 0, 0, &@5), &@$); /*% %*/ - /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/ + /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/ } | arg keyword_in { @@ -1792,14 +1796,18 @@ expr : command_call p->ctxt.in_kwarg = 1; $$ = push_pvtbl(p); } + { + $$ = push_pktbl(p); + } p_top_expr_body { + pop_pktbl(p, $4); pop_pvtbl(p, $3); p->ctxt.in_kwarg = $2.in_kwarg; /*%%%*/ - $$ = NEW_CASE3($1, NEW_IN($4, NEW_TRUE(&@4), NEW_FALSE(&@4), &@4), &@$); + $$ = NEW_CASE3($1, NEW_IN($5, NEW_TRUE(&@5), NEW_FALSE(&@5), &@5), &@$); /*% %*/ - /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/ + /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/ } | arg %prec tLBRACE_ARG ; diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index 36731e14f9609d..fbb934dc84a290 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -1570,6 +1570,18 @@ def test_one_line assert_equal false, (1 in 2) end + def test_bug18990 + {a: 0} => a: + assert_equal 0, a + {a: 0} => a: + assert_equal 0, a + + {a: 0} in a: + assert_equal 0, a + {a: 0} in a: + assert_equal 0, a + end + ################################################################ def test_single_pattern_error_value_pattern From 2dcaeb75ff0e0a2fbafc4df0df9d7be119235415 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 9 Sep 2022 11:51:11 +0900 Subject: [PATCH 024/269] Built-in function table sentinels do not need names --- builtin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin.h b/builtin.h index b827b289281674..7ad52f44f47b7b 100644 --- a/builtin.h +++ b/builtin.h @@ -17,7 +17,7 @@ struct rb_builtin_function { }; #define RB_BUILTIN_FUNCTION(_i, _name, _fname, _arity, _compiler) {\ - .name = #_name, \ + .name = _i < 0 ? NULL : #_name, \ .func_ptr = (void *)_fname, \ .argc = _arity, \ .index = _i, \ From be560332484c8893bec004f1250e48edcd4720e0 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 9 Sep 2022 11:52:57 +0900 Subject: [PATCH 025/269] Move RDoc `--`/`++` directives to comments --- template/prelude.c.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl index 3d792867f3bcf2..ac748704ed2a2e 100644 --- a/template/prelude.c.tmpl +++ b/template/prelude.c.tmpl @@ -48,7 +48,7 @@ class Prelude line.rstrip! lineno += 1 @preludes[filename] ||= result - comment = ($1 || '' if line.sub!(/(?:^|\s+)\#(?:$|[#\s](.*))/, '')) + comment = ($1 || '' if line.sub!(/(?:^|\s+)\#(?!\{)(?:$|(?:[#\s]|(?=\W))(.*))/, '')) if !line.empty? or start_line line << "\n" start_line ||= lineno From 56e5210cdeda685b5948148b7ea5c72eac0b5e06 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 8 Sep 2022 23:19:23 -0700 Subject: [PATCH 026/269] More robust macro parser (#6343) I want to use more complicated macros with MJIT. For example: ``` # define SHAPE_MASK (((unsigned int)1 << SHAPE_BITS) - 1) ``` This commit adds a simple recursive descent parser that produces an AST and a small visitor that converts the AST to Ruby. --- lib/mjit/c_32.rb | 6 +- lib/mjit/c_64.rb | 6 +- tool/mjit/bindgen.rb | 218 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 214 insertions(+), 16 deletions(-) diff --git a/lib/mjit/c_32.rb b/lib/mjit/c_32.rb index c628e60967ca32..a369a3e852e5dd 100644 --- a/lib/mjit/c_32.rb +++ b/lib/mjit/c_32.rb @@ -3,15 +3,15 @@ module RubyVM::MJIT C = Object.new - def C.NOT_COMPILED_STACK_SIZE = - 1 + def C.NOT_COMPILED_STACK_SIZE = -1 def C.USE_LAZY_LOAD = false def C.USE_RVARGC = true - def C.VM_CALL_KW_SPLAT = ( 0x01 << self.VM_CALL_KW_SPLAT_bit ) + def C.VM_CALL_KW_SPLAT = (0x01 << self.VM_CALL_KW_SPLAT_bit) - def C.VM_CALL_TAILCALL = ( 0x01 << self.VM_CALL_TAILCALL_bit ) + def C.VM_CALL_TAILCALL = (0x01 << self.VM_CALL_TAILCALL_bit) def C.VM_METHOD_TYPE_CFUNC = 1 diff --git a/lib/mjit/c_64.rb b/lib/mjit/c_64.rb index 864cb32b64e600..e3989a12e23d46 100644 --- a/lib/mjit/c_64.rb +++ b/lib/mjit/c_64.rb @@ -3,15 +3,15 @@ module RubyVM::MJIT C = Object.new - def C.NOT_COMPILED_STACK_SIZE = - 1 + def C.NOT_COMPILED_STACK_SIZE = -1 def C.USE_LAZY_LOAD = false def C.USE_RVARGC = true - def C.VM_CALL_KW_SPLAT = ( 0x01 << self.VM_CALL_KW_SPLAT_bit ) + def C.VM_CALL_KW_SPLAT = (0x01 << self.VM_CALL_KW_SPLAT_bit) - def C.VM_CALL_TAILCALL = ( 0x01 << self.VM_CALL_TAILCALL_bit ) + def C.VM_CALL_TAILCALL = (0x01 << self.VM_CALL_TAILCALL_bit) def C.VM_METHOD_TYPE_CFUNC = 1 diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 9fded4a98548a6..8bee57de7d4cf7 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -39,6 +39,212 @@ class Node < Struct.new( ) end +class CParser + def initialize(tokens) + @tokens = lex(tokens) + @pos = 0 + end + + def parse + expression + end + + private + + def lex(toks) + toks.map do |tok| + case tok + when /\A\d+\z/ then [:NUMBER, tok] + when /\A0x[0-9a-f]*\z/ then [:NUMBER, tok] + when '(' then [:LEFT_PAREN, tok] + when ')' then [:RIGHT_PAREN, tok] + when 'unsigned', 'int' then [:TYPE, tok] + when '<<' then [:LSHIFT, tok] + when '>>' then [:RSHIFT, tok] + when '-' then [:MINUS, tok] + when '+' then [:PLUS, tok] + when /\A\w+\z/ then [:IDENT, tok] + else + raise "Unknown token: #{tok}" + end + end + end + + def expression + equality + end + + def equality + exp = comparison + + while match(:BANG_EQUAL, :EQUAL_EQUAL) + operator = previous + right = comparison + exp = [:BINARY, operator, exp, right] + end + + exp + end + + def comparison + expr = term + + while match(:GREATER, :GREATER_EQUAL, :LESS, :LESS_EQUAL) + operator = previous + right = comparison + expr = [:BINARY, operator, expr, right] + end + + expr + end + + def term + expr = bitwise + + while match(:MINUS, :PLUS) + operator = previous + right = bitwise + expr = [:BINARY, operator, expr, right] + end + + expr + end + + def bitwise + expr = unary + + while match(:RSHIFT, :LSHIFT) + operator = previous + right = unary + expr = [:BINARY, operator, expr, right] + end + + expr + end + + def unary + if match(:BANG, :MINUS) + [:UNARY, previous, primary] + else + primary + end + end + + def primary + if match(:LEFT_PAREN) + grouping + else + if match(:IDENT) + [:VAR, previous] + elsif match(:NUMBER) + previous + else + raise peek.inspect + end + end + end + + def grouping + if peek.first == :TYPE + cast = types + consume(:RIGHT_PAREN) + exp = [:TYPECAST, cast, unary] + else + exp = [:GROUP, expression] + consume(:RIGHT_PAREN) + end + exp + end + + def consume(tok) + unless peek.first == tok + raise "Expected #{tok} but was #{peek}" + end + advance + end + + def types + list = [] + loop do + thing = peek + break unless thing.first == :TYPE + list << thing + advance + end + list + end + + def match(*toks) + advance if peek && toks.grep(peek.first).any? + end + + def advance + @pos += 1 + raise("nope") if @pos > @tokens.length + true + end + + def peek + @tokens[@pos] + end + + def previous + @tokens[@pos - 1] + end +end + +class ToRuby + def initialize(enums) + @enums = enums + end + + def visit(node) + send node.first, node + end + + private + + def GROUP(node) + "(" + visit(node[1]) + ")" + end + + def BINARY(node) + visit(node[2]) + " " + visit(node[1]) + " " + visit(node[3]) + end + + def TYPECAST(node) + visit node[2] + end + + def NUMBER(node) + node[1].to_s + end + + def UNARY(node) + visit(node[1]) + visit(node[2]) + end + + def lit(node) + node.last + end + + alias MINUS lit + alias RSHIFT lit + alias LSHIFT lit + + def IDENT(node) + if @enums.include?(node.last) + "self.#{node.last}" + else + "unexpected macro token: #{node.last}" + end + end + + def VAR(node) + visit node[1] + end +end + # Parse a C header with ffi-clang and return Node objects. # To ease the maintenance, ffi-clang should be used only inside this class. class HeaderParser @@ -197,16 +403,8 @@ def generate_macro(node) if tokens.first != node.spelling raise "unexpected first token: '#{tokens.first}' != '#{node.spelling}'" end - tokens.drop(1).map do |token| - case token - when /\A(0x)?\d+\z/, '(', '-', '<<', ')' - token - when *@enums.values.flatten - "self.#{token}" - else - raise "unexpected macro token: #{token}" - end - end.join(' ') + ast = CParser.new(tokens.drop(1)).parse + ToRuby.new(@enums.values.flatten).visit(ast) end end end From 9faa9ced9640d23fc5dc1efd635f6b8ebc1a3ceb Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 9 Sep 2022 14:13:42 +0900 Subject: [PATCH 027/269] Support sub-library in builtin-loader Previously, it was supported in prelude.c, but has not followed up the builtin-loader system. --- template/prelude.c.tmpl | 70 +++------------------------------------ tool/mk_builtin_loader.rb | 36 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 65 deletions(-) diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl index ac748704ed2a2e..58453636bf15ce 100644 --- a/template/prelude.c.tmpl +++ b/template/prelude.c.tmpl @@ -20,7 +20,6 @@ class Prelude def initialize(output, preludes, vpath) @output = output - @have_sublib = false @vpath = vpath @prelude_count = 0 @builtin_count = 0 @@ -64,8 +63,10 @@ class Prelude end path = translate("#{path}.rb", true) rescue nil if path - @have_sublib = true - "TMP_RUBY_PREFIX.require(#{path[0]})" + # This library will be loaded before this, + # the order cannot be preserved + comment = "#{orig} #{comment}".rstrip + "" else orig end @@ -131,24 +132,6 @@ static const struct { COMPILER_WARNING_POP -% if @have_sublib -#define PRELUDE_COUNT <%=preludes.size%> - -struct prelude_env { - volatile VALUE prefix_path; -#if PRELUDE_COUNT > 0 - char loaded[PRELUDE_COUNT]; -#endif -}; - -static VALUE -prelude_prefix_path(VALUE self) -{ - struct prelude_env *ptr = DATA_PTR(self); - return ptr->prefix_path; -} - -% end % unless preludes.empty? #define PRELUDE_NAME(n) rb_usascii_str_new_static(prelude_name##n, sizeof(prelude_name##n)-1) #define PRELUDE_CODE(n) rb_utf8_str_new_static(prelude_code##n.L0, sizeof(prelude_code##n)) @@ -179,7 +162,7 @@ rb_builtin_ast(const char *feature_name, VALUE *name_str) rb_ast_t *ast = 0; % @preludes.each_value do |i, prelude, lines, sub, start_line| -% if sub and sub != true +% if sub if ((ast = PRELUDE_AST(<%=i%><%=%>, *name_str, <%=start_line%>)) != 0) return ast; % end % end @@ -216,36 +199,6 @@ prelude_eval(VALUE code, VALUE name, int line) } COMPILER_WARNING_POP -% end -% if @have_sublib -static VALUE -prelude_require(VALUE self, VALUE nth) -{ - struct prelude_env *ptr = DATA_PTR(self); - VALUE code, name; - int n = FIX2INT(nth); - int start_line; - - if (n > PRELUDE_COUNT) return Qfalse; - if (ptr->loaded[n]) return Qfalse; - ptr->loaded[n] = 1; - switch (n) { -% @preludes.each_value do |i, prelude, lines, sub, start_line| -% if sub == true - case <%=i%><%=%>: - code = PRELUDE_CODE(<%=i%><%=%>); - name = PRELUDE_NAME(<%=i%><%=%>); - start_line = <%=start_line%>; - break; -% end -% end - default: - return Qfalse; - } - prelude_eval(code, name, start_line); - return Qtrue; -} - % end %end % init_name = @output && @output[/\w+(?=_prelude.c\b)/] || 'prelude' @@ -253,19 +206,6 @@ void Init_<%=init_name%><%=%>(void) { %unless @prelude_count.zero? -% if @have_sublib - struct prelude_env memo; - ID name = rb_intern("TMP_RUBY_PREFIX"); - VALUE prelude = Data_Wrap_Struct(rb_cObject, 0, 0, &memo); - - memo.prefix_path = rb_const_remove(rb_cObject, name); - rb_const_set(rb_cObject, name, prelude); - rb_define_singleton_method(prelude, "to_s", prelude_prefix_path, 0); -% end -% if @have_sublib - memset(memo.loaded, 0, sizeof(memo.loaded)); - rb_define_singleton_method(prelude, "require", prelude_require, 1); -% end % preludes.each do |i, prelude, lines, sub, start_line| % next if sub prelude_eval(PRELUDE_CODE(<%=i%><%=%>), PRELUDE_NAME(<%=i%><%=%>), <%=start_line%>); diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 23e6a01017b572..784dd30dc14efa 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -4,6 +4,9 @@ require 'stringio' require_relative 'ruby_vm/helpers/c_escape' +SUBLIBS = {} +REQUIRED = {} + def string_literal(lit, str = []) while lit case lit.first @@ -174,6 +177,21 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil end bs[func_name] = [argc, cfunc_name] if func_name + elsif /\Arequire(?:_relative)\z/ =~ mid and args.size == 1 and + (arg1 = args[0])[0] == :string_literal and + (arg1 = arg1[1])[0] == :string_content and + (arg1 = arg1[1])[0] == :@tstring_content and + sublib = arg1[1] + if File.exist?(f = File.join(@dir, sublib)+".rb") + puts "- #{@base}.rb requires #{sublib}" + if REQUIRED[sublib] + warn "!!! #{sublib} is required from #{REQUIRED[sublib]} already; ignored" + else + REQUIRED[sublib] = @base + (SUBLIBS[@base] ||= []) << sublib + end + ARGV.push(f) + end end break unless tree = args end @@ -242,7 +260,9 @@ def generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_nam end def mk_builtin_header file + @dir = File.dirname(file) base = File.basename(file, '.rb') + @base = base ofile = "#{file}inc" # bs = { func_name => argc } @@ -331,6 +351,14 @@ def mk_builtin_header file f.puts } + if SUBLIBS[base] + f.puts "// sub libraries" + SUBLIBS[base].each do |sub| + f.puts %[#include #{(sub+".rbinc").dump}] + end + f.puts + end + f.puts "void Init_builtin_#{base}(void)" f.puts "{" @@ -354,6 +382,14 @@ def mk_builtin_header file } f.puts "COMPILER_WARNING_POP" + if SUBLIBS[base] + f.puts + f.puts " // sub libraries" + SUBLIBS[base].each do |sub| + f.puts " Init_builtin_#{sub}();" + end + end + f.puts f.puts " // load" f.puts " rb_load_with_builtin_functions(#{base.dump}, #{table});" From aff6534e32b5e46036808ed0579d31a8dbad8190 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 8 Sep 2022 22:12:02 +0900 Subject: [PATCH 028/269] Avoid unnecessary copying when removing the leading part of a string Remove the superfluous str_modify_keep_cr() call from rb_str_update(). It ends up calling either rb_str_drop_bytes() or rb_str_splice_0(), which already does checks if necessary. The extra call makes the string "independent". This is not always wanted, in other words, it can keep the same shared root when merely removing the leading part of a shared string. --- string.c | 1 - 1 file changed, 1 deletion(-) diff --git a/string.c b/string.c index 963652cdb967c8..c9ec827708f3cf 100644 --- a/string.c +++ b/string.c @@ -5359,7 +5359,6 @@ rb_str_update(VALUE str, long beg, long len, VALUE val) if (len > slen - beg) { len = slen - beg; } - str_modify_keep_cr(str); p = str_nth(RSTRING_PTR(str), RSTRING_END(str), beg, enc, singlebyte); if (!p) p = RSTRING_END(str); e = str_nth(p, RSTRING_END(str), len, enc, singlebyte); From cfe10e482e0657195cadf01f0be72e11b7fb62b4 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 9 Sep 2022 18:58:07 +0900 Subject: [PATCH 029/269] [DOC] Update `Struct#new` behavior with `keyword_init: true` --- struct.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/struct.c b/struct.c index 7085042b43c2b3..1e7294eb5ef42b 100644 --- a/struct.c +++ b/struct.c @@ -471,8 +471,8 @@ rb_struct_define_under(VALUE outer, const char *name, ...) * * - May be anonymous, or may have the name given by +class_name+. * - May have members as given by +member_names+. - * - May have initialization via ordinary arguments (the default) - * or via keyword arguments (if keyword_init: true is given). + * - May have initialization via ordinary arguments (unless + * keyword_init: true is given), or via keyword arguments * * The new subclass has its own method ::new; thus: * @@ -557,7 +557,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...) * By default, the arguments for initializing an instance of the new subclass * are ordinary arguments (not keyword arguments). * With optional keyword argument keyword_init: true, - * the new subclass is initialized with keyword arguments: + * the new subclass must be initialized with keyword arguments: * * # Without keyword_init: true. * Foo = Struct.new('Foo', :foo, :bar) @@ -567,6 +567,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...) * Bar = Struct.new(:foo, :bar, keyword_init: true) * Bar # => # => Bar(keyword_init: true) * Bar.new(bar: 1, foo: 0) # => # + * Bar.new(0, 1) # Raises ArgumentError: wrong number of arguments * */ From b350053ae47dc4653ecb85f64360cf6eafba6323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 7 Sep 2022 23:01:03 +0200 Subject: [PATCH 030/269] [rubygems/rubygems] Fix resolution on non-musl platforms Gems without specific platform were being preferred over matching platform specific gems. https://github.com/rubygems/rubygems/commit/37b95b9159 --- lib/rubygems/resolver.rb | 2 +- test/rubygems/test_gem_resolver.rb | 33 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb index bf7d6d943b8fb7..76d1e9d0cc7426 100644 --- a/lib/rubygems/resolver.rb +++ b/lib/rubygems/resolver.rb @@ -246,7 +246,7 @@ def search_for(dependency) sources.each do |source| groups[source]. - sort_by {|spec| [spec.version, Gem::Platform.local =~ spec.platform ? 1 : 0] }. + sort_by {|spec| [spec.version, spec.platform =~ Gem::Platform.local ? 1 : 0] }. map {|spec| ActivationRequest.new spec, dependency }. each {|activation_request| activation_requests << activation_request } end diff --git a/test/rubygems/test_gem_resolver.rb b/test/rubygems/test_gem_resolver.rb index c816d5484ba7ba..54497c6e4f95d8 100644 --- a/test/rubygems/test_gem_resolver.rb +++ b/test/rubygems/test_gem_resolver.rb @@ -391,6 +391,39 @@ def test_does_not_pick_musl_variants_on_non_musl_linux end end + def test_pick_generic_linux_variants_on_musl_linux + util_set_arch "aarch64-linux-musl" do + is = Gem::Resolver::IndexSpecification + + linux = Gem::Platform.new("aarch64-linux") + + spec_fetcher do |fetcher| + fetcher.spec "libv8-node", "15.14.0.1" do |s| + s.platform = linux + end + + fetcher.spec "libv8-node", "15.14.0.1" + end + + v15 = v("15.14.0.1") + source = Gem::Source.new @gem_repo + + s = set + + v15_ruby = is.new s, "libv8-node", v15, source, Gem::Platform::RUBY + v15_linux = is.new s, "libv8-node", v15, source, linux.to_s + + s.add v15_linux + s.add v15_ruby + + ad = make_dep "libv8-node", "= 15.14.0.1" + + res = Gem::Resolver.new([ad], s) + + assert_resolves_to [v15_linux.spec], res + end + end + def test_only_returns_spec_once a1 = util_spec "a", "1", "c" => "= 1" b1 = util_spec "b", "1", "c" => "= 1" From 848037cadd63091d5c39d06cd5d49aeee2258b4e Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 9 Sep 2022 11:37:41 -0400 Subject: [PATCH 031/269] Better offsets (#6315) * Introduce InstructionOffset for AArch64 There are a lot of instructions on AArch64 where we take an offset from PC in terms of the number of instructions. This is for loading a value relative to the PC or for jumping. We were usually accepting an A64Opnd or an i32. It can get confusing and inconsistent though because sometimes you would divide by 4 to get the number of instructions or multiply by 4 to get the number of bytes. This commit adds a struct that wraps an i32 in order to keep all of that logic in one place. It makes it much easier to read and reason about how these offsets are getting used. * Use b instruction when the offset fits on AArch64 --- yjit/src/asm/arm64/arg/inst_offset.rs | 47 ++++++++++++++++++ yjit/src/asm/arm64/arg/mod.rs | 2 + yjit/src/asm/arm64/inst/branch_cond.rs | 18 +++---- yjit/src/asm/arm64/inst/call.rs | 28 +++++------ yjit/src/asm/arm64/inst/load_literal.rs | 14 +++--- yjit/src/asm/arm64/mod.rs | 63 ++++++++++--------------- yjit/src/backend/arm64/mod.rs | 52 ++++++++++---------- 7 files changed, 133 insertions(+), 91 deletions(-) create mode 100644 yjit/src/asm/arm64/arg/inst_offset.rs diff --git a/yjit/src/asm/arm64/arg/inst_offset.rs b/yjit/src/asm/arm64/arg/inst_offset.rs new file mode 100644 index 00000000000000..f4a6bc73a06711 --- /dev/null +++ b/yjit/src/asm/arm64/arg/inst_offset.rs @@ -0,0 +1,47 @@ +/// There are a lot of instructions in the AArch64 architectrue that take an +/// offset in terms of number of instructions. Usually they are jump +/// instructions or instructions that load a value relative to the current PC. +/// +/// This struct is used to mark those locations instead of a generic operand in +/// order to give better clarity to the developer when reading the AArch64 +/// backend code. It also helps to clarify that everything is in terms of a +/// number of instructions and not a number of bytes (i.e., the offset is the +/// number of bytes divided by 4). +#[derive(Copy, Clone)] +pub struct InstructionOffset(i32); + +impl InstructionOffset { + /// Create a new instruction offset. + pub fn from_insns(insns: i32) -> Self { + InstructionOffset(insns) + } + + /// Create a new instruction offset from a number of bytes. + pub fn from_bytes(bytes: i32) -> Self { + assert_eq!(bytes % 4, 0, "Byte offset must be a multiple of 4"); + InstructionOffset(bytes / 4) + } +} + +impl From for InstructionOffset { + /// Convert an i64 into an instruction offset. + fn from(value: i32) -> Self { + InstructionOffset(value) + } +} + +impl From for i32 { + /// Convert an instruction offset into a number of instructions as an i32. + fn from(offset: InstructionOffset) -> Self { + offset.0 + } +} + +impl From for i64 { + /// Convert an instruction offset into a number of instructions as an i64. + /// This is useful for when we're checking how many bits this offset fits + /// into. + fn from(offset: InstructionOffset) -> Self { + offset.0.into() + } +} diff --git a/yjit/src/asm/arm64/arg/mod.rs b/yjit/src/asm/arm64/arg/mod.rs index 9bf4a8ea1322b3..7eb37834f9aada 100644 --- a/yjit/src/asm/arm64/arg/mod.rs +++ b/yjit/src/asm/arm64/arg/mod.rs @@ -3,6 +3,7 @@ mod bitmask_imm; mod condition; +mod inst_offset; mod sf; mod shifted_imm; mod sys_reg; @@ -10,6 +11,7 @@ mod truncate; pub use bitmask_imm::BitmaskImmediate; pub use condition::Condition; +pub use inst_offset::InstructionOffset; pub use sf::Sf; pub use shifted_imm::ShiftedImmediate; pub use sys_reg::SystemRegister; diff --git a/yjit/src/asm/arm64/inst/branch_cond.rs b/yjit/src/asm/arm64/inst/branch_cond.rs index c489bacef05ac0..4338cf0f4ffc2a 100644 --- a/yjit/src/asm/arm64/inst/branch_cond.rs +++ b/yjit/src/asm/arm64/inst/branch_cond.rs @@ -1,4 +1,4 @@ -use super::super::arg::{Condition, truncate_imm}; +use super::super::arg::{Condition, InstructionOffset, truncate_imm}; /// The struct that represents an A64 conditional branch instruction that can be /// encoded. @@ -14,14 +14,14 @@ pub struct BranchCond { cond: u8, /// The instruction offset from this instruction to branch to. - imm19: i32 + offset: InstructionOffset } impl BranchCond { /// B.cond /// https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/B-cond--Branch-conditionally- - pub fn bcond(cond: u8, imm19: i32) -> Self { - Self { cond, imm19 } + pub fn bcond(cond: u8, offset: InstructionOffset) -> Self { + Self { cond, offset } } } @@ -34,7 +34,7 @@ impl From for u32 { 0 | (1 << 30) | (FAMILY << 26) - | (truncate_imm::<_, 19>(inst.imm19) << 5) + | (truncate_imm::<_, 19>(inst.offset) << 5) | (inst.cond as u32) } } @@ -53,25 +53,25 @@ mod tests { #[test] fn test_b_eq() { - let result: u32 = BranchCond::bcond(Condition::EQ, 32).into(); + let result: u32 = BranchCond::bcond(Condition::EQ, 32.into()).into(); assert_eq!(0x54000400, result); } #[test] fn test_b_vs() { - let result: u32 = BranchCond::bcond(Condition::VS, 32).into(); + let result: u32 = BranchCond::bcond(Condition::VS, 32.into()).into(); assert_eq!(0x54000406, result); } #[test] fn test_b_eq_max() { - let result: u32 = BranchCond::bcond(Condition::EQ, (1 << 18) - 1).into(); + let result: u32 = BranchCond::bcond(Condition::EQ, ((1 << 18) - 1).into()).into(); assert_eq!(0x547fffe0, result); } #[test] fn test_b_eq_min() { - let result: u32 = BranchCond::bcond(Condition::EQ, -(1 << 18)).into(); + let result: u32 = BranchCond::bcond(Condition::EQ, (-(1 << 18)).into()).into(); assert_eq!(0x54800000, result); } } diff --git a/yjit/src/asm/arm64/inst/call.rs b/yjit/src/asm/arm64/inst/call.rs index 32d924f799186f..74debac7f75bc8 100644 --- a/yjit/src/asm/arm64/inst/call.rs +++ b/yjit/src/asm/arm64/inst/call.rs @@ -1,4 +1,4 @@ -use super::super::arg::truncate_imm; +use super::super::arg::{InstructionOffset, truncate_imm}; /// The operation to perform for this instruction. enum Op { @@ -20,8 +20,8 @@ enum Op { /// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ /// pub struct Call { - /// The PC-relative offset to jump to (which will be multiplied by 4). - imm26: i32, + /// The PC-relative offset to jump to in terms of number of instructions. + offset: InstructionOffset, /// The operation to perform for this instruction. op: Op @@ -30,14 +30,14 @@ pub struct Call { impl Call { /// B /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/B--Branch- - pub fn b(imm26: i32) -> Self { - Self { imm26, op: Op::Branch } + pub fn b(offset: InstructionOffset) -> Self { + Self { offset, op: Op::Branch } } /// BL /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/BL--Branch-with-Link-?lang=en - pub fn bl(imm26: i32) -> Self { - Self { imm26, op: Op::BranchWithLink } + pub fn bl(offset: InstructionOffset) -> Self { + Self { offset, op: Op::BranchWithLink } } } @@ -50,7 +50,7 @@ impl From for u32 { 0 | ((inst.op as u32) << 31) | (FAMILY << 26) - | truncate_imm::<_, 26>(inst.imm26) + | truncate_imm::<_, 26>(inst.offset) } } @@ -68,37 +68,37 @@ mod tests { #[test] fn test_bl() { - let result: u32 = Call::bl(0).into(); + let result: u32 = Call::bl(0.into()).into(); assert_eq!(0x94000000, result); } #[test] fn test_bl_positive() { - let result: u32 = Call::bl(256).into(); + let result: u32 = Call::bl(256.into()).into(); assert_eq!(0x94000100, result); } #[test] fn test_bl_negative() { - let result: u32 = Call::bl(-256).into(); + let result: u32 = Call::bl((-256).into()).into(); assert_eq!(0x97ffff00, result); } #[test] fn test_b() { - let result: u32 = Call::b(0).into(); + let result: u32 = Call::b(0.into()).into(); assert_eq!(0x14000000, result); } #[test] fn test_b_positive() { - let result: u32 = Call::b((1 << 25) - 1).into(); + let result: u32 = Call::b(((1 << 25) - 1).into()).into(); assert_eq!(0x15ffffff, result); } #[test] fn test_b_negative() { - let result: u32 = Call::b(-(1 << 25)).into(); + let result: u32 = Call::b((-(1 << 25)).into()).into(); assert_eq!(0x16000000, result); } } diff --git a/yjit/src/asm/arm64/inst/load_literal.rs b/yjit/src/asm/arm64/inst/load_literal.rs index c5ab09713c0ea1..3eade205c808fc 100644 --- a/yjit/src/asm/arm64/inst/load_literal.rs +++ b/yjit/src/asm/arm64/inst/load_literal.rs @@ -1,4 +1,4 @@ -use super::super::arg::truncate_imm; +use super::super::arg::{InstructionOffset, truncate_imm}; /// The size of the operands being operated on. enum Opc { @@ -32,7 +32,7 @@ pub struct LoadLiteral { rt: u8, /// The PC-relative number of instructions to load the value from. - imm19: i32, + offset: InstructionOffset, /// The size of the operands being operated on. opc: Opc @@ -41,8 +41,8 @@ pub struct LoadLiteral { impl LoadLiteral { /// LDR (load literal) /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDR--literal---Load-Register--literal--?lang=en - pub fn ldr_literal(rt: u8, imm19: i32, num_bits: u8) -> Self { - Self { rt, imm19, opc: num_bits.into() } + pub fn ldr_literal(rt: u8, offset: InstructionOffset, num_bits: u8) -> Self { + Self { rt, offset, opc: num_bits.into() } } } @@ -56,7 +56,7 @@ impl From for u32 { | ((inst.opc as u32) << 30) | (1 << 28) | (FAMILY << 25) - | (truncate_imm::<_, 19>(inst.imm19) << 5) + | (truncate_imm::<_, 19>(inst.offset) << 5) | (inst.rt as u32) } } @@ -75,14 +75,14 @@ mod tests { #[test] fn test_ldr_positive() { - let inst = LoadLiteral::ldr_literal(0, 5, 64); + let inst = LoadLiteral::ldr_literal(0, 5.into(), 64); let result: u32 = inst.into(); assert_eq!(0x580000a0, result); } #[test] fn test_ldr_negative() { - let inst = LoadLiteral::ldr_literal(0, -5, 64); + let inst = LoadLiteral::ldr_literal(0, (-5).into(), 64); let result: u32 = inst.into(); assert_eq!(0x58ffff60, result); } diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs index b73b3125e27646..420151c6d18a17 100644 --- a/yjit/src/asm/arm64/mod.rs +++ b/yjit/src/asm/arm64/mod.rs @@ -190,15 +190,9 @@ pub const fn b_offset_fits_bits(offset: i64) -> bool { } /// B - branch without link (offset is number of instructions to jump) -pub fn b(cb: &mut CodeBlock, imm26: A64Opnd) { - let bytes: [u8; 4] = match imm26 { - A64Opnd::Imm(imm26) => { - assert!(b_offset_fits_bits(imm26), "The immediate operand must be 26 bits or less."); - - Call::b(imm26 as i32).into() - }, - _ => panic!("Invalid operand combination to b instruction.") - }; +pub fn b(cb: &mut CodeBlock, offset: InstructionOffset) { + assert!(b_offset_fits_bits(offset.into()), "The immediate operand must be 26 bits or less."); + let bytes: [u8; 4] = Call::b(offset).into(); cb.write_bytes(&bytes); } @@ -208,33 +202,21 @@ pub fn b(cb: &mut CodeBlock, imm26: A64Opnd) { /// value into a register first, then use the b.cond instruction to skip past a /// direct jump. pub const fn bcond_offset_fits_bits(offset: i64) -> bool { - imm_fits_bits(offset, 21) && (offset & 0b11 == 0) + imm_fits_bits(offset, 19) } /// B.cond - branch to target if condition is true -pub fn bcond(cb: &mut CodeBlock, cond: u8, byte_offset: A64Opnd) { - let bytes: [u8; 4] = match byte_offset { - A64Opnd::Imm(imm) => { - assert!(bcond_offset_fits_bits(imm), "The immediate operand must be 21 bits or less and be aligned to a 2-bit boundary."); - - BranchCond::bcond(cond, (imm / 4) as i32).into() - }, - _ => panic!("Invalid operand combination to bcond instruction."), - }; +pub fn bcond(cb: &mut CodeBlock, cond: u8, offset: InstructionOffset) { + assert!(bcond_offset_fits_bits(offset.into()), "The offset must be 19 bits or less."); + let bytes: [u8; 4] = BranchCond::bcond(cond, offset).into(); cb.write_bytes(&bytes); } /// BL - branch with link (offset is number of instructions to jump) -pub fn bl(cb: &mut CodeBlock, imm26: A64Opnd) { - let bytes: [u8; 4] = match imm26 { - A64Opnd::Imm(imm26) => { - assert!(b_offset_fits_bits(imm26), "The immediate operand must be 26 bits or less."); - - Call::bl(imm26 as i32).into() - }, - _ => panic!("Invalid operand combination to bl instruction.") - }; +pub fn bl(cb: &mut CodeBlock, offset: InstructionOffset) { + assert!(b_offset_fits_bits(offset.into()), "The offset must be 26 bits or less."); + let bytes: [u8; 4] = Call::bl(offset).into(); cb.write_bytes(&bytes); } @@ -413,7 +395,7 @@ pub fn ldr(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd, rm: A64Opnd) { } /// LDR - load a PC-relative memory address into a register -pub fn ldr_literal(cb: &mut CodeBlock, rt: A64Opnd, rn: i32) { +pub fn ldr_literal(cb: &mut CodeBlock, rt: A64Opnd, rn: InstructionOffset) { let bytes: [u8; 4] = match rt { A64Opnd::Reg(rt) => { LoadLiteral::ldr_literal(rt.reg_no, rn, rt.num_bits).into() @@ -1087,45 +1069,52 @@ mod tests { #[test] fn test_bcond() { - check_bytes("01200054", |cb| bcond(cb, Condition::NE, A64Opnd::new_imm(0x400))); + let offset = InstructionOffset::from_insns(0x100); + check_bytes("01200054", |cb| bcond(cb, Condition::NE, offset)); } #[test] fn test_b() { - check_bytes("ffffff15", |cb| b(cb, A64Opnd::new_imm((1 << 25) - 1))); + let offset = InstructionOffset::from_insns((1 << 25) - 1); + check_bytes("ffffff15", |cb| b(cb, offset)); } #[test] #[should_panic] fn test_b_too_big() { // There are 26 bits available - check_bytes("", |cb| b(cb, A64Opnd::new_imm(1 << 25))); + let offset = InstructionOffset::from_insns(1 << 25); + check_bytes("", |cb| b(cb, offset)); } #[test] #[should_panic] fn test_b_too_small() { // There are 26 bits available - check_bytes("", |cb| b(cb, A64Opnd::new_imm(-(1 << 25) - 1))); + let offset = InstructionOffset::from_insns(-(1 << 25) - 1); + check_bytes("", |cb| b(cb, offset)); } #[test] fn test_bl() { - check_bytes("00000096", |cb| bl(cb, A64Opnd::new_imm(-(1 << 25)))); + let offset = InstructionOffset::from_insns(-(1 << 25)); + check_bytes("00000096", |cb| bl(cb, offset)); } #[test] #[should_panic] fn test_bl_too_big() { // There are 26 bits available - check_bytes("", |cb| bl(cb, A64Opnd::new_imm(1 << 25))); + let offset = InstructionOffset::from_insns(1 << 25); + check_bytes("", |cb| bl(cb, offset)); } #[test] #[should_panic] fn test_bl_too_small() { // There are 26 bits available - check_bytes("", |cb| bl(cb, A64Opnd::new_imm(-(1 << 25) - 1))); + let offset = InstructionOffset::from_insns(-(1 << 25) - 1); + check_bytes("", |cb| bl(cb, offset)); } #[test] @@ -1200,7 +1189,7 @@ mod tests { #[test] fn test_ldr_literal() { - check_bytes("40010058", |cb| ldr_literal(cb, X0, 10)); + check_bytes("40010058", |cb| ldr_literal(cb, X0, 10.into())); } #[test] diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 32db0ab3dcdafd..446332788a052f 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -589,13 +589,15 @@ impl Assembler Target::CodePtr(dst_ptr) => { let dst_addr = dst_ptr.into_i64(); let src_addr = cb.get_write_ptr().into_i64(); - let offset = dst_addr - src_addr; - let num_insns = if bcond_offset_fits_bits(offset) { + let num_insns = if bcond_offset_fits_bits((dst_addr - src_addr) / 4) { // If the jump offset fits into the conditional jump as // an immediate value and it's properly aligned, then we - // can use the b.cond instruction directly. - bcond(cb, CONDITION, A64Opnd::new_imm(offset)); + // can use the b.cond instruction directly. We're safe + // to use as i32 here since we already checked that it + // fits. + let bytes = (dst_addr - src_addr) as i32; + bcond(cb, CONDITION, InstructionOffset::from_bytes(bytes)); // Here we're going to return 1 because we've only // written out 1 instruction. @@ -604,12 +606,12 @@ impl Assembler // Otherwise, we need to load the address into a // register and use the branch register instruction. let dst_addr = dst_ptr.into_u64(); - let load_insns: i64 = emit_load_size(dst_addr).into(); + let load_insns: i32 = emit_load_size(dst_addr).into(); // We're going to write out the inverse condition so // that if it doesn't match it will skip over the // instructions used for branching. - bcond(cb, Condition::inverse(CONDITION), A64Opnd::new_imm((load_insns + 2) * 4)); + bcond(cb, Condition::inverse(CONDITION), (load_insns + 2).into()); emit_load_value(cb, Assembler::SCRATCH0, dst_addr); br(cb, Assembler::SCRATCH0); @@ -630,7 +632,8 @@ impl Assembler // offset. We're going to assume we can fit into a single // b.cond instruction. It will panic otherwise. cb.label_ref(label_idx, 4, |cb, src_addr, dst_addr| { - bcond(cb, CONDITION, A64Opnd::new_imm(dst_addr - (src_addr - 4))); + let bytes: i32 = (dst_addr - (src_addr - 4)).try_into().unwrap(); + bcond(cb, CONDITION, InstructionOffset::from_bytes(bytes)); }); }, Target::FunPtr(_) => unreachable!() @@ -756,8 +759,8 @@ impl Assembler // references to GC'd Value operands. If the value // being loaded is a heap object, we'll report that // back out to the gc_offsets list. - ldr_literal(cb, out.into(), 2); - b(cb, A64Opnd::new_imm(1 + (SIZEOF_VALUE as i64) / 4)); + ldr_literal(cb, out.into(), 2.into()); + b(cb, InstructionOffset::from_bytes(4 + (SIZEOF_VALUE as i32))); cb.write_bytes(&value.as_u64().to_le_bytes()); let ptr_offset: u32 = (cb.get_write_pos() as u32) - (SIZEOF_VALUE as u32); @@ -844,14 +847,11 @@ impl Assembler // The offset to the call target in bytes let src_addr = cb.get_write_ptr().into_i64(); let dst_addr = target.unwrap_fun_ptr() as i64; - let offset = dst_addr - src_addr; - // The offset in instruction count for BL's immediate - let offset = offset / 4; // Use BL if the offset is short enough to encode as an immediate. // Otherwise, use BLR with a register. - if b_offset_fits_bits(offset) { - bl(cb, A64Opnd::new_imm(offset)); + if b_offset_fits_bits((dst_addr - src_addr) / 4) { + bl(cb, InstructionOffset::from_bytes((dst_addr - src_addr) as i32)); } else { emit_load_value(cb, Self::SCRATCH0, dst_addr as u64); blr(cb, Self::SCRATCH0); @@ -875,19 +875,22 @@ impl Assembler let src_addr = cb.get_write_ptr().into_i64(); let dst_addr = dst_ptr.into_i64(); - // The offset between the two instructions in bytes. - // Note that when we encode this into a b - // instruction, we'll divide by 4 because it accepts - // the number of instructions to jump over. - let offset = dst_addr - src_addr; - let offset = offset / 4; - // 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 = emit_load_value(cb, Self::SCRATCH0, dst_addr as u64); - br(cb, Self::SCRATCH0); + 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); } @@ -899,7 +902,8 @@ impl Assembler // to assume we can fit into a single b instruction. // It will panic otherwise. cb.label_ref(*label_idx, 4, |cb, src_addr, dst_addr| { - b(cb, A64Opnd::new_imm((dst_addr - (src_addr - 4)) / 4)); + let bytes: i32 = (dst_addr - (src_addr - 4)).try_into().unwrap(); + b(cb, InstructionOffset::from_bytes(bytes)); }); }, _ => unreachable!() From 2a08a39d7d788fc30b5242ef7ed40cc78d1982c3 Mon Sep 17 00:00:00 2001 From: git Date: Sat, 10 Sep 2022 00:38:09 +0900 Subject: [PATCH 032/269] * 2022-09-10 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index c60ab92249698a..d4932232d1790a 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 9 +#define RUBY_RELEASE_DAY 10 #include "ruby/version.h" #include "ruby/internal/abi.h" From 5b5c627d37c1773dd280e0f14d9510f8fa8db04f Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert Date: Fri, 9 Sep 2022 18:41:19 -0400 Subject: [PATCH 033/269] YJIT: eliminate redundant mov in csel/cmov on x86 (#6348) * Eliminate redundant mov in csel/cmov. Translate mov reg,0 into xor * Fix x86 asm test * Remove dbg!() * xor optimization unsound because it resets flags --- yjit/src/backend/x86_64/mod.rs | 55 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index ca07d50ffc4a5e..2f770c2eac7923 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -145,7 +145,7 @@ impl Assembler if !value.special_const_p() || imm_num_bits(value.as_i64()) > 32 { asm.load(iterator.map_opnd(*opnd)) } else { - iterator.map_opnd(*opnd) + Opnd::UImm(value.as_u64()) } } else { iterator.map_opnd(*opnd) @@ -221,18 +221,25 @@ impl Assembler Insn::CSelLE { truthy, falsy, out } | Insn::CSelG { truthy, falsy, out } | Insn::CSelGE { truthy, falsy, out } => { - match truthy { - Opnd::Reg(_) | Opnd::InsnOut { .. } => {}, - _ => { + match unmapped_opnds[0] { + // If we have an instruction output whose live range + // spans beyond this instruction, we have to load it. + Opnd::InsnOut { idx, .. } => { + if live_ranges[idx] > index { + *truthy = asm.load(*truthy); + } + }, + Opnd::UImm(_) | Opnd::Imm(_) | Opnd::Value(_) => { *truthy = asm.load(*truthy); - } + }, + _ => {} }; match falsy { - Opnd::Reg(_) | Opnd::InsnOut { .. } => {}, - _ => { + Opnd::UImm(_) | Opnd::Imm(_) => { *falsy = asm.load(*falsy); - } + }, + _ => {} }; *out = asm.next_opnd_out(Opnd::match_num_bits(&[*truthy, *falsy])); @@ -350,6 +357,14 @@ impl Assembler } } + + fn emit_csel(cb: &mut CodeBlock, truthy: Opnd, falsy: Opnd, out: Opnd, cmov_fn: fn(&mut CodeBlock, X86Opnd, X86Opnd)) { + if out != truthy { + mov(cb, out.into(), truthy.into()); + } + cmov_fn(cb, out.into(), falsy.into()); + } + //dbg!(&self.insns); // List of GC offsets @@ -609,36 +624,28 @@ impl Assembler Insn::Breakpoint => int3(cb), Insn::CSelZ { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovnz(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovnz); }, Insn::CSelNZ { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovz(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovz); }, Insn::CSelE { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovne(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovne); }, Insn::CSelNE { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmove(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmove); }, Insn::CSelL { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovge(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovge); }, Insn::CSelLE { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovg(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovg); }, Insn::CSelG { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovle(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovle); }, Insn::CSelGE { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovl(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovl); } Insn::LiveReg { .. } => (), // just a reg alloc signal, no code Insn::PadEntryExit => { From 518301883edee12218657c77ba977e2bde1b17d0 Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Fri, 9 Sep 2022 19:32:20 +0200 Subject: [PATCH 034/269] Fix parallel build on MINGW When the build is running with a base ruby then generating `x64-ucrt-ruby320.rc` could fail due to a missing dependency to `x64-mingw-ucrt-fake.rb`. This commit adds this dependency. A failing build looks like so: ``` generating x64-mingw-ucrt-fake.rb generating x64-ucrt-ruby320.rc ../snapshot-master/win32/resource.rb:in `require': cannot load such file -- ./x64-mingw-ucrt-fake (LoadError) make: *** [GNUmakefile:57: x64-ucrt-ruby320.rc] Error 1 make: *** Waiting for unfinished jobs.... linking miniruby.exe x64-mingw-ucrt-fake.rb updated ``` --- cygwin/GNUmakefile.in | 4 ++-- template/Makefile.in | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in index 3b11d3f69c2ca8..43e92a27f06a3f 100644 --- a/cygwin/GNUmakefile.in +++ b/cygwin/GNUmakefile.in @@ -52,7 +52,7 @@ $(RUBY_EXP) $(LIBRUBY_SO): $(DLL_BASE_NAME).res.$(OBJEXT) $(ECHO) compiling $@ $(Q) $(WINDRES) --include-dir . --include-dir $( Date: Sat, 10 Sep 2022 02:00:34 +0900 Subject: [PATCH 035/269] Using is_broken_string function --- file.c | 4 ++-- marshal.c | 2 +- transcode.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/file.c b/file.c index eb5851de7d66bb..de7ed5e33ba601 100644 --- a/file.c +++ b/file.c @@ -4500,9 +4500,9 @@ rb_check_realpath_internal(VALUE basedir, VALUE path, rb_encoding *origenc, enum rb_enc_associate(resolved, origenc); } - if (rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) { + if (is_broken_string(resolved)) { rb_enc_associate(resolved, rb_filesystem_encoding()); - if (rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) { + if (is_broken_string(resolved)) { rb_enc_associate(resolved, rb_ascii8bit_encoding()); } } diff --git a/marshal.c b/marshal.c index 9e2f1c4381d786..e4b40c0607b608 100644 --- a/marshal.c +++ b/marshal.c @@ -1554,7 +1554,7 @@ r_symreal(struct load_arg *arg, int ivar) } if (idx > 0) { rb_enc_associate_index(s, idx); - if (rb_enc_str_coderange(s) == ENC_CODERANGE_BROKEN) { + if (is_broken_string(s)) { rb_raise(rb_eArgError, "invalid byte sequence in %s: %+"PRIsVALUE, rb_enc_name(rb_enc_from_index(idx)), s); } diff --git a/transcode.c b/transcode.c index 535e436b039a87..e315a8f1ab06b2 100644 --- a/transcode.c +++ b/transcode.c @@ -2590,7 +2590,7 @@ rb_econv_prepare_options(VALUE opthash, VALUE *opts, int ecflags) v = rb_hash_aref(opthash, sym_replace); if (!NIL_P(v)) { StringValue(v); - if (rb_enc_str_coderange(v) == ENC_CODERANGE_BROKEN) { + if (is_broken_string(v)) { VALUE dumped = rb_str_dump(v); rb_raise(rb_eArgError, "replacement string is broken: %s as %s", StringValueCStr(dumped), From d3191843909208477ada3b82db00fb6204696479 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 5 Sep 2022 15:20:08 -0700 Subject: [PATCH 036/269] YJIT: Branch directly when truthyness is known --- yjit/src/codegen.rs | 88 ++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 58b30d5c85bd25..0551491bbb952c 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -3197,12 +3197,6 @@ fn gen_branchif( gen_check_ints(asm, side_exit); } - // Test if any bit (outside of the Qnil bit) is on - // RUBY_Qfalse /* ...0000 0000 */ - // RUBY_Qnil /* ...0000 1000 */ - let val_opnd = ctx.stack_pop(1); - asm.test(val_opnd, Opnd::Imm(!Qnil.as_i64())); - // Get the branch target instruction offsets let next_idx = jit_next_insn_idx(jit); let jump_idx = (next_idx as i32) + jump_offset; @@ -3215,18 +3209,31 @@ fn gen_branchif( idx: jump_idx as u32, }; - // Generate the branch instructions - gen_branch( - jit, - ctx, - asm, - ocb, - jump_block, - ctx, - Some(next_block), - Some(ctx), - gen_branchif_branch, - ); + // Test if any bit (outside of the Qnil bit) is on + // RUBY_Qfalse /* ...0000 0000 */ + // RUBY_Qnil /* ...0000 1000 */ + let val_type = ctx.get_opnd_type(StackOpnd(0)); + let val_opnd = ctx.stack_pop(1); + + if let Some(result) = val_type.known_truthy() { + let target = if result { jump_block } else { next_block }; + gen_direct_jump(jit, ctx, target, asm); + } else { + asm.test(val_opnd.into(), Opnd::Imm(!Qnil.as_i64())); + + // Generate the branch instructions + gen_branch( + jit, + ctx, + asm, + ocb, + jump_block, + ctx, + Some(next_block), + Some(ctx), + gen_branchif_branch, + ); + } EndBlock } @@ -3261,13 +3268,6 @@ fn gen_branchunless( gen_check_ints(asm, side_exit); } - // Test if any bit (outside of the Qnil bit) is on - // RUBY_Qfalse /* ...0000 0000 */ - // RUBY_Qnil /* ...0000 1000 */ - let val_opnd = ctx.stack_pop(1); - let not_qnil = !Qnil.as_i64(); - asm.test(val_opnd, not_qnil.into()); - // Get the branch target instruction offsets let next_idx = jit_next_insn_idx(jit) as i32; let jump_idx = next_idx + jump_offset; @@ -3280,18 +3280,32 @@ fn gen_branchunless( idx: jump_idx.try_into().unwrap(), }; - // Generate the branch instructions - gen_branch( - jit, - ctx, - asm, - ocb, - jump_block, - ctx, - Some(next_block), - Some(ctx), - gen_branchunless_branch, - ); + let val_type = ctx.get_opnd_type(StackOpnd(0)); + let val_opnd = ctx.stack_pop(1); + + if let Some(result) = val_type.known_truthy() { + let target = if result { next_block } else { jump_block }; + gen_direct_jump(jit, ctx, target, asm); + } else { + // Test if any bit (outside of the Qnil bit) is on + // RUBY_Qfalse /* ...0000 0000 */ + // RUBY_Qnil /* ...0000 1000 */ + let not_qnil = !Qnil.as_i64(); + asm.test(val_opnd.into(), not_qnil.into()); + + // Generate the branch instructions + gen_branch( + jit, + ctx, + asm, + ocb, + jump_block, + ctx, + Some(next_block), + Some(ctx), + gen_branchunless_branch, + ); + } EndBlock } From 5e39b3b844d5601541deb7cb0648d88cc6fdc080 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Thu, 8 Sep 2022 21:51:39 -0700 Subject: [PATCH 037/269] YJIT: Branch directly when nil? is known from types --- yjit/src/codegen.rs | 40 +++++++++++++++++++++++----------------- yjit/src/core.rs | 12 +++++++++++- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 0551491bbb952c..1c48a1b040ce5a 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -3340,11 +3340,6 @@ fn gen_branchnil( gen_check_ints(asm, side_exit); } - // Test if the value is Qnil - // RUBY_Qnil /* ...0000 1000 */ - let val_opnd = ctx.stack_pop(1); - asm.cmp(val_opnd, Opnd::UImm(Qnil.into())); - // Get the branch target instruction offsets let next_idx = jit_next_insn_idx(jit) as i32; let jump_idx = next_idx + jump_offset; @@ -3357,18 +3352,29 @@ fn gen_branchnil( idx: jump_idx.try_into().unwrap(), }; - // Generate the branch instructions - gen_branch( - jit, - ctx, - asm, - ocb, - jump_block, - ctx, - Some(next_block), - Some(ctx), - gen_branchnil_branch, - ); + let val_type = ctx.get_opnd_type(StackOpnd(0)); + let val_opnd = ctx.stack_pop(1); + + if let Some(result) = val_type.known_nil() { + let target = if result { jump_block } else { next_block }; + gen_direct_jump(jit, ctx, target, asm); + } else { + // Test if the value is Qnil + // RUBY_Qnil /* ...0000 1000 */ + asm.cmp(val_opnd, Opnd::UImm(Qnil.into())); + // Generate the branch instructions + gen_branch( + jit, + ctx, + asm, + ocb, + jump_block, + ctx, + Some(next_block), + Some(ctx), + gen_branchnil_branch, + ); + } EndBlock } diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 687dc21013202a..102988db11c78c 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -172,7 +172,7 @@ impl Type { } } - /// Returns an Option with the exact value if it is known, otherwise None + /// Returns an Option boolean representing whether the value is truthy if known, otherwise None pub fn known_truthy(&self) -> Option { match self { Type::Nil => Some(false), @@ -183,6 +183,16 @@ impl Type { } } + /// Returns an Option boolean representing whether the value is equal to nil if known, otherwise None + pub fn known_nil(&self) -> Option { + match (self, self.known_truthy()) { + (Type::Nil, _) => Some(true), + (Type::False, _) => Some(false), // Qfalse is not nil + (_, Some(true)) => Some(false), // if truthy, can't be nil + (_, _) => None // otherwise unknown + } + } + /// Compute a difference between two value types /// Returns 0 if the two are the same /// Returns > 0 if different but compatible From 92b907d12d6de6f74f1efa6d8cd4fed8fa50763e Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sat, 3 Sep 2022 15:47:49 +0200 Subject: [PATCH 038/269] Enable deprecation warnings for test-all * So deprecated methods/constants/functions are dealt with early, instead of many tests breaking suddenly when removing a deprecated method/constant/function. * Follows https://bugs.ruby-lang.org/issues/17591 --- test/ruby/test_exception.rb | 2 +- tool/lib/test/unit.rb | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 0b05ff7c51bdfd..f04106713761d5 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -1053,7 +1053,7 @@ def capture_warning_warn(category: false) warning << [str, category] end else - define_method(:warn) do |str| + define_method(:warn) do |str, category: nil| warning << str end end diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 8cb6d8f651cbbf..b2190843c8bc4e 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1,5 +1,20 @@ # frozen_string_literal: true +# Enable deprecation warnings for test-all, so deprecated methods/constants/functions are dealt with early. +Warning[:deprecated] = true + +if ENV['BACKTRACE_FOR_DEPRECATION_WARNINGS'] + Warning.extend Module.new { + def warn(message, category: nil, **kwargs) + if category == :deprecated and $stderr.respond_to?(:puts) + $stderr.puts nil, message, caller, nil + else + super + end + end + } +end + require_relative '../envutil' require_relative '../colorize' require_relative '../leakchecker' From c7225971036afbf552673efbe25bc76d77ea6fe2 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 10 Sep 2022 17:19:14 +0900 Subject: [PATCH 039/269] [MSWin] Get rid of single quotes in sed command lines GnuWin32 sed strips only double quotes, but not single quotes, and dies: ``` sed: -e expression #1, char 1: unknown command: `'' ``` --- common.mk | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common.mk b/common.mk index 1385db9a4976e5..fc05b3bd45d908 100644 --- a/common.mk +++ b/common.mk @@ -1222,7 +1222,7 @@ $(REVISION_H): revision.tmp $(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y $(srcdir)/defs/id.def $(srcdir)/ext/ripper/depend $(ECHO) generating $@ $(Q) $(CHDIR) $(@D) && \ - sed -e 's/{\$$([^(){}]*)[^{}]*}//g' -e /AUTOGENERATED/q depend | \ + sed -e "s/{\$$([^(){}]*)[^{}]*}//g" -e /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) RM="$(RM1)" BISON=$(YACC) top_srcdir=../.. srcdir=. VPATH=../.. \ RUBY="$(BASERUBY)" PATH_SEPARATOR="$(PATH_SEPARATOR)" LANG=C @@ -1241,7 +1241,7 @@ $(srcdir)/ext/rbconfig/sizeof/sizes.c: $(srcdir)/ext/rbconfig/sizeof/depend \ $(tooldir)/generic_erb.rb $(srcdir)/template/sizes.c.tmpl $(srcdir)/configure.ac $(ECHO) generating $@ $(Q) $(CHDIR) $(@D) && \ - sed '/AUTOGENERATED/q' depend | \ + sed /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F) @@ -1249,19 +1249,19 @@ $(srcdir)/ext/rbconfig/sizeof/limits.c: $(srcdir)/ext/rbconfig/sizeof/depend \ $(tooldir)/generic_erb.rb $(srcdir)/template/limits.c.tmpl $(ECHO) generating $@ $(Q) $(CHDIR) $(@D) && \ - sed '/AUTOGENERATED/q' depend | \ + sed /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F) $(srcdir)/ext/socket/constdefs.c: $(srcdir)/ext/socket/depend $(Q) $(CHDIR) $(@D) && \ - sed '/AUTOGENERATED/q' depend | \ + sed /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)" $(srcdir)/ext/etc/constdefs.h: $(srcdir)/ext/etc/depend $(Q) $(CHDIR) $(@D) && \ - sed '/AUTOGENERATED/q' depend | \ + sed /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)" From f64117952534b71a11636837bddbfea7506ee785 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 28 Aug 2022 16:28:41 +0900 Subject: [PATCH 040/269] [win32] Set proper `NULLCMD` in rbconfig.rb [ci skip] --- win32/Makefile.sub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 0b1f41f70ec193..9a6bb47e102919 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -976,7 +976,7 @@ s,@target_alias@,$(ARCH)-$(PLATFORM),;t t s,@target_cpu@,$(ARCH),;t t s,@target_vendor@,pc,;t t s,@target_os@,$(PLATFORM),;t t -s,@NULLCMD@,:,;t t +s,@NULLCMD@,$(NULLCMD),;t t s,@CC@,$(CC),;t t s,@CPP@,$(CPP),;t t s,@CXX@,$$(CC),;t t From 14bcf69c9c36875c7956d0ec8c04bfeaec514dd1 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sat, 3 Sep 2022 12:46:57 +0200 Subject: [PATCH 041/269] Deprecate Encoding#replicate * See [Feature #18949]. --- NEWS.md | 4 + common.mk | 1 + encoding.c | 6 +- spec/ruby/core/encoding/replicate_spec.rb | 108 ++++++++++++---------- test/ruby/test_encoding.rb | 5 +- 5 files changed, 71 insertions(+), 53 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4dd8cbbe2b2eca..407458bbe55452 100644 --- a/NEWS.md +++ b/NEWS.md @@ -96,6 +96,9 @@ Note that each entry is kept to a minimum, see links for details. Note: We're only listing outstanding class updates. +* Encoding + * Encoding#replicate has been deprecated and will be removed in 3.3. [[Feature #18949]] + * Enumerator * Enumerator.product has been added. Enumerator::Product is the implementation. [[Feature #18685]] @@ -292,3 +295,4 @@ The following deprecated APIs are removed. [Feature #18788]: https://bugs.ruby-lang.org/issues/18788 [Feature #18809]: https://bugs.ruby-lang.org/issues/18809 [Feature #18481]: https://bugs.ruby-lang.org/issues/18481 +[Feature #18949]: https://bugs.ruby-lang.org/issues/18949 diff --git a/common.mk b/common.mk index fc05b3bd45d908..94c5a3a9947ab4 100644 --- a/common.mk +++ b/common.mk @@ -5418,6 +5418,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/class.h encoding.$(OBJEXT): $(top_srcdir)/internal/compilers.h encoding.$(OBJEXT): $(top_srcdir)/internal/enc.h encoding.$(OBJEXT): $(top_srcdir)/internal/encoding.h +encoding.$(OBJEXT): $(top_srcdir)/internal/error.h encoding.$(OBJEXT): $(top_srcdir)/internal/gc.h encoding.$(OBJEXT): $(top_srcdir)/internal/inits.h encoding.$(OBJEXT): $(top_srcdir)/internal/load.h diff --git a/encoding.c b/encoding.c index b8fedfb7976724..5c4dfad3f0208c 100644 --- a/encoding.c +++ b/encoding.c @@ -17,6 +17,7 @@ #include "internal.h" #include "internal/enc.h" #include "internal/encoding.h" +#include "internal/error.h" #include "internal/inits.h" #include "internal/load.h" #include "internal/object.h" @@ -569,7 +570,10 @@ rb_enc_replicate(const char *name, rb_encoding *encoding) static VALUE enc_replicate_m(VALUE encoding, VALUE name) { - int idx = rb_enc_replicate(name_for_encoding(&name), rb_to_encoding(encoding)); + int idx; + rb_warn_deprecated_to_remove("3.3", "Encoding#replicate", "the original encoding"); + + idx = rb_enc_replicate(name_for_encoding(&name), rb_to_encoding(encoding)); RB_GC_GUARD(name); return rb_enc_from_encoding_index(idx); } diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb index 45727a5c0d4494..848415eeb4e7ca 100644 --- a/spec/ruby/core/encoding/replicate_spec.rb +++ b/spec/ruby/core/encoding/replicate_spec.rb @@ -2,66 +2,74 @@ require_relative '../../spec_helper' describe "Encoding#replicate" do - before :all do - @i = 0 - end + ruby_version_is ""..."3.3" do + before :all do + @i = 0 + end - before :each do - @i += 1 - @prefix = "RS#{@i}" - end + before :each do + @i += 1 + @prefix = "RS#{@i}" + end - it "returns a replica of ASCII" do - name = @prefix + '-ASCII' - e = Encoding::ASCII.replicate(name) - e.name.should == name - Encoding.find(name).should == e + it "returns a replica of ASCII" do + name = @prefix + '-ASCII' + e = suppress_warning { Encoding::ASCII.replicate(name) } + e.name.should == name + Encoding.find(name).should == e - "a".force_encoding(e).valid_encoding?.should be_true - "\x80".force_encoding(e).valid_encoding?.should be_false - end + "a".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end - it "returns a replica of UTF-8" do - name = @prefix + 'UTF-8' - e = Encoding::UTF_8.replicate(name) - e.name.should == name - Encoding.find(name).should == e + it "returns a replica of UTF-8" do + name = @prefix + 'UTF-8' + e = suppress_warning { Encoding::UTF_8.replicate(name) } + e.name.should == name + Encoding.find(name).should == e - "a".force_encoding(e).valid_encoding?.should be_true - "\u3042".force_encoding(e).valid_encoding?.should be_true - "\x80".force_encoding(e).valid_encoding?.should be_false - end + "a".force_encoding(e).valid_encoding?.should be_true + "\u3042".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end - it "returns a replica of UTF-16BE" do - name = @prefix + 'UTF-16-BE' - e = Encoding::UTF_16BE.replicate(name) - e.name.should == name - Encoding.find(name).should == e + it "returns a replica of UTF-16BE" do + name = @prefix + 'UTF-16-BE' + e = suppress_warning { Encoding::UTF_16BE.replicate(name) } + e.name.should == name + Encoding.find(name).should == e - "a".force_encoding(e).valid_encoding?.should be_false - "\x30\x42".force_encoding(e).valid_encoding?.should be_true - "\x80".force_encoding(e).valid_encoding?.should be_false - end + "a".force_encoding(e).valid_encoding?.should be_false + "\x30\x42".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end - it "returns a replica of ISO-2022-JP" do - name = @prefix + 'ISO-2022-JP' - e = Encoding::ISO_2022_JP.replicate(name) - Encoding.find(name).should == e + it "returns a replica of ISO-2022-JP" do + name = @prefix + 'ISO-2022-JP' + e = suppress_warning { Encoding::ISO_2022_JP.replicate(name) } + Encoding.find(name).should == e - e.name.should == name - e.dummy?.should be_true - end + e.name.should == name + e.dummy?.should be_true + end - # NOTE: it's unclear of the value of this (for the complexity cost of it), - # but it is the current CRuby behavior. - it "can be associated with a String" do - name = @prefix + '-US-ASCII' - e = Encoding::US_ASCII.replicate(name) - e.name.should == name - Encoding.find(name).should == e + # NOTE: it's unclear of the value of this (for the complexity cost of it), + # but it is the current CRuby behavior. + it "can be associated with a String" do + name = @prefix + '-US-ASCII' + e = suppress_warning { Encoding::US_ASCII.replicate(name) } + e.name.should == name + Encoding.find(name).should == e + + s = "abc".force_encoding(e) + s.encoding.should == e + s.encoding.name.should == name + end + end - s = "abc".force_encoding(e) - s.encoding.should == e - s.encoding.name.should == name + ruby_version_is "3.3" do + it "has been removed" do + Encoding::US_ASCII.should_not.respond_to?(:replicate, true) + end end end diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb index 4a6dd932ed2eff..64af8b488a6c58 100644 --- a/test/ruby/test_encoding.rb +++ b/test/ruby/test_encoding.rb @@ -57,6 +57,7 @@ def test_find def test_replicate assert_separately([], "#{<<~'END;'}") + Warning[:deprecated] = false assert_instance_of(Encoding, Encoding::UTF_8.replicate("UTF-8-ANOTHER#{Time.now.to_f}")) assert_instance_of(Encoding, Encoding::ISO_2022_JP.replicate("ISO-2022-JP-ANOTHER#{Time.now.to_f}")) bug3127 = '[ruby-dev:40954]' @@ -69,7 +70,7 @@ def test_extra_encoding assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; 200.times {|i| - Encoding::UTF_8.replicate("dummy#{i}") + EnvUtil.suppress_warning { Encoding::UTF_8.replicate("dummy#{i}") } } e = Encoding.list.last format = "%d".force_encoding(e) @@ -82,7 +83,7 @@ def test_extra_encoding name = "A" * 64 Encoding.list.each do |enc| - assert_raise(ArgumentError) {enc.replicate(name)} + assert_raise(ArgumentError) { EnvUtil.suppress_warning { enc.replicate(name) } } name.succ! end end; From c22a668580fc9a0c211d55c350a395f830a0740e Mon Sep 17 00:00:00 2001 From: git Date: Sun, 11 Sep 2022 02:03:41 +0900 Subject: [PATCH 042/269] * 2022-09-11 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index d4932232d1790a..f45db7190184b3 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 10 +#define RUBY_RELEASE_DAY 11 #include "ruby/version.h" #include "ruby/internal/abi.h" From aa8a3b2358fbdc176c6bcfbcfd3ed1646d287d62 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 11 Sep 2022 08:59:27 +0900 Subject: [PATCH 043/269] MJIT: Do not hang after forking with threads First, rb_mjit_fork should call rb_thread_atfork to stop threads after fork in the child process. Unfortunately, we cannot use rb_fork_ruby to prevent this kind of mistakes because MJIT needs special handling of waiting_pid and mjit_pause/resume. Second, mjit_waitpid_finished should be checked regardless of trap_interrupt. It doesn't seem like the flag is not set when SIGCHLD is handled for an MJIT child process. --- process.c | 1 + thread.c | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/process.c b/process.c index 405c7edcb31c84..4465b36c514e89 100644 --- a/process.c +++ b/process.c @@ -4222,6 +4222,7 @@ rb_mjit_fork(void) after_fork_ruby(); disable_child_handler_fork_parent(&old); + if (pid == 0) rb_thread_atfork(); return pid; } diff --git a/thread.c b/thread.c index e50b2ce6ca43ab..1364d73be23706 100644 --- a/thread.c +++ b/thread.c @@ -2321,16 +2321,16 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing) ret |= rb_signal_exec(th, sig); } th->status = prev_status; + } #if USE_MJIT - // Handle waitpid_signal for MJIT issued by ruby_sigchld_handler. This needs to be done - // outside ruby_sigchld_handler to avoid recursively relying on the SIGCHLD handler. - if (mjit_waitpid_finished) { - mjit_waitpid_finished = false; - mjit_notify_waitpid(WIFEXITED(mjit_waitpid_status) ? WEXITSTATUS(mjit_waitpid_status) : -1); - } -#endif + // Handle waitpid_signal for MJIT issued by ruby_sigchld_handler. This needs to be done + // outside ruby_sigchld_handler to avoid recursively relying on the SIGCHLD handler. + if (mjit_waitpid_finished && th == th->vm->ractor.main_thread) { + mjit_waitpid_finished = false; + mjit_notify_waitpid(WIFEXITED(mjit_waitpid_status) ? WEXITSTATUS(mjit_waitpid_status) : -1); } +#endif /* exception from another thread */ if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) { From 7232f264df41ae58b126a2915eacfd85882ef6a4 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 11 Sep 2022 10:14:56 +0900 Subject: [PATCH 044/269] Remove -j from MinGW test-all MinGW CI has been crashing too often. Now that we don't have slow test_mjit in MinGW, I'd like to see if not using parallel test workers fixes the problem. --- .github/workflows/mingw.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 6f93aa5392e3c2..68965b845cb862 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -70,7 +70,6 @@ jobs: - name: set env run: | echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV - echo "TEST_JOBS=$((15 * NUMBER_OF_PROCESSORS / 10))" >> $GITHUB_ENV - name: where check run: | @@ -140,7 +139,7 @@ jobs: make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }} env: RUBY_TESTOPTS: >- - -j${{env.TEST_JOBS}} --retry --job-status=normal --show-skip --timeout-scale=1.5 + --retry --job-status=normal --show-skip --timeout-scale=1.5 ${{ matrix.test-all-opts }} BUNDLER_VERSION: if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-all' || StartsWith(matrix.test_task, 'test/')}} From 106744107b485a371ed788fe590c6ce778de16df Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 5 Sep 2022 00:06:43 -0700 Subject: [PATCH 045/269] [ruby/fiddle] Fix PACK_MAP for unsigned types (https://github.com/ruby/fiddle/pull/110) https://github.com/ruby/fiddle/commit/4a71246645ccff001292c9d80b855b2ef5bf06c1 --- ext/fiddle/lib/fiddle/pack.rb | 15 +++++++------- test/fiddle/test_pack.rb | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 test/fiddle/test_pack.rb diff --git a/ext/fiddle/lib/fiddle/pack.rb b/ext/fiddle/lib/fiddle/pack.rb index 22eccedb76920a..eb99fe090de5b5 100644 --- a/ext/fiddle/lib/fiddle/pack.rb +++ b/ext/fiddle/lib/fiddle/pack.rb @@ -18,17 +18,17 @@ module PackInfo # :nodoc: all } PACK_MAP = { - TYPE_VOIDP => "l!", + TYPE_VOIDP => "L!", TYPE_CHAR => "c", TYPE_SHORT => "s!", TYPE_INT => "i!", TYPE_LONG => "l!", TYPE_FLOAT => "f", TYPE_DOUBLE => "d", - -TYPE_CHAR => "c", - -TYPE_SHORT => "s!", - -TYPE_INT => "i!", - -TYPE_LONG => "l!", + -TYPE_CHAR => "C", + -TYPE_SHORT => "S!", + -TYPE_INT => "I!", + -TYPE_LONG => "L!", } SIZE_MAP = { @@ -46,9 +46,10 @@ module PackInfo # :nodoc: all } if defined?(TYPE_LONG_LONG) ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG - PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q" + PACK_MAP[TYPE_LONG_LONG] = "q" + PACK_MAP[-TYPE_LONG_LONG] = "Q" SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG - PACK_MAP[TYPE_VOIDP] = "q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP + PACK_MAP[TYPE_VOIDP] = "Q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP end def align(addr, align) diff --git a/test/fiddle/test_pack.rb b/test/fiddle/test_pack.rb new file mode 100644 index 00000000000000..ade1dd5040c8bf --- /dev/null +++ b/test/fiddle/test_pack.rb @@ -0,0 +1,37 @@ +begin + require_relative 'helper' + require 'fiddle/pack' +rescue LoadError + return +end + +module Fiddle + class TestPack < TestCase + def test_pack_map + if defined?(TYPE_LONG_LONG) + assert_equal [0xffff_ffff_ffff_ffff], [0xffff_ffff_ffff_ffff].pack(PackInfo::PACK_MAP[-TYPE_LONG_LONG]).unpack(PackInfo::PACK_MAP[-TYPE_LONG_LONG]) + end + + case Fiddle::SIZEOF_VOIDP + when 8 + assert_equal [0xffff_ffff_ffff_ffff], [0xffff_ffff_ffff_ffff].pack(PackInfo::PACK_MAP[TYPE_VOIDP]).unpack(PackInfo::PACK_MAP[TYPE_VOIDP]) + when 4 + assert_equal [0xffff_ffff], [0xffff_ffff].pack(PackInfo::PACK_MAP[TYPE_VOIDP]).unpack(PackInfo::PACK_MAP[TYPE_VOIDP]) + end + + case Fiddle::SIZEOF_LONG + when 8 + assert_equal [0xffff_ffff_ffff_ffff], [0xffff_ffff_ffff_ffff].pack(PackInfo::PACK_MAP[-TYPE_LONG]).unpack(PackInfo::PACK_MAP[-TYPE_LONG]) + when 4 + assert_equal [0xffff_ffff], [0xffff_ffff].pack(PackInfo::PACK_MAP[-TYPE_LONG]).unpack(PackInfo::PACK_MAP[-TYPE_LONG]) + end + + if Fiddle::SIZEOF_INT == 4 + assert_equal [0xffff_ffff], [0xffff_ffff].pack(PackInfo::PACK_MAP[-TYPE_INT]).unpack(PackInfo::PACK_MAP[-TYPE_INT]) + end + + assert_equal [0xffff], [0xffff].pack(PackInfo::PACK_MAP[-TYPE_SHORT]).unpack(PackInfo::PACK_MAP[-TYPE_SHORT]) + assert_equal [0xff], [0xff].pack(PackInfo::PACK_MAP[-TYPE_CHAR]).unpack(PackInfo::PACK_MAP[-TYPE_CHAR]) + end + end +end From 472e7b8518fbde98be3f0a06192a6695697bf879 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 11 Sep 2022 15:39:32 +0900 Subject: [PATCH 046/269] MJIT: Use the built-in PACK_MAP 106744107b made this possible. --- lib/mjit/c_pointer.rb | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/mjit/c_pointer.rb b/lib/mjit/c_pointer.rb index 75ed8babd59a69..ce0948197d9856 100644 --- a/lib/mjit/c_pointer.rb +++ b/lib/mjit/c_pointer.rb @@ -2,14 +2,6 @@ module RubyVM::MJIT # Every class under this namespace is a pointer. Even if the type is # immediate, it shouldn't be dereferenced until `*` is called. module CPointer - # Patched PACK_MAP to support unsigned operations - PACK_MAP = Fiddle::PackInfo::PACK_MAP.dup - PACK_MAP.keys.each do |type| - if type.negative? || type == Fiddle::TYPE_VOIDP - PACK_MAP[type] = PACK_MAP[type].upcase - end - end - # Note: We'd like to avoid alphabetic method names to avoid a conflict # with member methods. to_i and to_s are considered an exception. class Struct @@ -188,7 +180,7 @@ def to_s # @param fiddle_type [Integer] Fiddle::TYPE_* def self.define(fiddle_type) size = Fiddle::PackInfo::SIZE_MAP.fetch(fiddle_type) - pack = PACK_MAP.fetch(fiddle_type) + pack = Fiddle::PackInfo::PACK_MAP.fetch(fiddle_type) Class.new(self) do define_method(:initialize) do |addr| @@ -252,13 +244,13 @@ def [](index) # @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself or an object that return an address with to_i def []=(index, value) Fiddle::Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP)[0, Fiddle::SIZEOF_VOIDP] = - [value.to_i].pack(PACK_MAP[Fiddle::TYPE_VOIDP]) + [value.to_i].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP]) end private def dest_addr - Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_VOIDP].unpack1(PACK_MAP[Fiddle::TYPE_VOIDP]) + Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_VOIDP].unpack1(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP]) end def self.define(block) @@ -272,7 +264,7 @@ def self.define(block) # @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself, or an object that return an address with to_i define_singleton_method(:[]=) do |addr, value| value = value.to_i - Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP] = [value].pack(PACK_MAP[Fiddle::TYPE_VOIDP]) + Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP] = [value].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP]) end end end From 684353fc03afd6e7c887b65bd18f0b3aeb98101c Mon Sep 17 00:00:00 2001 From: Jeremy Bopp Date: Sun, 11 Sep 2022 09:08:14 -0500 Subject: [PATCH 047/269] [Win32] Negative length `IO#sysread` Raise `ArgumentError` in `IO#sysread` on Windows when given a negative length. Fixes [Bug #18880] --- io.c | 3 ++- test/ruby/test_io.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/io.c b/io.c index 30165e16169e70..675c0ad3c28587 100644 --- a/io.c +++ b/io.c @@ -3053,7 +3053,8 @@ static int io_setstrbuf(VALUE *str, long len) { #ifdef _WIN32 - len = (len + 1) & ~1L; /* round up for wide char */ + if (len > 0) + len = (len + 1) & ~1L; /* round up for wide char */ #endif if (NIL_P(*str)) { *str = rb_str_new(0, len); diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index d6fcf16ddd1ee2..f791b4415dd828 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -2213,6 +2213,14 @@ def test_sysread_with_not_empty_buffer end) end + def test_sysread_with_negative_length + make_tempfile {|t| + open(t.path) do |f| + assert_raise(ArgumentError) { f.sysread(-1) } + end + } + end + def test_flag make_tempfile {|t| assert_raise(ArgumentError) do From 9205b8c8010f474b2d301ccc22335c9a2c4a6cae Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 12 Sep 2022 01:23:52 +0900 Subject: [PATCH 048/269] Revert "defs/gmake.mk: no _FORTIFY_SOURCE unless optimization" This reverts commit b8c376cb9d91854fd40f6e06f07773404899b54f, as it seems no longer needed probably. --- defs/gmake.mk | 4 ---- 1 file changed, 4 deletions(-) diff --git a/defs/gmake.mk b/defs/gmake.mk index 944b9b41ed9912..c593c1c033ddad 100644 --- a/defs/gmake.mk +++ b/defs/gmake.mk @@ -44,10 +44,6 @@ TEST_DEPENDS += $(if $(filter great exam love check,$(MAKECMDGOALS)),all exts) in-srcdir := $(if $(filter-out .,$(srcdir)),$(CHDIR) $(srcdir) &&) -ifneq ($(filter -O0 -Od,$(optflags)),) -override XCFLAGS := $(filter-out -D_FORTIFY_SOURCE=%,$(XCFLAGS)) -endif - ifeq ($(if $(filter all main exts enc trans libencs libenc libtrans \ prog program ruby ruby$(EXEEXT) \ wprogram rubyw rubyw$(EXEEXT) \ From 6f8267b7f3bf6fba065c7abd9d764a801b292ff7 Mon Sep 17 00:00:00 2001 From: git Date: Mon, 12 Sep 2022 07:48:12 +0900 Subject: [PATCH 049/269] * 2022-09-12 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index f45db7190184b3..2b2aec15a4cc16 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 11 +#define RUBY_RELEASE_DAY 12 #include "ruby/version.h" #include "ruby/internal/abi.h" From 89077b4c5a8022ef563e11a7b682a4661dc9278c Mon Sep 17 00:00:00 2001 From: Maple Ong Date: Sun, 11 Sep 2022 18:50:55 -0400 Subject: [PATCH 050/269] Add comments for some peephole optimizations [ci skip] --- compile.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/compile.c b/compile.c index b495d8ced80527..45cb116983f7ba 100644 --- a/compile.c +++ b/compile.c @@ -3571,6 +3571,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (IS_INSN_ID(iobj, dup)) { if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) { LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL; + + /* + * dup + * setlocal x, y + * setlocal x, y + * => + * dup + * setlocal x, y + */ if (IS_NEXT_INSN_ID(set1, setlocal)) { set2 = set1->next; if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) && @@ -3579,6 +3588,16 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal ELEM_REMOVE(&iobj->link); } } + + /* + * dup + * setlocal x, y + * dup + * setlocal x, y + * => + * dup + * setlocal x, y + */ else if (IS_NEXT_INSN_ID(set1, dup) && IS_NEXT_INSN_ID(set1->next, setlocal)) { set2 = set1->next->next; @@ -3591,6 +3610,13 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * getlocal x, y + * dup + * setlocal x, y + * => + * dup + */ if (IS_INSN_ID(iobj, getlocal)) { LINK_ELEMENT *niobj = &iobj->link; if (IS_NEXT_INSN_ID(niobj, dup)) { @@ -3606,6 +3632,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * opt_invokebuiltin_delegate + * trace + * leave + * => + * opt_invokebuiltin_delegate_leave + * trace + * leave + */ if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) { if (IS_TRACE(iobj->link.next)) { if (IS_NEXT_INSN_ID(iobj->link.next, leave)) { @@ -3614,6 +3649,13 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * getblockparam + * branchif / branchunless + * => + * getblockparamproxy + * branchif / branchunless + */ if (IS_INSN_ID(iobj, getblockparam)) { if (IS_NEXT_INSN_ID(&iobj->link, branchif) || IS_NEXT_INSN_ID(&iobj->link, branchunless)) { iobj->insn_id = BIN(getblockparamproxy); From ed029e9bd42d7749fe22007e33c0f61d1dd3d792 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 12 Sep 2022 19:53:10 +0900 Subject: [PATCH 051/269] Autoconf 2.70 or AC_PROG_CC_C99 for earlier is checking for C99 It is no longer necessary to add it to `CFLAGS`/`CPPFLAGS` later. Furthermore, as `CPPFLAGS` is used also with C++ compiler, the option particular to C such as `-std=gnu99` results in an error. --- configure.ac | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/configure.ac b/configure.ac index e5454f117e29ea..038993538994f8 100644 --- a/configure.ac +++ b/configure.ac @@ -849,33 +849,6 @@ AS_IF([test "$GCC" = yes], [ # need lgamma_r() ]) - # ANSI (no XCFLAGS because this is C only) - AS_CASE(["$target_os"], - [solaris*], [ - # Because "-std=gnu99" affects existence of functions on Solaris, - # "-std=gnu99" will be appended to CPPFLAGS. - for ansi_options in -std=gnu99; do - RUBY_TRY_CFLAGS(${ansi_options}, [ - RUBY_APPEND_OPTIONS(CPPFLAGS, ${ansi_options}) - ], [ansi_options=]) - test "x${ansi_options}" = x || break - done - ], - [ - # ANSI (no XCFLAGS because this is C only) - rb_tmp_std_check=`echo $CC $CFLAGS $optflags $warnflags $debugflags | fgrep std= | tr -d '\015'` - AS_IF([test "x$rb_tmp_std_check" = "x"], - [ - for ansi_options in -std=gnu99; do - RUBY_TRY_CFLAGS(${ansi_options}, [ - RUBY_APPEND_OPTIONS(warnflags, ${ansi_options}) - RUBY_APPEND_OPTIONS(strict_warnflags, ${ansi_options}) - ], [ansi_options=]) - test "x${ansi_options}" = x || break - done - ]) - ]) - # suppress annoying -Wstrict-overflow warnings RUBY_TRY_CFLAGS(-fno-strict-overflow, [RUBY_APPEND_OPTION(XCFLAGS, -fno-strict-overflow)]) From 6525b6f760ccd9612c9546b0313ab1c7e4af5e66 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sat, 10 Sep 2022 19:15:49 +0200 Subject: [PATCH 052/269] Remove get_actual_encoding() and the dynamic endian detection for dummy UTF-16/UTF-32 * And simplify callers of get_actual_encoding(). * See [Feature #18949]. * See https://github.com/ruby/ruby/pull/6322#issuecomment-1242758474 --- enc/encdb.c | 2 +- enc/utf_16_32.h | 2 +- encoding.c | 9 ------ internal/encoding.h | 1 - string.c | 61 ++++++------------------------------- test/ruby/test_m17n.rb | 42 ++++++------------------- test/ruby/test_transcode.rb | 12 ++++---- 7 files changed, 27 insertions(+), 102 deletions(-) diff --git a/enc/encdb.c b/enc/encdb.c index a1936df804fbbd..8247e9ff6a08ae 100644 --- a/enc/encdb.c +++ b/enc/encdb.c @@ -17,7 +17,7 @@ #define ENC_DEFINE(name) rb_encdb_declare(name) #define ENC_SET_BASE(name, orig) rb_enc_set_base((name), (orig)) #define ENC_SET_DUMMY(name, orig) rb_enc_set_dummy(name) -#define ENC_DUMMY_UNICODE(name) rb_encdb_set_unicode(rb_enc_set_dummy(ENC_REPLICATE((name), name "BE"))) +#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name) void Init_encdb(void) diff --git a/enc/utf_16_32.h b/enc/utf_16_32.h index 9f9216d8ffaaea..4d669019bfb949 100644 --- a/enc/utf_16_32.h +++ b/enc/utf_16_32.h @@ -1,5 +1,5 @@ #include "regenc.h" /* dummy for unsupported, stateful encoding */ -#define ENC_DUMMY_UNICODE(name) ENC_REPLICATE(name, name "BE") +#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name) ENC_DUMMY_UNICODE("UTF-16"); ENC_DUMMY_UNICODE("UTF-32"); diff --git a/encoding.c b/encoding.c index 5c4dfad3f0208c..b8e7f790b8a187 100644 --- a/encoding.c +++ b/encoding.c @@ -50,7 +50,6 @@ void rb_encdb_declare(const char *name); int rb_encdb_replicate(const char *name, const char *orig); int rb_encdb_dummy(const char *name); int rb_encdb_alias(const char *alias, const char *orig); -void rb_encdb_set_unicode(int index); #pragma GCC visibility pop #endif @@ -760,14 +759,6 @@ rb_encdb_alias(const char *alias, const char *orig) return r; } -void -rb_encdb_set_unicode(int index) -{ - rb_raw_encoding *enc = (rb_raw_encoding *)rb_enc_from_index(index); - ASSUME(enc); - enc->flags |= ONIGENC_FLAG_UNICODE; -} - static void rb_enc_init(struct enc_table *enc_table) { diff --git a/internal/encoding.h b/internal/encoding.h index c48cb24b0459ee..853426a58d1daa 100644 --- a/internal/encoding.h +++ b/internal/encoding.h @@ -24,7 +24,6 @@ int rb_encdb_dummy(const char *name); void rb_encdb_declare(const char *name); void rb_enc_set_base(const char *name, const char *orig); int rb_enc_set_dummy(int index); -void rb_encdb_set_unicode(int index); PUREFUNC(int rb_data_is_encoding(VALUE obj)); #endif /* INTERNAL_ENCODING_H */ diff --git a/string.c b/string.c index c9ec827708f3cf..ac0a2acb11de9d 100644 --- a/string.c +++ b/string.c @@ -355,40 +355,10 @@ rb_debug_rstring_null_ptr(const char *func) /* symbols for [up|down|swap]case/capitalize options */ static VALUE sym_ascii, sym_turkic, sym_lithuanian, sym_fold; -static rb_encoding * -get_actual_encoding(const int encidx, VALUE str) -{ - const unsigned char *q; - - switch (encidx) { - case ENCINDEX_UTF_16: - if (RSTRING_LEN(str) < 2) break; - q = (const unsigned char *)RSTRING_PTR(str); - if (q[0] == 0xFE && q[1] == 0xFF) { - return rb_enc_get_from_index(ENCINDEX_UTF_16BE); - } - if (q[0] == 0xFF && q[1] == 0xFE) { - return rb_enc_get_from_index(ENCINDEX_UTF_16LE); - } - return rb_ascii8bit_encoding(); - case ENCINDEX_UTF_32: - if (RSTRING_LEN(str) < 4) break; - q = (const unsigned char *)RSTRING_PTR(str); - if (q[0] == 0 && q[1] == 0 && q[2] == 0xFE && q[3] == 0xFF) { - return rb_enc_get_from_index(ENCINDEX_UTF_32BE); - } - if (q[3] == 0 && q[2] == 0 && q[1] == 0xFE && q[0] == 0xFF) { - return rb_enc_get_from_index(ENCINDEX_UTF_32LE); - } - return rb_ascii8bit_encoding(); - } - return rb_enc_from_index(encidx); -} - static rb_encoding * get_encoding(VALUE str) { - return get_actual_encoding(ENCODING_GET(str), str); + return rb_enc_from_index(ENCODING_GET(str)); } static void @@ -832,21 +802,15 @@ rb_enc_cr_str_exact_copy(VALUE dest, VALUE src) } static int -enc_coderange_scan(VALUE str, rb_encoding *enc, int encidx) +enc_coderange_scan(VALUE str, rb_encoding *enc) { - if (rb_enc_mbminlen(enc) > 1 && rb_enc_dummy_p(enc) && - rb_enc_mbminlen(enc = get_actual_encoding(encidx, str)) == 1) { - return ENC_CODERANGE_BROKEN; - } - else { - return coderange_scan(RSTRING_PTR(str), RSTRING_LEN(str), enc); - } + return coderange_scan(RSTRING_PTR(str), RSTRING_LEN(str), enc); } int rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc) { - return enc_coderange_scan(str, enc, rb_enc_to_index(enc)); + return enc_coderange_scan(str, enc); } int @@ -855,9 +819,7 @@ rb_enc_str_coderange(VALUE str) int cr = ENC_CODERANGE(str); if (cr == ENC_CODERANGE_UNKNOWN) { - int encidx = ENCODING_GET(str); - rb_encoding *enc = rb_enc_from_index(encidx); - cr = enc_coderange_scan(str, enc, encidx); + cr = enc_coderange_scan(str, get_encoding(str)); ENC_CODERANGE_SET(str, cr); } return cr; @@ -1123,7 +1085,7 @@ is_enc_ascii_string(VALUE str, rb_encoding *enc) int encidx = rb_enc_to_index(enc); if (rb_enc_get_index(str) == encidx) return is_ascii_string(str); - return enc_coderange_scan(str, enc, encidx) == ENC_CODERANGE_7BIT; + return enc_coderange_scan(str, enc) == ENC_CODERANGE_7BIT; } VALUE @@ -6730,7 +6692,7 @@ VALUE rb_str_inspect(VALUE str) { int encidx = ENCODING_GET(str); - rb_encoding *enc = rb_enc_from_index(encidx), *actenc; + rb_encoding *enc = rb_enc_from_index(encidx); const char *p, *pend, *prev; char buf[CHAR_ESC_LEN + 1]; VALUE result = rb_str_buf_new(0); @@ -6745,11 +6707,6 @@ rb_str_inspect(VALUE str) p = RSTRING_PTR(str); pend = RSTRING_END(str); prev = p; - actenc = get_actual_encoding(encidx, str); - if (actenc != enc) { - enc = actenc; - if (unicode_p) unicode_p = rb_enc_unicode_p(enc); - } while (p < pend) { unsigned int c, cc; int n; @@ -9374,7 +9331,7 @@ rb_str_each_grapheme_cluster_size(VALUE str, VALUE args, VALUE eobj) { size_t grapheme_cluster_count = 0; regex_t *reg_grapheme_cluster = NULL; - rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + rb_encoding *enc = get_encoding(str); const char *ptr, *end; if (!rb_enc_unicode_p(enc)) { @@ -9402,7 +9359,7 @@ rb_str_enumerate_grapheme_clusters(VALUE str, VALUE ary) { VALUE orig = str; regex_t *reg_grapheme_cluster = NULL; - rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + rb_encoding *enc = get_encoding(str); const char *ptr0, *ptr, *end; if (!rb_enc_unicode_p(enc)) { diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb index da04ae7fa72029..28293ffffcbc55 100644 --- a/test/ruby/test_m17n.rb +++ b/test/ruby/test_m17n.rb @@ -226,38 +226,16 @@ def test_string_inspect_encoding end end - STR_WITHOUT_BOM = "\u3042".freeze - STR_WITH_BOM = "\uFEFF\u3042".freeze - bug8940 = '[ruby-core:59757] [Bug #8940]' - bug9415 = '[ruby-dev:47895] [Bug #9415]' - %w/UTF-16 UTF-32/.each do |enc| - %w/BE LE/.each do |endian| - bom = "\uFEFF".encode("#{enc}#{endian}").force_encoding(enc) - - define_method("test_utf_16_32_inspect(#{enc}#{endian})") do - s = STR_WITHOUT_BOM.encode(enc + endian) - # When a UTF-16/32 string doesn't have a BOM, - # inspect as a dummy encoding string. - assert_equal(s.dup.force_encoding("ISO-2022-JP").inspect, - s.dup.force_encoding(enc).inspect) - assert_normal_exit("#{bom.b.dump}.force_encoding('#{enc}').inspect", bug8940) - end - - define_method("test_utf_16_32_codepoints(#{enc}#{endian})") do - assert_equal([0xFEFF], bom.codepoints, bug9415) - end - - define_method("test_utf_16_32_ord(#{enc}#{endian})") do - assert_equal(0xFEFF, bom.ord, bug9415) - end - - define_method("test_utf_16_32_inspect(#{enc}#{endian}-BOM)") do - s = STR_WITH_BOM.encode(enc + endian) - # When a UTF-16/32 string has a BOM, - # inspect as a particular encoding string. - assert_equal(s.inspect, - s.dup.force_encoding(enc).inspect) - end + def test_utf_dummy_are_like_regular_dummy_encodings + [Encoding::UTF_16, Encoding::UTF_32].each do |enc| + s = "\u3042".encode("UTF-32BE") + assert_equal(s.dup.force_encoding("ISO-2022-JP").inspect, s.dup.force_encoding(enc).inspect) + s = "\x00\x00\xFE\xFF" + assert_equal(s.dup.force_encoding("ISO-2022-JP").inspect, s.dup.force_encoding(enc).inspect) + + assert_equal [0, 0, 254, 255], "\x00\x00\xFE\xFF".force_encoding(enc).codepoints + assert_equal 0, "\x00\x00\xFE\xFF".force_encoding(enc).ord + assert_equal 255, "\xFF\xFE\x00\x00".force_encoding(enc).ord end end diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb index 73737be0ad7457..24ee9b9533c8a2 100644 --- a/test/ruby/test_transcode.rb +++ b/test/ruby/test_transcode.rb @@ -2232,12 +2232,12 @@ def fallback.[](x) assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback)) end - bug8940 = '[ruby-core:57318] [Bug #8940]' - %w[UTF-32 UTF-16].each do |enc| - define_method("test_pseudo_encoding_inspect(#{enc})") do - assert_normal_exit("'aaa'.encode('#{enc}').inspect", bug8940) - assert_equal(4, 'aaa'.encode(enc).length, "should count in #{enc} with BOM") - end + def test_pseudo_encoding_inspect + s = 'aaa'.encode "UTF-16" + assert_equal '"\xFE\xFF\x00\x61\x00\x61\x00\x61"', s.inspect + + s = 'aaa'.encode "UTF-32" + assert_equal '"\x00\x00\xFE\xFF\x00\x00\x00\x61\x00\x00\x00\x61\x00\x00\x00\x61"', s.inspect end def test_encode_with_invalid_chars From acc61cbf4a6520904f8253c2ecf6b6665fe20e89 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 12 Sep 2022 14:59:45 +0200 Subject: [PATCH 053/269] Add NEWS entry for the previous commit --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 407458bbe55452..df9d7e42b0989a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -98,6 +98,10 @@ Note: We're only listing outstanding class updates. * Encoding * Encoding#replicate has been deprecated and will be removed in 3.3. [[Feature #18949]] + * The dummy `Encoding::UTF_16` and `Encoding::UTF_32` encodings no longer + try to dynamically guess the endian based on a byte order mark. + Use `Encoding::UTF_16BE/UTF_16LE` and `Encoding::UTF_32BE/UTF_32LE` instead. + This change speeds up getting the encoding of a String. [[Feature #18949]] * Enumerator * Enumerator.product has been added. Enumerator::Product is the implementation. [[Feature #18685]] From a4860e043e073e9caa2494284df4af10ca6550da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 24 Aug 2022 08:39:00 +0200 Subject: [PATCH 054/269] [rubygems/rubygems] Remove unnecessary spec sorting Specs in a SpecSet with the same name are only sorted by platform priority when they are read. No need to sort everything eagerly. https://github.com/rubygems/rubygems/commit/aeafff52df --- lib/bundler/spec_set.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index 1d0b7a460d37bd..21d57fdab4370e 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -176,7 +176,7 @@ def extract_circular_gems(error) def lookup @lookup ||= begin lookup = Hash.new {|h, k| h[k] = [] } - Index.sort_specs(@specs).reverse_each do |s| + @specs.each do |s| lookup[s.name] << s end lookup From 8d2bcc88ffe603290099d626cab40afcc834a12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 24 Aug 2022 08:51:47 +0200 Subject: [PATCH 055/269] [rubygems/rubygems] This sorting seems unnecessary too https://github.com/rubygems/rubygems/commit/823cb1fef9 --- lib/bundler/dsl.rb | 1 - lib/bundler/index.rb | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 385fdd43838b36..547db16190bc4f 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -67,7 +67,6 @@ def gemspec(opts = nil) gemspecs = Gem::Util.glob_files_in_dir("{,*}.gemspec", expanded_path).map {|g| Bundler.load_gemspec(g) }.compact gemspecs.reject! {|s| s.name != name } if name - Index.sort_specs(gemspecs) specs_by_name_and_version = gemspecs.group_by {|s| [s.name, s.version] } case specs_by_name_and_version.size diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index 00c7a9e00d7c17..c639936cb03d58 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -78,17 +78,13 @@ def unsorted_search(query) end protected :unsorted_search - def self.sort_specs(specs) + def sort_specs(specs) specs.sort_by do |s| platform_string = s.platform.to_s [s.version, platform_string == RUBY ? NULL : platform_string] end end - def sort_specs(specs) - self.class.sort_specs(specs) - end - def local_search(query) case query when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query) From 6a21d196ba774f219956ede5d4cf276b1af433c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 24 Aug 2022 10:05:20 +0200 Subject: [PATCH 056/269] [rubygems/rubygems] Let specs be sorted just once by the gem version promoter https://github.com/rubygems/rubygems/commit/3cea25a39d --- lib/bundler/gem_version_promoter.rb | 13 ++++++------- lib/bundler/index.rb | 14 +------------- lib/bundler/resolver.rb | 2 +- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb index 1ae41e2928f306..ee2c38a6ecdefa 100644 --- a/lib/bundler/gem_version_promoter.rb +++ b/lib/bundler/gem_version_promoter.rb @@ -116,15 +116,14 @@ def filter_dep_specs(spec_groups, locked_spec) end def sort_dep_specs(spec_groups, locked_spec) - return spec_groups unless locked_spec - @gem_name = locked_spec.name - @locked_version = locked_spec.version + @locked_version = locked_spec&.version + @gem_name = locked_spec&.name result = spec_groups.sort do |a, b| @a_ver = a.version @b_ver = b.version - unless @prerelease_specified[@gem_name] + unless @gem_name && @prerelease_specified[@gem_name] a_pre = @a_ver.prerelease? b_pre = @b_ver.prerelease? @@ -148,7 +147,7 @@ def sort_dep_specs(spec_groups, locked_spec) end def either_version_older_than_locked - @a_ver < @locked_version || @b_ver < @locked_version + @locked_version && (@a_ver < @locked_version || @b_ver < @locked_version) end def segments_do_not_match(level) @@ -157,7 +156,7 @@ def segments_do_not_match(level) end def unlocking_gem? - unlock_gems.empty? || unlock_gems.include?(@gem_name) + unlock_gems.empty? || (@gem_name && unlock_gems.include?(@gem_name)) end # Specific version moves can't always reliably be done during sorting @@ -165,7 +164,7 @@ def unlocking_gem? def post_sort(result) # default :major behavior in Bundler does not do this return result if major? - if unlocking_gem? + if unlocking_gem? || @locked_version.nil? result else move_version_to_end(result, @locked_version) diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index c639936cb03d58..9c48c9032bb8ef 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -57,16 +57,12 @@ def search_all(name) # Search this index's specs, and any source indexes that this index knows # about, returning all of the results. def search(query) - sort_specs(unsorted_search(query)) - end - - def unsorted_search(query) results = local_search(query) seen = results.map(&:full_name).uniq unless @sources.empty? @sources.each do |source| - source.unsorted_search(query).each do |spec| + source.search(query).each do |spec| next if seen.include?(spec.full_name) seen << spec.full_name @@ -76,14 +72,6 @@ def unsorted_search(query) results end - protected :unsorted_search - - def sort_specs(specs) - specs.sort_by do |s| - platform_string = s.platform.to_s - [s.version, platform_string == RUBY ? NULL : platform_string] - end - end def local_search(query) case query diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index fcb3812c5a797c..86b717762489a2 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -284,7 +284,7 @@ def verify_gemfile_dependencies_are_found!(requirements) end def gem_not_found_message(name, requirement, source, extra_message = "") - specs = source.specs.search(name) + specs = source.specs.search(name).sort_by {|s| [s.version, s.platform.to_s] } matching_part = name requirement_label = SharedHelpers.pretty_dependency(requirement) cache_message = begin From dce73c8616eb2e91cb0330c9b32f3edf8e771a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 24 Aug 2022 10:17:04 +0200 Subject: [PATCH 057/269] [rubygems/rubygems] Simplify instantiating the gem version promoter https://github.com/rubygems/rubygems/commit/c4e2737f2c --- lib/bundler/definition.rb | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 79369ec374cfdf..a46d7387de76c8 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -106,6 +106,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti @locked_gems = nil @locked_deps = {} @locked_specs = SpecSet.new([]) + @originally_locked_specs = @locked_specs @locked_sources = [] @locked_platforms = [] end @@ -149,18 +150,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti end def gem_version_promoter - @gem_version_promoter ||= begin - locked_specs = - if unlocking? && @locked_specs.empty? && !@lockfile_contents.empty? - # Definition uses an empty set of locked_specs to indicate all gems - # are unlocked, but GemVersionPromoter needs the locked_specs - # for conservative comparison. - Bundler::SpecSet.new(@locked_gems.specs) - else - @locked_specs - end - GemVersionPromoter.new(locked_specs, @unlock[:gems]) - end + @gem_version_promoter ||= GemVersionPromoter.new(@originally_locked_specs, @unlock[:gems]) end def resolve_only_locally! From 03d1962703a5c5eb137d25bf382861157605e6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 24 Aug 2022 10:32:38 +0200 Subject: [PATCH 058/269] [rubygems/rubygems] Remove unnecessary sorting Already done by the gem version promoter. https://github.com/rubygems/rubygems/commit/aae2cc9fe0 --- lib/bundler/resolver.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 86b717762489a2..f70e1a33955f7d 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -127,13 +127,6 @@ def search_for(dependency_proxy) results = results_for(dependency) + locked_results results = results.select {|spec| requirement_satisfied_by?(locked_requirement, nil, spec) } if locked_requirement - if !@prerelease_specified[name] && locked_results.empty? - # Move prereleases to the beginning of the list, so they're considered - # last during resolution. - pre, results = results.partition {|spec| spec.version.prerelease? } - results = pre + results - end - if results.any? results = @gem_version_promoter.sort_versions(dependency, results) From 75d90cc8e55ec662782526094b31ada1853a161b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 24 Aug 2022 10:57:35 +0200 Subject: [PATCH 059/269] [rubygems/rubygems] Use a single hash to keep track of prereleases https://github.com/rubygems/rubygems/commit/9d7bd177b0 --- lib/bundler/resolver.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index f70e1a33955f7d..161a3c0518c639 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -42,8 +42,7 @@ def start(requirements, exclude_specs: []) remove_from_candidates(spec) end - @gem_version_promoter.prerelease_specified = @prerelease_specified = {} - requirements.each {|dep| @prerelease_specified[dep.name] ||= dep.prerelease? } + requirements.each {|dep| prerelease_specified[dep.name] ||= dep.prerelease? } verify_gemfile_dependencies_are_found!(requirements) result = @resolver.resolve(requirements). @@ -214,6 +213,10 @@ def base_requirements @base.base_requirements end + def prerelease_specified + @gem_version_promoter.prerelease_specified + end + def remove_from_candidates(spec) @base.delete(spec) @@ -248,7 +251,7 @@ def amount_constrained(dependency) all - 1_000_000 else search = search_for(dependency) - search = @prerelease_specified[dependency.name] ? search.count : search.count {|s| !s.version.prerelease? } + search = prerelease_specified[dependency.name] ? search.count : search.count {|s| !s.version.prerelease? } search - all end end From 2aa8edaec701ca2d76e7be7a9b792b69574ac869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 9 Sep 2022 00:05:49 +0200 Subject: [PATCH 060/269] [rubygems/rubygems] Deduplicate results just once Instead of checking for uniqueness for every spec. https://github.com/rubygems/rubygems/commit/97d28c9665 --- lib/bundler/index.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index 9c48c9032bb8ef..d3743adb688553 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -58,19 +58,12 @@ def search_all(name) # about, returning all of the results. def search(query) results = local_search(query) - - seen = results.map(&:full_name).uniq unless @sources.empty? + return results unless @sources.any? @sources.each do |source| - source.search(query).each do |spec| - next if seen.include?(spec.full_name) - - seen << spec.full_name - results << spec - end + results.concat(source.search(query)) end - - results + results.uniq(&:full_name) end def local_search(query) From fcf861a138c39dbbf83091e5dd018cbf847a327b Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Tue, 13 Sep 2022 02:36:44 +0900 Subject: [PATCH 061/269] Try latest debug.gem --- gems/bundled_gems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/bundled_gems b/gems/bundled_gems index be637125fb5b47..9d57e838b36008 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -13,4 +13,4 @@ matrix 0.4.2 https://github.com/ruby/matrix prime 0.1.2 https://github.com/ruby/prime rbs 2.6.0 https://github.com/ruby/rbs 5ec9d53efe4bf0a97f33c3016aed430be135583a typeprof 0.21.3 https://github.com/ruby/typeprof -debug 1.6.2 https://github.com/ruby/debug e7c37486ff9579251e5d25645b8d38ec96708f12 +debug 1.6.2 https://github.com/ruby/debug 2cb44483c681f6806bf6843a8f996d3a10d0e42c From bc8bbd23ab7e2a2a008fba9c8b95a5f4b9c87743 Mon Sep 17 00:00:00 2001 From: git Date: Tue, 13 Sep 2022 09:25:25 +0900 Subject: [PATCH 062/269] * 2022-09-13 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index 2b2aec15a4cc16..9ab0bc682c0edf 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 12 +#define RUBY_RELEASE_DAY 13 #include "ruby/version.h" #include "ruby/internal/abi.h" From 9179f666fbabfe244801060d76bb95de3831c217 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 13 Sep 2022 10:17:17 +0900 Subject: [PATCH 063/269] Configure auto_request_review for YJIT team We need to use pull_request_target instead of pull_request to use secrets. pull_request_target seems to be executed from the default branch, so pushing this to master to test this. --- .github/auto_request_review.yml | 9 +++++++++ .github/workflows/auto_request_review.yml | 14 ++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .github/auto_request_review.yml create mode 100644 .github/workflows/auto_request_review.yml diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml new file mode 100644 index 00000000000000..57fc5566d1e97a --- /dev/null +++ b/.github/auto_request_review.yml @@ -0,0 +1,9 @@ +files: + 'yjit*': [team:yjit] + 'yjit/*': [team:yjit] + 'doc/yjit/*': [team:yjit] + 'bootstraptest/test_yjit*': [team:yjit] + 'test/ruby/test_yjit*': [team:yjit] + '.github/workflows/yjit*': [team:yjit] +options: + ignore_draft: true diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml new file mode 100644 index 00000000000000..0fc3e9d8524639 --- /dev/null +++ b/.github/workflows/auto_request_review.yml @@ -0,0 +1,14 @@ +name: Auto Request Review +on: + pull_request_target: + types: [opened, synchronized, ready_for_review, reopened] +jobs: + auto-request-review: + name: Auto Request Review + 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 + with: + # scope: repo:status + token: ${{ secrets.MATZBOT_GITHUB_TOKEN }} From 64cd097009f10e09e7e96abe3dce4c06bac43c29 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 13 Sep 2022 10:24:35 +0900 Subject: [PATCH 064/269] Tweak the glob for auto_request_review --- .github/auto_request_review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml index 57fc5566d1e97a..d058b6ca00c9bc 100644 --- a/.github/auto_request_review.yml +++ b/.github/auto_request_review.yml @@ -1,6 +1,6 @@ files: 'yjit*': [team:yjit] - 'yjit/*': [team:yjit] + 'yjit/**/*': [team:yjit] 'doc/yjit/*': [team:yjit] 'bootstraptest/test_yjit*': [team:yjit] 'test/ruby/test_yjit*': [team:yjit] From 2e21fef6a4d10a12ef3f99b1fb9c0741a174b3cd Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 13 Sep 2022 10:38:10 +0900 Subject: [PATCH 065/269] Drop synchronized event because it doesn't seem to be working. It's a bit annoying that synchronized doesn't work, but in many cases, opened and ready_for_review should be enough. As a last resort, you could close/reopen it. Also, the token scope needs to be public_repo instead of repo:status. --- .github/workflows/auto_request_review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml index 0fc3e9d8524639..7e163de6978893 100644 --- a/.github/workflows/auto_request_review.yml +++ b/.github/workflows/auto_request_review.yml @@ -1,7 +1,7 @@ name: Auto Request Review on: pull_request_target: - types: [opened, synchronized, ready_for_review, reopened] + types: [opened, ready_for_review, reopened] jobs: auto-request-review: name: Auto Request Review @@ -10,5 +10,5 @@ jobs: - 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 with: - # scope: repo:status + # scope: public_repo token: ${{ secrets.MATZBOT_GITHUB_TOKEN }} From 391eebfe6979f72ddff1d086dceb8e415aac0433 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 13 Sep 2022 10:48:31 +0900 Subject: [PATCH 066/269] Increase the timeout for make all on MinGW https://github.com/ruby/ruby/actions/runs/3041702828/jobs/4899106526#step:14:1219 --- .github/workflows/mingw.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 68965b845cb862..020295baa12189 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -115,7 +115,7 @@ jobs: make update-gems - name: make all - timeout-minutes: 20 + timeout-minutes: 30 run: | make From 316b44df09d3bf9dcabb8e2ce9528c72304c1bd4 Mon Sep 17 00:00:00 2001 From: "U.Nakamura" Date: Tue, 13 Sep 2022 15:43:05 +0900 Subject: [PATCH 067/269] Add GMP support for mswin --- win32/Makefile.sub | 6 ++++++ win32/configure.bat | 7 +++++++ win32/setup.mak | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 9a6bb47e102919..361af556cdab9e 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -288,6 +288,9 @@ LIBS = user32.lib advapi32.lib shell32.lib ws2_32.lib !if $(MSC_VER) >= 1400 LIBS = $(LIBS) iphlpapi.lib !endif +!if defined(USE_GMP) +LIBS = $(LIBS) gmp.lib +!endif LIBS = $(LIBS) imagehlp.lib shlwapi.lib bcrypt.lib $(EXTLIBS) !endif !if !defined(MISSING) @@ -904,6 +907,9 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub !if "$(EXTSTATIC)" == "static" #define EXTSTATIC 1 !endif +!if "$(USE_GMP)" != "" +#define USE_GMP 1 +!endif #define EXECUTABLE_EXTS $(EXECUTABLE_EXTS) #define RUBY_COREDLL "$(RT)" #define RUBY_PLATFORM "$(arch)" diff --git a/win32/configure.bat b/win32/configure.bat index a3df0bb4eb8092..7253ade28bd0ff 100755 --- a/win32/configure.bat +++ b/win32/configure.bat @@ -48,6 +48,7 @@ if "%1" == "--with-git" goto :git if "%1" == "--without-git" goto :nogit if "%1" == "--without-ext" goto :witharg if "%1" == "--without-extensions" goto :witharg +if "%1" == "--with-gmp" goto :gmp if "%opt:~0,10%" == "--without-" goto :withoutarg if "%opt:~0,7%" == "--with-" goto :witharg if "%1" == "-h" goto :help @@ -210,6 +211,12 @@ goto :loop ; echo>>confargs.tmp %1 \ shift goto :loop ; +:gmp + echo>> ~tmp~.mak "WITH_GMP=yes" \ + echo>>confargs.tmp %1=1 \ + shift + shift +goto :loop ; :witharg echo>>confargs.tmp %1=%2\ set witharg=1 diff --git a/win32/setup.mak b/win32/setup.mak index 70565973c06f18..ddad0614420858 100644 --- a/win32/setup.mak +++ b/win32/setup.mak @@ -90,6 +90,10 @@ $(BANG)endif @echo HAVE_GIT = $(HAVE_GIT)>> $(MAKEFILE) !endif +!if "$(WITH_GMP)" == "yes" + @echo>>$(MAKEFILE) USE_GMP = 1 +!endif + -osname-section-: @$(APPEND) @echo # TARGET>>$(MAKEFILE) From 6a78ae2fe27d31eca233fb0aba5457d954ea7b02 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 14 Sep 2022 07:21:19 +0900 Subject: [PATCH 068/269] Remove CODEOWNERS to switch to auto-request-review (#6365) * Remove CODEOWNERS to switch to auto-request-review * Random change to test reviews * Revert "Random change to test reviews" This reverts commit 814b10d062d82d953019ea4afadc3f8fcf638379. --- .github/CODEOWNERS | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index b7cd624b0dda95..00000000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,11 +0,0 @@ -# Lines starting with '#' are comments. -# Each line is a file pattern followed by one or more owners. -# Code owners will be automatically tagged as reviewers when a pull request is opened - -# YJIT sources and tests -yjit* @maximecb @xrxr @tenderlove @k0kubun -yjit/* @maximecb @xrxr @tenderlove @k0kubun -doc/yjit/* @maximecb @xrxr @tenderlove @k0kubun -bootstraptest/test_yjit* @maximecb @xrxr @tenderlove @k0kubun -test/ruby/test_yjit* @maximecb @xrxr @tenderlove @k0kubun -.github/workflows/yjit* @maximecb @xrxr @tenderlove @k0kubun From b876230e5cad02523105d517dce38e1bf1eec5b5 Mon Sep 17 00:00:00 2001 From: git Date: Wed, 14 Sep 2022 07:21:38 +0900 Subject: [PATCH 069/269] * 2022-09-14 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index 9ab0bc682c0edf..263f1cc8f74c54 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 13 +#define RUBY_RELEASE_DAY 14 #include "ruby/version.h" #include "ruby/internal/abi.h" From 9299db49f567025e28082be698e1f624b3e3f3ed Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Wed, 14 Sep 2022 10:15:32 +0900 Subject: [PATCH 070/269] [ruby/irb] Fix history file saving with concurrent irb sessions when history file doesn't exist If history file didn't exist when irb was started, @loaded_history_mtime would be nil. However, if the history file didn't exist before, but it exists when saving history, that means the history file was modified, and we should handle it the same way as we handle the other case where the history file was modified. Fixes #388 https://github.com/ruby/irb/commit/8d277aafcb --- lib/irb/ext/save-history.rb | 4 ++-- test/irb/test_history.rb | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb index 7acaebe36a137e..ad954c35588e87 100644 --- a/lib/irb/ext/save-history.rb +++ b/lib/irb/ext/save-history.rb @@ -107,9 +107,9 @@ def save_history raise end - if File.exist?(history_file) && @loaded_history_mtime && + if File.exist?(history_file) && File.mtime(history_file) != @loaded_history_mtime - history = history[@loaded_history_lines..-1] + history = history[@loaded_history_lines..-1] if @loaded_history_lines append_history = true end diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index 81b7fe8679eec8..38a002d3193bdf 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -158,6 +158,28 @@ def test_history_concurrent_use end end + def test_history_concurrent_use_not_present + backup_home = ENV["HOME"] + backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") + IRB.conf[:SAVE_HISTORY] = 1 + Dir.mktmpdir("test_irb_history_#{$$}") do |tmpdir| + ENV["HOME"] = tmpdir + io = TestInputMethod.new + io.class::HISTORY.clear + io.load_history + io.class::HISTORY.concat(%w"line1 line2") + + history_file = IRB.rc_file("_history") + assert !File.file?(history_file) + File.write(history_file, "line0\n") + io.save_history + assert_equal(%w"line0 line1 line2", File.read(history_file).split) + end + ensure + ENV["HOME"] = backup_home + ENV["XDG_CONFIG_HOME"] = backup_xdg_config_home + end + private def assert_history(expected_history, initial_irb_history, input) From e2b47b832f53b2fd0626774a573a22c15a933c64 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 10:42:38 +0900 Subject: [PATCH 071/269] configure.ac: Manage OPT_DIR better (#6367) * Check rpath flag earlier * Manage OPT_DIR at once --- configure.ac | 97 +++++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 54 deletions(-) diff --git a/configure.ac b/configure.ac index 038993538994f8..90a53c109f95f4 100644 --- a/configure.ac +++ b/configure.ac @@ -497,6 +497,7 @@ AS_CASE(["$target_os"], AC_DEFINE_UNQUOTED(RUBY_MSVCRT_VERSION, $RT_VER) sysconfdir= ]) + rb_cv_binary_elf=no : ${enable_shared=yes} ], [hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi @@ -606,22 +607,39 @@ RUBY_WERROR_FLAG([ cd .. && rm -fr tmp.$$.try_link ]) -: ${RPATHFLAG=''} -rpathflag='' -AS_IF([test x"${RPATHFLAG}" = x], [ - AS_CASE(["$target_os"], +: "rpath" && { + AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf, + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[ + AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"], + [.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])], + [rb_cv_binary_elf=no])]) + + rpathflag='' + AS_IF([test x"${RPATHFLAG=}" = x], [ + AS_CASE(["$target_os"], [aix*], [rpathflag='-blibpath:'], - [for rpathflag in -R "-rpath "; do + [for rpathflag in "-rpath " -R; do AS_CASE("$rpathflag", [*" "], [AS_CASE(["${linker_flag}"], [*,], [rpathflag=`echo "$rpathflag" | tr ' ' ,`])]) rpathflag="${linker_flag}${rpathflag}" RUBY_TRY_LDFLAGS([${rpathflag}.], [], [rpathflag=]) - AS_IF([test "x${rpathflag}" != x], []) + AS_IF([test "x${rpathflag}" != x], [break]) done]) -], [ - rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'` -]) + ], [ + rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'` + ]) + + AC_ARG_ENABLE(rpath, + AS_HELP_STRING([--enable-rpath], [embed run path into extension libraries. + enabled by default on ELF platforms]), + [enable_rpath=$enableval], [enable_rpath="$rb_cv_binary_elf"]) + + AS_IF([test "$enable_rpath:${RPATHFLAG}" = yes:], [ + RPATHFLAG="${rpathflag:+ ${rpathflag}%1\$-s}" + ]) + AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}]) +} RUBY_TRY_LDFLAGS(-fdeclspec, [fdeclspec=yes], [fdeclspec=no]) AS_IF([test "$fdeclspec" = yes], [ @@ -911,17 +929,25 @@ AS_CASE(["$target_cpu"], [[i[3-6]86*]], [ AS_IF([test "$rb_cv_gcc_compiler_cas" = i486], [ARCH_FLAG="-march=i486"]) ]) +OPT_DIR= AC_ARG_WITH(opt-dir, AS_HELP_STRING([--with-opt-dir=DIR-LIST], [add optional headers and libraries directories separated by $PATH_SEPARATOR]), - [ - val=`echo "$PATH_SEPARATOR$withval" | sed "s|$PATH_SEPARATOR\([[^$PATH_SEPARATOR]*]\)| -I\1/include|g;s/^ //"` - CPPFLAGS="$CPPFLAGS $val" - val=`echo "$PATH_SEPARATOR$withval" | sed "s|$PATH_SEPARATOR\([[^$PATH_SEPARATOR]*]\)| -L\1/lib${rpathflag:+ $rpathflag\\\\1/lib}|g;s/^ //"` - LDFLAGS="$LDFLAGS $val" - LDFLAGS_OPTDIR="$val" - OPT_DIR="$withval" - ], [OPT_DIR=]) + [OPT_DIR="${OPT_DIR:+$OPT_DIR$PATH_SEPARATOR}$withval"], []) + +AS_IF([test "x$OPT_DIR" != x], [ + val=`IFS="$PATH_SEPARATOR" + for dir in $OPT_DIR; do + test -z "$dir" && continue + echo x ${LIBPATHFLAG} ${RPATHFLAG} | + sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${dir}/lib${IFS}g;s${IFS}%s${IFS}${dir}/lib${IFS}g" + done | tr '\012' ' ' | sed 's/ *$//'` + LDFLAGS="${LDFLAGS:+$LDFLAGS }$val" + DLDFLAGS="${DLDFLAGS:+$DLDFLAGS }$val" + LDFLAGS_OPTDIR="$val" + CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }"`echo "$OPT_DIR" | tr "${PATH_SEPARATOR}" '\012' | + sed '/^$/d;s|^|-I|;s|$|/include|' | tr '\012' ' ' | sed 's/ *$//'` +]) test -z "${ac_env_CFLAGS_set}" -a -n "${cflags+set}" && eval CFLAGS="\"$cflags $ARCH_FLAG\"" test -z "${ac_env_CXXFLAGS_set}" -a -n "${cxxflags+set}" && eval CXXFLAGS="\"$cxxflags $ARCH_FLAG\"" @@ -1140,7 +1166,6 @@ main() ac_cv_func_getpgrp_void=no ac_cv_func_memcmp_working=yes ac_cv_lib_dl_dlopen=no - rb_cv_binary_elf=no rb_cv_negative_time_t=yes ac_cv_func_fcntl=yes ac_cv_func_flock=yes @@ -2850,12 +2875,6 @@ AC_ARG_WITH(dln-a-out, ]) ]) -AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf, -[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[ -AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"], -[.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])], -rb_cv_binary_elf=no)]) - AS_IF([test "$rb_cv_binary_elf" = yes], [ AC_DEFINE(USE_ELF) AC_CHECK_HEADERS([elf.h elf_abi.h]) @@ -2933,11 +2952,6 @@ STATIC= } : "rpath" && { - AC_ARG_ENABLE(rpath, - AS_HELP_STRING([--enable-rpath], [embed run path into extension libraries. - enabled by default on ELF platforms]), - [enable_rpath=$enableval], [enable_rpath="$rb_cv_binary_elf"]) - AS_CASE(["$target_os"], [solaris*], [ AS_IF([test "$GCC" = yes], [ : ${LDSHARED='$(CC) -shared'} @@ -2978,7 +2992,6 @@ STATIC= rb_cv_dlopen=yes], [interix*], [ : ${LDSHARED='$(CC) -shared'} XLDFLAGS="$XLDFLAGS -Wl,-E" - LIBPATHFLAG=" -L%1\$-s" rb_cv_dlopen=yes], [freebsd*|dragonfly*], [ : ${LDSHARED='$(CC) -shared'} @@ -3055,12 +3068,6 @@ STATIC= done ]) ]) - - AS_IF([test "$enable_rpath:${RPATHFLAG}" = yes:], [ - AS_IF([test "x$rpathflag" != x], [ - RPATHFLAG=" ${rpathflag}%1\$-s" - ]) - ]) } AS_IF([test "${LDSHAREDXX}" = ""], [ @@ -3077,7 +3084,6 @@ AS_IF([test "${LDSHAREDXX}" = ""], [ [ld" "*], [ ]) ]) -AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}]) AC_SUBST(LINK_SO) AC_SUBST(LIBPATHFLAG) @@ -3086,23 +3092,6 @@ AC_SUBST(LIBPATHENV, "${LIBPATHENV-LD_LIBRARY_PATH}") AC_SUBST(PRELOADENV, "${PRELOADENV-LD_PRELOAD}") AC_SUBST(TRY_LINK) -AS_IF([test "x$OPT_DIR" != x], [ - pat=`echo "${LDFLAGS_OPTDIR}" | sed ['s/[][\\.*|]/\\\\&/']` - LDFLAGS=`echo "${LDFLAGS}" | sed "s| ${pat}||"` - val=`IFS="$PATH_SEPARATOR" - for dir in $OPT_DIR; do - echo x ${LIBPATHFLAG} ${RPATHFLAG} | - sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${dir}/lib${IFS}g;s${IFS}%s${IFS}${dir}/lib${IFS}g" - done | tr '\012' ' ' | sed 's/ *$//'` - AS_IF([test x"$val" != x], [ - test x"${LDFLAGS}" = x || LDFLAGS="$LDFLAGS " - LDFLAGS="$LDFLAGS$val" - test x"${DLDFLAGS}" = x || DLDFLAGS="$DLDFLAGS " - DLDFLAGS="$DLDFLAGS$val" - ]) - LDFLAGS_OPTDIR="$val" -]) - AS_CASE(["$target_os"], [freebsd*], [ AC_CHECK_LIB([procstat], [procstat_open_sysctl]) From f863bc505cf1a976aecd0ed3cb21a3a82956953e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 11:05:43 +0900 Subject: [PATCH 072/269] [ruby/irb] Fix the error when LC_MESSAGES config value is nil https://github.com/ruby/irb/commit/6bbde84369 --- lib/irb/ext/save-history.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb index ad954c35588e87..2135184dba37a9 100644 --- a/lib/irb/ext/save-history.rb +++ b/lib/irb/ext/save-history.rb @@ -113,7 +113,7 @@ def save_history append_history = true end - open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f| + File.open(history_file, (append_history ? 'a' : 'w'), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f| hist = history.map{ |l| l.split("\n").join("\\\n") } unless append_history begin From c28a4e03400d71af95801e1e7cb4e50bebaa4075 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 11:18:58 +0900 Subject: [PATCH 073/269] Replace "Fixes"/"Fixed" in commit logs as well as vcs.rb [ci skip] Use the same regexp to replace "(#NNNN)" and "GH-NNNN" style references in vcs.rb, too. --- tool/lib/vcs.rb | 2 +- tool/sync_default_gems.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/lib/vcs.rb b/tool/lib/vcs.rb index 65ab3d4eec1739..05d95061c4f456 100644 --- a/tool/lib/vcs.rb +++ b/tool/lib/vcs.rb @@ -659,7 +659,7 @@ def format_changelog(path, arg, base_url = nil) if %r[^ +(https://github\.com/[^/]+/[^/]+/)commit/\h+\n(?=(?: +\n(?i: +Co-authored-by: .*\n)+)?(?:\n|\Z))] =~ s issue = "#{$1}pull/" - s.gsub!(/\b[Ff]ix(?:e[sd])? \K#(?=\d+)/) {issue} + s.gsub!(/\b(?:(?i:fix(?:e[sd])?) +|GH-)\K#(?=\d+\b)|\(\K#(?=\d+\))/) {issue} end s.gsub!(/ +\n/, "\n") diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 5415e0f2b4346d..1e8c086dcf1409 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -397,7 +397,7 @@ def message_filter(repo, sha) log = STDIN.read log.delete!("\r") url = "https://github.com/#{repo}" - print "[#{repo}] ", log.gsub(/\b(?i:fix) +\K#(?=\d+\b)|\(\K#(?=\d+\))|\bGH-(?=\d+\b)/) { + print "[#{repo}] ", log.gsub(/\b(?:(?i:fix(?:e[sd])?) +|GH-)\K#(?=\d+\b)|\(\K#(?=\d+\))/) { "#{url}/pull/" }.gsub(%r{(? Date: Tue, 13 Sep 2022 23:56:34 +0900 Subject: [PATCH 074/269] -undefined dynamic_lookup is obsolete --- configure.ac | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.ac b/configure.ac index 90a53c109f95f4..2595c6ee6a76ec 100644 --- a/configure.ac +++ b/configure.ac @@ -3057,7 +3057,6 @@ STATIC= AS_CASE(["$target_os"], [darwin*], [ for flag in \ - "-undefined dynamic_lookup" \ "-multiply_defined suppress" \ ; do test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`" From 035978d7be9bc3819f42f964fe6193d983cce63f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 00:06:46 +0900 Subject: [PATCH 075/269] Pass job-server FDs to bundler tests --- common.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common.mk b/common.mk index 94c5a3a9947ab4..64d771fa1c4108 100644 --- a/common.mk +++ b/common.mk @@ -1475,14 +1475,14 @@ RSPECOPTS = BUNDLER_SPECS = test-bundler: $(TEST_RUNNABLE)-test-bundler yes-test-bundler: yes-test-bundler-prepare - $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \ + $(gnumake_recursive)$(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \ --require spec_helper $(RSPECOPTS) spec/bundler/$(BUNDLER_SPECS) no-test-bundler: PARALLELRSPECOPTS = --runtime-log $(srcdir)/tmp/parallel_runtime_rspec.log test-bundler-parallel: $(TEST_RUNNABLE)-test-bundler-parallel yes-test-bundler-parallel: yes-test-bundler-prepare - $(XRUBY) \ + $(gnumake_recursive)$(XRUBY) \ -e "ARGV[-1] = File.expand_path(ARGV[-1])" \ -e "exec(*ARGV)" -- \ $(XRUBY) -I$(srcdir)/spec/bundler \ From 2e324b645e16e67c14de80ea34b1d61165045f22 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 00:58:43 +0900 Subject: [PATCH 076/269] Manage paths for bundler tests --- common.mk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common.mk b/common.mk index 64d771fa1c4108..9d7b30b32b937e 100644 --- a/common.mk +++ b/common.mk @@ -1475,7 +1475,10 @@ RSPECOPTS = BUNDLER_SPECS = test-bundler: $(TEST_RUNNABLE)-test-bundler yes-test-bundler: yes-test-bundler-prepare - $(gnumake_recursive)$(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \ + $(gnumake_recursive)$(XRUBY) \ + -r./$(arch)-fake \ + -e "exec(*ARGV)" -- \ + $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \ --require spec_helper $(RSPECOPTS) spec/bundler/$(BUNDLER_SPECS) no-test-bundler: @@ -1483,6 +1486,7 @@ PARALLELRSPECOPTS = --runtime-log $(srcdir)/tmp/parallel_runtime_rspec.log test-bundler-parallel: $(TEST_RUNNABLE)-test-bundler-parallel yes-test-bundler-parallel: yes-test-bundler-prepare $(gnumake_recursive)$(XRUBY) \ + -r./$(arch)-fake \ -e "ARGV[-1] = File.expand_path(ARGV[-1])" \ -e "exec(*ARGV)" -- \ $(XRUBY) -I$(srcdir)/spec/bundler \ From d66b29ccfbedb8d669ebfed19888f9ba22a48b75 Mon Sep 17 00:00:00 2001 From: "NARUSE, Yui" Date: Wed, 14 Sep 2022 17:27:38 +0900 Subject: [PATCH 077/269] Add link to how to build Ruby --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index da2bbbdd22dfde..35ef2d9e8989bc 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ to see the list of branches: You may also want to use https://git.ruby-lang.org/ruby.git (actual master of Ruby source) if you are a committer. +## How to build + +see [Building Ruby](doc/contributing/building_ruby.md) + ## Ruby home page https://www.ruby-lang.org/ From f2429f0af57536a354159f08aac729d6e4cdef38 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 19 Aug 2022 15:22:22 +0900 Subject: [PATCH 078/269] Expand dependency for `$(YJIT_LIBS)` Currently, miniruby is rebuild **always** when yjit is enabled, even if nothing is changed. --- yjit/yjit.mk | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/yjit/yjit.mk b/yjit/yjit.mk index 9e3155deb33195..315ad9521f44dc 100644 --- a/yjit/yjit.mk +++ b/yjit/yjit.mk @@ -5,12 +5,17 @@ CARGO_VERBOSE_0 = -q CARGO_VERBOSE_1 = CARGO_VERBOSE = $(CARGO_VERBOSE_$(V)) -# Select between different build profiles with macro substitution -.PHONY: yjit-static-lib -yjit-static-lib: yjit-static-lib-$(YJIT_SUPPORT) +YJIT_SRC_FILES = $(wildcard \ + $(top_srcdir)/yjit/Cargo.* \ + $(top_srcdir)/yjit/src/*.rs \ + $(top_srcdir)/yjit/src/*/*.rs \ + $(top_srcdir)/yjit/src/*/*/*.rs \ + $(top_srcdir)/yjit/src/*/*/*/*.rs \ + ) # YJIT_SUPPORT=yes when `configure` gets `--enable-yjit` -yjit-static-lib-yes: +ifeq ($(YJIT_SUPPORT),yes) +$(YJIT_LIBS): $(YJIT_SRC_FILES) $(ECHO) 'building Rust YJIT (release mode)' $(Q) $(RUSTC) \ --crate-name=yjit \ @@ -20,28 +25,19 @@ yjit-static-lib-yes: -C overflow-checks=on \ '--out-dir=$(CARGO_TARGET_DIR)/release/' \ $(top_srcdir)/yjit/src/lib.rs - -yjit-static-lib-no: +else ifeq ($(YJIT_SUPPORT),no) +$(YJIT_LIBS): $(ECHO) 'Error: Tried to build YJIT without configuring it first. Check `make showconfig`?' @false - -yjit-static-lib-cargo: +else ifeq ($(YJIT_SUPPORT),$(filter dev dev_nodebug stats,$(YJIT_SUPPORT))) +$(YJIT_LIBS): $(YJIT_SRC_FILES) $(ECHO) 'building Rust YJIT ($(YJIT_SUPPORT) mode)' $(Q)$(CHDIR) $(top_srcdir)/yjit && \ CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \ CARGO_TERM_PROGRESS_WHEN='never' \ $(CARGO) $(CARGO_VERBOSE) build $(CARGO_BUILD_ARGS) - -yjit-static-lib-dev: yjit-static-lib-cargo -yjit-static-lib-dev_nodebug: yjit-static-lib-cargo -yjit-static-lib-stats: yjit-static-lib-cargo - -# This PHONY prerequisite makes it so that we always run cargo. When there are -# no Rust changes on rebuild, Cargo does not touch the mtime of the static -# library and GNU make avoids relinking. $(empty) seems to be important to -# trigger rebuild each time in release mode. -$(YJIT_LIBS): yjit-static-lib - $(empty) +else +endif # Put this here instead of in common.mk to avoid breaking nmake builds # TODO: might need to move for BSD Make support From e1a9d8849403afea04ca2e11f97245590dcc1597 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 13:46:45 +0900 Subject: [PATCH 079/269] Touch libyjit.a which may be still old due to the cache --- yjit/yjit.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yjit/yjit.mk b/yjit/yjit.mk index 315ad9521f44dc..e25d7c1ce63acd 100644 --- a/yjit/yjit.mk +++ b/yjit/yjit.mk @@ -25,6 +25,7 @@ $(YJIT_LIBS): $(YJIT_SRC_FILES) -C overflow-checks=on \ '--out-dir=$(CARGO_TARGET_DIR)/release/' \ $(top_srcdir)/yjit/src/lib.rs + touch $@ else ifeq ($(YJIT_SUPPORT),no) $(YJIT_LIBS): $(ECHO) 'Error: Tried to build YJIT without configuring it first. Check `make showconfig`?' @@ -36,6 +37,7 @@ $(YJIT_LIBS): $(YJIT_SRC_FILES) CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \ CARGO_TERM_PROGRESS_WHEN='never' \ $(CARGO) $(CARGO_VERBOSE) build $(CARGO_BUILD_ARGS) + touch $@ else endif From 075df960c93cb2d3ebe308f67871f39565b4426e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 15:23:04 +0900 Subject: [PATCH 080/269] Add comments to touch libyjit --- yjit/yjit.mk | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/yjit/yjit.mk b/yjit/yjit.mk index e25d7c1ce63acd..81441a7925a607 100644 --- a/yjit/yjit.mk +++ b/yjit/yjit.mk @@ -13,6 +13,12 @@ YJIT_SRC_FILES = $(wildcard \ $(top_srcdir)/yjit/src/*/*/*/*.rs \ ) +# Because of Cargo cache, if the actual binary is not changed from the +# previous build, the mtime is preserved as the cached file. +# This means the target is not updated actually, and it will need to +# rebuild at the next build. +YJIT_LIB_TOUCH = touch $@ + # YJIT_SUPPORT=yes when `configure` gets `--enable-yjit` ifeq ($(YJIT_SUPPORT),yes) $(YJIT_LIBS): $(YJIT_SRC_FILES) @@ -25,7 +31,7 @@ $(YJIT_LIBS): $(YJIT_SRC_FILES) -C overflow-checks=on \ '--out-dir=$(CARGO_TARGET_DIR)/release/' \ $(top_srcdir)/yjit/src/lib.rs - touch $@ + $(YJIT_LIB_TOUCH) else ifeq ($(YJIT_SUPPORT),no) $(YJIT_LIBS): $(ECHO) 'Error: Tried to build YJIT without configuring it first. Check `make showconfig`?' @@ -37,7 +43,7 @@ $(YJIT_LIBS): $(YJIT_SRC_FILES) CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \ CARGO_TERM_PROGRESS_WHEN='never' \ $(CARGO) $(CARGO_VERBOSE) build $(CARGO_BUILD_ARGS) - touch $@ + $(YJIT_LIB_TOUCH) else endif From f512df73986c74e2f4bd65ca642879a0618da213 Mon Sep 17 00:00:00 2001 From: Kenta Murata <3959+mrkn@users.noreply.github.com> Date: Wed, 14 Sep 2022 22:17:39 +0900 Subject: [PATCH 081/269] configure.ac: Add --with-gmp-dir (#6366) Add the `--with-gmp-dir` to specify the prefix directory of GMP. The`--without-gmp` option is preserved for convenience. It can be used to force to reject using GMP even if the `--with-gmp-dir` option is specified. --- configure.ac | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 2595c6ee6a76ec..92b1a6a6966a2b 100644 --- a/configure.ac +++ b/configure.ac @@ -930,6 +930,15 @@ AS_CASE(["$target_cpu"], [[i[3-6]86*]], [ ]) OPT_DIR= +AC_ARG_WITH([gmp-dir], + AS_HELP_STRING([--with-gmp-dir=DIR], + [specify the prefix directory where gmp is installed]), + [OPT_DIR="$withval"], []) +AC_ARG_WITH([gmp], + [AS_HELP_STRING([--without-gmp], + [disable GNU GMP to accelerate Bignum operations])], + [OPT_DIR=], [with_gmp=yes]) + AC_ARG_WITH(opt-dir, AS_HELP_STRING([--with-opt-dir=DIR-LIST], [add optional headers and libraries directories separated by $PATH_SEPARATOR]), @@ -1319,11 +1328,6 @@ AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [ ]) RUBY_UNIVERSAL_CHECK_HEADER([x86_64, i386], x86intrin.h) -AC_ARG_WITH([gmp], - [AS_HELP_STRING([--without-gmp], - [disable GNU GMP to accelerate Bignum operations])], - [], - [with_gmp=yes]) AS_IF([test "x$with_gmp" != xno], [AC_CHECK_HEADERS(gmp.h) AS_IF([test "x$ac_cv_header_gmp_h" != xno], From 960db13c472773926dc8c3dec8ba62d60aaa77f9 Mon Sep 17 00:00:00 2001 From: "S.H" Date: Wed, 14 Sep 2022 23:10:21 +0900 Subject: [PATCH 082/269] Reuse `opt_arg_append` function --- parse.y | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/parse.y b/parse.y index 38f7690ccc01d7..4f0aa6a6cedacf 100644 --- a/parse.y +++ b/parse.y @@ -10561,13 +10561,7 @@ static NODE * kwd_append(NODE *kwlist, NODE *kw) { if (kwlist) { - NODE *kws = kwlist; - kws->nd_loc.end_pos = kw->nd_loc.end_pos; - while (kws->nd_next) { - kws = kws->nd_next; - kws->nd_loc.end_pos = kw->nd_loc.end_pos; - } - kws->nd_next = kw; + opt_arg_append(kwlist, kw); } return kwlist; } From 2e25b85a7e4268676fcdf17b5975c2fd60066ce1 Mon Sep 17 00:00:00 2001 From: Kenta Murata <3959+mrkn@users.noreply.github.com> Date: Wed, 14 Sep 2022 21:20:32 +0900 Subject: [PATCH 083/269] configure.ac: Apply suggestions from code review in #6366 Co-authored-by: Nobuyoshi Nakada --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 92b1a6a6966a2b..43f1541f3b6fcc 100644 --- a/configure.ac +++ b/configure.ac @@ -933,11 +933,11 @@ OPT_DIR= AC_ARG_WITH([gmp-dir], AS_HELP_STRING([--with-gmp-dir=DIR], [specify the prefix directory where gmp is installed]), - [OPT_DIR="$withval"], []) + [OPT_DIR="${OPT_DIR:+$OPT_DIR$PATH_SEPARATOR}$withval"], []) AC_ARG_WITH([gmp], [AS_HELP_STRING([--without-gmp], [disable GNU GMP to accelerate Bignum operations])], - [OPT_DIR=], [with_gmp=yes]) + [], [with_gmp=yes]) AC_ARG_WITH(opt-dir, AS_HELP_STRING([--with-opt-dir=DIR-LIST], From 8f37e9c91814357f79911e208ef4d0d56dfa9433 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 14 Sep 2022 23:27:52 +0900 Subject: [PATCH 084/269] YJIT: Add Opnd#with_num_bits to use only 8 bits (#6359) * YJIT: Add Opnd#sub_opnd to use only 8 bits * Add with_num_bits and let arm64_split use it * Add another assertion to with_num_bits * Use only with_num_bits --- yjit/src/asm/arm64/inst/load_store.rs | 14 ++++++++++++++ yjit/src/asm/arm64/mod.rs | 16 ++++++++++++++++ yjit/src/asm/arm64/opnd.rs | 6 ++---- yjit/src/asm/x86_64/mod.rs | 5 +---- yjit/src/backend/arm64/mod.rs | 15 +++++++++++++-- yjit/src/backend/ir.rs | 18 ++++++++++++++---- yjit/src/codegen.rs | 3 +-- 7 files changed, 61 insertions(+), 16 deletions(-) diff --git a/yjit/src/asm/arm64/inst/load_store.rs b/yjit/src/asm/arm64/inst/load_store.rs index ea42f2d17f2d87..e877c6de774e9e 100644 --- a/yjit/src/asm/arm64/inst/load_store.rs +++ b/yjit/src/asm/arm64/inst/load_store.rs @@ -2,6 +2,7 @@ use super::super::arg::truncate_imm; /// The size of the operands being operated on. enum Size { + Size8 = 0b00, Size32 = 0b10, Size64 = 0b11, } @@ -81,6 +82,12 @@ impl LoadStore { Self { rt, rn, idx: Index::None, imm9, opc: Opc::LDR, size: num_bits.into() } } + /// LDURB (load register, byte, unscaled) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDURB--Load-Register-Byte--unscaled--?lang=en + pub fn ldurb(rt: u8, rn: u8, imm9: i16) -> Self { + Self { rt, rn, idx: Index::None, imm9, opc: Opc::LDR, size: Size::Size8 } + } + /// LDURSW (load register, unscaled, signed) /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDURSW--Load-Register-Signed-Word--unscaled--?lang=en pub fn ldursw(rt: u8, rn: u8, imm9: i16) -> Self { @@ -157,6 +164,13 @@ mod tests { assert_eq!(0xf8400020, result); } + #[test] + fn test_ldurb() { + let inst = LoadStore::ldurb(0, 1, 0); + let result: u32 = inst.into(); + assert_eq!(0x38400020, result); + } + #[test] fn test_ldur_with_imm() { let inst = LoadStore::ldur(0, 1, 123, 64); diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs index 420151c6d18a17..d97452a0457538 100644 --- a/yjit/src/asm/arm64/mod.rs +++ b/yjit/src/asm/arm64/mod.rs @@ -507,6 +507,22 @@ pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { cb.write_bytes(&bytes); } +/// LDURB - load a byte from memory, zero-extend it, and write it to a register +pub fn ldurb(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rt, rn) { + (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => { + assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size"); + assert!(rt.num_bits == 8, "Expected registers to have size 8"); + assert!(mem_disp_fits_bits(rn.disp), "Expected displacement to be 9 bits or less"); + + LoadStore::ldurb(rt.reg_no, rn.base_reg_no, rn.disp as i16).into() + }, + _ => panic!("Invalid operands for LDURB") + }; + + cb.write_bytes(&bytes); +} + /// LDURSW - load a 32-bit memory address into a register and sign-extend it pub fn ldursw(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt, rn) { diff --git a/yjit/src/asm/arm64/opnd.rs b/yjit/src/asm/arm64/opnd.rs index c89481fb03db2f..52b2a84637f166 100644 --- a/yjit/src/asm/arm64/opnd.rs +++ b/yjit/src/asm/arm64/opnd.rs @@ -12,10 +12,8 @@ pub struct A64Reg } impl A64Reg { - pub fn sub_reg(&self, num_bits: u8) -> Self { - assert!(num_bits == 32 || num_bits == 64); - assert!(num_bits <= self.num_bits); - + pub fn with_num_bits(&self, num_bits: u8) -> Self { + assert!(num_bits == 8 || num_bits == 16 || num_bits == 32 || num_bits == 64); Self { num_bits, reg_no: self.reg_no } } } diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs index 3f865b82a52fe4..d310e3bf129137 100644 --- a/yjit/src/asm/x86_64/mod.rs +++ b/yjit/src/asm/x86_64/mod.rs @@ -89,16 +89,13 @@ pub enum X86Opnd } impl X86Reg { - pub fn sub_reg(&self, num_bits: u8) -> Self { + pub fn with_num_bits(&self, num_bits: u8) -> Self { assert!( num_bits == 8 || num_bits == 16 || num_bits == 32 || num_bits == 64 ); - - assert!(num_bits <= self.num_bits); - Self { num_bits, reg_type: self.reg_type, diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 446332788a052f..9d19c651145206 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -140,7 +140,14 @@ impl Assembler Opnd::Reg(_) | Opnd::InsnOut { .. } => opnd, Opnd::Mem(_) => { let split_opnd = split_memory_address(asm, opnd); - asm.load(split_opnd) + let out_opnd = asm.load(split_opnd); + // Many Arm insns support only 32-bit or 64-bit operands. asm.load with fewer + // bits zero-extends the value, so it's safe to recognize it as a 32-bit value. + if out_opnd.rm_num_bits() < 32 { + out_opnd.with_num_bits(32).unwrap() + } else { + out_opnd + } }, _ => asm.load(opnd) } @@ -747,7 +754,11 @@ impl Assembler emit_load_value(cb, out.into(), imm as u64); }, Opnd::Mem(_) => { - ldur(cb, out.into(), opnd.into()); + match opnd.rm_num_bits() { + 64 | 32 => ldur(cb, out.into(), opnd.into()), + 8 => ldurb(cb, out.into(), opnd.into()), + num_bits => panic!("unexpected num_bits: {}", num_bits) + }; }, Opnd::Value(value) => { // We dont need to check if it's a special const diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index ee6499ff64b069..609ca8eaf4d8cf 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -151,6 +151,16 @@ impl Opnd } } + pub fn with_num_bits(&self, num_bits: u8) -> Option { + assert!(num_bits == 8 || num_bits == 16 || num_bits == 32 || num_bits == 64); + match *self { + Opnd::Reg(reg) => Some(Opnd::Reg(reg.with_num_bits(num_bits))), + Opnd::Mem(Mem { base, disp, .. }) => Some(Opnd::Mem(Mem { base, disp, num_bits })), + Opnd::InsnOut { idx, .. } => Some(Opnd::InsnOut { idx, num_bits }), + _ => None, + } + } + /// Get the size in bits for register/memory operands. pub fn rm_num_bits(&self) -> u8 { self.num_bits().unwrap() @@ -1052,21 +1062,21 @@ impl Assembler // output operand on this instruction because the live range // extends beyond the index of the instruction. let out = insn.out_opnd_mut().unwrap(); - *out = Opnd::Reg(out_reg.unwrap().sub_reg(out_num_bits)); + *out = Opnd::Reg(out_reg.unwrap().with_num_bits(out_num_bits)); } // Replace InsnOut operands by their corresponding register let mut opnd_iter = insn.opnd_iter_mut(); while let Some(opnd) = opnd_iter.next() { match *opnd { - Opnd::InsnOut { idx, .. } => { - *opnd = *asm.insns[idx].out_opnd().unwrap(); + Opnd::InsnOut { idx, num_bits } => { + *opnd = (*asm.insns[idx].out_opnd().unwrap()).with_num_bits(num_bits).unwrap(); }, Opnd::Mem(Mem { base: MemBase::InsnOut(idx), disp, num_bits }) => { let base = MemBase::Reg(asm.insns[idx].out_opnd().unwrap().unwrap_reg().reg_no); *opnd = Opnd::Mem(Mem { base, disp, num_bits }); } - _ => {}, + _ => {}, } } diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 1c48a1b040ce5a..ca9ec655d1d9e6 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -3477,8 +3477,7 @@ fn jit_guard_known_klass( asm.comment("guard object is static symbol"); assert!(RUBY_SPECIAL_SHIFT == 8); - let flag_bits = asm.and(obj_opnd, Opnd::UImm(0xf)); - asm.cmp(flag_bits, Opnd::UImm(RUBY_SYMBOL_FLAG as u64)); + asm.cmp(obj_opnd.with_num_bits(8).unwrap(), Opnd::UImm(RUBY_SYMBOL_FLAG as u64)); jit_chain_guard(JCC_JNE, jit, ctx, asm, ocb, max_chain_depth, side_exit); ctx.upgrade_opnd_type(insn_opnd, Type::ImmSymbol); } From 758a1d730230ad0f4adfd7681c1fe4c8ac398bde Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Wed, 14 Sep 2022 10:32:22 -0400 Subject: [PATCH 085/269] Initial support for VM_CALL_ARGS_SPLAT (#6341) * Initial support for VM_CALL_ARGS_SPLAT This implements support for calls with splat (*) for some methods. In benchmarks this made very little difference for most benchmarks, but a large difference for binarytrees. Looking at side exits, many benchmarks now don't exit for splat, but exit for some other reason. Binarytrees however had a number of calls that used splat args that are now much faster. In my non-scientific benchmarking this made splat args performance on par with not using splat args at all. * Fix wording and whitespace Co-authored-by: Maxime Chevalier-Boisvert * Get rid of side_effect reassignment Co-authored-by: Maxime Chevalier-Boisvert --- yjit.c | 6 + yjit/bindgen/src/main.rs | 1 + yjit/src/codegen.rs | 200 +++++++++++++++++++++++++++------ yjit/src/cruby.rs | 2 + yjit/src/cruby_bindings.inc.rs | 3 + yjit/src/stats.rs | 7 +- 6 files changed, 182 insertions(+), 37 deletions(-) diff --git a/yjit.c b/yjit.c index a8341910706e9d..facddcca0ad921 100644 --- a/yjit.c +++ b/yjit.c @@ -603,6 +603,12 @@ rb_get_iseq_flags_has_rest(const rb_iseq_t *iseq) return iseq->body->param.flags.has_rest; } +bool +rb_get_iseq_flags_ruby2_keywords(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.ruby2_keywords; +} + bool rb_get_iseq_flags_has_block(const rb_iseq_t *iseq) { diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index eaf030a1ded6ab..294da213786f81 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -342,6 +342,7 @@ fn main() { .allowlist_function("rb_get_iseq_flags_has_kwrest") .allowlist_function("rb_get_iseq_flags_has_block") .allowlist_function("rb_get_iseq_flags_has_accepts_no_kwarg") + .allowlist_function("rb_get_iseq_flags_ruby2_keywords") .allowlist_function("rb_get_iseq_body_local_table_size") .allowlist_function("rb_get_iseq_body_param_keyword") .allowlist_function("rb_get_iseq_body_param_size") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index ca9ec655d1d9e6..f13505365e0f5a 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -482,7 +482,7 @@ fn gen_outlined_exit(exit_pc: *mut VALUE, ctx: &Context, ocb: &mut OutlinedCb) - // // No guards change the logic for reconstructing interpreter state at the // moment, so there is one unique side exit for each context. Note that -// it's incorrect to jump to the side exit after any ctx stack push/pop operations +// it's incorrect to jump to the side exit after any ctx stack push operations // since they change the logic required for reconstructing interpreter state. fn get_side_exit(jit: &mut JITState, ocb: &mut OutlinedCb, ctx: &Context) -> CodePtr { match jit.side_exit_for_pc { @@ -1427,7 +1427,7 @@ fn gen_expandarray( return KeepCompiling; } - // Move the array from the stack into REG0 and check that it's an array. + // Move the array from the stack and check that it's an array. let array_reg = asm.load(array_opnd); guard_object_is_heap( asm, @@ -3911,6 +3911,12 @@ fn gen_send_cfunc( ) -> CodegenStatus { let cfunc = unsafe { get_cme_def_body_cfunc(cme) }; let cfunc_argc = unsafe { get_mct_argc(cfunc) }; + let mut argc = argc; + + // 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 { @@ -3925,25 +3931,6 @@ fn gen_send_cfunc( unsafe { get_cikw_keyword_len(kw_arg) } }; - // Number of args which will be passed through to the callee - // This is adjusted by the kwargs being combined into a hash. - let passed_argc = if kw_arg.is_null() { - argc - } else { - argc - kw_arg_num + 1 - }; - - // If the argument count doesn't match - if cfunc_argc >= 0 && cfunc_argc != passed_argc { - gen_counter_incr!(asm, send_cfunc_argc_mismatch); - return CantCompile; - } - - // Don't JIT functions that need C stack arguments for now - if cfunc_argc >= 0 && passed_argc + 1 > (C_ARG_OPNDS.len() as i32) { - gen_counter_incr!(asm, send_cfunc_toomany_args); - return CantCompile; - } if c_method_tracing_currently_enabled(jit) { // Don't JIT if tracing c_call or c_return @@ -3964,9 +3951,6 @@ fn gen_send_cfunc( } } - // Create a side-exit to fall back to the interpreter - let side_exit = get_side_exit(jit, ocb, ctx); - // Check for interrupts gen_check_ints(asm, side_exit); @@ -3978,6 +3962,38 @@ fn gen_send_cfunc( asm.cmp(CFP, stack_limit); asm.jbe(counted_exit!(ocb, side_exit, send_se_cf_overflow).into()); + if flags & VM_CALL_ARGS_SPLAT != 0 { + if cfunc_argc > 0 { + // push_splat_args does a ctx.stack_push so we can no longer side exit + argc = push_splat_args(argc, ctx, asm, ocb, side_exit, cfunc_argc as u32); + } else { + // This is a variadic c function and we'd need to pop + // each of the elements off the array, but the array may be dynamically sized + gen_counter_incr!(asm, send_args_splat_variadic); + return CantCompile + } + } + + // Number of args which will be passed through to the callee + // This is adjusted by the kwargs being combined into a hash. + let passed_argc = if kw_arg.is_null() { + argc + } else { + argc - kw_arg_num + 1 + }; + + // If the argument count doesn't match + if cfunc_argc >= 0 && cfunc_argc != passed_argc { + gen_counter_incr!(asm, send_cfunc_argc_mismatch); + return CantCompile; + } + + // Don't JIT functions that need C stack arguments for now + if cfunc_argc >= 0 && passed_argc + 1 > (C_ARG_OPNDS.len() as i32) { + gen_counter_incr!(asm, send_cfunc_toomany_args); + return CantCompile; + } + // Points to the receiver operand on the stack let recv = ctx.stack_opnd(argc); @@ -4152,6 +4168,85 @@ fn gen_return_branch( } } + +/// Pushes arguments from an array to the stack that are passed with a splat (i.e. *args) +/// It optimistically compiles to a static size that is the exact number of arguments +/// needed for the function. +fn push_splat_args(argc: i32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut OutlinedCb, side_exit: CodePtr, num_params: u32) -> i32 { + + let mut argc = argc; + + asm.comment("push_splat_args"); + + let array_opnd = ctx.stack_opnd(0); + + argc = argc - 1; + + let array_reg = asm.load(array_opnd); + guard_object_is_heap( + asm, + array_reg, + counted_exit!(ocb, side_exit, send_splat_not_array), + ); + guard_object_is_array( + asm, + array_reg, + counted_exit!(ocb, side_exit, send_splat_not_array), + ); + + // Pull out the embed flag to check if it's an embedded array. + let flags_opnd = Opnd::mem((8 * SIZEOF_VALUE) as u8, array_reg, RUBY_OFFSET_RBASIC_FLAGS); + + // Get the length of the array + let emb_len_opnd = asm.and(flags_opnd, (RARRAY_EMBED_LEN_MASK as u64).into()); + let emb_len_opnd = asm.rshift(emb_len_opnd, (RARRAY_EMBED_LEN_SHIFT as u64).into()); + + // Conditionally move the length of the heap array + let flags_opnd = Opnd::mem((8 * SIZEOF_VALUE) as u8, array_reg, RUBY_OFFSET_RBASIC_FLAGS); + asm.test(flags_opnd, (RARRAY_EMBED_FLAG as u64).into()); + let array_len_opnd = Opnd::mem( + (8 * size_of::()) as u8, + asm.load(array_opnd), + RUBY_OFFSET_RARRAY_AS_HEAP_LEN, + ); + let array_len_opnd = asm.csel_nz(emb_len_opnd, array_len_opnd); + + let required_args = num_params - argc as u32; + + // Only handle the case where the number of values in the array is equal to the number requested + asm.cmp(array_len_opnd, required_args.into()); + asm.jne(counted_exit!(ocb, side_exit, send_splatarray_rhs_too_small).into()); + + let array_opnd = ctx.stack_pop(1); + + if required_args > 0 { + + // Load the address of the embedded array + // (struct RArray *)(obj)->as.ary + let array_reg = asm.load(array_opnd); + let ary_opnd = asm.lea(Opnd::mem((8 * SIZEOF_VALUE) as u8, array_reg, RUBY_OFFSET_RARRAY_AS_ARY)); + + // Conditionally load the address of the heap array + // (struct RArray *)(obj)->as.heap.ptr + let flags_opnd = Opnd::mem((8 * SIZEOF_VALUE) as u8, array_reg, RUBY_OFFSET_RBASIC_FLAGS); + asm.test(flags_opnd, Opnd::UImm(RARRAY_EMBED_FLAG as u64)); + let heap_ptr_opnd = Opnd::mem( + (8 * size_of::()) as u8, + asm.load(array_opnd), + RUBY_OFFSET_RARRAY_AS_HEAP_PTR, + ); + + let ary_opnd = asm.csel_nz(ary_opnd, heap_ptr_opnd); + + for i in (0..required_args as i32) { + let top = ctx.stack_push(Type::Unknown); + asm.mov(top, Opnd::mem(64, ary_opnd, i * (SIZEOF_VALUE as i32))); + argc += 1; + } + } + argc +} + fn gen_send_iseq( jit: &mut JITState, ctx: &mut Context, @@ -4165,6 +4260,11 @@ fn gen_send_iseq( let iseq = unsafe { get_def_iseq_ptr((*cme).def) }; 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); + // When you have keyword arguments, there is an extra object that gets // placed on the stack the represents a bitmap of the keywords that were not // specified at the call site. We need to keep track of the fact that this @@ -4190,6 +4290,18 @@ fn gen_send_iseq( return CantCompile; } + // In order to handle backwards compatibility between ruby 3 and 2 + // ruby2_keywords was introduced. It is called only on methods + // with splat and changes they way they handle them. + // We are just going to not compile these. + // https://www.rubydoc.info/stdlib/core/Proc:ruby2_keywords + if unsafe { + get_iseq_flags_ruby2_keywords(jit.iseq) + } { + gen_counter_incr!(asm, send_iseq_ruby2_keywords); + return CantCompile; + } + // If we have keyword arguments being passed to a callee that only takes // positionals, then we need to allocate a hash. For now we're going to // call that too complex and bail. @@ -4221,6 +4333,16 @@ fn gen_send_iseq( } } + + if flags & VM_CALL_ARGS_SPLAT != 0 && flags & VM_CALL_ZSUPER != 0 { + // zsuper methods are super calls without any arguments. + // They are also marked as splat, but don't actually have an array + // they pull arguments from, instead we need to change to call + // a different method with the current stack. + gen_counter_incr!(asm, send_iseq_zsuper); + return CantCompile; + } + let mut start_pc_offset = 0; let required_num = unsafe { get_iseq_body_param_lead_num(iseq) }; @@ -4238,6 +4360,11 @@ fn gen_send_iseq( let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) }; let opts_missing: i32 = opt_num - opts_filled; + if opt_num > 0 && flags & VM_CALL_ARGS_SPLAT != 0 { + gen_counter_incr!(asm, send_iseq_complex_callee); + return CantCompile; + } + if opts_filled < 0 || opts_filled > opt_num { gen_counter_incr!(asm, send_iseq_arity_error); return CantCompile; @@ -4334,8 +4461,7 @@ fn gen_send_iseq( // Number of locals that are not parameters let num_locals = unsafe { get_iseq_body_local_table_size(iseq) as i32 } - (num_params as i32); - // Create a side-exit to fall back to the interpreter - let side_exit = get_side_exit(jit, ocb, ctx); + // Check for interrupts gen_check_ints(asm, side_exit); @@ -4384,6 +4510,11 @@ fn gen_send_iseq( asm.cmp(CFP, stack_limit); asm.jbe(counted_exit!(ocb, side_exit, send_se_cf_overflow).into()); + // push_splat_args does a ctx.stack_push so we can no longer side exit + if flags & VM_CALL_ARGS_SPLAT != 0 { + argc = push_splat_args(argc, ctx, asm, ocb, side_exit, num_params); + } + if doing_kw_call { // Here we're calling a method with keyword arguments and specifying // keyword arguments at this call site. @@ -4787,12 +4918,7 @@ fn gen_send_general( return CantCompile; } - // Don't JIT calls that aren't simple - // Note, not using VM_CALL_ARGS_SIMPLE because sometimes we pass a block. - if flags & VM_CALL_ARGS_SPLAT != 0 { - gen_counter_incr!(asm, send_args_splat); - return CantCompile; - } + if flags & VM_CALL_ARGS_BLOCKARG != 0 { gen_counter_incr!(asm, send_block_arg); return CantCompile; @@ -4865,6 +4991,11 @@ fn gen_send_general( // To handle the aliased method case (VM_METHOD_TYPE_ALIAS) loop { let def_type = unsafe { get_cme_def_type(cme) }; + if flags & VM_CALL_ARGS_SPLAT != 0 && (def_type != VM_METHOD_TYPE_ISEQ && def_type != VM_METHOD_TYPE_CFUNC) { + // We can't handle splat calls to non-iseq methods + return CantCompile; + } + match def_type { VM_METHOD_TYPE_ISEQ => { return gen_send_iseq(jit, ctx, asm, ocb, ci, cme, block, argc); @@ -5087,10 +5218,7 @@ fn gen_invokesuper( // Don't JIT calls that aren't simple // Note, not using VM_CALL_ARGS_SIMPLE because sometimes we pass a block. - if ci_flags & VM_CALL_ARGS_SPLAT != 0 { - gen_counter_incr!(asm, send_args_splat); - return CantCompile; - } + if ci_flags & VM_CALL_KWARG != 0 { gen_counter_incr!(asm, send_keywords); return CantCompile; diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 2f823e1b615354..25149ab7304d45 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -164,6 +164,7 @@ pub use rb_get_iseq_body_stack_max as get_iseq_body_stack_max; pub use rb_get_iseq_flags_has_opt as get_iseq_flags_has_opt; pub use rb_get_iseq_flags_has_kw as get_iseq_flags_has_kw; pub use rb_get_iseq_flags_has_rest as get_iseq_flags_has_rest; +pub use rb_get_iseq_flags_ruby2_keywords as get_iseq_flags_ruby2_keywords; pub use rb_get_iseq_flags_has_post as get_iseq_flags_has_post; pub use rb_get_iseq_flags_has_kwrest as get_iseq_flags_has_kwrest; pub use rb_get_iseq_flags_has_block as get_iseq_flags_has_block; @@ -619,6 +620,7 @@ mod manual_defs { pub const VM_CALL_KWARG: u32 = 1 << VM_CALL_KWARG_bit; 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; // 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 98699987884bf0..e3dbdc0d4b7287 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1145,6 +1145,9 @@ extern "C" { extern "C" { pub fn rb_get_iseq_flags_has_rest(iseq: *const rb_iseq_t) -> bool; } +extern "C" { + pub fn rb_get_iseq_flags_ruby2_keywords(iseq: *const rb_iseq_t) -> bool; +} extern "C" { pub fn rb_get_iseq_flags_has_block(iseq: *const rb_iseq_t) -> bool; } diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index 4843cecf92ab50..56aa0b408122dc 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -164,7 +164,8 @@ make_counters! { send_keywords, send_kw_splat, - send_args_splat, + send_args_splat_super, + send_iseq_zsuper, send_block_arg, send_ivar_set_method, send_zsuper_method, @@ -192,6 +193,10 @@ make_counters! { send_getter_arity, send_se_cf_overflow, send_se_protected_check_failed, + send_splatarray_rhs_too_small, + send_splat_not_array, + send_args_splat_variadic, + send_iseq_ruby2_keywords, traced_cfunc_return, From b6a9e683917745df2822a611fce64df9ae8090a7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 23:08:24 +0900 Subject: [PATCH 086/269] Add noarch-fake.rb target `yes-fake` depends on it when `arch=noarch` is given, but the rule to generate it from fake.rb.in is ignored now. --- common.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common.mk b/common.mk index 9d7b30b32b937e..44f5190b6f87df 100644 --- a/common.mk +++ b/common.mk @@ -783,6 +783,9 @@ $(arch:noarch=ignore)-fake.rb: $(srcdir)/template/fake.rb.in $(tooldir)/generic_ $(BOOTSTRAPRUBY) "$(tooldir)/generic_erb.rb" -o $@ "$(srcdir)/template/fake.rb.in" \ i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)" +noarch-fake.rb: # prerequisite of yes-fake + touch $@ + btest: $(TEST_RUNNABLE)-btest no-btest: PHONY yes-btest: yes-fake miniruby$(EXEEXT) PHONY From 0953ea64a2195de8c2eb5a89172e1d8f486e1d2d Mon Sep 17 00:00:00 2001 From: git Date: Thu, 15 Sep 2022 00:32:30 +0900 Subject: [PATCH 087/269] * 2022-09-15 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index 263f1cc8f74c54..d14f810366489b 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 14 +#define RUBY_RELEASE_DAY 15 #include "ruby/version.h" #include "ruby/internal/abi.h" From fb2e9053b3697b856785c0478762291e43e0fc55 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 15 Sep 2022 00:47:46 +0900 Subject: [PATCH 088/269] [TravisCI] Make gems/src directory writable --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6875c766a9f00f..be39cdfbc2e594 100644 --- a/.travis.yml +++ b/.travis.yml @@ -170,9 +170,9 @@ before_script: else mkdir build fi - - mkdir config_1st config_2nd + - mkdir config_1st config_2nd gems/src - chmod -R a-w . - - chmod -R u+w build config_1st config_2nd + - chmod -R u+w build config_1st config_2nd gems/src - cd build - |- case "$CC" in From d5cdc2edd02eb6990d045c932aaedb60213143e1 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 15 Sep 2022 00:58:54 +0900 Subject: [PATCH 089/269] [TravisCI] The source of YAML is no longer bundled --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index be39cdfbc2e594..665feba914a28b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -113,6 +113,7 @@ env: libncursesw5-dev:armhf libreadline-dev:armhf libssl-dev:armhf + libyaml-dev:armhf linux-libc-dev:armhf zlib1g-dev:armhf From f98d6d3f389e8e46775c5895ddc1a3eec4544533 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Wed, 14 Sep 2022 13:15:55 -0700 Subject: [PATCH 090/269] YJIT: Implement specialized respond_to? (#6363) * Add rb_callable_method_entry_or_negative * YJIT: Implement specialized respond_to? This implements a specialized respond_to? in YJIT. * Update yjit/src/codegen.rs Co-authored-by: Maxime Chevalier-Boisvert --- bootstraptest/test_yjit.rb | 54 +++++++++++++++++++ method.h | 1 + vm_method.c | 16 +++++- yjit.c | 6 ++- yjit/bindgen/src/main.rs | 2 + yjit/src/codegen.rs | 98 ++++++++++++++++++++++++++++++++++ yjit/src/cruby_bindings.inc.rs | 9 ++++ yjit/src/invariants.rs | 19 +++++++ 8 files changed, 203 insertions(+), 2 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 364ed7094b03cf..2dff7d591ab845 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -3255,3 +3255,57 @@ def foo foo foo } + +# respond_to? with changing symbol +assert_equal 'false', %q{ + def foo(name) + :sym.respond_to?(name) + end + foo(:to_s) + foo(:to_s) + foo(:not_exist) +} + +# respond_to? with method being defined +assert_equal 'true', %q{ + def foo + :sym.respond_to?(:not_yet_defined) + end + foo + foo + module Kernel + def not_yet_defined = true + end + foo +} + +# respond_to? with undef method +assert_equal 'false', %q{ + module Kernel + def to_be_removed = true + end + def foo + :sym.respond_to?(:to_be_removed) + end + foo + foo + class Object + undef_method :to_be_removed + end + foo +} + +# respond_to? with respond_to_missing? +assert_equal 'true', %q{ + class Foo + end + def foo(x) + x.respond_to?(:bar) + end + foo(Foo.new) + foo(Foo.new) + class Foo + def respond_to_missing?(*) = true + end + foo(Foo.new) +} diff --git a/method.h b/method.h index 16d212a1c802f4..d33ab5053cbcaa 100644 --- a/method.h +++ b/method.h @@ -226,6 +226,7 @@ const rb_method_entry_t *rb_resolve_me_location(const rb_method_entry_t *, VALUE RUBY_SYMBOL_EXPORT_END const rb_callable_method_entry_t *rb_callable_method_entry(VALUE klass, ID id); +const rb_callable_method_entry_t *rb_callable_method_entry_or_negative(VALUE klass, ID id); const rb_callable_method_entry_t *rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class); const rb_callable_method_entry_t *rb_callable_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class); diff --git a/vm_method.c b/vm_method.c index e88a30c5f3eec4..fbe62ecd4ca28e 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1347,7 +1347,7 @@ negative_cme(ID mid) } static const rb_callable_method_entry_t * -callable_method_entry(VALUE klass, ID mid, VALUE *defined_class_ptr) +callable_method_entry_or_negative(VALUE klass, ID mid, VALUE *defined_class_ptr) { const rb_callable_method_entry_t *cme; @@ -1376,6 +1376,20 @@ callable_method_entry(VALUE klass, ID mid, VALUE *defined_class_ptr) } RB_VM_LOCK_LEAVE(); + return cme; +} + +// This is exposed for YJIT so that we can make assumptions that methods are +// not defined. +const rb_callable_method_entry_t * +rb_callable_method_entry_or_negative(VALUE klass, ID mid) { + return callable_method_entry_or_negative(klass, mid, NULL); +} + +static const rb_callable_method_entry_t * +callable_method_entry(VALUE klass, ID mid, VALUE *defined_class_ptr) { + const rb_callable_method_entry_t *cme; + cme = callable_method_entry_or_negative(klass, mid, defined_class_ptr); return !UNDEFINED_METHOD_ENTRY_P(cme) ? cme : NULL; } diff --git a/yjit.c b/yjit.c index facddcca0ad921..afa329e024a647 100644 --- a/yjit.c +++ b/yjit.c @@ -486,7 +486,11 @@ rb_METHOD_ENTRY_VISI(const rb_callable_method_entry_t *me) rb_method_type_t rb_get_cme_def_type(const rb_callable_method_entry_t *cme) { - return cme->def->type; + if (UNDEFINED_METHOD_ENTRY_P(cme)) { + return VM_METHOD_TYPE_UNDEF; + } else { + return cme->def->type; + } } ID diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 294da213786f81..c3d4a39a2ba656 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -225,6 +225,7 @@ fn main() { .allowlist_var(".*_REDEFINED_OP_FLAG") .allowlist_type("rb_num_t") .allowlist_function("rb_callable_method_entry") + .allowlist_function("rb_callable_method_entry_or_negative") .allowlist_function("rb_vm_frame_method_entry") .allowlist_type("IVC") // pointer to iseq_inline_iv_cache_entry .allowlist_type("IC") // pointer to iseq_inline_constant_cache @@ -367,6 +368,7 @@ fn main() { .allowlist_function("rb_vm_ci_kwarg") .allowlist_function("rb_METHOD_ENTRY_VISI") .allowlist_function("rb_RCLASS_ORIGIN") + .allowlist_function("rb_method_basic_definition_p") // We define VALUE manually, don't import it .blocklist_type("VALUE") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index f13505365e0f5a..11f7085635ff3e 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -3841,6 +3841,102 @@ fn jit_rb_str_concat( true } +fn jit_obj_respond_to( + jit: &mut JITState, + ctx: &mut Context, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + _ci: *const rb_callinfo, + _cme: *const rb_callable_method_entry_t, + _block: Option, + argc: i32, + known_recv_class: *const VALUE, +) -> bool { + // respond_to(:sym) or respond_to(:sym, true) + if argc != 1 && argc != 2 { + return false; + } + + if known_recv_class.is_null() { + return false; + } + + let recv_class = unsafe { *known_recv_class }; + + // Get the method_id from compile time. We will later add a guard against it. + let mid_sym = jit_peek_at_stack(jit, ctx, (argc - 1) as isize); + if !mid_sym.static_sym_p() { + return false + } + let mid = unsafe { rb_sym2id(mid_sym) }; + + // Option representing the value of the "include_all" argument and whether it's known + let allow_priv = if argc == 1 { + // Default is false + Some(false) + } else { + // Get value from type information (may or may not be known) + ctx.get_opnd_type(StackOpnd(0)).known_truthy() + }; + + let mut target_cme = unsafe { rb_callable_method_entry_or_negative(recv_class, mid) }; + + // Should never be null, as in that case we will be returned a "negative CME" + assert!(!target_cme.is_null()); + + let cme_def_type = unsafe { get_cme_def_type(target_cme) }; + + if cme_def_type == VM_METHOD_TYPE_REFINED { + return false; + } + + let visibility = if cme_def_type == VM_METHOD_TYPE_UNDEF { + METHOD_VISI_UNDEF + } else { + unsafe { METHOD_ENTRY_VISI(target_cme) } + }; + + let result = match (visibility, allow_priv) { + (METHOD_VISI_UNDEF, _) => Qfalse, // No method => false + (METHOD_VISI_PUBLIC, _) => Qtrue, // Public method => true regardless of include_all + (_, Some(true)) => Qtrue, // include_all => always true + (_, _) => return false // not public and include_all not known, can't compile + }; + + if result != Qtrue { + // Only if respond_to_missing? hasn't been overridden + // In the future, we might want to jit the call to respond_to_missing? + if !assume_method_basic_definition(jit, ocb, recv_class, idRespond_to_missing.into()) { + return false; + } + } + + // Invalidate this block if method lookup changes for the method being queried. This works + // both for the case where a method does or does not exist, as for the latter we asked for a + // "negative CME" earlier. + assume_method_lookup_stable(jit, ocb, recv_class, target_cme); + + // Generate a side exit + let side_exit = get_side_exit(jit, ocb, ctx); + + if argc == 2 { + // pop include_all argument (we only use its type info) + ctx.stack_pop(1); + } + + let sym_opnd = ctx.stack_pop(1); + let recv_opnd = ctx.stack_pop(1); + + // This is necessary because we have no guarantee that sym_opnd is a constant + asm.comment("guard known mid"); + asm.cmp(sym_opnd, mid_sym.into()); + asm.jne(side_exit.into()); + + jit_putobject(jit, ctx, asm, result); + + true +} + fn jit_thread_s_current( _jit: &mut JITState, ctx: &mut Context, @@ -6292,6 +6388,8 @@ impl CodegenGlobals { self.yjit_reg_method(rb_cString, "<<", jit_rb_str_concat); self.yjit_reg_method(rb_cString, "+@", jit_rb_str_uplus); + self.yjit_reg_method(rb_mKernel, "respond_to?", jit_obj_respond_to); + // Thread.current self.yjit_reg_method( rb_singleton_class(rb_cThread), diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index e3dbdc0d4b7287..b391a6cda5a475 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -26,6 +26,9 @@ pub type rb_alloc_func_t = ::std::option::Option rb_alloc_func_t; } +extern "C" { + pub fn rb_method_basic_definition_p(klass: VALUE, mid: ID) -> ::std::os::raw::c_int; +} #[repr(C)] pub struct RBasic { pub flags: VALUE, @@ -575,6 +578,12 @@ extern "C" { extern "C" { pub fn rb_callable_method_entry(klass: VALUE, id: ID) -> *const rb_callable_method_entry_t; } +extern "C" { + pub fn rb_callable_method_entry_or_negative( + klass: VALUE, + id: ID, + ) -> *const rb_callable_method_entry_t; +} pub type rb_num_t = ::std::os::raw::c_ulong; #[repr(C)] pub struct iseq_inline_constant_cache_entry { diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs index 4ed21118cc3b85..ee79b2938a43a4 100644 --- a/yjit/src/invariants.rs +++ b/yjit/src/invariants.rs @@ -154,6 +154,25 @@ pub fn assume_method_lookup_stable( .insert(block); } +// Checks rb_method_basic_definition_p and registers the current block for invalidation if method +// lookup changes. +// A "basic method" is one defined during VM boot, so we can use this to check assumptions based on +// default behavior. +pub fn assume_method_basic_definition( + jit: &mut JITState, + ocb: &mut OutlinedCb, + klass: VALUE, + mid: ID + ) -> bool { + if unsafe { rb_method_basic_definition_p(klass, mid) } != 0 { + let mut cme = unsafe { rb_callable_method_entry(klass, mid) }; + assume_method_lookup_stable(jit, ocb, klass, cme); + true + } else { + false + } +} + /// Tracks that a block is assuming it is operating in single-ractor mode. #[must_use] pub fn assume_single_ractor_mode(jit: &mut JITState, ocb: &mut OutlinedCb) -> bool { From 513a11b477bb334413452b6004bc7d2d0a3eed4f Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Wed, 14 Sep 2022 16:48:40 -0400 Subject: [PATCH 091/269] Add vm_ivar get, get_unitialized, and lazy_set benchmarks --- benchmark/vm_ivar_get.yml | 37 ++++++++++++++++++++++++++ benchmark/vm_ivar_get_unintialized.yml | 12 +++++++++ benchmark/vm_ivar_lazy_set.yml | 12 +++++++++ 3 files changed, 61 insertions(+) create mode 100644 benchmark/vm_ivar_get.yml create mode 100644 benchmark/vm_ivar_get_unintialized.yml create mode 100644 benchmark/vm_ivar_lazy_set.yml diff --git a/benchmark/vm_ivar_get.yml b/benchmark/vm_ivar_get.yml new file mode 100644 index 00000000000000..8c64ffc77a0355 --- /dev/null +++ b/benchmark/vm_ivar_get.yml @@ -0,0 +1,37 @@ +prelude: | + class Example + def initialize + @v0 = 1 + @v1 = 2 + @v3 = 3 + @levar = 1 + end + + def get_value_loop + sum = 0 + + i = 0 + while i < 1000000 + # 10 times to de-emphasize loop overhead + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + i += 1 + end + + return sum + end + end + + obj = Example.new +benchmark: + vm_ivar_get: | + obj.get_value_loop +loop_count: 100 diff --git a/benchmark/vm_ivar_get_unintialized.yml b/benchmark/vm_ivar_get_unintialized.yml new file mode 100644 index 00000000000000..64db6a79a5072a --- /dev/null +++ b/benchmark/vm_ivar_get_unintialized.yml @@ -0,0 +1,12 @@ +prelude: | + class Example + def read + @uninitialized + end + end + + obj = Example.new +benchmark: + vm_ivar_get_uninitialized: | + obj.read +loop_count: 30000000 diff --git a/benchmark/vm_ivar_lazy_set.yml b/benchmark/vm_ivar_lazy_set.yml new file mode 100644 index 00000000000000..bbe42574e69f93 --- /dev/null +++ b/benchmark/vm_ivar_lazy_set.yml @@ -0,0 +1,12 @@ +prelude: | + class Example + def lazy_set + @uninitialized ||= 123 + end + end + + objs = 10000000.times.map { Example.new } +benchmark: + vm_ivar_lazy_set: | + objs.each(&:lazy_set) +loop_count: 1 From ae2e8d364f685d4034f04900e0e3ad53cecc0e05 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 15 Sep 2022 01:31:34 +0900 Subject: [PATCH 092/269] [ruby/irb] `Dir.mktmpdir` creates a directory including the process ID https://github.com/ruby/irb/commit/a15f68ffdb --- test/irb/test_history.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index 38a002d3193bdf..98d24f0880eaed 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -162,7 +162,7 @@ def test_history_concurrent_use_not_present backup_home = ENV["HOME"] backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") IRB.conf[:SAVE_HISTORY] = 1 - Dir.mktmpdir("test_irb_history_#{$$}") do |tmpdir| + Dir.mktmpdir("test_irb_history_") do |tmpdir| ENV["HOME"] = tmpdir io = TestInputMethod.new io.class::HISTORY.clear @@ -188,7 +188,7 @@ def assert_history(expected_history, initial_irb_history, input) backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") IRB.conf[:LC_MESSAGES] = IRB::Locale.new actual_history = nil - Dir.mktmpdir("test_irb_history_#{$$}") do |tmpdir| + Dir.mktmpdir("test_irb_history_") do |tmpdir| ENV["HOME"] = tmpdir open(IRB.rc_file("_history"), "w") do |f| f.write(initial_irb_history) From 752ae81ed168444380d265ebefcb3101ef68d890 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 15 Sep 2022 01:33:17 +0900 Subject: [PATCH 093/269] [ruby/irb] Refine assertion for failures https://github.com/ruby/irb/commit/fd047512b3 --- test/irb/test_history.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index 98d24f0880eaed..206970714a3088 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -170,7 +170,7 @@ def test_history_concurrent_use_not_present io.class::HISTORY.concat(%w"line1 line2") history_file = IRB.rc_file("_history") - assert !File.file?(history_file) + assert_not_send [File, :file?, history_file] File.write(history_file, "line0\n") io.save_history assert_equal(%w"line0 line1 line2", File.read(history_file).split) From aecb57ceb0c690b05ad29a91c418e075342ea84a Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Wed, 14 Sep 2022 20:39:39 -0400 Subject: [PATCH 094/269] Fix style on vm_ivar benchmarks (#6379) --- benchmark/vm_ivar_get.yml | 50 +++++++++++++------------- benchmark/vm_ivar_get_unintialized.yml | 2 +- benchmark/vm_ivar_lazy_set.yml | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/benchmark/vm_ivar_get.yml b/benchmark/vm_ivar_get.yml index 8c64ffc77a0355..9174af6965490c 100644 --- a/benchmark/vm_ivar_get.yml +++ b/benchmark/vm_ivar_get.yml @@ -1,33 +1,33 @@ prelude: | class Example - def initialize - @v0 = 1 - @v1 = 2 - @v3 = 3 - @levar = 1 - end - - def get_value_loop - sum = 0 + def initialize + @v0 = 1 + @v1 = 2 + @v3 = 3 + @levar = 1 + end - i = 0 - while i < 1000000 - # 10 times to de-emphasize loop overhead - sum += @levar - sum += @levar - sum += @levar - sum += @levar - sum += @levar - sum += @levar - sum += @levar - sum += @levar - sum += @levar - sum += @levar - i += 1 - end + def get_value_loop + sum = 0 - return sum + i = 0 + while i < 1000000 + # 10 times to de-emphasize loop overhead + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + i += 1 end + + return sum + end end obj = Example.new diff --git a/benchmark/vm_ivar_get_unintialized.yml b/benchmark/vm_ivar_get_unintialized.yml index 64db6a79a5072a..a1ccfb06ce9cae 100644 --- a/benchmark/vm_ivar_get_unintialized.yml +++ b/benchmark/vm_ivar_get_unintialized.yml @@ -1,5 +1,5 @@ prelude: | - class Example + class Example def read @uninitialized end diff --git a/benchmark/vm_ivar_lazy_set.yml b/benchmark/vm_ivar_lazy_set.yml index bbe42574e69f93..7372ffcfbcad56 100644 --- a/benchmark/vm_ivar_lazy_set.yml +++ b/benchmark/vm_ivar_lazy_set.yml @@ -1,5 +1,5 @@ prelude: | - class Example + class Example def lazy_set @uninitialized ||= 123 end From d152ac677cffdbc136530dd56a56c42518d29ea4 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 15 Sep 2022 09:25:16 +0900 Subject: [PATCH 095/269] Make CodeQL ignore syntax_suggest because of the performance problem https://github.com/github/codeql/discussions/10120#discussioncomment-3485880 --- .github/codeql/codeql-config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 91f82b842bf802..6036043d8cbda2 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -1,3 +1,7 @@ name: "CodeQL config for the Ruby language" languages: cpp + +# syntax_suggest consumes an hour. +paths-ignore: + - lib/syntax_suggest From ec0d9a6a6f5da97a6727629ee8bd6c282c519697 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 15 Sep 2022 11:22:09 +0900 Subject: [PATCH 096/269] [rubygems/rubygems] Added arm64-darwin-22 to lockfiles for testing https://github.com/rubygems/rubygems/commit/d7956e9ce5 --- tool/bundler/rubocop_gems.rb.lock | 1 + tool/bundler/standard_gems.rb.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock index 9b097ba710c7cb..02f081ea3562d4 100644 --- a/tool/bundler/rubocop_gems.rb.lock +++ b/tool/bundler/rubocop_gems.rb.lock @@ -47,6 +47,7 @@ PLATFORMS aarch64-linux arm64-darwin-20 arm64-darwin-21 + arm64-darwin-22 universal-java-11 universal-java-18 x64-mingw-ucrt diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock index 03d81955577a6c..e939483dace7ab 100644 --- a/tool/bundler/standard_gems.rb.lock +++ b/tool/bundler/standard_gems.rb.lock @@ -53,6 +53,7 @@ PLATFORMS aarch64-linux arm64-darwin-20 arm64-darwin-21 + arm64-darwin-22 universal-java-11 universal-java-18 x64-mingw-ucrt From 6b2b9e0019de4e08c9164d83c81418db6584f243 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 15 Sep 2022 11:23:38 +0900 Subject: [PATCH 097/269] [rubygems/rubygems] Also added x86_64-darwin-21 https://github.com/rubygems/rubygems/commit/c03e7e1564 --- tool/bundler/rubocop_gems.rb.lock | 1 + tool/bundler/standard_gems.rb.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock index 02f081ea3562d4..2e47e5a894b7ae 100644 --- a/tool/bundler/rubocop_gems.rb.lock +++ b/tool/bundler/rubocop_gems.rb.lock @@ -53,6 +53,7 @@ PLATFORMS x64-mingw-ucrt x86_64-darwin-19 x86_64-darwin-20 + x86_64-darwin-21 x86_64-linux DEPENDENCIES diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock index e939483dace7ab..bf21ad5da2c34d 100644 --- a/tool/bundler/standard_gems.rb.lock +++ b/tool/bundler/standard_gems.rb.lock @@ -59,6 +59,7 @@ PLATFORMS x64-mingw-ucrt x86_64-darwin-19 x86_64-darwin-20 + x86_64-darwin-21 x86_64-linux DEPENDENCIES From bf72afa76603d1ebd9e9d6e99c6dbc4d765344e6 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 15 Sep 2022 09:59:05 +0900 Subject: [PATCH 098/269] Remove warning for old TLS version connection RubyGems.org already has refused connection requests using older than TLS 1.2. --- lib/bundler/vendored_persistent.rb | 34 +------- .../bundler/vendored_persistent_spec.rb | 77 ------------------- 2 files changed, 1 insertion(+), 110 deletions(-) delete mode 100644 spec/bundler/bundler/vendored_persistent_spec.rb diff --git a/lib/bundler/vendored_persistent.rb b/lib/bundler/vendored_persistent.rb index dc9573e025b7a8..e29f27cdfd54d9 100644 --- a/lib/bundler/vendored_persistent.rb +++ b/lib/bundler/vendored_persistent.rb @@ -11,37 +11,5 @@ module HTTP require_relative "vendor/net-http-persistent/lib/net/http/persistent" module Bundler - class PersistentHTTP < Persistent::Net::HTTP::Persistent - def connection_for(uri) - super(uri) do |connection| - result = yield connection - warn_old_tls_version_rubygems_connection(uri, connection) - result - end - end - - def warn_old_tls_version_rubygems_connection(uri, connection) - return unless connection.http.use_ssl? - return unless (uri.host || "").end_with?("rubygems.org") - - socket = connection.instance_variable_get(:@socket) - return unless socket - socket_io = socket.io - return unless socket_io.respond_to?(:ssl_version) - ssl_version = socket_io.ssl_version - - case ssl_version - when /TLSv([\d\.]+)/ - version = Gem::Version.new($1) - if version < Gem::Version.new("1.2") - Bundler.ui.warn \ - "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \ - "Starting in January 2018, RubyGems.org will refuse connection requests from these " \ - "very old versions of OpenSSL. If you will need to continue installing gems after " \ - "January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.", - :wrap => true - end - end - end - end + PersistentHTTP = Persistent::Net::HTTP::Persistent end diff --git a/spec/bundler/bundler/vendored_persistent_spec.rb b/spec/bundler/bundler/vendored_persistent_spec.rb deleted file mode 100644 index 3ed899dbcfdfde..00000000000000 --- a/spec/bundler/bundler/vendored_persistent_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_persistent" - -RSpec.describe Bundler::PersistentHTTP do - describe "#warn_old_tls_version_rubygems_connection" do - let(:uri) { "https://index.rubygems.org" } - let(:connection) { instance_double(Bundler::Persistent::Net::HTTP::Persistent::Connection) } - let(:tls_version) { "TLSv1.2" } - let(:socket) { double("Socket") } - let(:socket_io) { double("SocketIO") } - - before do - allow(connection).to receive_message_chain(:http, :use_ssl?).and_return(!tls_version.nil?) - allow(socket).to receive(:io).and_return(socket_io) if socket - connection.instance_variable_set(:@socket, socket) - - if tls_version - allow(socket_io).to receive(:ssl_version).and_return(tls_version) - end - end - - shared_examples_for "does not warn" do - it "does not warn" do - allow(Bundler.ui).to receive(:warn).never - subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection) - end - end - - shared_examples_for "does warn" do |*expected| - it "warns" do - expect(Bundler.ui).to receive(:warn).with(*expected) - subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection) - end - end - - context "an HTTPS uri with TLSv1.2" do - include_examples "does not warn" - end - - context "without SSL" do - let(:tls_version) { nil } - - include_examples "does not warn" - end - - context "without a socket" do - let(:socket) { nil } - - include_examples "does not warn" - end - - context "with a different TLD" do - let(:uri) { "https://foo.bar" } - include_examples "does not warn" - - context "and an outdated TLS version" do - let(:tls_version) { "TLSv1" } - include_examples "does not warn" - end - end - - context "with a nonsense TLS version" do - let(:tls_version) { "BlahBlah2.0Blah" } - include_examples "does not warn" - end - - context "with an outdated TLS version" do - let(:tls_version) { "TLSv1" } - include_examples "does warn", - "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \ - "Starting in January 2018, RubyGems.org will refuse connection requests from these very old versions of OpenSSL. " \ - "If you will need to continue installing gems after January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.", - :wrap => true - end - end -end From 68a5b0f086c9d54f271cdff231a1a2c2a40b0aa6 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 6 Sep 2022 12:33:23 -0400 Subject: [PATCH 099/269] [rubygems/rubygems] Mask the file mode when extracting files When extracting files from the tarball, a mode is retrieved from the header. Occasionally you'll encounter a gem that was packaged on a system whose permission bits result in a value that is larger than the value that File.chmod will allow (anything >= 2^16). In that case the extraction fails with a RangeError, which is pretty esoteric. If you extract the tarball with the tar and gunzip utilities, the file permissions end up being just the bottom 16 bits masked off from the original value. I've mirrored that behavior here. Per the tar spec: > Modes which are not supported by the operating system restoring > files from the archive will be ignored. I think that basically means what I've done here. --- This commit also changes the behavior very slightly with regard to when the chmod is called. Previously it was called while the file descriptor was still open, but after the write call. When write flushes, the file permissions are changed to the mode value from the File.open call, undoing the changes made by FileUtils.chmod. CRuby appears to flush the buffer after the chmod call, whereas TruffleRuby flushes before the chmod call. So the file permissions can change depending on implementation. Both implementations end up getting the correct file permissions for the bottom 9 bits (user, group, world), but differ with regard to the sticky bit in the next 3. To get consistent behavior, this commit changes it to close the file descriptor before attempting to chmod anything, which makes it consistent because the write flushes in both cases. https://github.com/rubygems/rubygems/commit/22ce076e99 --- lib/rubygems/package.rb | 13 +++++++++---- test/rubygems/packages/Bluebie-legs-0.6.2.gem | Bin 0 -> 14336 bytes test/rubygems/test_gem_package.rb | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 test/rubygems/packages/Bluebie-legs-0.6.2.gem diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb index 084dc5d2d9523a..4672866985f866 100644 --- a/lib/rubygems/package.rb +++ b/lib/rubygems/package.rb @@ -444,10 +444,10 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc: directories << mkdir end - File.open destination, "wb" do |out| - out.write entry.read + if entry.file? + File.open(destination, "wb") {|out| out.write entry.read } FileUtils.chmod file_mode(entry.header.mode), destination - end if entry.file? + end verbose destination end @@ -467,7 +467,12 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc: end def file_mode(mode) # :nodoc: - ((mode & 0111).zero? ? data_mode : prog_mode) || mode + ((mode & 0111).zero? ? data_mode : prog_mode) || + # If we're not using one of the default modes, then we're going to fall + # back to the mode from the tarball. In this case we need to mask it down + # to fit into 2^16 bits (the maximum value for a mode in CRuby since it + # gets put into an unsigned short). + (mode & ((1 << 16) - 1)) end ## diff --git a/test/rubygems/packages/Bluebie-legs-0.6.2.gem b/test/rubygems/packages/Bluebie-legs-0.6.2.gem new file mode 100644 index 0000000000000000000000000000000000000000..60918f3bc53abc47d783d04e26a33bdba0d51bfe GIT binary patch literal 14336 zcmeI2Q;@ICx8LVCwr$Vsv2EM7Z5w-R+qP}nHul)qW9LmyrE==rF?2Dcb1`(HGxq}eKV=O67#j-<(0{N0vHwsru`)3*|Ie|1^D{CtGcf`Y zGW?&~(7&J8)%l<3|5@JM!qn8}-#h+8`+w*EAEEub;Qqts|5dvLQIJ4r^L`RQAXmQD z&N%F?`#vXi0_7bHF4BUmstUUq6r2`D6Mh;^gu9 zTTONRoV-5Y?)8`NZ`lg>n<_4vgKz56w9H?Yz0W+IoqlBb%rBxO8!A@Vy%b%!>e5P; z?)*PlRi2cprk7nyRckLV3w@0LhGe3R(BxE9YMfu1YEdVtTPt5=WZJAuxwMROp#-F3 zv`96<<_napn02a@gzjyL`07^4mQbfu!d{>{tJ>w;>Smtf=nkd{(N1gEw2p|Ob*L@T z)EKbvlwOtqB$imz&r$bD;-%8;T{(m8m!{9U@hc0139M_RS(mJH<{TtMYg0#90U@&~ z6(~<%$k@lsnWvpJ7e-npcL5L(8trd$QMY~GkB12KE0Rt=?j6;aPiJRmr%$RJuo^s#%tWVnK4J zmz*&XX3@%|!<8wuX(Q2VK&6@XNJtv?T1OqB86b(Za0c$ccjWZ~cit(z024qYHLGNq zeLZA=3Opuye_{Gtsv>vTSU?6eBA_REt#nb?N9iDO)sk;TgJOtLQC&3GcIf*`A>L=- zd{Whu)nb$beXV061Nq8I6It*GW4*6au8oQ|N|p}t{|#;+L1G4 z@v4wUre|9w(XxT7Q_n80^z*Ttu7yvlM73s$8hf#HaCBXwX4*(|$!9;WGZjF1k0wk? zQ)v?4uOwTS*=A{bs%V}yA|GmeWS8{QqdRaAP zS#*%~xj*TI;oDVl_2TIMybMA38m^)+46-_Zm2rX))hZgc1Dq|0ISh`6f=j3nhneW1H;N`TowdQAY^Dkt^4QVD|nd2|5?MsVWG()Bf6B zI@gJ8lja8=bg^emQ?%I-tQ*D@rT8^!ydcK<2VyuWv$C=cmBA~en55ZI^bGWiPv(-( z-q8r_WSLQu2E3<1_?x-wSN1AgYGHLQ-hg~6E_t3=7M2Ts?da2D0|p`O7XKs|;k9U> zHNKkoc6@r&xUlM0x7;3CWO*`V3>7DMRw`M&Te=IiLN?6JQm0781hJ(l)IPQu^>41a z>1Yw+0XU5nP>(wtv5F{Gu71~~fXaZ~%_=E3dBQnZWk=l5F2l0@3aTo2V z5m5AXE;wBTF2lm%OQaazu*{5GipXeuo0Z`$X)zg)n3q5nbXZvQfbw%TwA0xssUy7L zbT6>D#PRWM+3dm09X-7)i`MtcVr zhkum_Td9)d3G-?mav3tltZNtvffgZ;7RypWNx?6s#%G(X&A&RD)WbZult_-XXdD`z zXp2T#WCoh_hH}o&wt)bqSINrTc=W~+AkQ0L!%M9KTk)PNx!72?q7Hp zI2AV&3cUE0OLTUKOrQZZ%TKAalFdgNvOuH2;L{jywKI*RhYs)u04Z1@0Y!th@T&l% zYoFzdNAc#{v8F>DHD92mNe%^tcrZg2!6GR9-J{8rIu!)_J&jdw)rO61@i9J)?{|Ey55HpF5 zy*YS{AA-13daTD|I6#t6pe^5zB~S`x5)XcM=15B=RA$YPb(yfcb&8zGnol`7>=!3_ zJ)b);HKevB9C6kLffJ8zDwq>j*RkY4FucKy%?^_hA^;1?qq%+>)>7Ov1TftJHl0_> zL9ygm-W(5C$tWbV8ef%0W+kvptHsk?r$>ZjzGayIAq-Jnto<5JY7R%th4@<#8~~i; zXnTJGkyg8A;|on5pv?N3V|p!;WtxzLo>tz+Y|K7xvzYy8R@OLyt%q_UddOatl7Xe+ zkv$SjikwfJ1NE~&tR($4C8j2L|h;iK~M@qNFDkLB@my=6~T%~++RYWuht z&Ic$s$5)SFd-x)%3Rpto$w!;{kJICT@K`~iceqrlS4<#|gJRBdddKcs86;{yzB}cR zG1Rekt`}_98ox?gK;FfDikM|SDQ3WKrJ`}lO0KH=bq^>oGyifFB9*69n{Q$a#GeJR z(Te%*to&V|LbIi;D!*0$7CjipyH;e>_DXF-6)CvFtdgZ!= zPqGbW5<#zq{Vis|jNSInd0(Qpkg8$?|1_9A(kvWw0pTIg%Bz^V@xS1aSCvOxRj!M4 z-=VH#WzRU_;(zZqbRSL;7ZJ^}T0U!m?OKeP4KU*}6S=k6z1C5-ys$adu72CLP0YVL9@^%$IUD(9W5{Z^YX^?J2lu zaE#jPhXy{(cTb7O07VsdmTnCc#7OPDT)!Ru4oWa?aK8SAGCz2rFo{4X3#@VX%J>=sheheg_C|2KE|v<(%SL|CUX<-S;jCMJN;I9 z#W(a$41(PTCVJ*d&KKrM>|*}}7l%~o>BjS=;)M^-!~Y>0c$#ff$CjK&#=7Dyo@Ir6 zE0hG@&>>mN{|MaNs-Ax4XVv$y+kkSk%P}$gkaGX4PMiw|SY<3!LL@ktC>1^GY z@A0awMeBH@Ak$mJ?CK1$XI}ajUyt5LW!|HEbNL;Q=M()08dh&@M|?T;JJ6G)68RDg z8{+}vYmMH&JwKH#lPmxTRIN3{jy%|uNcIz7_`|Mgu_S|uCP%eKS8tijCVl67TVZ@8 z^x8wwcE-TfCi-_-R=#38Xcgk>5WR>7y{3I78E<^a0(zB{%<>I26WWp!KzZESiKZbl zpgA(gFs)G&f%7{Y1a8RVw~1aQnSy=kgdrHpM*&T#obhm@_PJJW(ox8YD7ys#yDRWn ztqTDG!qwF=f&pq2z4yUwN1{{7yIX>tJqRjnBICylX)5AS2q!OMG+HD(Uj*-`BCU-XdxN5W~Y+A=F~Aab9!u$YvpZ(27Nqq_zAnXx@9d z?6(2=$HAkUG^Ivm0H8Gl`~a3}`~V32_n<`*qF4aa_;Du2G{{nj8c!aO6xjz#b_ zIWCP|v@8F5KBbUH(h+2xx}mKgF)gusj0qd|SAdIJLv}QP$Tb^0 z{tywY-*J1>anggQX5vShx9@!%q4AvveONI#kWu9(~>%7>v zt51^?nl%_fyitq5T^+#$Upll!FI_SJvzx z(%7e*0pak7z~9@$1p$A-y?ePQXJ?pZ97DqySNs9ClM#@%&`?-rR3M;%Ql4FQbTTsC z{gB!|irEE+otXxg`))TOchQutc4m1`;9W0|@A0Q-l!olwndruy%liORfG~fR_BpDC zzC)PjzWRNw3(UQ^!wU%)0ErA09esHq>K`2IzaF;m94wU{d((RS{ZrZO+tL>VVr#4 zGHl|57|khFj|NN|=Sb&u&2B*jMe@Q_Y3=Gk|5LocTFrZ&ZzDd)mFU$8ENhryR+v;N zyi&PQ@6ahO-k5#Y$pVDsg)_{97<69Z3A3Yxx@>}4dNtY9K*adlLPV1d31n<6{k0znVF^YKJ>2+&f-~1v?XrrSRPk2?2MVvORSWfvQsPn^+iu=bGUQEYLedrFV623Ys>@#7 z@(!ejuuKIvvH0(XW$@Mwr%UYcpoAPp^NTH`)(Mv>%tWi4j81B;$yKS@9W>n>x)jR=XyklvpZ0NrLt zbCc?NvarbH(XCZ@?N<{pl?`70_P|ogWa3_dc<`W}p$0h+zy&YqeXuE%KPqjGQnZo< zXr*d*byAFmu8fI+mr*5WqnLs9kwaN=&A4c$%N8#4A0Wy@ckzx6@ty(jH2d1jn+1Uu zM`eAlQ44Q>e%`U|(SF&eE(3MjEk@A!rLbS~Ca{~N%0hoy;Fm_#O&G#WwH-aQZOD-z zx!I1DUFrG;PKvAd%R!q0Hkb;ONg)`(ftHNpP_LwQ7m1&JYQ0q@B5Cq+=h=a&&?&g@Eu3*1^a7#X& zO-#-2_EyhqMvJ}LmP9`sUeDlBXtc0b=3`;sM-z24n$Bk5eU6REWZQ^R=blmFu59Et@lA=NSv$Fd;Pm% zpLaGxrfk~E^K=ExZ%89Q-Oh>4FZ}Hu#iWm>4$)9lzBAklV9hWf=8T$Ijm#OLvjSO( zz!2z~)ew#}v<3KurD{ok@ZY^P**fdZ8I_o$%E0wl4Z_WZP-z;~N|+bAy#lu1G{VbO$i z%FoUz2=}8PpKX(ZQ8BJ?_%2XbVp1y)o>ox(dRkEa1MQvbeCJSx0d=Ev^ES)e_@f<0 z_)8XW_=a{E{=}u|@{N=Yzdv-Uz3sb75oHxMl4oM(E?oY>(kqck*w;-q=d*Z`gy5Qj zXe0n$2b%-{7PJg{oPHZjA1^KK6rqI!u_K(mq$NjER$|i9G`3+06(gareuKSF3YQzK zG^S6fNHZ{tA(I%7jiNZm393F(^jMCCCw1o(Tjb?;!}?*v4RjrQlgeR%mQ=_elj!6K1{CDR(*Z zkF+%AB5NycfsEXC#JQL;9omT}eolZ6e+}=2K)kLps%4M@|565xcx^}kh zbVd2zURFVwu6G$B6c`Qi=g4@yz3``nf>wuKwp2CQR-~B-DfbYi;!kp3X;9!2*EqW? z`f}#Ez7E=$z~OX4*>iBGFQH*YmDmuKKtulTh@y(1wky9_k68vw5;e=$rlgtQ(Z{)G zP6CS@hb9#C#A~x|hY3}=FojRs5q^hU@t}fiSKA45qcp!M4G^-U@qng&O3jOqz8VVp;a^EXa;Js|?A#tG5~ddh885Juh2p=CD8(6FeAGAJ)18 z$sT+qZDMkVA1;ODXPS#z6C7@ERvrty_x3(w0v$yYs9oMKy8KV{Hwl*|TvrMf`KU&+ zz-+WF*72GR>U8@d4mRK`L>UT*TeM-FP?PfrnIXBZohK<~MK!0MWv+}^@ARRFttK&r zVny0$lU?Lj0~oppWgeSTHqhxN?|Ur$y-mxFT*)|Eug8Y-jUDBEck?WdTv#V5$nFX< z(MFh3HVEGL%Tt=*c1kU#fktYO2pVbLVyc zlvEctavCS?07g13E895KHlqs(akwynJ`gDzPwcWFt92C-e;@IbF?k>?6u55=_upd^ z@+eD59FWwAGH5EzZ;XC}=}Igjl@&O8AhmHB9iu3aO*Th<;=7@Y*sv~}bcyouwG+vE zrDer#Ih71rISL`ZM9Of+IG0|HV|OSY`J{fZ%`*yF<_F7%(VVhhLS(42FlKva6I>me z@o|gy}qmc#e;<5#htD+M2pWuYVMR*6}-~3in%0P_m>?4azVp9$A!LE zrUN;0BE%-M3=W6XN#xeHQp@l8IQf2y-3HWeuw>~nwYAWF&4R)n`rwozLVf1f`Jm=jxIFg*j zkxHQ29Cq)EwLZ7qmha{U-*qGgu~W`KP#JA>k`Xns?_KQ8Y+&D5*jfy7{}-djZX{Yr z-#IdB_e|uC(OqxU{#~qW>m`d%1Y}E-GjQnN`nz^Wikw3NZkK8-(lO0kwBu`TEI&E0 zO)0n!W|uK77}8M0jjvP3_uMTlM~^@qGyxJO4k2rfADCo%{lcG6=Kj1uz~~FaqJ6G@ zG=q`pt?t$_G%NM(fxGG-QyjIQ#0L>fg&~S(4@F)#)^|l+X;%N|i57&sy@RGxgB$zJ zG2zo6d3k>O>>r=M8%Y!dv;N;m1nvyXlCf8s`}lwTaZ-ksXr18sdM8jrticb|=}ckj zfJs_V&MFWJt74O!$W_ zTS4wM>a0!T0)-`GNlg(9IfKo z+Z4vL=J^LdXhTMWBpHpN%u4xUAAAruj$D?#q@v9#w`2{iyi}uaJ2c5{QkLVvp%H^e zTCY2yF|0%^VQ5zH4Gq34AEBGPny#(L!QkrL%RxoC==?WdzI>4T!(UOJ@0;v}{kdJe zpFRKQ?cTer=X;=^pR0+9B)spRjR}bFA90`Ozy97`b{Dl1CEDFAxxf-@r4(tuK{k$H z*sLh>SNgCP$y}J3(i5;cv>g|;$lW3IdMOs>X9HWD06hAExV!$_bQ;~nQx~3^s8x5< zoEjzUyp4+-Q`o*0tJ^PRt_^I;?;9WRA_(6?C2j8JC~lBfrngx}OdCd5-~r&a-@W}kml_H8476rZ#jl1{6W?a5oI=ocYwGsK1 zzJ^>~LCL5-+|w@tQl+O)LzIF7Mt$OAG$V?FLq}gUDl|!jjm()V^L(4^O*xYHtK#oT zso&xK-;#`Y$U!dBc!-+q0Mo$otQ?SHrl$RO{S0J3-pdcN$l9fnnLtH>E{Sd^q#_o@ zD23ZWr|`a=4Q1{wJM;Tbo)_9ovW1_oc?=U|} z5e1}U(DJdmhKC#wJb>J)sA|*FYS~PPH)>wou_6CDlWlTOSdZslw?t`=sY4Wl47YE3Fb2KP<2opcEgx~-uGFEOgBO45ObLcsdLQUmsf>wQuvp4HlP%FhZ z`0R|cWS;1>;69im()kJTLl3XgwpkLqzZ;mI7w`DgHSmm1x_h!5Z6 z2j}y)+3?5z`R%-n;Od9}KzNsBUD;x&GR-KuD7=6gIbbc_={?+NUD^LHaIqa%k@m+9 zmh5k)BaFrg?`8#k;7d>hgf-KK0hG(8Ur<|%(mhA$L~0+9*!m@YcWt-ydj2mX zZTchb2d;w$Q2HvFEcPM)$gz0fV7SgrW(0#mb+6b$oro&D{6$*=@?PC>MN!ak_25@Q zdZ&KuoDl0nYoHA|OQ1#qtAf<%~}Hbi*;zOTPP{#xA;x4GQrtuZ3~?8uP$W`P|-dXAF=c z+TKe)=Pn^0R2%f)SenmqNfaKTOww(X&^OySPiL+~Yg0AvTC?@@)bMu_Z7RC?gu-Ip zHL{$}^y5Lc(&8l~JPs(&nfaghnyeOFv}dfz7Q-D)=x%N-)R&=P->`10XPeXgi@@6F zM|*LB=xLd|hrxX)*2j)B46b2uj%-9cTwgTc_h+d7>Pw9Q5d-#*x%pw}$u3E8(%QBS z12~R&E-$9JfW_zK*mq9e*rndNfkgZ)AK91Zvy6(d*TD`yhQ*}ALxvufS1ns|YUMV) z+mXC@tZe2XLZirR?7#g8r1yW0b`%$dRlo;>P`%?i0V~U*ySI7*F*gi41I7X`V?2O8 zw1#r>ac4QOkCbA>+klhR=kE{gYEU^%)mp1-ENZhNC{=6PLt87=F zhPMLdrGGykBiBY{{YbXEL0CmA!7k^(^_?({*yAGJZ+HH6{%obT*-9j%N5&LOk_-vWJ?G;uLp z1VyI;YuCA|x;z_1ElxJuY`hjhry z(5TcVtDcftABX`lQ$8{ENEkqQ+Ux`|#wjvPANL{6a3lC=q9$F*KA8b$QgHBB!SPXt zEWJyzjg3Nq8k>tV&TxANWeGrt$p6XV=eAdP!+ZDW|8f6&ewY;cbGw9a>;E3a2XUbt z`O9!sk5T=n#CiIc%<`HCxT?-A@2~5ETbWwO0}KIdPj;?8u?S{NTl~@<{2cnN(WCJT zl3Q_-&aI0j3!(0*!_~E!s;>g~sb$=~JO(u=-D)9C9}CV_DE1=>79RpbYJ0G z)Sq@g3U?t(IRq+H5$YakXKYFz^qO*0o1kuBU#-&|*v7VkIhL9?BJGnCNbvky`aUCK zJt(8yDz#<&M!Vv)&<9Ht1yewGm?-4WFF||xPU>DutCtWn6xOd`$3=ldc-ZcLfwA9r zd^lxZpXEdyL8X@7ytKaJ{9+$KH@~A{Mi&+5a%VgDdJM37{y^%+qD^ zhr5g#%4du`$(VzjJS2)E^x*~3R{sEa6&92_`K&I@&F=vAYgPBSKhrOsUp8{Vn_$%7 zQ#x?mDO2*GUq~05S?)X`f?hD=xl>OW7lFn;r=}^h?Xckc+|_53%>H~Fdw;~E$qzkk z5nwg;#1cD*gV_7Fz+U8Lh^mWSR)CEJ|Uv9`RFmHkuNLVR(kTHG#_)M+_|=zYgy*5+#D~T9}p@M zjBUl8bzppYC=jv-m{k873CA-$9PqaJC>Q+H064O=dnI{NF$8wD2O;7Mk@vv**t=)BK#4yN(dEI2`rLLZ)}9dC+AR&Y$&9=KVSe-pI^Xwdl#uQy~)6CAHY zGY|T8Gw{nyh(Y|6&$dU<8o}9_^~;nK4(gzLq)Vsv=}CZ3oey8n;W7|8Zp7mUCWg}c zDSYa5yGVES`v$E0EQ-UWIh+*)vRWCcE8)j3D(v0I6j?O0)aTZ z(B86q$aE+yZGH8Hcu0=~YMu%}69&{-#0GsP8N=q>Fs@e*^8~$T>8xzAuci0P6wWG? zYL;=|*X)U~C9fK(BHJ__Q)uh*DJLI6^d5v++W*1F&~6mqNH6KcY~~9r=P6@I!cTQ- z5$HZ7%&UiV>r0)2#!v>Ves2^NAk*E3Nw;+JZX7ZOj6p1_jqYuHXnMs}VNmn=#k%UB zOm!p1wWhl%=y6L`*>B6t14zwNb=$)7z1N7UAwa;-2i|MgaL!z=OZGro5)B|R4e+Pp zJypSA_-UPMKCL&E_Pk zDd1bc&viWTnn*Vosv%M#h1uYmm25LKVDW-jq1?+N(t%r|d5(a+E4WwTq%VVvfQoMy zG@Is9u16#h4U>^?AZ4qUEw4?FajGg?_`GE6sx!8-;@0-;|DN?%ap1gxfAoW(rgiES z)9OD!yECn_(I~XA;cW0V)R&$&k~O0Xr(PKcig6p}i+)zYdum}JZzHcZ!cb~$6&=g0N%N(XMK$~+$vIVw_t}IKHrk8J#rbT=fFHA~WjS(}<4cRG=kgL*f zBgnJ-$lMq0?#&GIPkyCasZGz`W{-s%DSuO_C?BcF!;f6-I*9hj0t9)&x~J87CI#(b zr#d$ea(f2fczvC8BN!fe=d7Xl_}saXtRE#=kP~uMGSv1OLxv;J*P*pq>8! literal 0 HcmV?d00001 diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb index 9295f42dba0140..eebe4d86d0a14a 100644 --- a/test/rubygems/test_gem_package.rb +++ b/test/rubygems/test_gem_package.rb @@ -510,6 +510,21 @@ def test_extract_files_empty assert_path_exist @destination end + def test_extract_file_permissions + pend "chmod not supported" if win_platform? + + gem_with_long_permissions = File.expand_path("packages/Bluebie-legs-0.6.2.gem", __dir__) + + package = Gem::Package.new gem_with_long_permissions + + package.extract_files @destination + + filepath = File.join @destination, "README.rdoc" + assert_path_exist filepath + + assert_equal 0104444, File.stat(filepath).mode + end + def test_extract_tar_gz_absolute package = Gem::Package.new @gem From 64a020324dc7453c5678f44b898eb5da47b9abcc Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert Date: Thu, 15 Sep 2022 10:12:27 -0400 Subject: [PATCH 100/269] Add asm comments to make disasm more readable (#6377) --- yjit/src/codegen.rs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 11f7085635ff3e..f1586cee4ba128 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -260,6 +260,7 @@ fn jit_save_pc(jit: &JITState, asm: &mut Assembler) { pc.offset(cur_insn_len) }; + asm.comment("save PC to CFP"); asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::const_ptr(ptr as *const u8)); } @@ -269,6 +270,7 @@ fn jit_save_pc(jit: &JITState, asm: &mut Assembler) { /// which could invalidate memory operands fn gen_save_sp(jit: &JITState, asm: &mut Assembler, ctx: &mut Context) { if ctx.get_sp_offset() != 0 { + asm.comment("save SP to CFP"); let stack_pointer = ctx.sp_opnd(0); let sp_addr = asm.lea(stack_pointer); asm.mov(SP, sp_addr); @@ -1184,7 +1186,8 @@ fn gen_newarray( let values_ptr = if n == 0 { Opnd::UImm(0) } else { - let offset_magnitude = SIZEOF_VALUE as u32 * n; + asm.comment("load pointer to array elts"); + let offset_magnitude = (SIZEOF_VALUE as u32) * n; let values_opnd = ctx.sp_opnd(-(offset_magnitude as isize)); asm.lea(values_opnd) }; @@ -4027,7 +4030,6 @@ fn gen_send_cfunc( unsafe { get_cikw_keyword_len(kw_arg) } }; - if c_method_tracing_currently_enabled(jit) { // Don't JIT if tracing c_call or c_return gen_counter_incr!(asm, send_cfunc_tracing); @@ -4111,6 +4113,7 @@ fn gen_send_cfunc( // sp[-3] = me; // Put compile time cme into REG1. It's assumed to be valid because we are notified when // any cme we depend on become outdated. See yjit_method_lookup_change(). + asm.comment("push cme, block handler, frame type"); asm.mov(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -3), Opnd::UImm(cme as u64)); // Write block handler at sp[-2] @@ -4134,6 +4137,7 @@ fn gen_send_cfunc( asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -1), Opnd::UImm(frame_type.into())); // Allocate a new CFP (ec->cfp--) + asm.comment("push callee control frame"); let ec_cfp_opnd = Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP); let new_cfp = asm.sub(ec_cfp_opnd, Opnd::UImm(RUBY_SIZEOF_CONTROL_FRAME as u64)); asm.mov(ec_cfp_opnd, new_cfp); @@ -4160,18 +4164,6 @@ fn gen_send_cfunc( asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_EP), ep); asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_SELF), recv); - /* - // Verify that we are calling the right function - if (YJIT_CHECK_MODE > 0) { // TODO: will we have a YJIT_CHECK_MODE? - // Call check_cfunc_dispatch - mov(cb, C_ARG_REGS[0], recv); - jit_mov_gc_ptr(jit, cb, C_ARG_REGS[1], (VALUE)ci); - mov(cb, C_ARG_REGS[2], const_ptr_opnd((void *)cfunc->func)); - jit_mov_gc_ptr(jit, cb, C_ARG_REGS[3], (VALUE)cme); - call_ptr(cb, REG0, (void *)&check_cfunc_dispatch); - } - */ - if !kw_arg.is_null() { // Build a hash from all kwargs passed asm.comment("build_kwhash"); @@ -4557,8 +4549,6 @@ fn gen_send_iseq( // Number of locals that are not parameters let num_locals = unsafe { get_iseq_body_local_table_size(iseq) as i32 } - (num_params as i32); - - // Check for interrupts gen_check_ints(asm, side_exit); @@ -5432,7 +5422,7 @@ fn gen_leave( asm.comment("pop stack frame"); let incr_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into()); asm.mov(CFP, incr_cfp); - asm.mov(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), incr_cfp); + asm.mov(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP); // Load the return value let retval_opnd = ctx.stack_pop(1); From cc7f6fe73454743da87d73ddb26626503fae248f Mon Sep 17 00:00:00 2001 From: Noah Gibbs Date: Thu, 15 Sep 2022 15:14:27 +0100 Subject: [PATCH 101/269] YJIT should die if we compile on Aarch64 with no instruction cache clear available (#6380) YJIT should die if we compile on ARM64 with no icache clear available --- yjit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yjit.c b/yjit.c index afa329e024a647..d3ec27ab1e03fd 100644 --- a/yjit.c +++ b/yjit.c @@ -89,6 +89,8 @@ rb_yjit_icache_invalidate(void *start, void *end) // On Darwin it's the same as calling sys_icache_invalidate(). #ifdef __GNUC__ __builtin___clear_cache(start, end); +#elif defined(__aarch64__) +#error No instruction cache clear available with this compiler on Aarch64! #endif } From 2387fbfb34f58ea05b4aceaab6d54febe9eab4c1 Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Fri, 16 Sep 2022 00:37:15 -0400 Subject: [PATCH 102/269] Fix splat args (#6385) * Fix splat args Cfuncs were not working properly so I disabled them right now. There were some checks above that were also actually preventing splat args from being called. Finally I did some basic code cleanup after realizing I didn't need to mutate argc so much * Add can't compile for direct cfunc splat call * Fix typo * Update yjit/src/codegen.rs Co-authored-by: Maxime Chevalier-Boisvert --- yjit/src/codegen.rs | 59 ++++++++++++++++++++++++--------------------- yjit/src/stats.rs | 5 ++-- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index f1586cee4ba128..71831a96ed1086 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4023,6 +4023,11 @@ fn gen_send_cfunc( return CantCompile; } + if flags & VM_CALL_ARGS_SPLAT != 0 { + gen_counter_incr!(asm, send_args_splat_cfunc); + return CantCompile; + } + let kw_arg = unsafe { vm_ci_kwarg(ci) }; let kw_arg_num = if kw_arg.is_null() { 0 @@ -4060,18 +4065,6 @@ fn gen_send_cfunc( asm.cmp(CFP, stack_limit); asm.jbe(counted_exit!(ocb, side_exit, send_se_cf_overflow).into()); - if flags & VM_CALL_ARGS_SPLAT != 0 { - if cfunc_argc > 0 { - // push_splat_args does a ctx.stack_push so we can no longer side exit - argc = push_splat_args(argc, ctx, asm, ocb, side_exit, cfunc_argc as u32); - } else { - // This is a variadic c function and we'd need to pop - // each of the elements off the array, but the array may be dynamically sized - gen_counter_incr!(asm, send_args_splat_variadic); - return CantCompile - } - } - // Number of args which will be passed through to the callee // This is adjusted by the kwargs being combined into a hash. let passed_argc = if kw_arg.is_null() { @@ -4257,19 +4250,16 @@ fn gen_return_branch( } + /// Pushes arguments from an array to the stack that are passed with a splat (i.e. *args) /// It optimistically compiles to a static size that is the exact number of arguments /// needed for the function. -fn push_splat_args(argc: i32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut OutlinedCb, side_exit: CodePtr, num_params: u32) -> i32 { - - let mut argc = argc; +fn push_splat_args(required_args: i32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut OutlinedCb, side_exit: CodePtr) { asm.comment("push_splat_args"); let array_opnd = ctx.stack_opnd(0); - argc = argc - 1; - let array_reg = asm.load(array_opnd); guard_object_is_heap( asm, @@ -4299,11 +4289,9 @@ fn push_splat_args(argc: i32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut ); let array_len_opnd = asm.csel_nz(emb_len_opnd, array_len_opnd); - let required_args = num_params - argc as u32; - // Only handle the case where the number of values in the array is equal to the number requested asm.cmp(array_len_opnd, required_args.into()); - asm.jne(counted_exit!(ocb, side_exit, send_splatarray_rhs_too_small).into()); + asm.jne(counted_exit!(ocb, side_exit, send_splatarray_length_not_equal).into()); let array_opnd = ctx.stack_pop(1); @@ -4329,10 +4317,8 @@ fn push_splat_args(argc: i32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut for i in (0..required_args as i32) { let top = ctx.stack_push(Type::Unknown); asm.mov(top, Opnd::mem(64, ary_opnd, i * (SIZEOF_VALUE as i32))); - argc += 1; } } - argc } fn gen_send_iseq( @@ -4448,12 +4434,25 @@ fn gen_send_iseq( let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) }; let opts_missing: i32 = opt_num - opts_filled; + if opt_num > 0 && flags & VM_CALL_ARGS_SPLAT != 0 { gen_counter_incr!(asm, send_iseq_complex_callee); return CantCompile; } - if opts_filled < 0 || opts_filled > opt_num { + if doing_kw_call && flags & VM_CALL_ARGS_SPLAT != 0 { + gen_counter_incr!(asm, send_iseq_complex_callee); + return CantCompile; + } + + if opts_filled < 0 && flags & VM_CALL_ARGS_SPLAT == 0 { + // Too few arguments and no splat to make up for it + gen_counter_incr!(asm, send_iseq_arity_error); + return CantCompile; + } + + if opts_filled > opt_num { + // Too many arguments gen_counter_incr!(asm, send_iseq_arity_error); return CantCompile; } @@ -4596,9 +4595,14 @@ fn gen_send_iseq( asm.cmp(CFP, stack_limit); asm.jbe(counted_exit!(ocb, side_exit, send_se_cf_overflow).into()); - // push_splat_args does a ctx.stack_push so we can no longer side exit + // push_splat_args does stack manipulation so we can no longer side exit if flags & VM_CALL_ARGS_SPLAT != 0 { - argc = push_splat_args(argc, ctx, asm, ocb, side_exit, num_params); + let required_args = num_params as i32 - (argc - 1); + // We are going to assume that the splat fills + // all the remaining arguments. In the generated code + // we test if this is true and if not side exit. + argc = num_params as i32; + push_splat_args(required_args, ctx, asm, ocb, side_exit) } if doing_kw_call { @@ -5077,8 +5081,9 @@ fn gen_send_general( // To handle the aliased method case (VM_METHOD_TYPE_ALIAS) loop { let def_type = unsafe { get_cme_def_type(cme) }; - if flags & VM_CALL_ARGS_SPLAT != 0 && (def_type != VM_METHOD_TYPE_ISEQ && def_type != VM_METHOD_TYPE_CFUNC) { - // We can't handle splat calls to non-iseq methods + + if flags & VM_CALL_ARGS_SPLAT != 0 && def_type != VM_METHOD_TYPE_ISEQ { + gen_counter_incr!(asm, send_args_splat_non_iseq); return CantCompile; } diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index 56aa0b408122dc..5fc83c38961bf2 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -193,9 +193,10 @@ make_counters! { send_getter_arity, send_se_cf_overflow, send_se_protected_check_failed, - send_splatarray_rhs_too_small, + send_splatarray_length_not_equal, send_splat_not_array, - send_args_splat_variadic, + send_args_splat_non_iseq, + send_args_splat_cfunc, send_iseq_ruby2_keywords, traced_cfunc_return, From 3ff65dcd8b36e6ad4efa4e204ebb9dfaeb5fc12f Mon Sep 17 00:00:00 2001 From: git Date: Fri, 16 Sep 2022 13:37:37 +0900 Subject: [PATCH 103/269] * 2022-09-16 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index d14f810366489b..aad84343405b72 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 15 +#define RUBY_RELEASE_DAY 16 #include "ruby/version.h" #include "ruby/internal/abi.h" From c8d94d2797f798e2666a057bb1940e1ffe41b717 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 16 Sep 2022 14:46:21 +0900 Subject: [PATCH 104/269] Now test-bundler nees fake.rb --- common.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.mk b/common.mk index 44f5190b6f87df..36fa9a2b39c942 100644 --- a/common.mk +++ b/common.mk @@ -1461,7 +1461,7 @@ no-test-syntax-suggest: test-bundler-precheck: $(TEST_RUNNABLE)-test-bundler-precheck no-test-bundler-precheck: -yes-test-bundler-precheck: main +yes-test-bundler-precheck: main $(arch)-fake.rb no-test-bundler-prepare: no-test-bundler-precheck yes-test-bundler-prepare: yes-test-bundler-precheck From 6ad69944572adddaf608aa33e4fdef80100bee08 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 16 Sep 2022 17:03:18 +0900 Subject: [PATCH 105/269] Omit a DRb test on MinGW This test seems to leak a thread and let TestIOWait fail: https://github.com/ruby/ruby/actions/runs/3065426880/jobs/4949517274 DRb almost never seemed to stably work on MinGW. I don't think we intend to fix it either. We should just omit DRb tests when they fail on MinGW. --- test/drb/test_drb.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/drb/test_drb.rb b/test/drb/test_drb.rb index 1ee1b265d0f77f..11e2219badcf16 100644 --- a/test/drb/test_drb.rb +++ b/test/drb/test_drb.rb @@ -335,6 +335,7 @@ def test_any_to_s class TestDRbTCP < Test::Unit::TestCase def test_immediate_close + omit 'MinGW leaks a thread in this test' if /mingw/ =~ RUBY_PLATFORM server = DRb::DRbServer.new('druby://localhost:0') host, port, = DRb::DRbTCPSocket.send(:parse_uri, server.uri) socket = TCPSocket.open host, port @@ -345,8 +346,8 @@ def test_immediate_close ensure client&.close socket&.close - server.stop_service - server.thread.join + server&.stop_service + server&.thread&.join DRb::DRbConn.stop_pool end end From a28e7871e54d7a87afbfd686291c500d71edb7cb Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 16 Sep 2022 18:12:46 +0900 Subject: [PATCH 106/269] Update bundled_gems Try latest patch to avoid some race on Mac OS X. --- gems/bundled_gems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/bundled_gems b/gems/bundled_gems index 9d57e838b36008..e8cb03d38cbdc6 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -13,4 +13,4 @@ matrix 0.4.2 https://github.com/ruby/matrix prime 0.1.2 https://github.com/ruby/prime rbs 2.6.0 https://github.com/ruby/rbs 5ec9d53efe4bf0a97f33c3016aed430be135583a typeprof 0.21.3 https://github.com/ruby/typeprof -debug 1.6.2 https://github.com/ruby/debug 2cb44483c681f6806bf6843a8f996d3a10d0e42c +debug 1.6.2 https://github.com/ruby/debug 19b4dde3308f532943e4234d1588d4fa26c52345 From 5b735d0bebe0d6187780d1c2ddea0b0861b15df4 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 16 Sep 2022 20:44:58 +0900 Subject: [PATCH 107/269] Invalidate i-cache after link_labels (#6388) --- yjit/src/backend/arm64/mod.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 9d19c651145206..0a5068be58a736 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -971,17 +971,6 @@ impl Assembler }; } - // 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 _) }; - } - gc_offsets } @@ -996,12 +985,23 @@ impl Assembler assert!(label_idx == idx); } + let start_write_pos = cb.get_write_pos(); let gc_offsets = asm.arm64_emit(cb); if !cb.has_dropped_bytes() { 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 _) }; + } + gc_offsets } } From 64200990c4425ad3d104d4695dbe9cb3356555cd Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 16 Sep 2022 09:47:32 -0400 Subject: [PATCH 108/269] [ci skip] Fix typos in documentation in io.c --- io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io.c b/io.c index 675c0ad3c28587..494e40bcd931c5 100644 --- a/io.c +++ b/io.c @@ -4056,7 +4056,7 @@ rb_io_gets_internal(VALUE io) * * With only integer argument +limit+ given, * limits the number of bytes in the line; - * see {Line Limit}}[rdoc-ref:IO@Line+Limit]: + * see {Line Limit}[rdoc-ref:IO@Line+Limit]: * * # No more than one line. * File.open('t.txt') {|f| f.gets(10) } # => "First line" @@ -4313,7 +4313,7 @@ io_readlines(const struct getline_arg *arg, VALUE io) * * With only integer argument +limit+ given, * limits the number of bytes in each line; - * see {Line Limit}}[rdoc-ref:IO@Line+Limit]: + * see {Line Limit}[rdoc-ref:IO@Line+Limit]: * * f = File.new('t.txt') * f.each_line(8) {|line| p line } From b07db967441161a84386bcbbb41d990a2f3ad31c Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sat, 17 Sep 2022 02:25:15 +0900 Subject: [PATCH 109/269] [ruby/irb] Support --noscript option to not use first non-option argument as script Also add --script option to turn the option back on. Previously there wasn't a way to get an interactive IRB session and access arguments provided on the command line. Additionally, handle `-` as script as stdin. In Unix-like tools, `-` means to take standard input instead of a file. This doesn't result in exactly the same output for: ``` echo 'p ARGV' > args.rb; irb args.rb a b c ``` and ``` echo 'p ARGV' | irb - a b c ``` Due to how irb handles whether stdin is a tty. However, this change allows use of `-` as a argument, instead of giving an unrecognized switch error. This required some small changes to context.rb (to handle `-` as standard input) and input-method.rb (to have FileInputMethod accept IO arguments in addition to strings). Implements [Feature #15371] https://github.com/ruby/irb/commit/4192683ba2 --- lib/irb/context.rb | 4 ++++ lib/irb/init.rb | 16 +++++++++++---- lib/irb/input-method.rb | 2 +- lib/irb/lc/help-message | 2 ++ test/irb/test_init.rb | 45 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/lib/irb/context.rb b/lib/irb/context.rb index e6c993d4230e6a..b74cae12231651 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -115,6 +115,10 @@ def initialize(irb, workspace = nil, input_method = nil) end @io = StdioInputMethod.new unless @io + when '-' + @io = FileInputMethod.new($stdin) + @irb_name = '-' + @irb_path = '-' when String @io = FileInputMethod.new(input_method) @irb_name = File.basename(input_method) diff --git a/lib/irb/init.rb b/lib/irb/init.rb index d2baee2017e241..d9c4353f39ced4 100644 --- a/lib/irb/init.rb +++ b/lib/irb/init.rb @@ -289,6 +289,10 @@ def IRB.parse_opts(argv: ::ARGV) @CONF[:PROMPT_MODE] = prompt_mode when "--noprompt" @CONF[:PROMPT_MODE] = :NULL + when "--script" + noscript = false + when "--noscript" + noscript = true when "--inf-ruby-mode" @CONF[:PROMPT_MODE] = :INF_RUBY when "--sample-book-mode", "--simple-prompt" @@ -309,16 +313,20 @@ def IRB.parse_opts(argv: ::ARGV) IRB.print_usage exit 0 when "--" - if opt = argv.shift + if !noscript && (opt = argv.shift) @CONF[:SCRIPT] = opt $0 = opt end break - when /^-/ + when /^-./ fail UnrecognizedSwitch, opt else - @CONF[:SCRIPT] = opt - $0 = opt + if noscript + argv.unshift(opt) + else + @CONF[:SCRIPT] = opt + $0 = opt + end break end end diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index b77fd3207def87..eec2daa549c22a 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -137,7 +137,7 @@ def open(file, &block) # Creates a new input method object def initialize(file) super - @io = IRB::MagicFile.open(file) + @io = file.is_a?(IO) ? file : IRB::MagicFile.open(file) @external_encoding = @io.external_encoding end # The file name of this input method, usually given during initialization. diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message index 3405fa775f5acc..5b23f4c41ed2c2 100644 --- a/lib/irb/lc/help-message +++ b/lib/irb/lc/help-message @@ -38,6 +38,8 @@ Usage: irb.rb [options] [programfile] [arguments] --sample-book-mode, --simple-prompt Set prompt mode to 'simple'. --noprompt Don't output prompt. + --script Script mode (default, treat first argument as script) + --noscript No script mode (leave arguments in argv) --single-irb Share self with sub-irb. --tracer Show stack trace for each command. --back-trace-limit n[=16] diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb index d8c7c79263be4a..3293b98d345264 100644 --- a/test/irb/test_init.rb +++ b/test/irb/test_init.rb @@ -16,6 +16,7 @@ def setup def teardown ENV.update(@backup_env) FileUtils.rm_rf(@tmpdir) + IRB.conf.delete(:SCRIPT) end def test_setup_with_argv_preserves_global_argv @@ -87,6 +88,50 @@ def test_no_color_environment_variable IRB.conf[:USE_COLORIZE] = orig_use_colorize end + def test_noscript + argv = %w[--noscript -- -f] + IRB.setup(eval("__FILE__"), argv: argv) + assert_nil IRB.conf[:SCRIPT] + assert_equal(['-f'], argv) + + argv = %w[--noscript -- a] + IRB.setup(eval("__FILE__"), argv: argv) + assert_nil IRB.conf[:SCRIPT] + assert_equal(['a'], argv) + + argv = %w[--noscript a] + IRB.setup(eval("__FILE__"), argv: argv) + assert_nil IRB.conf[:SCRIPT] + assert_equal(['a'], argv) + + argv = %w[--script --noscript a] + IRB.setup(eval("__FILE__"), argv: argv) + assert_nil IRB.conf[:SCRIPT] + assert_equal(['a'], argv) + + argv = %w[--noscript --script a] + IRB.setup(eval("__FILE__"), argv: argv) + assert_equal('a', IRB.conf[:SCRIPT]) + assert_equal([], argv) + end + + def test_dash + argv = %w[-] + IRB.setup(eval("__FILE__"), argv: argv) + assert_equal('-', IRB.conf[:SCRIPT]) + assert_equal([], argv) + + argv = %w[-- -] + IRB.setup(eval("__FILE__"), argv: argv) + assert_equal('-', IRB.conf[:SCRIPT]) + assert_equal([], argv) + + argv = %w[-- - -f] + IRB.setup(eval("__FILE__"), argv: argv) + assert_equal('-', IRB.conf[:SCRIPT]) + assert_equal(['-f'], argv) + end + private def with_argv(argv) From e3a32abe2ef726c552ed04603413a0b7befd3ec0 Mon Sep 17 00:00:00 2001 From: git Date: Sat, 17 Sep 2022 02:25:39 +0900 Subject: [PATCH 110/269] * 2022-09-17 [ci skip] --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index aad84343405b72..7ce07104d1be88 100644 --- a/version.h +++ b/version.h @@ -15,7 +15,7 @@ #define RUBY_RELEASE_YEAR 2022 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 16 +#define RUBY_RELEASE_DAY 17 #include "ruby/version.h" #include "ruby/internal/abi.h" From 6898984f1cd1b0375c3da44d7832724489e0e470 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 17 Sep 2022 12:09:34 +0900 Subject: [PATCH 111/269] [Bug #19005] dynamic_lookup linker option in external libraries The warning against `-undefined dynamic_lookup` is just a warning yet, and many gems seem to pay no attention to warnings. Until it fails actually, keep it as a migration path, except for standard extension libraries and bundled extension gems. --- configure.ac | 36 ++++++++++++++++++++++++------------ lib/mkmf.rb | 1 + 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index 43f1541f3b6fcc..8cf0f58067a4cf 100644 --- a/configure.ac +++ b/configure.ac @@ -2956,7 +2956,7 @@ STATIC= } : "rpath" && { - AS_CASE(["$target_os"], + AS_CASE(["$target_os"], [solaris*], [ AS_IF([test "$GCC" = yes], [ : ${LDSHARED='$(CC) -shared'} AS_IF([test "$rb_cv_prog_gnu_ld" = yes], [ @@ -3055,23 +3055,35 @@ STATIC= [atheos*], [ : ${LDSHARED='$(CC) -shared'} rb_cv_dlopen=yes], [ : ${LDSHARED='$(LD)'}]) - AC_MSG_RESULT($rb_cv_dlopen) + AC_MSG_RESULT($rb_cv_dlopen) +} - AS_IF([test "$rb_cv_dlopen" = yes], [ +AS_IF([test "$rb_cv_dlopen" = yes], [ AS_CASE(["$target_os"], - [darwin*], [ + [darwin*], [ + AC_SUBST(ADDITIONAL_DLDFLAGS, "") for flag in \ "-multiply_defined suppress" \ + "-undefined dynamic_lookup" \ ; do - test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`" - RUBY_TRY_LDFLAGS([$flag], [], [flag=]) - AS_IF([test "x$flag" != x], [ - RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag]) - ]) + test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`" + RUBY_TRY_LDFLAGS([$flag], [], [$flag=]) + AS_IF([test x"$flag" = x], [continue]) + + AC_MSG_CHECKING([whether $flag is accepted for bundle]) + : > conftest.c + AS_IF([${LDSHARED/'$(CC)'/$CC} -o conftest.bundle $flag conftest.c >/dev/null 2>conftest.err && + test ! -s conftest.err], [ + AC_MSG_RESULT([yes]) + RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag]) + ], [ + AC_MSG_RESULT([no]) + RUBY_APPEND_OPTIONS(ADDITIONAL_DLDFLAGS, [$flag]) + ]) + rm -fr conftest.* done - ]) - ]) -} + ]) +]) AS_IF([test "${LDSHAREDXX}" = ""], [ AS_CASE(["${LDSHARED}"], diff --git a/lib/mkmf.rb b/lib/mkmf.rb index efe3419fd700fb..61ca9e53acdd11 100644 --- a/lib/mkmf.rb +++ b/lib/mkmf.rb @@ -2599,6 +2599,7 @@ def init_mkmf(config = CONFIG, rbconfig = RbConfig::CONFIG) $INCFLAGS << " -I$(hdrdir)/ruby/backward" unless $extmk $INCFLAGS << " -I$(hdrdir) -I$(srcdir)" $DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup + config_string("ADDITIONAL_DLDFLAGS") {|flags| $DLDFLAGS << " " << flags} unless $extmk $LIBEXT = config['LIBEXT'].dup $OBJEXT = config["OBJEXT"].dup $EXEEXT = config["EXEEXT"].dup From 76c00565052368f693bc4e18e8e40b614f744d74 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 18:03:48 +0900 Subject: [PATCH 112/269] Follow emoji data files header change The header of emoji data files in UCD, which were moved at 13.0.0, has been changed since 14.0.0. It seems to be the same as other files in UCD. --- tool/enc-unicode.rb | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tool/enc-unicode.rb b/tool/enc-unicode.rb index 93f6e869f842fb..6fb0f36d5b6d7e 100755 --- a/tool/enc-unicode.rb +++ b/tool/enc-unicode.rb @@ -311,18 +311,19 @@ def get_file(name) def data_foreach(name, &block) fn = get_file(name) warn "Reading #{name}" - if /^emoji/ =~ name - sep = "" - pat = /^# #{Regexp.quote(File.basename(name))}.*^# Version: ([\d.]+)/m - type = :Emoji - else - sep = "\n" - pat = /^# #{File.basename(name).sub(/\./, '-([\\d.]+)\\.')}/ - type = :Unicode - end File.open(fn, 'rb') do |f| - line = f.gets(sep) - unless version = line[pat, 1] + if /^emoji/ =~ name + line = f.gets("") + # Headers till Emoji 13 + version = line[/^# #{Regexp.quote(File.basename(name))}.*^# Version: ([\d.]+)/m, 1] + type = :Emoji + else + # Headers since Emoji 14 or other Unicode data + line = f.gets("\n") + type = :Unicode + end + version ||= line[/^# #{File.basename(name).sub(/\./, '-([\\d.]+)\\.')}/, 1] + unless version raise ArgumentError, <<-ERROR #{name}: no #{type} version #{line.gsub(/^/, '> ')} @@ -330,7 +331,7 @@ def data_foreach(name, &block) end if !(v = $versions[type]) $versions[type] = version - elsif v != version + elsif v != version and "#{v}.0" != version raise ArgumentError, <<-ERROR #{name}: #{type} version mismatch: #{version} to #{v} #{line.gsub(/^/, '> ')} From 03ce48dac74099dd725c7ab8dce5ca8b20df65f5 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 18:56:51 +0900 Subject: [PATCH 113/269] Emoji files header changed at 15.0 again --- tool/enc-unicode.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/enc-unicode.rb b/tool/enc-unicode.rb index 6fb0f36d5b6d7e..60d3fca753a9b2 100755 --- a/tool/enc-unicode.rb +++ b/tool/enc-unicode.rb @@ -314,8 +314,8 @@ def data_foreach(name, &block) File.open(fn, 'rb') do |f| if /^emoji/ =~ name line = f.gets("") - # Headers till Emoji 13 - version = line[/^# #{Regexp.quote(File.basename(name))}.*^# Version: ([\d.]+)/m, 1] + # Headers till Emoji 13 or 15 + version = line[/^# #{Regexp.quote(File.basename(name))}.*(?:^# Version:|Emoji Version) ([\d.]+)/m, 1] type = :Emoji else # Headers since Emoji 14 or other Unicode data From e9982fd679cb7cb80fbf65fa0090fd01f94884c7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 19:00:23 +0900 Subject: [PATCH 114/269] Derive UNICODE_EMOJI_VERSION from UNICODE_VERSION --- common.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common.mk b/common.mk index 36fa9a2b39c942..5e34aedcbcd136 100644 --- a/common.mk +++ b/common.mk @@ -19,7 +19,9 @@ gnumake_recursive = enable_shared = $(ENABLE_SHARED:no=) UNICODE_VERSION = 14.0.0 -UNICODE_EMOJI_VERSION = 14.0 +UNICODE_EMOJI_VERSION_0 = $(UNICODE_VERSION)/// +UNICODE_EMOJI_VERSION_1 = $(UNICODE_EMOJI_VERSION_0:.0///=) +UNICODE_EMOJI_VERSION = $(UNICODE_EMOJI_VERSION_1:///=) UNICODE_BETA = NO ### set the following environment variable or uncomment the line if From ca4cbe59eda77a3855094c843486759868794e85 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 14 Sep 2022 19:15:45 +0900 Subject: [PATCH 115/269] Move case-folding.rb to tooldir with enc-prefix --- common.mk | 6 +++--- enc/unicode/case-folding.rb => tool/enc-case-folding.rb | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename enc/unicode/case-folding.rb => tool/enc-case-folding.rb (98%) mode change 100644 => 100755 diff --git a/common.mk b/common.mk index 5e34aedcbcd136..ad492c00795e7a 100644 --- a/common.mk +++ b/common.mk @@ -1644,19 +1644,19 @@ $(UNICODE_HDR_DIR)/name2ctype.h: $(MV) $@.new $@ # the next non-comment line was: -# $(UNICODE_HDR_DIR)/casefold.h: $(srcdir)/enc/unicode/case-folding.rb \ +# $(UNICODE_HDR_DIR)/casefold.h: $(tooldir)/enc-case-folding.rb \ # but was changed to make sure CI works on systems that don't have gperf unicode-up: $(UNICODE_DATA_HEADERS) $(UNICODE_HDR_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=casefold.h): \ - $(srcdir)/enc/unicode/case-folding.rb \ + $(tooldir)/enc-case-folding.rb \ $(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \ $(UNICODE_SRC_DATA_DIR)/SpecialCasing.txt \ $(UNICODE_SRC_DATA_DIR)/CaseFolding.txt $(UNICODE_HDR_DIR)/casefold.h: $(MAKEDIRS) $(@D) - $(Q) $(BASERUBY) $(srcdir)/enc/unicode/case-folding.rb \ + $(Q) $(BASERUBY) $(tooldir)/enc-case-folding.rb \ --output-file=$@ \ --mapping-data-directory=$(UNICODE_SRC_DATA_DIR) diff --git a/enc/unicode/case-folding.rb b/tool/enc-case-folding.rb old mode 100644 new mode 100755 similarity index 98% rename from enc/unicode/case-folding.rb rename to tool/enc-case-folding.rb index 4a29fdebf755ff..6d43a27df854ce --- a/enc/unicode/case-folding.rb +++ b/tool/enc-case-folding.rb @@ -3,12 +3,12 @@ # Usage (for case folding only): # $ wget http://www.unicode.org/Public/UNIDATA/CaseFolding.txt -# $ ruby case-folding.rb CaseFolding.txt -o casefold.h +# $ ruby enc-case-folding.rb CaseFolding.txt -o casefold.h # or (for case folding and case mapping): # $ wget http://www.unicode.org/Public/UNIDATA/CaseFolding.txt # $ wget http://www.unicode.org/Public/UNIDATA/UnicodeData.txt # $ wget http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt -# $ ruby case-folding.rb -m . -o casefold.h +# $ ruby enc-case-folding.rb -m . -o casefold.h # using -d or --debug will include UTF-8 characters in comments for debugging class CaseFolding @@ -158,7 +158,7 @@ def lookup_hash(key, type, data) def display(dest, mapping_data) # print the header dest.print("/* DO NOT EDIT THIS FILE. */\n") - dest.print("/* Generated by enc/unicode/case-folding.rb */\n\n") + dest.print("/* Generated by enc-case-folding.rb */\n\n") versions = version.scan(/\d+/) dest.print("#if defined ONIG_UNICODE_VERSION_STRING && !( \\\n") From 38a7a13ac6ed6bba2584def7d7121257eb85e051 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 17 Sep 2022 21:16:06 +0900 Subject: [PATCH 116/269] Auto-generate the release date on version.h from git CommitDate (#6382) * Auto-generate the release date on version.h from git CommitDate * Generate revision.h on mswin --- common.mk | 3 ++- tool/file2lastrev.rb | 10 ++++++++-- tool/lib/vcs.rb | 22 ++++++++++++++++++---- tool/make-snapshot | 2 +- version.h | 13 +++++-------- win32/Makefile.sub | 2 ++ win32/ifchange.bat | 1 + 7 files changed, 37 insertions(+), 16 deletions(-) diff --git a/common.mk b/common.mk index ad492c00795e7a..fb1b785534be45 100644 --- a/common.mk +++ b/common.mk @@ -1214,7 +1214,7 @@ $(BUILTIN_RB_INCS): $(top_srcdir)/tool/mk_builtin_loader.rb $(srcdir)/revision.h: $(srcdir)/revision.h$(gnumake:yes=-nongnumake): $(Q)$(RM) $(@F) - $(Q)$(NULLCMD) > $@ || $(NULLCMD) > $(@F) + $(NULLCMD) > $(@F) revision.tmp:: $(Q) $(NULLCMD) > $@ @@ -8275,6 +8275,7 @@ load.$(OBJEXT): {$(VPATH)}vm_core.h load.$(OBJEXT): {$(VPATH)}vm_opts.h loadpath.$(OBJEXT): $(hdrdir)/ruby/ruby.h loadpath.$(OBJEXT): $(hdrdir)/ruby/version.h +loadpath.$(OBJEXT): $(top_srcdir)/revision.h loadpath.$(OBJEXT): $(top_srcdir)/version.h loadpath.$(OBJEXT): {$(VPATH)}assert.h loadpath.$(OBJEXT): {$(VPATH)}backward/2/assume.h diff --git a/tool/file2lastrev.rb b/tool/file2lastrev.rb index 008e4b55e5bca6..476437a995dd4b 100755 --- a/tool/file2lastrev.rb +++ b/tool/file2lastrev.rb @@ -66,7 +66,13 @@ def self.output=(output) new_vcs["."] end } -exit unless vcs +unless vcs + # Output only release_date when .git is missing + if @output == :revision_h + puts VCS.release_date(Time.now - 10) # same as make-snapshot + end + exit +end @output = case @output @@ -76,7 +82,7 @@ def self.output=(output) } when :revision_h Proc.new {|last, changed, modified, branch, title| - vcs.revision_header(last, modified, branch, title, limit: @limit) + vcs.revision_header(last, modified, modified, branch, title, limit: @limit) } when :doxygen Proc.new {|last, changed| diff --git a/tool/lib/vcs.rb b/tool/lib/vcs.rb index 05d95061c4f456..d33d41dba18607 100644 --- a/tool/lib/vcs.rb +++ b/tool/lib/vcs.rb @@ -95,6 +95,15 @@ def self.define_options(parser, opts = {}) opts end + def self.release_date(time) + t = time.utc + [ + t.strftime('#define RUBY_RELEASE_YEAR %Y'), + t.strftime('#define RUBY_RELEASE_MONTH %-m'), + t.strftime('#define RUBY_RELEASE_DAY %-d'), + ] + end + attr_reader :srcdir def initialize(path) @@ -204,7 +213,8 @@ def short_revision(rev) revision_handler(rev).short_revision(rev) end - def revision_header(last, modified = nil, branch = nil, title = nil, limit: 20) + # make-snapshot generates only release_date whereas file2lastrev generates both release_date and release_datetime + def revision_header(last, release_date, release_datetime = nil, branch = nil, title = nil, limit: 20) short = short_revision(last) if /[^\x00-\x7f]/ =~ title and title.respond_to?(:force_encoding) title = title.dup.force_encoding("US-ASCII") @@ -225,10 +235,11 @@ def revision_header(last, modified = nil, branch = nil, title = nil, limit: 20) title = title.dump.sub(/\\#/, '#') code << "#define RUBY_LAST_COMMIT_TITLE #{title}" end - if modified - t = modified.utc + if release_datetime + t = release_datetime.utc code << t.strftime('#define RUBY_RELEASE_DATETIME "%FT%TZ"') end + code += VCS.release_date(release_date) code end @@ -386,7 +397,10 @@ def commit end class GIT < self - register(".git") {|path, dir| File.exist?(File.join(path, dir))} + register(".git") do |path, dir| + File.exist?(File.join(path, dir)) && + (`#{COMMAND} -v` rescue false) # make sure git command exists + end COMMAND = ENV["GIT"] || 'git' def cmd_args(cmds, srcdir = nil) diff --git a/tool/make-snapshot b/tool/make-snapshot index 02b5d182f5a213..22ae360c3ea8e4 100755 --- a/tool/make-snapshot +++ b/tool/make-snapshot @@ -347,7 +347,7 @@ def package(vcs, rev, destdir, tmp = nil) end File.open("#{v}/revision.h", "wb") {|f| - f.puts vcs.revision_header(revision) + f.puts vcs.revision_header(revision, modified) } version ||= (versionhdr = IO.read("#{v}/version.h"))[RUBY_VERSION_PATTERN, 1] version ||= diff --git a/version.h b/version.h index 7ce07104d1be88..46f14e3f14ff3f 100644 --- a/version.h +++ b/version.h @@ -13,13 +13,12 @@ #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR #define RUBY_PATCHLEVEL -1 -#define RUBY_RELEASE_YEAR 2022 -#define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 17 - #include "ruby/version.h" #include "ruby/internal/abi.h" +#ifndef RUBY_REVISION +#include "revision.h" + #ifndef TOKEN_PASTE #define TOKEN_PASTE(x,y) x##y #endif @@ -38,6 +37,8 @@ #define RUBY_RELEASE_DAY_STR STRINGIZE(RUBY_RELEASE_DAY) #endif +#endif + #ifdef RUBY_ABI_VERSION # define RUBY_ABI_VERSION_SUFFIX "+"STRINGIZE(RUBY_ABI_VERSION) #else @@ -61,8 +62,4 @@ #define RUBY_PATCHLEVEL_STR "" #endif -#ifndef RUBY_REVISION -# include "revision.h" -#endif - #endif /* RUBY_TOPLEVEL_VERSION_H */ diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 361af556cdab9e..e802b809682e99 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1244,6 +1244,8 @@ $(RCFILES): $(RBCONFIG) $(srcdir)/revision.h $(srcdir)/win32/resource.rb -so_name=$(RUBY_SO_NAME) \ . $(icondirs) $(win_srcdir) +$(srcdir)/revision.h: $(REVISION_H) + update-benchmark-driver: $(GIT) clone https://github.com/benchmark-driver/benchmark-driver $(srcdir)/benchmark/benchmark-driver || \ $(GIT) -C $(srcdir)/benchmark/benchmark-driver pull origin master diff --git a/win32/ifchange.bat b/win32/ifchange.bat index c7db628a0428c8..fb9ad27337c77b 100755 --- a/win32/ifchange.bat +++ b/win32/ifchange.bat @@ -89,6 +89,7 @@ if exist %dest% ( ) ) for %%I in (%1) do echo %%~I updated +del /f %dest% copy %src% %dest% > nul del %src% From 1825d3673f2a5504b65ba31a9fb6c2e52edf1fc3 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 17 Sep 2022 22:06:53 +0900 Subject: [PATCH 117/269] Skip test_wait on MinGW CI This test has been unstable, and it seems like we're not interested in fixing that for MinGW. https://github.com/ruby/ruby/actions/runs/3073317191/jobs/4965373284 --- test/io/wait/test_io_wait.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/io/wait/test_io_wait.rb b/test/io/wait/test_io_wait.rb index fb42b6a70087a1..8a2b6c2d12a2b4 100644 --- a/test/io/wait/test_io_wait.rb +++ b/test/io/wait/test_io_wait.rb @@ -50,6 +50,7 @@ def test_buffered_ready? end def test_wait + omit 'unstable on MinGW' if /mingw/ =~ RUBY_PLATFORM assert_nil @r.wait(0) @w.syswrite "." sleep 0.1 From 33c6dd2cc89c27bbf406508ec39038a181fb99bc Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 17 Sep 2022 22:24:09 +0900 Subject: [PATCH 118/269] Fallback to VCS.release_date on VCS::NotFoundError when -q is given. One of the RubyCI servers, freebsd12, had a broken git environment: ``` $ git show fatal: detected dubious ownership in repository at '/usr/home/chkbuild/chkbuild/tmp/build/20220917T123002Z/ruby' To add an exception for this directory, call: git config --global --add safe.directory /usr/home/chkbuild/chkbuild/tmp/build/20220917T123002Z/ruby ``` tool/lib/vcs.rb doesn't work normally for that server. Even for such cases, we'd like to generate a usable revision.h. So this patch lets revision.h fallback to default VCS.release_date when VCS::NotFoundError is raised. --- tool/file2lastrev.rb | 13 +++++++------ tool/lib/vcs.rb | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tool/file2lastrev.rb b/tool/file2lastrev.rb index 476437a995dd4b..a3cdcf05c3769f 100755 --- a/tool/file2lastrev.rb +++ b/tool/file2lastrev.rb @@ -68,13 +68,11 @@ def self.output=(output) } unless vcs # Output only release_date when .git is missing - if @output == :revision_h - puts VCS.release_date(Time.now - 10) # same as make-snapshot - end + puts VCS.release_date if @output == :revision_h exit end -@output = +output = case @output when :changed, nil Proc.new {|last, changed| @@ -99,9 +97,12 @@ def self.output=(output) ok = true (ARGV.empty? ? [nil] : ARGV).each do |arg| begin - puts @output[*vcs.get_revisions(arg)] + puts output[*vcs.get_revisions(arg)] rescue => e - next if @suppress_not_found and VCS::NotFoundError === e + if @suppress_not_found and VCS::NotFoundError === e + puts VCS.release_date if @output == :revision_h + next + end warn "#{File.basename(Program)}: #{e.message}" ok = false end diff --git a/tool/lib/vcs.rb b/tool/lib/vcs.rb index d33d41dba18607..29f2327bc3a173 100644 --- a/tool/lib/vcs.rb +++ b/tool/lib/vcs.rb @@ -95,7 +95,7 @@ def self.define_options(parser, opts = {}) opts end - def self.release_date(time) + def self.release_date(time = Time.now - 10) # the same default as make-snapshot t = time.utc [ t.strftime('#define RUBY_RELEASE_YEAR %Y'), From 922e61fcc22abfc86a9805ccf52ba9f8edeca17c Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 17 Sep 2022 23:05:06 +0900 Subject: [PATCH 119/269] Remove git command existence check again With 33c6dd2cc89c27bbf406508ec39038a181fb99bc, it's no longer necessary. This is what I got on openbsd-current: ``` -bash-5.1$ git -v unknown option: -v usage: git [--version] [--help] [-C ] [-c =] [--exec-path[=]] [--html-path] [--man-path] [--info-path] [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare] [--git-dir=] [--work-tree=] [--namespace=] [--super-prefix=] [--config-env==] [] -bash-5.1$ git version git version 2.35.1 ``` --- tool/lib/vcs.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tool/lib/vcs.rb b/tool/lib/vcs.rb index 29f2327bc3a173..87ed15f7d16a44 100644 --- a/tool/lib/vcs.rb +++ b/tool/lib/vcs.rb @@ -397,10 +397,7 @@ def commit end class GIT < self - register(".git") do |path, dir| - File.exist?(File.join(path, dir)) && - (`#{COMMAND} -v` rescue false) # make sure git command exists - end + register(".git") { |path, dir| File.exist?(File.join(path, dir)) } COMMAND = ENV["GIT"] || 'git' def cmd_args(cmds, srcdir = nil) From 39f91bc24baa37db3c3d1485902056f43b013b54 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 17 Sep 2022 23:16:55 +0900 Subject: [PATCH 120/269] Always generate non-empty revision.h Non-GNU make seems to generate empty revision.h, but it doesn't make sense since https://github.com/ruby/ruby/pull/6382. Also the $(HAVE_BASERUBY:yes=tmp) hack doesn't seem to be working on OpenBSD. I'll remove it to focus on fixing RubyCI first, and then deal with baseruby-missing environments. At least a snapshot should have revision.h and it might work fine though. --- common.mk | 11 +++-------- defs/gmake.mk | 13 ------------- win32/Makefile.sub | 2 -- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/common.mk b/common.mk index fb1b785534be45..90d507806ad8c4 100644 --- a/common.mk +++ b/common.mk @@ -1211,14 +1211,9 @@ builtin_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/template/builtin_binary $(BUILTIN_RB_INCS): $(top_srcdir)/tool/mk_builtin_loader.rb -$(srcdir)/revision.h: -$(srcdir)/revision.h$(gnumake:yes=-nongnumake): - $(Q)$(RM) $(@F) - $(NULLCMD) > $(@F) - -revision.tmp:: - $(Q) $(NULLCMD) > $@ -revision.$(HAVE_BASERUBY:yes=tmp):: $(srcdir)/version.h $(tooldir)/file2lastrev.rb $(REVISION_FORCE) +$(srcdir)/revision.h: $(REVISION_H) + +revision.tmp:: $(srcdir)/version.h $(tooldir)/file2lastrev.rb $(REVISION_FORCE) $(Q) $(BASERUBY) $(tooldir)/file2lastrev.rb -q --revision.h --srcdir="$(srcdir)" > $@ $(REVISION_H): revision.tmp diff --git a/defs/gmake.mk b/defs/gmake.mk index c593c1c033ddad..cebb181fd12233 100644 --- a/defs/gmake.mk +++ b/defs/gmake.mk @@ -383,19 +383,6 @@ $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time: \ $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES) endif -ifeq ($(wildcard $(srcdir)/revision.h),) -REVISION_IN_HEADER := none -REVISION_LATEST := update -else -REVISION_IN_HEADER := $(shell sed -n 's/^\#define RUBY_FULL_REVISION "\(.*\)"/\1/p' $(srcdir)/revision.h 2>/dev/null) -REVISION_LATEST := $(shell $(CHDIR) $(srcdir) && $(GIT) log -1 --format=%H 2>/dev/null) -endif -ifneq ($(REVISION_IN_HEADER),$(REVISION_LATEST)) -# GNU make treat the target as unmodified when its dependents get -# updated but it is not updated, while others may not. -$(srcdir)/revision.h: $(REVISION_H) -endif - include $(top_srcdir)/yjit/yjit.mk # Query on the generated rdoc diff --git a/win32/Makefile.sub b/win32/Makefile.sub index e802b809682e99..361af556cdab9e 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1244,8 +1244,6 @@ $(RCFILES): $(RBCONFIG) $(srcdir)/revision.h $(srcdir)/win32/resource.rb -so_name=$(RUBY_SO_NAME) \ . $(icondirs) $(win_srcdir) -$(srcdir)/revision.h: $(REVISION_H) - update-benchmark-driver: $(GIT) clone https://github.com/benchmark-driver/benchmark-driver $(srcdir)/benchmark/benchmark-driver || \ $(GIT) -C $(srcdir)/benchmark/benchmark-driver pull origin master From 67417e795a3e5af8e64d3b3c3440ff5e7306707d Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 00:09:41 +0900 Subject: [PATCH 121/269] Replace revision.tmp with the HAVE_BASERUBY trick but without relying on replacement. This seems to work on OpenBSD as well. --- common.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common.mk b/common.mk index 90d507806ad8c4..5f016d6fc34b21 100644 --- a/common.mk +++ b/common.mk @@ -1213,7 +1213,9 @@ $(BUILTIN_RB_INCS): $(top_srcdir)/tool/mk_builtin_loader.rb $(srcdir)/revision.h: $(REVISION_H) -revision.tmp:: $(srcdir)/version.h $(tooldir)/file2lastrev.rb $(REVISION_FORCE) +revision.$(HAVE_BASERUBY:no=tmp):: + $(Q) $(NULLCMD) > $@ +revision.$(HAVE_BASERUBY:yes=tmp):: $(srcdir)/version.h $(tooldir)/file2lastrev.rb $(REVISION_FORCE) $(Q) $(BASERUBY) $(tooldir)/file2lastrev.rb -q --revision.h --srcdir="$(srcdir)" > $@ $(REVISION_H): revision.tmp From 0e816e6d3022892da438940b09c4d4b49ceb670f Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 14:02:09 +0900 Subject: [PATCH 122/269] Demote mjit_instruction.rb from builtin to stdlib --- .gitignore | 2 +- common.mk | 7 +------ inits.c | 1 - mjit_compiler.c | 1 - mjit_compiler.rb | 1 + template/Makefile.in | 9 +++++---- .../{mjit_instruction.rb.erb => instruction.rb.erb} | 0 win32/Makefile.sub | 4 ---- 8 files changed, 8 insertions(+), 17 deletions(-) rename tool/ruby_vm/views/{mjit_instruction.rb.erb => instruction.rb.erb} (100%) diff --git a/.gitignore b/.gitignore index 52089ac21cf3a7..73012bb3ed1b40 100644 --- a/.gitignore +++ b/.gitignore @@ -236,7 +236,7 @@ lcov*.info /rb_mjit_header.h /mjit_config.h /include/ruby-*/*/rb_mjit_min_header-*.h -/mjit_instruction.rb +/lib/mjit/instruction.rb # /wasm/ /wasm/tests/*.wasm diff --git a/common.mk b/common.mk index 5f016d6fc34b21..2956e2c93e6ffc 100644 --- a/common.mk +++ b/common.mk @@ -1081,7 +1081,6 @@ BUILTIN_RB_SRCS = \ $(srcdir)/marshal.rb \ $(srcdir)/mjit.rb \ $(srcdir)/mjit_compiler.rb \ - $(srcdir)/mjit_instruction.rb \ $(srcdir)/pack.rb \ $(srcdir)/trace_point.rb \ $(srcdir)/warning.rb \ @@ -1173,7 +1172,7 @@ vm_call_iseq_optimized.inc: $(srcdir)/template/call_iseq_optimized.inc.tmpl $(ECHO) generating $@ $(Q) $(BASERUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/call_iseq_optimized.inc.tmpl -$(MINIPRELUDE_C): $(COMPILE_PRELUDE) $(BUILTIN_RB_SRCS) $(srcdir)/mjit_instruction.rb +$(MINIPRELUDE_C): $(COMPILE_PRELUDE) $(BUILTIN_RB_SRCS) $(ECHO) generating $@ $(Q) $(BASERUBY) $(tooldir)/generic_erb.rb -I$(srcdir) -o $@ \ $(srcdir)/template/prelude.c.tmpl $(BUILTIN_RB_SRCS) @@ -9302,7 +9301,6 @@ miniinit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h miniinit.$(OBJEXT): $(CCAN_DIR)/list/list.h miniinit.$(OBJEXT): $(CCAN_DIR)/str/str.h miniinit.$(OBJEXT): $(hdrdir)/ruby/ruby.h -miniinit.$(OBJEXT): $(srcdir)/mjit_instruction.rb miniinit.$(OBJEXT): $(top_srcdir)/internal/array.h miniinit.$(OBJEXT): $(top_srcdir)/internal/compilers.h miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h @@ -9494,7 +9492,6 @@ miniinit.$(OBJEXT): {$(VPATH)}miniprelude.c miniinit.$(OBJEXT): {$(VPATH)}missing.h miniinit.$(OBJEXT): {$(VPATH)}mjit.rb miniinit.$(OBJEXT): {$(VPATH)}mjit_compiler.rb -miniinit.$(OBJEXT): {$(VPATH)}mjit_instruction.rb miniinit.$(OBJEXT): {$(VPATH)}nilclass.rb miniinit.$(OBJEXT): {$(VPATH)}node.h miniinit.$(OBJEXT): {$(VPATH)}numeric.rb @@ -9748,7 +9745,6 @@ mjit_compiler.$(OBJEXT): $(CCAN_DIR)/list/list.h mjit_compiler.$(OBJEXT): $(CCAN_DIR)/str/str.h mjit_compiler.$(OBJEXT): $(hdrdir)/ruby.h mjit_compiler.$(OBJEXT): $(hdrdir)/ruby/ruby.h -mjit_compiler.$(OBJEXT): $(srcdir)/mjit_instruction.rb mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/array.h mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/class.h mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/compile.h @@ -9934,7 +9930,6 @@ mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.c mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.h mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rb mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rbinc -mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_instruction.rbinc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_unit.h mjit_compiler.$(OBJEXT): {$(VPATH)}node.h mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_assert.h diff --git a/inits.c b/inits.c index f43ece0e47a024..65a54d8f29b026 100644 --- a/inits.c +++ b/inits.c @@ -105,7 +105,6 @@ rb_call_builtin_inits(void) #if USE_MJIT BUILTIN(mjit); BUILTIN(mjit_compiler); - BUILTIN(mjit_instruction); #endif Init_builtin_prelude(); } diff --git a/mjit_compiler.c b/mjit_compiler.c index 5163bbcf6bf7a6..2594678f779aad 100644 --- a/mjit_compiler.c +++ b/mjit_compiler.c @@ -165,6 +165,5 @@ has_cache_for_send(rb_execution_context_t *ec, VALUE self, VALUE cc_addr, VALUE extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); #include "mjit_compiler.rbinc" -#include "mjit_instruction.rbinc" #endif // USE_MJIT diff --git a/mjit_compiler.rb b/mjit_compiler.rb index 9e032fc747703e..4996e773dcd2a5 100644 --- a/mjit_compiler.rb +++ b/mjit_compiler.rb @@ -132,5 +132,6 @@ def rb_cSymbol; Primitive.cexpr! 'PTR2NUM(rb_cSymbol)' end def rb_cFloat; Primitive.cexpr! 'PTR2NUM(rb_cFloat)' end end + require "mjit/instruction" require "mjit/compiler" end diff --git a/template/Makefile.in b/template/Makefile.in index a644a92754f425..06878d552f13ad 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -504,6 +504,11 @@ clean-local:: $(Q)$(RM) -r mjit_build_dir.* -$(Q) $(RMDIRS) $(MJIT_HEADER_INSTALL_DIR) $(MJIT_HEADER_BUILD_DIR) $(TIMESTAMPDIR) 2> $(NULL) || $(NULLCMD) +main: $(srcdir)/lib/mjit/instruction.rb +$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/instruction.rb.erb $(srcdir)/insns.def + $(ECHO) generating $@ + $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ + # DTrace static library hacks described here: # https://marc.info/?l=opensolaris-dtrace-discuss&m=114761203110734&w=4 ruby-glommed.$(OBJEXT): @@ -669,10 +674,6 @@ $(INSNS): $(srcdir)/insns.def vm_opts.h \ $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ -$(srcdir)/mjit_instruction.rb: $(tooldir)/ruby_vm/views/mjit_instruction.rb.erb - $(ECHO) generating $@ - $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ - loadpath: verconf.h @$(CPP) $(XCFLAGS) $(CPPFLAGS) $(srcdir)/loadpath.c | \ sed -e '1,/^const char ruby_initial_load_paths/d;/;/,$$d' \ diff --git a/tool/ruby_vm/views/mjit_instruction.rb.erb b/tool/ruby_vm/views/instruction.rb.erb similarity index 100% rename from tool/ruby_vm/views/mjit_instruction.rb.erb rename to tool/ruby_vm/views/instruction.rb.erb diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 361af556cdab9e..e84f978bb72e4f 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1351,10 +1351,6 @@ INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \ ! endif !endif -$(srcdir)/mjit_instruction.rb: $(tooldir)/ruby_vm/views/mjit_instruction.rb.erb - $(ECHO) generating $@ - $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ - verconf.h: verconf.mk loadpath: verconf.h From 62ec621f8c7457374d1f08aec97138ac1b7bdf2a Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 14:16:08 +0900 Subject: [PATCH 123/269] Preserve the directory structure under tool/ruby_vm/views for nested target directories --- template/Makefile.in | 2 +- tool/ruby_vm/helpers/dumper.rb | 3 ++- tool/ruby_vm/views/{ => lib/mjit}/instruction.rb.erb | 0 3 files changed, 3 insertions(+), 2 deletions(-) rename tool/ruby_vm/views/{ => lib/mjit}/instruction.rb.erb (100%) diff --git a/template/Makefile.in b/template/Makefile.in index 06878d552f13ad..5c2f81381e22f4 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -505,7 +505,7 @@ clean-local:: -$(Q) $(RMDIRS) $(MJIT_HEADER_INSTALL_DIR) $(MJIT_HEADER_BUILD_DIR) $(TIMESTAMPDIR) 2> $(NULL) || $(NULLCMD) main: $(srcdir)/lib/mjit/instruction.rb -$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/instruction.rb.erb $(srcdir)/insns.def +$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb index 7aec9c76312e9a..c4294e1b3e2ef7 100644 --- a/tool/ruby_vm/helpers/dumper.rb +++ b/tool/ruby_vm/helpers/dumper.rb @@ -25,10 +25,11 @@ def new_binding end def new_erb spec + srcdir = Pathname.new(__dir__).parent.parent.parent path = Pathname.new(__FILE__) path = (path.relative_path_from(Pathname.pwd) rescue path).dirname path += '../views' - path += File.basename(spec) + path += Pathname.pwd.join(spec).to_s.sub("#{srcdir}/", '') src = path.read mode: 'rt:utf-8:utf-8' rescue Errno::ENOENT raise "don't know how to generate #{path}" diff --git a/tool/ruby_vm/views/instruction.rb.erb b/tool/ruby_vm/views/lib/mjit/instruction.rb.erb similarity index 100% rename from tool/ruby_vm/views/instruction.rb.erb rename to tool/ruby_vm/views/lib/mjit/instruction.rb.erb From 12023c833fb2ba3b50a34ab7f6b53f5f77d78658 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 14:21:23 +0900 Subject: [PATCH 124/269] Revert "Preserve the directory structure under tool/ruby_vm/views" This reverts commit 62ec621f8c7457374d1f08aec97138ac1b7bdf2a. will revisit this once fixing non-MJIT targets --- template/Makefile.in | 2 +- tool/ruby_vm/helpers/dumper.rb | 3 +-- tool/ruby_vm/views/{lib/mjit => }/instruction.rb.erb | 0 3 files changed, 2 insertions(+), 3 deletions(-) rename tool/ruby_vm/views/{lib/mjit => }/instruction.rb.erb (100%) diff --git a/template/Makefile.in b/template/Makefile.in index 5c2f81381e22f4..06878d552f13ad 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -505,7 +505,7 @@ clean-local:: -$(Q) $(RMDIRS) $(MJIT_HEADER_INSTALL_DIR) $(MJIT_HEADER_BUILD_DIR) $(TIMESTAMPDIR) 2> $(NULL) || $(NULLCMD) main: $(srcdir)/lib/mjit/instruction.rb -$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def +$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/instruction.rb.erb $(srcdir)/insns.def $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb index c4294e1b3e2ef7..7aec9c76312e9a 100644 --- a/tool/ruby_vm/helpers/dumper.rb +++ b/tool/ruby_vm/helpers/dumper.rb @@ -25,11 +25,10 @@ def new_binding end def new_erb spec - srcdir = Pathname.new(__dir__).parent.parent.parent path = Pathname.new(__FILE__) path = (path.relative_path_from(Pathname.pwd) rescue path).dirname path += '../views' - path += Pathname.pwd.join(spec).to_s.sub("#{srcdir}/", '') + path += File.basename(spec) src = path.read mode: 'rt:utf-8:utf-8' rescue Errno::ENOENT raise "don't know how to generate #{path}" diff --git a/tool/ruby_vm/views/lib/mjit/instruction.rb.erb b/tool/ruby_vm/views/instruction.rb.erb similarity index 100% rename from tool/ruby_vm/views/lib/mjit/instruction.rb.erb rename to tool/ruby_vm/views/instruction.rb.erb From a988fe0b3e4cd5a3955706affdc1f498ff9b5d77 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 14:22:35 +0900 Subject: [PATCH 125/269] Introduce --basedir to insns2vm.rb and leverage that to preserve the directory structure under tool/ruby_vm/views --- template/Makefile.in | 4 ++-- tool/ruby_vm/controllers/application_controller.rb | 5 +++-- tool/ruby_vm/helpers/dumper.rb | 5 +++-- tool/ruby_vm/scripts/insns2vm.rb | 12 ++++++++++-- tool/ruby_vm/views/{ => lib/mjit}/instruction.rb.erb | 0 5 files changed, 18 insertions(+), 8 deletions(-) rename tool/ruby_vm/views/{ => lib/mjit}/instruction.rb.erb (100%) diff --git a/template/Makefile.in b/template/Makefile.in index 06878d552f13ad..7e12efabb767ab 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -505,9 +505,9 @@ clean-local:: -$(Q) $(RMDIRS) $(MJIT_HEADER_INSTALL_DIR) $(MJIT_HEADER_BUILD_DIR) $(TIMESTAMPDIR) 2> $(NULL) || $(NULLCMD) main: $(srcdir)/lib/mjit/instruction.rb -$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/instruction.rb.erb $(srcdir)/insns.def +$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def $(ECHO) generating $@ - $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ + $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ # DTrace static library hacks described here: # https://marc.info/?l=opensolaris-dtrace-discuss&m=114761203110734&w=4 diff --git a/tool/ruby_vm/controllers/application_controller.rb b/tool/ruby_vm/controllers/application_controller.rb index 25c10947ed5cd4..e03e54e3971a5c 100644 --- a/tool/ruby_vm/controllers/application_controller.rb +++ b/tool/ruby_vm/controllers/application_controller.rb @@ -16,10 +16,11 @@ require_relative '../loaders/vm_opts_h' class ApplicationController - def generate i, destdir + def generate i, destdir, basedir path = Pathname.new i dst = destdir ? Pathname.new(destdir).join(i) : Pathname.new(i) - dumper = RubyVM::Dumper.new dst + base = basedir ? Pathname.new(basedir) : Pathname.pwd + dumper = RubyVM::Dumper.new dst, base.expand_path return [path, dumper] end end diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb index 7aec9c76312e9a..c8a65e06506cb8 100644 --- 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 += File.basename(spec) + path += Pathname.pwd.join(spec).to_s.sub("#{@base}/", '') src = path.read mode: 'rt:utf-8:utf-8' rescue Errno::ENOENT raise "don't know how to generate #{path}" @@ -85,10 +85,11 @@ def replace_pragma str . join end - def initialize dst + def initialize dst, base @erb = {} @empty = new_binding @file = cstr dst.to_path + @base = base end def render partial, opts = { :locals => {} } diff --git a/tool/ruby_vm/scripts/insns2vm.rb b/tool/ruby_vm/scripts/insns2vm.rb index 8325dd364f8fa5..47d8da5513434f 100644 --- a/tool/ruby_vm/scripts/insns2vm.rb +++ b/tool/ruby_vm/scripts/insns2vm.rb @@ -15,10 +15,10 @@ module RubyVM::Insns2VM def self.router argv - options = { destdir: nil } + options = { destdir: nil, basedir: nil } targets = generate_parser(options).parse argv return targets.map do |i| - next ApplicationController.new.generate i, options[:destdir] + next ApplicationController.new.generate i, options[:destdir], options[:basedir] end end @@ -84,6 +84,14 @@ def self.generate_parser(options) options[:destdir] = dir end + this.on "--basedir=DIR", <<-'begin' do |dir| + Change the base directory from the current working directory + to the given path. Used for searching the source template. + begin + raise "directory was not found in '#{dir}'" unless Dir.exist?(dir) + options[:basedir] = dir + end + this.on "-V", "--[no-]verbose", <<-'end' Please let us ignore this and be modest. end diff --git a/tool/ruby_vm/views/instruction.rb.erb b/tool/ruby_vm/views/lib/mjit/instruction.rb.erb similarity index 100% rename from tool/ruby_vm/views/instruction.rb.erb rename to tool/ruby_vm/views/lib/mjit/instruction.rb.erb From 0ca037b35cdf14bc6b3910560c786936585cf6fc Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 20:42:57 +0900 Subject: [PATCH 126/269] Update bindgen crate (#6397) to get rid of deprecated indirect dependency, ansi_term --- yjit/bindgen/Cargo.lock | 83 ++++++++++++++++++++-------------- yjit/bindgen/Cargo.toml | 2 +- yjit/src/cruby_bindings.inc.rs | 2 +- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/yjit/bindgen/Cargo.lock b/yjit/bindgen/Cargo.lock index b6ee5ae6f8abe3..15746ee3edf217 100644 --- a/yjit/bindgen/Cargo.lock +++ b/yjit/bindgen/Cargo.lock @@ -11,15 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "atty" version = "0.2.14" @@ -31,11 +22,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bindgen" -version = "0.59.2" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" dependencies = [ "bitflags", "cexpr", @@ -88,17 +85,26 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ - "ansi_term", "atty", "bitflags", + "clap_lex", + "indexmap", "strsim", + "termcolor", "textwrap", - "unicode-width", - "vec_map", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", ] [[package]] @@ -126,6 +132,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -141,6 +153,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -200,6 +222,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -255,9 +283,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "termcolor" @@ -270,18 +298,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.11.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "unicode-xid" @@ -289,12 +308,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "which" version = "4.2.5" diff --git a/yjit/bindgen/Cargo.toml b/yjit/bindgen/Cargo.toml index 57fd874939142a..3b13b8855e89f8 100644 --- a/yjit/bindgen/Cargo.toml +++ b/yjit/bindgen/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bindgen = "0.59.2" +bindgen = "0.60.1" env_logger = "0.9.0" diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index b391a6cda5a475..f58bf1ca05879b 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1,4 +1,4 @@ -/* automatically generated by rust-bindgen 0.59.2 */ +/* automatically generated by rust-bindgen 0.60.1 */ pub const USE_RVARGC: u32 = 1; pub const INTEGER_REDEFINED_OP_FLAG: u32 = 1; From 45ecc30a6342e1322f07e68280a5107db8075da0 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 20:49:46 +0900 Subject: [PATCH 127/269] Move mjit/instruction.rb rule to common.mk as suggested by nobu. We don't really need to generate this for Windows, but using common.mk whenever possible would probably make maintenance easier. --- common.mk | 5 +++++ template/Makefile.in | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common.mk b/common.mk index 2956e2c93e6ffc..14bb2ead5cc6ec 100644 --- a/common.mk +++ b/common.mk @@ -225,6 +225,11 @@ all: $(SHOWFLAGS) main docs main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs @$(NULLCMD) +main: $(srcdir)/lib/mjit/instruction.rb +$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def + $(ECHO) generating $@ + $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ + mjit-headers: $(MJIT_SUPPORT)-mjit-headers no-mjit-headers: PHONY yes-mjit-headers: mjit_config.h PHONY diff --git a/template/Makefile.in b/template/Makefile.in index 7e12efabb767ab..1a7843edb6987c 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -504,11 +504,6 @@ clean-local:: $(Q)$(RM) -r mjit_build_dir.* -$(Q) $(RMDIRS) $(MJIT_HEADER_INSTALL_DIR) $(MJIT_HEADER_BUILD_DIR) $(TIMESTAMPDIR) 2> $(NULL) || $(NULLCMD) -main: $(srcdir)/lib/mjit/instruction.rb -$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def - $(ECHO) generating $@ - $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ - # DTrace static library hacks described here: # https://marc.info/?l=opensolaris-dtrace-discuss&m=114761203110734&w=4 ruby-glommed.$(OBJEXT): From e2e1058e668f875bfc882e94d470b01044303da1 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 21:19:37 +0900 Subject: [PATCH 128/269] Include lib/mjit/instruction.rb in a snapshot baseruby shouldn't be necessary once a snapshot is built. --- common.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/common.mk b/common.mk index 14bb2ead5cc6ec..d7b20b73b68857 100644 --- a/common.mk +++ b/common.mk @@ -226,6 +226,7 @@ main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs @$(NULLCMD) main: $(srcdir)/lib/mjit/instruction.rb +srcs: $(srcdir)/lib/mjit/instruction.rb $(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ From a0b0991eed5b202b61323496560d8a803fddab02 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 17 Sep 2022 22:48:40 +0900 Subject: [PATCH 129/269] Downloader: Define per-class command line options Move `Downloader::Unicode` specific options, and parse options after the downloader specificier. --- tool/downloader.rb | 60 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/tool/downloader.rb b/tool/downloader.rb index d3a9f7563754bb..f09d44851e7a61 100644 --- a/tool/downloader.rb +++ b/tool/downloader.rb @@ -36,6 +36,12 @@ def File.directory? files end class Downloader + def self.find(dlname) + constants.find do |name| + return const_get(name) if dlname.casecmp(name.to_s) == 0 + end + end + def self.https=(https) @@https = https end @@ -48,6 +54,10 @@ def self.https @@https end + def self.get_option(argv, options) + false + end + class GNU < self def self.download(name, *rest) if https? @@ -78,6 +88,21 @@ class Unicode < self INDEX = {} # cache index file information across files in the same directory UNICODE_PUBLIC = "https://www.unicode.org/Public/" + def self.get_option(argv, options) + case argv[0] + when '--unicode-beta' + options[:unicode_beta] = argv[1] + argv.shift(2) + true + when /\A--unicode-beta=(.*)/m + options[:unicode_beta] = $1 + argv.shift + true + else + super + end + end + def self.download(name, dir = nil, since = true, options = {}) options = options.dup unicode_beta = options.delete(:unicode_beta) @@ -173,7 +198,6 @@ def self.download(url, name, dir = nil, since = true, options = {}) options = options.dup url = URI(url) dryrun = options.delete(:dryrun) - options.delete(:unicode_beta) # just to be on the safe side for gems and gcc if name file = Pathname.new(under(dir, name)) @@ -351,7 +375,25 @@ def self.with_retry(max_times, &block) if $0 == __FILE__ since = true options = {} + dl = nil + (args = []).singleton_class.__send__(:define_method, :downloader?) do |arg| + !dl and args.empty? and (dl = Downloader.find(arg)) + end until ARGV.empty? + if ARGV[0] == '--' + ARGV.shift + break if ARGV.empty? + ARGV.shift if args.downloader? ARGV[0] + args.concat(ARGV) + break + end + + if dl and dl.get_option(ARGV, options) + # the downloader dealt with the arguments, and should be removed + # from ARGV. + next + end + case ARGV[0] when '-d' destdir = ARGV[1] @@ -370,26 +412,18 @@ def self.with_retry(max_times, &block) when '--cache-dir' options[:cache_dir] = ARGV[1] ARGV.shift - when '--unicode-beta' - options[:unicode_beta] = ARGV[1] - ARGV.shift when /\A--cache-dir=(.*)/m options[:cache_dir] = $1 when /\A-/ abort "#{$0}: unknown option #{ARGV[0]}" else - break + args << ARGV[0] unless args.downloader? ARGV[0] end ARGV.shift end - dl = Downloader.constants.find do |name| - ARGV[0].casecmp(name.to_s) == 0 - end unless ARGV.empty? $VERBOSE = true if dl - dl = Downloader.const_get(dl) - ARGV.shift - ARGV.each do |name| + args.each do |name| dir = destdir if prefix name = name.sub(/\A\.\//, '') @@ -409,7 +443,7 @@ def self.with_retry(max_times, &block) dl.download(name, dir, since, options) end else - abort "usage: #{$0} url name" unless ARGV.size == 2 - Downloader.download(ARGV[0], ARGV[1], destdir, since, options) + abort "usage: #{$0} url name" unless args.size == 2 + Downloader.download(args[0], args[1], destdir, since, options) end end From cc533cb607d9d96356c12b880d0871a3b24f35e9 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 17 Sep 2022 22:51:30 +0900 Subject: [PATCH 130/269] Downloader: Define long option aliases --- tool/downloader.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tool/downloader.rb b/tool/downloader.rb index f09d44851e7a61..3020322a980a86 100644 --- a/tool/downloader.rb +++ b/tool/downloader.rb @@ -395,18 +395,20 @@ def self.with_retry(max_times, &block) end case ARGV[0] - when '-d' + when '-d', '--destdir' destdir = ARGV[1] ARGV.shift - when '-p' + when '-p', '--prefix' # strip directory names from the name to download, and add the # prefix instead. prefix = ARGV[1] ARGV.shift - when '-e' + when '-e', '--exist', '--non-existent-only' since = nil - when '-a' + when '-a', '--always' since = false + when '-u', '--update', '--if-modified' + since = true when '-n', '--dryrun' options[:dryrun] = true when '--cache-dir' From ea22403653878e1c8c7e307b0d15c7d5881bad66 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 17 Sep 2022 22:52:07 +0900 Subject: [PATCH 131/269] Extract UNICODE_DOWNLOADER --- common.mk | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/common.mk b/common.mk index d7b20b73b68857..3e8626532a520e 100644 --- a/common.mk +++ b/common.mk @@ -1552,34 +1552,28 @@ update-unicode: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES) \ $(UNICODE_AUXILIARY_FILES) $(UNICODE_UCD_EMOJI_FILES) $(UNICODE_EMOJI_FILES) CACHE_DIR = $(srcdir)/.downloaded-cache -UNICODE_DOWNLOAD = \ +UNICODE_DOWNLOADER_ALWAYS_UPDATE = $(ALWAYS_UPDATE_UNICODE:yes=--always) +UNICODE_DOWNLOADER = \ $(BASERUBY) $(tooldir)/downloader.rb \ --cache-dir=$(CACHE_DIR) \ - --unicode-beta $(UNICODE_BETA) \ + --exist $(UNICODE_DOWNLOADER_ALWAYS_UPDATE:no=) \ + unicode --unicode-beta=$(UNICODE_BETA) +UNICODE_DOWNLOAD = \ + $(UNICODE_DOWNLOADER) \ -d $(UNICODE_SRC_DATA_DIR) \ - -p $(UNICODE_VERSION)/ucd \ - -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode + -p $(UNICODE_VERSION)/ucd UNICODE_AUXILIARY_DOWNLOAD = \ - $(BASERUBY) $(tooldir)/downloader.rb \ - --cache-dir=$(CACHE_DIR) \ - --unicode-beta $(UNICODE_BETA) \ + $(UNICODE_DOWNLOADER) \ -d $(UNICODE_SRC_DATA_DIR)/auxiliary \ - -p $(UNICODE_VERSION)/ucd/auxiliary \ - -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode + -p $(UNICODE_VERSION)/ucd/auxiliary UNICODE_UCD_EMOJI_DOWNLOAD = \ - $(BASERUBY) $(tooldir)/downloader.rb \ - --cache-dir=$(CACHE_DIR) \ - --unicode-beta $(UNICODE_BETA) \ + $(UNICODE_DOWNLOADER) \ -d $(UNICODE_SRC_DATA_DIR)/emoji \ - -p $(UNICODE_VERSION)/ucd/emoji \ - -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode + -p $(UNICODE_VERSION)/ucd/emoji UNICODE_EMOJI_DOWNLOAD = \ - $(BASERUBY) $(tooldir)/downloader.rb \ - --cache-dir=$(CACHE_DIR) \ - --unicode-beta $(UNICODE_BETA) \ + $(UNICODE_DOWNLOADER) \ -d $(UNICODE_SRC_EMOJI_DATA_DIR) \ - -p emoji/$(UNICODE_EMOJI_VERSION) \ - -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode + -p emoji/$(UNICODE_EMOJI_VERSION) $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES): update-unicode-files update-unicode-files: From 12e5e5b5739657284789a966e1dcb205545ad0ce Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 19 Sep 2022 14:11:37 +0900 Subject: [PATCH 132/269] Fix the option name in the error message [ci skip] --- bootstraptest/runner.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb index 3d423902545a8d..9699b10bc0f882 100755 --- a/bootstraptest/runner.rb +++ b/bootstraptest/runner.rb @@ -154,8 +154,7 @@ def main end } if tests and not ARGV.empty? - $stderr.puts "--tests and arguments are exclusive" - exit false + abort "--sets and arguments are exclusive" end tests ||= ARGV tests = Dir.glob("#{File.dirname($0)}/test_*.rb").sort if tests.empty? From e75d96368577276cbebadb56a0fb286f66afdf3f Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Mon, 19 Sep 2022 02:09:38 -0400 Subject: [PATCH 133/269] Only exit if ruby2_keywords and splat together (#6395) Before this change railsbench spent less time in yjit than before splat. This brings it back to parity. --- yjit/src/codegen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 71831a96ed1086..ef7c3386a2ae4f 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4370,7 +4370,7 @@ fn gen_send_iseq( // We are just going to not compile these. // https://www.rubydoc.info/stdlib/core/Proc:ruby2_keywords if unsafe { - get_iseq_flags_ruby2_keywords(jit.iseq) + get_iseq_flags_ruby2_keywords(jit.iseq) && flags & VM_CALL_ARGS_SPLAT != 0 } { gen_counter_incr!(asm, send_iseq_ruby2_keywords); return CantCompile; From 7cab7e5fde150bdef955ba4182d26f84e1c5d122 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 19 Sep 2022 15:13:53 +0900 Subject: [PATCH 134/269] Stop recommending duplicated options [ci skip] (#6400) --- doc/yjit/yjit.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md index 1eeb75824a950e..c2521eba426bf9 100644 --- a/doc/yjit/yjit.md +++ b/doc/yjit/yjit.md @@ -79,7 +79,7 @@ The YJIT `ruby` binary can be built with either GCC or Clang. It can be built ei ``` # Configure in release mode for maximum performance, build and install ./autogen.sh -./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc +./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc make -j install ``` @@ -88,7 +88,7 @@ or ``` # Configure in dev (debug) mode for development, build and install ./autogen.sh -./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc +./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc make -j install ``` @@ -100,7 +100,7 @@ brew install openssl readline libyaml # Configure in dev (debug) mode for development, build and install ./autogen.sh -./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)" +./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)" make -j install ``` From 1c9381283e68b9021df2509fb64d663998d8cb5a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 19 Sep 2022 00:26:49 +0900 Subject: [PATCH 135/269] Add another test for `Process.daemon` Check for that the daemon process is detached, that means it is not a child and not waitable. --- test/ruby/test_process.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index c4888598a8930a..6340f622fce36a 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1876,6 +1876,26 @@ def test_daemon_pid assert_not_equal(cpid, dpid) end + def test_daemon_detached + IO.popen("-", "r+") do |f| + if f + assert_equal(f.pid, Process.wait(f.pid)) + + dpid, ppid = Integer(f.gets), Integer(f.gets) + + message = "daemon #{dpid} should be detached" + assert_not_equal($$, ppid, message) # would be 1 almost always + assert_raise(Errno::ECHILD, message) {Process.wait(dpid)} + assert_kind_of(Integer, Process.kill(0, dpid), message) + + break # close f, and let the daemon resume and exit + end + Process.daemon(false, true) + puts $$, Process.ppid + $stdin.gets # wait for the above assertions using signals + end + end + if File.directory?("/proc/self/task") && /netbsd[a-z]*[1-6]/ !~ RUBY_PLATFORM def test_daemon_no_threads pid, data = IO.popen("-", "r+") do |f| From ae07336529ee0955bb08b12eb69a90aa9ab4b9f9 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 19 Sep 2022 00:46:52 +0900 Subject: [PATCH 136/269] Reduce fork calls in daemon The forked child process is a grandchild process from the viewpoint of the process which invoked the caller process. That means the child is detached at that point, and it does not need to fork twice. --- process.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/process.c b/process.c index 4465b36c514e89..a3d3bcbb78aae6 100644 --- a/process.c +++ b/process.c @@ -7109,20 +7109,14 @@ rb_daemon(int nochdir, int noclose) #else int n; -#define fork_daemon() \ - switch (rb_fork_ruby(NULL)) { \ - case -1: return -1; \ - case 0: break; \ - default: _exit(EXIT_SUCCESS); \ + switch (rb_fork_ruby(NULL)) { + case -1: return -1; + case 0: break; + default: _exit(EXIT_SUCCESS); } - fork_daemon(); - if (setsid() < 0) return -1; - /* must not be process-leader */ - fork_daemon(); - if (!nochdir) err = chdir("/"); From 5883bc7c0791de2ce5e8b22175aef07705f0c618 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 19 Sep 2022 16:34:03 +0900 Subject: [PATCH 137/269] YJIT: Check if the processor supports --yjit-stats (#6401) * YJIT: Add asm comment for incr_counter * YJIT: Check if the processor supports --yjit-stats --- yjit/src/codegen.rs | 1 + yjit/src/options.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index ef7c3386a2ae4f..a61795f5d42c97 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -209,6 +209,7 @@ macro_rules! gen_counter_incr { let ptr = ptr_to_counter!($counter_name); // Load the pointer into a register + $asm.comment(&format!("increment counter {}", stringify!($counter_name))); let ptr_reg = $asm.load(Opnd::const_ptr(ptr as *const u8)); let counter_opnd = Opnd::mem(64, ptr_reg, 0); diff --git a/yjit/src/options.rs b/yjit/src/options.rs index f73dca67de21bb..cad7bf332a0315 100644 --- a/yjit/src/options.rs +++ b/yjit/src/options.rs @@ -151,7 +151,16 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { ("greedy-versioning", "") => unsafe { OPTIONS.greedy_versioning = true }, ("no-type-prop", "") => unsafe { OPTIONS.no_type_prop = true }, - ("stats", "") => unsafe { OPTIONS.gen_stats = true }, + ("stats", "") => { + // Insn::IncrCounter uses ldaddal, which works only on ARMv8.1+. + #[cfg(target_arch = "aarch64")] + if !std::arch::is_aarch64_feature_detected!("lse") { + eprintln!("Your processor does not support --yjit-stats. Aborting."); + std::process::exit(1); + } + + unsafe { OPTIONS.gen_stats = true } + }, ("trace-exits", "") => unsafe { OPTIONS.gen_trace_exits = true; OPTIONS.gen_stats = true }, ("dump-insns", "") => unsafe { OPTIONS.dump_insns = true }, ("verify-ctx", "") => unsafe { OPTIONS.verify_ctx = true }, From a44f48cadc8a319e233bc5a8c6ab9d52aaf52bae Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 19 Sep 2022 10:15:04 -0400 Subject: [PATCH 138/269] [ruby/bigdecimal] Remove array defs in missing.h for old Rubies Commit 02b6053 added these to support Ruby 2.0.0. The rb_array_const_ptr function is defined since Ruby 2.3. https://github.com/ruby/bigdecimal/commit/678699ca1b --- ext/bigdecimal/extconf.rb | 1 - ext/bigdecimal/missing.h | 29 ----------------------------- 2 files changed, 30 deletions(-) diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb index 4920374b1a9bc8..e74196e18b921f 100644 --- a/ext/bigdecimal/extconf.rb +++ b/ext/bigdecimal/extconf.rb @@ -71,7 +71,6 @@ def have_builtin_func(name, check_expr, opt = "", &b) have_func("rb_rational_den", "ruby.h") have_func("rb_complex_real", "ruby.h") have_func("rb_complex_imag", "ruby.h") -have_func("rb_array_const_ptr", "ruby.h") have_func("rb_sym2str", "ruby.h") have_func("rb_opts_exception_p", "ruby.h") have_func("rb_category_warn", "ruby.h") diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h index 49b7c7667f9f98..02d2348e4b4ad1 100644 --- a/ext/bigdecimal/missing.h +++ b/ext/bigdecimal/missing.h @@ -172,35 +172,6 @@ rb_complex_imag(VALUE cmp) } #endif -/* array */ - -#ifndef FIX_CONST_VALUE_PTR -# if defined(__fcc__) || defined(__fcc_version) || \ - defined(__FCC__) || defined(__FCC_VERSION) -/* workaround for old version of Fujitsu C Compiler (fcc) */ -# define FIX_CONST_VALUE_PTR(x) ((const VALUE *)(x)) -# else -# define FIX_CONST_VALUE_PTR(x) (x) -# endif -#endif - -#ifndef HAVE_RB_ARRAY_CONST_PTR -static inline const VALUE * -rb_array_const_ptr(VALUE a) -{ - return FIX_CONST_VALUE_PTR((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ? - RARRAY(a)->as.ary : RARRAY(a)->as.heap.ptr); -} -#endif - -#ifndef RARRAY_CONST_PTR -# define RARRAY_CONST_PTR(a) rb_array_const_ptr(a) -#endif - -#ifndef RARRAY_AREF -# define RARRAY_AREF(a, i) (RARRAY_CONST_PTR(a)[i]) -#endif - /* symbol */ #ifndef HAVE_RB_SYM2STR From 12889fad4100569c212c9813243db5ec5380b5fb Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 19 Sep 2022 10:23:29 -0400 Subject: [PATCH 139/269] [ruby/bigdecimal] Remove symbol defs in missing.h for old Rubies Commit 2885514 added these to support Ruby 2.1. The rb_sym2str function is defined since Ruby 2.2. https://github.com/ruby/bigdecimal/commit/be366c9cf2 --- ext/bigdecimal/extconf.rb | 1 - ext/bigdecimal/missing.h | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb index e74196e18b921f..17e7905dd67e3b 100644 --- a/ext/bigdecimal/extconf.rb +++ b/ext/bigdecimal/extconf.rb @@ -71,7 +71,6 @@ def have_builtin_func(name, check_expr, opt = "", &b) have_func("rb_rational_den", "ruby.h") have_func("rb_complex_real", "ruby.h") have_func("rb_complex_imag", "ruby.h") -have_func("rb_sym2str", "ruby.h") have_func("rb_opts_exception_p", "ruby.h") have_func("rb_category_warn", "ruby.h") have_const("RB_WARN_CATEGORY_DEPRECATED", "ruby.h") diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h index 02d2348e4b4ad1..307147c0fdc6bf 100644 --- a/ext/bigdecimal/missing.h +++ b/ext/bigdecimal/missing.h @@ -172,16 +172,6 @@ rb_complex_imag(VALUE cmp) } #endif -/* symbol */ - -#ifndef HAVE_RB_SYM2STR -static inline VALUE -rb_sym2str(VALUE sym) -{ - return rb_id2str(SYM2ID(sym)); -} -#endif - /* st */ #ifndef ST2FIX From 26135312f61014967ef223dd16ad6577ebd9c5d8 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 15 Sep 2022 17:48:52 -0700 Subject: [PATCH 140/269] [ruby/psych] Convert some of Parser#parse to Ruby This commit just converts some of the parse method to Ruby https://github.com/ruby/psych/commit/bca7d2c549 --- ext/psych/lib/psych/parser.rb | 13 +++++++++++++ ext/psych/psych_parser.c | 22 ++-------------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/ext/psych/lib/psych/parser.rb b/ext/psych/lib/psych/parser.rb index 39bc8289be7fb1..2181c730e59ce9 100644 --- a/ext/psych/lib/psych/parser.rb +++ b/ext/psych/lib/psych/parser.rb @@ -48,5 +48,18 @@ def initialize handler = Handler.new @handler = handler @external_encoding = ANY end + + ### + # call-seq: + # parser.parse(yaml) + # + # Parse the YAML document contained in +yaml+. Events will be called on + # the handler set on the parser instance. + # + # See Psych::Parser and Psych::Parser#handler + + def parse yaml, path = yaml.respond_to?(:path) ? yaml.path : "" + _native_parse @handler, yaml, path + end end end diff --git a/ext/psych/psych_parser.c b/ext/psych/psych_parser.c index f91475b835debf..9c5179cc44398d 100644 --- a/ext/psych/psych_parser.c +++ b/ext/psych/psych_parser.c @@ -245,18 +245,8 @@ static VALUE protected_event_location(VALUE pointer) return rb_funcall3(args[0], id_event_location, 4, args + 1); } -/* - * call-seq: - * parser.parse(yaml) - * - * Parse the YAML document contained in +yaml+. Events will be called on - * the handler set on the parser instance. - * - * See Psych::Parser and Psych::Parser#handler - */ -static VALUE parse(int argc, VALUE *argv, VALUE self) +static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) { - VALUE yaml, path; yaml_parser_t * parser; yaml_event_t event; int done = 0; @@ -264,14 +254,6 @@ static VALUE parse(int argc, VALUE *argv, VALUE self) int parser_encoding = YAML_ANY_ENCODING; int encoding = rb_utf8_encindex(); rb_encoding * internal_enc = rb_default_internal_encoding(); - VALUE handler = rb_iv_get(self, "@handler"); - - if (rb_scan_args(argc, argv, "11", &yaml, &path) == 1) { - if(rb_respond_to(yaml, id_path)) - path = rb_funcall(yaml, id_path, 0); - else - path = rb_str_new2(""); - } TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser); @@ -562,7 +544,7 @@ void Init_psych_parser(void) rb_require("psych/syntax_error"); - rb_define_method(cPsychParser, "parse", parse, -1); + rb_define_private_method(cPsychParser, "_native_parse", parse, 3); rb_define_method(cPsychParser, "mark", mark, 0); id_read = rb_intern("read"); From f8dad616c2ee2d83b3162da8d86865b0f2a782de Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 20 Sep 2022 06:07:28 +0900 Subject: [PATCH 141/269] YJIT: Show --yjit-stats of railsbench on CI (#6403) * YJIT: Show --yjit-stats of railsbench on CI * YJIT: Use --enable-yjit=dev to see ratio_in_yjit * YJIT: Show master GitHub URL for quick comparison * YJIT: Avoid making CI red by a yjit-bench failure --- .github/workflows/yjit-ubuntu.yml | 18 ++++++++++++++++-- .gitignore | 3 +++ yjit/yjit.mk | 13 +++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index bf90b80efb1ee0..3c0d5c046c802b 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -59,10 +59,16 @@ jobs: - test_task: "test-bundled-gems" configure: "--enable-yjit=dev" + + - test_task: "yjit-bench" + configure: "--enable-yjit=dev" + yjit_bench_opts: "--yjit-stats" env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} RUN_OPTS: ${{ matrix.yjit_opts }} + YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }} RUBY_DEBUG: ci + BUNDLE_JOBS: 8 # for yjit-bench runs-on: ubuntu-20.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: @@ -98,7 +104,7 @@ jobs: - run: ./autogen.sh working-directory: src - name: Run configure - run: ../src/configure -C --disable-install-doc ${{ matrix.configure }} + run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }} - run: make incs - run: make prepare-gems if: ${{ matrix.test_task == 'test-bundled-gems' }} @@ -111,12 +117,20 @@ jobs: if: ${{ matrix.test_task == 'check' }} - name: Enable YJIT through ENV run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV - - run: make -s ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" + - name: make ${{ matrix.test_task }} + run: make -s -j ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS" timeout-minutes: 60 env: RUBY_TESTOPTS: "-q --tty=no" TEST_BUNDLED_GEMS_ALLOW_FAILURES: "" PRECHECK_BUNDLED_GEMS: "no" + continue-on-error: ${{ matrix.test_task == 'yjit-bench' }} + - name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison + run: echo "https://github.com/${BASE_REPO}/commit/${BASE_SHA}" + env: + BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + if: ${{ matrix.test_task == 'yjit-bench' && startsWith(github.event_name, 'pull') }} - uses: k0kubun/action-slack@v2.0.0 with: payload: | diff --git a/.gitignore b/.gitignore index 73012bb3ed1b40..c15b68f676ccc7 100644 --- a/.gitignore +++ b/.gitignore @@ -238,5 +238,8 @@ lcov*.info /include/ruby-*/*/rb_mjit_min_header-*.h /lib/mjit/instruction.rb +# YJIT +/yjit-bench + # /wasm/ /wasm/tests/*.wasm diff --git a/yjit/yjit.mk b/yjit/yjit.mk index 81441a7925a607..fe182b3dcca3c6 100644 --- a/yjit/yjit.mk +++ b/yjit/yjit.mk @@ -51,6 +51,19 @@ endif # TODO: might need to move for BSD Make support miniruby$(EXEEXT): $(YJIT_LIBS) +# By using YJIT_BENCH_OPTS instead of RUN_OPTS, you can skip passing the options to `make install` +YJIT_BENCH_OPTS = $(RUN_OPTS) --enable-gems +YJIT_BENCH = benchmarks/railsbench/benchmark.rb + +# Run yjit-bench's ./run_once.sh for CI +yjit-bench: install update-yjit-bench PHONY + $(Q) cd $(srcdir)/yjit-bench && PATH=$(prefix)/bin:$$PATH \ + ./run_once.sh $(YJIT_BENCH_OPTS) $(YJIT_BENCH) + +update-yjit-bench: + $(Q) $(tooldir)/git-refresh -C $(srcdir) --branch main \ + https://github.com/Shopify/yjit-bench yjit-bench $(GIT_OPTS) + # Generate Rust bindings. See source for details. # Needs `./configure --enable-yjit=dev` and Clang. ifneq ($(strip $(CARGO)),) # if configure found Cargo From 76a0e81f409baf810411677e115bce4f4b05ba5e Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 20 Sep 2022 07:38:18 +0900 Subject: [PATCH 142/269] Support trailing commas in builtin `foo(Primitive.cexpr!('Qnil'),)` causes SEGV without this change. --- tool/mk_builtin_loader.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 784dd30dc14efa..c5bbd79a80f329 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -87,7 +87,7 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil tree = tree[2] next when :method_add_arg - _, mid, (_, (_, args)) = tree + _method_add_arg, mid, (_arg_paren, args) = tree case mid.first when :call _, recv, sep, mid = mid @@ -96,6 +96,11 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil else mid = nil end + # w/ trailing comma: [[:method_add_arg, ...]] + # w/o trailing comma: [:args_add_block, [[:method_add_arg, ...]]] + if args && args.first == :args_add_block + args = args[1] + end when :vcall _, mid = tree when :command # FCALL From b4546d26f297d25c0e03f5f09864e7b371d65fa8 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 20 Sep 2022 09:43:49 +0900 Subject: [PATCH 143/269] Fix the trailing comma comment for builtin [ci skip] so that it's clear why not args.last but args[1] --- tool/mk_builtin_loader.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index c5bbd79a80f329..6b4ae121d92b22 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -97,7 +97,7 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil mid = nil end # w/ trailing comma: [[:method_add_arg, ...]] - # w/o trailing comma: [:args_add_block, [[:method_add_arg, ...]]] + # w/o trailing comma: [:args_add_block, [[:method_add_arg, ...]], false] if args && args.first == :args_add_block args = args[1] end From 55e540f7ab6adb3ea0de16912dfde8e0bf94cc6e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 20 Sep 2022 11:12:11 +0900 Subject: [PATCH 144/269] Ignore EPERM which means already being process-leader --- process.c | 3 ++- test/ruby/test_process.rb | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/process.c b/process.c index a3d3bcbb78aae6..735dd2d855e7cc 100644 --- a/process.c +++ b/process.c @@ -7115,7 +7115,8 @@ rb_daemon(int nochdir, int noclose) default: _exit(EXIT_SUCCESS); } - if (setsid() < 0) return -1; + /* ignore EPERM which means already being process-leader */ + if (setsid() < 0) (void)0; if (!nochdir) err = chdir("/"); diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 6340f622fce36a..10c4aadadf2605 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1881,17 +1881,19 @@ def test_daemon_detached if f assert_equal(f.pid, Process.wait(f.pid)) - dpid, ppid = Integer(f.gets), Integer(f.gets) + dpid, ppid, dsid = 3.times.map {Integer(f.gets)} message = "daemon #{dpid} should be detached" assert_not_equal($$, ppid, message) # would be 1 almost always assert_raise(Errno::ECHILD, message) {Process.wait(dpid)} assert_kind_of(Integer, Process.kill(0, dpid), message) + assert_equal(dpid, dsid) break # close f, and let the daemon resume and exit end + Process.setsid rescue nil Process.daemon(false, true) - puts $$, Process.ppid + puts $$, Process.ppid, Process.getsid $stdin.gets # wait for the above assertions using signals end end From 9de11fe796e8caca2d87650278028eb95c1e09a0 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 20 Sep 2022 12:54:08 +0900 Subject: [PATCH 145/269] Quiet if the target is already linked the same source --- ext/extmk.rb | 2 +- tool/ln_sr.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/extmk.rb b/ext/extmk.rb index 40fc10ea1c4258..939eb735659bee 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -607,7 +607,7 @@ def create_makefile(*args, &block) gemlib = $(TARGET_TOPDIR)/gems/$(gem)/lib gemlib:#{%{ $(gemlib)\n$(gemlib): $(gem_srcdir)/lib} if $nmake} - $(Q) #{@inplace ? '$(NULLCMD) ' : ''}$(RUBY) $(top_srcdir)/tool/ln_sr.rb -f -T $(gem_srcdir)/lib $(gemlib) + $(Q) #{@inplace ? '$(NULLCMD) ' : ''}$(RUBY) $(top_srcdir)/tool/ln_sr.rb -q -f -T $(gem_srcdir)/lib $(gemlib) clean-gemlib: $(Q) $(#{@inplace ? 'NULLCMD' : 'RM_RF'}) $(gemlib) diff --git a/tool/ln_sr.rb b/tool/ln_sr.rb index 81fd803355874d..2aa8391e17592f 100755 --- a/tool/ln_sr.rb +++ b/tool/ln_sr.rb @@ -3,6 +3,7 @@ target_directory = true noop = false force = false +quiet = false until ARGV.empty? case ARGV[0] @@ -12,6 +13,8 @@ force = true when '-T' target_directory = false + when '-q' + quiet = true else break end @@ -114,6 +117,9 @@ def fu_starting_path?(path) end if File.respond_to?(:symlink) + if quiet and File.identical?(src, dest) + exit + end begin ln_sr(src, dest, verbose: true, target_directory: target_directory, force: force, noop: noop) rescue NotImplementedError, Errno::EPERM, Errno::EACCES From e40fa6c480b58a8d1699b24193cf67945eaec202 Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Tue, 20 Sep 2022 16:09:42 +0900 Subject: [PATCH 146/269] [DOC] socket: fix wrong sample addresses (#6372) IPv6 link local address is fe80::/10 not ff80::/10: https://www.rfc-editor.org/rfc/rfc4291.html Link-Local unicast 1111111010 FE80::/10 2.5.6 IPv6 (deprecated) site local address is fec0::/10 not ffc0::/10: https://www.rfc-editor.org/rfc/rfc3513.html Site-local unicast 1111111011 FEC0::/10 2.5.6 --- ext/socket/raddrinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index b914aa6520141c..a4e1ed37a352d6 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -2192,7 +2192,7 @@ addrinfo_ipv6_multicast_p(VALUE self) } /* - * Returns true for IPv6 link local address (ff80::/10). + * Returns true for IPv6 link local address (fe80::/10). * It returns false otherwise. */ static VALUE @@ -2204,7 +2204,7 @@ addrinfo_ipv6_linklocal_p(VALUE self) } /* - * Returns true for IPv6 site local address (ffc0::/10). + * Returns true for IPv6 site local address (fec0::/10). * It returns false otherwise. */ static VALUE From b3d8dddee7a9ea0bc9c278a5c9faa4df81afd57e Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Tue, 20 Sep 2022 17:07:42 +0900 Subject: [PATCH 147/269] Try to ignore a noisy ASAN warning for continuation --- common.mk | 1 + cont.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/common.mk b/common.mk index 3e8626532a520e..c6d6e54c729fa5 100644 --- a/common.mk +++ b/common.mk @@ -3367,6 +3367,7 @@ cont.$(OBJEXT): $(top_srcdir)/internal/cont.h cont.$(OBJEXT): $(top_srcdir)/internal/gc.h cont.$(OBJEXT): $(top_srcdir)/internal/imemo.h 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/vm.h diff --git a/cont.c b/cont.c index 8a9aded7133318..19e719ccd95e5a 100644 --- a/cont.c +++ b/cont.c @@ -30,6 +30,7 @@ extern int madvise(caddr_t, size_t, int); #include "internal.h" #include "internal/cont.h" #include "internal/proc.h" +#include "internal/sanitizers.h" #include "internal/warnings.h" #include "ruby/fiber/scheduler.h" #include "mjit.h" @@ -1161,6 +1162,7 @@ cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont) } FLUSH_REGISTER_WINDOWS; + asan_unpoison_memory_region(cont->machine.stack_src, size, false); MEMCPY(cont->machine.stack, cont->machine.stack_src, VALUE, size); } From a8dc49b4d57e0a2d07691176f43e4b7f71874fa7 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 20 Sep 2022 08:36:06 -0400 Subject: [PATCH 148/269] YJIT: Support MAKE=bmake for release build This add support for bmake, which should allow building with `configure --enable-yjit` for the BSDs. Tested on FreeBSD 13 and on macOS with `configure MAKE=bmake` on a case-sensitive file system. It works by including a fragment into the Makefile through the configure script, similar to common.mk. It uses the always rebuild approach to keep build system changes minimal. --- common.mk | 8 ++++++++ configure.ac | 6 +++++- yjit/not_gmake.mk | 14 ++++++++++++++ yjit/yjit.mk | 10 +--------- 4 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 yjit/not_gmake.mk diff --git a/common.mk b/common.mk index c6d6e54c729fa5..bef00d191fa31e 100644 --- a/common.mk +++ b/common.mk @@ -219,6 +219,14 @@ MAKE_LINK = $(MINIRUBY) -rfileutils -e "include FileUtils::Verbose" \ -e "noraise {ln(src, dest)} or" \ -e "cp(src, dest)" +# For release builds +YJIT_RUSTC_ARGS = --crate-name=yjit \ + --crate-type=staticlib \ + --edition=2021 \ + -C opt-level=3 \ + -C overflow-checks=on \ + '--out-dir=$(CARGO_TARGET_DIR)/release/' \ + $(top_srcdir)/yjit/src/lib.rs all: $(SHOWFLAGS) main docs diff --git a/configure.ac b/configure.ac index 8cf0f58067a4cf..98231989603eef 100644 --- a/configure.ac +++ b/configure.ac @@ -4370,6 +4370,10 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [ AS_IF([test "$gnumake" != yes], [ echo ['$(MKFILES): $(srcdir)/common.mk'] sed ['s/{\$([^(){}]*)[^{}]*}//g'] ${srcdir}/common.mk + AS_IF([test "$YJIT_SUPPORT" = yes], [ + cat ${srcdir}/yjit/not_gmake.mk + echo ['$(MKFILES): ${srcdir}/yjit/not_gmake.mk'] + ]) ], [ echo 'distclean-local::; @$(RM) GNUmakefile uncommon.mk' ]) @@ -4387,7 +4391,7 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [ echo 'ruby: $(PROGRAM);' >> $tmpmk test "$tmpmk" = "$tmpgmk" || rm -f "$tmpgmk" ]) && mv -f $tmpmk Makefile], -[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT']) +[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT' YJIT_SUPPORT='$YJIT_SUPPORT']) AC_ARG_WITH([ruby-pc], AS_HELP_STRING([--with-ruby-pc=FILENAME], [pc file basename]), diff --git a/yjit/not_gmake.mk b/yjit/not_gmake.mk new file mode 100644 index 00000000000000..cbc60c09f19dfe --- /dev/null +++ b/yjit/not_gmake.mk @@ -0,0 +1,14 @@ +# This file is included into the Makefile when +# we're *not* using GNU make. Stick to basic features. + +# Rebuild every time since we don't want to list Rust source +# file dependencies. +.PHONY: yjit-static-lib +$(YJIT_LIBS): yjit-static-lib + $(empty) + +yjit-static-lib: + $(ECHO) 'building Rust YJIT (release mode)' + $(Q) $(RUSTC) $(YJIT_RUSTC_ARGS) + +miniruby$(EXEEXT): $(YJIT_LIBS) diff --git a/yjit/yjit.mk b/yjit/yjit.mk index fe182b3dcca3c6..fc0061eb297a91 100644 --- a/yjit/yjit.mk +++ b/yjit/yjit.mk @@ -23,14 +23,7 @@ YJIT_LIB_TOUCH = touch $@ ifeq ($(YJIT_SUPPORT),yes) $(YJIT_LIBS): $(YJIT_SRC_FILES) $(ECHO) 'building Rust YJIT (release mode)' - $(Q) $(RUSTC) \ - --crate-name=yjit \ - --crate-type=staticlib \ - --edition=2021 \ - -C opt-level=3 \ - -C overflow-checks=on \ - '--out-dir=$(CARGO_TARGET_DIR)/release/' \ - $(top_srcdir)/yjit/src/lib.rs + $(Q) $(RUSTC) $(YJIT_RUSTC_ARGS) $(YJIT_LIB_TOUCH) else ifeq ($(YJIT_SUPPORT),no) $(YJIT_LIBS): @@ -48,7 +41,6 @@ else endif # Put this here instead of in common.mk to avoid breaking nmake builds -# TODO: might need to move for BSD Make support miniruby$(EXEEXT): $(YJIT_LIBS) # By using YJIT_BENCH_OPTS instead of RUN_OPTS, you can skip passing the options to `make install` From d3733c2ba5207d29ef5b52a93672873ae369b52c Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert Date: Tue, 20 Sep 2022 19:30:10 -0400 Subject: [PATCH 149/269] Guard `--yjit-stats` behind `#[cfg(feature = "stats")]` (#6409) * Guard --yjit-stats behind #[cfg(feature = "stats")] * Only ask for --yjit-stats with dev builds on cirrus CI * Revert "Only ask for --yjit-stats with dev builds on cirrus CI" This reverts commit cfb5ddfa4b9394ca240447eee02637788435b02a. * Make it so the --yjit-stats option works for non-release builds * Revert accidental changes --- yjit/src/options.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yjit/src/options.rs b/yjit/src/options.rs index cad7bf332a0315..e588876173c90d 100644 --- a/yjit/src/options.rs +++ b/yjit/src/options.rs @@ -151,8 +151,10 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { ("greedy-versioning", "") => unsafe { OPTIONS.greedy_versioning = true }, ("no-type-prop", "") => unsafe { OPTIONS.no_type_prop = true }, + ("stats", "") => { // Insn::IncrCounter uses ldaddal, which works only on ARMv8.1+. + #[cfg(feature = "stats")] #[cfg(target_arch = "aarch64")] if !std::arch::is_aarch64_feature_detected!("lse") { eprintln!("Your processor does not support --yjit-stats. Aborting."); @@ -161,6 +163,7 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { unsafe { OPTIONS.gen_stats = true } }, + ("trace-exits", "") => unsafe { OPTIONS.gen_trace_exits = true; OPTIONS.gen_stats = true }, ("dump-insns", "") => unsafe { OPTIONS.dump_insns = true }, ("verify-ctx", "") => unsafe { OPTIONS.verify_ctx = true }, From 45482fea3c874c4441e1846fb30cac1a92ffbd51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Mon, 12 Sep 2022 14:41:23 +0900 Subject: [PATCH 150/269] LLVM 15 released See https://discourse.llvm.org/t/llvm-15-0-0-release/65099 --- .github/workflows/compilers.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 340dd20f575ff2..70d0e3ae6ca08b 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -22,7 +22,7 @@ concurrency: # environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that # restriction. env: - default_cc: clang-14 + default_cc: clang-15 append_cc: '' # -O1 is faster than -O3 in our tests... Majority of time are consumed trying @@ -91,6 +91,7 @@ jobs: # https://sourceware.org/annobin/annobin.html/Test-pie.html TEST_ANNOCHECK_OPTS: "--skip-pie" check: true + - { name: clang-16, env: { default_cc: clang-16 } } - { name: clang-15, env: { default_cc: clang-15 } } - { name: clang-14, env: { default_cc: clang-14 } } - { name: clang-13, env: { default_cc: clang-13 } } @@ -214,7 +215,7 @@ jobs: name: ${{ matrix.entry.name }} runs-on: ubuntu-latest container: - image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-14' }} + image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-15' }} options: --user root if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} env: ${{ matrix.entry.env || matrix.env }} From 104069e75a385d9b04c86cd31f2aa60547ad6a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Tue, 13 Sep 2022 15:08:50 +0900 Subject: [PATCH 151/269] syserr_initialize: delete redundant strerror() declaration This line issues a warning on clang. strerror is of course a part of ISO C since its dawn. We practically have never needed it. --- error.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/error.c b/error.c index 07fa04627e8d5d..113264eeb7fb91 100644 --- a/error.c +++ b/error.c @@ -2437,9 +2437,6 @@ get_syserr(int n) static VALUE syserr_initialize(int argc, VALUE *argv, VALUE self) { -#if !defined(_WIN32) - char *strerror(); -#endif const char *err; VALUE mesg, error, func, errmsg; VALUE klass = rb_obj_class(self); From 406ebb25a017dba5b066a42a1c325e57fa644e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Tue, 13 Sep 2022 15:18:46 +0900 Subject: [PATCH 152/269] obj_refer_only_sharables_p_i: need derefernce Clang says "warning: variable 'pcnt' set but not used" here. In fact it doesn't. The intention is clear that we want to increment cnt, not pcnt. Adding a * mark solves everything. --- ractor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ractor.c b/ractor.c index 0eddc165fa4b23..3bd6c04af00b35 100644 --- a/ractor.c +++ b/ractor.c @@ -2685,7 +2685,7 @@ obj_refer_only_shareables_p_i(VALUE obj, void *ptr) int *pcnt = (int *)ptr; if (!rb_ractor_shareable_p(obj)) { - pcnt++; + *pcnt++; } } From 06cb0305161eaae0f006ffb3a7f341a5771f239f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Tue, 13 Sep 2022 16:26:33 +0900 Subject: [PATCH 153/269] vm_insnhelper.c: add casts Why they have not been at the first place? Siblings have proper casts. --- vm_insnhelper.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index c68872a9bf6b62..8a82a8db32a8cf 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -2700,14 +2700,16 @@ static VALUE call_cfunc_m2(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) { ractor_unsafe_check(); - return (*func)(recv, rb_ary_new4(argc, argv)); + VALUE(*f)(VALUE, VALUE) = (VALUE(*)(VALUE, VALUE))func; + return (*f)(recv, rb_ary_new4(argc, argv)); } static VALUE call_cfunc_m1(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) { ractor_unsafe_check(); - return (*func)(argc, argv, recv); + VALUE(*f)(int, const VALUE *, VALUE) = (VALUE(*)(int, const VALUE *, VALUE))func; + return (*f)(argc, argv, recv); } static VALUE @@ -2841,13 +2843,15 @@ call_cfunc_15(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) static VALUE ractor_safe_call_cfunc_m2(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) { - return (*func)(recv, rb_ary_new4(argc, argv)); + VALUE(*f)(VALUE, VALUE) = (VALUE(*)(VALUE, VALUE))func; + return (*f)(recv, rb_ary_new4(argc, argv)); } static VALUE ractor_safe_call_cfunc_m1(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) { - return (*func)(argc, argv, recv); + VALUE(*f)(int, const VALUE *, VALUE) = (VALUE(*)(int, const VALUE *, VALUE))func; + return (*f)(argc, argv, recv); } static VALUE From 0cd86ffb2e4b270084510a0ed056329ddc949e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Wed, 14 Sep 2022 11:32:24 +0900 Subject: [PATCH 154/269] avoid ANYARGS Use macro instead of a static functon. This isn't very amusing but doing this wihtout a macro (is possibe but) seems just too much. --- file.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/file.c b/file.c index de7ed5e33ba601..862f9630dfde88 100644 --- a/file.c +++ b/file.c @@ -6504,12 +6504,10 @@ rb_find_file(VALUE path) return copy_path_class(tmp, path); } -static void -define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc) -{ - rb_define_module_function(rb_mFileTest, name, func, argc); - rb_define_singleton_method(rb_cFile, name, func, argc); -} +#define define_filetest_function(name, func, argc) do { \ + rb_define_module_function(rb_mFileTest, name, func, argc); \ + rb_define_singleton_method(rb_cFile, name, func, argc); \ +} while(false) const char ruby_null_device[] = #if defined DOSISH From 2f8012c9491e7cf10f4080f6eecdb67d3aa83556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Wed, 14 Sep 2022 14:12:17 +0900 Subject: [PATCH 155/269] rb_define_method: dedicated overload for rb_f_notimplement rb_f_notimplement was type-compatible with VALUE(*)(ANYARGS), but not any longer in C23. Provide a dedicated path for it. --- include/ruby/internal/anyargs.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h index 9d8d16fdab6bd8..e3e1b6166db3fa 100644 --- a/include/ruby/internal/anyargs.h +++ b/include/ruby/internal/anyargs.h @@ -239,15 +239,16 @@ # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_method_12(n)) # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n)) # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n)) # define RBIMPL_ANYARGS_ATTRSET(sym) RBIMPL_ATTR_MAYBE_UNUSED() RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_WEAKREF(sym) # define RBIMPL_ANYARGS_DECL(sym, ...) \ +RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _notimpl(__VA_ARGS__, VALUE(*)(int, const VALUE *, VALUE, VALUE), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m3(__VA_ARGS__, VALUE(*)(ANYARGS), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m2(__VA_ARGS__, VALUE(*)(VALUE, VALUE), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m1(__VA_ARGS__, VALUE(*)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE), int); \ From 8a577cbc63e164a0771581ea7234578f5beab100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Wed, 14 Sep 2022 14:17:56 +0900 Subject: [PATCH 156/269] cref_replace_with_duplicated_cref_each_frame: returns a pointer Why use FALSE here? --- vm_insnhelper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 8a82a8db32a8cf..ed1c175eb3c287 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -799,7 +799,7 @@ cref_replace_with_duplicated_cref_each_frame(const VALUE *vptr, int can_be_svar, break; } } - return FALSE; + return NULL; } static rb_cref_t * From 4e64edb6cd8d1b444c591bfd50ec3d357e794f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Wed, 14 Sep 2022 14:35:47 +0900 Subject: [PATCH 157/269] vm_method_cfunc_is: get rid of ANYARGS ANYARGS-ed function prototypes are basically prohibited in C23. Use __attribute__((__transparent_union__)) instead. --- vm_insnhelper.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index ed1c175eb3c287..a662de468dda9a 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1980,8 +1980,33 @@ vm_search_method(VALUE cd_owner, struct rb_call_data *cd, VALUE recv) return vm_search_method_fastpath(cd_owner, cd, klass); } +#if __has_attribute(transparent_union) +typedef union { + VALUE (*anyargs)(ANYARGS); + VALUE (*f00)(VALUE); + VALUE (*f01)(VALUE, VALUE); + VALUE (*f02)(VALUE, VALUE, VALUE); + VALUE (*f03)(VALUE, VALUE, VALUE, VALUE); + VALUE (*f04)(VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f05)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f06)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f07)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f08)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f09)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f10)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f11)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f12)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f13)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f14)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f15)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*fm1)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE); +} __attribute__((__transparent_union__)) cfunc_type; +#else +typedef VALUE (*cfunc_type)(ANYARGS); +#endif + static inline int -check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)(ANYARGS)) +check_cfunc(const rb_callable_method_entry_t *me, cfunc_type func) { if (! me) { return false; @@ -1994,13 +2019,17 @@ check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)(ANYARGS)) return false; } else { +#if __has_attribute(transparent_union) + return me->def->body.cfunc.func == func.anyargs; +#else return me->def->body.cfunc.func == func; +#endif } } } static inline int -vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, VALUE (*func)(ANYARGS)) +vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, cfunc_type func) { VM_ASSERT(iseq != NULL); const struct rb_callcache *cc = vm_search_method((VALUE)iseq, cd, recv); From ec5798d28bca2c214da886e27d8e870c55301563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Wed, 14 Sep 2022 14:43:31 +0900 Subject: [PATCH 158/269] type pun rb_f_notimplement Other functions are already type-punned elsewhere. rb_f_notimplement is the only exceptional function that appear literally. We have to take care of it by hand. --- vm_method.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm_method.c b/vm_method.c index fbe62ecd4ca28e..03ab66d580ef60 100644 --- a/vm_method.c +++ b/vm_method.c @@ -372,7 +372,7 @@ void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi) { if (argc < -2 || 15 < argc) rb_raise(rb_eArgError, "arity out of range: %d for -2..15", argc); - if (func != rb_f_notimplement) { + if (func != (VALUE(*)(ANYARGS))rb_f_notimplement) { rb_method_cfunc_t opt; opt.func = func; opt.argc = argc; @@ -561,7 +561,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de RB_OBJ_WRITE(me, &def->body.bmethod.defined_ractor, rb_ractor_self(GET_RACTOR())); return; case VM_METHOD_TYPE_NOTIMPLEMENTED: - setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), rb_f_notimplement_internal, -1); + setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), (VALUE(*)(ANYARGS))rb_f_notimplement_internal, -1); return; case VM_METHOD_TYPE_OPTIMIZED: def->body.optimized = *(rb_method_optimized_t *)opts; From 77c61ae3ab51746c986ff853c85b5e05bc6a942b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Wed, 14 Sep 2022 14:47:26 +0900 Subject: [PATCH 159/269] cc_new: vm_ci_new's 4th argument is a pointer Don't pass boolean. --- vm_eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm_eval.c b/vm_eval.c index db8ca455d94ec6..c7ad71e2792675 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -408,7 +408,7 @@ cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme) } if (cc == NULL) { - const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, false); // TODO: proper ci + const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, NULL); // TODO: proper ci cc = vm_cc_new(klass, cme, vm_call_general); METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme); vm_ccs_push(klass, ccs, ci, cc); From 45741918e1963fce3416b253712cfd4745368e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Fri, 16 Sep 2022 15:14:05 +0900 Subject: [PATCH 160/269] reserved_word: just use gperf 3.1 declaration The reason why this was commented out was because of gperf 3.0 vs 3.1 differences (see [Feature #13883]). Five years passed, I am pretty confident that we can drop support of old versions here. Ditto for uniname2ctype_p(), onig_jis_property(), and zonetab(). --- defs/keywords | 2 +- defs/lex.c.src | 2 +- enc/jis/props.h.blt | 4 ++-- enc/jis/props.kwd | 2 +- enc/jis/props.src | 2 +- enc/unicode/14.0.0/name2ctype.h | 6 +----- ext/date/zonetab.h | 2 +- ext/date/zonetab.list | 2 +- lex.c.blt | 2 +- tool/enc-unicode.rb | 6 +----- 10 files changed, 11 insertions(+), 19 deletions(-) diff --git a/defs/keywords b/defs/keywords index fc30ec2d15923f..a1b1f4f60fff88 100644 --- a/defs/keywords +++ b/defs/keywords @@ -2,7 +2,7 @@ struct kwtable {short name, id[2], state;}; const struct kwtable *rb_reserved_word(const char *, unsigned int); #ifndef RIPPER -static const struct kwtable *reserved_word(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/); +static const struct kwtable *reserved_word(register const char *str, register size_t len); #define rb_reserved_word(str, len) reserved_word(str, len) %} diff --git a/defs/lex.c.src b/defs/lex.c.src index fc30ec2d15923f..a1b1f4f60fff88 100644 --- a/defs/lex.c.src +++ b/defs/lex.c.src @@ -2,7 +2,7 @@ struct kwtable {short name, id[2], state;}; const struct kwtable *rb_reserved_word(const char *, unsigned int); #ifndef RIPPER -static const struct kwtable *reserved_word(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/); +static const struct kwtable *reserved_word(register const char *str, register size_t len); #define rb_reserved_word(str, len) reserved_word(str, len) %} diff --git a/enc/jis/props.h.blt b/enc/jis/props.h.blt index 54aa94f8bcbabe..508a0844498441 100644 --- a/enc/jis/props.h.blt +++ b/enc/jis/props.h.blt @@ -69,7 +69,7 @@ struct enc_property { unsigned char ctype; }; -static const struct enc_property *onig_jis_property(/*const char *str, unsigned int len*/); +static const struct enc_property *onig_jis_property(register const char *str, register size_t len); #line 43 "enc/jis/props.kwd" struct enc_property; @@ -82,7 +82,7 @@ struct enc_property; #ifndef GPERF_DOWNCASE #define GPERF_DOWNCASE 1 -static unsigned char gperf_downcase[256] = +static const unsigned char gperf_downcase[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, diff --git a/enc/jis/props.kwd b/enc/jis/props.kwd index 659cf0aff40a57..9606828459e4aa 100644 --- a/enc/jis/props.kwd +++ b/enc/jis/props.kwd @@ -37,7 +37,7 @@ struct enc_property { unsigned char ctype; }; -static const struct enc_property *onig_jis_property(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/); +static const struct enc_property *onig_jis_property(register const char *str, register size_t len); %} struct enc_property; diff --git a/enc/jis/props.src b/enc/jis/props.src index 659cf0aff40a57..9606828459e4aa 100644 --- a/enc/jis/props.src +++ b/enc/jis/props.src @@ -37,7 +37,7 @@ struct enc_property { unsigned char ctype; }; -static const struct enc_property *onig_jis_property(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/); +static const struct enc_property *onig_jis_property(register const char *str, register size_t len); %} struct enc_property; diff --git a/enc/unicode/14.0.0/name2ctype.h b/enc/unicode/14.0.0/name2ctype.h index 99a3eeca1902d6..61c16bafc283fc 100644 --- a/enc/unicode/14.0.0/name2ctype.h +++ b/enc/unicode/14.0.0/name2ctype.h @@ -40642,11 +40642,7 @@ struct uniname2ctype_struct { }; #define uniname2ctype_offset(str) offsetof(struct uniname2ctype_pool_t, uniname2ctype_pool_##str) -static const struct uniname2ctype_struct *uniname2ctype_p( -#if !(1+0) /* if ANSI, old style not to conflict with generated prototype */ - const char *, unsigned int -#endif -); +static const struct uniname2ctype_struct *uniname2ctype_p(register const char *str, register size_t len); #ifndef USE_UNICODE_PROPERTIES #define TOTAL_KEYWORDS 15 diff --git a/ext/date/zonetab.h b/ext/date/zonetab.h index 39a435db16536b..7ced9e03087408 100644 --- a/ext/date/zonetab.h +++ b/ext/date/zonetab.h @@ -36,7 +36,7 @@ struct zone { int name; int offset; }; -static const struct zone *zonetab(); +static const struct zone *zonetab(register const char *str, register size_t len); #line 9 "zonetab.list" struct zone; diff --git a/ext/date/zonetab.list b/ext/date/zonetab.list index d2f902d2d5c53b..748aec1d8a93f0 100644 --- a/ext/date/zonetab.list +++ b/ext/date/zonetab.list @@ -3,7 +3,7 @@ struct zone { int name; int offset; }; -static const struct zone *zonetab(); +static const struct zone *zonetab(register const char *str, register size_t len); %} struct zone; diff --git a/lex.c.blt b/lex.c.blt index 92a4793b00970b..85727ed00f520d 100644 --- a/lex.c.blt +++ b/lex.c.blt @@ -34,7 +34,7 @@ struct kwtable {short name, id[2], state;}; const struct kwtable *rb_reserved_word(const char *, unsigned int); #ifndef RIPPER -static const struct kwtable *reserved_word(/*const char *, unsigned int*/); +static const struct kwtable *reserved_word(register const char *str, register size_t len); #define rb_reserved_word(str, len) reserved_word(str, len) #line 9 "defs/keywords" struct kwtable; diff --git a/tool/enc-unicode.rb b/tool/enc-unicode.rb index 60d3fca753a9b2..6f2576cc37b1c8 100755 --- a/tool/enc-unicode.rb +++ b/tool/enc-unicode.rb @@ -465,11 +465,7 @@ def write(str) }; #define uniname2ctype_offset(str) offsetof(struct uniname2ctype_pool_t, uniname2ctype_pool_##str) -static const struct uniname2ctype_struct *uniname2ctype_p( -#if !(/*ANSI*/+0) /* if ANSI, old style not to conflict with generated prototype */ - const char *, unsigned int -#endif -); +static const struct uniname2ctype_struct *uniname2ctype_p(register const char *str, register size_t len); %} struct uniname2ctype_struct; %% From 437800d3b0bdacf30b82fff15dd5532fbf65c3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Wed, 21 Sep 2022 10:03:30 +0900 Subject: [PATCH 161/269] getenv: is in stdlib.h getenv is a very basic function that has been in stdlib.h since ISO/IEC 9899:1990. There is absolutely zero need for us to redeclare. pty.c already includes stdlib.h out of the box so we need nothing. --- ext/etc/etc.c | 8 ++++++-- ext/pty/pty.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ext/etc/etc.c b/ext/etc/etc.c index 4cd941f5862a5b..c355fe117a2d65 100644 --- a/ext/etc/etc.c +++ b/ext/etc/etc.c @@ -47,8 +47,12 @@ static VALUE sGroup; #define HAVE_UNAME 1 #endif -#ifndef _WIN32 -char *getenv(); +#ifdef STDC_HEADERS +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif #endif char *getlogin(); diff --git a/ext/pty/pty.c b/ext/pty/pty.c index 155d2159421bc2..acec33f9bf5342 100644 --- a/ext/pty/pty.c +++ b/ext/pty/pty.c @@ -170,7 +170,7 @@ establishShell(int argc, VALUE *argv, struct pty_info *info, { int master, slave, status = 0; rb_pid_t pid; - char *p, *getenv(); + char *p; VALUE v; struct child_info carg; char errbuf[32]; From 7060b23ffa25fb53884e99e4ab1fa8919f666beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Wed, 21 Sep 2022 10:14:00 +0900 Subject: [PATCH 162/269] proper function prototypes for openssl Just to reroute compiler warnings. --- ext/openssl/ossl_asn1.c | 19 ++++++++++--------- ext/openssl/ossl_ssl.c | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 0d3fa9ad15f11c..71c452c88af26f 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -509,7 +509,8 @@ ossl_asn1_get_asn1type(VALUE obj) ASN1_TYPE *ret; VALUE value, rflag; void *ptr; - void (*free_func)(); + typedef void free_func_type(void *); + free_func_type *free_func; int tag; tag = ossl_asn1_default_tag(obj); @@ -522,16 +523,16 @@ ossl_asn1_get_asn1type(VALUE obj) case V_ASN1_INTEGER: /* FALLTHROUGH */ case V_ASN1_ENUMERATED: ptr = obj_to_asn1int(value); - free_func = ASN1_INTEGER_free; + free_func = (free_func_type *)ASN1_INTEGER_free; break; case V_ASN1_BIT_STRING: rflag = rb_attr_get(obj, sivUNUSED_BITS); ptr = obj_to_asn1bstr(value, NUM2INT(rflag)); - free_func = ASN1_BIT_STRING_free; + free_func = (free_func_type *)ASN1_BIT_STRING_free; break; case V_ASN1_NULL: ptr = obj_to_asn1null(value); - free_func = ASN1_NULL_free; + free_func = (free_func_type *)ASN1_NULL_free; break; case V_ASN1_OCTET_STRING: /* FALLTHROUGH */ case V_ASN1_UTF8STRING: /* FALLTHROUGH */ @@ -546,24 +547,24 @@ ossl_asn1_get_asn1type(VALUE obj) case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */ case V_ASN1_BMPSTRING: ptr = obj_to_asn1str(value); - free_func = ASN1_STRING_free; + free_func = (free_func_type *)ASN1_STRING_free; break; case V_ASN1_OBJECT: ptr = obj_to_asn1obj(value); - free_func = ASN1_OBJECT_free; + free_func = (free_func_type *)ASN1_OBJECT_free; break; case V_ASN1_UTCTIME: ptr = obj_to_asn1utime(value); - free_func = ASN1_TIME_free; + free_func = (free_func_type *)ASN1_TIME_free; break; case V_ASN1_GENERALIZEDTIME: ptr = obj_to_asn1gtime(value); - free_func = ASN1_TIME_free; + free_func = (free_func_type *)ASN1_TIME_free; break; case V_ASN1_SET: /* FALLTHROUGH */ case V_ASN1_SEQUENCE: ptr = obj_to_asn1derstr(obj); - free_func = ASN1_STRING_free; + free_func = (free_func_type *)ASN1_STRING_free; break; default: ossl_raise(eASN1Error, "unsupported ASN.1 type"); diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index af262d9f561552..6e1a50fd6da809 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1662,7 +1662,7 @@ io_wait_readable(rb_io_t *fptr) } static VALUE -ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) +ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) { SSL *ssl; rb_io_t *fptr; From 1ad1f07306abc1ade6d8865fea0695771e676af3 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 21 Sep 2022 13:38:42 +0900 Subject: [PATCH 163/269] Fix a broken Slack notification The matrix doesn't have `os`. --- .github/workflows/yjit-ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 3c0d5c046c802b..ae108d72a5bbbc 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -136,7 +136,7 @@ jobs: payload: | { "ci": "GitHub Actions", - "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}", + "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}", "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "commit": "${{ github.sha }}", "branch": "${{ github.ref }}".split('/').reverse()[0] From d6e84d97fee0eed62d0df20a144b11ec3c8240d4 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 21 Sep 2022 10:46:35 +0900 Subject: [PATCH 164/269] Fix the missing locale error --- test/irb/test_history.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index 206970714a3088..8da86ca7a8e72d 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -161,6 +161,7 @@ def test_history_concurrent_use def test_history_concurrent_use_not_present backup_home = ENV["HOME"] backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") + IRB.conf[:LC_MESSAGES] = IRB::Locale.new IRB.conf[:SAVE_HISTORY] = 1 Dir.mktmpdir("test_irb_history_") do |tmpdir| ENV["HOME"] = tmpdir From c287deecb6431110df9613181bc12b7ae6d77c2c Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 21 Sep 2022 10:53:06 +0900 Subject: [PATCH 165/269] backup IRBRC environmental variable. It's used by test methods when it's defined. --- test/irb/test_history.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index 8da86ca7a8e72d..fab38074470ed5 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -161,6 +161,7 @@ def test_history_concurrent_use def test_history_concurrent_use_not_present backup_home = ENV["HOME"] backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") + backup_irbrc = ENV.delete("IRBRC") IRB.conf[:LC_MESSAGES] = IRB::Locale.new IRB.conf[:SAVE_HISTORY] = 1 Dir.mktmpdir("test_irb_history_") do |tmpdir| @@ -179,6 +180,7 @@ def test_history_concurrent_use_not_present ensure ENV["HOME"] = backup_home ENV["XDG_CONFIG_HOME"] = backup_xdg_config_home + ENV["IRBRC"] = backup_irbrc end private From 76b4305a595c4d35241cc55f6382a86bb0c2af23 Mon Sep 17 00:00:00 2001 From: Mau Magnaguagno Date: Mon, 29 Aug 2022 18:47:32 -0300 Subject: [PATCH 166/269] [DOC] Improve NEWS.md Fix missing dot and replace error with ArgumentError. --- NEWS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index df9d7e42b0989a..ec8a4aae9fd6fc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -133,8 +133,8 @@ Note: We're only listing outstanding class updates. * Regexp * Regexp.new now supports passing the regexp flags not only as an Integer, - but also as a String Unknown flags raise errors. Otherwise, anything - other than `true`, `false`, `nil` or Integer will be warned. + but also as a String. Unknown flags raise ArgumentError. + Otherwise, anything other than `true`, `false`, `nil` or Integer will be warned. [[Feature #18788]] * Refinement From e3b178066b6ec6c2f4f6e038c303cc50db35cf3d Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 23 May 2022 16:40:15 +0900 Subject: [PATCH 167/269] [DOC] Tweak the doc for `Process.kill` signature Replacing `...` with `*pids` seems to clarify the expected variadic arguments. Note that the expected arguments are two or more with a signal and pids. That is, the method must have at least one pid, which cannot be omitted: ```console % ruby -e 'Process.kill(0)' -e:1:in `kill': wrong number of arguments (given 1, expected 2+) (ArgumentError) from -e:1:in `
' ``` --- process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process.c b/process.c index 735dd2d855e7cc..cb0a0bc542cac4 100644 --- a/process.c +++ b/process.c @@ -8722,7 +8722,7 @@ get_PROCESS_ID(ID _x, VALUE *_y) /* * call-seq: - * Process.kill(signal, pid, ...) -> integer + * Process.kill(signal, pid, *pids) -> integer * * Sends the given signal to the specified process id(s) if _pid_ is positive. * If _pid_ is zero, _signal_ is sent to all processes whose group ID is equal From 017573c3b373290e52f6e9a8fca217039980247d Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Wed, 18 May 2022 21:30:47 +0100 Subject: [PATCH 168/269] socket add FreeBSD's SO_SETFIB constant. --- ext/socket/mkconstants.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index 577958a358d5c2..4de4b2248c6536 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -661,6 +661,7 @@ def def_intern(func_name, pat, prefix_optional=nil) SO_BUSY_POLL nil Set the threshold in microseconds for low latency polling (Linux 3.11) SO_MAX_PACING_RATE nil Cap the rate computed by transport layer. [bytes per second] (Linux 3.13) SO_BPF_EXTENSIONS nil Query supported BPF extensions (Linux 3.14) +SO_SETFIB nil Set the associated routing table for the socket (FreeBSD) SOPRI_INTERACTIVE nil Interactive socket priority SOPRI_NORMAL nil Normal socket priority From 8a9dfb676b0df74644ddd1db6cfefdd2c9283e8a Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Mon, 2 May 2022 11:33:57 +0100 Subject: [PATCH 169/269] sockets add `TCP_CONNECTION_INFO` and `TCP_KEEPALIVE` constants. --- ext/socket/mkconstants.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index 4de4b2248c6536..5e1fcc40d12c5f 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -671,9 +671,11 @@ def def_intern(func_name, pat, prefix_optional=nil) TCP_NODELAY nil Don't delay sending to coalesce packets TCP_MAXSEG nil Set maximum segment size +TCP_CONNECTION_INFO nil Retrieve information about this socket (macOS) TCP_CORK nil Don't send partial frames (Linux 2.2, glibc 2.2) TCP_DEFER_ACCEPT nil Don't notify a listening socket until data is ready (Linux 2.4, glibc 2.2) TCP_INFO nil Retrieve information about this socket (Linux 2.4, glibc 2.2) +TCP_KEEPALIVE nil Idle time before keepalive probes are sent (macOS) TCP_KEEPCNT nil Maximum number of keepalive probes allowed before dropping a connection (Linux 2.4, glibc 2.2) TCP_KEEPIDLE nil Idle time before keepalive probes are sent (Linux 2.4, glibc 2.2) TCP_KEEPINTVL nil Time between keepalive probes (Linux 2.4, glibc 2.2) From 3f387e60ef87d61d5503132f02b96bba8caa32bd Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 17 Feb 2022 17:31:40 +0000 Subject: [PATCH 170/269] Rescue File.expand_path in MSpecScript#try_load if HOME is unavailable mspec tries to load ~/.mspecrc, but some platforms (e.g. WASI) doesn't have HOME concept, so `~` cannot be expanded and `File.expand_path` can fail. --- spec/mspec/lib/mspec/utils/script.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/mspec/lib/mspec/utils/script.rb b/spec/mspec/lib/mspec/utils/script.rb index b9f8b17fdc65f6..dd1603c20a053d 100644 --- a/spec/mspec/lib/mspec/utils/script.rb +++ b/spec/mspec/lib/mspec/utils/script.rb @@ -84,7 +84,12 @@ def try_load(target) names.each do |name| config[:path].each do |dir| - file = File.expand_path name, dir + begin + file = File.expand_path name, dir + rescue ArgumentError + # File.expand_path can issue error e.g. if HOME is not available + next + end if @loaded.include?(file) return true elsif File.exist? file From ec2d13567e396e2e1537d05321785c3d772d1e5b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 21 Feb 2021 13:16:57 +0000 Subject: [PATCH 171/269] Introduces FreeBSD's SO_USER_COOKIE among socketopt's options. --- ext/socket/mkconstants.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index 5e1fcc40d12c5f..2d8998bdef12b5 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -626,6 +626,7 @@ def def_intern(func_name, pat, prefix_optional=nil) SO_ACCEPTCONN nil Socket has had listen() called on it SO_USELOOPBACK nil Bypass hardware when possible SO_ACCEPTFILTER nil There is an accept filter +SO_USER_COOKIE nil Setting an identifier for ipfw purpose mainly SO_DONTTRUNC nil Retain unread data SO_WANTMORE nil Give a hint when more data is ready SO_WANTOOBFLAG nil OOB data is wanted in MSG_FLAG on receive From 8cbbc061c40d0868491e0dc2c42f5cc5eb8d482d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 7 Jul 2021 21:40:15 +0100 Subject: [PATCH 172/269] openbsd sockets add SO_RTABLE constant --- ext/socket/mkconstants.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index 2d8998bdef12b5..9182abb8380660 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -663,6 +663,7 @@ def def_intern(func_name, pat, prefix_optional=nil) SO_MAX_PACING_RATE nil Cap the rate computed by transport layer. [bytes per second] (Linux 3.13) SO_BPF_EXTENSIONS nil Query supported BPF extensions (Linux 3.14) SO_SETFIB nil Set the associated routing table for the socket (FreeBSD) +SO_RTABLE nil Set the routing table for this socket (OpenBSD) SOPRI_INTERACTIVE nil Interactive socket priority SOPRI_NORMAL nil Normal socket priority From d35bc88b37a9cfc491f3bc2aa35ec317febd8289 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 12 Dec 2021 11:08:19 +0000 Subject: [PATCH 173/269] sockopt adding Linux constants, SO_INCOMING_CPU/SO_INCOMING_NAPI_ID. --- ext/socket/mkconstants.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index 9182abb8380660..5e6c0668f63da9 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -664,6 +664,8 @@ def def_intern(func_name, pat, prefix_optional=nil) SO_BPF_EXTENSIONS nil Query supported BPF extensions (Linux 3.14) SO_SETFIB nil Set the associated routing table for the socket (FreeBSD) SO_RTABLE nil Set the routing table for this socket (OpenBSD) +SO_INCOMING_CPU nil Receive the cpu attached to the socket (Linux 3.19) +SO_INCOMING_NAPI_ID nil Receive the napi ID attached to a RX queue (Linux 4.12) SOPRI_INTERACTIVE nil Interactive socket priority SOPRI_NORMAL nil Normal socket priority From 02e25db68f94920810c1c110138ab7b1d6e2ca59 Mon Sep 17 00:00:00 2001 From: Akihiro Sada Date: Tue, 5 Apr 2022 20:37:05 +0900 Subject: [PATCH 174/269] Add URI.escape and URI.unescape to NEWS-3.0.0 [ci skip] --- doc/NEWS/NEWS-3.0.0.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/NEWS/NEWS-3.0.0.md b/doc/NEWS/NEWS-3.0.0.md index 00c26fe5854874..bdbd47327bde3d 100644 --- a/doc/NEWS/NEWS-3.0.0.md +++ b/doc/NEWS/NEWS-3.0.0.md @@ -512,6 +512,18 @@ Outstanding ones only. * This version is Ractor compatible. +* URI + + * URI.escape and URI.unescape have been removed. + Instead, use the following methods depending on your specific use case. + + * CGI.escape + * URI.encode_www_form + * URI.encode_www_form_component + * CGI.unescape + * URI.decode_www_form + * URI.decode_www_form_component + ## Compatibility issues Excluding feature bug fixes. From 796069b2882d8a67ecc36bda7c72affcbad09dae Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 10 Apr 2021 12:47:16 +0100 Subject: [PATCH 175/269] getrlimit adding RLIMIT_NPTS constant. --- process.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/process.c b/process.c index cb0a0bc542cac4..dcab3d4da7ee57 100644 --- a/process.c +++ b/process.c @@ -8975,6 +8975,14 @@ InitVM_process(void) */ rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC)); #endif +#ifdef RLIMIT_NPTS + /* The maximum number of pseudo-terminals that can be created for the + * real user ID of the calling process. + * + * see the system getrlimit(2) manual for details. + */ + rb_define_const(rb_mProcess, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS)); +#endif #ifdef RLIMIT_RSS /* Specifies the limit (in pages) of the process's resident set. * From 1a2ee4cfabba31d367da854f2e834d4f47cbc4b1 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 21 Sep 2022 22:01:16 +0900 Subject: [PATCH 176/269] Try reordering test-all and test-spec for mswin CI Visual Studio 2019 GitHub Actions has been super unstable. Somehow nmake test-spec triggers rebuilding the interpreter and fails. usa suggested a possibility of test-all leaving something impacting test-spec. I'd like to try this patch and see what happens. --- .github/workflows/windows.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4418c78d813cf2..ab9e35d5a303ba 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -128,14 +128,12 @@ jobs: YACC: win_bison - run: nmake test timeout-minutes: 5 + - run: nmake test-spec + timeout-minutes: 10 - run: nmake test-all env: RUBY_TESTOPTS: -j${{env.TEST_JOBS}} --job-status=normal timeout-minutes: 60 - continue-on-error: ${{ matrix.continue-on-error || false }} - - run: nmake test-spec - timeout-minutes: 10 - continue-on-error: ${{ matrix.continue-on-error || false }} - uses: k0kubun/action-slack@v2.0.0 with: payload: | From 6325fc885474937e1250d6605fd594cf70f0794c Mon Sep 17 00:00:00 2001 From: st0012 Date: Mon, 19 Sep 2022 16:14:03 +0100 Subject: [PATCH 177/269] [ruby/irb] Handle non-String $LOAD_PATH values more carefully In addition to String values, $LOAD_PATH can also take objects that respond_to the `to_path` method, like Pathname objects. So `irb` should be able to handle those objects too. And if $LOAD_PATH contains objects that can't be converted into String, `irb` should simply ignore it. https://github.com/ruby/irb/commit/b2f562176b --- lib/irb/completion.rb | 9 +++++++- test/irb/test_completion.rb | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index 9121174a508070..bfe6c7e7a4aac8 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -60,7 +60,14 @@ def self.retrieve_gem_and_system_load_path end } }.flatten if defined?(Gem::Specification) - (gem_paths.to_a | $LOAD_PATH).sort + candidates = (gem_paths.to_a | $LOAD_PATH) + candidates.map do |p| + if p.respond_to?(:to_path) + p.to_path + else + String(p) rescue nil + end + end.compact.sort end def self.retrieve_files_to_require_from_load_path diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb index 3aa99d74d34c4f..4bc3fc7783e521 100644 --- a/test/irb/test_completion.rb +++ b/test/irb/test_completion.rb @@ -4,6 +4,11 @@ module TestIRB class TestCompletion < Test::Unit::TestCase + def setup + # make sure require completion candidates are not cached + IRB::InputCompletor.class_variable_set(:@@files_from_load_path, nil) + end + def test_nonstring_module_name begin require "irb/completion" @@ -84,6 +89,43 @@ def test_complete_require end end + def test_complete_require_with_pathname_in_load_path + temp_dir = Dir.mktmpdir + File.write(File.join(temp_dir, "foo.rb"), "test") + test_path = Pathname.new(temp_dir) + $LOAD_PATH << test_path + + candidates = IRB::InputCompletor::CompletionProc.("'foo", "require ", "") + assert_equal ["'foo"], candidates + ensure + $LOAD_PATH.pop if test_path + FileUtils.remove_entry(temp_dir) if temp_dir + end + + def test_complete_require_with_string_convertable_in_load_path + temp_dir = Dir.mktmpdir + File.write(File.join(temp_dir, "foo.rb"), "test") + object = Object.new + object.define_singleton_method(:to_s) { temp_dir } + $LOAD_PATH << object + + candidates = IRB::InputCompletor::CompletionProc.("'foo", "require ", "") + assert_equal ["'foo"], candidates + ensure + $LOAD_PATH.pop if object + FileUtils.remove_entry(temp_dir) if temp_dir + end + + def test_complete_require_with_malformed_object_in_load_path + object = Object.new + def object.to_s; raise; end + $LOAD_PATH << object + + assert_empty IRB::InputCompletor::CompletionProc.("'foo", "require ", "") + ensure + $LOAD_PATH.pop if object + end + def test_complete_require_library_name_first pend 'Need to use virtual library paths' candidates = IRB::InputCompletor::CompletionProc.("'csv", "require ", "") From c21f820b496351d0f5a8df68192a510973369ac7 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Wed, 21 Sep 2022 14:53:06 +0100 Subject: [PATCH 178/269] [ruby/irb] Fix completion tests https://github.com/ruby/irb/commit/eb1691f636 --- test/irb/test_completion.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb index 4bc3fc7783e521..a3349bc049f111 100644 --- a/test/irb/test_completion.rb +++ b/test/irb/test_completion.rb @@ -1,5 +1,6 @@ # frozen_string_literal: false require "test/unit" +require "pathname" require "irb" module TestIRB @@ -96,7 +97,7 @@ def test_complete_require_with_pathname_in_load_path $LOAD_PATH << test_path candidates = IRB::InputCompletor::CompletionProc.("'foo", "require ", "") - assert_equal ["'foo"], candidates + assert_include candidates, "'foo" ensure $LOAD_PATH.pop if test_path FileUtils.remove_entry(temp_dir) if temp_dir @@ -110,7 +111,7 @@ def test_complete_require_with_string_convertable_in_load_path $LOAD_PATH << object candidates = IRB::InputCompletor::CompletionProc.("'foo", "require ", "") - assert_equal ["'foo"], candidates + assert_include candidates, "'foo" ensure $LOAD_PATH.pop if object FileUtils.remove_entry(temp_dir) if temp_dir @@ -121,7 +122,9 @@ def test_complete_require_with_malformed_object_in_load_path def object.to_s; raise; end $LOAD_PATH << object - assert_empty IRB::InputCompletor::CompletionProc.("'foo", "require ", "") + assert_nothing_raised do + IRB::InputCompletor::CompletionProc.("'foo", "require ", "") + end ensure $LOAD_PATH.pop if object end From 2c6fdc4d65af035769e969dbe5d5718e859973d3 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 22 Sep 2022 00:22:21 +0900 Subject: [PATCH 179/269] Improve Ubuntu GitHub Actions (#6413) Make the job names more understandable and avoid testing too many duplicated things. --- .github/workflows/ubuntu.yml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 6ea8c06b930534..5d7acd7143b11d 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -21,32 +21,35 @@ jobs: make: strategy: matrix: - test_task: ["check", "test-syntax-suggest", "test-bundler-parallel", "test-bundled-gems"] - os: - - ubuntu-20.04 - configure: ["", "cppflags=-DRUBY_DEBUG"] + # main variables included in the job name + test_task: [check] + configure: [cppflags=-DRUBY_DEBUG] # default to use more assertions + arch: [''] + # specify all jobs with `include` to avoid testing duplicated things include: - - test_task: "check" - configure: "" + - test_task: check + - test_task: check arch: i686 - - test_task: "check" + configure: '' # test without -DRUBY_DEBUG as well + - test_task: check configure: "--enable-shared --enable-load-relative" skipped_tests: "TestGem#test_.*_from_binstubs.*" continue-on-skipped_tests: true - - test_task: "test-all TESTS=--repeat-count=2" + - test_task: test-all TESTS=--repeat-count=2 + - test_task: test-syntax-suggest + - test_task: test-bundler-parallel + - test_task: test-bundled-gems fail-fast: false env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} RUBY_DEBUG: ci SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }} - runs-on: ${{ matrix.os || 'ubuntu-20.04' }} + runs-on: ubuntu-20.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build working-directory: - name: Set ENV - env: - configure: ${{matrix.configure}} run: | echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - name: Install libraries @@ -120,7 +123,7 @@ jobs: payload: | { "ci": "GitHub Actions", - "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}", + "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }}", "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "commit": "${{ github.sha }}", "branch": "${{ github.ref }}".split('/').reverse()[0] From 9f686878794291b088d07866c00f8056a0c7dec7 Mon Sep 17 00:00:00 2001 From: tompng Date: Sun, 7 Aug 2022 20:58:17 +0900 Subject: [PATCH 180/269] [ruby/irb] Scan every single characters in IRB::Color.scan https://github.com/ruby/irb/commit/d14e56a65d --- lib/irb/color.rb | 55 +++++++++++++++++++++--------------------- test/irb/test_color.rb | 2 ++ 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/lib/irb/color.rb b/lib/irb/color.rb index 8307af25a94758..401966d181a2b7 100644 --- a/lib/irb/color.rb +++ b/lib/irb/color.rb @@ -128,10 +128,14 @@ def colorize_code(code, complete: true, ignore_error: false, colorable: colorabl symbol_state = SymbolState.new colored = +'' - length = 0 - end_seen = false scan(code, allow_last_error: !complete) do |token, str, expr| + # handle uncolorable code + if token.nil? + colored << Reline::Unicode.escape_for_print(str) + next + end + # IRB::ColorPrinter skips colorizing fragments with any invalid token if ignore_error && ERROR_TOKENS.include?(token) return Reline::Unicode.escape_for_print(code) @@ -147,15 +151,7 @@ def colorize_code(code, complete: true, ignore_error: false, colorable: colorabl colored << line end end - length += str.bytesize - end_seen = true if token == :on___end__ - end - - # give up colorizing incomplete Ripper tokens - unless end_seen or length == code.bytesize - return Reline::Unicode.escape_for_print(code) end - colored end @@ -170,33 +166,38 @@ def without_circular_ref(obj, seen:, &block) end def scan(code, allow_last_error:) - pos = [1, 0] - verbose, $VERBOSE = $VERBOSE, nil RubyLex.compile_with_errors_suppressed(code) do |inner_code, line_no| lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no) + byte_pos = 0 + line_positions = [0] + inner_code.lines.each do |line| + line_positions << line_positions.last + line.bytesize + end + + on_scan = proc do |elem| + str = elem.tok + start_pos = line_positions[elem.pos[0] - 1] + elem.pos[1] + end_pos = start_pos + str.bytesize + next if start_pos < byte_pos + + yield(nil, inner_code.byteslice(byte_pos...start_pos), nil) if byte_pos < start_pos + yield(elem.event, str, elem.state) + byte_pos = end_pos + end + if lexer.respond_to?(:scan) # Ruby 2.7+ lexer.scan.each do |elem| - str = elem.tok next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message - next if ([elem.pos[0], elem.pos[1] + str.bytesize] <=> pos) <= 0 - - str.each_line do |line| - if line.end_with?("\n") - pos[0] += 1 - pos[1] = 0 - else - pos[1] += line.bytesize - end - end - - yield(elem.event, str, elem.state) + on_scan.call(elem) end else - lexer.parse.each do |elem| - yield(elem.event, elem.tok, elem.state) + lexer.parse.sort_by(&:pos).each do |elem| + on_scan.call(elem) end end + # yield uncolorable DATA section + yield(nil, inner_code.byteslice(byte_pos...inner_code.bytesize), nil) if byte_pos < inner_code.bytesize end ensure $VERBOSE = verbose diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb index 6ad64a0ae24133..73e9b389c2e8aa 100644 --- a/test/irb/test_color.rb +++ b/test/irb/test_color.rb @@ -88,6 +88,8 @@ def test_colorize_code "foo(*%W(bar))" => "foo(*#{RED}#{BOLD}%W(#{CLEAR}#{RED}bar#{CLEAR}#{RED}#{BOLD})#{CLEAR})", "$stdout" => "#{GREEN}#{BOLD}$stdout#{CLEAR}", "__END__" => "#{GREEN}__END__#{CLEAR}", + "foo\n__END__\nbar" => "foo\n#{GREEN}__END__#{CLEAR}\nbar", + "foo\n< "foo\n#{RED}< Date: Thu, 11 Aug 2022 06:15:32 +0900 Subject: [PATCH 181/269] [ruby/irb] Update expected colorize result that were uncolored before https://github.com/ruby/irb/commit/52446eb77f --- test/irb/test_color.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb index 73e9b389c2e8aa..dc394f9d68ab52 100644 --- a/test/irb/test_color.rb +++ b/test/irb/test_color.rb @@ -90,6 +90,7 @@ def test_colorize_code "__END__" => "#{GREEN}__END__#{CLEAR}", "foo\n__END__\nbar" => "foo\n#{GREEN}__END__#{CLEAR}\nbar", "foo\n< "foo\n#{RED}< "#{RED}< "#{GREEN}def#{CLEAR} #{BLUE}#{BOLD}req#{CLEAR}(#{RED}#{REVERSE}@a#{CLEAR}) #{GREEN}end#{CLEAR}", }) else - tests.merge!({ - "[1]]]\u0013" => "[1]]]^S", + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7.0') + tests.merge!({ + "[1]]]\u0013" => "[#{BLUE}#{BOLD}1#{CLEAR}]#{RED}#{REVERSE}]#{CLEAR}]^S", + "def req(true) end" => "#{GREEN}def#{CLEAR} #{BLUE}#{BOLD}req#{CLEAR}(#{RED}#{REVERSE}true#{CLEAR}) end", + }) + else + tests.merge!({ + "[1]]]\u0013" => "[#{BLUE}#{BOLD}1#{CLEAR}]]]^S", + "def req(true) end" => "#{GREEN}def#{CLEAR} #{BLUE}#{BOLD}req#{CLEAR}(#{CYAN}#{BOLD}true#{CLEAR}) end", }) + end tests.merge!({ - "def req(true) end" => "def req(true) end", "nil = 1" => "#{CYAN}#{BOLD}nil#{CLEAR} = #{BLUE}#{BOLD}1#{CLEAR}", "alias $x $1" => "#{GREEN}alias#{CLEAR} #{GREEN}#{BOLD}$x#{CLEAR} $1", "class bad; end" => "#{GREEN}class#{CLEAR} bad; #{GREEN}end#{CLEAR}", From 369f1668cd9dd4f361d9082bb729aa510835126b Mon Sep 17 00:00:00 2001 From: tompng Date: Mon, 19 Sep 2022 14:14:10 +0900 Subject: [PATCH 182/269] [ruby/irb] Rewrite on_scan proc to be more readable. https://github.com/ruby/irb/commit/da54e7f081 --- lib/irb/color.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/irb/color.rb b/lib/irb/color.rb index 401966d181a2b7..7071696cb29c7c 100644 --- a/lib/irb/color.rb +++ b/lib/irb/color.rb @@ -176,14 +176,18 @@ def scan(code, allow_last_error:) end on_scan = proc do |elem| - str = elem.tok start_pos = line_positions[elem.pos[0] - 1] + elem.pos[1] - end_pos = start_pos + str.bytesize - next if start_pos < byte_pos - yield(nil, inner_code.byteslice(byte_pos...start_pos), nil) if byte_pos < start_pos - yield(elem.event, str, elem.state) - byte_pos = end_pos + # yield uncolorable code + if byte_pos < start_pos + yield(nil, inner_code.byteslice(byte_pos...start_pos), nil) + end + + if byte_pos <= start_pos + str = elem.tok + yield(elem.event, str, elem.state) + byte_pos = start_pos + str.bytesize + end end if lexer.respond_to?(:scan) # Ruby 2.7+ From 56d773dc6f8a1a9ded3f20cdabf263800e5bf75d Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Wed, 21 Sep 2022 16:34:55 -0500 Subject: [PATCH 183/269] New page IO Streams (#6383) This page provides an overview of IO streams. It's meant to be linked to from many other doc spots. In particular it will be linked to from many places in ARGF, File, IO, and StringIO. --- doc/examples/files.rdoc | 26 ++ doc/io_streams.rdoc | 350 ++++++++++++++++++++++ file.c | 624 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 972 insertions(+), 28 deletions(-) create mode 100644 doc/examples/files.rdoc create mode 100644 doc/io_streams.rdoc diff --git a/doc/examples/files.rdoc b/doc/examples/files.rdoc new file mode 100644 index 00000000000000..f7361327709812 --- /dev/null +++ b/doc/examples/files.rdoc @@ -0,0 +1,26 @@ +# English text with newlines. +text = <<~EOT + First line + Second line + + Fourth line + Fifth line +EOT + +# Russian text. +russian = "\u{442 435 441 442}" # => "тест" + +# Binary data. +data = "\u9990\u9991\u9992\u9993\u9994" + +# Text file. +File.write('t.txt', text) + +# File with Russian text. +File.write('t.rus', russian) + +# File with binary data. +f = File.new('t.dat', 'wb:UTF-16') +f.write(data) +f.close + diff --git a/doc/io_streams.rdoc b/doc/io_streams.rdoc new file mode 100644 index 00000000000000..b686d67eb5fdbf --- /dev/null +++ b/doc/io_streams.rdoc @@ -0,0 +1,350 @@ +== \IO Streams + +Ruby supports processing data as \IO streams; +that is, as data that may be read, re-read, written, re-written, +and traversed via iteration. + +Core classes with such support include: + +- IO, and its derived class File. +- {StringIO}[rdoc-ref:StringIO]: for processing a string. +- {ARGF}[rdoc-ref:ARGF]: for processing files cited on the command line. + +Pre-existing stream objects that are referenced by constants include: + +- $stdin: read-only instance of \IO. +- $stdout: write-only instance of \IO. +- $stderr: read-only instance of \IO. +- \ARGF: read-only instance of \ARGF. + +You can create stream objects: + +- \File: + + - File.new: returns a new \File object. + - File.open: passes a new \File object to given the block. + +- \IO: + + - IO.new: returns a new \IO object for the given integer file descriptor. + - IO.open: passes a new \IO object to the given block. + - IO.popen: returns a new \IO object that is connected to the $stdin + and $stdout of a newly-launched subprocess. + - Kernel#open: returns a new \IO object connected to a given source: + stream, file, or subprocess. + +- \StringIO: + + - StringIO.new: returns a new \StringIO object. + - StringIO.open: passes a new \StringIO object to the given block. + +(You cannot create an \ARGF object, but one already exists.) + +=== About the Examples + +Many examples here use these variables: + + # English text with newlines. + text = <<~EOT + First line + Second line + + Fourth line + Fifth line + EOT + + # Russian text. + russian = "\u{442 435 441 442}" # => "тест" + + # Binary data. + data = "\u9990\u9991\u9992\u9993\u9994" + + # Text file. + File.write('t.txt', text) + + # File with Russian text. + File.write('t.rus', russian) + + # File with binary data. + f = File.new('t.dat', 'wb:UTF-16') + f.write(data) + f.close + +=== Position + +An \IO stream has a nonnegative integer _position_, +which is the byte offset at which the next read or write is to occur; +the relevant methods: + +- +#tell+ (aliased as #pos): Returns the current position (in bytes) in the stream: + + f = File.new('t.txt') + f.tell # => 0 + f.gets # => "First line\n" + f.tell # => 12 + f.close + +- +#pos=+: Sets the position of the stream (in bytes): + + f = File.new('t.txt') + f.tell # => 0 + f.pos = 20 # => 20 + f.tell # => 20 + f.close + +- +#seek+: Sets the position of the stream to a given integer +offset+ + (in bytes), with respect to a given constant +whence+, which is one of: + + - +:CUR+ or IO::SEEK_CUR: + Repositions the stream to its current position plus the given +offset+: + + f = File.new('t.txt') + f.tell # => 0 + f.seek(20, :CUR) # => 0 + f.tell # => 20 + f.seek(-10, :CUR) # => 0 + f.tell # => 10 + f.close + + - +:END+ or IO::SEEK_END: + Repositions the stream to its end plus the given +offset+: + + f = File.new('t.txt') + f.tell # => 0 + f.seek(0, :END) # => 0 # Repositions to stream end. + f.tell # => 52 + f.seek(-20, :END) # => 0 + f.tell # => 32 + f.seek(-40, :END) # => 0 + f.tell # => 12 + f.close + + - +:SET+ or IO:SEEK_SET: + Repositions the stream to the given +offset+: + + f = File.new('t.txt') + f.tell # => 0 + f.seek(20, :SET) # => 0 + f.tell # => 20 + f.seek(40, :SET) # => 0 + f.tell # => 40 + f.close + +- +#rewind+: Positions the stream to the beginning: + + f = File.new('t.txt') + f.tell # => 0 + f.gets # => "First line\n" + f.tell # => 12 + f.rewind # => 0 + f.tell # => 0 + f.close + +=== Lines + +Some reader methods in \IO streams are line-oriented; +such a method reads one or more lines, +which are separated by an implicit or explicit line separator. + +These methods are included (except as noted) in classes Kernel, IO, File, +and {ARGF}[rdoc-ref:ARGF]: + +- +#each_line+ - passes each line to the block; not in Kernel: + + f = File.new('t.txt') + f.each_line {|line| p line } + + Output: + + "First line\n" + "Second line\n" + "\n" + "Fourth line\n" + "Fifth line\n" + + The reading may begin mid-line: + + f = File.new('t.txt') + f.pos = 27 + f.each_line {|line| p line } + + Output: + + "rth line\n" + "Fifth line\n" + +- +#gets+ - returns the next line (which may begin mid-line): + + f = File.new('t.txt') + f.gets # => "First line\n" + f.gets # => "Second line\n" + f.pos = 27 + f.gets # => "rth line\n" + f.readlines # => ["Fifth line\n"] + f.gets # => nil + +- +#readline+ - like #gets, but raises an exception at end-of-file; + not in StringIO. + +- +#readlines+ - returns all remaining lines in an array; + may begin mid-line: + + f = File.new('t.txt') + f.pos = 19 + f.readlines # => ["ine\n", "\n", "Fourth line\n", "Fifth line\n"] + f.readlines # => [] + +Each of these methods may be called with: + +- An optional line separator, +sep+. +- An optional line-size limit, +limit+. +- Both +sep+ and +limit+. + +==== Line Separator + +The default line separator is the given by the global variable $/, +whose value is by default "\n". +The line to be read next is all data from the current position +to the next line separator: + + f = File.new('t.txt') + f.gets # => "First line\n" + f.gets # => "Second line\n" + f.gets # => "\n" + f.gets # => "Fourth line\n" + f.gets # => "Fifth line\n" + f.close + +You can specify a different line separator: + + f = File.new('t.txt') + f.gets('l') # => "First l" + f.gets('li') # => "ine\nSecond li" + f.gets('lin') # => "ne\n\nFourth lin" + f.gets # => "e\n" + f.close + +There are two special line separators: + +- +nil+: The entire stream is read into a single string: + + f = File.new('t.txt') + f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n" + f.close + +- '' (the empty string): The next "paragraph" is read + (paragraphs being separated by two consecutive line separators): + + f = File.new('t.txt') + f.gets('') # => "First line\nSecond line\n\n" + f.gets('') # => "Fourth line\nFifth line\n" + f.close + +==== Line Limit + +The line to be read may be further defined by an optional integer argument +limit+, +which specifies that the number of bytes returned may not be (much) longer +than the given +limit+; +a multi-byte character will not be split, and so a line may be slightly longer +than the given limit. + +If +limit+ is not given, the line is determined only by +sep+. + + # Text with 1-byte characters. + File.new('t.txt') {|f| f.gets(1) } # => "F" + File.new('t.txt') {|f| f.gets(2) } # => "Fi" + File.new('t.txt') {|f| f.gets(3) } # => "Fir" + File.new('t.txt') {|f| f.gets(4) } # => "Firs" + # No more than one line. + File.new('t.txt') {|f| f.gets(10) } # => "First line" + File.new('t.txt') {|f| f.gets(11) } # => "First line\n" + File.new('t.txt') {|f| f.gets(12) } # => "First line\n" + + # Text with 2-byte characters, which will not be split. + File.new('r.rus') {|f| f.gets(1).size } # => 1 + File.new('r.rus') {|f| f.gets(2).size } # => 1 + File.new('r.rus') {|f| f.gets(3).size } # => 2 + File.new('r.rus') {|f| f.gets(4).size } # => 2 + +==== Line Separator and Line Limit + +With arguments +sep+ and +limit+ given, +combines the two behaviors: + +- Returns the next line as determined by line separator +sep+. +- But returns no more bytes than are allowed by the limit. + +Example: + + File.new('t.txt') {|f| f.gets('li', 20) } # => "First li" + File.new('t.txt') {|f| f.gets('li', 2) } # => "Fi" + +==== Line Number + +A readable \IO stream has a _line_ _number_, +which is the non-negative integer line number +in the stream where the next read will occur. + +A new stream is initially has line number +0+. + +\Method IO#lineno returns the line number. + +Reading lines from a stream usually changes its line number: + + f = File.new('t.txt', 'r') + f.lineno # => 0 + f.readline # => "This is line one.\n" + f.lineno # => 1 + f.readline # => "This is the second line.\n" + f.lineno # => 2 + f.readline # => "Here's the third line.\n" + f.lineno # => 3 + f.eof? # => true + f.close + +Iterating over lines in a stream usually changes its line number: + + f = File.new('t.txt') + f.each_line do |line| + p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}" + end + f.close + +Output: + + "position=11 eof?=false lineno=1" + "position=23 eof?=false lineno=2" + "position=24 eof?=false lineno=3" + "position=36 eof?=false lineno=4" + "position=47 eof?=true lineno=5" + +==== Line Options + +A number of \IO methods accept optional keyword arguments +that determine how lines in a stream are to be treated: + +- +:chomp+: If +true+, line separators are omitted; default is +false+. + +=== Open and Closed \IO Streams + +A new \IO stream may be open for reading, open for writing, or both. + +You can close a stream using these methods: + +- +#close+ - closes the stream for both reading and writing. + +- +#close_read+ (not available in \ARGF) - closes the stream for reading. + +- +#close_write+ (not available in \ARGF) - closes the stream for writing. + +You can query whether a stream is closed using these methods: + +- +#closed?+ - returns whether the stream is closed. + +=== Stream End-of-File + +You can query whether a stream is at end-of-file using this method: + +- +#eof?+ (also aliased as +#eof+) - + returns whether the stream is at end-of-file. + diff --git a/file.c b/file.c index 862f9630dfde88..cf67dd2aafd962 100644 --- a/file.c +++ b/file.c @@ -6527,6 +6527,602 @@ const char ruby_null_device[] = * \Class \File extends module FileTest, supporting such singleton methods * as File.exist?. * + * === About the Examples + * + * Many examples here use these variables: + * + * :include: doc/examples/files.rdoc + * + * == \File Access Modes + * + * \Methods File.new and File.open each create a \File object for a given file path. + * + * === \String Access Modes + * + * \Methods File.new and File.open each may take string argument +mode+, which: + * + * - Begins with a 1- or 2-character + * {read/write mode}[rdoc-ref:File@Read-2FWrite+Mode]. + * - May also contain a 1-character {data mode}[rdoc-ref:File@Data+Mode]. + * - May also contain a 1-character + * {file-create mode}[rdoc-ref:File@File-Create+Mode]. + * + * ==== Read/Write Mode + * + * The read/write +mode+ determines: + * + * - Whether the file is to be initially truncated. + * + * - Whether reading is allowed, and if so: + * + * - The initial read position in the file. + * - Where in the file reading can occur. + * + * - Whether writing is allowed, and if so: + * + * - The initial write position in the file. + * - Where in the file writing can occur. + * + * These tables summarize: + * + * Read/Write Modes for Existing File + * + * |------|-----------|----------|----------|----------|-----------| + * | R/W | Initial | | Initial | | Initial | + * | Mode | Truncate? | Read | Read Pos | Write | Write Pos | + * |------|-----------|----------|----------|----------|-----------| + * | 'r' | No | Anywhere | 0 | Error | - | + * | 'w' | Yes | Error | - | Anywhere | 0 | + * | 'a' | No | Error | - | End only | End | + * | 'r+' | No | Anywhere | 0 | Anywhere | 0 | + * | 'w+' | Yes | Anywhere | 0 | Anywhere | 0 | + * | 'a+' | No | Anywhere | End | End only | End | + * |------|-----------|----------|----------|----------|-----------| + * + * Read/Write Modes for \File To Be Created + * + * |------|----------|----------|----------|-----------| + * | R/W | | Initial | | Initial | + * | Mode | Read | Read Pos | Write | Write Pos | + * |------|----------|----------|----------|-----------| + * | 'w' | Error | - | Anywhere | 0 | + * | 'a' | Error | - | End only | 0 | + * | 'w+' | Anywhere | 0 | Anywhere | 0 | + * | 'a+' | Anywhere | 0 | End only | End | + * |------|----------|----------|----------|-----------| + * + * Note that modes 'r' and 'r+' are not allowed + * for a non-existent file (exception raised). + * + * In the tables: + * + * - +Anywhere+ means that methods IO#rewind, IO#pos=, and IO#seek + * may be used to change the file's position, + * so that allowed reading or writing may occur anywhere in the file. + * - End only means that writing can occur only at end-of-file, + * and that methods IO#rewind, IO#pos=, and IO#seek do not affect writing. + * - +Error+ means that an exception is raised if disallowed reading or writing + * is attempted. + * + * ===== Read/Write Modes for Existing \File + * + * - 'r': + * + * - File is not initially truncated: + * + * f = File.new('t.txt') # => # + * f.size == 0 # => false + * + * - File's initial read position is 0: + * + * f.pos # => 0 + * + * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek: + * + * f.readline # => "First line\n" + * f.readline # => "Second line\n" + * + * f.rewind + * f.readline # => "First line\n" + * + * f.pos = 1 + * f.readline # => "irst line\n" + * + * f.seek(1, :CUR) + * f.readline # => "econd line\n" + * + * - Writing is not allowed: + * + * f.write('foo') # Raises IOError. + * + * - 'w': + * + * - File is initially truncated: + * + * path = 't.tmp' + * File.write(path, text) + * f = File.new(path, 'w') + * f.size == 0 # => true + * + * - File's initial write position is 0: + * + * f.pos # => 0 + * + * - File may be written anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.write('foo') + * f.flush + * File.read(path) # => "foo" + * f.pos # => 3 + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.pos # => 6 + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "bazbar" + * f.pos # => 3 + * + * f.pos = 3 + * f.write('foo') + * f.flush + * File.read(path) # => "bazfoo" + * f.pos # => 6 + * + * f.seek(-3, :END) + * f.write('bam') + * f.flush + * File.read(path) # => "bazbam" + * f.pos # => 6 + * + * f.pos = 8 + * f.write('bah') # Zero padding as needed. + * f.flush + * File.read(path) # => "bazbam\u0000\u0000bah" + * f.pos # => 11 + * + * - Reading is not allowed: + * + * f.read # Raises IOError. + * + * - 'a': + * + * - File is not initially truncated: + * + * path = 't.tmp' + * File.write(path, 'foo') + * f = File.new(path, 'a') + * f.size == 0 # => false + * + * - File's initial position is 0 (but is ignored): + * + * f.pos # => 0 + * + * - File may be written only at end-of-file; + * IO#rewind, IO#pos=, IO#seek do not affect writing: + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.write('baz') + * f.flush + * File.read(path) # => "foobarbaz" + * + * f.rewind + * f.write('bat') + * f.flush + * File.read(path) # => "foobarbazbat" + * + * - Reading is not allowed: + * + * f.read # Raises IOError. + * + * - 'r+': + * + * - File is not initially truncated: + * + * path = 't.tmp' + * File.write(path, text) + * f = File.new(path, 'r+') + * f.size == 0 # => false + * + * - File's initial read position is 0: + * + * f.pos # => 0 + * + * - File may be read or written anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.readline # => "First line\n" + * f.readline # => "Second line\n" + * + * f.rewind + * f.readline # => "First line\n" + * + * f.pos = 1 + * f.readline # => "irst line\n" + * + * f.seek(1, :CUR) + * f.readline # => "econd line\n" + * + * f.rewind + * f.write('WWW') + * f.flush + * File.read(path) + * # => "WWWst line\nSecond line\nFourth line\nFifth line\n" + * + * f.pos = 10 + * f.write('XXX') + * f.flush + * File.read(path) + * # => "WWWst lineXXXecond line\nFourth line\nFifth line\n" + * + * f.seek(-6, :END) + * # => 0 + * f.write('YYY') + * # => 3 + * f.flush + * # => # + * File.read(path) + * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n" + * + * f.seek(2, :END) + * f.write('ZZZ') # Zero padding as needed. + * f.flush + * File.read(path) + * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n\u0000\u0000ZZZ" + * + * + * - 'a+': + * + * - File is not initially truncated: + * + * path = 't.tmp' + * File.write(path, 'foo') + * f = File.new(path, 'a+') + * f.size == 0 # => false + * + * - File's initial read position is 0: + * + * f.pos # => 0 + * + * - File may be written only at end-of-file; + * IO#rewind, IO#pos=, IO#seek do not affect writing: + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.write('baz') + * f.flush + * File.read(path) # => "foobarbaz" + * + * f.rewind + * f.write('bat') + * f.flush + * File.read(path) # => "foobarbazbat" + * + * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek: + * + * f.rewind + * f.read # => "foobarbazbat" + * + * f.pos = 3 + * f.read # => "barbazbat" + * + * f.seek(-3, :END) + * f.read # => "bat" + * + * ===== Read/Write Modes for \File To Be Created + * + * Note that modes 'r' and 'r+' are not allowed + * for a non-existent file (exception raised). + * + * - 'w': + * + * - File's initial write position is 0: + * + * path = 't.tmp' + * FileUtils.rm_f(path) + * f = File.new(path, 'w') + * f.pos # => 0 + * + * - File may be written anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.write('foo') + * f.flush + * File.read(path) # => "foo" + * f.pos # => 3 + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.pos # => 6 + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "bazbar" + * f.pos # => 3 + * + * f.pos = 3 + * f.write('foo') + * f.flush + * File.read(path) # => "bazfoo" + * f.pos # => 6 + * + * f.seek(-3, :END) + * f.write('bam') + * f.flush + * File.read(path) # => "bazbam" + * f.pos # => 6 + * + * f.pos = 8 + * f.write('bah') # Zero padding as needed. + * f.flush + * File.read(path) # => "bazbam\u0000\u0000bah" + * f.pos # => 11 + * + * - Reading is not allowed: + * + * f.read # Raises IOError. + * + * - 'a': + * + * - File's initial write position is 0: + * + * path = 't.tmp' + * FileUtils.rm_f(path) + * f = File.new(path, 'a') + * f.pos # => 0 + * + * - Writing occurs only at end-of-file: + * + * f.write('foo') + * f.pos # => 3 + * f.write('bar') + * f.pos # => 6 + * f.flush + * File.read(path) # => "foobar" + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "foobarbaz" + * + * - Reading is not allowed: + * + * f.read # Raises IOError. + * + * - 'w+': + * + * - File's initial position is 0: + * + * path = 't.tmp' + * FileUtils.rm_f(path) + * f = File.new(path, 'w+') + * f.pos # => 0 + * + * - File may be written anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.write('foo') + * f.flush + * File.read(path) # => "foo" + * f.pos # => 3 + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.pos # => 6 + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "bazbar" + * f.pos # => 3 + * + * f.pos = 3 + * f.write('foo') + * f.flush + * File.read(path) # => "bazfoo" + * f.pos # => 6 + * + * f.seek(-3, :END) + * f.write('bam') + * f.flush + * File.read(path) # => "bazbam" + * f.pos # => 6 + * + * f.pos = 8 + * f.write('bah') # Zero padding as needed. + * f.flush + * File.read(path) # => "bazbam\u0000\u0000bah" + * f.pos # => 11 + * + * - File may be read anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.rewind + * # => 0 + * f.read + * # => "bazbam\u0000\u0000bah" + * + * f.pos = 3 + * # => 3 + * f.read + * # => "bam\u0000\u0000bah" + * + * f.seek(-3, :END) + * # => 0 + * f.read + * # => "bah" + * + * - 'a+': + * + * - File's initial write position is 0: + * + * path = 't.tmp' + * FileUtils.rm_f(path) + * f = File.new(path, 'a+') + * f.pos # => 0 + * + * - Writing occurs only at end-of-file: + * + * f.write('foo') + * f.pos # => 3 + * f.write('bar') + * f.pos # => 6 + * f.flush + * File.read(path) # => "foobar" + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "foobarbaz" + * + * - File may be read anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.rewind + * f.read # => "foobarbaz" + * + * f.pos = 3 + * f.read # => "barbaz" + * + * f.seek(-3, :END) + * f.read # => "baz" + * + * f.pos = 800 + * f.read # => "" + * + * ==== Data Mode + * + * To specify whether data is to be treated as text or as binary data, + * either of the following may be suffixed to any of the string read/write modes + * above: + * + * - 't': Text data; sets the default external encoding + * to Encoding::UTF_8; + * on Windows, enables conversion between EOL and CRLF + * and enables interpreting 0x1A as an end-of-file marker. + * - 'b': Binary data; sets the default external encoding + * to Encoding::ASCII_8BIT; + * on Windows, suppresses conversion between EOL and CRLF + * and disables interpreting 0x1A as an end-of-file marker. + * + * If neither is given, the stream defaults to text data. + * + * Examples: + * + * File.new('t.txt', 'rt') + * File.new('t.dat', 'rb') + * + * When the data mode is specified, the read/write mode may not be omitted, + * and the data mode must precede the file-create mode, if given: + * + * File.new('t.dat', 'b') # Raises an exception. + * File.new('t.dat', 'rxb') # Raises an exception. + * + * ==== \File-Create Mode + * + * The following may be suffixed to any writable string mode above: + * + * - 'x': Creates the file if it does not exist; + * raises an exception if the file exists. + * + * Example: + * + * File.new('t.tmp', 'wx') + * + * When the file-create mode is specified, the read/write mode may not be omitted, + * and the file-create mode must follow the data mode: + * + * File.new('t.dat', 'x') # Raises an exception. + * File.new('t.dat', 'rxb') # Raises an exception. + * + * === \Integer Access Modes + * + * When mode is an integer it must be one or more of the following constants, + * which may be combined by the bitwise OR operator |: + * + * - +File::RDONLY+: Open for reading only. + * - +File::WRONLY+: Open for writing only. + * - +File::RDWR+: Open for reading and writing. + * - +File::APPEND+: Open for appending only. + * + * Examples: + * + * File.new('t.txt', File::RDONLY) + * File.new('t.tmp', File::RDWR | File::CREAT | File::EXCL) + * + * Note: Method IO#set_encoding does not allow the mode to be specified as an integer. + * + * === File-Create Mode Specified as an \Integer + * + * These constants may also be ORed into the integer mode: + * + * - +File::CREAT+: Create file if it does not exist. + * - +File::EXCL+: Raise an exception if +File::CREAT+ is given and the file exists. + * + * === Data Mode Specified as an \Integer + * + * Data mode cannot be specified as an integer. + * When the stream access mode is given as an integer, + * the data mode is always text, never binary. + * + * Note that although there is a constant +File::BINARY+, + * setting its value in an integer stream mode has no effect; + * this is because, as documented in File::Constants, + * the +File::BINARY+ value disables line code conversion, + * but does not change the external encoding. + * + * === Encodings + * + * Any of the string modes above may specify encodings - + * either external encoding only or both external and internal encodings - + * by appending one or both encoding names, separated by colons: + * + * f = File.new('t.dat', 'rb') + * f.external_encoding # => # + * f.internal_encoding # => nil + * f = File.new('t.dat', 'rb:UTF-16') + * f.external_encoding # => # + * f.internal_encoding # => nil + * f = File.new('t.dat', 'rb:UTF-16:UTF-16') + * f.external_encoding # => # + * f.internal_encoding # => # + * f.close + * + * The numerous encoding names are available in array Encoding.name_list: + * + * Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"] + * + * When the external encoding is set, strings read are tagged by that encoding + * when reading, and strings written are converted to that encoding when + * writing. + * + * When both external and internal encodings are set, + * strings read are converted from external to internal encoding, + * and strings written are converted from internal to external encoding. + * For further details about transcoding input and output, + * see {Encodings}[rdoc-ref:io_streams.rdoc@Encodings]. + * + * If the external encoding is 'BOM|UTF-8', 'BOM|UTF-16LE' + * or 'BOM|UTF16-BE', + * Ruby checks for a Unicode BOM in the input document + * to help determine the encoding. + * For UTF-16 encodings the file open mode must be binary. + * If the BOM is found, + * it is stripped and the external encoding from the BOM is used. + * + * Note that the BOM-style encoding option is case insensitive, + * so 'bom|utf-8' is also valid. + * * == \File Permissions * * A \File object has _permissions_, an octal integer representing @@ -6584,34 +7180,6 @@ const char ruby_null_device[] = * may be found in module File::Constants; * an array of their names is returned by File::Constants.constants. * - * == Example Files - * - * Many examples here use these filenames and their corresponding files: - * - * - t.txt: A text-only file that is assumed to exist via: - * - * text = <<~EOT - * First line - * Second line - * - * Fourth line - * Fifth line - * EOT - * File.write('t.txt', text) - * - * - t.dat: A data file that is assumed to exist via: - * - * data = "\u9990\u9991\u9992\u9993\u9994" - * f = File.open('t.dat', 'wb:UTF-16') - * f.write(data) - * f.close - * - * - t.rus: A Russian-language text file that is assumed to exist via: - * - * File.write('t.rus', "\u{442 435 441 442}") - * - * - t.tmp: A file that is assumed _not_ to exist. - * * == What's Here * * First, what's elsewhere. \Class \File: From 6b0b326ef26478e96d176ed2013530e368e32440 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 22 Sep 2022 09:39:20 +0900 Subject: [PATCH 184/269] Followed up 796069b2882d8a67ecc36bda7c72affcbad09dae --- process.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/process.c b/process.c index dcab3d4da7ee57..d95b717960d097 100644 --- a/process.c +++ b/process.c @@ -5548,6 +5548,9 @@ rlimit_resource_name2int(const char *name, long len, int casetype) #ifdef RLIMIT_NPROC RESCHECK(NPROC); #endif +#ifdef RLIMIT_NPTS + RESCHECK(NPTS); +#endif #ifdef RLIMIT_NICE RESCHECK(NICE); #endif @@ -5776,6 +5779,7 @@ proc_getrlimit(VALUE obj, VALUE resource) * [NICE] ceiling on process's nice(2) value (number) (GNU/Linux) * [NOFILE] file descriptors (number) (SUSv3) * [NPROC] number of processes for the user (number) (4.4BSD, GNU/Linux) + * [NPTS] number of pseudo terminals (number) (FreeBSD) * [RSS] resident memory size (bytes) (4.2BSD, GNU/Linux) * [RTPRIO] ceiling on the process's real-time priority (number) (GNU/Linux) * [RTTIME] CPU time for real-time process (us) (GNU/Linux) From 830b2e217b6298f696cc20df25e6a547a642f0f2 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Tue, 20 Sep 2022 07:22:44 +0000 Subject: [PATCH 185/269] [rubygems/rubygems] Update GitLab CI template with new one GitLab CI now needs the default keyword on specification of image and before_script. https://docs.gitlab.com/ee/ci/yaml/#default Signed-off-by: Takuya Noguchi https://github.com/rubygems/rubygems/commit/b79e78e733 --- lib/bundler/templates/newgem/gitlab-ci.yml.tt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/bundler/templates/newgem/gitlab-ci.yml.tt b/lib/bundler/templates/newgem/gitlab-ci.yml.tt index 0e71ff26a4a91b..42e00392de9380 100644 --- a/lib/bundler/templates/newgem/gitlab-ci.yml.tt +++ b/lib/bundler/templates/newgem/gitlab-ci.yml.tt @@ -1,8 +1,9 @@ -image: ruby:<%= RUBY_VERSION %> +default: + image: ruby:<%= RUBY_VERSION %> -before_script: - - gem install bundler -v <%= Bundler::VERSION %> - - bundle install + before_script: + - gem install bundler -v <%= Bundler::VERSION %> + - bundle install example_job: script: From 9058ba218c382744a737d47e2f9c8c0055386dc8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 22 Sep 2022 12:54:23 +0900 Subject: [PATCH 186/269] Binstubs test with relative path seems passing now --- .github/workflows/ubuntu.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 5d7acd7143b11d..a1818182a34beb 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -33,8 +33,6 @@ jobs: configure: '' # test without -DRUBY_DEBUG as well - test_task: check configure: "--enable-shared --enable-load-relative" - skipped_tests: "TestGem#test_.*_from_binstubs.*" - continue-on-skipped_tests: true - test_task: test-all TESTS=--repeat-count=2 - test_task: test-syntax-suggest - test_task: test-bundler-parallel From 083b4bb65500376e6c5b9fdf9b7901d5ce4e421a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Sep 2022 08:58:19 -0700 Subject: [PATCH 187/269] add rb_control_frame_t --- lib/mjit/c_32.rb | 14 ++++++++++++++ lib/mjit/c_64.rb | 14 ++++++++++++++ tool/mjit/bindgen.rb | 1 + 3 files changed, 29 insertions(+) diff --git a/lib/mjit/c_32.rb b/lib/mjit/c_32.rb index a369a3e852e5dd..e966cc2919e152 100644 --- a/lib/mjit/c_32.rb +++ b/lib/mjit/c_32.rb @@ -191,6 +191,20 @@ def C.rb_callinfo ) end + def C.rb_control_frame_t + @rb_control_frame_t ||= CType::Struct.new( + "rb_control_frame_struct", 32, + pc: [0, CType::Pointer.new { self.VALUE }], + sp: [32, CType::Pointer.new { self.VALUE }], + iseq: [64, CType::Pointer.new { self.rb_iseq_t }], + self: [96, self.VALUE], + ep: [128, CType::Pointer.new { self.VALUE }], + block_code: [160, CType::Pointer.new { CType::Immediate.new(0) }], + __bp__: [192, CType::Pointer.new { self.VALUE }], + jit_return: [224, CType::Pointer.new { CType::Immediate.new(0) }], + ) + end + def C.rb_cref_t @rb_cref_t ||= CType::Struct.new( "rb_cref_struct", 20, diff --git a/lib/mjit/c_64.rb b/lib/mjit/c_64.rb index e3989a12e23d46..ad2cfa74c61b1a 100644 --- a/lib/mjit/c_64.rb +++ b/lib/mjit/c_64.rb @@ -191,6 +191,20 @@ def C.rb_callinfo ) end + def C.rb_control_frame_t + @rb_control_frame_t ||= CType::Struct.new( + "rb_control_frame_struct", 64, + pc: [0, CType::Pointer.new { self.VALUE }], + sp: [64, CType::Pointer.new { self.VALUE }], + iseq: [128, CType::Pointer.new { self.rb_iseq_t }], + self: [192, self.VALUE], + ep: [256, CType::Pointer.new { self.VALUE }], + block_code: [320, CType::Pointer.new { CType::Immediate.new(0) }], + __bp__: [384, CType::Pointer.new { self.VALUE }], + jit_return: [448, CType::Pointer.new { CType::Immediate.new(0) }], + ) + end + def C.rb_cref_t @rb_cref_t ||= CType::Struct.new( "rb_cref_struct", 40, diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 8bee57de7d4cf7..9839baa8d2ec0c 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -565,6 +565,7 @@ def push_target(target) rb_callcache rb_callinfo rb_cref_t + rb_control_frame_t rb_iseq_constant_body rb_iseq_location_t rb_iseq_struct From ec93d09c94401af94bcbf5100a9853540d620f16 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Sep 2022 12:27:59 -0700 Subject: [PATCH 188/269] add rb_execution_context --- lib/mjit/c_32.rb | 53 ++++++++++++++++++++++++++++++++++++++++++-- lib/mjit/c_64.rb | 53 ++++++++++++++++++++++++++++++++++++++++++-- tool/mjit/bindgen.rb | 2 ++ 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/lib/mjit/c_32.rb b/lib/mjit/c_32.rb index e966cc2919e152..993108827161c8 100644 --- a/lib/mjit/c_32.rb +++ b/lib/mjit/c_32.rb @@ -216,6 +216,43 @@ def C.rb_cref_t ) end + def C.rb_execution_context_struct + @rb_execution_context_struct ||= CType::Struct.new( + "rb_execution_context_struct", 244, + vm_stack: [0, CType::Pointer.new { self.VALUE }], + vm_stack_size: [32, CType::Immediate.new(-4)], + cfp: [64, CType::Pointer.new { self.rb_control_frame_t }], + tag: [96, CType::Pointer.new { self.rb_vm_tag }], + interrupt_flag: [128, self.rb_atomic_t], + interrupt_mask: [160, self.rb_atomic_t], + fiber_ptr: [192, CType::Pointer.new { self.rb_fiber_t }], + thread_ptr: [224, CType::Pointer.new { self.rb_thread_struct }], + local_storage: [256, CType::Pointer.new { self.rb_id_table }], + local_storage_recursive_hash: [288, self.VALUE], + local_storage_recursive_hash_for_trace: [320, self.VALUE], + root_lep: [352, CType::Pointer.new { self.VALUE }], + root_svar: [384, self.VALUE], + ensure_list: [416, CType::Pointer.new { self.rb_ensure_list_t }], + trace_arg: [448, CType::Pointer.new { self.rb_trace_arg_struct }], + errinfo: [480, self.VALUE], + passed_block_handler: [512, self.VALUE], + raised_flag: [544, CType::Immediate.new(-2)], + method_missing_reason: [552, self.method_missing_reason], + private_const_reference: [576, self.VALUE], + machine: [608, CType::Struct.new( + "", 168, + stack_start: [0, CType::Pointer.new { self.VALUE }], + stack_end: [32, CType::Pointer.new { self.VALUE }], + stack_maxsize: [64, CType::Immediate.new(-4)], + regs: [96, self.jmp_buf], + )], + ) + end + + def C.rb_execution_context_t + @rb_execution_context_t ||= self.rb_execution_context_struct + end + def C.rb_iseq_constant_body @rb_iseq_constant_body ||= CType::Struct.new( "rb_iseq_constant_body", 204, @@ -414,6 +451,20 @@ def C.rb_cref_struct = CType::Stub.new(:rb_cref_struct) def C.rb_scope_visibility_t = CType::Stub.new(:rb_scope_visibility_t) + def C.rb_vm_tag = CType::Stub.new(:rb_vm_tag) + + def C.rb_atomic_t = CType::Stub.new(:rb_atomic_t) + + def C.rb_fiber_t = CType::Stub.new(:rb_fiber_t) + + def C.rb_id_table = CType::Stub.new(:rb_id_table) + + def C.rb_ensure_list_t = CType::Stub.new(:rb_ensure_list_t) + + def C.rb_trace_arg_struct = CType::Stub.new(:rb_trace_arg_struct) + + def C.jmp_buf = CType::Stub.new(:jmp_buf) + def C.rb_iseq_type = CType::Stub.new(:rb_iseq_type) def C.rb_iseq_param_keyword = CType::Stub.new(:rb_iseq_param_keyword) @@ -426,8 +477,6 @@ def C.rb_snum_t = CType::Stub.new(:rb_snum_t) def C.iseq_bits_t = CType::Stub.new(:iseq_bits_t) - def C.rb_id_table = CType::Stub.new(:rb_id_table) - def C.rb_code_location_t = CType::Stub.new(:rb_code_location_t) def C.iseq_compile_data = CType::Stub.new(:iseq_compile_data) diff --git a/lib/mjit/c_64.rb b/lib/mjit/c_64.rb index ad2cfa74c61b1a..2746ed13a05226 100644 --- a/lib/mjit/c_64.rb +++ b/lib/mjit/c_64.rb @@ -216,6 +216,43 @@ def C.rb_cref_t ) end + def C.rb_execution_context_struct + @rb_execution_context_struct ||= CType::Struct.new( + "rb_execution_context_struct", 368, + vm_stack: [0, CType::Pointer.new { self.VALUE }], + vm_stack_size: [64, CType::Immediate.new(-5)], + cfp: [128, CType::Pointer.new { self.rb_control_frame_t }], + tag: [192, CType::Pointer.new { self.rb_vm_tag }], + interrupt_flag: [256, self.rb_atomic_t], + interrupt_mask: [288, self.rb_atomic_t], + fiber_ptr: [320, CType::Pointer.new { self.rb_fiber_t }], + thread_ptr: [384, CType::Pointer.new { self.rb_thread_struct }], + local_storage: [448, CType::Pointer.new { self.rb_id_table }], + local_storage_recursive_hash: [512, self.VALUE], + local_storage_recursive_hash_for_trace: [576, self.VALUE], + root_lep: [640, CType::Pointer.new { self.VALUE }], + root_svar: [704, self.VALUE], + ensure_list: [768, CType::Pointer.new { self.rb_ensure_list_t }], + trace_arg: [832, CType::Pointer.new { self.rb_trace_arg_struct }], + errinfo: [896, self.VALUE], + passed_block_handler: [960, self.VALUE], + raised_flag: [1024, CType::Immediate.new(-2)], + method_missing_reason: [1032, self.method_missing_reason], + private_const_reference: [1088, self.VALUE], + machine: [1152, CType::Struct.new( + "", 224, + stack_start: [0, CType::Pointer.new { self.VALUE }], + stack_end: [64, CType::Pointer.new { self.VALUE }], + stack_maxsize: [128, CType::Immediate.new(-5)], + regs: [192, self.jmp_buf], + )], + ) + end + + def C.rb_execution_context_t + @rb_execution_context_t ||= self.rb_execution_context_struct + end + def C.rb_iseq_constant_body @rb_iseq_constant_body ||= CType::Struct.new( "rb_iseq_constant_body", 336, @@ -415,6 +452,20 @@ def C.rb_cref_struct = CType::Stub.new(:rb_cref_struct) def C.rb_scope_visibility_t = CType::Stub.new(:rb_scope_visibility_t) + def C.rb_vm_tag = CType::Stub.new(:rb_vm_tag) + + def C.rb_atomic_t = CType::Stub.new(:rb_atomic_t) + + def C.rb_fiber_t = CType::Stub.new(:rb_fiber_t) + + def C.rb_id_table = CType::Stub.new(:rb_id_table) + + def C.rb_ensure_list_t = CType::Stub.new(:rb_ensure_list_t) + + def C.rb_trace_arg_struct = CType::Stub.new(:rb_trace_arg_struct) + + def C.jmp_buf = CType::Stub.new(:jmp_buf) + def C.rb_iseq_type = CType::Stub.new(:rb_iseq_type) def C.rb_iseq_param_keyword = CType::Stub.new(:rb_iseq_param_keyword) @@ -427,8 +478,6 @@ def C.rb_snum_t = CType::Stub.new(:rb_snum_t) def C.iseq_bits_t = CType::Stub.new(:iseq_bits_t) - def C.rb_id_table = CType::Stub.new(:rb_id_table) - def C.rb_code_location_t = CType::Stub.new(:rb_code_location_t) def C.iseq_compile_data = CType::Stub.new(:iseq_compile_data) diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 9839baa8d2ec0c..80bdd36c6fa5b0 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -566,6 +566,8 @@ def push_target(target) rb_callinfo rb_cref_t rb_control_frame_t + rb_execution_context_t + rb_execution_context_struct rb_iseq_constant_body rb_iseq_location_t rb_iseq_struct From f07e651a90b3dd95b68a12a484275a27f068b117 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 22 Sep 2022 14:37:40 +0900 Subject: [PATCH 189/269] Mentioned new constants for Process and Socket classes on NEWS.md --- NEWS.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/NEWS.md b/NEWS.md index ec8a4aae9fd6fc..cc46f5eec44b0f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -131,6 +131,9 @@ Note: We're only listing outstanding class updates. * Proc#dup returns an instance of subclass. [[Bug #17545]] * Proc#parameters now accepts lambda keyword. [[Feature #15357]] +* Process + * Added `RLIMIT_NPTS` constant to FreeBSD platform + * Regexp * Regexp.new now supports passing the regexp flags not only as an Integer, but also as a String. Unknown flags raise ArgumentError. @@ -144,6 +147,16 @@ Note: We're only listing outstanding class updates. * Set is now available as a built-in class without the need for `require "set"`. [[Feature #16989]] It is currently autoloaded via the `Set` constant or a call to `Enumerable#to_set`. +* Socket + * Added the following constants for supported platforms. + * `SO_INCOMING_CPU` + * `SO_INCOMING_NAPI_ID` + * `SO_RTABLE` + * `SO_SETFIB` + * `SO_USER_COOKIE` + * `TCP_KEEPALIVE` + * `TCP_CONNECTION_INFO` + * String * String#byteindex and String#byterindex have been added. [[Feature #13110]] * Update Unicode to Version 14.0.0 and Emoji Version 14.0. [[Feature #18037]] From 928aeef3301d4dc0de3b80ee80c8effa094958c4 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 22 Sep 2022 17:28:28 +0900 Subject: [PATCH 190/269] [ruby/cgi] Bump up 0.3.3 https://github.com/ruby/cgi/commit/c1ffa3a428 --- lib/cgi.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cgi.rb b/lib/cgi.rb index affe8fd3fcefa1..8d51cb24fe17f9 100644 --- a/lib/cgi.rb +++ b/lib/cgi.rb @@ -288,7 +288,7 @@ # class CGI - VERSION = "0.3.2" + VERSION = "0.3.3" end require 'cgi/core' From 4c37eaa979d89be14c2142659daba0588aa64912 Mon Sep 17 00:00:00 2001 From: git Date: Thu, 22 Sep 2022 08:31:12 +0000 Subject: [PATCH 191/269] Update default gems list at 928aeef3301d4dc0de3b80ee80c8ef [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index cc46f5eec44b0f..1f0cb77fee2295 100644 --- a/NEWS.md +++ b/NEWS.md @@ -185,7 +185,7 @@ Note: We're only listing outstanding class updates. * RubyGems 3.4.0.dev * bigdecimal 3.1.2 * bundler 2.4.0.dev - * cgi 0.3.2 + * cgi 0.3.3 * date 3.2.3 * error_highlight 0.4.0 * etc 1.4.0 From 9434a7333c2a23c680a977331a60ca7c502c1ac0 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 17 Sep 2022 20:19:57 +1200 Subject: [PATCH 192/269] Enable coverage for eval. --- compile.c | 6 ++-- iseq.c | 13 ++++++++- spec/ruby/library/coverage/result_spec.rb | 34 +++++++++++++++++------ test/coverage/test_coverage.rb | 17 +++++++++++- vm_eval.c | 8 ++++++ 5 files changed, 64 insertions(+), 14 deletions(-) diff --git a/compile.c b/compile.c index 45cb116983f7ba..7d553bfba1e9fe 100644 --- a/compile.c +++ b/compile.c @@ -2308,9 +2308,9 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) if (ISEQ_COVERAGE(iseq)) { if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) && !(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) { - int line = iobj->insn_info.line_no; - if (line >= 1) { - RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, INT2FIX(0)); + int line = iobj->insn_info.line_no - 1; + if (line >= 0 && line < RARRAY_LEN(ISEQ_LINE_COVERAGE(iseq))) { + RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line, INT2FIX(0)); } } if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) { diff --git a/iseq.c b/iseq.c index 4892d93df1608c..9359fcfe4e982d 100644 --- a/iseq.c +++ b/iseq.c @@ -697,7 +697,6 @@ prepare_iseq_build(rb_iseq_t *iseq, ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL; ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table; - if (option->coverage_enabled) { VALUE coverages = rb_get_coverages(); if (RTEST(coverages)) { @@ -928,6 +927,18 @@ rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_ rb_iseq_t * rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth) { + VALUE coverages = rb_get_coverages(); + if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) { + int line_offset = RB_NUM2INT(first_lineno) - 1; + int line_count = line_offset + ast_line_count(ast); + + if (line_count >= 0) { + int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count; + VALUE coverage = rb_default_coverage(len); + rb_hash_aset(coverages, path, coverage); + } + } + return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno, parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT); } diff --git a/spec/ruby/library/coverage/result_spec.rb b/spec/ruby/library/coverage/result_spec.rb index 4cc43e8462ae87..61283e4545a710 100644 --- a/spec/ruby/library/coverage/result_spec.rb +++ b/spec/ruby/library/coverage/result_spec.rb @@ -91,15 +91,31 @@ Coverage.result.should_not include(@config_file) end - it 'returns the correct results when eval is used' do - Coverage.start - require @eval_code_file.chomp('.rb') - result = Coverage.result + ruby_version_is '3.1'...'3.2' do + it 'returns the correct results when eval is used' do + Coverage.start + require @eval_code_file.chomp('.rb') + result = Coverage.result - result.should == { - @eval_code_file => [ - 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1 - ] - } + result.should == { + @eval_code_file => [ + 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1 + ] + } + end + end + + ruby_version_is '3.2' do + it 'returns the correct results when eval is used' do + Coverage.start + require @eval_code_file.chomp('.rb') + result = Coverage.result + + result.should == { + @eval_code_file => [ + 1, nil, 1, nil, 1, 1, nil, nil, nil, nil, 1 + ] + } + end end end diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb index eefe7e7da6a47c..1bb85b67b3b14d 100644 --- a/test/coverage/test_coverage.rb +++ b/test/coverage/test_coverage.rb @@ -136,7 +136,7 @@ def test_eval f.puts 'REPEATS = 400' f.puts 'def add_method(target)' f.puts ' REPEATS.times do' - f.puts ' target.class_eval(<<~RUBY, __FILE__, __LINE__ + 1)' + f.puts ' target.class_eval(<<~RUBY)' f.puts ' def foo' f.puts ' #{"\n" * rand(REPEATS)}' f.puts ' end' @@ -157,6 +157,21 @@ def test_eval } end + def test_eval_coverage + assert_in_out_err(%w[-rcoverage], <<-"end;", ["[1, nil, 1, nil]"], []) + Coverage.start + + eval(<<-RUBY, TOPLEVEL_BINDING, "test.rb") + s = String.new + begin + s << "foo + bar".freeze; end + RUBY + + p Coverage.result["test.rb"] + end; + end + def test_nocoverage_optimized_line assert_ruby_status(%w[], "#{<<-"begin;"}\n#{<<-'end;'}") begin; diff --git a/vm_eval.c b/vm_eval.c index c7ad71e2792675..9d7a736ad923ff 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1672,6 +1672,8 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, rb_iseq_t *iseq = NULL; rb_ast_t *ast; int isolated_depth = 0; + int coverage_enabled = Qtrue; + { int depth = 1; const VALUE *ep = vm_block_ep(base_block); @@ -1703,11 +1705,17 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, rb_gc_register_mark_object(eval_default_path); } fname = eval_default_path; + coverage_enabled = Qfalse; } rb_parser_set_context(parser, parent, FALSE); ast = rb_parser_compile_string_path(parser, fname, src, line); if (ast->body.root) { + if (ast->body.compile_option == Qnil) { + ast->body.compile_option = rb_obj_hide(rb_ident_hash_new()); + } + rb_hash_aset(ast->body.compile_option, rb_sym_intern_ascii_cstr("coverage_enabled"), coverage_enabled); + iseq = rb_iseq_new_eval(&ast->body, ISEQ_BODY(parent)->location.label, fname, Qnil, INT2FIX(line), From 69130e1614b8b0cae8494c249a04d8086448e864 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 22 Sep 2022 21:07:00 +0900 Subject: [PATCH 193/269] Expand paths used for dumper.rb This seems to be needed on Samuel's environment --- tool/ruby_vm/helpers/dumper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb index c8a65e06506cb8..c083dffa7abf53 100644 --- a/tool/ruby_vm/helpers/dumper.rb +++ b/tool/ruby_vm/helpers/dumper.rb @@ -28,8 +28,8 @@ 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).to_s.sub("#{@base}/", '') - src = path.read mode: 'rt:utf-8:utf-8' + 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}" else From 9fb18e631497fd308b2d1fdd9ad0bef1471de54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Schwanck=20dos=20Santos?= Date: Thu, 15 Sep 2022 13:19:02 +0100 Subject: [PATCH 194/269] [ruby/reline] fix vi-operator-arg https://github.com/ruby/reline/commit/d42cdb8f91 --- lib/reline/line_editor.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 3e0aac759a24b3..9703a8c9f314af 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -1430,7 +1430,7 @@ def editing_mode if @waiting_operator_proc if VI_MOTIONS.include?(method_symbol) old_cursor, old_byte_pointer = @cursor, @byte_pointer - @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1 + @vi_arg = @waiting_operator_vi_arg if (@waiting_operator_vi_arg || 1) > 1 block.(true) unless @waiting_proc cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer From 224a3ea718f32daad2a9c874104c33b072dc361a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Schwanck=20dos=20Santos?= Date: Thu, 15 Sep 2022 13:19:44 +0100 Subject: [PATCH 195/269] [ruby/reline] update version https://github.com/ruby/reline/commit/ce1ac86179 --- lib/reline/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/reline/version.rb b/lib/reline/version.rb index 1bb1c02f3d1813..67a3d694bdc9e8 100644 --- a/lib/reline/version.rb +++ b/lib/reline/version.rb @@ -1,3 +1,3 @@ module Reline - VERSION = '0.3.1' + VERSION = '0.3.2' end From 9d19d910c077d40a1d60d36baa4458926df857d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Schwanck=20dos=20Santos?= Date: Tue, 20 Sep 2022 14:11:55 +0100 Subject: [PATCH 196/269] [ruby/reline] Revert "update version" This reverts commit https://github.com/ruby/reline/commit/ce1ac86179e6. https://github.com/ruby/reline/commit/86602cd244 --- lib/reline/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/reline/version.rb b/lib/reline/version.rb index 67a3d694bdc9e8..1bb1c02f3d1813 100644 --- a/lib/reline/version.rb +++ b/lib/reline/version.rb @@ -1,3 +1,3 @@ module Reline - VERSION = '0.3.2' + VERSION = '0.3.1' end From 696e8914b74cd10047917ce677b1872ae1f04660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Schwanck=20dos=20Santos?= Date: Tue, 20 Sep 2022 14:40:48 +0100 Subject: [PATCH 197/269] [ruby/reline] PR changes https://github.com/ruby/reline/commit/e8e8d81f47 --- lib/reline/line_editor.rb | 2 +- test/reline/test_key_actor_vi.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 9703a8c9f314af..8153aaba05574e 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -1430,7 +1430,7 @@ def editing_mode if @waiting_operator_proc if VI_MOTIONS.include?(method_symbol) old_cursor, old_byte_pointer = @cursor, @byte_pointer - @vi_arg = @waiting_operator_vi_arg if (@waiting_operator_vi_arg || 1) > 1 + @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1 block.(true) unless @waiting_proc cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer diff --git a/test/reline/test_key_actor_vi.rb b/test/reline/test_key_actor_vi.rb index b3d49c9bbb2ddb..5a8c4ac64d23ed 100644 --- a/test/reline/test_key_actor_vi.rb +++ b/test/reline/test_key_actor_vi.rb @@ -1454,4 +1454,16 @@ def test_vi_kill_line_prev assert_cursor_max(1) assert_line('c') end + + def test_vi_motion_operators + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + + begin + input_keys("test = { foo: bar }\C-[BBBldt}b") + + assert true + rescue NoMethodError + assert false + end + end end From 4bdd8f2ddfce293136fb69257a25e29677612819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Schwanck=20dos=20Santos?= Date: Wed, 21 Sep 2022 15:39:21 +0100 Subject: [PATCH 198/269] [ruby/reline] use assert_nothing_raised https://github.com/ruby/reline/commit/f08be5da09 --- test/reline/test_key_actor_vi.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/reline/test_key_actor_vi.rb b/test/reline/test_key_actor_vi.rb index 5a8c4ac64d23ed..6e1d9dbbdabeaa 100644 --- a/test/reline/test_key_actor_vi.rb +++ b/test/reline/test_key_actor_vi.rb @@ -1458,12 +1458,8 @@ def test_vi_kill_line_prev def test_vi_motion_operators assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) - begin + assert_nothing_raised do input_keys("test = { foo: bar }\C-[BBBldt}b") - - assert true - rescue NoMethodError - assert false end end end From aafbc9068f96e5e1f7f5c2c1fa6cba01ec8422db Mon Sep 17 00:00:00 2001 From: HParker Date: Wed, 21 Sep 2022 13:53:10 -0700 Subject: [PATCH 199/269] avoid extra dup and pop in compile_op_log Co-authored-by: John Hawthorn --- compile.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/compile.c b/compile.c index 7d553bfba1e9fe..c6cd941d22e20f 100644 --- a/compile.c +++ b/compile.c @@ -8831,7 +8831,10 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, } CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head)); - ADD_INSN(ret, node, dup); + + if (!popped) { + ADD_INSN(ret, node, dup); + } if (type == NODE_OP_ASGN_AND) { ADD_INSNL(ret, node, branchunless, lfin); @@ -8840,15 +8843,15 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, ADD_INSNL(ret, node, branchif, lfin); } - ADD_INSN(ret, node, pop); - ADD_LABEL(ret, lassign); - CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value)); - ADD_LABEL(ret, lfin); - - if (popped) { - /* we can apply more optimize */ + if (!popped) { ADD_INSN(ret, node, pop); } + + ADD_LABEL(ret, lassign); + + CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value, popped)); + + ADD_LABEL(ret, lfin); return COMPILE_OK; } From fbaac837cfba23a9d34dc7ee144d7940248222a2 Mon Sep 17 00:00:00 2001 From: HParker Date: Wed, 21 Sep 2022 14:12:45 -0700 Subject: [PATCH 200/269] avoid extra dup and pop in compile_op_asgn2 Co-authored-by: John Hawthorn --- benchmark/vm_lvar_cond_set.yml | 8 ++++++++ compile.c | 35 +++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 benchmark/vm_lvar_cond_set.yml diff --git a/benchmark/vm_lvar_cond_set.yml b/benchmark/vm_lvar_cond_set.yml new file mode 100644 index 00000000000000..1845f9d12ede19 --- /dev/null +++ b/benchmark/vm_lvar_cond_set.yml @@ -0,0 +1,8 @@ +benchmark: + vm_lvar_cond_set: | + a ||= 1 + b ||= 1 + c ||= 1 + d ||= 1 + nil +loop_count: 30000000 diff --git a/compile.c b/compile.c index c6cd941d22e20f..1296f8fb46bb7f 100644 --- a/compile.c +++ b/compile.c @@ -8663,6 +8663,17 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node lfin: # o ? pop # o + # or (popped) + if lcfin # r + eval v # r v + send a= # ? + jump lfin # ? + + lcfin: # r + + lfin: # ? + pop # + # and dup # r o o unless lcfin @@ -8691,32 +8702,36 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag)); if (atype == idOROP || atype == idANDOP) { - ADD_INSN(ret, node, dup); + if (!popped) { + ADD_INSN(ret, node, dup); + } if (atype == idOROP) { ADD_INSNL(ret, node, branchif, lcfin); } else { /* idANDOP */ ADD_INSNL(ret, node, branchunless, lcfin); } - ADD_INSN(ret, node, pop); + if (!popped) { + ADD_INSN(ret, node, pop); + } CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value)); - ADD_INSN(ret, node, swap); - ADD_INSN1(ret, node, topn, INT2FIX(1)); + if (!popped) { + ADD_INSN(ret, node, swap); + ADD_INSN1(ret, node, topn, INT2FIX(1)); + } ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag)); ADD_INSNL(ret, node, jump, lfin); ADD_LABEL(ret, lcfin); - ADD_INSN(ret, node, swap); + if (!popped) { + ADD_INSN(ret, node, swap); + } ADD_LABEL(ret, lfin); ADD_INSN(ret, node, pop); if (lskip) { ADD_LABEL(ret, lskip); } - if (popped) { - /* we can apply more optimize */ - ADD_INSN(ret, node, pop); - } } else { CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value)); @@ -8848,9 +8863,7 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, } ADD_LABEL(ret, lassign); - CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value, popped)); - ADD_LABEL(ret, lfin); return COMPILE_OK; } From b5c459d57af182173ff888b362a38d721c780aa9 Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Thu, 22 Sep 2022 12:20:45 -0400 Subject: [PATCH 201/269] Adds a benchmark to measure freezing objects --- benchmark/vm_freezeobj.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 benchmark/vm_freezeobj.yml diff --git a/benchmark/vm_freezeobj.yml b/benchmark/vm_freezeobj.yml new file mode 100644 index 00000000000000..69a795a35403c0 --- /dev/null +++ b/benchmark/vm_freezeobj.yml @@ -0,0 +1,6 @@ +prelude: | + objs = 100000.times.map { Object.new } +benchmark: + vm_freeze_obj: | + objs.map(&:freeze) +loop_count: 600 From 88bf8ad6e99fa36c7b4a0b5cbb6fc99777360855 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Thu, 22 Sep 2022 09:35:51 -0700 Subject: [PATCH 202/269] Allow --enable-yjit on OpenBSD yjit uses _Unwind_* functions from libunwind. These functions are available in libc++abi (which requires libpthread), so add those to LDFLAGS if enabling yjit on OpenBSD. --- configure.ac | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index 98231989603eef..a3566358bc9b9b 100644 --- a/configure.ac +++ b/configure.ac @@ -3767,6 +3767,10 @@ AS_CASE(["${YJIT_SUPPORT}"], ])) YJIT_LIBS="yjit/target/${rb_rust_target_subdir}/libyjit.a" + AS_CASE(["$target_os"],[openbsd*],[ + # Link libc++abi (which requires libpthread) for _Unwind_* functions needed by yjit + LDFLAGS="$LDFLAGS -lpthread -lc++abi" + ]) YJIT_OBJ='yjit.$(OBJEXT)' AC_DEFINE(USE_YJIT, 1) ], [AC_DEFINE(USE_YJIT, 0)]) From f2bea691cd12150a526ddc4be95c5396f07920ba Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 21:45:58 +0900 Subject: [PATCH 203/269] Builtin RubyVM::MJIT::C --- .gitignore | 5 +++-- common.mk | 12 +++++++++++- inits.c | 1 + lib/mjit/c_32.rb | 2 -- lib/mjit/c_64.rb | 2 -- mjit_compiler.c | 1 + tool/mjit/bindgen.rb | 2 -- tool/ruby_vm/views/lib/mjit/instruction.rb.erb | 2 +- tool/ruby_vm/views/mjit_c.rb.erb | 3 +++ 9 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 tool/ruby_vm/views/mjit_c.rb.erb diff --git a/.gitignore b/.gitignore index c15b68f676ccc7..b7779c0ef253b7 100644 --- a/.gitignore +++ b/.gitignore @@ -233,10 +233,11 @@ lcov*.info /win32/*.ico # MJIT -/rb_mjit_header.h -/mjit_config.h /include/ruby-*/*/rb_mjit_min_header-*.h /lib/mjit/instruction.rb +/mjit_c.rb +/mjit_config.h +/rb_mjit_header.h # YJIT /yjit-bench diff --git a/common.mk b/common.mk index bef00d191fa31e..ff0a2ca17f4458 100644 --- a/common.mk +++ b/common.mk @@ -235,7 +235,12 @@ main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs main: $(srcdir)/lib/mjit/instruction.rb srcs: $(srcdir)/lib/mjit/instruction.rb -$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def +$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def + $(ECHO) generating $@ + $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ + +srcs: $(srcdir)/mjit_c.rb +$(srcdir)/mjit_c.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/views/mjit_c.rb.erb $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ @@ -1094,6 +1099,7 @@ BUILTIN_RB_SRCS = \ $(srcdir)/io.rb \ $(srcdir)/marshal.rb \ $(srcdir)/mjit.rb \ + $(srcdir)/mjit_c.rb \ $(srcdir)/mjit_compiler.rb \ $(srcdir)/pack.rb \ $(srcdir)/trace_point.rb \ @@ -9310,6 +9316,7 @@ miniinit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h miniinit.$(OBJEXT): $(CCAN_DIR)/list/list.h miniinit.$(OBJEXT): $(CCAN_DIR)/str/str.h miniinit.$(OBJEXT): $(hdrdir)/ruby/ruby.h +miniinit.$(OBJEXT): $(srcdir)/mjit_c.rb miniinit.$(OBJEXT): $(top_srcdir)/internal/array.h miniinit.$(OBJEXT): $(top_srcdir)/internal/compilers.h miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h @@ -9500,6 +9507,7 @@ miniinit.$(OBJEXT): {$(VPATH)}miniinit.c miniinit.$(OBJEXT): {$(VPATH)}miniprelude.c miniinit.$(OBJEXT): {$(VPATH)}missing.h miniinit.$(OBJEXT): {$(VPATH)}mjit.rb +miniinit.$(OBJEXT): {$(VPATH)}mjit_c.rb miniinit.$(OBJEXT): {$(VPATH)}mjit_compiler.rb miniinit.$(OBJEXT): {$(VPATH)}nilclass.rb miniinit.$(OBJEXT): {$(VPATH)}node.h @@ -9754,6 +9762,7 @@ mjit_compiler.$(OBJEXT): $(CCAN_DIR)/list/list.h mjit_compiler.$(OBJEXT): $(CCAN_DIR)/str/str.h mjit_compiler.$(OBJEXT): $(hdrdir)/ruby.h mjit_compiler.$(OBJEXT): $(hdrdir)/ruby/ruby.h +mjit_compiler.$(OBJEXT): $(srcdir)/mjit_c.rb mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/array.h mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/class.h mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/compile.h @@ -9939,6 +9948,7 @@ mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.c mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.h mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rb mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rbinc +mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_c.rbinc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_unit.h mjit_compiler.$(OBJEXT): {$(VPATH)}node.h mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_assert.h diff --git a/inits.c b/inits.c index 65a54d8f29b026..9decba3c11006a 100644 --- a/inits.c +++ b/inits.c @@ -104,6 +104,7 @@ rb_call_builtin_inits(void) BUILTIN(marshal); #if USE_MJIT BUILTIN(mjit); + BUILTIN(mjit_c); BUILTIN(mjit_compiler); #endif Init_builtin_prelude(); diff --git a/lib/mjit/c_32.rb b/lib/mjit/c_32.rb index 993108827161c8..7d70595ba24b6b 100644 --- a/lib/mjit/c_32.rb +++ b/lib/mjit/c_32.rb @@ -1,8 +1,6 @@ require_relative 'c_type' module RubyVM::MJIT - C = Object.new - def C.NOT_COMPILED_STACK_SIZE = -1 def C.USE_LAZY_LOAD = false diff --git a/lib/mjit/c_64.rb b/lib/mjit/c_64.rb index 2746ed13a05226..33fb8475d67419 100644 --- a/lib/mjit/c_64.rb +++ b/lib/mjit/c_64.rb @@ -1,8 +1,6 @@ require_relative 'c_type' module RubyVM::MJIT - C = Object.new - def C.NOT_COMPILED_STACK_SIZE = -1 def C.USE_LAZY_LOAD = false diff --git a/mjit_compiler.c b/mjit_compiler.c index 2594678f779aad..f04f7b0c036147 100644 --- a/mjit_compiler.c +++ b/mjit_compiler.c @@ -164,6 +164,7 @@ has_cache_for_send(rb_execution_context_t *ec, VALUE self, VALUE cc_addr, VALUE extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); +#include "mjit_c.rbinc" #include "mjit_compiler.rbinc" #endif // USE_MJIT diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 80bdd36c6fa5b0..8bc537f0d1f73d 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -343,8 +343,6 @@ def generate(nodes) println "require_relative 'c_type'" println println "module RubyVM::MJIT" - println " C = Object.new" - println # Define macros @macros.each do |macro| diff --git a/tool/ruby_vm/views/lib/mjit/instruction.rb.erb b/tool/ruby_vm/views/lib/mjit/instruction.rb.erb index 1c462de53a437c..dac53668a030a9 100644 --- a/tool/ruby_vm/views/lib/mjit/instruction.rb.erb +++ b/tool/ruby_vm/views/lib/mjit/instruction.rb.erb @@ -37,4 +37,4 @@ module RubyVM::MJIT } private_constant(*constants) -end if RubyVM::MJIT.enabled? +end diff --git a/tool/ruby_vm/views/mjit_c.rb.erb b/tool/ruby_vm/views/mjit_c.rb.erb new file mode 100644 index 00000000000000..06cc88c072628f --- /dev/null +++ b/tool/ruby_vm/views/mjit_c.rb.erb @@ -0,0 +1,3 @@ +module RubyVM::MJIT + C = Object.new +end if RubyVM::MJIT.enabled? From e81a612413500caa6763a765d12255ea7f150f68 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 22:32:21 +0900 Subject: [PATCH 204/269] Auto-generate mjit_c.rb.erb --- .github/workflows/mjit-bindgen.yml | 5 +---- tool/mjit/bindgen.rb | 10 ++++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/mjit-bindgen.yml b/.github/workflows/mjit-bindgen.yml index 33a1650f922ac4..04c9ac4a9f2ade 100644 --- a/.github/workflows/mjit-bindgen.yml +++ b/.github/workflows/mjit-bindgen.yml @@ -23,9 +23,6 @@ jobs: matrix: include: - task: mjit-bindgen - configure: '--enable-yjit=dev_nodebug' - - task: mjit-bindgen - arch: i686 fail-fast: false env: SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }} @@ -74,7 +71,7 @@ jobs: env: arch: ${{ matrix.arch }} run: >- - $SETARCH ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }} + $SETARCH ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug ${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE} - run: $SETARCH make incs - run: $SETARCH make diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 8bc537f0d1f73d..c35a6dfcde9d70 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -336,7 +336,13 @@ def initialize(macros:, enums:, types:, ruby_fields:) @references = Set.new end - def generate(nodes) + def generate(_nodes) + println "module RubyVM::MJIT" + println " C = Object.new" + println "end if RubyVM::MJIT.enabled?" + end + + def legacy_generate(nodes) # TODO: Support nested declarations nodes_index = nodes.group_by(&:spelling).transform_values(&:last) @@ -589,4 +595,4 @@ def push_target(target) ) generator.generate(nodes) -File.write(File.join(src_dir, "lib/mjit/c_#{arch_bits}.rb"), generator.src) +File.write(File.join(src_dir, 'tool/ruby_vm/views/mjit_c.rb.erb'), generator.src) From 00c441ce7ac76dedd272acf9a9daec59d9d5d908 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 22:44:29 +0900 Subject: [PATCH 205/269] Bindgen macro with builtin --- lib/mjit/c_32.rb | 10 -- lib/mjit/c_64.rb | 10 -- tool/mjit/bindgen.rb | 261 ++----------------------------- tool/ruby_vm/views/mjit_c.rb.erb | 10 ++ 4 files changed, 26 insertions(+), 265 deletions(-) diff --git a/lib/mjit/c_32.rb b/lib/mjit/c_32.rb index 7d70595ba24b6b..f2acfd1761b7e5 100644 --- a/lib/mjit/c_32.rb +++ b/lib/mjit/c_32.rb @@ -1,16 +1,6 @@ require_relative 'c_type' module RubyVM::MJIT - def C.NOT_COMPILED_STACK_SIZE = -1 - - def C.USE_LAZY_LOAD = false - - def C.USE_RVARGC = true - - def C.VM_CALL_KW_SPLAT = (0x01 << self.VM_CALL_KW_SPLAT_bit) - - def C.VM_CALL_TAILCALL = (0x01 << self.VM_CALL_TAILCALL_bit) - def C.VM_METHOD_TYPE_CFUNC = 1 def C.VM_METHOD_TYPE_ISEQ = 0 diff --git a/lib/mjit/c_64.rb b/lib/mjit/c_64.rb index 33fb8475d67419..4b0d5276ebfb47 100644 --- a/lib/mjit/c_64.rb +++ b/lib/mjit/c_64.rb @@ -1,16 +1,6 @@ require_relative 'c_type' module RubyVM::MJIT - def C.NOT_COMPILED_STACK_SIZE = -1 - - def C.USE_LAZY_LOAD = false - - def C.USE_RVARGC = true - - def C.VM_CALL_KW_SPLAT = (0x01 << self.VM_CALL_KW_SPLAT_bit) - - def C.VM_CALL_TAILCALL = (0x01 << self.VM_CALL_TAILCALL_bit) - def C.VM_METHOD_TYPE_CFUNC = 1 def C.VM_METHOD_TYPE_ISEQ = 0 diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index c35a6dfcde9d70..32a487d4bbc950 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -32,226 +32,17 @@ class Node < Struct.new( :bitwidth, :sizeof_type, :offsetof, - :tokens, :enum_value, :children, keyword_init: true, ) end -class CParser - def initialize(tokens) - @tokens = lex(tokens) - @pos = 0 - end - - def parse - expression - end - - private - - def lex(toks) - toks.map do |tok| - case tok - when /\A\d+\z/ then [:NUMBER, tok] - when /\A0x[0-9a-f]*\z/ then [:NUMBER, tok] - when '(' then [:LEFT_PAREN, tok] - when ')' then [:RIGHT_PAREN, tok] - when 'unsigned', 'int' then [:TYPE, tok] - when '<<' then [:LSHIFT, tok] - when '>>' then [:RSHIFT, tok] - when '-' then [:MINUS, tok] - when '+' then [:PLUS, tok] - when /\A\w+\z/ then [:IDENT, tok] - else - raise "Unknown token: #{tok}" - end - end - end - - def expression - equality - end - - def equality - exp = comparison - - while match(:BANG_EQUAL, :EQUAL_EQUAL) - operator = previous - right = comparison - exp = [:BINARY, operator, exp, right] - end - - exp - end - - def comparison - expr = term - - while match(:GREATER, :GREATER_EQUAL, :LESS, :LESS_EQUAL) - operator = previous - right = comparison - expr = [:BINARY, operator, expr, right] - end - - expr - end - - def term - expr = bitwise - - while match(:MINUS, :PLUS) - operator = previous - right = bitwise - expr = [:BINARY, operator, expr, right] - end - - expr - end - - def bitwise - expr = unary - - while match(:RSHIFT, :LSHIFT) - operator = previous - right = unary - expr = [:BINARY, operator, expr, right] - end - - expr - end - - def unary - if match(:BANG, :MINUS) - [:UNARY, previous, primary] - else - primary - end - end - - def primary - if match(:LEFT_PAREN) - grouping - else - if match(:IDENT) - [:VAR, previous] - elsif match(:NUMBER) - previous - else - raise peek.inspect - end - end - end - - def grouping - if peek.first == :TYPE - cast = types - consume(:RIGHT_PAREN) - exp = [:TYPECAST, cast, unary] - else - exp = [:GROUP, expression] - consume(:RIGHT_PAREN) - end - exp - end - - def consume(tok) - unless peek.first == tok - raise "Expected #{tok} but was #{peek}" - end - advance - end - - def types - list = [] - loop do - thing = peek - break unless thing.first == :TYPE - list << thing - advance - end - list - end - - def match(*toks) - advance if peek && toks.grep(peek.first).any? - end - - def advance - @pos += 1 - raise("nope") if @pos > @tokens.length - true - end - - def peek - @tokens[@pos] - end - - def previous - @tokens[@pos - 1] - end -end - -class ToRuby - def initialize(enums) - @enums = enums - end - - def visit(node) - send node.first, node - end - - private - - def GROUP(node) - "(" + visit(node[1]) + ")" - end - - def BINARY(node) - visit(node[2]) + " " + visit(node[1]) + " " + visit(node[3]) - end - - def TYPECAST(node) - visit node[2] - end - - def NUMBER(node) - node[1].to_s - end - - def UNARY(node) - visit(node[1]) + visit(node[2]) - end - - def lit(node) - node.last - end - - alias MINUS lit - alias RSHIFT lit - alias LSHIFT lit - - def IDENT(node) - if @enums.include?(node.last) - "self.#{node.last}" - else - "unexpected macro token: #{node.last}" - end - end - - def VAR(node) - visit node[1] - end -end - # Parse a C header with ffi-clang and return Node objects. # To ease the maintenance, ffi-clang should be used only inside this class. class HeaderParser def initialize(header, cflags:) - @translation_unit = FFI::Clang::Index.new.parse_translation_unit( - header, cflags, [], { detailed_preprocessing_record: true } - ) + @translation_unit = FFI::Clang::Index.new.parse_translation_unit(header, cflags, [], {}) end def parse @@ -263,10 +54,7 @@ def parse def parse_children(cursor) children = [] cursor.visit_children do |cursor, _parent| - child = parse_cursor(cursor) - if child.kind != :macro_expansion - children << child - end + children << parse_cursor(cursor) next :continue end children @@ -291,11 +79,6 @@ def parse_cursor(cursor) sizeof_type = cursor.type.sizeof end - tokens = nil - if kind == :macro_definition - tokens = @translation_unit.tokenize(cursor.extent).map(&:spelling) - end - enum_value = nil if kind == :enum_constant_decl enum_value = cursor.enum_value @@ -309,7 +92,6 @@ def parse_cursor(cursor) bitwidth: cursor.bitwidth, sizeof_type: sizeof_type, offsetof: offsetof, - tokens: tokens, enum_value: enum_value, children: children, ) @@ -339,9 +121,19 @@ def initialize(macros:, enums:, types:, ruby_fields:) def generate(_nodes) println "module RubyVM::MJIT" println " C = Object.new" + println + + # Define macros + @macros.each do |macro| + println " def C.#{macro} = #{generate_macro(macro)}" + println + end + + chomp println "end if RubyVM::MJIT.enabled?" end + # TODO: Remove this def legacy_generate(nodes) # TODO: Support nested declarations nodes_index = nodes.group_by(&:spelling).transform_values(&:last) @@ -350,15 +142,6 @@ def legacy_generate(nodes) println println "module RubyVM::MJIT" - # Define macros - @macros.each do |macro| - unless definition = generate_macro(nodes_index[macro]) - raise "Failed to generate macro: #{macro}" - end - println " def C.#{macro} = #{definition}" - println - end - # Define enum values @enums.each do |enum, values| values.each do |value| @@ -393,23 +176,11 @@ def legacy_generate(nodes) private - def generate_macro(node) - if node.spelling.start_with?('USE_') - # Special case: Always force USE_* to be true or false - case node - in Node[kind: :macro_definition, tokens: [_, '0' | '1' => token], children: []] - (Integer(token) == 1).to_s - end + def generate_macro(macro) + if macro.start_with?('USE_') + "Primitive.cexpr! %q{ RBOOL(#{macro} != 0) }" else - # Otherwise, convert a C expression to a Ruby expression when possible - case node - in Node[kind: :macro_definition, tokens: tokens, children: []] - if tokens.first != node.spelling - raise "unexpected first token: '#{tokens.first}' != '#{node.spelling}'" - end - ast = CParser.new(tokens.drop(1)).parse - ToRuby.new(@enums.values.flatten).visit(ast) - end + "Primitive.cexpr! %q{ INT2NUM(#{macro}) }" end end diff --git a/tool/ruby_vm/views/mjit_c.rb.erb b/tool/ruby_vm/views/mjit_c.rb.erb index 06cc88c072628f..416f8bfeafb5c0 100644 --- a/tool/ruby_vm/views/mjit_c.rb.erb +++ b/tool/ruby_vm/views/mjit_c.rb.erb @@ -1,3 +1,13 @@ module RubyVM::MJIT C = Object.new + + def C.NOT_COMPILED_STACK_SIZE = Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) } + + def C.USE_LAZY_LOAD = Primitive.cexpr! %q{ RBOOL(USE_LAZY_LOAD != 0) } + + def C.USE_RVARGC = Primitive.cexpr! %q{ RBOOL(USE_RVARGC != 0) } + + def C.VM_CALL_KW_SPLAT = Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT) } + + def C.VM_CALL_TAILCALL = Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL) } end if RubyVM::MJIT.enabled? From 334b8bd4596cd2fff737f4ced2f2bdf1d7f82679 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 23:17:22 +0900 Subject: [PATCH 206/269] Mix manual and auto-generated C APIs --- mjit_compiler.rb | 118 ----------------------------- tool/mjit/bindgen.rb | 31 ++++++-- tool/ruby_vm/views/mjit_c.rb.erb | 124 +++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 125 deletions(-) diff --git a/mjit_compiler.rb b/mjit_compiler.rb index 4996e773dcd2a5..0a558f67e496fb 100644 --- a/mjit_compiler.rb +++ b/mjit_compiler.rb @@ -14,124 +14,6 @@ require 'mjit/c_32' end - class << RubyVM::MJIT::C - def ROBJECT_EMBED_LEN_MAX - Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' - end - - def cdhash_to_hash(cdhash_addr) - Primitive.cdhash_to_hash(cdhash_addr) - end - - def builtin_compiler(f, bf, index, stack_size, builtin_inline_p) - Primitive.builtin_compile(f, bf.to_i, index, stack_size, builtin_inline_p) - end - - def has_cache_for_send(cc, insn) - Primitive.has_cache_for_send(cc.to_i, insn) - 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)))' - rb_iseq_t.new(iseq_addr) - end - - def rb_iseq_path(iseq) - _iseq_addr = iseq.to_i - Primitive.cexpr! 'rb_iseq_path((rb_iseq_t *)NUM2PTR(_iseq_addr))' - end - - def vm_ci_argc(ci) - _ci_addr = ci.to_i - Primitive.cexpr! 'UINT2NUM(vm_ci_argc((CALL_INFO)NUM2PTR(_ci_addr)))' - end - - def vm_ci_flag(ci) - _ci_addr = ci.to_i - Primitive.cexpr! 'UINT2NUM(vm_ci_flag((CALL_INFO)NUM2PTR(_ci_addr)))' - end - - def rb_splat_or_kwargs_p(ci) - _ci_addr = ci.to_i - Primitive.cexpr! 'RBOOL(rb_splat_or_kwargs_p((CALL_INFO)NUM2PTR(_ci_addr)))' - end - - def fastpath_applied_iseq_p(ci, cc, iseq) - _ci_addr = ci.to_i - _cc_addr = cc.to_i - _iseq_addr = iseq.to_i - Primitive.cexpr! 'RBOOL(fastpath_applied_iseq_p((CALL_INFO)NUM2PTR(_ci_addr), (CALL_CACHE)NUM2PTR(_cc_addr), (rb_iseq_t *)NUM2PTR(_iseq_addr)))' - end - - def mjit_opts - addr = Primitive.cexpr! 'PTR2NUM((VALUE)&mjit_opts)' - mjit_options.new(addr) - end - - def mjit_call_attribute_sp_inc(insn, operands) - _operands_addr = operands.to_i - Primitive.cexpr! 'LONG2NUM(mjit_call_attribute_sp_inc(NUM2INT(insn), (VALUE *)NUM2PTR(_operands_addr)))' - end - - def mjit_capture_cc_entries(compiled_body, captured_body) - _compiled_body_addr = compiled_body.to_i - _captured_body_addr = captured_body.to_i - Primitive.cexpr! 'INT2NUM(mjit_capture_cc_entries((struct rb_iseq_constant_body *)NUM2PTR(_compiled_body_addr), (struct rb_iseq_constant_body *)NUM2PTR(_captured_body_addr)))' - end - - #const struct rb_iseq_constant_body *body, union iseq_inline_storage_entry *is_entries - def mjit_capture_is_entries(body, is_entries) - _body_addr = body.to_i - _is_entries_addr = is_entries.to_i - Primitive.cstmt! %{ - mjit_capture_is_entries((struct rb_iseq_constant_body *)NUM2PTR(_body_addr), (union iseq_inline_storage_entry *)NUM2PTR(_is_entries_addr)); - return Qnil; - } - end - - # Convert encoded VM pointers to insn BINs. - def rb_vm_insn_decode(encoded) - Primitive.cexpr! 'INT2NUM(rb_vm_insn_decode(NUM2PTR(encoded)))' - end - - # Convert insn BINs to encoded VM pointers. This one is not used by the compiler, but useful for debugging. - def rb_vm_insn_encode(bin) - Primitive.cexpr! 'PTR2NUM((VALUE)rb_vm_get_insns_address_table()[NUM2INT(bin)])' - end - - def insn_may_depend_on_sp_or_pc(insn, opes) - _opes_addr = opes.to_i - Primitive.cexpr! 'RBOOL(insn_may_depend_on_sp_or_pc(NUM2INT(insn), (VALUE *)NUM2PTR(_opes_addr)))' - end - - # Convert Integer VALUE to an actual Ruby object - def to_ruby(value) - Primitive.cexpr! '(VALUE)NUM2PTR(value)' - end - - # Convert RubyVM::InstructionSequence to C.rb_iseq_t. Not used by the compiler, but useful for debugging. - def rb_iseqw_to_iseq(iseqw) - iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseqw_to_iseq(iseqw))' - rb_iseq_t.new(iseq_addr) - end - - # TODO: remove this after migration - def fprintf(f, str) - Primitive.cstmt! %{ - fprintf((FILE *)NUM2PTR(f), "%s", RSTRING_PTR(str)); - return Qnil; - } - end - - def rb_cFalseClass; Primitive.cexpr! 'PTR2NUM(rb_cFalseClass)' end - def rb_cNilClass; Primitive.cexpr! 'PTR2NUM(rb_cNilClass)' end - def rb_cTrueClass; Primitive.cexpr! 'PTR2NUM(rb_cTrueClass)' end - def rb_cInteger; Primitive.cexpr! 'PTR2NUM(rb_cInteger)' end - def rb_cSymbol; Primitive.cexpr! 'PTR2NUM(rb_cSymbol)' end - def rb_cFloat; Primitive.cexpr! 'PTR2NUM(rb_cFloat)' end - end - require "mjit/instruction" require "mjit/compiler" end diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 32a487d4bbc950..8fa20fe64c14e4 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -100,16 +100,20 @@ def parse_cursor(cursor) # Convert Node objects to a Ruby binding source. class BindingGenerator + BINDGEN_BEG = '### MJIT bindgen begin ###' + BINDGEN_END = '### MJIT bindgen end ###' DEFAULTS = { '_Bool' => 'CType::Bool.new' } DEFAULTS.default_proc = proc { |_h, k| "CType::Stub.new(:#{k})" } attr_reader :src + # @param src_path [String] Source path used for preamble/postamble # @param macros [Array] Imported macros # @param enums [Hash{ Symbol => Array }] Imported enum values # @param types [Array] Imported types # @param ruby_fields [Hash{ Symbol => Array }] Struct VALUE fields that are considered Ruby objects - def initialize(macros:, enums:, types:, ruby_fields:) + def initialize(src_path:, macros:, enums:, types:, ruby_fields:) + @preamble, @postamble = split_ambles(src_path) @src = String.new @macros = macros.sort @enums = enums.transform_keys(&:to_s).transform_values(&:sort).sort.to_h @@ -119,9 +123,7 @@ def initialize(macros:, enums:, types:, ruby_fields:) end def generate(_nodes) - println "module RubyVM::MJIT" - println " C = Object.new" - println + println @preamble # Define macros @macros.each do |macro| @@ -129,8 +131,7 @@ def generate(_nodes) println end - chomp - println "end if RubyVM::MJIT.enabled?" + print @postamble end # TODO: Remove this @@ -176,6 +177,20 @@ def legacy_generate(nodes) private + # Return code before BINDGEN_BEG and code after BINDGEN_END + def split_ambles(src_path) + lines = File.read(src_path).lines + + preamble_end = lines.index { |l| l.include?(BINDGEN_BEG) } + raise "`#{BINDGEN_BEG}` was not found in '#{src_path}'" if preamble_end.nil? + + postamble_beg = lines.index { |l| l.include?(BINDGEN_END) } + raise "`#{BINDGEN_END}` was not found in '#{src_path}'" if postamble_beg.nil? + raise "`#{BINDGEN_BEG}` was found after `#{BINDGEN_END}`" if preamble_end >= postamble_beg + + return lines[0..preamble_end].join, lines[postamble_beg..-1].join + end + def generate_macro(macro) if macro.start_with?('USE_') "Primitive.cexpr! %q{ RBOOL(#{macro} != 0) }" @@ -293,6 +308,7 @@ def push_target(target) end src_dir = File.expand_path('../..', __dir__) +src_path = File.join(src_dir, 'tool/ruby_vm/views/mjit_c.rb.erb') build_dir = File.expand_path(build_dir) cflags = [ src_dir, @@ -303,6 +319,7 @@ def push_target(target) nodes = HeaderParser.new(File.join(src_dir, 'mjit_compiler.h'), cflags: cflags).parse generator = BindingGenerator.new( + src_path: src_path, macros: %w[ NOT_COMPILED_STACK_SIZE USE_LAZY_LOAD @@ -366,4 +383,4 @@ def push_target(target) ) generator.generate(nodes) -File.write(File.join(src_dir, 'tool/ruby_vm/views/mjit_c.rb.erb'), generator.src) +File.write(src_path, generator.src) diff --git a/tool/ruby_vm/views/mjit_c.rb.erb b/tool/ruby_vm/views/mjit_c.rb.erb index 416f8bfeafb5c0..5def975dccc639 100644 --- a/tool/ruby_vm/views/mjit_c.rb.erb +++ b/tool/ruby_vm/views/mjit_c.rb.erb @@ -1,6 +1,128 @@ +# Part of this file is generated by tool/mjit/bindgen.rb. +# Run `make mjit-bindgen` to update code between "MJIT bindgen begin" and "MJIT bindgen end". module RubyVM::MJIT C = Object.new + class << C + def ROBJECT_EMBED_LEN_MAX + Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' + end + + def cdhash_to_hash(cdhash_addr) + Primitive.cdhash_to_hash(cdhash_addr) + end + + def builtin_compiler(f, bf, index, stack_size, builtin_inline_p) + Primitive.builtin_compile(f, bf.to_i, index, stack_size, builtin_inline_p) + end + + def has_cache_for_send(cc, insn) + Primitive.has_cache_for_send(cc.to_i, insn) + 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)))' + rb_iseq_t.new(iseq_addr) + end + + def rb_iseq_path(iseq) + _iseq_addr = iseq.to_i + Primitive.cexpr! 'rb_iseq_path((rb_iseq_t *)NUM2PTR(_iseq_addr))' + end + + def vm_ci_argc(ci) + _ci_addr = ci.to_i + Primitive.cexpr! 'UINT2NUM(vm_ci_argc((CALL_INFO)NUM2PTR(_ci_addr)))' + end + + def vm_ci_flag(ci) + _ci_addr = ci.to_i + Primitive.cexpr! 'UINT2NUM(vm_ci_flag((CALL_INFO)NUM2PTR(_ci_addr)))' + end + + def rb_splat_or_kwargs_p(ci) + _ci_addr = ci.to_i + Primitive.cexpr! 'RBOOL(rb_splat_or_kwargs_p((CALL_INFO)NUM2PTR(_ci_addr)))' + end + + def fastpath_applied_iseq_p(ci, cc, iseq) + _ci_addr = ci.to_i + _cc_addr = cc.to_i + _iseq_addr = iseq.to_i + Primitive.cexpr! 'RBOOL(fastpath_applied_iseq_p((CALL_INFO)NUM2PTR(_ci_addr), (CALL_CACHE)NUM2PTR(_cc_addr), (rb_iseq_t *)NUM2PTR(_iseq_addr)))' + end + + def mjit_opts + addr = Primitive.cexpr! 'PTR2NUM((VALUE)&mjit_opts)' + mjit_options.new(addr) + end + + def mjit_call_attribute_sp_inc(insn, operands) + _operands_addr = operands.to_i + Primitive.cexpr! 'LONG2NUM(mjit_call_attribute_sp_inc(NUM2INT(insn), (VALUE *)NUM2PTR(_operands_addr)))' + end + + def mjit_capture_cc_entries(compiled_body, captured_body) + _compiled_body_addr = compiled_body.to_i + _captured_body_addr = captured_body.to_i + Primitive.cexpr! 'INT2NUM(mjit_capture_cc_entries((struct rb_iseq_constant_body *)NUM2PTR(_compiled_body_addr), (struct rb_iseq_constant_body *)NUM2PTR(_captured_body_addr)))' + end + + #const struct rb_iseq_constant_body *body, union iseq_inline_storage_entry *is_entries + def mjit_capture_is_entries(body, is_entries) + _body_addr = body.to_i + _is_entries_addr = is_entries.to_i + Primitive.cstmt! %{ + mjit_capture_is_entries((struct rb_iseq_constant_body *)NUM2PTR(_body_addr), (union iseq_inline_storage_entry *)NUM2PTR(_is_entries_addr)); + return Qnil; + } + end + + # Convert encoded VM pointers to insn BINs. + def rb_vm_insn_decode(encoded) + Primitive.cexpr! 'INT2NUM(rb_vm_insn_decode(NUM2PTR(encoded)))' + end + + # Convert insn BINs to encoded VM pointers. This one is not used by the compiler, but useful for debugging. + def rb_vm_insn_encode(bin) + Primitive.cexpr! 'PTR2NUM((VALUE)rb_vm_get_insns_address_table()[NUM2INT(bin)])' + end + + def insn_may_depend_on_sp_or_pc(insn, opes) + _opes_addr = opes.to_i + Primitive.cexpr! 'RBOOL(insn_may_depend_on_sp_or_pc(NUM2INT(insn), (VALUE *)NUM2PTR(_opes_addr)))' + end + + # Convert Integer VALUE to an actual Ruby object + def to_ruby(value) + Primitive.cexpr! '(VALUE)NUM2PTR(value)' + end + + # Convert RubyVM::InstructionSequence to C.rb_iseq_t. Not used by the compiler, but useful for debugging. + def rb_iseqw_to_iseq(iseqw) + iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseqw_to_iseq(iseqw))' + rb_iseq_t.new(iseq_addr) + end + + # TODO: remove this after migration + def fprintf(f, str) + Primitive.cstmt! %{ + fprintf((FILE *)NUM2PTR(f), "%s", RSTRING_PTR(str)); + return Qnil; + } + end + + def rb_cFalseClass; Primitive.cexpr! 'PTR2NUM(rb_cFalseClass)' end + def rb_cNilClass; Primitive.cexpr! 'PTR2NUM(rb_cNilClass)' end + def rb_cTrueClass; Primitive.cexpr! 'PTR2NUM(rb_cTrueClass)' end + def rb_cInteger; Primitive.cexpr! 'PTR2NUM(rb_cInteger)' end + def rb_cSymbol; Primitive.cexpr! 'PTR2NUM(rb_cSymbol)' end + def rb_cFloat; Primitive.cexpr! 'PTR2NUM(rb_cFloat)' end + end + + ### MJIT bindgen begin ### + def C.NOT_COMPILED_STACK_SIZE = Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) } def C.USE_LAZY_LOAD = Primitive.cexpr! %q{ RBOOL(USE_LAZY_LOAD != 0) } @@ -10,4 +132,6 @@ module RubyVM::MJIT def C.VM_CALL_KW_SPLAT = Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT) } def C.VM_CALL_TAILCALL = Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL) } + + ### MJIT bindgen end ### end if RubyVM::MJIT.enabled? From 4e0db2f75348fd28ae8ffd242044a020e41afef5 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 23:20:44 +0900 Subject: [PATCH 207/269] mjit_c.rb doesn't need to be an erb --- .gitignore | 1 - common.mk | 5 ----- tool/ruby_vm/views/mjit_c.rb.erb => mjit_c.rb | 0 tool/mjit/bindgen.rb | 2 +- 4 files changed, 1 insertion(+), 7 deletions(-) rename tool/ruby_vm/views/mjit_c.rb.erb => mjit_c.rb (100%) diff --git a/.gitignore b/.gitignore index b7779c0ef253b7..4bbf24f0941934 100644 --- a/.gitignore +++ b/.gitignore @@ -235,7 +235,6 @@ lcov*.info # MJIT /include/ruby-*/*/rb_mjit_min_header-*.h /lib/mjit/instruction.rb -/mjit_c.rb /mjit_config.h /rb_mjit_header.h diff --git a/common.mk b/common.mk index ff0a2ca17f4458..52f132a2f8fecc 100644 --- a/common.mk +++ b/common.mk @@ -239,11 +239,6 @@ $(srcdir)/lib/mjit/instruction.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/vie $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ -srcs: $(srcdir)/mjit_c.rb -$(srcdir)/mjit_c.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/views/mjit_c.rb.erb - $(ECHO) generating $@ - $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ - mjit-headers: $(MJIT_SUPPORT)-mjit-headers no-mjit-headers: PHONY yes-mjit-headers: mjit_config.h PHONY diff --git a/tool/ruby_vm/views/mjit_c.rb.erb b/mjit_c.rb similarity index 100% rename from tool/ruby_vm/views/mjit_c.rb.erb rename to mjit_c.rb diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 8fa20fe64c14e4..57070192663fd4 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -308,7 +308,7 @@ def push_target(target) end src_dir = File.expand_path('../..', __dir__) -src_path = File.join(src_dir, 'tool/ruby_vm/views/mjit_c.rb.erb') +src_path = File.join(src_dir, 'mjit_c.rb') build_dir = File.expand_path(build_dir) cflags = [ src_dir, From 591c3c7a1aae41bb2702dc40609a9bcae0e40ea5 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 23:25:20 +0900 Subject: [PATCH 208/269] Automatically setup bundler of bindgen.rb to easily use it with `tool/mjit/bindgen.rb BUILDDIR` instead of using `make mjit-bindgen`. --- tool/mjit/bindgen.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 57070192663fd4..750fde98055bbe 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -1,5 +1,9 @@ #!/usr/bin/env ruby # frozen_string_literal: true + +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('./Gemfile', __dir__) +require 'bundler/setup' + require 'etc' require 'fiddle/import' require 'set' From 2f5b37533e6129646ddfacffc3da34be02bc9e87 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 23:32:59 +0900 Subject: [PATCH 209/269] Builtin needs to be baseruby-compatible --- mjit_c.rb | 20 +++++++++++++++----- tool/mjit/bindgen.rb | 4 +++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/mjit_c.rb b/mjit_c.rb index 5def975dccc639..ac3b2a2ff20f6f 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -123,15 +123,25 @@ def rb_cFloat; Primitive.cexpr! 'PTR2NUM(rb_cFloat)' end ### MJIT bindgen begin ### - def C.NOT_COMPILED_STACK_SIZE = Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) } + def C.NOT_COMPILED_STACK_SIZE + Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) } + end - def C.USE_LAZY_LOAD = Primitive.cexpr! %q{ RBOOL(USE_LAZY_LOAD != 0) } + def C.USE_LAZY_LOAD + Primitive.cexpr! %q{ RBOOL(USE_LAZY_LOAD != 0) } + end - def C.USE_RVARGC = Primitive.cexpr! %q{ RBOOL(USE_RVARGC != 0) } + def C.USE_RVARGC + Primitive.cexpr! %q{ RBOOL(USE_RVARGC != 0) } + end - def C.VM_CALL_KW_SPLAT = Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT) } + def C.VM_CALL_KW_SPLAT + Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT) } + end - def C.VM_CALL_TAILCALL = Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL) } + def C.VM_CALL_TAILCALL + Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL) } + end ### MJIT bindgen end ### end if RubyVM::MJIT.enabled? diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 750fde98055bbe..98d89e17a423cd 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -131,7 +131,9 @@ def generate(_nodes) # Define macros @macros.each do |macro| - println " def C.#{macro} = #{generate_macro(macro)}" + println " def C.#{macro}" + println " #{generate_macro(macro)}" + println " end" println end From 5cda5938f899ca9ac72f1e44c216f63826516422 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 18 Sep 2022 23:43:24 +0900 Subject: [PATCH 210/269] Bindgen enum with builtin --- common.mk | 3 +- lib/mjit/c_32.rb | 8 ----- lib/mjit/c_64.rb | 8 ----- mjit_c.rb | 24 +++++++++++--- tool/mjit/bindgen.rb | 76 +++++++++++++++----------------------------- 5 files changed, 48 insertions(+), 71 deletions(-) diff --git a/common.mk b/common.mk index 52f132a2f8fecc..4678fd8940c882 100644 --- a/common.mk +++ b/common.mk @@ -9938,12 +9938,13 @@ mjit_compiler.$(OBJEXT): {$(VPATH)}iseq.h mjit_compiler.$(OBJEXT): {$(VPATH)}method.h mjit_compiler.$(OBJEXT): {$(VPATH)}missing.h mjit_compiler.$(OBJEXT): {$(VPATH)}mjit.h +mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_c.rb +mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_c.rbinc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compile_attr.inc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.c mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.h mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rb mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rbinc -mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_c.rbinc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_unit.h mjit_compiler.$(OBJEXT): {$(VPATH)}node.h mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_assert.h diff --git a/lib/mjit/c_32.rb b/lib/mjit/c_32.rb index f2acfd1761b7e5..59ca97709307ba 100644 --- a/lib/mjit/c_32.rb +++ b/lib/mjit/c_32.rb @@ -1,14 +1,6 @@ require_relative 'c_type' module RubyVM::MJIT - def C.VM_METHOD_TYPE_CFUNC = 1 - - def C.VM_METHOD_TYPE_ISEQ = 0 - - def C.VM_CALL_KW_SPLAT_bit = 7 - - def C.VM_CALL_TAILCALL_bit = 8 - def C.CALL_DATA @CALL_DATA ||= self.rb_call_data end diff --git a/lib/mjit/c_64.rb b/lib/mjit/c_64.rb index 4b0d5276ebfb47..9ea32bd327fd8b 100644 --- a/lib/mjit/c_64.rb +++ b/lib/mjit/c_64.rb @@ -1,14 +1,6 @@ require_relative 'c_type' module RubyVM::MJIT - def C.VM_METHOD_TYPE_CFUNC = 1 - - def C.VM_METHOD_TYPE_ISEQ = 0 - - def C.VM_CALL_KW_SPLAT_bit = 7 - - def C.VM_CALL_TAILCALL_bit = 8 - def C.CALL_DATA @CALL_DATA ||= self.rb_call_data end diff --git a/mjit_c.rb b/mjit_c.rb index ac3b2a2ff20f6f..452b534bd3cdbd 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -123,10 +123,6 @@ def rb_cFloat; Primitive.cexpr! 'PTR2NUM(rb_cFloat)' end ### MJIT bindgen begin ### - def C.NOT_COMPILED_STACK_SIZE - Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) } - end - def C.USE_LAZY_LOAD Primitive.cexpr! %q{ RBOOL(USE_LAZY_LOAD != 0) } end @@ -135,13 +131,33 @@ def C.USE_RVARGC Primitive.cexpr! %q{ RBOOL(USE_RVARGC != 0) } end + def C.NOT_COMPILED_STACK_SIZE + Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) } + end + def C.VM_CALL_KW_SPLAT Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT) } end + def C.VM_CALL_KW_SPLAT_bit + Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT_bit) } + end + def C.VM_CALL_TAILCALL Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL) } end + def C.VM_CALL_TAILCALL_bit + Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL_bit) } + end + + def C.VM_METHOD_TYPE_CFUNC + Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_CFUNC) } + end + + def C.VM_METHOD_TYPE_ISEQ + Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) } + end + ### MJIT bindgen end ### end if RubyVM::MJIT.enabled? diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 98d89e17a423cd..ac95bbe639aa8f 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -111,16 +111,16 @@ class BindingGenerator attr_reader :src - # @param src_path [String] Source path used for preamble/postamble - # @param macros [Array] Imported macros - # @param enums [Hash{ Symbol => Array }] Imported enum values + # @param src_path [String] + # @param uses [Array] + # @param ints [Array] # @param types [Array] Imported types # @param ruby_fields [Hash{ Symbol => Array }] Struct VALUE fields that are considered Ruby objects - def initialize(src_path:, macros:, enums:, types:, ruby_fields:) + def initialize(src_path:, uses:, ints:, types:, ruby_fields:) @preamble, @postamble = split_ambles(src_path) @src = String.new - @macros = macros.sort - @enums = enums.transform_keys(&:to_s).transform_values(&:sort).sort.to_h + @uses = uses.sort + @ints = ints.sort @types = types.sort @ruby_fields = ruby_fields.transform_keys(&:to_s) @references = Set.new @@ -129,10 +129,18 @@ def initialize(src_path:, macros:, enums:, types:, ruby_fields:) def generate(_nodes) println @preamble - # Define macros - @macros.each do |macro| - println " def C.#{macro}" - println " #{generate_macro(macro)}" + # Define USE_* macros + @uses.each do |use| + println " def C.#{use}" + println " Primitive.cexpr! %q{ RBOOL(#{use} != 0) }" + println " end" + println + end + + # Define int macros/enums + @ints.each do |int| + println " def C.#{int}" + println " Primitive.cexpr! %q{ INT2NUM(#{int}) }" println " end" println end @@ -149,17 +157,6 @@ def legacy_generate(nodes) println println "module RubyVM::MJIT" - # Define enum values - @enums.each do |enum, values| - values.each do |value| - unless definition = generate_enum(nodes_index[enum], value) - raise "Failed to generate enum value: #{value}" - end - println " def C.#{value} = #{definition}" - println - end - end - # Define types @types.each do |type| unless definition = generate_node(nodes_index[type]) @@ -197,23 +194,6 @@ def split_ambles(src_path) return lines[0..preamble_end].join, lines[postamble_beg..-1].join end - def generate_macro(macro) - if macro.start_with?('USE_') - "Primitive.cexpr! %q{ RBOOL(#{macro} != 0) }" - else - "Primitive.cexpr! %q{ INT2NUM(#{macro}) }" - end - end - - def generate_enum(node, value) - case node - in Node[kind: :enum_decl, children:] - children.find { |c| c.spelling == value }&.enum_value - in Node[kind: :typedef_decl, children: [child]] - generate_enum(child, value) - end - end - # Generate code from a node. Used for constructing a complex nested node. # @param node [Node] def generate_node(node) @@ -326,23 +306,19 @@ def push_target(target) nodes = HeaderParser.new(File.join(src_dir, 'mjit_compiler.h'), cflags: cflags).parse generator = BindingGenerator.new( src_path: src_path, - macros: %w[ - NOT_COMPILED_STACK_SIZE + uses: %w[ USE_LAZY_LOAD USE_RVARGC + ], + ints: %w[ + NOT_COMPILED_STACK_SIZE VM_CALL_KW_SPLAT + VM_CALL_KW_SPLAT_bit VM_CALL_TAILCALL + VM_CALL_TAILCALL_bit + VM_METHOD_TYPE_CFUNC + VM_METHOD_TYPE_ISEQ ], - enums: { - rb_method_type_t: %w[ - VM_METHOD_TYPE_CFUNC - VM_METHOD_TYPE_ISEQ - ], - vm_call_flag_bits: %w[ - VM_CALL_KW_SPLAT_bit - VM_CALL_TAILCALL_bit - ], - }, types: %w[ CALL_DATA IC From 280ff1707e1e0e5f06d9819dc5f8d8deb38b3f6c Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 19 Sep 2022 09:25:04 +0900 Subject: [PATCH 211/269] Drop c_64 and c_32 --- lib/mjit/c_32.rb | 481 -------------------------------------- lib/mjit/c_64.rb | 482 -------------------------------------- mjit_c.rb | 539 +++++++++++++++++++++++++++++++++++++++++++ mjit_compiler.rb | 9 +- tool/mjit/bindgen.rb | 21 +- 5 files changed, 548 insertions(+), 984 deletions(-) delete mode 100644 lib/mjit/c_32.rb delete mode 100644 lib/mjit/c_64.rb diff --git a/lib/mjit/c_32.rb b/lib/mjit/c_32.rb deleted file mode 100644 index 59ca97709307ba..00000000000000 --- a/lib/mjit/c_32.rb +++ /dev/null @@ -1,481 +0,0 @@ -require_relative 'c_type' - -module RubyVM::MJIT - def C.CALL_DATA - @CALL_DATA ||= self.rb_call_data - end - - def C.IC - @IC ||= self.iseq_inline_constant_cache - end - - def C.IVC - @IVC ||= self.iseq_inline_iv_cache_entry - end - - def C.RB_BUILTIN - @RB_BUILTIN ||= self.rb_builtin_function - end - - def C.VALUE - @VALUE ||= CType::Immediate.new(-5) - end - - def C.compile_branch - @compile_branch ||= CType::Struct.new( - "compile_branch", 8, - stack_size: [0, CType::Immediate.new(-4)], - finish_p: [32, self._Bool], - ) - end - - def C.compile_status - @compile_status ||= CType::Struct.new( - "compile_status", 68, - success: [0, self._Bool], - stack_size_for_pos: [32, CType::Pointer.new { CType::Immediate.new(4) }], - local_stack_p: [64, self._Bool], - is_entries: [96, CType::Pointer.new { self.iseq_inline_storage_entry }], - cc_entries_index: [128, CType::Immediate.new(4)], - compiled_iseq: [160, CType::Pointer.new { self.rb_iseq_constant_body }], - compiled_id: [192, CType::Immediate.new(4)], - compile_info: [224, CType::Pointer.new { self.rb_mjit_compile_info }], - merge_ivar_guards_p: [256, self._Bool], - ivar_serial: [288, self.rb_serial_t], - max_ivar_index: [352, CType::Immediate.new(-4)], - inlined_iseqs: [384, CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], - inline_context: [416, self.inlined_call_context], - ) - end - - def C.inlined_call_context - @inlined_call_context ||= CType::Struct.new( - "inlined_call_context", 16, - orig_argc: [0, CType::Immediate.new(4)], - me: [32, self.VALUE], - param_size: [64, CType::Immediate.new(4)], - local_size: [96, CType::Immediate.new(4)], - ) - end - - def C.iseq_inline_constant_cache - @iseq_inline_constant_cache ||= CType::Struct.new( - "iseq_inline_constant_cache", 8, - entry: [0, CType::Pointer.new { self.iseq_inline_constant_cache_entry }], - segments: [32, CType::Pointer.new { self.ID }], - ) - end - - def C.iseq_inline_constant_cache_entry - @iseq_inline_constant_cache_entry ||= CType::Struct.new( - "iseq_inline_constant_cache_entry", 20, - flags: [0, self.VALUE], - value: [32, self.VALUE], - _unused1: [64, self.VALUE], - _unused2: [96, self.VALUE], - ic_cref: [128, CType::Pointer.new { self.rb_cref_t }], - ) - end - - def C.iseq_inline_iv_cache_entry - @iseq_inline_iv_cache_entry ||= CType::Struct.new( - "iseq_inline_iv_cache_entry", 4, - entry: [0, CType::Pointer.new { self.rb_iv_index_tbl_entry }], - ) - end - - def C.iseq_inline_storage_entry - @iseq_inline_storage_entry ||= CType::Union.new( - "iseq_inline_storage_entry", 8, - once: CType::Struct.new( - "", 8, - running_thread: [0, CType::Pointer.new { self.rb_thread_struct }], - value: [32, self.VALUE], - ), - ic_cache: self.iseq_inline_constant_cache, - iv_cache: self.iseq_inline_iv_cache_entry, - ) - end - - def C.mjit_options - @mjit_options ||= CType::Struct.new( - "mjit_options", 28, - on: [0, self._Bool], - save_temps: [8, self._Bool], - warnings: [16, self._Bool], - debug: [24, self._Bool], - debug_flags: [32, CType::Pointer.new { CType::Immediate.new(2) }], - wait: [64, self._Bool], - min_calls: [96, CType::Immediate.new(-4)], - verbose: [128, CType::Immediate.new(4)], - max_cache_size: [160, CType::Immediate.new(4)], - pause: [192, self._Bool], - custom: [200, self._Bool], - ) - end - - def C.rb_builtin_function - @rb_builtin_function ||= CType::Struct.new( - "rb_builtin_function", 20, - func_ptr: [0, CType::Pointer.new { CType::Immediate.new(0) }], - argc: [32, CType::Immediate.new(4)], - index: [64, CType::Immediate.new(4)], - name: [96, CType::Pointer.new { CType::Immediate.new(2) }], - compiler: [128, CType::Immediate.new(1)], - ) - end - - def C.rb_call_data - @rb_call_data ||= CType::Struct.new( - "rb_call_data", 8, - ci: [0, CType::Pointer.new { self.rb_callinfo }], - cc: [32, CType::Pointer.new { self.rb_callcache }], - ) - end - - def C.rb_callable_method_entry_struct - @rb_callable_method_entry_struct ||= CType::Struct.new( - "rb_callable_method_entry_struct", 20, - flags: [0, self.VALUE], - defined_class: [32, self.VALUE], - def: [64, CType::Pointer.new { self.rb_method_definition_struct }], - called_id: [96, self.ID], - owner: [128, self.VALUE], - ) - end - - def C.rb_callcache - @rb_callcache ||= CType::Struct.new( - "rb_callcache", 20, - flags: [0, self.VALUE], - klass: [32, self.VALUE], - cme_: [64, CType::Pointer.new { self.rb_callable_method_entry_struct }], - call_: [96, self.vm_call_handler], - aux_: [128, CType::Union.new( - "", 4, - attr_index: CType::Immediate.new(-4), - method_missing_reason: self.method_missing_reason, - v: self.VALUE, - )], - ) - end - - def C.rb_callinfo - @rb_callinfo ||= CType::Struct.new( - "rb_callinfo", 20, - flags: [0, self.VALUE], - kwarg: [32, CType::Pointer.new { self.rb_callinfo_kwarg }], - mid: [64, self.VALUE], - flag: [96, self.VALUE], - argc: [128, self.VALUE], - ) - end - - def C.rb_control_frame_t - @rb_control_frame_t ||= CType::Struct.new( - "rb_control_frame_struct", 32, - pc: [0, CType::Pointer.new { self.VALUE }], - sp: [32, CType::Pointer.new { self.VALUE }], - iseq: [64, CType::Pointer.new { self.rb_iseq_t }], - self: [96, self.VALUE], - ep: [128, CType::Pointer.new { self.VALUE }], - block_code: [160, CType::Pointer.new { CType::Immediate.new(0) }], - __bp__: [192, CType::Pointer.new { self.VALUE }], - jit_return: [224, CType::Pointer.new { CType::Immediate.new(0) }], - ) - end - - def C.rb_cref_t - @rb_cref_t ||= CType::Struct.new( - "rb_cref_struct", 20, - flags: [0, self.VALUE], - refinements: [32, self.VALUE], - klass_or_self: [64, self.VALUE], - next: [96, CType::Pointer.new { self.rb_cref_struct }], - scope_visi: [128, self.rb_scope_visibility_t], - ) - end - - def C.rb_execution_context_struct - @rb_execution_context_struct ||= CType::Struct.new( - "rb_execution_context_struct", 244, - vm_stack: [0, CType::Pointer.new { self.VALUE }], - vm_stack_size: [32, CType::Immediate.new(-4)], - cfp: [64, CType::Pointer.new { self.rb_control_frame_t }], - tag: [96, CType::Pointer.new { self.rb_vm_tag }], - interrupt_flag: [128, self.rb_atomic_t], - interrupt_mask: [160, self.rb_atomic_t], - fiber_ptr: [192, CType::Pointer.new { self.rb_fiber_t }], - thread_ptr: [224, CType::Pointer.new { self.rb_thread_struct }], - local_storage: [256, CType::Pointer.new { self.rb_id_table }], - local_storage_recursive_hash: [288, self.VALUE], - local_storage_recursive_hash_for_trace: [320, self.VALUE], - root_lep: [352, CType::Pointer.new { self.VALUE }], - root_svar: [384, self.VALUE], - ensure_list: [416, CType::Pointer.new { self.rb_ensure_list_t }], - trace_arg: [448, CType::Pointer.new { self.rb_trace_arg_struct }], - errinfo: [480, self.VALUE], - passed_block_handler: [512, self.VALUE], - raised_flag: [544, CType::Immediate.new(-2)], - method_missing_reason: [552, self.method_missing_reason], - private_const_reference: [576, self.VALUE], - machine: [608, CType::Struct.new( - "", 168, - stack_start: [0, CType::Pointer.new { self.VALUE }], - stack_end: [32, CType::Pointer.new { self.VALUE }], - stack_maxsize: [64, CType::Immediate.new(-4)], - regs: [96, self.jmp_buf], - )], - ) - end - - def C.rb_execution_context_t - @rb_execution_context_t ||= self.rb_execution_context_struct - end - - def C.rb_iseq_constant_body - @rb_iseq_constant_body ||= CType::Struct.new( - "rb_iseq_constant_body", 204, - type: [0, self.rb_iseq_type], - iseq_size: [32, CType::Immediate.new(-4)], - iseq_encoded: [64, CType::Pointer.new { self.VALUE }], - param: [96, CType::Struct.new( - "", 40, - flags: [0, CType::Struct.new( - "", 4, - has_lead: [0, CType::BitField.new(1, 0)], - has_opt: [1, CType::BitField.new(1, 1)], - has_rest: [2, CType::BitField.new(1, 2)], - has_post: [3, CType::BitField.new(1, 3)], - has_kw: [4, CType::BitField.new(1, 4)], - has_kwrest: [5, CType::BitField.new(1, 5)], - has_block: [6, CType::BitField.new(1, 6)], - ambiguous_param0: [7, CType::BitField.new(1, 7)], - accepts_no_kwarg: [8, CType::BitField.new(1, 0)], - ruby2_keywords: [9, CType::BitField.new(1, 1)], - )], - size: [32, CType::Immediate.new(-4)], - lead_num: [64, CType::Immediate.new(4)], - opt_num: [96, CType::Immediate.new(4)], - rest_start: [128, CType::Immediate.new(4)], - post_start: [160, CType::Immediate.new(4)], - post_num: [192, CType::Immediate.new(4)], - block_start: [224, CType::Immediate.new(4)], - opt_table: [256, CType::Pointer.new { self.VALUE }], - keyword: [288, CType::Pointer.new { self.rb_iseq_param_keyword }], - )], - location: [416, self.rb_iseq_location_t], - insns_info: [704, self.iseq_insn_info], - local_table: [832, CType::Pointer.new { self.ID }], - catch_table: [864, CType::Pointer.new { self.iseq_catch_table }], - parent_iseq: [896, CType::Pointer.new { self.rb_iseq_struct }], - local_iseq: [928, CType::Pointer.new { self.rb_iseq_struct }], - is_entries: [960, CType::Pointer.new { self.iseq_inline_storage_entry }], - call_data: [992, CType::Pointer.new { self.rb_call_data }], - variable: [1024, CType::Struct.new( - "", 20, - flip_count: [0, self.rb_snum_t], - script_lines: [32, self.VALUE], - coverage: [64, self.VALUE], - pc2branchindex: [96, self.VALUE], - original_iseq: [128, CType::Pointer.new { self.VALUE }], - )], - local_table_size: [1184, CType::Immediate.new(-4)], - ic_size: [1216, CType::Immediate.new(-4)], - ise_size: [1248, CType::Immediate.new(-4)], - ivc_size: [1280, CType::Immediate.new(-4)], - icvarc_size: [1312, CType::Immediate.new(-4)], - ci_size: [1344, CType::Immediate.new(-4)], - stack_max: [1376, CType::Immediate.new(-4)], - mark_bits: [1408, CType::Union.new( - "", 4, - list: CType::Pointer.new { self.iseq_bits_t }, - single: self.iseq_bits_t, - )], - catch_except_p: [1440, self._Bool], - builtin_inline_p: [1448, self._Bool], - outer_variables: [1472, CType::Pointer.new { self.rb_id_table }], - mandatory_only_iseq: [1504, CType::Pointer.new { self.rb_iseq_t }], - jit_func: [1536, CType::Immediate.new(1)], - total_calls: [1568, CType::Immediate.new(-5)], - jit_unit: [1600, CType::Pointer.new { self.rb_mjit_unit }], - ) - end - - def C.rb_iseq_location_t - @rb_iseq_location_t ||= CType::Struct.new( - "rb_iseq_location_struct", 36, - pathobj: [0, self.VALUE, true], - base_label: [32, self.VALUE, true], - label: [64, self.VALUE, true], - first_lineno: [96, self.VALUE, true], - node_id: [128, CType::Immediate.new(4)], - code_location: [160, self.rb_code_location_t], - ) - end - - def C.rb_iseq_struct - @rb_iseq_struct ||= CType::Struct.new( - "rb_iseq_struct", 20, - flags: [0, self.VALUE], - wrapper: [32, self.VALUE], - body: [64, CType::Pointer.new { self.rb_iseq_constant_body }], - aux: [96, CType::Union.new( - "", 8, - compile_data: CType::Pointer.new { self.iseq_compile_data }, - loader: CType::Struct.new( - "", 8, - obj: [0, self.VALUE], - index: [32, CType::Immediate.new(4)], - ), - exec: CType::Struct.new( - "", 8, - local_hooks: [0, CType::Pointer.new { self.rb_hook_list_struct }], - global_trace_events: [32, self.rb_event_flag_t], - ), - )], - ) - end - - def C.rb_iseq_t - @rb_iseq_t ||= self.rb_iseq_struct - end - - def C.rb_iv_index_tbl_entry - @rb_iv_index_tbl_entry ||= CType::Struct.new( - "rb_iv_index_tbl_entry", 16, - index: [0, CType::Immediate.new(-4)], - class_serial: [32, self.rb_serial_t], - class_value: [96, self.VALUE], - ) - end - - def C.rb_method_definition_struct - @rb_method_definition_struct ||= CType::Struct.new( - "rb_method_definition_struct", 28, - type: [0, self.rb_method_type_t], - iseq_overload: [4, CType::BitField.new(1, 4)], - alias_count: [5, CType::BitField.new(27, 5)], - complemented_count: [32, CType::BitField.new(28, 0)], - no_redef_warning: [60, CType::BitField.new(1, 4)], - body: [64, CType::Union.new( - "", 12, - iseq: self.rb_method_iseq_t, - cfunc: self.rb_method_cfunc_t, - attr: self.rb_method_attr_t, - alias: self.rb_method_alias_t, - refined: self.rb_method_refined_t, - bmethod: self.rb_method_bmethod_t, - optimized: self.rb_method_optimized_t, - )], - original_id: [160, self.ID], - method_serial: [192, CType::Immediate.new(-4)], - ) - end - - def C.rb_method_iseq_t - @rb_method_iseq_t ||= CType::Struct.new( - "rb_method_iseq_struct", 8, - iseqptr: [0, CType::Pointer.new { self.rb_iseq_t }], - cref: [32, CType::Pointer.new { self.rb_cref_t }], - ) - end - - def C.rb_method_type_t - @rb_method_type_t ||= CType::Immediate.new(4) - end - - def C.rb_mjit_compile_info - @rb_mjit_compile_info ||= CType::Struct.new( - "rb_mjit_compile_info", 5, - disable_ivar_cache: [0, self._Bool], - disable_exivar_cache: [8, self._Bool], - disable_send_cache: [16, self._Bool], - disable_inlining: [24, self._Bool], - disable_const_cache: [32, self._Bool], - ) - end - - def C.rb_mjit_unit - @rb_mjit_unit ||= CType::Struct.new( - "rb_mjit_unit", 36, - unode: [0, self.ccan_list_node], - id: [64, CType::Immediate.new(4)], - handle: [96, CType::Pointer.new { CType::Immediate.new(0) }], - iseq: [128, CType::Pointer.new { self.rb_iseq_t }], - used_code_p: [160, self._Bool], - compact_p: [168, self._Bool], - compile_info: [176, self.rb_mjit_compile_info], - cc_entries: [224, CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], - cc_entries_size: [256, CType::Immediate.new(-4)], - ) - end - - def C.rb_serial_t - @rb_serial_t ||= CType::Immediate.new(-6) - end - - def C._Bool = CType::Bool.new - - def C.ID = CType::Stub.new(:ID) - - def C.rb_thread_struct = CType::Stub.new(:rb_thread_struct) - - def C.vm_call_handler = CType::Stub.new(:vm_call_handler) - - def C.method_missing_reason = CType::Stub.new(:method_missing_reason) - - def C.rb_callinfo_kwarg = CType::Stub.new(:rb_callinfo_kwarg) - - def C.rb_cref_struct = CType::Stub.new(:rb_cref_struct) - - def C.rb_scope_visibility_t = CType::Stub.new(:rb_scope_visibility_t) - - def C.rb_vm_tag = CType::Stub.new(:rb_vm_tag) - - def C.rb_atomic_t = CType::Stub.new(:rb_atomic_t) - - def C.rb_fiber_t = CType::Stub.new(:rb_fiber_t) - - def C.rb_id_table = CType::Stub.new(:rb_id_table) - - def C.rb_ensure_list_t = CType::Stub.new(:rb_ensure_list_t) - - def C.rb_trace_arg_struct = CType::Stub.new(:rb_trace_arg_struct) - - def C.jmp_buf = CType::Stub.new(:jmp_buf) - - def C.rb_iseq_type = CType::Stub.new(:rb_iseq_type) - - def C.rb_iseq_param_keyword = CType::Stub.new(:rb_iseq_param_keyword) - - def C.iseq_insn_info = CType::Stub.new(:iseq_insn_info) - - def C.iseq_catch_table = CType::Stub.new(:iseq_catch_table) - - def C.rb_snum_t = CType::Stub.new(:rb_snum_t) - - def C.iseq_bits_t = CType::Stub.new(:iseq_bits_t) - - def C.rb_code_location_t = CType::Stub.new(:rb_code_location_t) - - def C.iseq_compile_data = CType::Stub.new(:iseq_compile_data) - - def C.rb_hook_list_struct = CType::Stub.new(:rb_hook_list_struct) - - def C.rb_event_flag_t = CType::Stub.new(:rb_event_flag_t) - - def C.rb_method_cfunc_t = CType::Stub.new(:rb_method_cfunc_t) - - def C.rb_method_attr_t = CType::Stub.new(:rb_method_attr_t) - - def C.rb_method_alias_t = CType::Stub.new(:rb_method_alias_t) - - def C.rb_method_refined_t = CType::Stub.new(:rb_method_refined_t) - - def C.rb_method_bmethod_t = CType::Stub.new(:rb_method_bmethod_t) - - def C.rb_method_optimized_t = CType::Stub.new(:rb_method_optimized_t) - - def C.ccan_list_node = CType::Stub.new(:ccan_list_node) -end diff --git a/lib/mjit/c_64.rb b/lib/mjit/c_64.rb deleted file mode 100644 index 9ea32bd327fd8b..00000000000000 --- a/lib/mjit/c_64.rb +++ /dev/null @@ -1,482 +0,0 @@ -require_relative 'c_type' - -module RubyVM::MJIT - def C.CALL_DATA - @CALL_DATA ||= self.rb_call_data - end - - def C.IC - @IC ||= self.iseq_inline_constant_cache - end - - def C.IVC - @IVC ||= self.iseq_inline_iv_cache_entry - end - - def C.RB_BUILTIN - @RB_BUILTIN ||= self.rb_builtin_function - end - - def C.VALUE - @VALUE ||= CType::Immediate.new(-5) - end - - def C.compile_branch - @compile_branch ||= CType::Struct.new( - "compile_branch", 8, - stack_size: [0, CType::Immediate.new(-4)], - finish_p: [32, self._Bool], - ) - end - - def C.compile_status - @compile_status ||= CType::Struct.new( - "compile_status", 120, - success: [0, self._Bool], - stack_size_for_pos: [64, CType::Pointer.new { CType::Immediate.new(4) }], - local_stack_p: [128, self._Bool], - is_entries: [192, CType::Pointer.new { self.iseq_inline_storage_entry }], - cc_entries_index: [256, CType::Immediate.new(4)], - compiled_iseq: [320, CType::Pointer.new { self.rb_iseq_constant_body }], - compiled_id: [384, CType::Immediate.new(4)], - compile_info: [448, CType::Pointer.new { self.rb_mjit_compile_info }], - merge_ivar_guards_p: [512, self._Bool], - ivar_serial: [576, self.rb_serial_t], - max_ivar_index: [640, CType::Immediate.new(-5)], - inlined_iseqs: [704, CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], - inline_context: [768, self.inlined_call_context], - ) - end - - def C.inlined_call_context - @inlined_call_context ||= CType::Struct.new( - "inlined_call_context", 24, - orig_argc: [0, CType::Immediate.new(4)], - me: [64, self.VALUE], - param_size: [128, CType::Immediate.new(4)], - local_size: [160, CType::Immediate.new(4)], - ) - end - - def C.iseq_inline_constant_cache - @iseq_inline_constant_cache ||= CType::Struct.new( - "iseq_inline_constant_cache", 16, - entry: [0, CType::Pointer.new { self.iseq_inline_constant_cache_entry }], - segments: [64, CType::Pointer.new { self.ID }], - ) - end - - def C.iseq_inline_constant_cache_entry - @iseq_inline_constant_cache_entry ||= CType::Struct.new( - "iseq_inline_constant_cache_entry", 40, - flags: [0, self.VALUE], - value: [64, self.VALUE], - _unused1: [128, self.VALUE], - _unused2: [192, self.VALUE], - ic_cref: [256, CType::Pointer.new { self.rb_cref_t }], - ) - end - - def C.iseq_inline_iv_cache_entry - @iseq_inline_iv_cache_entry ||= CType::Struct.new( - "iseq_inline_iv_cache_entry", 8, - entry: [0, CType::Pointer.new { self.rb_iv_index_tbl_entry }], - ) - end - - def C.iseq_inline_storage_entry - @iseq_inline_storage_entry ||= CType::Union.new( - "iseq_inline_storage_entry", 16, - once: CType::Struct.new( - "", 16, - running_thread: [0, CType::Pointer.new { self.rb_thread_struct }], - value: [64, self.VALUE], - ), - ic_cache: self.iseq_inline_constant_cache, - iv_cache: self.iseq_inline_iv_cache_entry, - ) - end - - def C.mjit_options - @mjit_options ||= CType::Struct.new( - "mjit_options", 40, - on: [0, self._Bool], - save_temps: [8, self._Bool], - warnings: [16, self._Bool], - debug: [24, self._Bool], - debug_flags: [64, CType::Pointer.new { CType::Immediate.new(2) }], - wait: [128, self._Bool], - min_calls: [160, CType::Immediate.new(-4)], - verbose: [192, CType::Immediate.new(4)], - max_cache_size: [224, CType::Immediate.new(4)], - pause: [256, self._Bool], - custom: [264, self._Bool], - ) - end - - def C.rb_builtin_function - @rb_builtin_function ||= CType::Struct.new( - "rb_builtin_function", 32, - func_ptr: [0, CType::Pointer.new { CType::Immediate.new(0) }], - argc: [64, CType::Immediate.new(4)], - index: [96, CType::Immediate.new(4)], - name: [128, CType::Pointer.new { CType::Immediate.new(2) }], - compiler: [192, CType::Immediate.new(1)], - ) - end - - def C.rb_call_data - @rb_call_data ||= CType::Struct.new( - "rb_call_data", 16, - ci: [0, CType::Pointer.new { self.rb_callinfo }], - cc: [64, CType::Pointer.new { self.rb_callcache }], - ) - end - - def C.rb_callable_method_entry_struct - @rb_callable_method_entry_struct ||= CType::Struct.new( - "rb_callable_method_entry_struct", 40, - flags: [0, self.VALUE], - defined_class: [64, self.VALUE], - def: [128, CType::Pointer.new { self.rb_method_definition_struct }], - called_id: [192, self.ID], - owner: [256, self.VALUE], - ) - end - - def C.rb_callcache - @rb_callcache ||= CType::Struct.new( - "rb_callcache", 40, - flags: [0, self.VALUE], - klass: [64, self.VALUE], - cme_: [128, CType::Pointer.new { self.rb_callable_method_entry_struct }], - call_: [192, self.vm_call_handler], - aux_: [256, CType::Union.new( - "", 8, - attr_index: CType::Immediate.new(-4), - method_missing_reason: self.method_missing_reason, - v: self.VALUE, - )], - ) - end - - def C.rb_callinfo - @rb_callinfo ||= CType::Struct.new( - "rb_callinfo", 40, - flags: [0, self.VALUE], - kwarg: [64, CType::Pointer.new { self.rb_callinfo_kwarg }], - mid: [128, self.VALUE], - flag: [192, self.VALUE], - argc: [256, self.VALUE], - ) - end - - def C.rb_control_frame_t - @rb_control_frame_t ||= CType::Struct.new( - "rb_control_frame_struct", 64, - pc: [0, CType::Pointer.new { self.VALUE }], - sp: [64, CType::Pointer.new { self.VALUE }], - iseq: [128, CType::Pointer.new { self.rb_iseq_t }], - self: [192, self.VALUE], - ep: [256, CType::Pointer.new { self.VALUE }], - block_code: [320, CType::Pointer.new { CType::Immediate.new(0) }], - __bp__: [384, CType::Pointer.new { self.VALUE }], - jit_return: [448, CType::Pointer.new { CType::Immediate.new(0) }], - ) - end - - def C.rb_cref_t - @rb_cref_t ||= CType::Struct.new( - "rb_cref_struct", 40, - flags: [0, self.VALUE], - refinements: [64, self.VALUE], - klass_or_self: [128, self.VALUE], - next: [192, CType::Pointer.new { self.rb_cref_struct }], - scope_visi: [256, self.rb_scope_visibility_t], - ) - end - - def C.rb_execution_context_struct - @rb_execution_context_struct ||= CType::Struct.new( - "rb_execution_context_struct", 368, - vm_stack: [0, CType::Pointer.new { self.VALUE }], - vm_stack_size: [64, CType::Immediate.new(-5)], - cfp: [128, CType::Pointer.new { self.rb_control_frame_t }], - tag: [192, CType::Pointer.new { self.rb_vm_tag }], - interrupt_flag: [256, self.rb_atomic_t], - interrupt_mask: [288, self.rb_atomic_t], - fiber_ptr: [320, CType::Pointer.new { self.rb_fiber_t }], - thread_ptr: [384, CType::Pointer.new { self.rb_thread_struct }], - local_storage: [448, CType::Pointer.new { self.rb_id_table }], - local_storage_recursive_hash: [512, self.VALUE], - local_storage_recursive_hash_for_trace: [576, self.VALUE], - root_lep: [640, CType::Pointer.new { self.VALUE }], - root_svar: [704, self.VALUE], - ensure_list: [768, CType::Pointer.new { self.rb_ensure_list_t }], - trace_arg: [832, CType::Pointer.new { self.rb_trace_arg_struct }], - errinfo: [896, self.VALUE], - passed_block_handler: [960, self.VALUE], - raised_flag: [1024, CType::Immediate.new(-2)], - method_missing_reason: [1032, self.method_missing_reason], - private_const_reference: [1088, self.VALUE], - machine: [1152, CType::Struct.new( - "", 224, - stack_start: [0, CType::Pointer.new { self.VALUE }], - stack_end: [64, CType::Pointer.new { self.VALUE }], - stack_maxsize: [128, CType::Immediate.new(-5)], - regs: [192, self.jmp_buf], - )], - ) - end - - def C.rb_execution_context_t - @rb_execution_context_t ||= self.rb_execution_context_struct - end - - def C.rb_iseq_constant_body - @rb_iseq_constant_body ||= CType::Struct.new( - "rb_iseq_constant_body", 336, - type: [0, self.rb_iseq_type], - iseq_size: [32, CType::Immediate.new(-4)], - iseq_encoded: [64, CType::Pointer.new { self.VALUE }], - param: [128, CType::Struct.new( - "", 48, - flags: [0, CType::Struct.new( - "", 4, - has_lead: [0, CType::BitField.new(1, 0)], - has_opt: [1, CType::BitField.new(1, 1)], - has_rest: [2, CType::BitField.new(1, 2)], - has_post: [3, CType::BitField.new(1, 3)], - has_kw: [4, CType::BitField.new(1, 4)], - has_kwrest: [5, CType::BitField.new(1, 5)], - has_block: [6, CType::BitField.new(1, 6)], - ambiguous_param0: [7, CType::BitField.new(1, 7)], - accepts_no_kwarg: [8, CType::BitField.new(1, 0)], - ruby2_keywords: [9, CType::BitField.new(1, 1)], - )], - size: [32, CType::Immediate.new(-4)], - lead_num: [64, CType::Immediate.new(4)], - opt_num: [96, CType::Immediate.new(4)], - rest_start: [128, CType::Immediate.new(4)], - post_start: [160, CType::Immediate.new(4)], - post_num: [192, CType::Immediate.new(4)], - block_start: [224, CType::Immediate.new(4)], - opt_table: [256, CType::Pointer.new { self.VALUE }], - keyword: [320, CType::Pointer.new { self.rb_iseq_param_keyword }], - )], - location: [512, self.rb_iseq_location_t], - insns_info: [960, self.iseq_insn_info], - local_table: [1216, CType::Pointer.new { self.ID }], - catch_table: [1280, CType::Pointer.new { self.iseq_catch_table }], - parent_iseq: [1344, CType::Pointer.new { self.rb_iseq_struct }], - local_iseq: [1408, CType::Pointer.new { self.rb_iseq_struct }], - is_entries: [1472, CType::Pointer.new { self.iseq_inline_storage_entry }], - call_data: [1536, CType::Pointer.new { self.rb_call_data }], - variable: [1600, CType::Struct.new( - "", 40, - flip_count: [0, self.rb_snum_t], - script_lines: [64, self.VALUE], - coverage: [128, self.VALUE], - pc2branchindex: [192, self.VALUE], - original_iseq: [256, CType::Pointer.new { self.VALUE }], - )], - local_table_size: [1920, CType::Immediate.new(-4)], - ic_size: [1952, CType::Immediate.new(-4)], - ise_size: [1984, CType::Immediate.new(-4)], - ivc_size: [2016, CType::Immediate.new(-4)], - icvarc_size: [2048, CType::Immediate.new(-4)], - ci_size: [2080, CType::Immediate.new(-4)], - stack_max: [2112, CType::Immediate.new(-4)], - mark_bits: [2176, CType::Union.new( - "", 8, - list: CType::Pointer.new { self.iseq_bits_t }, - single: self.iseq_bits_t, - )], - catch_except_p: [2240, self._Bool], - builtin_inline_p: [2248, self._Bool], - outer_variables: [2304, CType::Pointer.new { self.rb_id_table }], - mandatory_only_iseq: [2368, CType::Pointer.new { self.rb_iseq_t }], - jit_func: [2432, CType::Immediate.new(1)], - total_calls: [2496, CType::Immediate.new(-5)], - jit_unit: [2560, CType::Pointer.new { self.rb_mjit_unit }], - yjit_payload: [2624, CType::Pointer.new { CType::Immediate.new(0) }], - ) - end - - def C.rb_iseq_location_t - @rb_iseq_location_t ||= CType::Struct.new( - "rb_iseq_location_struct", 56, - pathobj: [0, self.VALUE, true], - base_label: [64, self.VALUE, true], - label: [128, self.VALUE, true], - first_lineno: [192, self.VALUE, true], - node_id: [256, CType::Immediate.new(4)], - code_location: [288, self.rb_code_location_t], - ) - end - - def C.rb_iseq_struct - @rb_iseq_struct ||= CType::Struct.new( - "rb_iseq_struct", 40, - flags: [0, self.VALUE], - wrapper: [64, self.VALUE], - body: [128, CType::Pointer.new { self.rb_iseq_constant_body }], - aux: [192, CType::Union.new( - "", 16, - compile_data: CType::Pointer.new { self.iseq_compile_data }, - loader: CType::Struct.new( - "", 16, - obj: [0, self.VALUE], - index: [64, CType::Immediate.new(4)], - ), - exec: CType::Struct.new( - "", 16, - local_hooks: [0, CType::Pointer.new { self.rb_hook_list_struct }], - global_trace_events: [64, self.rb_event_flag_t], - ), - )], - ) - end - - def C.rb_iseq_t - @rb_iseq_t ||= self.rb_iseq_struct - end - - def C.rb_iv_index_tbl_entry - @rb_iv_index_tbl_entry ||= CType::Struct.new( - "rb_iv_index_tbl_entry", 24, - index: [0, CType::Immediate.new(-4)], - class_serial: [64, self.rb_serial_t], - class_value: [128, self.VALUE], - ) - end - - def C.rb_method_definition_struct - @rb_method_definition_struct ||= CType::Struct.new( - "rb_method_definition_struct", 48, - type: [0, self.rb_method_type_t], - iseq_overload: [4, CType::BitField.new(1, 4)], - alias_count: [5, CType::BitField.new(27, 5)], - complemented_count: [32, CType::BitField.new(28, 0)], - no_redef_warning: [60, CType::BitField.new(1, 4)], - body: [64, CType::Union.new( - "", 24, - iseq: self.rb_method_iseq_t, - cfunc: self.rb_method_cfunc_t, - attr: self.rb_method_attr_t, - alias: self.rb_method_alias_t, - refined: self.rb_method_refined_t, - bmethod: self.rb_method_bmethod_t, - optimized: self.rb_method_optimized_t, - )], - original_id: [256, self.ID], - method_serial: [320, CType::Immediate.new(-5)], - ) - end - - def C.rb_method_iseq_t - @rb_method_iseq_t ||= CType::Struct.new( - "rb_method_iseq_struct", 16, - iseqptr: [0, CType::Pointer.new { self.rb_iseq_t }], - cref: [64, CType::Pointer.new { self.rb_cref_t }], - ) - end - - def C.rb_method_type_t - @rb_method_type_t ||= CType::Immediate.new(4) - end - - def C.rb_mjit_compile_info - @rb_mjit_compile_info ||= CType::Struct.new( - "rb_mjit_compile_info", 5, - disable_ivar_cache: [0, self._Bool], - disable_exivar_cache: [8, self._Bool], - disable_send_cache: [16, self._Bool], - disable_inlining: [24, self._Bool], - disable_const_cache: [32, self._Bool], - ) - end - - def C.rb_mjit_unit - @rb_mjit_unit ||= CType::Struct.new( - "rb_mjit_unit", 64, - unode: [0, self.ccan_list_node], - id: [128, CType::Immediate.new(4)], - handle: [192, CType::Pointer.new { CType::Immediate.new(0) }], - iseq: [256, CType::Pointer.new { self.rb_iseq_t }], - used_code_p: [320, self._Bool], - compact_p: [328, self._Bool], - compile_info: [336, self.rb_mjit_compile_info], - cc_entries: [384, CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], - cc_entries_size: [448, CType::Immediate.new(-4)], - ) - end - - def C.rb_serial_t - @rb_serial_t ||= CType::Immediate.new(-6) - end - - def C._Bool = CType::Bool.new - - def C.ID = CType::Stub.new(:ID) - - def C.rb_thread_struct = CType::Stub.new(:rb_thread_struct) - - def C.vm_call_handler = CType::Stub.new(:vm_call_handler) - - def C.method_missing_reason = CType::Stub.new(:method_missing_reason) - - def C.rb_callinfo_kwarg = CType::Stub.new(:rb_callinfo_kwarg) - - def C.rb_cref_struct = CType::Stub.new(:rb_cref_struct) - - def C.rb_scope_visibility_t = CType::Stub.new(:rb_scope_visibility_t) - - def C.rb_vm_tag = CType::Stub.new(:rb_vm_tag) - - def C.rb_atomic_t = CType::Stub.new(:rb_atomic_t) - - def C.rb_fiber_t = CType::Stub.new(:rb_fiber_t) - - def C.rb_id_table = CType::Stub.new(:rb_id_table) - - def C.rb_ensure_list_t = CType::Stub.new(:rb_ensure_list_t) - - def C.rb_trace_arg_struct = CType::Stub.new(:rb_trace_arg_struct) - - def C.jmp_buf = CType::Stub.new(:jmp_buf) - - def C.rb_iseq_type = CType::Stub.new(:rb_iseq_type) - - def C.rb_iseq_param_keyword = CType::Stub.new(:rb_iseq_param_keyword) - - def C.iseq_insn_info = CType::Stub.new(:iseq_insn_info) - - def C.iseq_catch_table = CType::Stub.new(:iseq_catch_table) - - def C.rb_snum_t = CType::Stub.new(:rb_snum_t) - - def C.iseq_bits_t = CType::Stub.new(:iseq_bits_t) - - def C.rb_code_location_t = CType::Stub.new(:rb_code_location_t) - - def C.iseq_compile_data = CType::Stub.new(:iseq_compile_data) - - def C.rb_hook_list_struct = CType::Stub.new(:rb_hook_list_struct) - - def C.rb_event_flag_t = CType::Stub.new(:rb_event_flag_t) - - def C.rb_method_cfunc_t = CType::Stub.new(:rb_method_cfunc_t) - - def C.rb_method_attr_t = CType::Stub.new(:rb_method_attr_t) - - def C.rb_method_alias_t = CType::Stub.new(:rb_method_alias_t) - - def C.rb_method_refined_t = CType::Stub.new(:rb_method_refined_t) - - def C.rb_method_bmethod_t = CType::Stub.new(:rb_method_bmethod_t) - - def C.rb_method_optimized_t = CType::Stub.new(:rb_method_optimized_t) - - def C.ccan_list_node = CType::Stub.new(:ccan_list_node) -end diff --git a/mjit_c.rb b/mjit_c.rb index 452b534bd3cdbd..ec014ae7d0cb5e 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -159,5 +159,544 @@ def C.VM_METHOD_TYPE_ISEQ Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) } end + def C.CALL_DATA + @CALL_DATA ||= self.rb_call_data + end + + def C.IC + @IC ||= self.iseq_inline_constant_cache + end + + def C.IVC + @IVC ||= self.iseq_inline_iv_cache_entry + end + + def C.RB_BUILTIN + @RB_BUILTIN ||= self.rb_builtin_function + end + + def C.VALUE + @VALUE ||= CType::Immediate.new(-5) + end + + def C.compile_branch + @compile_branch ||= CType::Struct.new( + "compile_branch", 8, + stack_size: [0, CType::Immediate.new(-4)], + finish_p: [32, self._Bool], + ) + end + + def C.compile_status + @compile_status ||= CType::Struct.new( + "compile_status", 120, + success: [0, self._Bool], + stack_size_for_pos: [64, CType::Pointer.new { CType::Immediate.new(4) }], + local_stack_p: [128, self._Bool], + is_entries: [192, CType::Pointer.new { self.iseq_inline_storage_entry }], + cc_entries_index: [256, CType::Immediate.new(4)], + compiled_iseq: [320, CType::Pointer.new { self.rb_iseq_constant_body }], + compiled_id: [384, CType::Immediate.new(4)], + compile_info: [448, CType::Pointer.new { self.rb_mjit_compile_info }], + merge_ivar_guards_p: [512, self._Bool], + ivar_serial: [576, self.rb_serial_t], + max_ivar_index: [640, CType::Immediate.new(-5)], + inlined_iseqs: [704, CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], + inline_context: [768, self.inlined_call_context], + ) + end + + def C.inlined_call_context + @inlined_call_context ||= CType::Struct.new( + "inlined_call_context", 24, + orig_argc: [0, CType::Immediate.new(4)], + me: [64, self.VALUE], + param_size: [128, CType::Immediate.new(4)], + local_size: [160, CType::Immediate.new(4)], + ) + end + + def C.iseq_inline_constant_cache + @iseq_inline_constant_cache ||= CType::Struct.new( + "iseq_inline_constant_cache", 16, + entry: [0, CType::Pointer.new { self.iseq_inline_constant_cache_entry }], + segments: [64, CType::Pointer.new { self.ID }], + ) + end + + def C.iseq_inline_constant_cache_entry + @iseq_inline_constant_cache_entry ||= CType::Struct.new( + "iseq_inline_constant_cache_entry", 40, + flags: [0, self.VALUE], + value: [64, self.VALUE], + _unused1: [128, self.VALUE], + _unused2: [192, self.VALUE], + ic_cref: [256, CType::Pointer.new { self.rb_cref_t }], + ) + end + + def C.iseq_inline_iv_cache_entry + @iseq_inline_iv_cache_entry ||= CType::Struct.new( + "iseq_inline_iv_cache_entry", 8, + entry: [0, CType::Pointer.new { self.rb_iv_index_tbl_entry }], + ) + end + + def C.iseq_inline_storage_entry + @iseq_inline_storage_entry ||= CType::Union.new( + "iseq_inline_storage_entry", 16, + once: CType::Struct.new( + "", 16, + running_thread: [0, CType::Pointer.new { self.rb_thread_struct }], + value: [64, self.VALUE], + ), + ic_cache: self.iseq_inline_constant_cache, + iv_cache: self.iseq_inline_iv_cache_entry, + ) + end + + def C.mjit_options + @mjit_options ||= CType::Struct.new( + "mjit_options", 40, + on: [0, self._Bool], + save_temps: [8, self._Bool], + warnings: [16, self._Bool], + debug: [24, self._Bool], + debug_flags: [64, CType::Pointer.new { CType::Immediate.new(2) }], + wait: [128, self._Bool], + min_calls: [160, CType::Immediate.new(-4)], + verbose: [192, CType::Immediate.new(4)], + max_cache_size: [224, CType::Immediate.new(4)], + pause: [256, self._Bool], + custom: [264, self._Bool], + ) + end + + def C.rb_builtin_function + @rb_builtin_function ||= CType::Struct.new( + "rb_builtin_function", 32, + func_ptr: [0, CType::Pointer.new { CType::Immediate.new(0) }], + argc: [64, CType::Immediate.new(4)], + index: [96, CType::Immediate.new(4)], + name: [128, CType::Pointer.new { CType::Immediate.new(2) }], + compiler: [192, CType::Immediate.new(1)], + ) + end + + def C.rb_call_data + @rb_call_data ||= CType::Struct.new( + "rb_call_data", 16, + ci: [0, CType::Pointer.new { self.rb_callinfo }], + cc: [64, CType::Pointer.new { self.rb_callcache }], + ) + end + + def C.rb_callable_method_entry_struct + @rb_callable_method_entry_struct ||= CType::Struct.new( + "rb_callable_method_entry_struct", 40, + flags: [0, self.VALUE], + defined_class: [64, self.VALUE], + def: [128, CType::Pointer.new { self.rb_method_definition_struct }], + called_id: [192, self.ID], + owner: [256, self.VALUE], + ) + end + + def C.rb_callcache + @rb_callcache ||= CType::Struct.new( + "rb_callcache", 40, + flags: [0, self.VALUE], + klass: [64, self.VALUE], + cme_: [128, CType::Pointer.new { self.rb_callable_method_entry_struct }], + call_: [192, self.vm_call_handler], + aux_: [256, CType::Union.new( + "", 8, + attr_index: CType::Immediate.new(-4), + method_missing_reason: self.method_missing_reason, + v: self.VALUE, + )], + ) + end + + def C.rb_callinfo + @rb_callinfo ||= CType::Struct.new( + "rb_callinfo", 40, + flags: [0, self.VALUE], + kwarg: [64, CType::Pointer.new { self.rb_callinfo_kwarg }], + mid: [128, self.VALUE], + flag: [192, self.VALUE], + argc: [256, self.VALUE], + ) + end + + def C.rb_control_frame_t + @rb_control_frame_t ||= CType::Struct.new( + "rb_control_frame_struct", 64, + pc: [0, CType::Pointer.new { self.VALUE }], + sp: [64, CType::Pointer.new { self.VALUE }], + iseq: [128, CType::Pointer.new { self.rb_iseq_t }], + self: [192, self.VALUE], + ep: [256, CType::Pointer.new { self.VALUE }], + block_code: [320, CType::Pointer.new { CType::Immediate.new(0) }], + __bp__: [384, CType::Pointer.new { self.VALUE }], + jit_return: [448, CType::Pointer.new { CType::Immediate.new(0) }], + ) + end + + def C.rb_cref_t + @rb_cref_t ||= CType::Struct.new( + "rb_cref_struct", 40, + flags: [0, self.VALUE], + refinements: [64, self.VALUE], + klass_or_self: [128, self.VALUE], + next: [192, CType::Pointer.new { self.rb_cref_struct }], + scope_visi: [256, self.rb_scope_visibility_t], + ) + end + + def C.rb_execution_context_struct + @rb_execution_context_struct ||= CType::Struct.new( + "rb_execution_context_struct", 1, + vm_stack: [-1, CType::Pointer.new { self.VALUE }], + vm_stack_size: [-1, CType::Immediate.new(-5)], + cfp: [-1, CType::Pointer.new { self.rb_control_frame_t }], + tag: [-1, CType::Pointer.new { self.rb_vm_tag }], + interrupt_flag: [-1, self.rb_atomic_t], + interrupt_mask: [-1, self.rb_atomic_t], + fiber_ptr: [-1, CType::Pointer.new { self.rb_fiber_t }], + thread_ptr: [-1, CType::Pointer.new { self.rb_thread_struct }], + local_storage: [-1, CType::Pointer.new { self.rb_id_table }], + local_storage_recursive_hash: [-1, self.VALUE], + local_storage_recursive_hash_for_trace: [-1, self.VALUE], + root_lep: [-1, CType::Pointer.new { self.VALUE }], + root_svar: [-1, self.VALUE], + ensure_list: [-1, CType::Pointer.new { self.rb_ensure_list_t }], + trace_arg: [-1, CType::Pointer.new { self.rb_trace_arg_struct }], + errinfo: [-1, self.VALUE], + passed_block_handler: [-1, self.VALUE], + raised_flag: [-1, CType::Immediate.new(-2)], + method_missing_reason: [-1, self.method_missing_reason], + private_const_reference: [-1, self.VALUE], + machine: [-1, CType::Struct.new( + "", 1, + stack_start: [-1, CType::Pointer.new { self.VALUE }], + stack_end: [-1, CType::Pointer.new { self.VALUE }], + stack_maxsize: [-1, CType::Immediate.new(-5)], + regs: [-1, CType::Immediate.new(4)], + )], + ) + end + + def C.rb_execution_context_t + @rb_execution_context_t ||= self.rb_execution_context_struct + end + + def C.rb_iseq_constant_body + @rb_iseq_constant_body ||= CType::Struct.new( + "rb_iseq_constant_body", 336, + type: [0, self.rb_iseq_type], + iseq_size: [32, CType::Immediate.new(-4)], + iseq_encoded: [64, CType::Pointer.new { self.VALUE }], + param: [128, CType::Struct.new( + "", 48, + flags: [0, CType::Struct.new( + "", 4, + has_lead: [0, CType::BitField.new(1, 0)], + has_opt: [1, CType::BitField.new(1, 1)], + has_rest: [2, CType::BitField.new(1, 2)], + has_post: [3, CType::BitField.new(1, 3)], + has_kw: [4, CType::BitField.new(1, 4)], + has_kwrest: [5, CType::BitField.new(1, 5)], + has_block: [6, CType::BitField.new(1, 6)], + ambiguous_param0: [7, CType::BitField.new(1, 7)], + accepts_no_kwarg: [8, CType::BitField.new(1, 0)], + ruby2_keywords: [9, CType::BitField.new(1, 1)], + )], + size: [32, CType::Immediate.new(-4)], + lead_num: [64, CType::Immediate.new(4)], + opt_num: [96, CType::Immediate.new(4)], + rest_start: [128, CType::Immediate.new(4)], + post_start: [160, CType::Immediate.new(4)], + post_num: [192, CType::Immediate.new(4)], + block_start: [224, CType::Immediate.new(4)], + opt_table: [256, CType::Pointer.new { self.VALUE }], + keyword: [320, CType::Pointer.new { self.rb_iseq_param_keyword }], + )], + location: [512, self.rb_iseq_location_t], + insns_info: [960, self.iseq_insn_info], + local_table: [1216, CType::Pointer.new { self.ID }], + catch_table: [1280, CType::Pointer.new { self.iseq_catch_table }], + parent_iseq: [1344, CType::Pointer.new { self.rb_iseq_struct }], + local_iseq: [1408, CType::Pointer.new { self.rb_iseq_struct }], + is_entries: [1472, CType::Pointer.new { self.iseq_inline_storage_entry }], + call_data: [1536, CType::Pointer.new { self.rb_call_data }], + variable: [1600, CType::Struct.new( + "", 40, + flip_count: [0, self.rb_snum_t], + script_lines: [64, self.VALUE], + coverage: [128, self.VALUE], + pc2branchindex: [192, self.VALUE], + original_iseq: [256, CType::Pointer.new { self.VALUE }], + )], + local_table_size: [1920, CType::Immediate.new(-4)], + ic_size: [1952, CType::Immediate.new(-4)], + ise_size: [1984, CType::Immediate.new(-4)], + ivc_size: [2016, CType::Immediate.new(-4)], + icvarc_size: [2048, CType::Immediate.new(-4)], + ci_size: [2080, CType::Immediate.new(-4)], + stack_max: [2112, CType::Immediate.new(-4)], + mark_bits: [2176, CType::Union.new( + "", 8, + list: CType::Pointer.new { self.iseq_bits_t }, + single: self.iseq_bits_t, + )], + catch_except_p: [2240, self._Bool], + builtin_inline_p: [2248, self._Bool], + outer_variables: [2304, CType::Pointer.new { self.rb_id_table }], + mandatory_only_iseq: [2368, CType::Pointer.new { self.rb_iseq_t }], + jit_func: [2432, CType::Immediate.new(1)], + total_calls: [2496, CType::Immediate.new(-5)], + jit_unit: [2560, CType::Pointer.new { self.rb_mjit_unit }], + yjit_payload: [2624, CType::Pointer.new { CType::Immediate.new(0) }], + ) + end + + def C.rb_iseq_location_t + @rb_iseq_location_t ||= CType::Struct.new( + "rb_iseq_location_struct", 56, + pathobj: [0, self.VALUE, true], + base_label: [64, self.VALUE, true], + label: [128, self.VALUE, true], + first_lineno: [192, self.VALUE, true], + node_id: [256, CType::Immediate.new(4)], + code_location: [288, self.rb_code_location_t], + ) + end + + def C.rb_iseq_struct + @rb_iseq_struct ||= CType::Struct.new( + "rb_iseq_struct", 40, + flags: [0, self.VALUE], + wrapper: [64, self.VALUE], + body: [128, CType::Pointer.new { self.rb_iseq_constant_body }], + aux: [192, CType::Union.new( + "", 16, + compile_data: CType::Pointer.new { self.iseq_compile_data }, + loader: CType::Struct.new( + "", 16, + obj: [0, self.VALUE], + index: [64, CType::Immediate.new(4)], + ), + exec: CType::Struct.new( + "", 16, + local_hooks: [0, CType::Pointer.new { self.rb_hook_list_struct }], + global_trace_events: [64, self.rb_event_flag_t], + ), + )], + ) + end + + def C.rb_iseq_t + @rb_iseq_t ||= self.rb_iseq_struct + end + + def C.rb_iv_index_tbl_entry + @rb_iv_index_tbl_entry ||= CType::Struct.new( + "rb_iv_index_tbl_entry", 24, + index: [0, CType::Immediate.new(-4)], + class_serial: [64, self.rb_serial_t], + class_value: [128, self.VALUE], + ) + end + + def C.rb_method_definition_struct + @rb_method_definition_struct ||= CType::Struct.new( + "rb_method_definition_struct", 48, + type: [0, self.rb_method_type_t], + iseq_overload: [4, CType::BitField.new(1, 4)], + alias_count: [5, CType::BitField.new(27, 5)], + complemented_count: [32, CType::BitField.new(28, 0)], + no_redef_warning: [60, CType::BitField.new(1, 4)], + body: [64, CType::Union.new( + "", 24, + iseq: self.rb_method_iseq_t, + cfunc: self.rb_method_cfunc_t, + attr: self.rb_method_attr_t, + alias: self.rb_method_alias_t, + refined: self.rb_method_refined_t, + bmethod: self.rb_method_bmethod_t, + optimized: self.rb_method_optimized_t, + )], + original_id: [256, self.ID], + method_serial: [320, CType::Immediate.new(-5)], + ) + end + + def C.rb_method_iseq_t + @rb_method_iseq_t ||= CType::Struct.new( + "rb_method_iseq_struct", 16, + iseqptr: [0, CType::Pointer.new { self.rb_iseq_t }], + cref: [64, CType::Pointer.new { self.rb_cref_t }], + ) + end + + def C.rb_method_type_t + @rb_method_type_t ||= CType::Immediate.new(4) + end + + def C.rb_mjit_compile_info + @rb_mjit_compile_info ||= CType::Struct.new( + "rb_mjit_compile_info", 5, + disable_ivar_cache: [0, self._Bool], + disable_exivar_cache: [8, self._Bool], + disable_send_cache: [16, self._Bool], + disable_inlining: [24, self._Bool], + disable_const_cache: [32, self._Bool], + ) + end + + def C.rb_mjit_unit + @rb_mjit_unit ||= CType::Struct.new( + "rb_mjit_unit", 64, + unode: [0, self.ccan_list_node], + id: [128, CType::Immediate.new(4)], + handle: [192, CType::Pointer.new { CType::Immediate.new(0) }], + iseq: [256, CType::Pointer.new { self.rb_iseq_t }], + used_code_p: [320, self._Bool], + compact_p: [328, self._Bool], + compile_info: [336, self.rb_mjit_compile_info], + cc_entries: [384, CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], + cc_entries_size: [448, CType::Immediate.new(-4)], + ) + end + + def C.rb_serial_t + @rb_serial_t ||= CType::Immediate.new(-6) + end + + def C._Bool + CType::Bool.new + end + + def C.ID + CType::Stub.new(:ID) + end + + def C.rb_thread_struct + CType::Stub.new(:rb_thread_struct) + end + + def C.vm_call_handler + CType::Stub.new(:vm_call_handler) + end + + def C.method_missing_reason + CType::Stub.new(:method_missing_reason) + end + + def C.rb_callinfo_kwarg + CType::Stub.new(:rb_callinfo_kwarg) + end + + def C.rb_cref_struct + CType::Stub.new(:rb_cref_struct) + end + + def C.rb_scope_visibility_t + CType::Stub.new(:rb_scope_visibility_t) + end + + def C.rb_vm_tag + CType::Stub.new(:rb_vm_tag) + end + + def C.rb_atomic_t + CType::Stub.new(:rb_atomic_t) + end + + def C.rb_fiber_t + CType::Stub.new(:rb_fiber_t) + end + + def C.rb_id_table + CType::Stub.new(:rb_id_table) + end + + def C.rb_ensure_list_t + CType::Stub.new(:rb_ensure_list_t) + end + + def C.rb_trace_arg_struct + CType::Stub.new(:rb_trace_arg_struct) + end + + def C.rb_iseq_type + CType::Stub.new(:rb_iseq_type) + end + + def C.rb_iseq_param_keyword + CType::Stub.new(:rb_iseq_param_keyword) + end + + def C.iseq_insn_info + CType::Stub.new(:iseq_insn_info) + end + + def C.iseq_catch_table + CType::Stub.new(:iseq_catch_table) + end + + def C.rb_snum_t + CType::Stub.new(:rb_snum_t) + end + + def C.iseq_bits_t + CType::Stub.new(:iseq_bits_t) + end + + def C.rb_code_location_t + CType::Stub.new(:rb_code_location_t) + end + + def C.iseq_compile_data + CType::Stub.new(:iseq_compile_data) + end + + def C.rb_hook_list_struct + CType::Stub.new(:rb_hook_list_struct) + end + + def C.rb_event_flag_t + CType::Stub.new(:rb_event_flag_t) + end + + def C.rb_method_cfunc_t + CType::Stub.new(:rb_method_cfunc_t) + end + + def C.rb_method_attr_t + CType::Stub.new(:rb_method_attr_t) + end + + def C.rb_method_alias_t + CType::Stub.new(:rb_method_alias_t) + end + + def C.rb_method_refined_t + CType::Stub.new(:rb_method_refined_t) + end + + def C.rb_method_bmethod_t + CType::Stub.new(:rb_method_bmethod_t) + end + + def C.rb_method_optimized_t + CType::Stub.new(:rb_method_optimized_t) + end + + def C.ccan_list_node + CType::Stub.new(:ccan_list_node) + end + ### MJIT bindgen end ### end if RubyVM::MJIT.enabled? diff --git a/mjit_compiler.rb b/mjit_compiler.rb index 0a558f67e496fb..51dc9e774f7159 100644 --- a/mjit_compiler.rb +++ b/mjit_compiler.rb @@ -2,18 +2,13 @@ # TODO: Merge this to mjit.rb if RubyVM::MJIT.enabled? begin - require 'etc' require 'fiddle' + require 'fiddle/import' rescue LoadError return # skip miniruby end - if Fiddle::SIZEOF_VOIDP == 8 - require 'mjit/c_64' - else - require 'mjit/c_32' - end - + require "mjit/c_type" require "mjit/instruction" require "mjit/compiler" end diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index ac95bbe639aa8f..9676069b16b32c 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -126,7 +126,7 @@ def initialize(src_path:, uses:, ints:, types:, ruby_fields:) @references = Set.new end - def generate(_nodes) + def generate(nodes) println @preamble # Define USE_* macros @@ -145,18 +145,9 @@ def generate(_nodes) println end - print @postamble - end - - # TODO: Remove this - def legacy_generate(nodes) # TODO: Support nested declarations nodes_index = nodes.group_by(&:spelling).transform_values(&:last) - println "require_relative 'c_type'" - println - println "module RubyVM::MJIT" - # Define types @types.each do |type| unless definition = generate_node(nodes_index[type]) @@ -170,12 +161,13 @@ def legacy_generate(nodes) # Leave a stub for types that are referenced but not targeted (@references - @types).each do |type| - println " def C.#{type} = #{DEFAULTS[type]}" + println " def C.#{type}" + println " #{DEFAULTS[type]}" + println " end" println end - chomp - println "end" + print @postamble end private @@ -261,10 +253,11 @@ def generate_type(type) else begin ctype = Fiddle::Importer.parse_ctype(type) - "CType::Immediate.new(#{ctype})" rescue Fiddle::DLError push_target(type) "self.#{type}" + else + "CType::Immediate.new(#{ctype})" end end end From 4c6e1556b109367f598e276f72feacefc72805fc Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 20 Sep 2022 23:23:50 +0900 Subject: [PATCH 212/269] Bindgen immediate types with builtin --- lib/mjit/c_type.rb | 16 ++++++- mjit_c.rb | 111 ++++++++++++++++++++++--------------------- mjit_compiler.c | 3 ++ tool/mjit/bindgen.rb | 30 ++++++++++-- 4 files changed, 99 insertions(+), 61 deletions(-) diff --git a/lib/mjit/c_type.rb b/lib/mjit/c_type.rb index be93b36e902f41..8ccdfcc4d34824 100644 --- a/lib/mjit/c_type.rb +++ b/lib/mjit/c_type.rb @@ -27,7 +27,7 @@ def self.new(name, sizeof, **members) end module Immediate - # @param fiddle_type [Integer] Fiddle::TYPE_* + # @param fiddle_type [Integer] def self.new(fiddle_type) name = Fiddle.constants.find do |const| const.start_with?('TYPE_') && Fiddle.const_get(const) == fiddle_type.abs @@ -40,6 +40,20 @@ def self.new(fiddle_type) CPointer::Immediate.define(fiddle_type) end end + + # @param type [String] + def self.parse(ctype) + new(Fiddle::Importer.parse_ctype(ctype)) + end + + def self.find(size, unsigned) + fiddle_type = TYPE_MAP.fetch(size) + fiddle_type = -fiddle_type if unsigned + new(fiddle_type) + end + + TYPE_MAP = Fiddle::PackInfo::SIZE_MAP.map { |type, size| [size, type.abs] }.to_h + private_constant :TYPE_MAP end module Bool diff --git a/mjit_c.rb b/mjit_c.rb index ec014ae7d0cb5e..67430c5b3d1912 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Part of this file is generated by tool/mjit/bindgen.rb. # Run `make mjit-bindgen` to update code between "MJIT bindgen begin" and "MJIT bindgen end". module RubyVM::MJIT @@ -175,14 +176,10 @@ def C.RB_BUILTIN @RB_BUILTIN ||= self.rb_builtin_function end - def C.VALUE - @VALUE ||= CType::Immediate.new(-5) - end - def C.compile_branch @compile_branch ||= CType::Struct.new( "compile_branch", 8, - stack_size: [0, CType::Immediate.new(-4)], + stack_size: [0, CType::Immediate.parse("unsigned int")], finish_p: [32, self._Bool], ) end @@ -191,16 +188,16 @@ def C.compile_status @compile_status ||= CType::Struct.new( "compile_status", 120, success: [0, self._Bool], - stack_size_for_pos: [64, CType::Pointer.new { CType::Immediate.new(4) }], + stack_size_for_pos: [64, CType::Pointer.new { CType::Immediate.parse("int") }], local_stack_p: [128, self._Bool], is_entries: [192, CType::Pointer.new { self.iseq_inline_storage_entry }], - cc_entries_index: [256, CType::Immediate.new(4)], + cc_entries_index: [256, CType::Immediate.parse("int")], compiled_iseq: [320, CType::Pointer.new { self.rb_iseq_constant_body }], - compiled_id: [384, CType::Immediate.new(4)], + compiled_id: [384, CType::Immediate.parse("int")], compile_info: [448, CType::Pointer.new { self.rb_mjit_compile_info }], merge_ivar_guards_p: [512, self._Bool], ivar_serial: [576, self.rb_serial_t], - max_ivar_index: [640, CType::Immediate.new(-5)], + max_ivar_index: [640, CType::Immediate.parse("size_t")], inlined_iseqs: [704, CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], inline_context: [768, self.inlined_call_context], ) @@ -209,10 +206,10 @@ def C.compile_status def C.inlined_call_context @inlined_call_context ||= CType::Struct.new( "inlined_call_context", 24, - orig_argc: [0, CType::Immediate.new(4)], + orig_argc: [0, CType::Immediate.parse("int")], me: [64, self.VALUE], - param_size: [128, CType::Immediate.new(4)], - local_size: [160, CType::Immediate.new(4)], + param_size: [128, CType::Immediate.parse("int")], + local_size: [160, CType::Immediate.parse("int")], ) end @@ -262,11 +259,11 @@ def C.mjit_options save_temps: [8, self._Bool], warnings: [16, self._Bool], debug: [24, self._Bool], - debug_flags: [64, CType::Pointer.new { CType::Immediate.new(2) }], + debug_flags: [64, CType::Pointer.new { CType::Immediate.parse("char") }], wait: [128, self._Bool], - min_calls: [160, CType::Immediate.new(-4)], - verbose: [192, CType::Immediate.new(4)], - max_cache_size: [224, CType::Immediate.new(4)], + min_calls: [160, CType::Immediate.parse("unsigned int")], + verbose: [192, CType::Immediate.parse("int")], + max_cache_size: [224, CType::Immediate.parse("int")], pause: [256, self._Bool], custom: [264, self._Bool], ) @@ -275,11 +272,11 @@ def C.mjit_options def C.rb_builtin_function @rb_builtin_function ||= CType::Struct.new( "rb_builtin_function", 32, - func_ptr: [0, CType::Pointer.new { CType::Immediate.new(0) }], - argc: [64, CType::Immediate.new(4)], - index: [96, CType::Immediate.new(4)], - name: [128, CType::Pointer.new { CType::Immediate.new(2) }], - compiler: [192, CType::Immediate.new(1)], + func_ptr: [0, CType::Pointer.new { CType::Immediate.parse("void") }], + argc: [64, CType::Immediate.parse("int")], + index: [96, CType::Immediate.parse("int")], + name: [128, CType::Pointer.new { CType::Immediate.parse("char") }], + compiler: [192, CType::Immediate.parse("void *")], ) end @@ -311,7 +308,7 @@ def C.rb_callcache call_: [192, self.vm_call_handler], aux_: [256, CType::Union.new( "", 8, - attr_index: CType::Immediate.new(-4), + attr_index: CType::Immediate.parse("unsigned int"), method_missing_reason: self.method_missing_reason, v: self.VALUE, )], @@ -337,9 +334,9 @@ def C.rb_control_frame_t iseq: [128, CType::Pointer.new { self.rb_iseq_t }], self: [192, self.VALUE], ep: [256, CType::Pointer.new { self.VALUE }], - block_code: [320, CType::Pointer.new { CType::Immediate.new(0) }], + block_code: [320, CType::Pointer.new { CType::Immediate.parse("void") }], __bp__: [384, CType::Pointer.new { self.VALUE }], - jit_return: [448, CType::Pointer.new { CType::Immediate.new(0) }], + jit_return: [448, CType::Pointer.new { CType::Immediate.parse("void") }], ) end @@ -358,7 +355,7 @@ def C.rb_execution_context_struct @rb_execution_context_struct ||= CType::Struct.new( "rb_execution_context_struct", 1, vm_stack: [-1, CType::Pointer.new { self.VALUE }], - vm_stack_size: [-1, CType::Immediate.new(-5)], + vm_stack_size: [-1, CType::Immediate.parse("size_t")], cfp: [-1, CType::Pointer.new { self.rb_control_frame_t }], tag: [-1, CType::Pointer.new { self.rb_vm_tag }], interrupt_flag: [-1, self.rb_atomic_t], @@ -374,15 +371,15 @@ def C.rb_execution_context_struct trace_arg: [-1, CType::Pointer.new { self.rb_trace_arg_struct }], errinfo: [-1, self.VALUE], passed_block_handler: [-1, self.VALUE], - raised_flag: [-1, CType::Immediate.new(-2)], + raised_flag: [-1, CType::Immediate.parse("uint8_t")], method_missing_reason: [-1, self.method_missing_reason], private_const_reference: [-1, self.VALUE], machine: [-1, CType::Struct.new( "", 1, stack_start: [-1, CType::Pointer.new { self.VALUE }], stack_end: [-1, CType::Pointer.new { self.VALUE }], - stack_maxsize: [-1, CType::Immediate.new(-5)], - regs: [-1, CType::Immediate.new(4)], + stack_maxsize: [-1, CType::Immediate.parse("size_t")], + regs: [-1, CType::Immediate.parse("int")], )], ) end @@ -395,7 +392,7 @@ def C.rb_iseq_constant_body @rb_iseq_constant_body ||= CType::Struct.new( "rb_iseq_constant_body", 336, type: [0, self.rb_iseq_type], - iseq_size: [32, CType::Immediate.new(-4)], + iseq_size: [32, CType::Immediate.parse("unsigned int")], iseq_encoded: [64, CType::Pointer.new { self.VALUE }], param: [128, CType::Struct.new( "", 48, @@ -412,13 +409,13 @@ def C.rb_iseq_constant_body accepts_no_kwarg: [8, CType::BitField.new(1, 0)], ruby2_keywords: [9, CType::BitField.new(1, 1)], )], - size: [32, CType::Immediate.new(-4)], - lead_num: [64, CType::Immediate.new(4)], - opt_num: [96, CType::Immediate.new(4)], - rest_start: [128, CType::Immediate.new(4)], - post_start: [160, CType::Immediate.new(4)], - post_num: [192, CType::Immediate.new(4)], - block_start: [224, CType::Immediate.new(4)], + size: [32, CType::Immediate.parse("unsigned int")], + lead_num: [64, CType::Immediate.parse("int")], + opt_num: [96, CType::Immediate.parse("int")], + rest_start: [128, CType::Immediate.parse("int")], + post_start: [160, CType::Immediate.parse("int")], + post_num: [192, CType::Immediate.parse("int")], + block_start: [224, CType::Immediate.parse("int")], opt_table: [256, CType::Pointer.new { self.VALUE }], keyword: [320, CType::Pointer.new { self.rb_iseq_param_keyword }], )], @@ -438,13 +435,13 @@ def C.rb_iseq_constant_body pc2branchindex: [192, self.VALUE], original_iseq: [256, CType::Pointer.new { self.VALUE }], )], - local_table_size: [1920, CType::Immediate.new(-4)], - ic_size: [1952, CType::Immediate.new(-4)], - ise_size: [1984, CType::Immediate.new(-4)], - ivc_size: [2016, CType::Immediate.new(-4)], - icvarc_size: [2048, CType::Immediate.new(-4)], - ci_size: [2080, CType::Immediate.new(-4)], - stack_max: [2112, CType::Immediate.new(-4)], + local_table_size: [1920, CType::Immediate.parse("unsigned int")], + ic_size: [1952, CType::Immediate.parse("unsigned int")], + ise_size: [1984, CType::Immediate.parse("unsigned int")], + ivc_size: [2016, CType::Immediate.parse("unsigned int")], + icvarc_size: [2048, CType::Immediate.parse("unsigned int")], + ci_size: [2080, CType::Immediate.parse("unsigned int")], + stack_max: [2112, CType::Immediate.parse("unsigned int")], mark_bits: [2176, CType::Union.new( "", 8, list: CType::Pointer.new { self.iseq_bits_t }, @@ -454,10 +451,10 @@ def C.rb_iseq_constant_body builtin_inline_p: [2248, self._Bool], outer_variables: [2304, CType::Pointer.new { self.rb_id_table }], mandatory_only_iseq: [2368, CType::Pointer.new { self.rb_iseq_t }], - jit_func: [2432, CType::Immediate.new(1)], - total_calls: [2496, CType::Immediate.new(-5)], + jit_func: [2432, CType::Immediate.parse("void *")], + total_calls: [2496, CType::Immediate.parse("unsigned long")], jit_unit: [2560, CType::Pointer.new { self.rb_mjit_unit }], - yjit_payload: [2624, CType::Pointer.new { CType::Immediate.new(0) }], + yjit_payload: [2624, CType::Pointer.new { CType::Immediate.parse("void") }], ) end @@ -468,7 +465,7 @@ def C.rb_iseq_location_t base_label: [64, self.VALUE, true], label: [128, self.VALUE, true], first_lineno: [192, self.VALUE, true], - node_id: [256, CType::Immediate.new(4)], + node_id: [256, CType::Immediate.parse("int")], code_location: [288, self.rb_code_location_t], ) end @@ -485,7 +482,7 @@ def C.rb_iseq_struct loader: CType::Struct.new( "", 16, obj: [0, self.VALUE], - index: [64, CType::Immediate.new(4)], + index: [64, CType::Immediate.parse("int")], ), exec: CType::Struct.new( "", 16, @@ -503,7 +500,7 @@ def C.rb_iseq_t def C.rb_iv_index_tbl_entry @rb_iv_index_tbl_entry ||= CType::Struct.new( "rb_iv_index_tbl_entry", 24, - index: [0, CType::Immediate.new(-4)], + index: [0, CType::Immediate.parse("uint32_t")], class_serial: [64, self.rb_serial_t], class_value: [128, self.VALUE], ) @@ -528,7 +525,7 @@ def C.rb_method_definition_struct optimized: self.rb_method_optimized_t, )], original_id: [256, self.ID], - method_serial: [320, CType::Immediate.new(-5)], + method_serial: [320, CType::Immediate.parse("uintptr_t")], ) end @@ -541,7 +538,7 @@ def C.rb_method_iseq_t end def C.rb_method_type_t - @rb_method_type_t ||= CType::Immediate.new(4) + @rb_method_type_t ||= CType::Immediate.parse("int") end def C.rb_mjit_compile_info @@ -559,19 +556,23 @@ def C.rb_mjit_unit @rb_mjit_unit ||= CType::Struct.new( "rb_mjit_unit", 64, unode: [0, self.ccan_list_node], - id: [128, CType::Immediate.new(4)], - handle: [192, CType::Pointer.new { CType::Immediate.new(0) }], + id: [128, CType::Immediate.parse("int")], + handle: [192, CType::Pointer.new { CType::Immediate.parse("void") }], iseq: [256, CType::Pointer.new { self.rb_iseq_t }], used_code_p: [320, self._Bool], compact_p: [328, self._Bool], compile_info: [336, self.rb_mjit_compile_info], cc_entries: [384, CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], - cc_entries_size: [448, CType::Immediate.new(-4)], + cc_entries_size: [448, CType::Immediate.parse("unsigned int")], ) end def C.rb_serial_t - @rb_serial_t ||= CType::Immediate.new(-6) + @rb_serial_t ||= CType::Immediate.parse("unsigned long long") + end + + def C.VALUE + @VALUE ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(VALUE)"), Primitive.cexpr!("SIGNED_TYPE_P(VALUE)")) end def C._Bool diff --git a/mjit_compiler.c b/mjit_compiler.c index f04f7b0c036147..2b518feffd5e82 100644 --- a/mjit_compiler.c +++ b/mjit_compiler.c @@ -164,6 +164,9 @@ has_cache_for_send(rb_execution_context_t *ec, VALUE self, VALUE cc_addr, VALUE extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); +#define SIZEOF(type) RB_SIZE2NUM(sizeof(type)) +#define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(0)) + #include "mjit_c.rbinc" #include "mjit_compiler.rbinc" diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 9676069b16b32c..793db965dd7d18 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -114,14 +114,16 @@ class BindingGenerator # @param src_path [String] # @param uses [Array] # @param ints [Array] - # @param types [Array] Imported types + # @param types [Array] + # @param dynamic_types [Array] #ifdef-dependent immediate types, which need Primitive.cexpr! for type detection # @param ruby_fields [Hash{ Symbol => Array }] Struct VALUE fields that are considered Ruby objects - def initialize(src_path:, uses:, ints:, types:, ruby_fields:) + def initialize(src_path:, uses:, ints:, types:, dynamic_types:, ruby_fields:) @preamble, @postamble = split_ambles(src_path) @src = String.new @uses = uses.sort @ints = ints.sort @types = types.sort + @dynamic_types = dynamic_types.sort @ruby_fields = ruby_fields.transform_keys(&:to_s) @references = Set.new end @@ -159,8 +161,19 @@ def generate(nodes) println end + # Define dynamic types + @dynamic_types.each do |type| + unless generate_node(nodes_index[type])&.start_with?('CType::Immediate') + raise "Non-immediate type is given to dynamic_types: #{type}" + end + println " def C.#{type}" + println " @#{type} ||= CType::Immediate.find(Primitive.cexpr!(\"SIZEOF(#{type})\"), Primitive.cexpr!(\"SIGNED_TYPE_P(#{type})\"))" + println " end" + println + end + # Leave a stub for types that are referenced but not targeted - (@references - @types).each do |type| + (@references - @types - @dynamic_types).each do |type| println " def C.#{type}" println " #{DEFAULTS[type]}" println " end" @@ -257,7 +270,12 @@ def generate_type(type) push_target(type) "self.#{type}" else - "CType::Immediate.new(#{ctype})" + # Convert any function pointers to void* to workaround FILE* vs int* + if ctype == Fiddle::TYPE_VOIDP + "CType::Immediate.parse(\"void *\")" + else + "CType::Immediate.parse(#{type.dump})" + end end end end @@ -317,7 +335,6 @@ def push_target(target) IC IVC RB_BUILTIN - VALUE compile_branch compile_status inlined_call_context @@ -347,6 +364,9 @@ def push_target(target) rb_mjit_unit rb_serial_t ], + dynamic_types: %w[ + VALUE + ], ruby_fields: { rb_iseq_location_struct: %w[ base_label From 2ce1460c6504bfefab85814c175de684b3e40381 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 21 Sep 2022 22:32:28 +0900 Subject: [PATCH 213/269] Bindgen sizeof struct and union with builtin --- mjit_c.rb | 64 ++++++++++++++++++++++---------------------- tool/mjit/bindgen.rb | 17 +++++++----- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/mjit_c.rb b/mjit_c.rb index 67430c5b3d1912..e540fceda9ca4c 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -178,7 +178,7 @@ def C.RB_BUILTIN def C.compile_branch @compile_branch ||= CType::Struct.new( - "compile_branch", 8, + "compile_branch", Primitive.cexpr!("SIZEOF(struct compile_branch)"), stack_size: [0, CType::Immediate.parse("unsigned int")], finish_p: [32, self._Bool], ) @@ -186,7 +186,7 @@ def C.compile_branch def C.compile_status @compile_status ||= CType::Struct.new( - "compile_status", 120, + "compile_status", Primitive.cexpr!("SIZEOF(struct compile_status)"), success: [0, self._Bool], stack_size_for_pos: [64, CType::Pointer.new { CType::Immediate.parse("int") }], local_stack_p: [128, self._Bool], @@ -205,7 +205,7 @@ def C.compile_status def C.inlined_call_context @inlined_call_context ||= CType::Struct.new( - "inlined_call_context", 24, + "inlined_call_context", Primitive.cexpr!("SIZEOF(struct inlined_call_context)"), orig_argc: [0, CType::Immediate.parse("int")], me: [64, self.VALUE], param_size: [128, CType::Immediate.parse("int")], @@ -215,7 +215,7 @@ def C.inlined_call_context def C.iseq_inline_constant_cache @iseq_inline_constant_cache ||= CType::Struct.new( - "iseq_inline_constant_cache", 16, + "iseq_inline_constant_cache", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache)"), entry: [0, CType::Pointer.new { self.iseq_inline_constant_cache_entry }], segments: [64, CType::Pointer.new { self.ID }], ) @@ -223,7 +223,7 @@ def C.iseq_inline_constant_cache def C.iseq_inline_constant_cache_entry @iseq_inline_constant_cache_entry ||= CType::Struct.new( - "iseq_inline_constant_cache_entry", 40, + "iseq_inline_constant_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache_entry)"), flags: [0, self.VALUE], value: [64, self.VALUE], _unused1: [128, self.VALUE], @@ -234,16 +234,16 @@ 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", 8, + "iseq_inline_iv_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_iv_cache_entry)"), entry: [0, CType::Pointer.new { self.rb_iv_index_tbl_entry }], ) end def C.iseq_inline_storage_entry @iseq_inline_storage_entry ||= CType::Union.new( - "iseq_inline_storage_entry", 16, + "iseq_inline_storage_entry", Primitive.cexpr!("SIZEOF(union iseq_inline_storage_entry)"), once: CType::Struct.new( - "", 16, + "", Primitive.cexpr!("SIZEOF(((union iseq_inline_storage_entry *)NULL)->once)"), running_thread: [0, CType::Pointer.new { self.rb_thread_struct }], value: [64, self.VALUE], ), @@ -254,7 +254,7 @@ def C.iseq_inline_storage_entry def C.mjit_options @mjit_options ||= CType::Struct.new( - "mjit_options", 40, + "mjit_options", Primitive.cexpr!("SIZEOF(struct mjit_options)"), on: [0, self._Bool], save_temps: [8, self._Bool], warnings: [16, self._Bool], @@ -271,7 +271,7 @@ def C.mjit_options def C.rb_builtin_function @rb_builtin_function ||= CType::Struct.new( - "rb_builtin_function", 32, + "rb_builtin_function", Primitive.cexpr!("SIZEOF(struct rb_builtin_function)"), func_ptr: [0, CType::Pointer.new { CType::Immediate.parse("void") }], argc: [64, CType::Immediate.parse("int")], index: [96, CType::Immediate.parse("int")], @@ -282,7 +282,7 @@ def C.rb_builtin_function def C.rb_call_data @rb_call_data ||= CType::Struct.new( - "rb_call_data", 16, + "rb_call_data", Primitive.cexpr!("SIZEOF(struct rb_call_data)"), ci: [0, CType::Pointer.new { self.rb_callinfo }], cc: [64, CType::Pointer.new { self.rb_callcache }], ) @@ -290,7 +290,7 @@ def C.rb_call_data def C.rb_callable_method_entry_struct @rb_callable_method_entry_struct ||= CType::Struct.new( - "rb_callable_method_entry_struct", 40, + "rb_callable_method_entry_struct", Primitive.cexpr!("SIZEOF(struct rb_callable_method_entry_struct)"), flags: [0, self.VALUE], defined_class: [64, self.VALUE], def: [128, CType::Pointer.new { self.rb_method_definition_struct }], @@ -301,13 +301,13 @@ def C.rb_callable_method_entry_struct def C.rb_callcache @rb_callcache ||= CType::Struct.new( - "rb_callcache", 40, + "rb_callcache", Primitive.cexpr!("SIZEOF(struct rb_callcache)"), flags: [0, self.VALUE], klass: [64, self.VALUE], cme_: [128, CType::Pointer.new { self.rb_callable_method_entry_struct }], call_: [192, self.vm_call_handler], aux_: [256, CType::Union.new( - "", 8, + "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_)"), attr_index: CType::Immediate.parse("unsigned int"), method_missing_reason: self.method_missing_reason, v: self.VALUE, @@ -317,7 +317,7 @@ def C.rb_callcache def C.rb_callinfo @rb_callinfo ||= CType::Struct.new( - "rb_callinfo", 40, + "rb_callinfo", Primitive.cexpr!("SIZEOF(struct rb_callinfo)"), flags: [0, self.VALUE], kwarg: [64, CType::Pointer.new { self.rb_callinfo_kwarg }], mid: [128, self.VALUE], @@ -342,7 +342,7 @@ def C.rb_control_frame_t def C.rb_cref_t @rb_cref_t ||= CType::Struct.new( - "rb_cref_struct", 40, + "rb_cref_struct", Primitive.cexpr!("SIZEOF(struct rb_cref_struct)"), flags: [0, self.VALUE], refinements: [64, self.VALUE], klass_or_self: [128, self.VALUE], @@ -390,14 +390,14 @@ def C.rb_execution_context_t def C.rb_iseq_constant_body @rb_iseq_constant_body ||= CType::Struct.new( - "rb_iseq_constant_body", 336, + "rb_iseq_constant_body", Primitive.cexpr!("SIZEOF(struct rb_iseq_constant_body)"), type: [0, self.rb_iseq_type], iseq_size: [32, CType::Immediate.parse("unsigned int")], iseq_encoded: [64, CType::Pointer.new { self.VALUE }], param: [128, CType::Struct.new( - "", 48, + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param)"), flags: [0, CType::Struct.new( - "", 4, + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param.flags)"), has_lead: [0, CType::BitField.new(1, 0)], has_opt: [1, CType::BitField.new(1, 1)], has_rest: [2, CType::BitField.new(1, 2)], @@ -428,7 +428,7 @@ def C.rb_iseq_constant_body is_entries: [1472, CType::Pointer.new { self.iseq_inline_storage_entry }], call_data: [1536, CType::Pointer.new { self.rb_call_data }], variable: [1600, CType::Struct.new( - "", 40, + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->variable)"), flip_count: [0, self.rb_snum_t], script_lines: [64, self.VALUE], coverage: [128, self.VALUE], @@ -443,7 +443,7 @@ def C.rb_iseq_constant_body ci_size: [2080, CType::Immediate.parse("unsigned int")], stack_max: [2112, CType::Immediate.parse("unsigned int")], mark_bits: [2176, CType::Union.new( - "", 8, + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->mark_bits)"), list: CType::Pointer.new { self.iseq_bits_t }, single: self.iseq_bits_t, )], @@ -460,7 +460,7 @@ def C.rb_iseq_constant_body def C.rb_iseq_location_t @rb_iseq_location_t ||= CType::Struct.new( - "rb_iseq_location_struct", 56, + "rb_iseq_location_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_location_struct)"), pathobj: [0, self.VALUE, true], base_label: [64, self.VALUE, true], label: [128, self.VALUE, true], @@ -472,20 +472,20 @@ def C.rb_iseq_location_t def C.rb_iseq_struct @rb_iseq_struct ||= CType::Struct.new( - "rb_iseq_struct", 40, + "rb_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_struct)"), flags: [0, self.VALUE], wrapper: [64, self.VALUE], body: [128, CType::Pointer.new { self.rb_iseq_constant_body }], aux: [192, CType::Union.new( - "", 16, + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux)"), compile_data: CType::Pointer.new { self.iseq_compile_data }, loader: CType::Struct.new( - "", 16, + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.loader)"), obj: [0, self.VALUE], index: [64, CType::Immediate.parse("int")], ), exec: CType::Struct.new( - "", 16, + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.exec)"), local_hooks: [0, CType::Pointer.new { self.rb_hook_list_struct }], global_trace_events: [64, self.rb_event_flag_t], ), @@ -499,7 +499,7 @@ def C.rb_iseq_t def C.rb_iv_index_tbl_entry @rb_iv_index_tbl_entry ||= CType::Struct.new( - "rb_iv_index_tbl_entry", 24, + "rb_iv_index_tbl_entry", Primitive.cexpr!("SIZEOF(struct rb_iv_index_tbl_entry)"), index: [0, CType::Immediate.parse("uint32_t")], class_serial: [64, self.rb_serial_t], class_value: [128, self.VALUE], @@ -508,14 +508,14 @@ def C.rb_iv_index_tbl_entry def C.rb_method_definition_struct @rb_method_definition_struct ||= CType::Struct.new( - "rb_method_definition_struct", 48, + "rb_method_definition_struct", Primitive.cexpr!("SIZEOF(struct rb_method_definition_struct)"), type: [0, self.rb_method_type_t], iseq_overload: [4, CType::BitField.new(1, 4)], alias_count: [5, CType::BitField.new(27, 5)], complemented_count: [32, CType::BitField.new(28, 0)], no_redef_warning: [60, CType::BitField.new(1, 4)], body: [64, CType::Union.new( - "", 24, + "", Primitive.cexpr!("SIZEOF(((struct rb_method_definition_struct *)NULL)->body)"), iseq: self.rb_method_iseq_t, cfunc: self.rb_method_cfunc_t, attr: self.rb_method_attr_t, @@ -531,7 +531,7 @@ def C.rb_method_definition_struct def C.rb_method_iseq_t @rb_method_iseq_t ||= CType::Struct.new( - "rb_method_iseq_struct", 16, + "rb_method_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_method_iseq_struct)"), iseqptr: [0, CType::Pointer.new { self.rb_iseq_t }], cref: [64, CType::Pointer.new { self.rb_cref_t }], ) @@ -543,7 +543,7 @@ def C.rb_method_type_t def C.rb_mjit_compile_info @rb_mjit_compile_info ||= CType::Struct.new( - "rb_mjit_compile_info", 5, + "rb_mjit_compile_info", Primitive.cexpr!("SIZEOF(struct rb_mjit_compile_info)"), disable_ivar_cache: [0, self._Bool], disable_exivar_cache: [8, self._Bool], disable_send_cache: [16, self._Bool], @@ -554,7 +554,7 @@ def C.rb_mjit_compile_info def C.rb_mjit_unit @rb_mjit_unit ||= CType::Struct.new( - "rb_mjit_unit", 64, + "rb_mjit_unit", Primitive.cexpr!("SIZEOF(struct rb_mjit_unit)"), unode: [0, self.ccan_list_node], id: [128, CType::Immediate.parse("int")], handle: [192, CType::Pointer.new { CType::Immediate.parse("void") }], diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 793db965dd7d18..06ece7d6fa5023 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -201,12 +201,12 @@ def split_ambles(src_path) # Generate code from a node. Used for constructing a complex nested node. # @param node [Node] - def generate_node(node) + def generate_node(node, sizeof_type: nil) case node&.kind when :struct, :union # node.spelling is often empty for union, but we'd like to give it a name when it has one. buf = +"CType::#{node.kind.to_s.sub(/\A[a-z]/, &:upcase)}.new(\n" - buf << " \"#{node.spelling}\", #{node.sizeof_type},\n" + buf << " \"#{node.spelling}\", Primitive.cexpr!(\"SIZEOF(#{sizeof_type || node.type})\"),\n" node.children.each do |child| field_builder = proc do |field, type| if node.kind == :struct @@ -221,12 +221,17 @@ def generate_node(node) # BitField is struct-specific. So it must be handled here. in Node[kind: :field_decl, spelling:, bitwidth:, children: [_grandchild]] if bitwidth > 0 buf << field_builder.call(spelling, "CType::BitField.new(#{bitwidth}, #{node.offsetof.fetch(spelling) % 8})") + # "(unnamed ...)" struct and union are handled here, which are also struct-specific. + in Node[kind: :field_decl, spelling:, type:, children: [grandchild]] if type.match?(/\((unnamed|anonymous) [^)]+\)\z/) + if sizeof_type + child_type = "#{sizeof_type}.#{child.spelling}" + else + child_type = "((#{node.type} *)NULL)->#{child.spelling}" + end + buf << field_builder.call(spelling, generate_node(grandchild, sizeof_type: child_type).gsub(/^/, ' ').sub(/\A +/, '')) # In most cases, we'd like to let generate_type handle the type unless it's "(unnamed ...)". - in Node[kind: :field_decl, spelling:, type:] if !type.empty? && !type.match?(/\((unnamed|anonymous) [^)]+\)\z/) + in Node[kind: :field_decl, spelling:, type:] if !type.empty? buf << field_builder.call(spelling, generate_type(type)) - # Lastly, "(unnamed ...)" struct and union are handled here, which are also struct-specific. - in Node[kind: :field_decl, spelling:, children: [grandchild]] - buf << field_builder.call(spelling, generate_node(grandchild).gsub(/^/, ' ').sub(/\A +/, '')) else # forward declarations are ignored end end From dc5b536468f8def4ab808da57462aee6a99a6fe0 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 22 Sep 2022 21:39:54 +0900 Subject: [PATCH 214/269] Bindgen offsetof struct and union with builtin except for bit fields. I made a risky assumption on leading bit fields and just gave up non-leading bit fields for now. I'll change it to let C code access bit fields later. --- mjit_c.rb | 366 +++++++++++++++++++++---------------------- mjit_compiler.c | 4 + tool/mjit/bindgen.rb | 12 +- 3 files changed, 197 insertions(+), 185 deletions(-) diff --git a/mjit_c.rb b/mjit_c.rb index e540fceda9ca4c..d4edbf28250f12 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -179,63 +179,63 @@ def C.RB_BUILTIN def C.compile_branch @compile_branch ||= CType::Struct.new( "compile_branch", Primitive.cexpr!("SIZEOF(struct compile_branch)"), - stack_size: [0, CType::Immediate.parse("unsigned int")], - finish_p: [32, self._Bool], + stack_size: [Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), stack_size)"), CType::Immediate.parse("unsigned int")], + finish_p: [Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), finish_p)"), self._Bool], ) end def C.compile_status @compile_status ||= CType::Struct.new( "compile_status", Primitive.cexpr!("SIZEOF(struct compile_status)"), - success: [0, self._Bool], - stack_size_for_pos: [64, CType::Pointer.new { CType::Immediate.parse("int") }], - local_stack_p: [128, self._Bool], - is_entries: [192, CType::Pointer.new { self.iseq_inline_storage_entry }], - cc_entries_index: [256, CType::Immediate.parse("int")], - compiled_iseq: [320, CType::Pointer.new { self.rb_iseq_constant_body }], - compiled_id: [384, CType::Immediate.parse("int")], - compile_info: [448, CType::Pointer.new { self.rb_mjit_compile_info }], - merge_ivar_guards_p: [512, self._Bool], - ivar_serial: [576, self.rb_serial_t], - max_ivar_index: [640, CType::Immediate.parse("size_t")], - inlined_iseqs: [704, CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], - inline_context: [768, self.inlined_call_context], + success: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), success)"), self._Bool], + stack_size_for_pos: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), stack_size_for_pos)"), CType::Pointer.new { CType::Immediate.parse("int") }], + local_stack_p: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), local_stack_p)"), self._Bool], + is_entries: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), is_entries)"), CType::Pointer.new { self.iseq_inline_storage_entry }], + cc_entries_index: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), cc_entries_index)"), CType::Immediate.parse("int")], + compiled_iseq: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_iseq)"), CType::Pointer.new { self.rb_iseq_constant_body }], + compiled_id: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_id)"), CType::Immediate.parse("int")], + compile_info: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compile_info)"), CType::Pointer.new { self.rb_mjit_compile_info }], + merge_ivar_guards_p: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), merge_ivar_guards_p)"), self._Bool], + ivar_serial: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), ivar_serial)"), self.rb_serial_t], + max_ivar_index: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), max_ivar_index)"), CType::Immediate.parse("size_t")], + inlined_iseqs: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inlined_iseqs)"), CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], + inline_context: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inline_context)"), self.inlined_call_context], ) end def C.inlined_call_context @inlined_call_context ||= CType::Struct.new( "inlined_call_context", Primitive.cexpr!("SIZEOF(struct inlined_call_context)"), - orig_argc: [0, CType::Immediate.parse("int")], - me: [64, self.VALUE], - param_size: [128, CType::Immediate.parse("int")], - local_size: [160, CType::Immediate.parse("int")], + orig_argc: [Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), orig_argc)"), CType::Immediate.parse("int")], + me: [Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), me)"), self.VALUE], + param_size: [Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), param_size)"), CType::Immediate.parse("int")], + local_size: [Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), local_size)"), CType::Immediate.parse("int")], ) end def C.iseq_inline_constant_cache @iseq_inline_constant_cache ||= CType::Struct.new( "iseq_inline_constant_cache", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache)"), - entry: [0, CType::Pointer.new { self.iseq_inline_constant_cache_entry }], - segments: [64, CType::Pointer.new { self.ID }], + entry: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), entry)"), CType::Pointer.new { self.iseq_inline_constant_cache_entry }], + segments: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), segments)"), CType::Pointer.new { self.ID }], ) end def C.iseq_inline_constant_cache_entry @iseq_inline_constant_cache_entry ||= CType::Struct.new( "iseq_inline_constant_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache_entry)"), - flags: [0, self.VALUE], - value: [64, self.VALUE], - _unused1: [128, self.VALUE], - _unused2: [192, self.VALUE], - ic_cref: [256, CType::Pointer.new { self.rb_cref_t }], + flags: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), flags)"), self.VALUE], + value: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), value)"), self.VALUE], + _unused1: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused1)"), self.VALUE], + _unused2: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused2)"), self.VALUE], + ic_cref: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), ic_cref)"), CType::Pointer.new { self.rb_cref_t }], ) end 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: [0, CType::Pointer.new { self.rb_iv_index_tbl_entry }], + entry: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), entry)"), CType::Pointer.new { self.rb_iv_index_tbl_entry }], ) end @@ -244,8 +244,8 @@ def C.iseq_inline_storage_entry "iseq_inline_storage_entry", Primitive.cexpr!("SIZEOF(union iseq_inline_storage_entry)"), once: CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((union iseq_inline_storage_entry *)NULL)->once)"), - running_thread: [0, CType::Pointer.new { self.rb_thread_struct }], - value: [64, self.VALUE], + running_thread: [Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, running_thread)"), CType::Pointer.new { self.rb_thread_struct }], + value: [Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, value)"), self.VALUE], ), ic_cache: self.iseq_inline_constant_cache, iv_cache: self.iseq_inline_iv_cache_entry, @@ -255,58 +255,58 @@ def C.iseq_inline_storage_entry def C.mjit_options @mjit_options ||= CType::Struct.new( "mjit_options", Primitive.cexpr!("SIZEOF(struct mjit_options)"), - on: [0, self._Bool], - save_temps: [8, self._Bool], - warnings: [16, self._Bool], - debug: [24, self._Bool], - debug_flags: [64, CType::Pointer.new { CType::Immediate.parse("char") }], - wait: [128, self._Bool], - min_calls: [160, CType::Immediate.parse("unsigned int")], - verbose: [192, CType::Immediate.parse("int")], - max_cache_size: [224, CType::Immediate.parse("int")], - pause: [256, self._Bool], - custom: [264, self._Bool], + on: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), on)"), self._Bool], + save_temps: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), save_temps)"), self._Bool], + warnings: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), warnings)"), self._Bool], + debug: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug)"), self._Bool], + debug_flags: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug_flags)"), CType::Pointer.new { CType::Immediate.parse("char") }], + wait: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), wait)"), self._Bool], + min_calls: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), min_calls)"), CType::Immediate.parse("unsigned int")], + verbose: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), verbose)"), CType::Immediate.parse("int")], + max_cache_size: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), max_cache_size)"), CType::Immediate.parse("int")], + pause: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), pause)"), self._Bool], + custom: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), custom)"), self._Bool], ) end def C.rb_builtin_function @rb_builtin_function ||= CType::Struct.new( "rb_builtin_function", Primitive.cexpr!("SIZEOF(struct rb_builtin_function)"), - func_ptr: [0, CType::Pointer.new { CType::Immediate.parse("void") }], - argc: [64, CType::Immediate.parse("int")], - index: [96, CType::Immediate.parse("int")], - name: [128, CType::Pointer.new { CType::Immediate.parse("char") }], - compiler: [192, CType::Immediate.parse("void *")], + func_ptr: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), func_ptr)"), CType::Pointer.new { CType::Immediate.parse("void") }], + argc: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), argc)"), CType::Immediate.parse("int")], + index: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), index)"), CType::Immediate.parse("int")], + name: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), name)"), CType::Pointer.new { CType::Immediate.parse("char") }], + compiler: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), compiler)"), CType::Immediate.parse("void *")], ) end def C.rb_call_data @rb_call_data ||= CType::Struct.new( "rb_call_data", Primitive.cexpr!("SIZEOF(struct rb_call_data)"), - ci: [0, CType::Pointer.new { self.rb_callinfo }], - cc: [64, CType::Pointer.new { self.rb_callcache }], + ci: [Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), ci)"), CType::Pointer.new { self.rb_callinfo }], + cc: [Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), cc)"), CType::Pointer.new { self.rb_callcache }], ) end def C.rb_callable_method_entry_struct @rb_callable_method_entry_struct ||= CType::Struct.new( "rb_callable_method_entry_struct", Primitive.cexpr!("SIZEOF(struct rb_callable_method_entry_struct)"), - flags: [0, self.VALUE], - defined_class: [64, self.VALUE], - def: [128, CType::Pointer.new { self.rb_method_definition_struct }], - called_id: [192, self.ID], - owner: [256, self.VALUE], + flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), flags)"), self.VALUE], + defined_class: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), defined_class)"), self.VALUE], + def: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), def)"), CType::Pointer.new { self.rb_method_definition_struct }], + called_id: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), called_id)"), self.ID], + owner: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), owner)"), self.VALUE], ) end def C.rb_callcache @rb_callcache ||= CType::Struct.new( "rb_callcache", Primitive.cexpr!("SIZEOF(struct rb_callcache)"), - flags: [0, self.VALUE], - klass: [64, self.VALUE], - cme_: [128, CType::Pointer.new { self.rb_callable_method_entry_struct }], - call_: [192, self.vm_call_handler], - aux_: [256, CType::Union.new( + flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), flags)"), self.VALUE], + klass: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), klass)"), self.VALUE], + cme_: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), cme_)"), CType::Pointer.new { self.rb_callable_method_entry_struct }], + call_: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), call_)"), self.vm_call_handler], + aux_: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)"), CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_)"), attr_index: CType::Immediate.parse("unsigned int"), method_missing_reason: self.method_missing_reason, @@ -318,68 +318,68 @@ def C.rb_callcache def C.rb_callinfo @rb_callinfo ||= CType::Struct.new( "rb_callinfo", Primitive.cexpr!("SIZEOF(struct rb_callinfo)"), - flags: [0, self.VALUE], - kwarg: [64, CType::Pointer.new { self.rb_callinfo_kwarg }], - mid: [128, self.VALUE], - flag: [192, self.VALUE], - argc: [256, self.VALUE], + flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flags)"), self.VALUE], + kwarg: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), kwarg)"), CType::Pointer.new { self.rb_callinfo_kwarg }], + mid: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), mid)"), self.VALUE], + flag: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flag)"), self.VALUE], + argc: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), argc)"), self.VALUE], ) end def C.rb_control_frame_t @rb_control_frame_t ||= CType::Struct.new( - "rb_control_frame_struct", 64, - pc: [0, CType::Pointer.new { self.VALUE }], - sp: [64, CType::Pointer.new { self.VALUE }], - iseq: [128, CType::Pointer.new { self.rb_iseq_t }], - self: [192, self.VALUE], - ep: [256, CType::Pointer.new { self.VALUE }], - block_code: [320, CType::Pointer.new { CType::Immediate.parse("void") }], - __bp__: [384, CType::Pointer.new { self.VALUE }], - jit_return: [448, CType::Pointer.new { CType::Immediate.parse("void") }], + "rb_control_frame_struct", Primitive.cexpr!("SIZEOF(struct rb_control_frame_struct)"), + pc: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), pc)"), CType::Pointer.new { self.VALUE }], + sp: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), sp)"), CType::Pointer.new { self.VALUE }], + iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), iseq)"), CType::Pointer.new { self.rb_iseq_t }], + self: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), self)"), self.VALUE], + ep: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), ep)"), CType::Pointer.new { self.VALUE }], + block_code: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), block_code)"), CType::Pointer.new { CType::Immediate.parse("void") }], + __bp__: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), __bp__)"), CType::Pointer.new { self.VALUE }], + jit_return: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), jit_return)"), CType::Pointer.new { CType::Immediate.parse("void") }], ) end def C.rb_cref_t @rb_cref_t ||= CType::Struct.new( "rb_cref_struct", Primitive.cexpr!("SIZEOF(struct rb_cref_struct)"), - flags: [0, self.VALUE], - refinements: [64, self.VALUE], - klass_or_self: [128, self.VALUE], - next: [192, CType::Pointer.new { self.rb_cref_struct }], - scope_visi: [256, self.rb_scope_visibility_t], + flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), flags)"), self.VALUE], + refinements: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), refinements)"), self.VALUE], + klass_or_self: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), klass_or_self)"), self.VALUE], + next: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), next)"), CType::Pointer.new { self.rb_cref_struct }], + scope_visi: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), scope_visi)"), self.rb_scope_visibility_t], ) end def C.rb_execution_context_struct @rb_execution_context_struct ||= CType::Struct.new( - "rb_execution_context_struct", 1, - vm_stack: [-1, CType::Pointer.new { self.VALUE }], - vm_stack_size: [-1, CType::Immediate.parse("size_t")], - cfp: [-1, CType::Pointer.new { self.rb_control_frame_t }], - tag: [-1, CType::Pointer.new { self.rb_vm_tag }], - interrupt_flag: [-1, self.rb_atomic_t], - interrupt_mask: [-1, self.rb_atomic_t], - fiber_ptr: [-1, CType::Pointer.new { self.rb_fiber_t }], - thread_ptr: [-1, CType::Pointer.new { self.rb_thread_struct }], - local_storage: [-1, CType::Pointer.new { self.rb_id_table }], - local_storage_recursive_hash: [-1, self.VALUE], - local_storage_recursive_hash_for_trace: [-1, self.VALUE], - root_lep: [-1, CType::Pointer.new { self.VALUE }], - root_svar: [-1, self.VALUE], - ensure_list: [-1, CType::Pointer.new { self.rb_ensure_list_t }], - trace_arg: [-1, CType::Pointer.new { self.rb_trace_arg_struct }], - errinfo: [-1, self.VALUE], - passed_block_handler: [-1, self.VALUE], - raised_flag: [-1, CType::Immediate.parse("uint8_t")], - method_missing_reason: [-1, self.method_missing_reason], - private_const_reference: [-1, self.VALUE], - machine: [-1, CType::Struct.new( - "", 1, - stack_start: [-1, CType::Pointer.new { self.VALUE }], - stack_end: [-1, CType::Pointer.new { self.VALUE }], - stack_maxsize: [-1, CType::Immediate.parse("size_t")], - regs: [-1, CType::Immediate.parse("int")], + "rb_execution_context_struct", Primitive.cexpr!("SIZEOF(struct rb_execution_context_struct)"), + vm_stack: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack)"), CType::Pointer.new { self.VALUE }], + vm_stack_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack_size)"), CType::Immediate.parse("size_t")], + cfp: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), cfp)"), CType::Pointer.new { self.rb_control_frame_t }], + tag: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), tag)"), CType::Pointer.new { self.rb_vm_tag }], + interrupt_flag: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_flag)"), self.rb_atomic_t], + interrupt_mask: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_mask)"), self.rb_atomic_t], + fiber_ptr: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), fiber_ptr)"), CType::Pointer.new { self.rb_fiber_t }], + thread_ptr: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), thread_ptr)"), CType::Pointer.new { self.rb_thread_struct }], + local_storage: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage)"), CType::Pointer.new { self.rb_id_table }], + local_storage_recursive_hash: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash)"), self.VALUE], + local_storage_recursive_hash_for_trace: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash_for_trace)"), self.VALUE], + root_lep: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_lep)"), CType::Pointer.new { self.VALUE }], + root_svar: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_svar)"), self.VALUE], + ensure_list: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), ensure_list)"), CType::Pointer.new { self.rb_ensure_list_t }], + trace_arg: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), trace_arg)"), CType::Pointer.new { self.rb_trace_arg_struct }], + errinfo: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), errinfo)"), self.VALUE], + passed_block_handler: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), passed_block_handler)"), self.VALUE], + raised_flag: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), raised_flag)"), CType::Immediate.parse("uint8_t")], + method_missing_reason: [nil, self.method_missing_reason], + private_const_reference: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), private_const_reference)"), self.VALUE], + machine: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), machine)"), CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_execution_context_struct *)NULL)->machine)"), + stack_start: [Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_start)"), CType::Pointer.new { self.VALUE }], + stack_end: [Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_end)"), CType::Pointer.new { self.VALUE }], + stack_maxsize: [Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_maxsize)"), CType::Immediate.parse("size_t")], + regs: [Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, regs)"), CType::Immediate.parse("int")], )], ) end @@ -391,12 +391,12 @@ def C.rb_execution_context_t def C.rb_iseq_constant_body @rb_iseq_constant_body ||= CType::Struct.new( "rb_iseq_constant_body", Primitive.cexpr!("SIZEOF(struct rb_iseq_constant_body)"), - type: [0, self.rb_iseq_type], - iseq_size: [32, CType::Immediate.parse("unsigned int")], - iseq_encoded: [64, CType::Pointer.new { self.VALUE }], - param: [128, CType::Struct.new( + type: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), type)"), self.rb_iseq_type], + iseq_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_size)"), CType::Immediate.parse("unsigned int")], + iseq_encoded: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_encoded)"), CType::Pointer.new { self.VALUE }], + param: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), param)"), CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param)"), - flags: [0, CType::Struct.new( + flags: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, flags)"), CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param.flags)"), has_lead: [0, CType::BitField.new(1, 0)], has_opt: [1, CType::BitField.new(1, 1)], @@ -409,85 +409,85 @@ def C.rb_iseq_constant_body accepts_no_kwarg: [8, CType::BitField.new(1, 0)], ruby2_keywords: [9, CType::BitField.new(1, 1)], )], - size: [32, CType::Immediate.parse("unsigned int")], - lead_num: [64, CType::Immediate.parse("int")], - opt_num: [96, CType::Immediate.parse("int")], - rest_start: [128, CType::Immediate.parse("int")], - post_start: [160, CType::Immediate.parse("int")], - post_num: [192, CType::Immediate.parse("int")], - block_start: [224, CType::Immediate.parse("int")], - opt_table: [256, CType::Pointer.new { self.VALUE }], - keyword: [320, CType::Pointer.new { self.rb_iseq_param_keyword }], + size: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, size)"), CType::Immediate.parse("unsigned int")], + lead_num: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, lead_num)"), CType::Immediate.parse("int")], + opt_num: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_num)"), CType::Immediate.parse("int")], + rest_start: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, rest_start)"), CType::Immediate.parse("int")], + post_start: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_start)"), CType::Immediate.parse("int")], + post_num: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_num)"), CType::Immediate.parse("int")], + block_start: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, block_start)"), CType::Immediate.parse("int")], + opt_table: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_table)"), CType::Pointer.new { self.VALUE }], + keyword: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, keyword)"), CType::Pointer.new { self.rb_iseq_param_keyword }], )], - location: [512, self.rb_iseq_location_t], - insns_info: [960, self.iseq_insn_info], - local_table: [1216, CType::Pointer.new { self.ID }], - catch_table: [1280, CType::Pointer.new { self.iseq_catch_table }], - parent_iseq: [1344, CType::Pointer.new { self.rb_iseq_struct }], - local_iseq: [1408, CType::Pointer.new { self.rb_iseq_struct }], - is_entries: [1472, CType::Pointer.new { self.iseq_inline_storage_entry }], - call_data: [1536, CType::Pointer.new { self.rb_call_data }], - variable: [1600, CType::Struct.new( + location: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), location)"), self.rb_iseq_location_t], + insns_info: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), insns_info)"), self.iseq_insn_info], + local_table: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table)"), CType::Pointer.new { self.ID }], + catch_table: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_table)"), CType::Pointer.new { self.iseq_catch_table }], + parent_iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), parent_iseq)"), CType::Pointer.new { self.rb_iseq_struct }], + local_iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_iseq)"), CType::Pointer.new { self.rb_iseq_struct }], + is_entries: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), is_entries)"), CType::Pointer.new { self.iseq_inline_storage_entry }], + call_data: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), call_data)"), CType::Pointer.new { self.rb_call_data }], + variable: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), variable)"), CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->variable)"), - flip_count: [0, self.rb_snum_t], - script_lines: [64, self.VALUE], - coverage: [128, self.VALUE], - pc2branchindex: [192, self.VALUE], - original_iseq: [256, CType::Pointer.new { self.VALUE }], + flip_count: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, flip_count)"), self.rb_snum_t], + script_lines: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, script_lines)"), self.VALUE], + coverage: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, coverage)"), self.VALUE], + pc2branchindex: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, pc2branchindex)"), self.VALUE], + original_iseq: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, original_iseq)"), CType::Pointer.new { self.VALUE }], )], - local_table_size: [1920, CType::Immediate.parse("unsigned int")], - ic_size: [1952, CType::Immediate.parse("unsigned int")], - ise_size: [1984, CType::Immediate.parse("unsigned int")], - ivc_size: [2016, CType::Immediate.parse("unsigned int")], - icvarc_size: [2048, CType::Immediate.parse("unsigned int")], - ci_size: [2080, CType::Immediate.parse("unsigned int")], - stack_max: [2112, CType::Immediate.parse("unsigned int")], - mark_bits: [2176, CType::Union.new( + local_table_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table_size)"), CType::Immediate.parse("unsigned int")], + ic_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ic_size)"), CType::Immediate.parse("unsigned int")], + ise_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ise_size)"), CType::Immediate.parse("unsigned int")], + ivc_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ivc_size)"), CType::Immediate.parse("unsigned int")], + icvarc_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), icvarc_size)"), CType::Immediate.parse("unsigned int")], + ci_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ci_size)"), CType::Immediate.parse("unsigned int")], + stack_max: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), stack_max)"), CType::Immediate.parse("unsigned int")], + mark_bits: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mark_bits)"), CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->mark_bits)"), list: CType::Pointer.new { self.iseq_bits_t }, single: self.iseq_bits_t, )], - catch_except_p: [2240, self._Bool], - builtin_inline_p: [2248, self._Bool], - outer_variables: [2304, CType::Pointer.new { self.rb_id_table }], - mandatory_only_iseq: [2368, CType::Pointer.new { self.rb_iseq_t }], - jit_func: [2432, CType::Immediate.parse("void *")], - total_calls: [2496, CType::Immediate.parse("unsigned long")], - jit_unit: [2560, CType::Pointer.new { self.rb_mjit_unit }], - yjit_payload: [2624, CType::Pointer.new { CType::Immediate.parse("void") }], + catch_except_p: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_except_p)"), self._Bool], + builtin_inline_p: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), builtin_inline_p)"), self._Bool], + outer_variables: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), outer_variables)"), CType::Pointer.new { self.rb_id_table }], + mandatory_only_iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mandatory_only_iseq)"), CType::Pointer.new { self.rb_iseq_t }], + jit_func: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)"), CType::Immediate.parse("void *")], + total_calls: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)"), CType::Immediate.parse("unsigned long")], + jit_unit: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_unit)"), CType::Pointer.new { self.rb_mjit_unit }], + yjit_payload: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), yjit_payload)"), CType::Pointer.new { CType::Immediate.parse("void") }], ) end def C.rb_iseq_location_t @rb_iseq_location_t ||= CType::Struct.new( "rb_iseq_location_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_location_struct)"), - pathobj: [0, self.VALUE, true], - base_label: [64, self.VALUE, true], - label: [128, self.VALUE, true], - first_lineno: [192, self.VALUE, true], - node_id: [256, CType::Immediate.parse("int")], - code_location: [288, self.rb_code_location_t], + pathobj: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), pathobj)"), self.VALUE, true], + base_label: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), base_label)"), self.VALUE, true], + label: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), label)"), self.VALUE, true], + first_lineno: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), first_lineno)"), self.VALUE, true], + node_id: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), node_id)"), CType::Immediate.parse("int")], + code_location: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), code_location)"), self.rb_code_location_t], ) end def C.rb_iseq_struct @rb_iseq_struct ||= CType::Struct.new( "rb_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_struct)"), - flags: [0, self.VALUE], - wrapper: [64, self.VALUE], - body: [128, CType::Pointer.new { self.rb_iseq_constant_body }], - aux: [192, CType::Union.new( + flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), flags)"), self.VALUE], + wrapper: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), wrapper)"), self.VALUE], + body: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), body)"), CType::Pointer.new { self.rb_iseq_constant_body }], + aux: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), aux)"), CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux)"), compile_data: CType::Pointer.new { self.iseq_compile_data }, loader: CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.loader)"), - obj: [0, self.VALUE], - index: [64, CType::Immediate.parse("int")], + obj: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, obj)"), self.VALUE], + index: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, index)"), CType::Immediate.parse("int")], ), exec: CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.exec)"), - local_hooks: [0, CType::Pointer.new { self.rb_hook_list_struct }], - global_trace_events: [64, self.rb_event_flag_t], + local_hooks: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, local_hooks)"), CType::Pointer.new { self.rb_hook_list_struct }], + global_trace_events: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, global_trace_events)"), self.rb_event_flag_t], ), )], ) @@ -500,9 +500,9 @@ def C.rb_iseq_t 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: [0, CType::Immediate.parse("uint32_t")], - class_serial: [64, self.rb_serial_t], - class_value: [128, self.VALUE], + index: [Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), index)"), CType::Immediate.parse("uint32_t")], + class_serial: [Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_serial)"), self.rb_serial_t], + class_value: [Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_value)"), self.VALUE], ) end @@ -514,7 +514,7 @@ def C.rb_method_definition_struct alias_count: [5, CType::BitField.new(27, 5)], complemented_count: [32, CType::BitField.new(28, 0)], no_redef_warning: [60, CType::BitField.new(1, 4)], - body: [64, CType::Union.new( + body: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), body)"), CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_method_definition_struct *)NULL)->body)"), iseq: self.rb_method_iseq_t, cfunc: self.rb_method_cfunc_t, @@ -524,16 +524,16 @@ def C.rb_method_definition_struct bmethod: self.rb_method_bmethod_t, optimized: self.rb_method_optimized_t, )], - original_id: [256, self.ID], - method_serial: [320, CType::Immediate.parse("uintptr_t")], + original_id: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), original_id)"), self.ID], + method_serial: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), method_serial)"), CType::Immediate.parse("uintptr_t")], ) end def C.rb_method_iseq_t @rb_method_iseq_t ||= CType::Struct.new( "rb_method_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_method_iseq_struct)"), - iseqptr: [0, CType::Pointer.new { self.rb_iseq_t }], - cref: [64, CType::Pointer.new { self.rb_cref_t }], + iseqptr: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), iseqptr)"), CType::Pointer.new { self.rb_iseq_t }], + cref: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), cref)"), CType::Pointer.new { self.rb_cref_t }], ) end @@ -544,26 +544,26 @@ def C.rb_method_type_t def C.rb_mjit_compile_info @rb_mjit_compile_info ||= CType::Struct.new( "rb_mjit_compile_info", Primitive.cexpr!("SIZEOF(struct rb_mjit_compile_info)"), - disable_ivar_cache: [0, self._Bool], - disable_exivar_cache: [8, self._Bool], - disable_send_cache: [16, self._Bool], - disable_inlining: [24, self._Bool], - disable_const_cache: [32, self._Bool], + disable_ivar_cache: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_ivar_cache)"), self._Bool], + disable_exivar_cache: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_exivar_cache)"), self._Bool], + disable_send_cache: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_send_cache)"), self._Bool], + disable_inlining: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_inlining)"), self._Bool], + disable_const_cache: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_const_cache)"), self._Bool], ) end def C.rb_mjit_unit @rb_mjit_unit ||= CType::Struct.new( "rb_mjit_unit", Primitive.cexpr!("SIZEOF(struct rb_mjit_unit)"), - unode: [0, self.ccan_list_node], - id: [128, CType::Immediate.parse("int")], - handle: [192, CType::Pointer.new { CType::Immediate.parse("void") }], - iseq: [256, CType::Pointer.new { self.rb_iseq_t }], - used_code_p: [320, self._Bool], - compact_p: [328, self._Bool], - compile_info: [336, self.rb_mjit_compile_info], - cc_entries: [384, CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], - cc_entries_size: [448, CType::Immediate.parse("unsigned int")], + unode: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), unode)"), self.ccan_list_node], + id: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), id)"), CType::Immediate.parse("int")], + handle: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), handle)"), CType::Pointer.new { CType::Immediate.parse("void") }], + iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), iseq)"), CType::Pointer.new { self.rb_iseq_t }], + used_code_p: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), used_code_p)"), self._Bool], + compact_p: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compact_p)"), self._Bool], + compile_info: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compile_info)"), self.rb_mjit_compile_info], + cc_entries: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries)"), CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], + cc_entries_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries_size)"), CType::Immediate.parse("unsigned int")], ) end diff --git a/mjit_compiler.c b/mjit_compiler.c index 2b518feffd5e82..5ed3aa5a68d877 100644 --- a/mjit_compiler.c +++ b/mjit_compiler.c @@ -164,6 +164,10 @@ has_cache_for_send(rb_execution_context_t *ec, VALUE self, VALUE cc_addr, VALUE extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); +// An offsetof implementation that works for unnamed struct and union. +// Multiplying 8 for compatibility with libclang's offsetof. +#define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8) + #define SIZEOF(type) RB_SIZE2NUM(sizeof(type)) #define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(0)) diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 06ece7d6fa5023..62ffeaaa3afd2f 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -207,11 +207,19 @@ def generate_node(node, sizeof_type: nil) # node.spelling is often empty for union, but we'd like to give it a name when it has one. buf = +"CType::#{node.kind.to_s.sub(/\A[a-z]/, &:upcase)}.new(\n" buf << " \"#{node.spelling}\", Primitive.cexpr!(\"SIZEOF(#{sizeof_type || node.type})\"),\n" - node.children.each do |child| + bit_fields_end = node.children.index { |c| c.bitwidth == -1 } || node.children.size # first non-bit field index + node.children.each_with_index do |child, i| field_builder = proc do |field, type| if node.kind == :struct to_ruby = @ruby_fields.fetch(node.spelling, []).include?(field) - " #{field}: [#{node.offsetof.fetch(field)}, #{type}#{', true' if to_ruby}],\n" + if child.bitwidth > 0 + # give up offsetof calculation for non-leading bit fields + offsetof = (i < bit_fields_end ? node.offsetof.fetch(field) : nil).inspect + else + off_type = sizeof_type || "(*((#{node.type} *)NULL))" + offsetof = "Primitive.cexpr!(\"OFFSETOF(#{off_type}, #{field})\")" + end + " #{field}: [#{offsetof}, #{type}#{', true' if to_ruby}],\n" else " #{field}: #{type},\n" end From dfc311c0b3b7d81afa7b153a473990959d46c7ad Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 22 Sep 2022 22:25:33 +0900 Subject: [PATCH 215/269] Swap the positions of offsetof and type --- lib/mjit/c_pointer.rb | 8 +- mjit_c.rb | 406 +++++++++++++++++++++--------------------- tool/mjit/bindgen.rb | 2 +- 3 files changed, 208 insertions(+), 208 deletions(-) diff --git a/lib/mjit/c_pointer.rb b/lib/mjit/c_pointer.rb index ce0948197d9856..7f64a8ac8f4906 100644 --- a/lib/mjit/c_pointer.rb +++ b/lib/mjit/c_pointer.rb @@ -7,7 +7,7 @@ module CPointer class Struct # @param name [String] # @param sizeof [Integer] - # @param members [Hash{ Symbol => [Integer, RubyVM::MJIT::CType::*] }] + # @param members [Hash{ Symbol => [RubyVM::MJIT::CType::*, Integer, TrueClass] }] def initialize(addr, sizeof, members) @addr = addr @sizeof = sizeof @@ -34,7 +34,7 @@ def -(struct) # TODO: remove this? # @param member [Symbol] def [](member) - offset, type = @members.fetch(member) + type, offset = @members.fetch(member) type.new(@addr + offset / 8) end @@ -43,7 +43,7 @@ def [](member) # @param member [Symbol] # @param value [Object] def []=(member, value) - offset, type = @members.fetch(member) + type, offset = @members.fetch(member) type[@addr + offset / 8] = value end @@ -61,7 +61,7 @@ def self.define(sizeof, members) super(addr, sizeof, members) end - members.each do |member, (offset, type, to_ruby)| + members.each do |member, (type, offset, to_ruby)| # Intelligent API that does automatic dereference define_method(member) do value = self[member] diff --git a/mjit_c.rb b/mjit_c.rb index d4edbf28250f12..5c84b1033106ab 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -179,63 +179,63 @@ def C.RB_BUILTIN def C.compile_branch @compile_branch ||= CType::Struct.new( "compile_branch", Primitive.cexpr!("SIZEOF(struct compile_branch)"), - stack_size: [Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), stack_size)"), CType::Immediate.parse("unsigned int")], - finish_p: [Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), finish_p)"), self._Bool], + stack_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), stack_size)")], + finish_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), finish_p)")], ) end def C.compile_status @compile_status ||= CType::Struct.new( "compile_status", Primitive.cexpr!("SIZEOF(struct compile_status)"), - success: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), success)"), self._Bool], - stack_size_for_pos: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), stack_size_for_pos)"), CType::Pointer.new { CType::Immediate.parse("int") }], - local_stack_p: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), local_stack_p)"), self._Bool], - is_entries: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), is_entries)"), CType::Pointer.new { self.iseq_inline_storage_entry }], - cc_entries_index: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), cc_entries_index)"), CType::Immediate.parse("int")], - compiled_iseq: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_iseq)"), CType::Pointer.new { self.rb_iseq_constant_body }], - compiled_id: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_id)"), CType::Immediate.parse("int")], - compile_info: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compile_info)"), CType::Pointer.new { self.rb_mjit_compile_info }], - merge_ivar_guards_p: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), merge_ivar_guards_p)"), self._Bool], - ivar_serial: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), ivar_serial)"), self.rb_serial_t], - max_ivar_index: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), max_ivar_index)"), CType::Immediate.parse("size_t")], - inlined_iseqs: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inlined_iseqs)"), CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], - inline_context: [Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inline_context)"), self.inlined_call_context], + success: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), success)")], + stack_size_for_pos: [CType::Pointer.new { CType::Immediate.parse("int") }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), stack_size_for_pos)")], + local_stack_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), local_stack_p)")], + is_entries: [CType::Pointer.new { self.iseq_inline_storage_entry }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), is_entries)")], + cc_entries_index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), cc_entries_index)")], + compiled_iseq: [CType::Pointer.new { self.rb_iseq_constant_body }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_iseq)")], + 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)")], ) end def C.inlined_call_context @inlined_call_context ||= CType::Struct.new( "inlined_call_context", Primitive.cexpr!("SIZEOF(struct inlined_call_context)"), - orig_argc: [Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), orig_argc)"), CType::Immediate.parse("int")], - me: [Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), me)"), self.VALUE], - param_size: [Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), param_size)"), CType::Immediate.parse("int")], - local_size: [Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), local_size)"), CType::Immediate.parse("int")], + orig_argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), orig_argc)")], + me: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), me)")], + param_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), param_size)")], + local_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), local_size)")], ) end def C.iseq_inline_constant_cache @iseq_inline_constant_cache ||= CType::Struct.new( "iseq_inline_constant_cache", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache)"), - entry: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), entry)"), CType::Pointer.new { self.iseq_inline_constant_cache_entry }], - segments: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), segments)"), CType::Pointer.new { self.ID }], + entry: [CType::Pointer.new { self.iseq_inline_constant_cache_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), entry)")], + segments: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), segments)")], ) end def C.iseq_inline_constant_cache_entry @iseq_inline_constant_cache_entry ||= CType::Struct.new( "iseq_inline_constant_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache_entry)"), - flags: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), flags)"), self.VALUE], - value: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), value)"), self.VALUE], - _unused1: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused1)"), self.VALUE], - _unused2: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused2)"), self.VALUE], - ic_cref: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), ic_cref)"), CType::Pointer.new { self.rb_cref_t }], + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), flags)")], + value: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), value)")], + _unused1: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused1)")], + _unused2: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused2)")], + ic_cref: [CType::Pointer.new { self.rb_cref_t }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), ic_cref)")], ) end 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: [Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), entry)"), CType::Pointer.new { self.rb_iv_index_tbl_entry }], + entry: [CType::Pointer.new { self.rb_iv_index_tbl_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), entry)")], ) end @@ -244,8 +244,8 @@ def C.iseq_inline_storage_entry "iseq_inline_storage_entry", Primitive.cexpr!("SIZEOF(union iseq_inline_storage_entry)"), once: CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((union iseq_inline_storage_entry *)NULL)->once)"), - running_thread: [Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, running_thread)"), CType::Pointer.new { self.rb_thread_struct }], - value: [Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, value)"), self.VALUE], + running_thread: [CType::Pointer.new { self.rb_thread_struct }, Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, running_thread)")], + value: [self.VALUE, Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, value)")], ), ic_cache: self.iseq_inline_constant_cache, iv_cache: self.iseq_inline_iv_cache_entry, @@ -255,132 +255,132 @@ def C.iseq_inline_storage_entry def C.mjit_options @mjit_options ||= CType::Struct.new( "mjit_options", Primitive.cexpr!("SIZEOF(struct mjit_options)"), - on: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), on)"), self._Bool], - save_temps: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), save_temps)"), self._Bool], - warnings: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), warnings)"), self._Bool], - debug: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug)"), self._Bool], - debug_flags: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug_flags)"), CType::Pointer.new { CType::Immediate.parse("char") }], - wait: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), wait)"), self._Bool], - min_calls: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), min_calls)"), CType::Immediate.parse("unsigned int")], - verbose: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), verbose)"), CType::Immediate.parse("int")], - max_cache_size: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), max_cache_size)"), CType::Immediate.parse("int")], - pause: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), pause)"), self._Bool], - custom: [Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), custom)"), self._Bool], + on: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), on)")], + save_temps: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), save_temps)")], + warnings: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), warnings)")], + debug: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug)")], + debug_flags: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug_flags)")], + wait: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), wait)")], + min_calls: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), min_calls)")], + verbose: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), verbose)")], + max_cache_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), max_cache_size)")], + pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), pause)")], + custom: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), custom)")], ) end def C.rb_builtin_function @rb_builtin_function ||= CType::Struct.new( "rb_builtin_function", Primitive.cexpr!("SIZEOF(struct rb_builtin_function)"), - func_ptr: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), func_ptr)"), CType::Pointer.new { CType::Immediate.parse("void") }], - argc: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), argc)"), CType::Immediate.parse("int")], - index: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), index)"), CType::Immediate.parse("int")], - name: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), name)"), CType::Pointer.new { CType::Immediate.parse("char") }], - compiler: [Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), compiler)"), CType::Immediate.parse("void *")], + func_ptr: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), func_ptr)")], + argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), argc)")], + index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), index)")], + name: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), name)")], + compiler: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), compiler)")], ) end def C.rb_call_data @rb_call_data ||= CType::Struct.new( "rb_call_data", Primitive.cexpr!("SIZEOF(struct rb_call_data)"), - ci: [Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), ci)"), CType::Pointer.new { self.rb_callinfo }], - cc: [Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), cc)"), CType::Pointer.new { self.rb_callcache }], + ci: [CType::Pointer.new { self.rb_callinfo }, Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), ci)")], + cc: [CType::Pointer.new { self.rb_callcache }, Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), cc)")], ) end def C.rb_callable_method_entry_struct @rb_callable_method_entry_struct ||= CType::Struct.new( "rb_callable_method_entry_struct", Primitive.cexpr!("SIZEOF(struct rb_callable_method_entry_struct)"), - flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), flags)"), self.VALUE], - defined_class: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), defined_class)"), self.VALUE], - def: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), def)"), CType::Pointer.new { self.rb_method_definition_struct }], - called_id: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), called_id)"), self.ID], - owner: [Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), owner)"), self.VALUE], + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), flags)")], + defined_class: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), defined_class)")], + def: [CType::Pointer.new { self.rb_method_definition_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), def)")], + called_id: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), called_id)")], + owner: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), owner)")], ) end def C.rb_callcache @rb_callcache ||= CType::Struct.new( "rb_callcache", Primitive.cexpr!("SIZEOF(struct rb_callcache)"), - flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), flags)"), self.VALUE], - klass: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), klass)"), self.VALUE], - cme_: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), cme_)"), CType::Pointer.new { self.rb_callable_method_entry_struct }], - call_: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), call_)"), self.vm_call_handler], - aux_: [Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)"), CType::Union.new( + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), flags)")], + klass: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), klass)")], + cme_: [CType::Pointer.new { self.rb_callable_method_entry_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), cme_)")], + 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"), method_missing_reason: self.method_missing_reason, v: self.VALUE, - )], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)")], ) end def C.rb_callinfo @rb_callinfo ||= CType::Struct.new( "rb_callinfo", Primitive.cexpr!("SIZEOF(struct rb_callinfo)"), - flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flags)"), self.VALUE], - kwarg: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), kwarg)"), CType::Pointer.new { self.rb_callinfo_kwarg }], - mid: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), mid)"), self.VALUE], - flag: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flag)"), self.VALUE], - argc: [Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), argc)"), self.VALUE], + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flags)")], + kwarg: [CType::Pointer.new { self.rb_callinfo_kwarg }, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), kwarg)")], + mid: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), mid)")], + flag: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flag)")], + argc: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), argc)")], ) end def C.rb_control_frame_t @rb_control_frame_t ||= CType::Struct.new( "rb_control_frame_struct", Primitive.cexpr!("SIZEOF(struct rb_control_frame_struct)"), - pc: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), pc)"), CType::Pointer.new { self.VALUE }], - sp: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), sp)"), CType::Pointer.new { self.VALUE }], - iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), iseq)"), CType::Pointer.new { self.rb_iseq_t }], - self: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), self)"), self.VALUE], - ep: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), ep)"), CType::Pointer.new { self.VALUE }], - block_code: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), block_code)"), CType::Pointer.new { CType::Immediate.parse("void") }], - __bp__: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), __bp__)"), CType::Pointer.new { self.VALUE }], - jit_return: [Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), jit_return)"), CType::Pointer.new { CType::Immediate.parse("void") }], + pc: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), pc)")], + sp: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), sp)")], + iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), iseq)")], + self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), self)")], + ep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), ep)")], + block_code: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), block_code)")], + __bp__: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), __bp__)")], + jit_return: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), jit_return)")], ) end def C.rb_cref_t @rb_cref_t ||= CType::Struct.new( "rb_cref_struct", Primitive.cexpr!("SIZEOF(struct rb_cref_struct)"), - flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), flags)"), self.VALUE], - refinements: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), refinements)"), self.VALUE], - klass_or_self: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), klass_or_self)"), self.VALUE], - next: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), next)"), CType::Pointer.new { self.rb_cref_struct }], - scope_visi: [Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), scope_visi)"), self.rb_scope_visibility_t], + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), flags)")], + refinements: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), refinements)")], + klass_or_self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), klass_or_self)")], + next: [CType::Pointer.new { self.rb_cref_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), next)")], + scope_visi: [self.rb_scope_visibility_t, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), scope_visi)")], ) end def C.rb_execution_context_struct @rb_execution_context_struct ||= CType::Struct.new( "rb_execution_context_struct", Primitive.cexpr!("SIZEOF(struct rb_execution_context_struct)"), - vm_stack: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack)"), CType::Pointer.new { self.VALUE }], - vm_stack_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack_size)"), CType::Immediate.parse("size_t")], - cfp: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), cfp)"), CType::Pointer.new { self.rb_control_frame_t }], - tag: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), tag)"), CType::Pointer.new { self.rb_vm_tag }], - interrupt_flag: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_flag)"), self.rb_atomic_t], - interrupt_mask: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_mask)"), self.rb_atomic_t], - fiber_ptr: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), fiber_ptr)"), CType::Pointer.new { self.rb_fiber_t }], - thread_ptr: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), thread_ptr)"), CType::Pointer.new { self.rb_thread_struct }], - local_storage: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage)"), CType::Pointer.new { self.rb_id_table }], - local_storage_recursive_hash: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash)"), self.VALUE], - local_storage_recursive_hash_for_trace: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash_for_trace)"), self.VALUE], - root_lep: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_lep)"), CType::Pointer.new { self.VALUE }], - root_svar: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_svar)"), self.VALUE], - ensure_list: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), ensure_list)"), CType::Pointer.new { self.rb_ensure_list_t }], - trace_arg: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), trace_arg)"), CType::Pointer.new { self.rb_trace_arg_struct }], - errinfo: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), errinfo)"), self.VALUE], - passed_block_handler: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), passed_block_handler)"), self.VALUE], - raised_flag: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), raised_flag)"), CType::Immediate.parse("uint8_t")], - method_missing_reason: [nil, self.method_missing_reason], - private_const_reference: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), private_const_reference)"), self.VALUE], - machine: [Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), machine)"), CType::Struct.new( + vm_stack: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack)")], + vm_stack_size: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack_size)")], + cfp: [CType::Pointer.new { self.rb_control_frame_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), cfp)")], + tag: [CType::Pointer.new { self.rb_vm_tag }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), tag)")], + interrupt_flag: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_flag)")], + interrupt_mask: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_mask)")], + fiber_ptr: [CType::Pointer.new { self.rb_fiber_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), fiber_ptr)")], + thread_ptr: [CType::Pointer.new { self.rb_thread_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), thread_ptr)")], + local_storage: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage)")], + local_storage_recursive_hash: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash)")], + local_storage_recursive_hash_for_trace: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash_for_trace)")], + root_lep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_lep)")], + root_svar: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_svar)")], + ensure_list: [CType::Pointer.new { self.rb_ensure_list_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), ensure_list)")], + trace_arg: [CType::Pointer.new { self.rb_trace_arg_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), trace_arg)")], + errinfo: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), errinfo)")], + passed_block_handler: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), passed_block_handler)")], + raised_flag: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), raised_flag)")], + method_missing_reason: [self.method_missing_reason, nil], + private_const_reference: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), private_const_reference)")], + machine: [CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_execution_context_struct *)NULL)->machine)"), - stack_start: [Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_start)"), CType::Pointer.new { self.VALUE }], - stack_end: [Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_end)"), CType::Pointer.new { self.VALUE }], - stack_maxsize: [Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_maxsize)"), CType::Immediate.parse("size_t")], - regs: [Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, regs)"), CType::Immediate.parse("int")], - )], + stack_start: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_start)")], + stack_end: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_end)")], + stack_maxsize: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_maxsize)")], + regs: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, regs)")], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), machine)")], ) end @@ -391,105 +391,105 @@ def C.rb_execution_context_t def C.rb_iseq_constant_body @rb_iseq_constant_body ||= CType::Struct.new( "rb_iseq_constant_body", Primitive.cexpr!("SIZEOF(struct rb_iseq_constant_body)"), - type: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), type)"), self.rb_iseq_type], - iseq_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_size)"), CType::Immediate.parse("unsigned int")], - iseq_encoded: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_encoded)"), CType::Pointer.new { self.VALUE }], - param: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), param)"), CType::Struct.new( + type: [self.rb_iseq_type, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), type)")], + iseq_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_size)")], + iseq_encoded: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_encoded)")], + param: [CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param)"), - flags: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, flags)"), CType::Struct.new( + flags: [CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param.flags)"), - has_lead: [0, CType::BitField.new(1, 0)], - has_opt: [1, CType::BitField.new(1, 1)], - has_rest: [2, CType::BitField.new(1, 2)], - has_post: [3, CType::BitField.new(1, 3)], - has_kw: [4, CType::BitField.new(1, 4)], - has_kwrest: [5, CType::BitField.new(1, 5)], - has_block: [6, CType::BitField.new(1, 6)], - ambiguous_param0: [7, CType::BitField.new(1, 7)], - accepts_no_kwarg: [8, CType::BitField.new(1, 0)], - ruby2_keywords: [9, CType::BitField.new(1, 1)], - )], - size: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, size)"), CType::Immediate.parse("unsigned int")], - lead_num: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, lead_num)"), CType::Immediate.parse("int")], - opt_num: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_num)"), CType::Immediate.parse("int")], - rest_start: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, rest_start)"), CType::Immediate.parse("int")], - post_start: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_start)"), CType::Immediate.parse("int")], - post_num: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_num)"), CType::Immediate.parse("int")], - block_start: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, block_start)"), CType::Immediate.parse("int")], - opt_table: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_table)"), CType::Pointer.new { self.VALUE }], - keyword: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, keyword)"), CType::Pointer.new { self.rb_iseq_param_keyword }], - )], - location: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), location)"), self.rb_iseq_location_t], - insns_info: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), insns_info)"), self.iseq_insn_info], - local_table: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table)"), CType::Pointer.new { self.ID }], - catch_table: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_table)"), CType::Pointer.new { self.iseq_catch_table }], - parent_iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), parent_iseq)"), CType::Pointer.new { self.rb_iseq_struct }], - local_iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_iseq)"), CType::Pointer.new { self.rb_iseq_struct }], - is_entries: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), is_entries)"), CType::Pointer.new { self.iseq_inline_storage_entry }], - call_data: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), call_data)"), CType::Pointer.new { self.rb_call_data }], - variable: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), variable)"), CType::Struct.new( + has_lead: [CType::BitField.new(1, 0), 0], + has_opt: [CType::BitField.new(1, 1), 1], + has_rest: [CType::BitField.new(1, 2), 2], + has_post: [CType::BitField.new(1, 3), 3], + has_kw: [CType::BitField.new(1, 4), 4], + has_kwrest: [CType::BitField.new(1, 5), 5], + has_block: [CType::BitField.new(1, 6), 6], + ambiguous_param0: [CType::BitField.new(1, 7), 7], + accepts_no_kwarg: [CType::BitField.new(1, 0), 8], + ruby2_keywords: [CType::BitField.new(1, 1), 9], + ), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, flags)")], + size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, size)")], + lead_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, lead_num)")], + opt_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_num)")], + rest_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, rest_start)")], + post_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_start)")], + post_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_num)")], + block_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, block_start)")], + opt_table: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_table)")], + keyword: [CType::Pointer.new { self.rb_iseq_param_keyword }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, keyword)")], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), param)")], + location: [self.rb_iseq_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), location)")], + insns_info: [self.iseq_insn_info, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), insns_info)")], + local_table: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table)")], + catch_table: [CType::Pointer.new { self.iseq_catch_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_table)")], + parent_iseq: [CType::Pointer.new { self.rb_iseq_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), parent_iseq)")], + local_iseq: [CType::Pointer.new { self.rb_iseq_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_iseq)")], + is_entries: [CType::Pointer.new { self.iseq_inline_storage_entry }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), is_entries)")], + call_data: [CType::Pointer.new { self.rb_call_data }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), call_data)")], + variable: [CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->variable)"), - flip_count: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, flip_count)"), self.rb_snum_t], - script_lines: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, script_lines)"), self.VALUE], - coverage: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, coverage)"), self.VALUE], - pc2branchindex: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, pc2branchindex)"), self.VALUE], - original_iseq: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, original_iseq)"), CType::Pointer.new { self.VALUE }], - )], - local_table_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table_size)"), CType::Immediate.parse("unsigned int")], - ic_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ic_size)"), CType::Immediate.parse("unsigned int")], - ise_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ise_size)"), CType::Immediate.parse("unsigned int")], - ivc_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ivc_size)"), CType::Immediate.parse("unsigned int")], - icvarc_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), icvarc_size)"), CType::Immediate.parse("unsigned int")], - ci_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ci_size)"), CType::Immediate.parse("unsigned int")], - stack_max: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), stack_max)"), CType::Immediate.parse("unsigned int")], - mark_bits: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mark_bits)"), CType::Union.new( + flip_count: [self.rb_snum_t, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, flip_count)")], + script_lines: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, script_lines)")], + coverage: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, coverage)")], + pc2branchindex: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, pc2branchindex)")], + original_iseq: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, original_iseq)")], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), variable)")], + local_table_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table_size)")], + ic_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ic_size)")], + ise_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ise_size)")], + ivc_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ivc_size)")], + icvarc_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), icvarc_size)")], + ci_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ci_size)")], + stack_max: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), stack_max)")], + mark_bits: [CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->mark_bits)"), list: CType::Pointer.new { self.iseq_bits_t }, single: self.iseq_bits_t, - )], - catch_except_p: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_except_p)"), self._Bool], - builtin_inline_p: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), builtin_inline_p)"), self._Bool], - outer_variables: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), outer_variables)"), CType::Pointer.new { self.rb_id_table }], - mandatory_only_iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mandatory_only_iseq)"), CType::Pointer.new { self.rb_iseq_t }], - jit_func: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)"), CType::Immediate.parse("void *")], - total_calls: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)"), CType::Immediate.parse("unsigned long")], - jit_unit: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_unit)"), CType::Pointer.new { self.rb_mjit_unit }], - yjit_payload: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), yjit_payload)"), CType::Pointer.new { CType::Immediate.parse("void") }], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mark_bits)")], + catch_except_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_except_p)")], + builtin_inline_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), builtin_inline_p)")], + outer_variables: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), outer_variables)")], + mandatory_only_iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mandatory_only_iseq)")], + jit_func: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)")], + total_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)")], + jit_unit: [CType::Pointer.new { self.rb_mjit_unit }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_unit)")], + yjit_payload: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), yjit_payload)")], ) end def C.rb_iseq_location_t @rb_iseq_location_t ||= CType::Struct.new( "rb_iseq_location_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_location_struct)"), - pathobj: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), pathobj)"), self.VALUE, true], - base_label: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), base_label)"), self.VALUE, true], - label: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), label)"), self.VALUE, true], - first_lineno: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), first_lineno)"), self.VALUE, true], - node_id: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), node_id)"), CType::Immediate.parse("int")], - code_location: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), code_location)"), self.rb_code_location_t], + pathobj: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), pathobj)"), true], + base_label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), base_label)"), true], + label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), label)"), true], + first_lineno: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), first_lineno)"), true], + node_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), node_id)")], + code_location: [self.rb_code_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), code_location)")], ) end def C.rb_iseq_struct @rb_iseq_struct ||= CType::Struct.new( "rb_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_struct)"), - flags: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), flags)"), self.VALUE], - wrapper: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), wrapper)"), self.VALUE], - body: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), body)"), CType::Pointer.new { self.rb_iseq_constant_body }], - aux: [Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), aux)"), CType::Union.new( + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), flags)")], + wrapper: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), wrapper)")], + body: [CType::Pointer.new { self.rb_iseq_constant_body }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), body)")], + aux: [CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux)"), compile_data: CType::Pointer.new { self.iseq_compile_data }, loader: CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.loader)"), - obj: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, obj)"), self.VALUE], - index: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, index)"), CType::Immediate.parse("int")], + obj: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, obj)")], + index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, index)")], ), exec: CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.exec)"), - local_hooks: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, local_hooks)"), CType::Pointer.new { self.rb_hook_list_struct }], - global_trace_events: [Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, global_trace_events)"), self.rb_event_flag_t], + local_hooks: [CType::Pointer.new { self.rb_hook_list_struct }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, local_hooks)")], + global_trace_events: [self.rb_event_flag_t, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, global_trace_events)")], ), - )], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), aux)")], ) end @@ -500,21 +500,21 @@ def C.rb_iseq_t 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: [Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), index)"), CType::Immediate.parse("uint32_t")], - class_serial: [Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_serial)"), self.rb_serial_t], - class_value: [Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_value)"), self.VALUE], + 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)")], ) end def C.rb_method_definition_struct @rb_method_definition_struct ||= CType::Struct.new( "rb_method_definition_struct", Primitive.cexpr!("SIZEOF(struct rb_method_definition_struct)"), - type: [0, self.rb_method_type_t], - iseq_overload: [4, CType::BitField.new(1, 4)], - alias_count: [5, CType::BitField.new(27, 5)], - complemented_count: [32, CType::BitField.new(28, 0)], - no_redef_warning: [60, CType::BitField.new(1, 4)], - body: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), body)"), CType::Union.new( + type: [self.rb_method_type_t, 0], + iseq_overload: [CType::BitField.new(1, 4), 4], + alias_count: [CType::BitField.new(27, 5), 5], + complemented_count: [CType::BitField.new(28, 0), 32], + no_redef_warning: [CType::BitField.new(1, 4), 60], + body: [CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_method_definition_struct *)NULL)->body)"), iseq: self.rb_method_iseq_t, cfunc: self.rb_method_cfunc_t, @@ -523,17 +523,17 @@ def C.rb_method_definition_struct refined: self.rb_method_refined_t, bmethod: self.rb_method_bmethod_t, optimized: self.rb_method_optimized_t, - )], - original_id: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), original_id)"), self.ID], - method_serial: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), method_serial)"), CType::Immediate.parse("uintptr_t")], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), body)")], + original_id: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), original_id)")], + method_serial: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), method_serial)")], ) end def C.rb_method_iseq_t @rb_method_iseq_t ||= CType::Struct.new( "rb_method_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_method_iseq_struct)"), - iseqptr: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), iseqptr)"), CType::Pointer.new { self.rb_iseq_t }], - cref: [Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), cref)"), CType::Pointer.new { self.rb_cref_t }], + iseqptr: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), iseqptr)")], + cref: [CType::Pointer.new { self.rb_cref_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), cref)")], ) end @@ -544,26 +544,26 @@ def C.rb_method_type_t def C.rb_mjit_compile_info @rb_mjit_compile_info ||= CType::Struct.new( "rb_mjit_compile_info", Primitive.cexpr!("SIZEOF(struct rb_mjit_compile_info)"), - disable_ivar_cache: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_ivar_cache)"), self._Bool], - disable_exivar_cache: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_exivar_cache)"), self._Bool], - disable_send_cache: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_send_cache)"), self._Bool], - disable_inlining: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_inlining)"), self._Bool], - disable_const_cache: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_const_cache)"), self._Bool], + disable_ivar_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_ivar_cache)")], + disable_exivar_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_exivar_cache)")], + disable_send_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_send_cache)")], + disable_inlining: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_inlining)")], + disable_const_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_const_cache)")], ) end def C.rb_mjit_unit @rb_mjit_unit ||= CType::Struct.new( "rb_mjit_unit", Primitive.cexpr!("SIZEOF(struct rb_mjit_unit)"), - unode: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), unode)"), self.ccan_list_node], - id: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), id)"), CType::Immediate.parse("int")], - handle: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), handle)"), CType::Pointer.new { CType::Immediate.parse("void") }], - iseq: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), iseq)"), CType::Pointer.new { self.rb_iseq_t }], - used_code_p: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), used_code_p)"), self._Bool], - compact_p: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compact_p)"), self._Bool], - compile_info: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compile_info)"), self.rb_mjit_compile_info], - cc_entries: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries)"), CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], - cc_entries_size: [Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries_size)"), CType::Immediate.parse("unsigned int")], + unode: [self.ccan_list_node, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), unode)")], + id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), id)")], + handle: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), handle)")], + iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), iseq)")], + used_code_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), used_code_p)")], + compact_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compact_p)")], + compile_info: [self.rb_mjit_compile_info, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compile_info)")], + cc_entries: [CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries)")], + cc_entries_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries_size)")], ) end diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 62ffeaaa3afd2f..d6431f8c4059f2 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -219,7 +219,7 @@ def generate_node(node, sizeof_type: nil) off_type = sizeof_type || "(*((#{node.type} *)NULL))" offsetof = "Primitive.cexpr!(\"OFFSETOF(#{off_type}, #{field})\")" end - " #{field}: [#{offsetof}, #{type}#{', true' if to_ruby}],\n" + " #{field}: [#{type}, #{offsetof}#{', true' if to_ruby}],\n" else " #{field}: #{type},\n" end From 0c9dc01a2a9a082843b287d30a18c6c67d79de51 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 22 Sep 2022 22:32:49 +0900 Subject: [PATCH 216/269] Skip struct fields whose output differs across different environments --- lib/mjit/c_type.rb | 4 ++-- mjit_c.rb | 3 --- tool/mjit/bindgen.rb | 32 ++++++++++++++++---------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/lib/mjit/c_type.rb b/lib/mjit/c_type.rb index 8ccdfcc4d34824..9e45d8d41c5379 100644 --- a/lib/mjit/c_type.rb +++ b/lib/mjit/c_type.rb @@ -46,9 +46,9 @@ def self.parse(ctype) new(Fiddle::Importer.parse_ctype(ctype)) end - def self.find(size, unsigned) + def self.find(size, signed) fiddle_type = TYPE_MAP.fetch(size) - fiddle_type = -fiddle_type if unsigned + fiddle_type = -fiddle_type unless signed new(fiddle_type) end diff --git a/mjit_c.rb b/mjit_c.rb index 5c84b1033106ab..6b316860bee92f 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -372,14 +372,12 @@ def C.rb_execution_context_struct errinfo: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), errinfo)")], passed_block_handler: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), passed_block_handler)")], raised_flag: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), raised_flag)")], - method_missing_reason: [self.method_missing_reason, nil], private_const_reference: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), private_const_reference)")], machine: [CType::Struct.new( "", Primitive.cexpr!("SIZEOF(((struct rb_execution_context_struct *)NULL)->machine)"), stack_start: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_start)")], stack_end: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_end)")], stack_maxsize: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_maxsize)")], - regs: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, regs)")], ), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), machine)")], ) end @@ -454,7 +452,6 @@ def C.rb_iseq_constant_body jit_func: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)")], total_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)")], jit_unit: [CType::Pointer.new { self.rb_mjit_unit }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_unit)")], - yjit_payload: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), yjit_payload)")], ) end diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index d6431f8c4059f2..56369c2d9844de 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -12,20 +12,9 @@ abort "Usage: #{$0} BUILD_DIR" end -if Fiddle::SIZEOF_VOIDP == 8 - arch_bits = 64 -else - arch_bits = 32 -end - # Help ffi-clang find libclang -if arch_bits == 64 - # apt install libclang1 - ENV['LIBCLANG'] ||= Dir.glob("/lib/#{RUBY_PLATFORM}-gnu/libclang-*.so*").grep_v(/-cpp/).sort.last -else - # apt install libclang1:i386 - ENV['LIBCLANG'] ||= Dir.glob("/lib/i386-linux-gnu/libclang-*.so*").sort.last -end +# Hint: apt install libclang1 +ENV['LIBCLANG'] ||= Dir.glob("/lib/#{RUBY_PLATFORM}-gnu/libclang-*.so*").grep_v(/-cpp/).sort.last require 'ffi/clang' class Node < Struct.new( @@ -116,14 +105,16 @@ class BindingGenerator # @param ints [Array] # @param types [Array] # @param dynamic_types [Array] #ifdef-dependent immediate types, which need Primitive.cexpr! for type detection + # @param skip_fields [Hash{ Symbol => Array }] Struct fields that are skipped from bindgen # @param ruby_fields [Hash{ Symbol => Array }] Struct VALUE fields that are considered Ruby objects - def initialize(src_path:, uses:, ints:, types:, dynamic_types:, ruby_fields:) + def initialize(src_path:, uses:, ints:, types:, dynamic_types:, skip_fields:, ruby_fields:) @preamble, @postamble = split_ambles(src_path) @src = String.new @uses = uses.sort @ints = ints.sort @types = types.sort @dynamic_types = dynamic_types.sort + @skip_fields = skip_fields.transform_keys(&:to_s) @ruby_fields = ruby_fields.transform_keys(&:to_s) @references = Set.new end @@ -209,12 +200,16 @@ def generate_node(node, sizeof_type: nil) buf << " \"#{node.spelling}\", Primitive.cexpr!(\"SIZEOF(#{sizeof_type || node.type})\"),\n" bit_fields_end = node.children.index { |c| c.bitwidth == -1 } || node.children.size # first non-bit field index node.children.each_with_index do |child, i| + skip_type = sizeof_type&.gsub(/\(\(struct ([^\)]+) \*\)NULL\)->/, '\1.') || node.spelling + next if @skip_fields.fetch(skip_type, []).include?(child.spelling) field_builder = proc do |field, type| if node.kind == :struct to_ruby = @ruby_fields.fetch(node.spelling, []).include?(field) if child.bitwidth > 0 - # give up offsetof calculation for non-leading bit fields - offsetof = (i < bit_fields_end ? node.offsetof.fetch(field) : nil).inspect + if bit_fields_end <= i # give up offsetof calculation for non-leading bit fields + raise "non-leading bit fields are not supported. consider including '#{field}' in skip_fields." + end + offsetof = node.offsetof.fetch(field) else off_type = sizeof_type || "(*((#{node.type} *)NULL))" offsetof = "Primitive.cexpr!(\"OFFSETOF(#{off_type}, #{field})\")" @@ -380,6 +375,11 @@ def push_target(target) dynamic_types: %w[ VALUE ], + skip_fields: { + 'rb_execution_context_struct.machine': %w[regs], # differs between macOS and Linux + rb_execution_context_struct: %w[method_missing_reason], # non-leading bit fields not supported + rb_iseq_constant_body: %w[yjit_payload], # conditionally defined + }, ruby_fields: { rb_iseq_location_struct: %w[ base_label From 4b97f1e5256336e259e898c52c30817e0639a42e Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Thu, 22 Sep 2022 14:47:26 -0700 Subject: [PATCH 217/269] YJIT: Refactor into gen_push_frame (#6412) This refactors the "push frame" operation common to both gen_send_iseq and gen_send_cfunc into its own method. This allows that logic to live in one place. --- yjit/src/codegen.rs | 263 +++++++++++++++++++++++++++----------------- 1 file changed, 161 insertions(+), 102 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index a61795f5d42c97..15ff2a467f2636 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -3998,6 +3998,135 @@ unsafe extern "C" fn build_kwhash(ci: *const rb_callinfo, sp: *const VALUE) -> V hash } +enum BlockHandler { + None, + CurrentFrame, +} + +struct ControlFrame { + recv: Opnd, + sp: Opnd, + iseq: Option, + pc: Option, + frame_type: u32, + block_handler: BlockHandler, + cme: *const rb_callable_method_entry_t, + local_size: i32 +} + +// Codegen performing a similar (but not identical) function to vm_push_frame +// +// This will generate the code to: +// * initialize locals to Qnil +// * push the environment (cme, block handler, frame type) +// * push a new CFP +// * save the new CFP to ec->cfp +// +// Notes: +// * Provided sp should point to the new frame's sp, immediately following locals and the environment +// * At entry, CFP points to the caller (not callee) frame +// * At exit, ec->cfp is updated to the pushed CFP +// * CFP and SP registers are updated only if switch_in_jit is set +// * Stack overflow is not checked (should be done by the caller) +// * Interrupts are not checked (should be done by the caller) +fn gen_push_frame( + jit: &mut JITState, + ctx: &mut Context, + asm: &mut Assembler, + set_pc_cfp: bool, // if true CFP and SP will be switched to the callee + frame: ControlFrame, +) { + assert!(frame.local_size >= 0); + + let sp = frame.sp; + + let num_locals = frame.local_size; + if num_locals > 0 { + asm.comment("initialize locals"); + + // Initialize local variables to Qnil + for i in 0..num_locals { + let offs = (SIZEOF_VALUE as i32) * (i - num_locals - 3); + asm.store(Opnd::mem(64, sp, offs), Qnil.into()); + } + } + + asm.comment("push cme, block handler, frame type"); + + // Write method entry at sp[-3] + // sp[-3] = me; + // Use compile time cme. It's assumed to be valid because we are notified when + // any cme we depend on become outdated. See yjit_method_lookup_change(). + asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -3), VALUE::from(frame.cme).into()); + + // Write block handler at sp[-2] + // sp[-2] = block_handler; + let block_handler: Opnd = match frame.block_handler { + BlockHandler::None => { + VM_BLOCK_HANDLER_NONE.into() + }, + BlockHandler::CurrentFrame => { + let cfp_self = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)); + asm.or(cfp_self, Opnd::Imm(1)) + }, + }; + asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2), block_handler); + + // Write env flags at sp[-1] + // sp[-1] = frame_type; + asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -1), frame.frame_type.into()); + + // Allocate a new CFP (ec->cfp--) + fn cfp_opnd(offset: i32) -> Opnd { + Opnd::mem(64, CFP, offset - (RUBY_SIZEOF_CONTROL_FRAME as i32)) + } + + // Setup the new frame + // *cfp = (const struct rb_control_frame_struct) { + // .pc = , + // .sp = sp, + // .iseq = , + // .self = recv, + // .ep = , + // .block_code = 0, + // .__bp__ = sp, + // }; + asm.comment("push callee control frame"); + + // For an iseq call PC may be None, in which case we will not set PC and will allow jitted code + // to set it as necessary. + let pc = if let Some(pc) = frame.pc { + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_PC), pc.into()); + }; + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_BP), sp); + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_SP), sp); + let iseq: Opnd = if let Some(iseq) = frame.iseq { + VALUE::from(iseq).into() + } else { + 0.into() + }; + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_ISEQ), iseq); + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_SELF), frame.recv); + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_BLOCK_CODE), 0.into()); + + if set_pc_cfp { + // Saving SP before calculating ep avoids a dependency on a register + // However this must be done after referencing frame.recv, which may be SP-relative + asm.mov(SP, sp); + } + let ep = asm.sub(sp, SIZEOF_VALUE.into()); + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_EP), ep); + + asm.comment("switch to new CFP"); + let new_cfp = asm.lea(cfp_opnd(0)); + if set_pc_cfp { + asm.mov(CFP, new_cfp); + asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP); + } else { + asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), new_cfp); + } +} + fn gen_send_cfunc( jit: &mut JITState, ctx: &mut Context, @@ -4103,60 +4232,27 @@ fn gen_send_cfunc( // sp += 3 let sp = asm.lea(ctx.sp_opnd((SIZEOF_VALUE as isize) * 3)); - // Write method entry at sp[-3] - // sp[-3] = me; - // Put compile time cme into REG1. It's assumed to be valid because we are notified when - // any cme we depend on become outdated. See yjit_method_lookup_change(). - asm.comment("push cme, block handler, frame type"); - asm.mov(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -3), Opnd::UImm(cme as u64)); - - // Write block handler at sp[-2] - // sp[-2] = block_handler; - if let Some(_block_iseq) = block { - // reg1 = VM_BH_FROM_ISEQ_BLOCK(VM_CFP_TO_CAPTURED_BLOCK(reg_cfp)); - let cfp_self = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)); - let block_handler = asm.or(cfp_self, Opnd::Imm(1)); - asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2), block_handler); + let frame_block_handler = if let Some(_block_iseq) = block { + BlockHandler::CurrentFrame } else { - let dst_opnd = Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2); - asm.store(dst_opnd, Opnd::UImm(VM_BLOCK_HANDLER_NONE.into())); - } + BlockHandler::None + }; - // Write env flags at sp[-1] - // sp[-1] = frame_type; let mut frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL; if !kw_arg.is_null() { frame_type |= VM_FRAME_FLAG_CFRAME_KW } - asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -1), Opnd::UImm(frame_type.into())); - - // Allocate a new CFP (ec->cfp--) - asm.comment("push callee control frame"); - let ec_cfp_opnd = Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP); - let new_cfp = asm.sub(ec_cfp_opnd, Opnd::UImm(RUBY_SIZEOF_CONTROL_FRAME as u64)); - asm.mov(ec_cfp_opnd, new_cfp); - // Setup the new frame - // *cfp = (const struct rb_control_frame_struct) { - // .pc = 0, - // .sp = sp, - // .iseq = 0, - // .self = recv, - // .ep = sp - 1, - // .block_code = 0, - // .__bp__ = sp, - // }; - - // Can we re-use ec_cfp_opnd from above? - let ec_cfp_opnd = asm.load(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_PC), Opnd::Imm(0)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_SP), sp); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_ISEQ), Opnd::Imm(0)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_BLOCK_CODE), Opnd::Imm(0)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_BP), sp); - let ep = asm.sub(sp, Opnd::UImm(SIZEOF_VALUE as u64)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_EP), ep); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_SELF), recv); + gen_push_frame(jit, ctx, asm, false, ControlFrame { + frame_type, + block_handler: frame_block_handler, + cme, + recv, + sp, + pc: Some(0), + iseq: None, + local_size: 0, + }); if !kw_arg.is_null() { // Build a hash from all kwargs passed @@ -4219,10 +4315,10 @@ fn gen_send_cfunc( asm.mov(stack_ret, ret); // Pop the stack frame (ec->cfp++) - // Can we reuse ec_cfp_opnd from above? + // Instead of recalculating, we can reuse the previous CFP, which is stored in a callee-saved + // register let ec_cfp_opnd = Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP); - let new_cfp = asm.add(ec_cfp_opnd, Opnd::UImm(RUBY_SIZEOF_CONTROL_FRAME as u64)); - asm.store(ec_cfp_opnd, new_cfp); + asm.store(ec_cfp_opnd, CFP); // cfunc calls may corrupt types ctx.clear_local_types(); @@ -4766,62 +4862,25 @@ fn gen_send_iseq( (SIZEOF_VALUE as isize) * (3 + (num_locals as isize) + if doing_kw_call { 1 } else { 0 }); let callee_sp = asm.lea(ctx.sp_opnd(offs)); - // Initialize local variables to Qnil - for i in 0..num_locals { - let offs = (SIZEOF_VALUE as i32) * (i - num_locals - 3); - asm.store(Opnd::mem(64, callee_sp, offs), Qnil.into()); - } - - // Write the callee CME on the stack. It's assumed to be valid because we are notified when - // any cme we depend on become outdated. See yjit_method_lookup_change(). - // Write method entry at sp[-3] - // sp[-3] = me; - asm.comment("push cme, block handler, frame type"); - asm.store(Opnd::mem(64, callee_sp, SIZEOF_VALUE_I32 * -3), VALUE(cme as usize).into()); - - // Write block handler at sp[-2] - // sp[-2] = block_handler; - match block { - Some(_) => { - // reg1 = VM_BH_FROM_ISEQ_BLOCK(VM_CFP_TO_CAPTURED_BLOCK(reg_cfp)); - let block_handler = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)); - let block_handler = asm.or(block_handler, 1.into()); - asm.store(Opnd::mem(64, callee_sp, SIZEOF_VALUE_I32 * -2), block_handler); - } - None => { - asm.store(Opnd::mem(64, callee_sp, SIZEOF_VALUE_I32 * -2), VM_BLOCK_HANDLER_NONE.into()); - } - } + let frame_block_handler = if let Some(_) = block { + BlockHandler::CurrentFrame + } else { + BlockHandler::None + }; - // Write env flags at sp[-1] - // sp[-1] = frame_type; let frame_type = VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL; - asm.store(Opnd::mem(64, callee_sp, SIZEOF_VALUE_I32 * -1), frame_type.into()); - - asm.comment("push callee control frame"); - // Allocate a new CFP (ec->cfp--) - let new_cfp = asm.sub(CFP, (RUBY_SIZEOF_CONTROL_FRAME as u64).into()); - asm.mov(CFP, new_cfp); - asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP); // Setup the new frame - // *cfp = (const struct rb_control_frame_struct) { - // .pc = pc, - // .sp = sp, - // .iseq = iseq, - // .self = recv, - // .ep = sp - 1, - // .block_code = 0, - // .__bp__ = sp, - // }; - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF), recv); - asm.mov(SP, callee_sp); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP), callee_sp); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BP), callee_sp); - let callee_ep = asm.sub(callee_sp, (SIZEOF_VALUE as u64).into()); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_EP), callee_ep); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_ISEQ), VALUE(iseq as usize).into()); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE), 0.into()); + gen_push_frame(jit, ctx, asm, true, ControlFrame { + frame_type, + block_handler: frame_block_handler, + cme, + recv, + sp: callee_sp, + iseq: Some(iseq), + pc: None, // We are calling into jitted code, which will set the PC as necessary + local_size: num_locals + }); // No need to set cfp->pc since the callee sets it whenever calling into routines // that could look at it through jit_save_pc(). From 4e40fdbcee51767de888c81ce4ad2baae639e35f Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert Date: Thu, 22 Sep 2022 17:47:54 -0400 Subject: [PATCH 218/269] YJIT: add chain guards in `guard_two_fixnums` (#6422) * Add chain guards in guard_two_fixnums, opt_eq with symbols * Remove symbol comparison in gen_equality_specialized --- yjit/src/codegen.rs | 50 +++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 15ff2a467f2636..c246c7b48febd8 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1150,7 +1150,7 @@ fn gen_opt_plus( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands from the stack let arg1 = ctx.stack_pop(1); @@ -2320,7 +2320,13 @@ fn gen_concatstrings( KeepCompiling } -fn guard_two_fixnums(ctx: &mut Context, asm: &mut Assembler, side_exit: CodePtr) { +fn guard_two_fixnums( + jit: &mut JITState, + ctx: &mut Context, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + side_exit: CodePtr +) { // Get the stack operand types let arg1_type = ctx.get_opnd_type(StackOpnd(0)); let arg0_type = ctx.get_opnd_type(StackOpnd(1)); @@ -2352,16 +2358,34 @@ fn guard_two_fixnums(ctx: &mut Context, asm: &mut Assembler, side_exit: CodePtr) let arg1 = ctx.stack_opnd(0); let arg0 = ctx.stack_opnd(1); - // If not fixnums, fall back + // If not fixnums at run-time, fall back if arg0_type != Type::Fixnum { asm.comment("guard arg0 fixnum"); asm.test(arg0, Opnd::UImm(RUBY_FIXNUM_FLAG as u64)); - asm.jz(side_exit.into()); + + jit_chain_guard( + JCC_JZ, + jit, + &ctx, + asm, + ocb, + SEND_MAX_DEPTH, + side_exit, + ); } if arg1_type != Type::Fixnum { asm.comment("guard arg1 fixnum"); asm.test(arg1, Opnd::UImm(RUBY_FIXNUM_FLAG as u64)); - asm.jz(side_exit.into()); + + jit_chain_guard( + JCC_JZ, + jit, + &ctx, + asm, + ocb, + SEND_MAX_DEPTH, + side_exit, + ); } // Set stack types in context @@ -2398,7 +2422,7 @@ fn gen_fixnum_cmp( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands from the stack let arg1 = ctx.stack_pop(1); @@ -2475,7 +2499,7 @@ fn gen_equality_specialized( return false; } - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); asm.cmp(a_opnd, b_opnd); @@ -2487,7 +2511,8 @@ fn gen_equality_specialized( asm.mov(dst, val); true - } else if unsafe { comptime_a.class_of() == rb_cString && comptime_b.class_of() == rb_cString } + } + else if unsafe { comptime_a.class_of() == rb_cString && comptime_b.class_of() == rb_cString } { if !assume_bop_not_redefined(jit, ocb, STRING_REDEFINED_OP_FLAG, BOP_EQ) { // if overridden, emit the generic version @@ -2851,7 +2876,7 @@ fn gen_opt_and( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands and destination from the stack let arg1 = ctx.stack_pop(1); @@ -2896,7 +2921,7 @@ fn gen_opt_or( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands and destination from the stack let arg1 = ctx.stack_pop(1); @@ -2941,7 +2966,7 @@ fn gen_opt_minus( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands and destination from the stack let arg1 = ctx.stack_pop(1); @@ -3008,7 +3033,7 @@ fn gen_opt_mod( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands and destination from the stack let arg1 = ctx.stack_pop(1); @@ -5068,7 +5093,6 @@ fn gen_send_general( return CantCompile; } - if flags & VM_CALL_ARGS_BLOCKARG != 0 { gen_counter_incr!(asm, send_block_arg); return CantCompile; From 6e46bf1e54e7fe83dc80e49394d980b71321b6f0 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 23 Sep 2022 08:52:15 +0900 Subject: [PATCH 219/269] Just a star [ci skip] --- include/ruby/internal/scan_args.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ruby/internal/scan_args.h b/include/ruby/internal/scan_args.h index cf5b18f77d89b5..1ed2bf636874f3 100644 --- a/include/ruby/internal/scan_args.h +++ b/include/ruby/internal/scan_args.h @@ -100,7 +100,7 @@ RBIMPL_ATTR_NONNULL((2, 3)) * param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec / * pre-opt-post-arg-spec * pre-arg-spec := num-of-leading-mandatory-args - [num-of-optional-args] + * [num-of-optional-args] * post-arg-spec := sym-for-variable-length-args * [num-of-trailing-mandatory-args] * pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args From a78c733cc32cc3da3796cbf65da21cdd40c63230 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 23 Sep 2022 16:40:59 +0900 Subject: [PATCH 220/269] Revert "Revert "error.c: Let Exception#inspect inspect its message"" This reverts commit b9f030954a8a1572032f3548b39c5b8ac35792ce. [Bug #18170] --- error.c | 12 ++++++++++-- internal/string.h | 1 + string.c | 2 +- test/ruby/test_exception.rb | 6 ++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/error.c b/error.c index 113264eeb7fb91..cfa086a1ebad26 100644 --- a/error.c +++ b/error.c @@ -34,6 +34,7 @@ #include "internal/io.h" #include "internal/load.h" #include "internal/object.h" +#include "internal/string.h" #include "internal/symbol.h" #include "internal/thread.h" #include "internal/variable.h" @@ -1439,8 +1440,15 @@ exc_inspect(VALUE exc) str = rb_str_buf_new2("#<"); klass = rb_class_name(klass); rb_str_buf_append(str, klass); - rb_str_buf_cat(str, ": ", 2); - rb_str_buf_append(str, exc); + + if (RTEST(rb_str_include(exc, rb_str_new2("\n")))) { + rb_str_catf(str, ":%+"PRIsVALUE, exc); + } + else { + rb_str_buf_cat(str, ": ", 2); + rb_str_buf_append(str, exc); + } + rb_str_buf_cat(str, ">", 1); return str; diff --git a/internal/string.h b/internal/string.h index 46862d77f5cbc2..43b716f9b25f4b 100644 --- a/internal/string.h +++ b/internal/string.h @@ -44,6 +44,7 @@ const char *ruby_escaped_char(int c); void rb_str_make_independent(VALUE str); int rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc); int rb_ascii8bit_appendable_encoding_index(rb_encoding *enc, unsigned int code); +VALUE rb_str_include(VALUE str, VALUE arg); static inline bool STR_EMBED_P(VALUE str); static inline bool STR_SHARED_P(VALUE str); diff --git a/string.c b/string.c index ac0a2acb11de9d..c5711bcfe301ac 100644 --- a/string.c +++ b/string.c @@ -6456,7 +6456,7 @@ rb_str_reverse_bang(VALUE str) * */ -static VALUE +VALUE rb_str_include(VALUE str, VALUE arg) { long i; diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index f04106713761d5..b87cef3fb637d2 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -478,6 +478,12 @@ def test_inspect def to_s; ""; end end assert_equal(e.inspect, e.new.inspect) + + # https://bugs.ruby-lang.org/issues/18170#note-13 + assert_equal('#', Exception.new("foo\nbar").inspect) + assert_equal('#', Exception.new("foo bar").inspect) + assert_equal('#', Exception.new("foo\\bar").inspect) + assert_equal('#', Exception.new('"foo\nbar"').inspect) end def test_to_s From 3ddab3a84e1fb44fb851c0edfee204e761cfc4d8 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Fri, 23 Sep 2022 09:41:21 -0500 Subject: [PATCH 221/269] [DOC] Housekeeping in iostreams doc (#6420) Write some method names in linkable form; make some capitalization consistent. --- doc/io_streams.rdoc | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/doc/io_streams.rdoc b/doc/io_streams.rdoc index b686d67eb5fdbf..aab1b21b9c2950 100644 --- a/doc/io_streams.rdoc +++ b/doc/io_streams.rdoc @@ -76,7 +76,8 @@ An \IO stream has a nonnegative integer _position_, which is the byte offset at which the next read or write is to occur; the relevant methods: -- +#tell+ (aliased as #pos): Returns the current position (in bytes) in the stream: +- IO#tell (aliased as +#pos+): + Returns the current position (in bytes) in the stream: f = File.new('t.txt') f.tell # => 0 @@ -84,7 +85,7 @@ the relevant methods: f.tell # => 12 f.close -- +#pos=+: Sets the position of the stream (in bytes): +- IO#pos=: Sets the position of the stream (in bytes): f = File.new('t.txt') f.tell # => 0 @@ -92,7 +93,7 @@ the relevant methods: f.tell # => 20 f.close -- +#seek+: Sets the position of the stream to a given integer +offset+ +- IO#seek: Sets the position of the stream to a given integer +offset+ (in bytes), with respect to a given constant +whence+, which is one of: - +:CUR+ or IO::SEEK_CUR: @@ -130,7 +131,7 @@ the relevant methods: f.tell # => 40 f.close -- +#rewind+: Positions the stream to the beginning: +- IO#rewind: Positions the stream to the beginning: f = File.new('t.txt') f.tell # => 0 @@ -149,7 +150,7 @@ which are separated by an implicit or explicit line separator. These methods are included (except as noted) in classes Kernel, IO, File, and {ARGF}[rdoc-ref:ARGF]: -- +#each_line+ - passes each line to the block; not in Kernel: +- IO#each_line: Passes each line to the block; not in Kernel: f = File.new('t.txt') f.each_line {|line| p line } @@ -173,7 +174,7 @@ and {ARGF}[rdoc-ref:ARGF]: "rth line\n" "Fifth line\n" -- +#gets+ - returns the next line (which may begin mid-line): +- IO#gets: Returns the next line (which may begin mid-line): f = File.new('t.txt') f.gets # => "First line\n" @@ -183,10 +184,10 @@ and {ARGF}[rdoc-ref:ARGF]: f.readlines # => ["Fifth line\n"] f.gets # => nil -- +#readline+ - like #gets, but raises an exception at end-of-file; +- IO#readline: Like #gets, but raises an exception at end-of-file; not in StringIO. -- +#readlines+ - returns all remaining lines in an array; +- IO#readlines: Returns all remaining lines in an array; may begin mid-line: f = File.new('t.txt') @@ -304,11 +305,11 @@ Reading lines from a stream usually changes its line number: Iterating over lines in a stream usually changes its line number: - f = File.new('t.txt') - f.each_line do |line| - p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}" - end - f.close + f = File.new('t.txt') + f.each_line do |line| + p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}" + end + f.close Output: @@ -331,20 +332,17 @@ A new \IO stream may be open for reading, open for writing, or both. You can close a stream using these methods: -- +#close+ - closes the stream for both reading and writing. - -- +#close_read+ (not available in \ARGF) - closes the stream for reading. - -- +#close_write+ (not available in \ARGF) - closes the stream for writing. +- IO#close: Closes the stream for both reading and writing. +- IO#close_read (not available in \ARGF): Closes the stream for reading. +- IO#close_write (not available in \ARGF): Closes the stream for writing. You can query whether a stream is closed using these methods: -- +#closed?+ - returns whether the stream is closed. +- IO#closed?: Returns whether the stream is closed. === Stream End-of-File You can query whether a stream is at end-of-file using this method: -- +#eof?+ (also aliased as +#eof+) - - returns whether the stream is at end-of-file. - +- IO#eof? (also aliased as +#eof+): + Returns whether the stream is at end-of-file. From 7588f21851ae7fd93103130e565b97c8249f904f Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 24 Sep 2022 00:02:19 +0900 Subject: [PATCH 222/269] Allow changing conversion macro in MJIT bindgen This is necessary for object shapes. --- tool/mjit/bindgen.rb | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 56369c2d9844de..d0f9bf527bf4c3 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -102,16 +102,16 @@ class BindingGenerator # @param src_path [String] # @param uses [Array] - # @param ints [Array] + # @param values [Hash{ Symbol => Array }] # @param types [Array] # @param dynamic_types [Array] #ifdef-dependent immediate types, which need Primitive.cexpr! for type detection # @param skip_fields [Hash{ Symbol => Array }] Struct fields that are skipped from bindgen # @param ruby_fields [Hash{ Symbol => Array }] Struct VALUE fields that are considered Ruby objects - def initialize(src_path:, uses:, ints:, types:, dynamic_types:, skip_fields:, ruby_fields:) + def initialize(src_path:, uses:, values:, types:, dynamic_types:, skip_fields:, ruby_fields:) @preamble, @postamble = split_ambles(src_path) @src = String.new @uses = uses.sort - @ints = ints.sort + @values = values.transform_values(&:sort) @types = types.sort @dynamic_types = dynamic_types.sort @skip_fields = skip_fields.transform_keys(&:to_s) @@ -130,12 +130,14 @@ def generate(nodes) println end - # Define int macros/enums - @ints.each do |int| - println " def C.#{int}" - println " Primitive.cexpr! %q{ INT2NUM(#{int}) }" - println " end" - println + # Define macros/enums + @values.each do |type, values| + values.each do |value| + println " def C.#{value}" + println " Primitive.cexpr! %q{ #{type}2NUM(#{value}) }" + println " end" + println + end end # TODO: Support nested declarations @@ -329,15 +331,17 @@ def push_target(target) USE_LAZY_LOAD USE_RVARGC ], - ints: %w[ - NOT_COMPILED_STACK_SIZE - VM_CALL_KW_SPLAT - VM_CALL_KW_SPLAT_bit - VM_CALL_TAILCALL - VM_CALL_TAILCALL_bit - VM_METHOD_TYPE_CFUNC - VM_METHOD_TYPE_ISEQ - ], + values: { + INT: %w[ + NOT_COMPILED_STACK_SIZE + VM_CALL_KW_SPLAT + VM_CALL_KW_SPLAT_bit + VM_CALL_TAILCALL + VM_CALL_TAILCALL_bit + VM_METHOD_TYPE_CFUNC + VM_METHOD_TYPE_ISEQ + ], + }, types: %w[ CALL_DATA IC From 912ea8257a533299d47d71aac8f1b363853493fe Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 24 Sep 2022 05:17:54 +0900 Subject: [PATCH 223/269] YJIT: Support Rust 1.58.1 for --yjit-stats on Arm (#6410) * YJIT: Test Rust 1.58.1 as well on Cirrus * YJIT: Avoid using a Rust 1.60.0 feature * YJIT: Use autoconf to detect support * YJIT: We actually need to run it for checking it properly * YJIT: Try cfg!(target_feature = "lse") * Revert "YJIT: Try cfg!(target_feature = "lse")" This reverts commit 4e2a9ca9a9c83052c23b5e205c91bdf79e88342e. * YJIT: Add --features stats only when it works * Update configure.ac Co-authored-by: Maxime Chevalier-Boisvert --- .cirrus.yml | 3 ++- configure.ac | 35 ++++++++++++++++++++++++++++------- yjit/src/options.rs | 14 +------------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 1e7832ed000716..854a3df98201fb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -90,6 +90,7 @@ yjit_task: matrix: - CC: clang-12 configure: --enable-yjit=dev + rustup_init: --default-toolchain=1.58.1 - CC: gcc-11 configure: --enable-yjit id_script: id @@ -107,7 +108,7 @@ yjit_task: install_rust_script: - sudo apt-get update -y - sudo apt-get install -y curl - - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y" + - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y $rustup_init" autogen_script: ./autogen.sh configure_script: >- source $HOME/.cargo/env && ./configure -C diff --git a/configure.ac b/configure.ac index a3566358bc9b9b..d477ea1dc4ae97 100644 --- a/configure.ac +++ b/configure.ac @@ -3748,23 +3748,44 @@ AS_CASE(["${YJIT_SUPPORT}"], ], [dev], [ rb_rust_target_subdir=debug - CARGO_BUILD_ARGS='--features stats,disasm,asm_comments' + CARGO_BUILD_ARGS='--features disasm,asm_comments' AC_DEFINE(RUBY_DEBUG, 1) ], [dev_nodebug], [ rb_rust_target_subdir=dev_nodebug - CARGO_BUILD_ARGS='--profile dev_nodebug --features stats,disasm,asm_comments' + CARGO_BUILD_ARGS='--profile dev_nodebug --features disasm,asm_comments' ], [stats], [ rb_rust_target_subdir=stats - CARGO_BUILD_ARGS='--profile stats --features stats' + CARGO_BUILD_ARGS='--profile stats' ]) AS_IF([test -n "${CARGO_BUILD_ARGS}"], [ - AC_CHECK_TOOL(CARGO, [cargo], [no]) - AS_IF([test x"$CARGO" = "xno"], - AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install]) - ])) + AC_CHECK_TOOL(CARGO, [cargo], [no]) + AS_IF([test x"$CARGO" = "xno"], + AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install]) + ]) + + # Insn::IncrCounter uses ldaddal, which works only on ARMv8.1+. + AC_CACHE_CHECK(yjit stats are broken, rb_cv_broken_yjit_stats, [ + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[]], [[ + @%:@ifdef __aarch64__ + asm volatile(".arch armv8-a+lse\n" + "ldaddal xzr, xzr, @<:@sp@:>@"); + @%:@endif + ]])], + [rb_cv_broken_yjit_stats=no], + [rb_cv_broken_yjit_stats=yes], + [rb_cv_broken_yjit_stats=yes] + ) + ]) + # This won't enable stats in release builds because we don't use cargo + # for release builds, use rustc directly + AS_IF([test "$rb_cv_broken_yjit_stats" = no], [ + CARGO_BUILD_ARGS="${CARGO_BUILD_ARGS} --features stats" + ]) + ) YJIT_LIBS="yjit/target/${rb_rust_target_subdir}/libyjit.a" AS_CASE(["$target_os"],[openbsd*],[ diff --git a/yjit/src/options.rs b/yjit/src/options.rs index e588876173c90d..f73dca67de21bb 100644 --- a/yjit/src/options.rs +++ b/yjit/src/options.rs @@ -151,19 +151,7 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { ("greedy-versioning", "") => unsafe { OPTIONS.greedy_versioning = true }, ("no-type-prop", "") => unsafe { OPTIONS.no_type_prop = true }, - - ("stats", "") => { - // Insn::IncrCounter uses ldaddal, which works only on ARMv8.1+. - #[cfg(feature = "stats")] - #[cfg(target_arch = "aarch64")] - if !std::arch::is_aarch64_feature_detected!("lse") { - eprintln!("Your processor does not support --yjit-stats. Aborting."); - std::process::exit(1); - } - - unsafe { OPTIONS.gen_stats = true } - }, - + ("stats", "") => unsafe { OPTIONS.gen_stats = true }, ("trace-exits", "") => unsafe { OPTIONS.gen_trace_exits = true; OPTIONS.gen_stats = true }, ("dump-insns", "") => unsafe { OPTIONS.dump_insns = true }, ("verify-ctx", "") => unsafe { OPTIONS.verify_ctx = true }, From a74f4cded2ba4183b680c14f5c9aa7151afaad58 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Fri, 23 Sep 2022 14:29:25 -0700 Subject: [PATCH 224/269] Remove reference to __classid__ This used to be used for module names but its uses were removed in b00f280d4b9569e7153365d7e1c522b3d6b3c6cf. --- class.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/class.c b/class.c index 5e57068f03c5ec..3a83a7f052e0f2 100644 --- a/class.c +++ b/class.c @@ -423,8 +423,6 @@ copy_tables(VALUE clone, VALUE orig) st_delete(RCLASS_IV_TBL(clone), &id, 0); CONST_ID(id, "__classpath__"); st_delete(RCLASS_IV_TBL(clone), &id, 0); - CONST_ID(id, "__classid__"); - st_delete(RCLASS_IV_TBL(clone), &id, 0); } if (RCLASS_CONST_TBL(orig)) { struct clone_const_arg arg; From 82ac4a2399516a3ffda750b815c244aad6d38277 Mon Sep 17 00:00:00 2001 From: Chris Salzberg Date: Tue, 9 Aug 2022 22:22:23 +0900 Subject: [PATCH 225/269] Support using at toplevel in wrapped script Allow refinements to be used at the toplevel within a script that is loaded under a module. Fixes [Bug #18960] --- eval.c | 6 ++++-- spec/ruby/core/main/fixtures/using.rb | 1 + spec/ruby/core/main/fixtures/using_in_main.rb | 5 +++++ .../core/main/fixtures/using_in_method.rb | 5 +++++ spec/ruby/core/main/using_spec.rb | 20 +++++++++++++++++++ 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 spec/ruby/core/main/fixtures/using.rb create mode 100644 spec/ruby/core/main/fixtures/using_in_main.rb create mode 100644 spec/ruby/core/main/fixtures/using_in_method.rb diff --git a/eval.c b/eval.c index cf32a82214557e..9567f801501d86 100644 --- a/eval.c +++ b/eval.c @@ -1792,10 +1792,12 @@ top_include(int argc, VALUE *argv, VALUE self) static VALUE top_using(VALUE self, VALUE module) { - const rb_cref_t *cref = rb_vm_cref(); + const rb_cref_t *cref = CREF_NEXT(rb_vm_cref());; rb_control_frame_t *prev_cfp = previous_frame(GET_EC()); + rb_thread_t *th = GET_THREAD(); - if (CREF_NEXT(cref) || (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) { + if ((th->top_wrapper ? CREF_NEXT(cref) : cref) || + (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) { rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel"); } if (rb_block_given_p()) { diff --git a/spec/ruby/core/main/fixtures/using.rb b/spec/ruby/core/main/fixtures/using.rb new file mode 100644 index 00000000000000..30713ef309dea1 --- /dev/null +++ b/spec/ruby/core/main/fixtures/using.rb @@ -0,0 +1 @@ +using Module.new diff --git a/spec/ruby/core/main/fixtures/using_in_main.rb b/spec/ruby/core/main/fixtures/using_in_main.rb new file mode 100644 index 00000000000000..a4a71c89ccebed --- /dev/null +++ b/spec/ruby/core/main/fixtures/using_in_main.rb @@ -0,0 +1,5 @@ +MAIN = self + +module X + MAIN.send(:using, Module.new) +end diff --git a/spec/ruby/core/main/fixtures/using_in_method.rb b/spec/ruby/core/main/fixtures/using_in_method.rb new file mode 100644 index 00000000000000..d9ea2e9ef08e1c --- /dev/null +++ b/spec/ruby/core/main/fixtures/using_in_method.rb @@ -0,0 +1,5 @@ +def foo + using Module.new +end + +foo diff --git a/spec/ruby/core/main/using_spec.rb b/spec/ruby/core/main/using_spec.rb index f9f709f7dc3256..8a23970c4bf248 100644 --- a/spec/ruby/core/main/using_spec.rb +++ b/spec/ruby/core/main/using_spec.rb @@ -129,4 +129,24 @@ def bar; 'quux'; end x.call_bar(cls2.new).should == 'bar' end + + it "raises error when called from method in wrapped script" do + -> do + load File.expand_path('../fixtures/using_in_method.rb', __FILE__), true + end.should raise_error(RuntimeError) + end + + it "raises error when called on toplevel from module" do + -> do + load File.expand_path('../fixtures/using_in_main.rb', __FILE__), true + end.should raise_error(RuntimeError) + end + + ruby_version_is "3.2" do + it "does not raise error when wrapped with module" do + -> do + load File.expand_path('../fixtures/using.rb', __FILE__), true + end.should_not raise_error + end + end end From b0e3ee454d48748b4aadf1aaedd52670370d503b Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 23 Sep 2022 23:06:27 -0700 Subject: [PATCH 226/269] Suppress -Wtype-limits warnings on mjit_c.rb --- mjit_compiler.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mjit_compiler.c b/mjit_compiler.c index 5ed3aa5a68d877..3191c10d5fa1b7 100644 --- a/mjit_compiler.c +++ b/mjit_compiler.c @@ -171,7 +171,11 @@ extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); #define SIZEOF(type) RB_SIZE2NUM(sizeof(type)) #define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(0)) +RBIMPL_WARNING_PUSH(); +RBIMPL_WARNING_IGNORED(-Wtype-limits); // for SIGNED_TYPE_P #include "mjit_c.rbinc" +RBIMPL_WARNING_POP(); + #include "mjit_compiler.rbinc" #endif // USE_MJIT From 08476c45799deeba0e76be54e160e87330e8cf9e Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 23 Sep 2022 23:13:43 -0700 Subject: [PATCH 227/269] Remove unneeded semicolons --- mjit_compiler.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mjit_compiler.c b/mjit_compiler.c index 3191c10d5fa1b7..82be9e77356882 100644 --- a/mjit_compiler.c +++ b/mjit_compiler.c @@ -171,10 +171,10 @@ extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); #define SIZEOF(type) RB_SIZE2NUM(sizeof(type)) #define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(0)) -RBIMPL_WARNING_PUSH(); -RBIMPL_WARNING_IGNORED(-Wtype-limits); // for SIGNED_TYPE_P +RBIMPL_WARNING_PUSH() +RBIMPL_WARNING_IGNORED(-Wtype-limits) // for SIGNED_TYPE_P #include "mjit_c.rbinc" -RBIMPL_WARNING_POP(); +RBIMPL_WARNING_POP() #include "mjit_compiler.rbinc" From 205c252ec7364ea30ab39cc3c316fdada781eb1b Mon Sep 17 00:00:00 2001 From: S-H-GAMELINKS Date: Sat, 24 Sep 2022 22:35:10 +0900 Subject: [PATCH 228/269] Reuse rb_method_call_kw function --- proc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proc.c b/proc.c index 200a592701d807..fb182efe4671d5 100644 --- a/proc.c +++ b/proc.c @@ -2452,8 +2452,7 @@ method_clone(VALUE self) static VALUE rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method) { - VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil; - return rb_method_call_with_block_kw(argc, argv, method, procval, RB_PASS_CALLED_KEYWORDS); + return rb_method_call_kw(argc, argv, method, RB_PASS_CALLED_KEYWORDS); } VALUE From 09ea4f3a9fc13214198fa2cf223ec601640d3eac Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 23 Sep 2022 22:54:39 +1200 Subject: [PATCH 229/269] Extract common code for coverage setup. --- iseq.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/iseq.c b/iseq.c index 9359fcfe4e982d..44c4f6697b9add 100644 --- a/iseq.c +++ b/iseq.c @@ -899,17 +899,29 @@ ast_line_count(const rb_ast_body_t *ast) return FIX2INT(ast->script_lines); } +static VALUE +iseq_setup_coverage(VALUE coverages, VALUE path, const rb_ast_body_t *ast, int line_offset) +{ + int line_count = line_offset + ast_line_count(ast); + + if (line_count >= 0) { + int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count; + + VALUE coverage = rb_default_coverage(len); + rb_hash_aset(coverages, path, coverage); + + return coverage; + } + + return Qnil; +} + rb_iseq_t * rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent) { VALUE coverages = rb_get_coverages(); if (RTEST(coverages)) { - int line_count = ast_line_count(ast); - if (line_count >= 0) { - int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count; - VALUE coverage = rb_default_coverage(len); - rb_hash_aset(coverages, path, coverage); - } + iseq_setup_coverage(coverages, path, ast, 0); } return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, 0, @@ -930,13 +942,7 @@ rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpat VALUE coverages = rb_get_coverages(); if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) { int line_offset = RB_NUM2INT(first_lineno) - 1; - int line_count = line_offset + ast_line_count(ast); - - if (line_count >= 0) { - int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count; - VALUE coverage = rb_default_coverage(len); - rb_hash_aset(coverages, path, coverage); - } + iseq_setup_coverage(coverages, path, ast, line_offset); } return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno, From 81b919ce5c85a82d68de9ccaebd5036adef8c7fb Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 23 Sep 2022 22:58:01 +1200 Subject: [PATCH 230/269] Add news for eval coverage. --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 1f0cb77fee2295..82558f8e9dc85e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -90,6 +90,9 @@ Note that each entry is kept to a minimum, see links for details. foo(k: 1) ``` +* `eval` and related methods are able to generate code coverage. + [[Feature #19008]] + ## Command line options ## Core classes updates @@ -313,3 +316,4 @@ The following deprecated APIs are removed. [Feature #18809]: https://bugs.ruby-lang.org/issues/18809 [Feature #18481]: https://bugs.ruby-lang.org/issues/18481 [Feature #18949]: https://bugs.ruby-lang.org/issues/18949 +[Feature #19008]: https://bugs.ruby-lang.org/issues/19008 \ No newline at end of file From 8e2c42518355f3e29f5693de9034fb66a2abbdaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciek=20Rz=C4=85sa?= Date: Thu, 28 Jul 2022 14:30:10 +0200 Subject: [PATCH 231/269] [ruby/bigdecimal] Improve documentation of BigDecimal#sign Fixes https://github.com/ruby/bigdecimal/issues/78 by describing behaviour for positive and negative zero in the docs. https://github.com/ruby/bigdecimal/commit/5415b120ab --- ext/bigdecimal/bigdecimal.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index b3ef70a2c96ec0..06c8251cc2b067 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3606,8 +3606,10 @@ BigDecimal_limit(int argc, VALUE *argv, VALUE self) /* Returns the sign of the value. * - * Returns a positive value if > 0, a negative value if < 0, and a - * zero if == 0. + * Returns a positive value if > 0, a negative value if < 0. + * It behaves the same with zeros - + * it returns a positive value for a positive zero (BigDecimal('0')) and + * a negative value for a negative zero (BigDecimal('-0')). * * The specific value returned indicates the type and sign of the BigDecimal, * as follows: From a95b741a973438a3ea666b8997efa8194e1fd5e7 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 25 Sep 2022 23:34:19 +1300 Subject: [PATCH 232/269] [ruby/bigdecimal] Remove trailing whitespace. https://github.com/ruby/bigdecimal/commit/223d193f01 --- ext/bigdecimal/bigdecimal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 06c8251cc2b067..61a8d4d8d5bd9a 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3607,9 +3607,9 @@ BigDecimal_limit(int argc, VALUE *argv, VALUE self) /* Returns the sign of the value. * * Returns a positive value if > 0, a negative value if < 0. - * It behaves the same with zeros - + * It behaves the same with zeros - * it returns a positive value for a positive zero (BigDecimal('0')) and - * a negative value for a negative zero (BigDecimal('-0')). + * a negative value for a negative zero (BigDecimal('-0')). * * The specific value returned indicates the type and sign of the BigDecimal, * as follows: From 75cf29f60d87b298edfd75611bfe272a92289cee Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 25 Sep 2022 20:45:28 +1300 Subject: [PATCH 233/269] Rework `first_lineno` to be `int`. --- compile.c | 6 +++--- iseq.c | 31 +++++++++++++++---------------- mini_builtin.c | 2 +- vm_core.h | 8 ++++---- vm_eval.c | 2 +- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/compile.c b/compile.c index 1296f8fb46bb7f..26b10ff43cbb23 100644 --- a/compile.c +++ b/compile.c @@ -1333,7 +1333,7 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node, int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth; ret_iseq = rb_iseq_new_with_opt(&ast, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(line_no), parent, + line_no, parent, isolated_depth ? isolated_depth + 1 : 0, type, ISEQ_COMPILE_DATA(iseq)->option); debugs("[new_child_iseq]< ---------------------------------------\n"); @@ -1349,7 +1349,7 @@ new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_call debugs("[new_child_iseq_with_callback]> ---------------------------------------\n"); ret_iseq = rb_iseq_new_with_callback(ifunc, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option); + line_no, parent, type, ISEQ_COMPILE_DATA(iseq)->option); debugs("[new_child_iseq_with_callback]< ---------------------------------------\n"); return ret_iseq; } @@ -8231,7 +8231,7 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N ISEQ_BODY(iseq)->mandatory_only_iseq = rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq), rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(nd_line(line_node)), NULL, 0, + nd_line(line_node), NULL, 0, ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option); GET_VM()->builtin_inline_index = prev_inline_index; diff --git a/iseq.c b/iseq.c index 44c4f6697b9add..5f2143baa906a5 100644 --- a/iseq.c +++ b/iseq.c @@ -591,14 +591,14 @@ rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath) } static rb_iseq_location_t * -iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id) +iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id) { rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location; rb_iseq_pathobj_set(iseq, path, realpath); RB_OBJ_WRITE(iseq, &loc->label, name); RB_OBJ_WRITE(iseq, &loc->base_label, name); - loc->first_lineno = first_lineno; + loc->first_lineno = RB_INT2NUM(first_lineno); if (code_location) { loc->node_id = node_id; loc->code_location = *code_location; @@ -656,7 +656,7 @@ new_arena(void) static VALUE prepare_iseq_build(rb_iseq_t *iseq, - VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id, + VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type, VALUE script_lines, const rb_compile_option_t *option) { @@ -882,7 +882,7 @@ rb_iseq_t * rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type type) { - return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, + return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, 0, type, &COMPILE_OPTION_DEFAULT); } @@ -924,7 +924,7 @@ rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath iseq_setup_coverage(coverages, path, ast, 0); } - return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, 0, + return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, 0, ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT); } @@ -932,17 +932,16 @@ rb_iseq_t * rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt) { return rb_iseq_new_with_opt(ast, rb_fstring_lit("
"), - path, realpath, INT2FIX(0), + path, realpath, 0, parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE); } rb_iseq_t * -rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth) +rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth) { VALUE coverages = rb_get_coverages(); if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) { - int line_offset = RB_NUM2INT(first_lineno) - 1; - iseq_setup_coverage(coverages, path, ast, line_offset); + iseq_setup_coverage(coverages, path, ast, first_lineno - 1); } return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno, @@ -965,7 +964,7 @@ iseq_translate(rb_iseq_t *iseq) rb_iseq_t * rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, - VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth, + int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type, const rb_compile_option_t *option) { const NODE *node = ast ? ast->root : 0; @@ -1003,7 +1002,7 @@ rb_iseq_t * rb_iseq_new_with_callback( const struct rb_iseq_new_with_callback_callback_func * ifunc, VALUE name, VALUE path, VALUE realpath, - VALUE first_lineno, const rb_iseq_t *parent, + int first_lineno, const rb_iseq_t *parent, enum rb_iseq_type type, const rb_compile_option_t *option) { /* TODO: argument check */ @@ -1069,7 +1068,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt) rb_iseq_t *iseq = iseq_alloc(); VALUE magic, version1, version2, format_type, misc; - VALUE name, path, realpath, first_lineno, code_location, node_id; + VALUE name, path, realpath, code_location, node_id; VALUE type, body, locals, params, exception; st_data_t iseq_type; @@ -1095,7 +1094,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt) path = CHECK_STRING(rb_ary_entry(data, i++)); realpath = rb_ary_entry(data, i++); realpath = NIL_P(realpath) ? Qnil : CHECK_STRING(realpath); - first_lineno = CHECK_INTEGER(rb_ary_entry(data, i++)); + int first_lineno = RB_NUM2INT(rb_ary_entry(data, i++)); type = CHECK_SYMBOL(rb_ary_entry(data, i++)); locals = CHECK_ARRAY(rb_ary_entry(data, i++)); @@ -1189,7 +1188,7 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V rb_exc_raise(GET_EC()->errinfo); } else { - iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, line, + iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option); rb_ast_dispose(ast); } @@ -1428,7 +1427,7 @@ iseqw_s_compile(int argc, VALUE *argv, VALUE self) static VALUE iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) { - VALUE file, line = INT2FIX(1), opt = Qnil; + VALUE file, opt = Qnil; VALUE parser, f, exc = Qnil, ret; rb_ast_t *ast; rb_compile_option_t option; @@ -1460,7 +1459,7 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("
"), file, rb_realpath_internal(Qnil, file, 1), - line, NULL, 0, ISEQ_TYPE_TOP, &option)); + 1, NULL, 0, ISEQ_TYPE_TOP, &option)); rb_ast_dispose(ast); return ret; } diff --git a/mini_builtin.c b/mini_builtin.c index 8c8cf66263e59b..c263d1ee71fc3b 100644 --- a/mini_builtin.c +++ b/mini_builtin.c @@ -36,7 +36,7 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta FALSE, /* unsigned int coverage_enabled; */ 0, /* int debug_level; */ }; - const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, INT2FIX(0), NULL, 0, ISEQ_TYPE_TOP, &optimization); + const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization); GET_VM()->builtin_function_table = NULL; rb_ast_dispose(ast); diff --git a/vm_core.h b/vm_core.h index 350f3fdd58be9f..7dc53399b40ccb 100644 --- a/vm_core.h +++ b/vm_core.h @@ -310,7 +310,7 @@ typedef struct rb_iseq_location_struct { VALUE pathobj; /* String (path) or Array [path, realpath]. Frozen. */ VALUE base_label; /* String */ VALUE label; /* String */ - VALUE first_lineno; /* TODO: may be unsigned short */ + VALUE first_lineno; int node_id; rb_code_location_t code_location; } rb_iseq_location_t; @@ -1128,8 +1128,8 @@ RUBY_SYMBOL_EXPORT_BEGIN rb_iseq_t *rb_iseq_new (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type); rb_iseq_t *rb_iseq_new_top (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent); rb_iseq_t *rb_iseq_new_main (const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt); -rb_iseq_t *rb_iseq_new_eval (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth); -rb_iseq_t *rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth, +rb_iseq_t *rb_iseq_new_eval (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth); +rb_iseq_t *rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t*); struct iseq_link_anchor; @@ -1147,7 +1147,7 @@ rb_iseq_new_with_callback_new_callback( return (struct rb_iseq_new_with_callback_callback_func *)memo; } rb_iseq_t *rb_iseq_new_with_callback(const struct rb_iseq_new_with_callback_callback_func * ifunc, - VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, + VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, enum rb_iseq_type, const rb_compile_option_t*); VALUE rb_iseq_disasm(const rb_iseq_t *iseq); diff --git a/vm_eval.c b/vm_eval.c index 9d7a736ad923ff..c0558fce2bcb0a 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1718,7 +1718,7 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, iseq = rb_iseq_new_eval(&ast->body, ISEQ_BODY(parent)->location.label, - fname, Qnil, INT2FIX(line), + fname, Qnil, line, parent, isolated_depth); } rb_ast_dispose(ast); From 22af2e9084d869b0d1eb24e4c11bc1fd62b7c50d Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 25 Sep 2022 21:07:18 +1300 Subject: [PATCH 234/269] Rework vm_core to use `int first_lineno` struct member. --- compile.c | 6 +++--- gc.c | 5 ++--- iseq.c | 6 +++--- lib/mjit/compiler.rb | 4 ++-- mjit.c | 26 ++++++++++---------------- mjit_c.rb | 7 ++++++- proc.c | 6 +++--- vm_backtrace.c | 4 ++-- vm_core.h | 4 ++-- vm_method.c | 2 +- 10 files changed, 34 insertions(+), 36 deletions(-) diff --git a/compile.c b/compile.c index 26b10ff43cbb23..d9a17560be0c9a 100644 --- a/compile.c +++ b/compile.c @@ -766,7 +766,7 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) end->rescued = LABEL_RESCUE_END; ADD_TRACE(ret, RUBY_EVENT_B_CALL); - NODE dummy_line_node = generate_dummy_line_node(FIX2INT(ISEQ_BODY(iseq)->location.first_lineno), -1); + NODE dummy_line_node = generate_dummy_line_node(ISEQ_BODY(iseq)->location.first_lineno, -1); ADD_INSN (ret, &dummy_line_node, nop); ADD_LABEL(ret, start); CHECK(COMPILE(ret, "block body", node->nd_body)); @@ -12024,7 +12024,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) ibf_dump_write_small_value(dump, location_pathobj_index); ibf_dump_write_small_value(dump, location_base_label_index); ibf_dump_write_small_value(dump, location_label_index); - ibf_dump_write_small_value(dump, body->location.first_lineno); + ibf_dump_write_small_value(dump, RB_INT2NUM(body->location.first_lineno)); ibf_dump_write_small_value(dump, body->location.node_id); ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.lineno); ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.column); @@ -12195,7 +12195,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) load_body->variable.flip_count = variable_flip_count; load_body->variable.script_lines = Qnil; - load_body->location.first_lineno = location_first_lineno; + load_body->location.first_lineno = RB_NUM2INT(location_first_lineno); load_body->location.node_id = location_node_id; load_body->location.code_location.beg_pos.lineno = location_code_location_beg_pos_lineno; load_body->location.code_location.beg_pos.column = location_code_location_beg_pos_column; diff --git a/gc.c b/gc.c index ecb4aa7e2085f5..d026139d7bebff 100644 --- a/gc.c +++ b/gc.c @@ -13804,11 +13804,10 @@ rb_raw_iseq_info(char *const buff, const size_t buff_size, const rb_iseq_t *iseq { if (buff_size > 0 && ISEQ_BODY(iseq) && ISEQ_BODY(iseq)->location.label && !RB_TYPE_P(ISEQ_BODY(iseq)->location.pathobj, T_MOVED)) { VALUE path = rb_iseq_path(iseq); - VALUE n = ISEQ_BODY(iseq)->location.first_lineno; + int n = ISEQ_BODY(iseq)->location.first_lineno; snprintf(buff, buff_size, " %s@%s:%d", RSTRING_PTR(ISEQ_BODY(iseq)->location.label), - RSTRING_PTR(path), - n ? FIX2INT(n) : 0 ); + RSTRING_PTR(path), n); } } diff --git a/iseq.c b/iseq.c index 5f2143baa906a5..a4792d81fdb5b6 100644 --- a/iseq.c +++ b/iseq.c @@ -598,7 +598,7 @@ iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int rb_iseq_pathobj_set(iseq, path, realpath); RB_OBJ_WRITE(iseq, &loc->label, name); RB_OBJ_WRITE(iseq, &loc->base_label, name); - loc->first_lineno = RB_INT2NUM(first_lineno); + loc->first_lineno = first_lineno; if (code_location) { loc->node_id = node_id; loc->code_location = *code_location; @@ -1235,7 +1235,7 @@ rb_iseq_base_label(const rb_iseq_t *iseq) VALUE rb_iseq_first_lineno(const rb_iseq_t *iseq) { - return ISEQ_BODY(iseq)->location.first_lineno; + return RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno); } VALUE @@ -3164,7 +3164,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq) rb_ary_push(val, iseq_body->location.label); rb_ary_push(val, rb_iseq_path(iseq)); rb_ary_push(val, rb_iseq_realpath(iseq)); - rb_ary_push(val, iseq_body->location.first_lineno); + rb_ary_push(val, RB_INT2NUM(iseq_body->location.first_lineno)); rb_ary_push(val, ID2SYM(type)); rb_ary_push(val, locals); rb_ary_push(val, params); diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 254ecc885fa6fd..49f28ab6908612 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -785,8 +785,8 @@ def precompile_inlinable_iseqs(f, iseq, status) if C.mjit_opts.verbose >= 1 # print beforehand because ISeq may be GCed during copy job. child_location = child_iseq.body.location - $stderr.puts "JIT inline: #{child_location.label}@#{C.rb_iseq_path(child_iseq)}:#{child_location.first_lineno} " \ - "=> #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{iseq.body.location.first_lineno}" + $stderr.puts "JIT inline: #{child_location.label}@#{C.rb_iseq_path(child_iseq)}:#{C.rb_iseq_first_lineno(child_iseq)} " \ + "=> #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{C.rb_iseq_first_lineno(iseq)}" end if !precompile_inlinable_child_iseq(f, child_iseq, status, ci, cc, pos) return false diff --git a/mjit.c b/mjit.c index 1997eaa939ab85..80743a150bdba3 100644 --- a/mjit.c +++ b/mjit.c @@ -707,15 +707,12 @@ mjit_compact(char* c_file) char funcname[MAXPATHLEN]; sprint_funcname(funcname, child_unit); - long iseq_lineno = 0; - if (FIXNUM_P(ISEQ_BODY(child_unit->iseq)->location.first_lineno)) - // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG. - iseq_lineno = FIX2LONG(ISEQ_BODY(child_unit->iseq)->location.first_lineno); + int iseq_lineno = ISEQ_BODY(child_unit->iseq)->location.first_lineno; const char *sep = "@"; const char *iseq_label = RSTRING_PTR(ISEQ_BODY(child_unit->iseq)->location.label); const char *iseq_path = RSTRING_PTR(rb_iseq_path(child_unit->iseq)); if (!iseq_label) iseq_label = sep = ""; - fprintf(f, "\n/* %s%s%s:%ld */\n", iseq_label, sep, iseq_path, iseq_lineno); + fprintf(f, "\n/* %s%s%s:%d */\n", iseq_label, sep, iseq_path, iseq_lineno); success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id); } @@ -875,24 +872,21 @@ mjit_compile_unit(struct rb_mjit_unit *unit) compile_prelude(f); // To make MJIT worker thread-safe against GC.compact, copy ISeq values while `in_jit` is true. - long iseq_lineno = 0; - if (FIXNUM_P(ISEQ_BODY(unit->iseq)->location.first_lineno)) - // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG. - iseq_lineno = FIX2LONG(ISEQ_BODY(unit->iseq)->location.first_lineno); + int iseq_lineno = ISEQ_BODY(unit->iseq)->location.first_lineno; char *iseq_label = alloca(RSTRING_LEN(ISEQ_BODY(unit->iseq)->location.label) + 1); char *iseq_path = alloca(RSTRING_LEN(rb_iseq_path(unit->iseq)) + 1); strcpy(iseq_label, RSTRING_PTR(ISEQ_BODY(unit->iseq)->location.label)); strcpy(iseq_path, RSTRING_PTR(rb_iseq_path(unit->iseq))); - verbose(2, "start compilation: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file); - fprintf(f, "/* %s@%s:%ld */\n\n", iseq_label, iseq_path, iseq_lineno); + verbose(2, "start compilation: %s@%s:%d -> %s", iseq_label, iseq_path, iseq_lineno, c_file); + fprintf(f, "/* %s@%s:%d */\n\n", iseq_label, iseq_path, iseq_lineno); bool success = mjit_compile(f, unit->iseq, funcname, unit->id); fclose(f); if (!success) { if (!mjit_opts.save_temps) remove_file(c_file); - verbose(1, "JIT failure: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file); + verbose(1, "JIT failure: %s@%s:%d -> %s", iseq_label, iseq_path, iseq_lineno, c_file); return 1; } @@ -1372,9 +1366,9 @@ mjit_notify_waitpid(int exit_code) rb_iseq_t *iseq = current_cc_unit->iseq; if ((uintptr_t)func > (uintptr_t)LAST_JIT_ISEQ_FUNC) { double end_time = real_ms_time(); - verbose(1, "JIT success (%.1fms): %s@%s:%ld -> %s", + verbose(1, "JIT success (%.1fms): %s@%s:%d -> %s", end_time - current_cc_ms, RSTRING_PTR(ISEQ_BODY(iseq)->location.label), - RSTRING_PTR(rb_iseq_path(iseq)), FIX2LONG(ISEQ_BODY(iseq)->location.first_lineno), c_file); + RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno, c_file); add_to_list(current_cc_unit, &active_units); } @@ -1531,7 +1525,7 @@ mjit_recompile(const rb_iseq_t *iseq) return; verbose(1, "JIT recompile: %s@%s:%d", RSTRING_PTR(ISEQ_BODY(iseq)->location.label), - RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno)); + RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno); VM_ASSERT(ISEQ_BODY(iseq)->jit_unit != NULL); mjit_add_iseq_to_process(iseq, &ISEQ_BODY(iseq)->jit_unit->compile_info, true); @@ -2011,7 +2005,7 @@ mjit_dump_total_calls(void) ccan_list_for_each(&active_units.head, unit, unode) { const rb_iseq_t *iseq = unit->iseq; fprintf(stderr, "%8ld: %s@%s:%d\n", ISEQ_BODY(iseq)->total_calls, RSTRING_PTR(ISEQ_BODY(iseq)->location.label), - RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno)); + RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno); } } #endif diff --git a/mjit_c.rb b/mjit_c.rb index 6b316860bee92f..d8e5628bda57d4 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -32,6 +32,11 @@ def rb_iseq_path(iseq) Primitive.cexpr! 'rb_iseq_path((rb_iseq_t *)NUM2PTR(_iseq_addr))' end + def rb_iseq_first_lineno(iseq) + _iseq_addr = iseq.to_i + Primitive.cexpr! 'rb_iseq_first_lineno((rb_iseq_t *)NUM2PTR(_iseq_addr))' + end + def vm_ci_argc(ci) _ci_addr = ci.to_i Primitive.cexpr! 'UINT2NUM(vm_ci_argc((CALL_INFO)NUM2PTR(_ci_addr)))' @@ -461,7 +466,7 @@ def C.rb_iseq_location_t pathobj: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), pathobj)"), true], base_label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), base_label)"), true], label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), label)"), true], - first_lineno: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), first_lineno)"), true], + first_lineno: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), first_lineno)"), true], node_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), node_id)")], code_location: [self.rb_code_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), code_location)")], ) diff --git a/proc.c b/proc.c index fb182efe4671d5..50db2daa460935 100644 --- a/proc.c +++ b/proc.c @@ -1363,7 +1363,7 @@ iseq_location(const rb_iseq_t *iseq) if (!iseq) return Qnil; rb_iseq_check(iseq); loc[0] = rb_iseq_path(iseq); - loc[1] = ISEQ_BODY(iseq)->location.first_lineno; + loc[1] = RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno); return rb_ary_new4(2, loc); } @@ -1544,7 +1544,7 @@ rb_block_to_s(VALUE self, const struct rb_block *block, const char *additional_i const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq); rb_str_catf(str, "%p %"PRIsVALUE":%d", (void *)self, rb_iseq_path(iseq), - FIX2INT(ISEQ_BODY(iseq)->location.first_lineno)); + ISEQ_BODY(iseq)->location.first_lineno); } break; case block_type_symbol: @@ -3506,7 +3506,7 @@ proc_binding(VALUE self) if (iseq) { rb_iseq_check(iseq); RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj); - bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq)); + bind->first_lineno = ISEQ_BODY(iseq)->location.first_lineno; } else { RB_OBJ_WRITE(bindval, &bind->pathobj, diff --git a/vm_backtrace.c b/vm_backtrace.c index 2e898507dfb995..3aae59bf68b016 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -46,7 +46,7 @@ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id) VM_ASSERT(! ISEQ_BODY(iseq)->local_table_size); return 0; } - if (lineno) *lineno = FIX2INT(ISEQ_BODY(iseq)->location.first_lineno); + if (lineno) *lineno = ISEQ_BODY(iseq)->location.first_lineno; #ifdef USE_ISEQ_NODE_ID if (node_id) *node_id = -1; #endif @@ -105,7 +105,7 @@ rb_vm_get_sourceline(const rb_control_frame_t *cfp) return line; } else { - return FIX2INT(rb_iseq_first_lineno(iseq)); + return ISEQ_BODY(iseq)->location.first_lineno; } } else { diff --git a/vm_core.h b/vm_core.h index 7dc53399b40ccb..e11838a59a3aaa 100644 --- a/vm_core.h +++ b/vm_core.h @@ -310,7 +310,7 @@ typedef struct rb_iseq_location_struct { VALUE pathobj; /* String (path) or Array [path, realpath]. Frozen. */ VALUE base_label; /* String */ VALUE label; /* String */ - VALUE first_lineno; + int first_lineno; int node_id; rb_code_location_t code_location; } rb_iseq_location_t; @@ -1193,7 +1193,7 @@ extern const rb_data_type_t ruby_binding_data_type; typedef struct { const struct rb_block block; const VALUE pathobj; - unsigned short first_lineno; + int first_lineno; } rb_binding_t; /* used by compile time and send insn */ diff --git a/vm_method.c b/vm_method.c index 03ab66d580ef60..08e91ea0392f91 100644 --- a/vm_method.c +++ b/vm_method.c @@ -909,7 +909,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil } if (iseq) { rb_compile_warning(RSTRING_PTR(rb_iseq_path(iseq)), - FIX2INT(ISEQ_BODY(iseq)->location.first_lineno), + ISEQ_BODY(iseq)->location.first_lineno, "previous definition of %"PRIsVALUE" was here", rb_id2str(old_def->original_id)); } From 85cc0ce5c8b6a369d439093453f7b87f1a154efb Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 25 Sep 2022 21:49:45 +1300 Subject: [PATCH 235/269] Use `int first_lineno` for binary format. --- compile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compile.c b/compile.c index d9a17560be0c9a..cf2ddea5e20247 100644 --- a/compile.c +++ b/compile.c @@ -12024,7 +12024,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) ibf_dump_write_small_value(dump, location_pathobj_index); ibf_dump_write_small_value(dump, location_base_label_index); ibf_dump_write_small_value(dump, location_label_index); - ibf_dump_write_small_value(dump, RB_INT2NUM(body->location.first_lineno)); + ibf_dump_write_small_value(dump, body->location.first_lineno); ibf_dump_write_small_value(dump, body->location.node_id); ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.lineno); ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.column); @@ -12135,7 +12135,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos); const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos); const VALUE location_label_index = ibf_load_small_value(load, &reading_pos); - const VALUE location_first_lineno = ibf_load_small_value(load, &reading_pos); + const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos); const int location_node_id = (int)ibf_load_small_value(load, &reading_pos); const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos); const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos); @@ -12195,7 +12195,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) load_body->variable.flip_count = variable_flip_count; load_body->variable.script_lines = Qnil; - load_body->location.first_lineno = RB_NUM2INT(location_first_lineno); + load_body->location.first_lineno = location_first_lineno; load_body->location.node_id = location_node_id; load_body->location.code_location.beg_pos.lineno = location_code_location_beg_pos_lineno; load_body->location.code_location.beg_pos.column = location_code_location_beg_pos_column; From dc7d929e542a3372113f7864b26384ceab596197 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 25 Sep 2022 22:40:05 +0900 Subject: [PATCH 236/269] Extract `RUBY_RELEASE_DATE` from also revision.h This make variable is very useful for daily build. --- configure.ac | 15 +++++++++------ template/Makefile.in | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index d477ea1dc4ae97..55288b92c0db59 100644 --- a/configure.ac +++ b/configure.ac @@ -4384,12 +4384,15 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [ AS_CASE("$VCS", ['$(GIT)'|git], [VCSUP='$(VCS) pull --rebase $(GITPULLOPTIONS)'], [VCSUP='$(VCS)']) - sed -n \ - -e '[/^@%:@define \(RUBY_RELEASE_[A-Z]*\) \([0-9][0-9]*\)/]{' \ - -e 's//\1 = \2/' \ - -e '[s/ \([0-9]\)$/ 0\1/]' \ - -e p \ - -e '}' "$srcdir/version.h" + for f in "$srcdir/version.h" "$srcdir/revision.h"; do + test -f "$f" || continue + sed -n \ + -e '[/^@%:@define \(RUBY_RELEASE_[A-Z]*\) \([0-9][0-9]*\)/]{' \ + -e 's//\1 = \2/' \ + -e '[s/ \([0-9]\)$/ 0\1/]' \ + -e p \ + -e '}' "$f" + done sed '/^MISSING/s/\$U\././g;/^VCS *=/s#@VCS@#'"$VCS"'#;/^VCSUP *=/s#@VCSUP@#'"$VCSUP"'#' Makefile echo; test x"$EXEEXT" = x || echo 'miniruby: miniruby$(EXEEXT)' AS_IF([test "$gnumake" != yes], [ diff --git a/template/Makefile.in b/template/Makefile.in index 1a7843edb6987c..854b0f00145f99 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -381,7 +381,7 @@ install-cross: $(arch)-fake.rb $(RBCONFIG) rbconfig.rb $(arch_hdrdir)/ruby/confi Makefile: $(srcdir)/template/Makefile.in $(srcdir)/enc/Makefile.in -$(MKFILES): config.status $(srcdir)/version.h $(ABI_VERSION_HDR) +$(MKFILES): config.status $(srcdir)/version.h $(srcdir)/revision.h $(ABI_VERSION_HDR) @[ -f $@ ] && mv $@ $@.old MAKE=$(MAKE) $(SHELL) ./config.status $@ @cmp $@ $@.old > /dev/null 2>&1 && echo $@ unchanged && exit 0; \ From 1481e6828ccbd42efe8c1a92620b2942fc089790 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 26 Sep 2022 09:21:05 +0900 Subject: [PATCH 237/269] Avoid type limits (#6435) --- mjit_compiler.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mjit_compiler.c b/mjit_compiler.c index 82be9e77356882..1982ad925cc590 100644 --- a/mjit_compiler.c +++ b/mjit_compiler.c @@ -169,12 +169,9 @@ extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); #define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8) #define SIZEOF(type) RB_SIZE2NUM(sizeof(type)) -#define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(0)) +#define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(1)) -RBIMPL_WARNING_PUSH() -RBIMPL_WARNING_IGNORED(-Wtype-limits) // for SIGNED_TYPE_P #include "mjit_c.rbinc" -RBIMPL_WARNING_POP() #include "mjit_compiler.rbinc" From d89f8a046753e2f166ee252510aadf1579dbcd63 Mon Sep 17 00:00:00 2001 From: Yuichiro Kaneko Date: Mon, 26 Sep 2022 09:38:24 +0900 Subject: [PATCH 238/269] [ruby/rdoc] Fix ruby script in "test_parse_method_bracket" (https://github.com/ruby/rdoc/pull/927) Because it's syntax error. https://github.com/ruby/rdoc/commit/993f2532ff --- test/rdoc/test_rdoc_parser_ruby.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb index ef8ad91668abcd..76fb28808c6c52 100644 --- a/test/rdoc/test_rdoc_parser_ruby.rb +++ b/test/rdoc/test_rdoc_parser_ruby.rb @@ -1960,10 +1960,10 @@ def meth_with_args_before def test_parse_method_bracket util_parser <<-RUBY class C - def [] end - def self.[] end - def []= end - def self.[]= end + def []; end + def self.[]; end + def []=; end + def self.[]=; end end RUBY From e3cc1a6cae0e6c88c04cd54c3afa3c022bb6772c Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 24 Sep 2022 03:36:04 +0900 Subject: [PATCH 239/269] Initialize Objective-C classes before fork() for macOS 13 Since macOS 13, CFString family API used in `rb_str_append_normalized_ospath` may internally use Objective-C classes (`NSTaggedPointerString` and `NSPlaceholderMutableString`) for small strings. On the other hand, Objective-C classes should not be used for the first time in a `fork()`'ed but not `exec()`'ed process. Violations for this rule can result deadlock during class initialization, so Objective-C runtime conservatively crashes on such cases by default. Therefore, we need to use CFString API to initialize Objective-C classes used internally *before* `fork()`. For more details, see https://bugs.ruby-lang.org/issues/18912 --- file.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/file.c b/file.c index cf67dd2aafd962..709c33cca7c863 100644 --- a/file.c +++ b/file.c @@ -268,6 +268,46 @@ rb_str_encode_ospath(VALUE path) #ifdef __APPLE__ # define NORMALIZE_UTF8PATH 1 + +# ifdef HAVE_WORKING_FORK +static void +rb_CFString_class_initialize_before_fork(void) +{ + /* + * Since macOS 13, CFString family API used in + * rb_str_append_normalized_ospath may internally use Objective-C classes + * (NSTaggedPointerString and NSPlaceholderMutableString) for small strings. + * + * On the other hand, Objective-C classes should not be used for the first + * time in a fork()'ed but not exec()'ed process. Violations for this rule + * can result deadlock during class initialization, so Objective-C runtime + * conservatively crashes on such cases by default. + * + * Therefore, we need to use CFString API to initialize Objective-C classes + * used internally *before* fork(). + * + * For future changes, please note that this initialization process cannot + * be done in ctor because NSTaggedPointerString in CoreFoundation is enabled + * after CFStringInitializeTaggedStrings(), which is called during loading + * Objective-C runtime after ctor. + * For more details, see https://bugs.ruby-lang.org/issues/18912 + */ + + /* Enough small but non-empty ASCII string to fit in NSTaggedPointerString. */ + const char small_str[] = "/"; + long len = sizeof(small_str) - 1; + + const CFAllocatorRef alloc = kCFAllocatorDefault; + CFStringRef s = CFStringCreateWithBytesNoCopy(alloc, + (const UInt8 *)small_str, + len, kCFStringEncodingUTF8, + FALSE, kCFAllocatorNull); + CFMutableStringRef m = CFStringCreateMutableCopy(alloc, len, s); + CFRelease(m); + CFRelease(s); +} +# endif + static VALUE rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len) { @@ -7317,6 +7357,10 @@ const char ruby_null_device[] = void Init_File(void) { +#if defined(__APPLE__) && defined(HAVE_WORKING_FORK) + rb_CFString_class_initialize_before_fork(); +#endif + VALUE separator; rb_mFileTest = rb_define_module("FileTest"); From b361bdc20036688f17f1e39a260a70254e7db9cd Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Sun, 25 Sep 2022 19:54:49 -0700 Subject: [PATCH 240/269] [Bug #19021] Fix safe call w/ conditional assign As of fbaac837cfba23a9d34dc7ee144d7940248222a2, when we were performing a safe call (`o&.x=`) with a conditional assign (`||= 1`) and discarding the result the stack would end up in a bad state due to a missing pop. This commit fixes that by adjusting the target label of the branchnil to be before a pop in that case (as was previously done in the non-conditional assignment case). --- compile.c | 18 +++++++----------- test/ruby/test_call.rb | 7 +++++++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compile.c b/compile.c index cf2ddea5e20247..a5da919c0ae4e9 100644 --- a/compile.c +++ b/compile.c @@ -8728,10 +8728,6 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node } ADD_LABEL(ret, lfin); - ADD_INSN(ret, node, pop); - if (lskip) { - ADD_LABEL(ret, lskip); - } } else { CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value)); @@ -8741,13 +8737,13 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_INSN1(ret, node, topn, INT2FIX(1)); } ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag)); - if (lskip && popped) { - ADD_LABEL(ret, lskip); - } - ADD_INSN(ret, node, pop); - if (lskip && !popped) { - ADD_LABEL(ret, lskip); - } + } + if (lskip && popped) { + ADD_LABEL(ret, lskip); + } + ADD_INSN(ret, node, pop); + if (lskip && !popped) { + ADD_LABEL(ret, lskip); } return COMPILE_OK; } diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb index 67b3a936d4f28e..88e2ddcb7b6f6e 100644 --- a/test/ruby/test_call.rb +++ b/test/ruby/test_call.rb @@ -47,12 +47,19 @@ def test_safe_call assert_equal(5, o.y) o&.z ||= 6 assert_equal(6, o.z) + o&.z &&= 7 + assert_equal(7, o.z) o = nil assert_nil(o&.x) assert_nothing_raised(NoMethodError) {o&.x = raise} + assert_nothing_raised(NoMethodError) {o&.x = raise; nil} assert_nothing_raised(NoMethodError) {o&.x *= raise} assert_nothing_raised(NoMethodError) {o&.x *= raise; nil} + assert_nothing_raised(NoMethodError) {o&.x ||= raise} + assert_nothing_raised(NoMethodError) {o&.x ||= raise; nil} + assert_nothing_raised(NoMethodError) {o&.x &&= raise} + assert_nothing_raised(NoMethodError) {o&.x &&= raise; nil} end def test_safe_call_evaluate_arguments_only_method_call_is_made From ecffc6a203822a828d00afece9c9cd782ff60a66 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 26 Sep 2022 10:20:24 +0900 Subject: [PATCH 241/269] Generate the revision.h before Makefile Except for GNU make which updates makefiles automatically, repeating configure in the same directory causes `make` to stop whenever pulled a new commit. This is unexpected in CIs. --- configure.ac | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configure.ac b/configure.ac index 55288b92c0db59..206470ae0f5936 100644 --- a/configure.ac +++ b/configure.ac @@ -4368,6 +4368,14 @@ AC_SUBST(XCC_WRAPPER) AS_CASE([" $CPP "], [*" $CC "*], [CPP=`echo " $CPP " | sed "s| $CC |"' $(CC) |;s/^ *//;s/ *$//'`]) +AS_IF([test ! -f "$srcdir/revision.h"], [ + AS_IF([test "x$HAVE_BASERUBY" = xyes], [ + ${BASERUBY} -C "$srcdir" tool/file2lastrev.rb -q --revision.h > "$srcdir/revision.h" + ], [ + touch "$srcdir/revision.h" + ]) +]) + AS_IF([test x"$firstmf" != x], [ AC_CONFIG_FILES($firstmf:$firsttmpl, [], [firstmf="$firstmf" firsttmpl="$firsttmpl"]) ]) From 025b8701c09813c40339bd8fa1c75e0e5eaf7120 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 26 Sep 2022 18:06:12 +1300 Subject: [PATCH 242/269] Add several new methods for getting and setting buffer contents. (#6434) --- benchmark/buffer_each.yml | 27 ++ benchmark/buffer_get.yml | 25 +- common.mk | 1 + io_buffer.c | 509 ++++++++++++++++++++++++++++-------- test/ruby/test_io_buffer.rb | 50 +++- 5 files changed, 500 insertions(+), 112 deletions(-) create mode 100644 benchmark/buffer_each.yml diff --git a/benchmark/buffer_each.yml b/benchmark/buffer_each.yml new file mode 100644 index 00000000000000..417941104ecef2 --- /dev/null +++ b/benchmark/buffer_each.yml @@ -0,0 +1,27 @@ +prelude: | + # frozen_string_literal: true + Warning[:experimental] = false + string = "The quick brown fox jumped over the lazy dog." + array = string.bytes + buffer = IO::Buffer.for(string) +benchmark: + string.each_byte: | + upcased = String.new + string.each_byte do |byte| + upcased << (byte ^ 32) + end + array.each: | + upcased = String.new + array.each do |byte| + upcased << (byte ^ 32) + end + buffer.each: | + upcased = String.new + buffer.each(:U8) do |offset, byte| + upcased << (byte ^ 32) + end + buffer.each_byte: | + upcased = String.new + buffer.each_byte do |byte| + upcased << (byte ^ 32) + end diff --git a/benchmark/buffer_get.yml b/benchmark/buffer_get.yml index bb9ca7e94aca7f..9e1f99d64e8643 100644 --- a/benchmark/buffer_get.yml +++ b/benchmark/buffer_get.yml @@ -1,10 +1,25 @@ prelude: | # frozen_string_literal: true Warning[:experimental] = false - buffer = IO::Buffer.new(32, IO::Buffer::MAPPED) - string = "\0" * 32 + string = "The quick brown fox jumped over the lazy dog." + buffer = IO::Buffer.for(string) + format = [:U32, :U32, :U32, :U32] benchmark: - buffer.get_value: | - buffer.get_value(:U32, 0) string.unpack1: | - string.unpack1("N") + [ + string.unpack1("N"), + string.unpack1("N", offset: 4), + string.unpack1("N", offset: 8), + string.unpack1("N", offset: 12), + ] + buffer.get_value: | + [ + buffer.get_value(:U32, 0), + buffer.get_value(:U32, 4), + buffer.get_value(:U32, 8), + buffer.get_value(:U32, 12), + ] + buffer.get_values: | + buffer.get_values(format, 0) + string.unpack: | + string.unpack("NNNN") diff --git a/common.mk b/common.mk index 4678fd8940c882..51adabc7836d8f 100644 --- a/common.mk +++ b/common.mk @@ -7669,6 +7669,7 @@ io.$(OBJEXT): {$(VPATH)}util.h 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/bits.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/compilers.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/error.h diff --git a/io_buffer.c b/io_buffer.c index 98f1da1fc12f53..8ec5dc984b401c 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -6,6 +6,7 @@ **********************************************************************/ +#include "internal/array.h" #include "ruby/io.h" #include "ruby/io/buffer.h" #include "ruby/fiber/scheduler.h" @@ -461,7 +462,6 @@ rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags * * Note that some operating systems may not have cache coherency between mapped * buffers and file reads. - * */ static VALUE io_buffer_map(int argc, VALUE *argv, VALUE klass) @@ -543,7 +543,6 @@ io_flags_for_size(size_t size) * # => * # # * # 0x00000000 74 65 73 74 test - * */ VALUE rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self) @@ -628,7 +627,6 @@ io_buffer_validate(struct rb_io_buffer *data) * * puts IO::Buffer.new(4) # uses to_s internally * # # - * */ VALUE rb_io_buffer_to_s(VALUE self) @@ -756,7 +754,6 @@ rb_io_buffer_inspect(VALUE self) * * Returns the size of the buffer that was explicitly set (on creation with ::new * or on #resize), or deduced on buffer's creation from string or file. - * */ VALUE rb_io_buffer_size(VALUE self) @@ -774,7 +771,6 @@ rb_io_buffer_size(VALUE self) * * A buffer becomes invalid if it is a slice of another buffer which has been * freed. - * */ static VALUE rb_io_buffer_valid_p(VALUE self) @@ -790,7 +786,6 @@ rb_io_buffer_valid_p(VALUE self) * * If the buffer was freed with #free or was never allocated in the first * place. - * */ static VALUE rb_io_buffer_null_p(VALUE self) @@ -807,7 +802,6 @@ rb_io_buffer_null_p(VALUE self) * If the buffer has 0 size: it is created by ::new with size 0, or with ::for * from an empty string. (Note that empty files can't be mapped, so the buffer * created with ::map will never be empty.) - * */ static VALUE rb_io_buffer_empty_p(VALUE self) @@ -828,7 +822,6 @@ rb_io_buffer_empty_p(VALUE self) * memory. * * External buffer can't be resized. - * */ static VALUE rb_io_buffer_external_p(VALUE self) @@ -854,7 +847,6 @@ rb_io_buffer_external_p(VALUE self) * * Internal buffers can be resized, and such an operation will typically * invalidate all slices, but not always. - * */ static VALUE rb_io_buffer_internal_p(VALUE self) @@ -877,7 +869,6 @@ rb_io_buffer_internal_p(VALUE self) * * Mapped buffers can usually be resized, and such an operation will typically * invalidate all slices, but not always. - * */ static VALUE rb_io_buffer_mapped_p(VALUE self) @@ -901,7 +892,6 @@ rb_io_buffer_mapped_p(VALUE self) * buffer.locked do * buffer.write(io) # theoretical system call interface * end - * */ static VALUE rb_io_buffer_locked_p(VALUE self) @@ -928,7 +918,6 @@ rb_io_buffer_readonly_p(VALUE self) * #set_value, #set_string or #copy and similar. * * Frozen strings and read-only files create read-only buffers. - * */ static VALUE io_buffer_readonly_p(VALUE self) @@ -1053,7 +1042,6 @@ rb_io_buffer_locked(VALUE self) * # => true * * You can resize a freed buffer to re-allocate it. - * */ VALUE rb_io_buffer_free(VALUE self) @@ -1115,7 +1103,6 @@ io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length * # ...and original string * string * # => tost - * */ VALUE rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length) @@ -1166,7 +1153,7 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size) return 0; } -static void +inline static void io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size) { if (data->flags & RB_IO_BUFFER_READONLY) { @@ -1238,7 +1225,6 @@ rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size) * # # * buffer.null? * # => true - * */ VALUE rb_io_buffer_transfer(VALUE self) @@ -1358,7 +1344,6 @@ rb_io_buffer_resize(VALUE self, size_t size) * * External buffer (created with ::for), and locked buffer * can not be resized. - * */ static VALUE io_buffer_resize(VALUE self, VALUE size) @@ -1373,7 +1358,6 @@ io_buffer_resize(VALUE self, VALUE size) * * Buffers are compared by size and exact contents of the memory they are * referencing using +memcmp+. - * */ static VALUE rb_io_buffer_compare(VALUE self, VALUE other) @@ -1399,7 +1383,7 @@ static void io_buffer_validate_type(size_t size, size_t offset) { if (offset > size) { - rb_raise(rb_eArgError, "Type extends beyond end of buffer!"); + rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%ld > size=%ld)", offset, size); } } @@ -1449,8 +1433,8 @@ ruby_swapf64(double value) return swap.value; } -#define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \ -static ID RB_IO_BUFFER_TYPE_##name; \ +#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \ +static ID RB_IO_BUFFER_DATA_TYPE_##name; \ \ static VALUE \ io_buffer_read_##name(const void* base, size_t size, size_t *offset) \ @@ -1471,67 +1455,123 @@ io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _val if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \ memcpy((char*)base + *offset, &value, sizeof(type)); \ *offset += sizeof(type); \ -} - -DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8) -DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8) - -DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) -DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) -DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) -DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) +} \ +\ +enum { \ + RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \ +}; -DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) -DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) -DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) -DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8) +IO_BUFFER_DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8) + +IO_BUFFER_DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) + +IO_BUFFER_DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) + +IO_BUFFER_DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) +IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) +IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) +IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) + +IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) +IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) +IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) +IO_BUFFER_DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) +#undef IO_BUFFER_DECLARE_TYPE + +static inline size_t +io_buffer_data_type_size(ID data_type) { +#define IO_BUFFER_DATA_TYPE_SIZE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE; + IO_BUFFER_DATA_TYPE_SIZE(U8) + IO_BUFFER_DATA_TYPE_SIZE(S8) + IO_BUFFER_DATA_TYPE_SIZE(u16) + IO_BUFFER_DATA_TYPE_SIZE(U16) + IO_BUFFER_DATA_TYPE_SIZE(s16) + IO_BUFFER_DATA_TYPE_SIZE(S16) + IO_BUFFER_DATA_TYPE_SIZE(u32) + IO_BUFFER_DATA_TYPE_SIZE(U32) + IO_BUFFER_DATA_TYPE_SIZE(s32) + IO_BUFFER_DATA_TYPE_SIZE(S32) + IO_BUFFER_DATA_TYPE_SIZE(u64) + IO_BUFFER_DATA_TYPE_SIZE(U64) + IO_BUFFER_DATA_TYPE_SIZE(s64) + IO_BUFFER_DATA_TYPE_SIZE(S64) + IO_BUFFER_DATA_TYPE_SIZE(f32) + IO_BUFFER_DATA_TYPE_SIZE(F32) + IO_BUFFER_DATA_TYPE_SIZE(f64) + IO_BUFFER_DATA_TYPE_SIZE(F64) +#undef IO_BUFFER_DATA_TYPE_SIZE -DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) -DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) -DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) -DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) + rb_raise(rb_eArgError, "Invalid type name!"); +} -DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) -DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) -DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) -DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) -#undef DECLARE_TYPE +/* + * call-seq: + * size_of(data_type) -> byte size + * size_of(array of data_type) -> byte size + * + * Returns the size of the given data type(s) in bytes. + * + * Example: + * + * IO::Buffer.size_of(:u32) # => 4 + * IO::Buffer.size_of([:u32, :u32]) # => 8 + */ +static VALUE +io_buffer_size_of(VALUE klass, VALUE data_type) +{ + if (RB_TYPE_P(data_type, T_ARRAY)) { + size_t total = 0; + for (long i = 0; i < RARRAY_LEN(data_type); i++) { + total += io_buffer_data_type_size(RB_SYM2ID(RARRAY_AREF(data_type, i))); + } + return SIZET2NUM(total); + } else { + return SIZET2NUM(io_buffer_data_type_size(RB_SYM2ID(data_type))); + } +} -VALUE -rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset) +static inline VALUE +rb_io_buffer_get_value(const void* base, size_t size, ID data_type, size_t *offset) { -#define READ_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) return io_buffer_read_##name(base, size, &offset); - READ_TYPE(U8); - READ_TYPE(S8); +#define IO_BUFFER_GET_VALUE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset); + IO_BUFFER_GET_VALUE(U8) + IO_BUFFER_GET_VALUE(S8) - READ_TYPE(u16); - READ_TYPE(U16); - READ_TYPE(s16); - READ_TYPE(S16); + IO_BUFFER_GET_VALUE(u16) + IO_BUFFER_GET_VALUE(U16) + IO_BUFFER_GET_VALUE(s16) + IO_BUFFER_GET_VALUE(S16) - READ_TYPE(u32); - READ_TYPE(U32); - READ_TYPE(s32); - READ_TYPE(S32); + IO_BUFFER_GET_VALUE(u32) + IO_BUFFER_GET_VALUE(U32) + IO_BUFFER_GET_VALUE(s32) + IO_BUFFER_GET_VALUE(S32) - READ_TYPE(u64); - READ_TYPE(U64); - READ_TYPE(s64); - READ_TYPE(S64); + IO_BUFFER_GET_VALUE(u64) + IO_BUFFER_GET_VALUE(U64) + IO_BUFFER_GET_VALUE(s64) + IO_BUFFER_GET_VALUE(S64) - READ_TYPE(f32); - READ_TYPE(F32); - READ_TYPE(f64); - READ_TYPE(F64); -#undef READ_TYPE + IO_BUFFER_GET_VALUE(f32) + IO_BUFFER_GET_VALUE(F32) + IO_BUFFER_GET_VALUE(f64) + IO_BUFFER_GET_VALUE(F64) +#undef IO_BUFFER_GET_VALUE rb_raise(rb_eArgError, "Invalid type name!"); } /* - * call-seq: get_value(type, offset) -> numeric + * call-seq: get_value(data_type, offset) -> numeric * - * Read from buffer a value of +type+ at +offset+. +type+ should be one + * Read from buffer a value of +type+ at +offset+. +data_type+ should be one * of symbols: * * * +:U8+: unsigned integer, 1 byte @@ -1553,13 +1593,16 @@ rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset) * * +:f64+: double, 8 bytes, little-endian * * +:F64+: double, 8 bytes, big-endian * + * A data type refers specifically to the type of binary data that is stored + * in the buffer. For example, a +:u32+ data type is a 32-bit unsigned + * integer in little-endian format. + * * Example: * * string = [1.5].pack('f') * # => "\x00\x00\xC0?" * IO::Buffer.for(string).get_value(:f32, 0) * # => 1.5 - * */ static VALUE io_buffer_get_value(VALUE self, VALUE type, VALUE _offset) @@ -1570,36 +1613,230 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset) rb_io_buffer_get_bytes_for_reading(self, &base, &size); - return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset); + return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset); } -void -rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VALUE value) +/* + * call-seq: get_values(data_types, offset) -> array + * + * Similar to #get_value, except that it can handle multiple data types and + * returns an array of values. + * + * Example: + * + * string = [1.5, 2.5].pack('ff') + * IO::Buffer.for(string).get_values([:f32, :f32], 0) + * # => [1.5, 2.5] + */ +static VALUE +io_buffer_get_values(VALUE self, VALUE data_types, VALUE _offset) +{ + size_t offset = NUM2SIZET(_offset); + + const void *base; + size_t size; + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + if (!RB_TYPE_P(data_types, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument data_types should be an array!"); + } + + VALUE array = rb_ary_new_capa(RARRAY_LEN(data_types)); + + for (long i = 0; i < RARRAY_LEN(data_types); i++) { + VALUE type = rb_ary_entry(data_types, i); + VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset); + rb_ary_push(array, value); + } + + return array; +} + +/* + * call-seq: + * each(data_type, [offset, [count]]) {|offset, value| ...} -> self + * each(data_type, [offset, [count]]) -> enumerator + * + * Iterates over the buffer, yielding each +value+ of +data_type+ starting + * from +offset+. + * + * If +count+ is given, only +count+ values will be yielded. + * + * Example: + * + * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value| + * puts "#{offset}: #{value}" + * end + * # 2: 108 + * # 3: 108 + */ +static VALUE +io_buffer_each(int argc, VALUE *argv, VALUE self) +{ + RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS); + + const void *base; + size_t size; + + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + ID data_type; + if (argc >= 1) { + data_type = RB_SYM2ID(argv[0]); + } else { + data_type = RB_IO_BUFFER_DATA_TYPE_U8; + } + + size_t offset; + if (argc >= 2) { + offset = NUM2SIZET(argv[1]); + } else { + offset = 0; + } + + size_t count; + if (argc >= 3) { + count = NUM2SIZET(argv[2]); + } else { + count = (size - offset) / io_buffer_data_type_size(data_type); + } + + for (size_t i = 0; i < count; i++) { + size_t current_offset = offset; + VALUE value = rb_io_buffer_get_value(base, size, data_type, &offset); + rb_yield_values(2, SIZET2NUM(current_offset), value); + } + + return self; +} + +/* + * call-seq: values(data_type, [offset, [count]]) -> array + * + * Returns an array of values of +data_type+ starting from +offset+. + * + * If +count+ is given, only +count+ values will be returned. + * + * Example: + * + * IO::Buffer.for("Hello World").values(:U8, 2, 2) + * # => [108, 108] + */ +static VALUE +io_buffer_values(int argc, VALUE *argv, VALUE self) +{ + const void *base; + size_t size; + + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + ID data_type; + if (argc >= 1) { + data_type = RB_SYM2ID(argv[0]); + } else { + data_type = RB_IO_BUFFER_DATA_TYPE_U8; + } + + size_t offset; + if (argc >= 2) { + offset = NUM2SIZET(argv[1]); + } else { + offset = 0; + } + + size_t count; + if (argc >= 3) { + count = NUM2SIZET(argv[2]); + } else { + count = (size - offset) / io_buffer_data_type_size(data_type); + } + + VALUE array = rb_ary_new_capa(count); + + for (size_t i = 0; i < count; i++) { + VALUE value = rb_io_buffer_get_value(base, size, data_type, &offset); + rb_ary_push(array, value); + } + + return array; +} + +/* + * call-seq: + * each_byte([offset, [count]]) {|offset, byte| ...} -> self + * each_byte([offset, [count]]) -> enumerator + * + * Iterates over the buffer, yielding each byte starting from +offset+. + * + * If +count+ is given, only +count+ bytes will be yielded. + * + * Example: + * + * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte| + * puts "#{offset}: #{byte}" + * end + * # 2: 108 + * # 3: 108 + */ +static VALUE +io_buffer_each_byte(int argc, VALUE *argv, VALUE self) +{ + RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS); + + const void *base; + size_t size; + + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + size_t offset; + if (argc >= 2) { + offset = NUM2SIZET(argv[1]); + } else { + offset = 0; + } + + size_t count; + if (argc >= 3) { + count = NUM2SIZET(argv[2]); + } else { + count = (size - offset); + } + + for (size_t i = 0; i < count; i++) { + unsigned char *value = (unsigned char *)base + i + offset; + rb_yield(RB_INT2FIX(*value)); + } + + return self; +} + +inline static void +rb_io_buffer_set_value(const void* base, size_t size, ID data_type, size_t *offset, VALUE value) { -#define WRITE_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) {io_buffer_write_##name(base, size, &offset, value); return;} - WRITE_TYPE(U8); - WRITE_TYPE(S8); +#define IO_BUFFER_SET_VALUE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;} + IO_BUFFER_SET_VALUE(U8); + IO_BUFFER_SET_VALUE(S8); - WRITE_TYPE(u16); - WRITE_TYPE(U16); - WRITE_TYPE(s16); - WRITE_TYPE(S16); + IO_BUFFER_SET_VALUE(u16); + IO_BUFFER_SET_VALUE(U16); + IO_BUFFER_SET_VALUE(s16); + IO_BUFFER_SET_VALUE(S16); - WRITE_TYPE(u32); - WRITE_TYPE(U32); - WRITE_TYPE(s32); - WRITE_TYPE(S32); + IO_BUFFER_SET_VALUE(u32); + IO_BUFFER_SET_VALUE(U32); + IO_BUFFER_SET_VALUE(s32); + IO_BUFFER_SET_VALUE(S32); - WRITE_TYPE(u64); - WRITE_TYPE(U64); - WRITE_TYPE(s64); - WRITE_TYPE(S64); + IO_BUFFER_SET_VALUE(u64); + IO_BUFFER_SET_VALUE(U64); + IO_BUFFER_SET_VALUE(s64); + IO_BUFFER_SET_VALUE(S64); - WRITE_TYPE(f32); - WRITE_TYPE(F32); - WRITE_TYPE(f64); - WRITE_TYPE(F64); -#undef WRITE_TYPE + IO_BUFFER_SET_VALUE(f32); + IO_BUFFER_SET_VALUE(F32); + IO_BUFFER_SET_VALUE(f64); + IO_BUFFER_SET_VALUE(F64); +#undef IO_BUFFER_SET_VALUE rb_raise(rb_eArgError, "Invalid type name!"); } @@ -1640,7 +1877,53 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value) rb_io_buffer_get_bytes_for_writing(self, &base, &size); - rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value); + rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value); + + return SIZET2NUM(offset); +} + +/* + * call-seq: set_values(data_types, offset, values) -> offset + * + * Write +values+ of +data_types+ at +offset+ to the buffer. +data_types+ + * should be an array of symbols as described in #get_value. +values+ should + * be an array of values to write. + * + * Example: + * + * buffer = IO::Buffer.new(8) + * buffer.set_values([:U8, :U16], 0, [1, 2]) + * buffer + * # => + * # # + * # 0x00000000 01 00 02 00 00 00 00 00 ........ + */ +static VALUE +io_buffer_set_values(VALUE self, VALUE data_types, VALUE _offset, VALUE values) +{ + if (!RB_TYPE_P(data_types, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument data_types should be an array!"); + } + + if (!RB_TYPE_P(values, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument values should be an array!"); + } + + if (RARRAY_LEN(data_types) != RARRAY_LEN(values)) { + rb_raise(rb_eArgError, "Argument data_types and values should have the same length!"); + } + + size_t offset = NUM2SIZET(_offset); + + void *base; + size_t size; + rb_io_buffer_get_bytes_for_writing(self, &base, &size); + + for (long i = 0; i < RARRAY_LEN(data_types); i++) { + VALUE type = rb_ary_entry(data_types, i); + VALUE value = rb_ary_entry(values, i); + rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value); + } return SIZET2NUM(offset); } @@ -1719,7 +2002,6 @@ io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t s * # => * # # * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World - * */ static VALUE rb_io_buffer_initialize_copy(VALUE self, VALUE source) @@ -1790,7 +2072,6 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source) * buffer = IO::Buffer.new(2) * buffer.copy(IO::Buffer.for('test'), 0) * # in `copy': Specified offset+length exceeds source size! (ArgumentError) - * */ static VALUE io_buffer_copy(int argc, VALUE *argv, VALUE self) @@ -1822,7 +2103,6 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self) * # => "st" * buffer.get_string(2, 1) * # => "s" - * */ static VALUE io_buffer_get_string(int argc, VALUE *argv, VALUE self) @@ -1944,7 +2224,6 @@ rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length) * # => * # * # 0x00000000 01 02 02 02 .... - * */ static VALUE io_buffer_clear(int argc, VALUE *argv, VALUE self) @@ -2685,17 +2964,41 @@ Init_IO_Buffer(void) rb_include_module(rb_cIOBuffer, rb_mComparable); -#define DEFINE_TYPE(name) RB_IO_BUFFER_TYPE_##name = rb_intern_const(#name) - DEFINE_TYPE(U8); DEFINE_TYPE(S8); - DEFINE_TYPE(u16); DEFINE_TYPE(U16); DEFINE_TYPE(s16); DEFINE_TYPE(S16); - DEFINE_TYPE(u32); DEFINE_TYPE(U32); DEFINE_TYPE(s32); DEFINE_TYPE(S32); - DEFINE_TYPE(u64); DEFINE_TYPE(U64); DEFINE_TYPE(s64); DEFINE_TYPE(S64); - DEFINE_TYPE(f32); DEFINE_TYPE(F32); DEFINE_TYPE(f64); DEFINE_TYPE(F64); -#undef DEFINE_TYPE +#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name) + IO_BUFFER_DEFINE_DATA_TYPE(U8); + IO_BUFFER_DEFINE_DATA_TYPE(S8); + + IO_BUFFER_DEFINE_DATA_TYPE(u16); + IO_BUFFER_DEFINE_DATA_TYPE(U16); + IO_BUFFER_DEFINE_DATA_TYPE(s16); + IO_BUFFER_DEFINE_DATA_TYPE(S16); + + IO_BUFFER_DEFINE_DATA_TYPE(u32); + IO_BUFFER_DEFINE_DATA_TYPE(U32); + IO_BUFFER_DEFINE_DATA_TYPE(s32); + IO_BUFFER_DEFINE_DATA_TYPE(S32); + + IO_BUFFER_DEFINE_DATA_TYPE(u64); + IO_BUFFER_DEFINE_DATA_TYPE(U64); + IO_BUFFER_DEFINE_DATA_TYPE(s64); + IO_BUFFER_DEFINE_DATA_TYPE(S64); + + IO_BUFFER_DEFINE_DATA_TYPE(f32); + IO_BUFFER_DEFINE_DATA_TYPE(F32); + IO_BUFFER_DEFINE_DATA_TYPE(f64); + IO_BUFFER_DEFINE_DATA_TYPE(F64); +#undef IO_BUFFER_DEFINE_DATA_TYPE + + rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1); // Data access: rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2); + rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2); + rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1); + rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1); + rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1); rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3); + rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3); rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1); diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 0f8a9c5e809edb..88b0a0280ae3c6 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -235,17 +235,59 @@ def test_get_string :F64 => [-1.0, 0.0, 0.5, 1.0, 128.0], } - def test_get_set_primitives + def test_get_set_value buffer = IO::Buffer.new(128) - RANGES.each do |type, values| + RANGES.each do |data_type, values| values.each do |value| - buffer.set_value(type, 0, value) - assert_equal value, buffer.get_value(type, 0), "Converting #{value} as #{type}." + buffer.set_value(data_type, 0, value) + assert_equal value, buffer.get_value(data_type, 0), "Converting #{value} as #{data_type}." end end end + def test_get_set_values + buffer = IO::Buffer.new(128) + + RANGES.each do |data_type, values| + format = [data_type] * values.size + + buffer.set_values(format, 0, values) + assert_equal values, buffer.get_values(format, 0), "Converting #{values} as #{format}." + end + end + + def test_values + buffer = IO::Buffer.new(128) + + RANGES.each do |data_type, values| + format = [data_type] * values.size + + buffer.set_values(format, 0, values) + assert_equal values, buffer.values(data_type, 0, values.size), "Reading #{values} as #{format}." + end + end + + def test_each + buffer = IO::Buffer.new(128) + + RANGES.each do |data_type, values| + format = [data_type] * values.size + data_type_size = IO::Buffer.size_of(data_type) + values_with_offsets = values.map.with_index{|value, index| [index * data_type_size, value]} + + buffer.set_values(format, 0, values) + assert_equal values_with_offsets, buffer.each(data_type, 0, values.size).to_a, "Reading #{values} as #{data_type}." + end + end + + def test_each_byte + string = "The quick brown fox jumped over the lazy dog." + buffer = IO::Buffer.for(string) + + assert_equal string.bytes, buffer.each_byte.to_a + end + def test_clear buffer = IO::Buffer.new(16) buffer.set_string("Hello World!") From a8ad22d926f2c2bc652ce030ccbe2774b13a4b2f Mon Sep 17 00:00:00 2001 From: Shugo Maeda Date: Mon, 26 Sep 2022 14:44:54 +0900 Subject: [PATCH 243/269] Suppress a warning on clang The following warning appears without this fix: ``` parse.y:78:1: warning: unknown warning group '-Wpsabi', ignored [-Wunknown-warning-option] RBIMPL_WARNING_IGNORED(-Wpsabi) ^ ./include/ruby/internal/warning_push.h:103:39: note: expanded from macro 'RBIMPL_WARNING_IGNORED' ^ ./include/ruby/internal/warning_push.h:99:39: note: expanded from macro 'RBIMPL_WARNING_PRAGMA2' ^ ./include/ruby/internal/warning_push.h:98:39: note: expanded from macro 'RBIMPL_WARNING_PRAGMA1' ^ ./include/ruby/internal/warning_push.h:97:39: note: expanded from macro 'RBIMPL_WARNING_PRAGMA0' ^ :49:27: note: expanded from here clang diagnostic ignored "-Wpsabi" ^ 1 warning generated. ``` --- parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse.y b/parse.y index 4f0aa6a6cedacf..b16dc35b88b42a 100644 --- a/parse.y +++ b/parse.y @@ -70,7 +70,7 @@ struct lex_context { BITFIELD(enum shareability, shareable_constant_value, 2); }; -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(__clang__) // Suppress "parameter passing for argument of type 'struct // lex_context' changed" notes. `struct lex_context` is file scope, // and has no ABI compatibility issue. From 411d0eec11f3618066a293ee72810bf48168adc8 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 26 Sep 2022 19:37:28 +1300 Subject: [PATCH 244/269] Update `IO::Buffer` read/write to use rb_thread_io_blocking_region. (#6438) --- common.mk | 1 + io_buffer.c | 161 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 122 insertions(+), 40 deletions(-) diff --git a/common.mk b/common.mk index 51adabc7836d8f..150add3026dac1 100644 --- a/common.mk +++ b/common.mk @@ -7675,6 +7675,7 @@ io_buffer.$(OBJEXT): $(top_srcdir)/internal/compilers.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/error.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): {$(VPATH)}assert.h io_buffer.$(OBJEXT): {$(VPATH)}backward/2/assume.h io_buffer.$(OBJEXT): {$(VPATH)}backward/2/attributes.h diff --git a/io_buffer.c b/io_buffer.c index 8ec5dc984b401c..4326d21defcb7e 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -6,15 +6,16 @@ **********************************************************************/ -#include "internal/array.h" #include "ruby/io.h" #include "ruby/io/buffer.h" #include "ruby/fiber/scheduler.h" #include "internal.h" -#include "internal/string.h" +#include "internal/array.h" #include "internal/bits.h" #include "internal/error.h" +#include "internal/string.h" +#include "internal/thread.h" VALUE rb_cIOBuffer; VALUE rb_eIOBufferLockedError; @@ -2281,6 +2282,20 @@ io_buffer_default_size(size_t page_size) return platform_agnostic_default_size; } +struct io_buffer_read_internal_argument { + int descriptor; + void *base; + size_t size; +}; + +static VALUE +io_buffer_read_internal(void *_argument) +{ + struct io_buffer_read_internal_argument *argument = _argument; + ssize_t result = read(argument->descriptor, argument->base, argument->size); + return rb_fiber_scheduler_io_result(result, errno); +} + VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length) { @@ -2304,9 +2319,13 @@ rb_io_buffer_read(VALUE self, VALUE io, size_t length) size_t size; io_buffer_get_bytes_for_writing(data, &base, &size); - ssize_t result = read(descriptor, base, size); + struct io_buffer_read_internal_argument argument = { + .descriptor = descriptor, + .base = base, + .size = length, + }; - return rb_fiber_scheduler_io_result(result, errno); + return rb_thread_io_blocking_region(io_buffer_read_internal, &argument, descriptor); } static VALUE @@ -2315,6 +2334,38 @@ io_buffer_read(VALUE self, VALUE io, VALUE length) return rb_io_buffer_read(self, io, RB_NUM2SIZE(length)); } +struct io_buffer_pread_internal_argument { + int descriptor; + void *base; + size_t size; + off_t offset; +}; + +static VALUE +io_buffer_pread_internal(void *_argument) +{ + struct io_buffer_pread_internal_argument *argument = _argument; + +#if defined(HAVE_PREAD) + ssize_t result = pread(argument->descriptor, argument->base, argument->size, argument->offset); +#else + // This emulation is not thread safe. + rb_off_t offset = lseek(argument->descriptor, 0, SEEK_CUR); + if (offset == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); + + if (lseek(argument->descriptor, argument->offset, SEEK_SET) == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); + + ssize_t result = read(argument->descriptor, argument->base, argument->size); + + if (lseek(argument->descriptor, offset, SEEK_SET) == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); +#endif + + return rb_fiber_scheduler_io_result(result, errno); +} + VALUE rb_io_buffer_pread(VALUE self, VALUE io, size_t length, rb_off_t offset) { @@ -2338,24 +2389,14 @@ rb_io_buffer_pread(VALUE self, VALUE io, size_t length, rb_off_t offset) size_t size; io_buffer_get_bytes_for_writing(data, &base, &size); -#if defined(HAVE_PREAD) - ssize_t result = pread(descriptor, base, size, offset); -#else - // This emulation is not thread safe, but the GVL means it's unlikely to be a problem. - rb_off_t current_offset = lseek(descriptor, 0, SEEK_CUR); - if (current_offset == (rb_off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); - - if (lseek(descriptor, offset, SEEK_SET) == (rb_off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); - - ssize_t result = read(descriptor, base, size); - - if (lseek(descriptor, current_offset, SEEK_SET) == (rb_off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); -#endif + struct io_buffer_pread_internal_argument argument = { + .descriptor = descriptor, + .base = base, + .size = length, + .offset = offset, + }; - return rb_fiber_scheduler_io_result(result, errno); + return rb_thread_io_blocking_region(io_buffer_pread_internal, &argument, descriptor); } static VALUE @@ -2364,6 +2405,20 @@ io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset) return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset)); } +struct io_buffer_write_internal_argument { + int descriptor; + const void *base; + size_t size; +}; + +static VALUE +io_buffer_write_internal(void *_argument) +{ + struct io_buffer_write_internal_argument *argument = _argument; + ssize_t result = write(argument->descriptor, argument->base, argument->size); + return rb_fiber_scheduler_io_result(result, errno); +} + VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length) { @@ -2387,9 +2442,13 @@ rb_io_buffer_write(VALUE self, VALUE io, size_t length) size_t size; io_buffer_get_bytes_for_reading(data, &base, &size); - ssize_t result = write(descriptor, base, length); + struct io_buffer_write_internal_argument argument = { + .descriptor = descriptor, + .base = base, + .size = length, + }; - return rb_fiber_scheduler_io_result(result, errno); + return rb_thread_io_blocking_region(io_buffer_write_internal, &argument, descriptor); } static VALUE @@ -2398,6 +2457,38 @@ io_buffer_write(VALUE self, VALUE io, VALUE length) return rb_io_buffer_write(self, io, RB_NUM2SIZE(length)); } +struct io_buffer_pwrite_internal_argument { + int descriptor; + const void *base; + size_t size; + off_t offset; +}; + +static VALUE +io_buffer_pwrite_internal(void *_argument) +{ + struct io_buffer_pwrite_internal_argument *argument = _argument; + +#if defined(HAVE_PWRITE) + ssize_t result = pwrite(argument->descriptor, argument->base, argument->size, argument->offset); +#else + // This emulation is not thread safe. + rb_off_t offset = lseek(argument->descriptor, 0, SEEK_CUR); + if (offset == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); + + if (lseek(argument->descriptor, argument->offset, SEEK_SET) == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); + + ssize_t result = write(argument->descriptor, argument->base, argument->size); + + if (lseek(argument->descriptor, offset, SEEK_SET) == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); +#endif + + return rb_fiber_scheduler_io_result(result, errno); +} + VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset) { @@ -2421,24 +2512,14 @@ rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset) size_t size; io_buffer_get_bytes_for_reading(data, &base, &size); -#if defined(HAVE_PWRITE) - ssize_t result = pwrite(descriptor, base, length, offset); -#else - // This emulation is not thread safe, but the GVL means it's unlikely to be a problem. - rb_off_t current_offset = lseek(descriptor, 0, SEEK_CUR); - if (current_offset == (rb_off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); - - if (lseek(descriptor, offset, SEEK_SET) == (rb_off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); + struct io_buffer_pwrite_internal_argument argument = { + .descriptor = descriptor, + .base = base, + .size = length, + .offset = offset, + }; - ssize_t result = write(descriptor, base, length); - - if (lseek(descriptor, current_offset, SEEK_SET) == (rb_off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); -#endif - - return rb_fiber_scheduler_io_result(result, errno); + return rb_thread_io_blocking_region(io_buffer_pwrite_internal, &argument, descriptor); } static VALUE From 1c14e406d3c4a4c660f66f0d1c642d1ed2aabed2 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 26 Sep 2022 19:40:23 +1300 Subject: [PATCH 245/269] Fix `io/buffer.h` header guard. --- include/ruby/io/buffer.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h index bb83fe0be6d9b7..16b23ec6290871 100644 --- a/include/ruby/io/buffer.h +++ b/include/ruby/io/buffer.h @@ -1,5 +1,5 @@ -#ifndef RUBY_IO_BUFFER_T -#define RUBY_IO_BUFFER_T 1 +#ifndef RUBY_IO_BUFFER_H +#define RUBY_IO_BUFFER_H /** * @file * @author Samuel Williams @@ -88,4 +88,4 @@ VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset); RBIMPL_SYMBOL_EXPORT_END() -#endif /* RUBY_IO_BUFFER_T */ +#endif /* RUBY_IO_BUFFER_H */ From 5b0396473bbcd70756a09d887fb7436d6cd72dce Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Mon, 26 Sep 2022 16:39:53 +0900 Subject: [PATCH 246/269] Fix coderange calculation in String#b Leave the new coderange unknown if the original encoding is not ASCII-compatible. Non-ASCII-compatible encoding strings with valid or broken coderange can end up as ascii-only. Fixes 9a8f6e392fbd ("Cheaply derive code range for String#b return value", 2022-07-25). --- string.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/string.c b/string.c index c5711bcfe301ac..b455c8b7d9f918 100644 --- a/string.c +++ b/string.c @@ -10771,20 +10771,22 @@ rb_str_b(VALUE str) } str_replace_shared_without_enc(str2, str); - // BINARY strings can never be broken; they're either 7-bit ASCII or VALID. - // If we know the receiver's code range then we know the result's code range. - int cr = ENC_CODERANGE(str); - switch (cr) { - case ENC_CODERANGE_7BIT: - ENC_CODERANGE_SET(str2, ENC_CODERANGE_7BIT); - break; - case ENC_CODERANGE_BROKEN: - case ENC_CODERANGE_VALID: - ENC_CODERANGE_SET(str2, ENC_CODERANGE_VALID); - break; - default: - ENC_CODERANGE_CLEAR(str2); - break; + if (rb_enc_asciicompat(STR_ENC_GET(str))) { + // BINARY strings can never be broken; they're either 7-bit ASCII or VALID. + // If we know the receiver's code range then we know the result's code range. + int cr = ENC_CODERANGE(str); + switch (cr) { + case ENC_CODERANGE_7BIT: + ENC_CODERANGE_SET(str2, ENC_CODERANGE_7BIT); + break; + case ENC_CODERANGE_BROKEN: + case ENC_CODERANGE_VALID: + ENC_CODERANGE_SET(str2, ENC_CODERANGE_VALID); + break; + default: + ENC_CODERANGE_CLEAR(str2); + break; + } } return str2; From 2e88bca24ff4cafeb6afe5b062ff7181bc4b3a9b Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 26 Sep 2022 12:48:24 +0200 Subject: [PATCH 247/269] string.c: don't create a frozen copy for str_new_shared str_new_shared already has all the necessary logic to do this and is also smart enough to skip this step if the source string is already a shared string itself. This saves a useless String allocation on each call. --- string.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/string.c b/string.c index b455c8b7d9f918..1e7b16fbba877a 100644 --- a/string.c +++ b/string.c @@ -2923,8 +2923,7 @@ str_substr(VALUE str, long beg, long len, int empty) if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && SHARABLE_SUBSTRING_P(p, len, RSTRING_END(str))) { long ofs = p - RSTRING_PTR(str); - str2 = rb_str_new_frozen(str); - str2 = str_new_shared(rb_cString, str2); + str2 = str_new_shared(rb_cString, str); RSTRING(str2)->as.heap.ptr += ofs; RSTRING(str2)->as.heap.len = len; ENC_CODERANGE_CLEAR(str2); @@ -6162,8 +6161,7 @@ str_byte_substr(VALUE str, long beg, long len, int empty) p = s + beg; if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && SHARABLE_SUBSTRING_P(beg, len, n)) { - str2 = rb_str_new_frozen(str); - str2 = str_new_shared(rb_cString, str2); + str2 = str_new_shared(rb_cString, str); RSTRING(str2)->as.heap.ptr += beg; RSTRING(str2)->as.heap.len = len; } From 9ddfd2ca004d1952be79cf1b84c52c79a55978f4 Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Fri, 23 Sep 2022 13:54:42 -0400 Subject: [PATCH 248/269] This commit implements the Object Shapes technique in CRuby. Object Shapes is used for accessing instance variables and representing the "frozenness" of objects. Object instances have a "shape" and the shape represents some attributes of the object (currently which instance variables are set and the "frozenness"). Shapes form a tree data structure, and when a new instance variable is set on an object, that object "transitions" to a new shape in the shape tree. Each shape has an ID that is used for caching. The shape structure is independent of class, so objects of different types can have the same shape. For example: ```ruby class Foo def initialize # Starts with shape id 0 @a = 1 # transitions to shape id 1 @b = 1 # transitions to shape id 2 end end class Bar def initialize # Starts with shape id 0 @a = 1 # transitions to shape id 1 @b = 1 # transitions to shape id 2 end end foo = Foo.new # `foo` has shape id 2 bar = Bar.new # `bar` has shape id 2 ``` Both `foo` and `bar` instances have the same shape because they both set instance variables of the same name in the same order. This technique can help to improve inline cache hits as well as generate more efficient machine code in JIT compilers. This commit also adds some methods for debugging shapes on objects. See `RubyVM::Shape` for more details. For more context on Object Shapes, see [Feature: #18776] Co-Authored-By: Aaron Patterson Co-Authored-By: Eileen M. Uchitelle Co-Authored-By: John Hawthorn --- bootstraptest/test_attr.rb | 16 + common.mk | 322 +++++++ compile.c | 26 +- debug_counter.h | 10 +- ext/coverage/depend | 4 + ext/objspace/depend | 5 + ext/objspace/objspace.c | 1 + gc.c | 219 +++-- 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/imemo.h | 1 + internal/object.h | 22 - internal/variable.h | 5 + iseq.c | 16 +- lib/mjit/compiler.rb | 117 +-- marshal.c | 10 +- misc/lldb_cruby.py | 6 +- mjit_c.rb | 37 +- mjit_compiler.h | 2 +- object.c | 46 +- ractor_core.h | 6 +- shape.c | 571 +++++++++++++ shape.h | 153 ++++ .../reachable_objects_from_spec.rb | 2 +- spec/ruby/optional/capi/shared/rbasic.rb | 11 +- test/-ext-/marshal/test_internal_ivar.rb | 1 + test/objspace/test_objspace.rb | 14 +- test/ruby/test_mjit.rb | 4 +- test/ruby/test_shapes.rb | 171 ++++ tool/mjit/bindgen.rb | 10 +- variable.c | 784 ++++++++---------- variable.h | 10 +- vm.c | 45 + vm_callinfo.h | 108 ++- vm_core.h | 11 +- vm_eval.c | 4 +- vm_insnhelper.c | 490 +++++++---- yjit/bindgen/src/main.rs | 7 + yjit/src/asm/x86_64/mod.rs | 2 +- yjit/src/codegen.rs | 135 ++- yjit/src/cruby.rs | 12 +- yjit/src/cruby_bindings.inc.rs | 41 +- 45 files changed, 2548 insertions(+), 946 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 150add3026dac1..cfcd6bd5a04fc5 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..01f6abe6bc6c36 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..256633727fc86d 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 * @@ -321,6 +320,7 @@ RB_DEBUG_COUNTER(obj_imemo_parser_strterm) RB_DEBUG_COUNTER(obj_imemo_callinfo) RB_DEBUG_COUNTER(obj_imemo_callcache) RB_DEBUG_COUNTER(obj_imemo_constcache) +RB_DEBUG_COUNTER(obj_imemo_shape) /* ar_table */ RB_DEBUG_COUNTER(artable_hint_hit) 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/ext/objspace/objspace.c b/ext/objspace/objspace.c index 0b1b0943250387..364d36e6963493 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -644,6 +644,7 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self) INIT_IMEMO_TYPE_ID(imemo_callinfo); INIT_IMEMO_TYPE_ID(imemo_callcache); INIT_IMEMO_TYPE_ID(imemo_constcache); + INIT_IMEMO_TYPE_ID(imemo_shape); #undef INIT_IMEMO_TYPE_ID } diff --git a/gc.c b/gc.c index d026139d7bebff..03f936f0d81231 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; @@ -2972,6 +2971,7 @@ rb_imemo_name(enum imemo_type type) IMEMO_NAME(callinfo); IMEMO_NAME(callcache); IMEMO_NAME(constcache); + IMEMO_NAME(shape); #undef IMEMO_NAME } return "unknown"; @@ -3018,6 +3018,14 @@ imemo_memsize(VALUE obj) case imemo_iseq: size += rb_iseq_memsize((rb_iseq_t *)obj); break; + case imemo_shape: + { + struct rb_id_table* edges = ((rb_shape_t *) obj)->edges; + if (edges) { + size += rb_id_table_memsize(edges); + } + break; + } case imemo_env: size += RANY(obj)->as.imemo.env.env_size * sizeof(VALUE); break; @@ -3206,20 +3214,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 @@ -3387,6 +3381,22 @@ obj_free_object_id(rb_objspace_t *objspace, VALUE obj) } } +static enum rb_id_table_iterator_result +remove_child_shapes_parent(VALUE value, void *ref) +{ + rb_shape_t * shape = (rb_shape_t *) value; + GC_ASSERT(IMEMO_TYPE_P(shape, imemo_shape)); + + // If both objects live on the same page and we're currently + // sweeping that page, then we need to assert that neither are marked + if (GET_HEAP_PAGE(shape) == GET_HEAP_PAGE(shape->parent)) { + GC_ASSERT(!MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(shape), shape)); + } + + shape->parent = NULL; + return ID_TABLE_CONTINUE; +} + static int obj_free(rb_objspace_t *objspace, VALUE obj) { @@ -3435,6 +3445,19 @@ obj_free(rb_objspace_t *objspace, VALUE obj) RB_DEBUG_COUNTER_INC(obj_obj_transient); } else { + // A shape can be collected before an object is collected (if both + // happened to be garbage at the same time), so when we look up the shape, _do not_ + // assert that the shape is an IMEMO because it could be null + rb_shape_t *shape = rb_shape_get_shape_by_id_without_assertion(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 +3472,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)); @@ -3728,8 +3748,39 @@ obj_free(rb_objspace_t *objspace, VALUE obj) case imemo_constcache: RB_DEBUG_COUNTER_INC(obj_imemo_constcache); break; - } - return TRUE; + case imemo_shape: + { + rb_shape_t *shape = (rb_shape_t *)obj; + rb_shape_t *parent = shape->parent; + + if (parent) { + RUBY_ASSERT(IMEMO_TYPE_P(parent, imemo_shape)); + RUBY_ASSERT(parent->edges); + VALUE res; // Only used to temporarily store lookup value + if (rb_id_table_lookup(parent->edges, shape->edge_name, &res)) { + if ((rb_shape_t *)res == shape) { + rb_id_table_delete(parent->edges, shape->edge_name); + } + } + else { + rb_bug("Edge %s should exist", rb_id2name(shape->edge_name)); + } + } + if (shape->edges) { + rb_id_table_foreach_values(shape->edges, remove_child_shapes_parent, NULL); + rb_id_table_free(shape->edges); + shape->edges = NULL; + } + + shape->parent = NULL; + + rb_shape_set_shape_by_id(SHAPE_ID(shape), NULL); + + RB_DEBUG_COUNTER_INC(obj_imemo_shape); + break; + } + } + return TRUE; default: rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE, @@ -4873,10 +4924,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); } @@ -7154,6 +7201,21 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj) const struct rb_callcache *cc = (const struct rb_callcache *)obj; // should not mark klass here gc_mark(objspace, (VALUE)vm_cc_cme(cc)); + + // Check it's an attr_(reader|writer) + if (cc->cme_ && (cc->cme_->def->type == VM_METHOD_TYPE_ATTRSET || + cc->cme_->def->type == VM_METHOD_TYPE_IVAR)) { + shape_id_t source_shape_id = vm_cc_attr_index_source_shape_id(cc); + shape_id_t dest_shape_id = vm_cc_attr_index_dest_shape_id(cc); + if (source_shape_id != INVALID_SHAPE_ID) { + rb_shape_t *shape = rb_shape_get_shape_by_id(source_shape_id); + rb_gc_mark((VALUE)shape); + } + if (dest_shape_id != INVALID_SHAPE_ID) { + rb_shape_t *shape = rb_shape_get_shape_by_id(dest_shape_id); + rb_gc_mark((VALUE)shape); + } + } } return; case imemo_constcache: @@ -7162,6 +7224,14 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj) gc_mark(objspace, ice->value); } return; + case imemo_shape: + { + rb_shape_t *shape = (rb_shape_t *)obj; + if (shape->edges) { + mark_m_tbl(objspace, shape->edges); + } + } + return; #if VM_CHECK_MODE > 0 default: VM_UNREACHABLE(gc_mark_imemo); @@ -9765,6 +9835,10 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj) GC_ASSERT(!SPECIAL_CONST_P(obj)); switch (BUILTIN_TYPE(obj)) { + case T_IMEMO: + if (IMEMO_TYPE_P(obj, imemo_shape)) { + return FALSE; + } case T_NONE: case T_NIL: case T_MOVED: @@ -9778,7 +9852,6 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj) case T_STRING: case T_OBJECT: case T_FLOAT: - case T_IMEMO: case T_ARRAY: case T_BIGNUM: case T_ICLASS: @@ -10178,6 +10251,38 @@ gc_update_values(rb_objspace_t *objspace, long n, VALUE *values) } } +static enum rb_id_table_iterator_result +check_id_table_move(VALUE value, void *data) +{ + rb_objspace_t *objspace = (rb_objspace_t *)data; + + if (gc_object_moved_p(objspace, (VALUE)value)) { + return ID_TABLE_REPLACE; + } + + return ID_TABLE_CONTINUE; +} + +static enum rb_id_table_iterator_result +update_id_table(VALUE *value, void *data, int existing) +{ + rb_objspace_t *objspace = (rb_objspace_t *)data; + + if (gc_object_moved_p(objspace, (VALUE)*value)) { + *value = rb_gc_location((VALUE)*value); + } + + return ID_TABLE_CONTINUE; +} + +static void +update_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl) +{ + if (tbl) { + rb_id_table_foreach_values_with_replace(tbl, check_id_table_move, update_id_table, objspace); + } +} + static void gc_ref_update_imemo(rb_objspace_t *objspace, VALUE obj) { @@ -10250,24 +10355,23 @@ gc_ref_update_imemo(rb_objspace_t *objspace, VALUE obj) case imemo_tmpbuf: case imemo_callinfo: break; + case imemo_shape: + { + rb_shape_t * shape = (rb_shape_t *)obj; + if(shape->edges) { + update_m_tbl(objspace, shape->edges); + } + if (shape->parent) { + shape->parent = (rb_shape_t *)rb_gc_location((VALUE)shape->parent); + } + } + break; default: rb_bug("not reachable %d", imemo_type(obj)); break; } } -static enum rb_id_table_iterator_result -check_id_table_move(VALUE value, void *data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - if (gc_object_moved_p(objspace, (VALUE)value)) { - return ID_TABLE_REPLACE; - } - - return ID_TABLE_CONTINUE; -} - /* Returns the new location of an object, if it moved. Otherwise returns * the existing location. */ VALUE @@ -10300,26 +10404,6 @@ rb_gc_location(VALUE value) return destination; } -static enum rb_id_table_iterator_result -update_id_table(VALUE *value, void *data, int existing) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - if (gc_object_moved_p(objspace, (VALUE)*value)) { - *value = rb_gc_location((VALUE)*value); - } - - return ID_TABLE_CONTINUE; -} - -static void -update_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl) -{ - if (tbl) { - rb_id_table_foreach_values_with_replace(tbl, check_id_table_move, update_id_table, objspace); - } -} - static enum rb_id_table_iterator_result update_cc_tbl_i(VALUE ccs_ptr, void *data) { @@ -10407,15 +10491,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) { @@ -10423,11 +10498,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 @@ -10669,6 +10739,8 @@ gc_update_references(rb_objspace_t *objspace) struct heap_page *page = NULL; + rb_vm_update_references(vm); + for (int i = 0; i < SIZE_POOL_COUNT; i++) { bool should_set_mark_bits = TRUE; rb_size_pool_t *size_pool = &size_pools[i]; @@ -10687,7 +10759,6 @@ gc_update_references(rb_objspace_t *objspace) } } } - rb_vm_update_references(vm); rb_transient_heap_update_references(); rb_gc_update_global_tbl(); global_symbols.ids = rb_gc_location(global_symbols.ids); 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..36635d6eaa819a 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 @@ -26,9 +27,9 @@ struct rb_subclass_entry { }; struct rb_iv_index_tbl_entry { - uint32_t index; - rb_serial_t class_serial; - VALUE class_value; + uint32_t idx; + 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,8 @@ struct rb_classext_struct { const VALUE refined_class; rb_alloc_func_t allocator; const VALUE includer; + uint32_t max_iv_count; + uint16_t shape_id; }; struct RClass { @@ -102,7 +104,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/imemo.h b/internal/imemo.h index 91b524e0a6ae77..20bfff8d7c5bb5 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -45,6 +45,7 @@ enum imemo_type { imemo_callinfo = 11, imemo_callcache = 12, imemo_constcache = 13, + imemo_shape = 14, }; /* CREF (Class REFerence) is defined in method.h */ 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 a4792d81fdb5b6..7014162a1951d7 100644 --- a/iseq.c +++ b/iseq.c @@ -233,13 +233,15 @@ rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data) // 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; - } + shape_id_t source_shape_id = vm_ic_attr_index_source_shape_id(ivc); + shape_id_t dest_shape_id = vm_ic_attr_index_dest_shape_id(ivc); + if (source_shape_id != INVALID_SHAPE_ID) { + rb_shape_t *shape = rb_shape_get_shape_by_id(source_shape_id); + func(data, (VALUE)shape); + } + if (dest_shape_id != INVALID_SHAPE_ID) { + rb_shape_t *shape = rb_shape_get_shape_by_id(dest_shape_id); + func(data, (VALUE)shape); } } 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..da97e28b698729 100755 --- a/misc/lldb_cruby.py +++ b/misc/lldb_cruby.py @@ -418,9 +418,13 @@ 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) + if imemo_type == imemo_shape: + append_command_output(debugger, "p *(rb_shape_t *) %0#x" % val.GetValueAsUnsigned(), result) + else: + append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result) elif flType == RUBY_T_STRUCT: tRTypedData = target.FindFirstType("struct RStruct").GetPointerType() val = val.Cast(tRTypedData) diff --git a/mjit_c.rb b/mjit_c.rb index d8e5628bda57d4..fd8991af3f03e7 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_)")], @@ -502,9 +523,9 @@ def C.rb_iseq_t 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)")], + idx: [CType::Immediate.parse("uint32_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), idx)")], + 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 2328b20757aae2..213f9760beebf4 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..fa914663401d3e --- /dev/null +++ b/shape.c @@ -0,0 +1,571 @@ +#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; +} + +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]; + RUBY_ASSERT(IMEMO_TYPE_P(shape, imemo_shape)); + 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; +} + +static inline shape_id_t +shape_set_shape_id(rb_shape_t *shape, shape_id_t id) { + VALUE flags = shape->flags & ~((uint64_t)SHAPE_MASK << 16); + return (shape_id_t)(shape->flags = (flags | ((VALUE)id << SHAPE_FLAG_SHIFT))); +} + +#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 SHAPE_ID(rb_shape_get_frozen_root_shape()); + } + +#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 shape_id_t +get_next_shape_id(void) +{ + rb_vm_t *vm = GET_VM(); + vm->max_shape_count++; + return vm->max_shape_count; +} + +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) || rb_objspace_garbage_object_p((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; + } + + shape_id_t next_shape_id = get_next_shape_id(); + + if (next_shape_id == MAX_SHAPE_ID) { + // TODO: Make an OutOfShapesError ?? + rb_bug("Out of shapes\n"); + } + else { + RUBY_ASSERT(next_shape_id < MAX_SHAPE_ID); + rb_shape_t * new_shape = rb_shape_alloc(next_shape_id, + id, + shape); + + new_shape->type = (uint8_t)shape_type; + + switch(shape_type) { + case SHAPE_FROZEN: + RB_OBJ_FREEZE_RAW((VALUE)new_shape); + break; + 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: + 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); + RB_OBJ_WRITTEN((VALUE)shape, Qundef, (VALUE)new_shape); + rb_shape_set_shape_by_id(next_shape_id, 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; + } + + RUBY_ASSERT(!rb_objspace_garbage_object_p((VALUE)next_shape)); + 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; + } + + RUBY_ASSERT(!rb_objspace_garbage_object_p((VALUE)next_shape)); + 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_shape_t *shape = (rb_shape_t *)rb_imemo_new(imemo_shape, 0, 0, 0, 0); + FL_SET_RAW((VALUE)shape, RUBY_FL_SHAREABLE); + FL_SET_RAW((VALUE)shape, RUBY_FL_PROMOTED1); + return shape; +} + +rb_shape_t * +rb_shape_alloc(shape_id_t shape_id, ID edge_name, rb_shape_t * parent) +{ + rb_shape_t * shape = shape_alloc(); + shape_set_shape_id(shape, shape_id); + + shape->edge_name = edge_name; + shape->iv_count = 0; + + RB_OBJ_WRITE(shape, &shape->parent, parent); + + RUBY_ASSERT(!parent || IMEMO_TYPE_P(parent, imemo_shape)); + + return shape; +} + +MJIT_FUNC_EXPORTED void +rb_shape_set_shape(VALUE obj, rb_shape_t* shape) +{ + RUBY_ASSERT(IMEMO_TYPE_P(shape, imemo_shape)); + RUBY_ASSERT(SHAPE_FROZEN == shape->type ? RB_OBJ_FROZEN(obj) : 1); + + if(rb_shape_set_shape_id(obj, SHAPE_ID(shape))) { + if (shape != rb_shape_get_frozen_root_shape()) { + RB_OBJ_WRITTEN(obj, Qundef, (VALUE)shape); + } + } +} + +void +rb_shape_set_shape_by_id(shape_id_t shape_id, rb_shape_t *shape) +{ + rb_vm_t *vm = GET_VM(); + + RUBY_ASSERT(shape == NULL || IMEMO_TYPE_P(shape, imemo_shape)); + vm->shape_list[shape_id] = shape; +} + +VALUE rb_cShape; + +static void +shape_mark(void *ptr) +{ + rb_gc_mark((VALUE)ptr); +} + +/* + * Exposing Shape to Ruby via RubyVM.debug_shape + */ +static const rb_data_type_t shape_data_type = { + "Shape", + {shape_mark, NULL, NULL,}, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED +}; + +static VALUE +rb_shape_id(VALUE self) { + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + return INT2NUM(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(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); + RB_OBJ_WRITTEN(res, Qundef, shape); + + 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(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(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->max_shape_count; 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()->max_shape_count); +} + +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_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..b381b9e6ba3555 --- /dev/null +++ b/shape.h @@ -0,0 +1,153 @@ +#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 + +#define SHAPE_ID(shape) ((((rb_shape_t *)shape)->flags >> SHAPE_FLAG_SHIFT) & SHAPE_MASK) + +struct rb_shape { + VALUE flags; // Shape ID and frozen status encoded within flags + 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); +MJIT_SYMBOL_EXPORT_END + +rb_shape_t * rb_shape_alloc(shape_id_t shape_id, ID edge_name, rb_shape_t * parent); + +bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id); +void rb_shape_set_shape_by_id(shape_id_t, rb_shape_t *); + +VALUE rb_obj_debug_shape(VALUE self, VALUE obj); +VALUE rb_shape_flags_mask(void); + +#endif diff --git a/spec/ruby/library/objectspace/reachable_objects_from_spec.rb b/spec/ruby/library/objectspace/reachable_objects_from_spec.rb index 7e70bc85691989..61ebc9f93d86b8 100644 --- a/spec/ruby/library/objectspace/reachable_objects_from_spec.rb +++ b/spec/ruby/library/objectspace/reachable_objects_from_spec.rb @@ -17,7 +17,7 @@ it "enumerates objects directly reachable from a given object" do ObjectSpace.reachable_objects_from(['a', 'b', 'c']).should include(Array, 'a', 'b', 'c') - ObjectSpace.reachable_objects_from(Object.new).should == [Object] + ObjectSpace.reachable_objects_from(Object.new).should include(Object) end it "finds an object stored in an Array" do diff --git a/spec/ruby/optional/capi/shared/rbasic.rb b/spec/ruby/optional/capi/shared/rbasic.rb index 99c2044bd7f375..105408ec3d822d 100644 --- a/spec/ruby/optional/capi/shared/rbasic.rb +++ b/spec/ruby/optional/capi/shared/rbasic.rb @@ -8,16 +8,9 @@ it "reports the appropriate FREEZE flag for the object when reading" do obj, _ = @data.call - initial = @specs.get_flags(obj) + (@specs.get_flags(obj) & @freeze).should == 0 obj.freeze - @specs.get_flags(obj).should == @freeze | initial - end - - it "supports setting the FREEZE flag" do - obj, _ = @data.call - initial = @specs.get_flags(obj) - @specs.set_flags(obj, @freeze | initial).should == @freeze | initial - obj.should.frozen? + (@specs.get_flags(obj) & @freeze).should == @freeze end it "supports retrieving the (meta)class" do 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/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 3b9031985886c2..94d68dc0932d9b 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -116,12 +116,16 @@ def test_reachable_objects_from opts = %w[--disable-gem --disable=frozen-string-literal -robjspace] assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" begin; - assert_equal(nil, ObjectSpace.reachable_objects_from(nil)) - assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c'])) + def assert_reachable_object_as_expected(expectation, reachable_objects_from_array) + reachable_objects = ObjectSpace.reachable_objects_from(reachable_objects_from_array) + assert_equal(expectation, reachable_objects) + end - assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a'])) - assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v])) - assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v])) + assert_equal(nil, ObjectSpace.reachable_objects_from(nil)) + assert_reachable_object_as_expected([Array, 'a', 'b', 'c'], ['a', 'b', 'c']) + assert_reachable_object_as_expected([Array, 'a', 'a', 'a'], ['a', 'a', 'a']) + assert_reachable_object_as_expected([Array, 'a', 'a'], ['a', v = 'a', v]) + assert_reachable_object_as_expected([Array, 'a'], [v = 'a', v, v]) long_ary = Array.new(1_000){''} max = 0 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_shapes.rb b/test/ruby/test_shapes.rb new file mode 100644 index 00000000000000..cdca08dbb43c8d --- /dev/null +++ b/test/ruby/test_shapes.rb @@ -0,0 +1,171 @@ +# 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?) + refute_equal(RubyVM.debug_shape(obj).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..a6f6d5ec1b22fc 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 = 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,8 +1043,12 @@ rb_mark_generic_ivar(VALUE obj) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) { - gen_ivtbl_mark(ivtbl); + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { + +#if !SHAPE_IN_BASIC_FLAGS + rb_gc_mark((VALUE)rb_shape_get_shape_by_id(ivtbl->shape_id)); +#endif + gen_ivtbl_mark(ivtbl); } } @@ -1182,11 +1076,35 @@ rb_generic_ivar_memsize(VALUE obj) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) - return gen_ivtbl_bytes(ivtbl->numiv); + 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 +1172,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 +1198,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 +1249,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 +1265,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 +1301,34 @@ 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)); + } + + if (!st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update, + (st_data_t)&ivup)) { + RB_OBJ_WRITTEN(obj, Qundef, shape); + } } 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 +1395,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 +1419,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 +1460,114 @@ 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; + } + case T_IMEMO: + if (imemo_type(obj) == imemo_shape) { + RBASIC(obj)->flags &= 0xffffffff0000ffff; + RBASIC(obj)->flags |= ((uint32_t)(shape_id) << 16); + } + 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 +1577,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 +1614,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 +1701,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,17 +1779,17 @@ rb_ivar_count(VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - if (ROBJECT_IV_INDEX_TBL(obj) != 0) { - st_index_t i, count, num = ROBJECT_NUMIV(obj); - const VALUE *const ivptr = ROBJECT_IVPTR(obj); - for (i = count = 0; i < num; ++i) { - if (ivptr[i] != Qundef) { - count++; - } - } - return count; - } - break; + 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) { + if (ivptr[i] != Qundef) { + count++; + } + } + return count; + } + break; case T_CLASS: case T_MODULE: if ((tbl = RCLASS_IV_TBL(obj)) != 0) { @@ -1867,11 +1800,11 @@ rb_ivar_count(VALUE obj) if (FL_TEST(obj, FL_EXIVAR)) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) { - return gen_ivtbl_count(ivtbl); - } - } - break; + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { + return gen_ivtbl_count(ivtbl); + } + } + break; } return 0; } @@ -1965,40 +1898,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..77393458c2a6c5 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" @@ -2720,6 +2721,12 @@ rb_vm_update_references(void *ptr) vm->top_self = rb_gc_location(vm->top_self); vm->orig_progname = rb_gc_location(vm->orig_progname); + for (shape_id_t i = 0; i <= vm->max_shape_count; i++) { + if (vm->shape_list[i]) { + vm->shape_list[i] = (rb_shape_t *)rb_gc_location((VALUE)vm->shape_list[i]); + } + } + rb_gc_update_tbl_refs(vm->overloaded_cme_table); if (vm->coverages) { @@ -2801,6 +2808,8 @@ rb_vm_mark(void *ptr) obj_ary++; } + rb_gc_mark((VALUE)vm->root_shape); + rb_gc_mark((VALUE)vm->frozen_root_shape); rb_gc_mark_movable(vm->load_path); rb_gc_mark_movable(vm->load_path_snapshot); RUBY_MARK_MOVABLE_UNLESS_NULL(vm->load_path_check_cache); @@ -4021,6 +4030,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 +4046,37 @@ 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(ROOT_SHAPE_ID, + 0, + 0); + rb_shape_set_shape_by_id(ROOT_SHAPE_ID, vm->root_shape); + RB_OBJ_WRITTEN(vm->root_shape, Qundef, (VALUE)vm); + + // Frozen root shape + vm->frozen_root_shape = rb_shape_alloc(FROZEN_ROOT_SHAPE_ID, + rb_make_internal_id(), + vm->root_shape); + vm->frozen_root_shape->type = (uint8_t)SHAPE_FROZEN; + RB_OBJ_FREEZE_RAW((VALUE)vm->frozen_root_shape); + rb_shape_set_shape_by_id(FROZEN_ROOT_SHAPE_ID, vm->frozen_root_shape); + RB_OBJ_WRITTEN(vm->frozen_root_shape, Qundef, (VALUE)vm); + vm->max_shape_count = 1; } /* 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..b4768be13629b5 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 max_shape_count; + /* load */ VALUE top_self; VALUE load_path; diff --git a/vm_eval.c b/vm_eval.c index c0558fce2bcb0a..bfa812f3d34341 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -47,7 +47,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, @@ -89,7 +89,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 a662de468dda9a..1cd66cf1eb387e 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; } @@ -1085,35 +1090,19 @@ 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); + RB_OBJ_WRITTEN(cc, Qundef, rb_shape_get_shape_by_id(shape_id)); + } } else { - vm_cc_attr_index_set(cc, ent->index); + vm_ic_attr_index_set(iseq, ic, index, shape_id, shape_id); + RB_OBJ_WRITTEN(iseq, Qundef, rb_shape_get_shape_by_id(shape_id)); } } @@ -1123,68 +1112,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 (LIKELY(BUILTIN_TYPE(obj) == T_OBJECT) && - LIKELY(index < ROBJECT_NUMIV(obj))) { - val = ROBJECT_IVPTR(obj)[index]; +#if SHAPE_IN_BASIC_FLAGS + shape_id = RBASIC_SHAPE_ID(obj); +#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); - } - else if (FL_TEST_RAW(obj, FL_EXIVAR)) { - val = rb_ivar_generic_lookup_with_index(obj, id, index); - } - goto ret; +#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; + } } - 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); + shape_id_t cached_id; - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &ent)) { - fill_ivar_cache(iseq, ic, cc, is_attr, ent); + if (is_attr) { + cached_id = vm_cc_attr_shape_id(cc); + } + else { + cached_id = vm_ic_attr_shape_id(ic); + } - // get value - if (ent->index < ROBJECT_NUMIV(obj)) { - val = ROBJECT_IVPTR(obj)[ent->index]; + attr_index_t index; - VM_ASSERT(rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); - } - } + 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 { + 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(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); @@ -1196,6 +1237,24 @@ 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, rb_shape_t *shape, rb_shape_t *next_shape, 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(shape), SHAPE_ID(next_shape)); + RB_OBJ_WRITTEN(cc, Qundef, (VALUE)shape); + RB_OBJ_WRITTEN(cc, Qundef, (VALUE)next_shape); + } + } + else { + vm_ic_attr_index_set(iseq, ic, index, SHAPE_ID(shape), SHAPE_ID(next_shape)); + RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)shape); + RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)next_shape); + } +} + 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)); @@ -1203,35 +1262,66 @@ 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); + + attr_index_t index; + + uint32_t num_iv = ROBJECT_NUMIV(obj); + rb_shape_t* shape = rb_shape_get_shape(obj); + rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); + if (shape != next_shape) { + rb_shape_set_shape(obj, next_shape); + } - 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)); - } + 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"); + } - uint32_t index = ent->index; + populate_cache(index, shape, next_shape, 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); + } - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - 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: + { + rb_shape_t * shape = rb_shape_get_shape(obj); + rb_ivar_set(obj, id, val); + rb_shape_t * next_shape = rb_shape_get_shape(obj); + 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, next_shape, 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); @@ -1250,39 +1340,99 @@ 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 + RB_OBJ_WRITTEN(obj, Qundef, rb_shape_get_shape_by_id(dest_shape_id)); + } + else { + // Just get the IV table + RUBY_ASSERT(GET_VM()->shape_list[dest_shape_id]); + 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); + } + else { + RUBY_ASSERT(GET_VM()->shape_list[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 @@ -1377,7 +1527,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 @@ -1386,28 +1551,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) { @@ -3100,17 +3243,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 @@ -3219,7 +3390,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); @@ -3389,7 +3560,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); } @@ -3415,7 +3586,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); } @@ -3522,7 +3693,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)) { @@ -3702,18 +3873,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 c3d4a39a2ba656..4b50d888de1f50 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 c246c7b48febd8..4018a314fc5754 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1938,14 +1938,12 @@ 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, - Opnd::Imm(ivar_index.into()), + Opnd::UImm(ivar_name.into()), val_opnd, ], ); @@ -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 25149ab7304d45..65f398f075e778 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 f58bf1ca05879b..84a778cbc530d4 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -269,6 +269,30 @@ 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 flags: VALUE, + 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; @@ -513,6 +537,7 @@ pub const imemo_parser_strterm: imemo_type = 10; pub const imemo_callinfo: imemo_type = 11; pub const imemo_callcache: imemo_type = 12; pub const imemo_constcache: imemo_type = 13; +pub const imemo_shape: imemo_type = 14; pub type imemo_type = u32; pub const METHOD_VISI_UNDEF: rb_method_visibility_t = 0; pub const METHOD_VISI_PUBLIC: rb_method_visibility_t = 1; @@ -572,6 +597,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; } @@ -600,9 +630,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)] @@ -698,12 +729,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 830b5b5c351c5c6efa5ad461ae4ec5085e5f0275 Mon Sep 17 00:00:00 2001 From: git Date: Tue, 27 Sep 2022 01:21:58 +0900 Subject: [PATCH 249/269] * expand tabs. [ci skip] Tabs were expanded because the file did not have any tab indentation in unedited lines. Please update your editor config, and use misc/expand_tabs.rb in the pre-commit hook. --- compile.c | 2 +- gc.c | 4 ++-- variable.c | 36 ++++++++++++++++++------------------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/compile.c b/compile.c index 01f6abe6bc6c36..9051ecfcd69ea1 100644 --- a/compile.c +++ b/compile.c @@ -2459,7 +2459,7 @@ 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 */ + 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); diff --git a/gc.c b/gc.c index 03f936f0d81231..77e2d1aeb8a06d 100644 --- a/gc.c +++ b/gc.c @@ -3779,8 +3779,8 @@ obj_free(rb_objspace_t *objspace, VALUE obj) RB_DEBUG_COUNTER_INC(obj_imemo_shape); break; } - } - return TRUE; + } + return TRUE; default: rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE, diff --git a/variable.c b/variable.c index a6f6d5ec1b22fc..abbe76eba7e356 100644 --- a/variable.c +++ b/variable.c @@ -1048,7 +1048,7 @@ rb_mark_generic_ivar(VALUE obj) #if !SHAPE_IN_BASIC_FLAGS rb_gc_mark((VALUE)rb_shape_get_shape_by_id(ivtbl->shape_id)); #endif - gen_ivtbl_mark(ivtbl); + gen_ivtbl_mark(ivtbl); } } @@ -1077,7 +1077,7 @@ rb_generic_ivar_memsize(VALUE obj) struct gen_ivtbl *ivtbl; if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) - return gen_ivtbl_bytes(ivtbl->numiv); + return gen_ivtbl_bytes(ivtbl->numiv); return 0; } @@ -1779,17 +1779,17 @@ 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); - const VALUE *const ivptr = ROBJECT_IVPTR(obj); - for (i = count = 0; i < num; ++i) { - if (ivptr[i] != Qundef) { - count++; - } - } - return count; - } - break; + 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) { + if (ivptr[i] != Qundef) { + count++; + } + } + return count; + } + break; case T_CLASS: case T_MODULE: if ((tbl = RCLASS_IV_TBL(obj)) != 0) { @@ -1800,11 +1800,11 @@ rb_ivar_count(VALUE obj) if (FL_TEST(obj, FL_EXIVAR)) { struct gen_ivtbl *ivtbl; - if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { - return gen_ivtbl_count(ivtbl); - } - } - break; + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { + return gen_ivtbl_count(ivtbl); + } + } + break; } return 0; } From aa2a428cfb3f4273f51358eb4e7fab235c7d36c2 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 26 Sep 2022 11:39:36 -0400 Subject: [PATCH 250/269] Refactor str_substr and str_subseq This commit extracts common code between str_substr and rb_str_subseq into a function called str_subseq. This commit also applies optimizations in commit 2e88bca to rb_str_subseq. --- string.c | 58 ++++++++++++++++++++------------------------------------ 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/string.c b/string.c index 1e7b16fbba877a..b9c567cbe250dd 100644 --- a/string.c +++ b/string.c @@ -2797,26 +2797,32 @@ rb_str_sublen(VALUE str, long pos) } } -VALUE -rb_str_subseq(VALUE str, long beg, long len) +static VALUE +str_subseq(VALUE str, long beg, long len) { VALUE str2; if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && - SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str))) { - long olen; - str2 = rb_str_new_shared(rb_str_new_frozen_String(str)); + SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str))) { + str2 = rb_str_new_shared(str); RSTRING(str2)->as.heap.ptr += beg; - olen = RSTRING(str2)->as.heap.len; - if (olen > len) RSTRING(str2)->as.heap.len = len; + if (RSTRING(str2)->as.heap.len > len) { + RSTRING(str2)->as.heap.len = len; + } } else { - str2 = rb_str_new(RSTRING_PTR(str)+beg, len); + str2 = rb_str_new(RSTRING_PTR(str) + beg, len); RB_GC_GUARD(str); } - rb_enc_cr_str_copy_for_substr(str2, str); + return str2; +} +VALUE +rb_str_subseq(VALUE str, long beg, long len) +{ + VALUE str2 = str_subseq(str, beg, len); + rb_enc_cr_str_copy_for_substr(str2, str); return str2; } @@ -2916,25 +2922,15 @@ rb_str_substr(VALUE str, long beg, long len) static VALUE str_substr(VALUE str, long beg, long len, int empty) { - VALUE str2; char *p = rb_str_subpos(str, beg, &len); if (!p) return Qnil; - if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && - SHARABLE_SUBSTRING_P(p, len, RSTRING_END(str))) { - long ofs = p - RSTRING_PTR(str); - str2 = str_new_shared(rb_cString, str); - RSTRING(str2)->as.heap.ptr += ofs; - RSTRING(str2)->as.heap.len = len; - ENC_CODERANGE_CLEAR(str2); - } - else { - if (!len && !empty) return Qnil; - str2 = rb_str_new(p, len); - RB_GC_GUARD(str); - } - rb_enc_cr_str_copy_for_substr(str2, str); + if (!len && !empty) return Qnil; + beg = p - RSTRING_PTR(str); + + VALUE str2 = str_subseq(str, beg, len); + rb_enc_cr_str_copy_for_substr(str2, str); return str2; } @@ -6141,9 +6137,7 @@ rb_str_setbyte(VALUE str, VALUE index, VALUE value) static VALUE str_byte_substr(VALUE str, long beg, long len, int empty) { - char *p, *s = RSTRING_PTR(str); long n = RSTRING_LEN(str); - VALUE str2; if (beg > n || len < 0) return Qnil; if (beg < 0) { @@ -6155,19 +6149,9 @@ str_byte_substr(VALUE str, long beg, long len, int empty) if (len <= 0) { if (!empty) return Qnil; len = 0; - p = 0; } - else - p = s + beg; - if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && SHARABLE_SUBSTRING_P(beg, len, n)) { - str2 = str_new_shared(rb_cString, str); - RSTRING(str2)->as.heap.ptr += beg; - RSTRING(str2)->as.heap.len = len; - } - else { - str2 = rb_str_new(p, len); - } + VALUE str2 = str_subseq(str, beg, len); str_enc_copy(str2, str); From b39690df3a90a838cdb2de71e70a20651ebafaf4 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Mon, 26 Sep 2022 10:53:45 -0700 Subject: [PATCH 251/269] Rename method name in keyword test from y to yo Kernel#y is defined by psych/yaml, which causes occasional nondeterministic problems with this test when doing parallel testing. --- test/ruby/test_keyword.rb | 214 +++++++++++++++++++------------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index 9978072744299c..94b5323ab07b21 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -247,17 +247,17 @@ def self.assert_equal_not_same(kw, res) assert_not_same(kw, res) end - def self.y(**kw) kw end - m = method(:y) - assert_equal(false, y(**{}).frozen?) - assert_equal_not_same(kw, y(**kw)) - assert_equal_not_same(h, y(**h)) - assert_equal(false, send(:y, **{}).frozen?) - assert_equal_not_same(kw, send(:y, **kw)) - assert_equal_not_same(h, send(:y, **h)) - assert_equal(false, public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, public_send(:y, **kw)) - assert_equal_not_same(h, public_send(:y, **h)) + def self.yo(**kw) kw end + m = method(:yo) + assert_equal(false, yo(**{}).frozen?) + assert_equal_not_same(kw, yo(**kw)) + assert_equal_not_same(h, yo(**h)) + assert_equal(false, send(:yo, **{}).frozen?) + assert_equal_not_same(kw, send(:yo, **kw)) + assert_equal_not_same(h, send(:yo, **h)) + assert_equal(false, public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, public_send(:yo, **kw)) + assert_equal_not_same(h, public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -266,25 +266,25 @@ def self.y(**kw) kw end assert_equal_not_same(h, m.send(:call, **h)) m = method(:send) - assert_equal(false, m.(:y, **{}).frozen?) - assert_equal_not_same(kw, m.(:y, **kw)) - assert_equal_not_same(h, m.(:y, **h)) - assert_equal(false, m.send(:call, :y, **{}).frozen?) - assert_equal_not_same(kw, m.send(:call, :y, **kw)) - assert_equal_not_same(h, m.send(:call, :y, **h)) - - singleton_class.send(:remove_method, :y) - define_singleton_method(:y) { |**kw| kw } - m = method(:y) - assert_equal(false, y(**{}).frozen?) - assert_equal_not_same(kw, y(**kw)) - assert_equal_not_same(h, y(**h)) - assert_equal(false, send(:y, **{}).frozen?) - assert_equal_not_same(kw, send(:y, **kw)) - assert_equal_not_same(h, send(:y, **h)) - assert_equal(false, public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, public_send(:y, **kw)) - assert_equal_not_same(h, public_send(:y, **h)) + assert_equal(false, m.(:yo, **{}).frozen?) + assert_equal_not_same(kw, m.(:yo, **kw)) + assert_equal_not_same(h, m.(:yo, **h)) + assert_equal(false, m.send(:call, :yo, **{}).frozen?) + assert_equal_not_same(kw, m.send(:call, :yo, **kw)) + assert_equal_not_same(h, m.send(:call, :yo, **h)) + + singleton_class.send(:remove_method, :yo) + define_singleton_method(:yo) { |**kw| kw } + m = method(:yo) + assert_equal(false, yo(**{}).frozen?) + assert_equal_not_same(kw, yo(**kw)) + assert_equal_not_same(h, yo(**h)) + assert_equal(false, send(:yo, **{}).frozen?) + assert_equal_not_same(kw, send(:yo, **kw)) + assert_equal_not_same(h, send(:yo, **h)) + assert_equal(false, public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, public_send(:yo, **kw)) + assert_equal_not_same(h, public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -292,17 +292,17 @@ def self.y(**kw) kw end assert_equal_not_same(kw, m.send(:call, **kw)) assert_equal_not_same(h, m.send(:call, **h)) - y = lambda { |**kw| kw } - m = y.method(:call) - assert_equal(false, y.(**{}).frozen?) - assert_equal_not_same(kw, y.(**kw)) - assert_equal_not_same(h, y.(**h)) - assert_equal(false, y.send(:call, **{}).frozen?) - assert_equal_not_same(kw, y.send(:call, **kw)) - assert_equal_not_same(h, y.send(:call, **h)) - assert_equal(false, y.public_send(:call, **{}).frozen?) - assert_equal_not_same(kw, y.public_send(:call, **kw)) - assert_equal_not_same(h, y.public_send(:call, **h)) + yo = lambda { |**kw| kw } + m = yo.method(:call) + assert_equal(false, yo.(**{}).frozen?) + assert_equal_not_same(kw, yo.(**kw)) + assert_equal_not_same(h, yo.(**h)) + assert_equal(false, yo.send(:call, **{}).frozen?) + assert_equal_not_same(kw, yo.send(:call, **kw)) + assert_equal_not_same(h, yo.send(:call, **h)) + assert_equal(false, yo.public_send(:call, **{}).frozen?) + assert_equal_not_same(kw, yo.public_send(:call, **kw)) + assert_equal_not_same(h, yo.public_send(:call, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -310,17 +310,17 @@ def self.y(**kw) kw end assert_equal_not_same(kw, m.send(:call, **kw)) assert_equal_not_same(h, m.send(:call, **h)) - y = :y.to_proc - m = y.method(:call) - assert_equal(false, y.(self, **{}).frozen?) - assert_equal_not_same(kw, y.(self, **kw)) - assert_equal_not_same(h, y.(self, **h)) - assert_equal(false, y.send(:call, self, **{}).frozen?) - assert_equal_not_same(kw, y.send(:call, self, **kw)) - assert_equal_not_same(h, y.send(:call, self, **h)) - assert_equal(false, y.public_send(:call, self, **{}).frozen?) - assert_equal_not_same(kw, y.public_send(:call, self, **kw)) - assert_equal_not_same(h, y.public_send(:call, self, **h)) + yo = :yo.to_proc + m = yo.method(:call) + assert_equal(false, yo.(self, **{}).frozen?) + assert_equal_not_same(kw, yo.(self, **kw)) + assert_equal_not_same(h, yo.(self, **h)) + assert_equal(false, yo.send(:call, self, **{}).frozen?) + assert_equal_not_same(kw, yo.send(:call, self, **kw)) + assert_equal_not_same(h, yo.send(:call, self, **h)) + assert_equal(false, yo.public_send(:call, self, **{}).frozen?) + assert_equal_not_same(kw, yo.public_send(:call, self, **kw)) + assert_equal_not_same(h, yo.public_send(:call, self, **h)) assert_equal(false, m.(self, **{}).frozen?) assert_equal_not_same(kw, m.(self, **kw)) assert_equal_not_same(h, m.(self, **h)) @@ -329,20 +329,20 @@ def self.y(**kw) kw end assert_equal_not_same(h, m.send(:call, self, **h)) c = Class.new do - def y(**kw) kw end + def yo(**kw) kw end end o = c.new - def o.y(**kw) super end - m = o.method(:y) - assert_equal(false, o.y(**{}).frozen?) - assert_equal_not_same(kw, o.y(**kw)) - assert_equal_not_same(h, o.y(**h)) - assert_equal(false, o.send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.send(:y, **kw)) - assert_equal_not_same(h, o.send(:y, **h)) - assert_equal(false, o.public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.public_send(:y, **kw)) - assert_equal_not_same(h, o.public_send(:y, **h)) + def o.yo(**kw) super end + m = o.method(:yo) + assert_equal(false, o.yo(**{}).frozen?) + assert_equal_not_same(kw, o.yo(**kw)) + assert_equal_not_same(h, o.yo(**h)) + assert_equal(false, o.send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.send(:yo, **kw)) + assert_equal_not_same(h, o.send(:yo, **h)) + assert_equal(false, o.public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.public_send(:yo, **kw)) + assert_equal_not_same(h, o.public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -350,17 +350,17 @@ def o.y(**kw) super end assert_equal_not_same(kw, m.send(:call, **kw)) assert_equal_not_same(h, m.send(:call, **h)) - o.singleton_class.send(:remove_method, :y) - def o.y(**kw) super(**kw) end - assert_equal(false, o.y(**{}).frozen?) - assert_equal_not_same(kw, o.y(**kw)) - assert_equal_not_same(h, o.y(**h)) - assert_equal(false, o.send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.send(:y, **kw)) - assert_equal_not_same(h, o.send(:y, **h)) - assert_equal(false, o.public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.public_send(:y, **kw)) - assert_equal_not_same(h, o.public_send(:y, **h)) + o.singleton_class.send(:remove_method, :yo) + def o.yo(**kw) super(**kw) end + assert_equal(false, o.yo(**{}).frozen?) + assert_equal_not_same(kw, o.yo(**kw)) + assert_equal_not_same(h, o.yo(**h)) + assert_equal(false, o.send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.send(:yo, **kw)) + assert_equal_not_same(h, o.send(:yo, **h)) + assert_equal(false, o.public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.public_send(:yo, **kw)) + assert_equal_not_same(h, o.public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -372,17 +372,17 @@ def o.y(**kw) super(**kw) end def method_missing(_, **kw) kw end end o = c.new - def o.y(**kw) super end - m = o.method(:y) - assert_equal(false, o.y(**{}).frozen?) - assert_equal_not_same(kw, o.y(**kw)) - assert_equal_not_same(h, o.y(**h)) - assert_equal(false, o.send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.send(:y, **kw)) - assert_equal_not_same(h, o.send(:y, **h)) - assert_equal(false, o.public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.public_send(:y, **kw)) - assert_equal_not_same(h, o.public_send(:y, **h)) + def o.yo(**kw) super end + m = o.method(:yo) + assert_equal(false, o.yo(**{}).frozen?) + assert_equal_not_same(kw, o.yo(**kw)) + assert_equal_not_same(h, o.yo(**h)) + assert_equal(false, o.send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.send(:yo, **kw)) + assert_equal_not_same(h, o.send(:yo, **h)) + assert_equal(false, o.public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.public_send(:yo, **kw)) + assert_equal_not_same(h, o.public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -390,17 +390,17 @@ def o.y(**kw) super end assert_equal_not_same(kw, m.send(:call, **kw)) assert_equal_not_same(h, m.send(:call, **h)) - o.singleton_class.send(:remove_method, :y) - def o.y(**kw) super(**kw) end - assert_equal(false, o.y(**{}).frozen?) - assert_equal_not_same(kw, o.y(**kw)) - assert_equal_not_same(h, o.y(**h)) - assert_equal(false, o.send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.send(:y, **kw)) - assert_equal_not_same(h, o.send(:y, **h)) - assert_equal(false, o.public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.public_send(:y, **kw)) - assert_equal_not_same(h, o.public_send(:y, **h)) + o.singleton_class.send(:remove_method, :yo) + def o.yo(**kw) super(**kw) end + assert_equal(false, o.yo(**{}).frozen?) + assert_equal_not_same(kw, o.yo(**kw)) + assert_equal_not_same(h, o.yo(**h)) + assert_equal(false, o.send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.send(:yo, **kw)) + assert_equal_not_same(h, o.send(:yo, **h)) + assert_equal(false, o.public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.public_send(:yo, **kw)) + assert_equal_not_same(h, o.public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -436,17 +436,17 @@ def initialize(**kw) @kw = kw end assert_equal_not_same(h, m.(**h)) assert_equal_not_same(h, m.send(:call, **h)) - singleton_class.send(:remove_method, :y) + singleton_class.send(:remove_method, :yo) def self.method_missing(_, **kw) kw end - assert_equal(false, y(**{}).frozen?) - assert_equal_not_same(kw, y(**kw)) - assert_equal_not_same(h, y(**h)) - assert_equal(false, send(:y, **{}).frozen?) - assert_equal_not_same(kw, send(:y, **kw)) - assert_equal_not_same(h, send(:y, **h)) - assert_equal(false, public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, public_send(:y, **kw)) - assert_equal_not_same(h, public_send(:y, **h)) + assert_equal(false, yo(**{}).frozen?) + assert_equal_not_same(kw, yo(**kw)) + assert_equal_not_same(h, yo(**h)) + assert_equal(false, send(:yo, **{}).frozen?) + assert_equal_not_same(kw, send(:yo, **kw)) + assert_equal_not_same(h, send(:yo, **h)) + assert_equal(false, public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, public_send(:yo, **kw)) + assert_equal_not_same(h, public_send(:yo, **h)) end def test_regular_kwsplat From 06abfa5be60e589052eb3bdfdae6c132bea3d20b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 26 Sep 2022 16:09:50 -0700 Subject: [PATCH 252/269] Revert this until we can figure out WB issues or remove shapes from GC Revert "* expand tabs. [ci skip]" This reverts commit 830b5b5c351c5c6efa5ad461ae4ec5085e5f0275. Revert "This commit implements the Object Shapes technique in CRuby." This reverts commit 9ddfd2ca004d1952be79cf1b84c52c79a55978f4. --- bootstraptest/test_attr.rb | 16 - common.mk | 322 -------- compile.c | 26 +- debug_counter.h | 10 +- ext/coverage/depend | 4 - ext/objspace/depend | 5 - ext/objspace/objspace.c | 1 - gc.c | 215 ++--- 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/imemo.h | 1 - internal/object.h | 22 + internal/variable.h | 5 - iseq.c | 16 +- lib/mjit/compiler.rb | 117 ++- marshal.c | 10 +- misc/lldb_cruby.py | 6 +- mjit_c.rb | 37 +- mjit_compiler.h | 2 +- object.c | 46 +- ractor_core.h | 6 +- shape.c | 571 ------------- shape.h | 153 ---- .../reachable_objects_from_spec.rb | 2 +- spec/ruby/optional/capi/shared/rbasic.rb | 11 +- test/-ext-/marshal/test_internal_ivar.rb | 1 - test/objspace/test_objspace.rb | 14 +- test/ruby/test_mjit.rb | 4 +- test/ruby/test_shapes.rb | 171 ---- tool/mjit/bindgen.rb | 10 +- variable.c | 752 ++++++++++-------- variable.h | 10 +- vm.c | 45 -- vm_callinfo.h | 108 +-- vm_core.h | 11 +- vm_eval.c | 4 +- vm_insnhelper.c | 490 ++++-------- yjit/bindgen/src/main.rs | 7 - yjit/src/asm/x86_64/mod.rs | 2 +- yjit/src/codegen.rs | 135 ++-- yjit/src/cruby.rs | 12 +- yjit/src/cruby_bindings.inc.rs | 41 +- 45 files changed, 928 insertions(+), 2530 deletions(-) delete mode 100644 shape.c delete mode 100644 shape.h delete mode 100644 test/ruby/test_shapes.rb diff --git a/bootstraptest/test_attr.rb b/bootstraptest/test_attr.rb index 3cb9d3eb39541c..721a847145a797 100644 --- a/bootstraptest/test_attr.rb +++ b/bootstraptest/test_attr.rb @@ -34,19 +34,3 @@ 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 cfcd6bd5a04fc5..150add3026dac1 100644 --- a/common.mk +++ b/common.mk @@ -136,7 +136,6 @@ COMMONOBJS = array.$(OBJEXT) \ regsyntax.$(OBJEXT) \ ruby.$(OBJEXT) \ scheduler.$(OBJEXT) \ - shape.$(OBJEXT) \ signal.$(OBJEXT) \ sprintf.$(OBJEXT) \ st.$(OBJEXT) \ @@ -1833,7 +1832,6 @@ 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 @@ -1850,7 +1848,6 @@ 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 @@ -2013,7 +2010,6 @@ 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 @@ -2032,7 +2028,6 @@ 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 @@ -2050,11 +2045,9 @@ 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 @@ -2214,7 +2207,6 @@ 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 @@ -2398,7 +2390,6 @@ 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 @@ -2414,7 +2405,6 @@ 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 @@ -2432,10 +2422,8 @@ 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 @@ -2584,7 +2572,6 @@ 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 @@ -2787,7 +2774,6 @@ 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 @@ -3191,7 +3177,6 @@ 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 @@ -3216,7 +3201,6 @@ 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 @@ -3231,7 +3215,6 @@ 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 @@ -3379,7 +3362,6 @@ 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 @@ -3397,7 +3379,6 @@ 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) @@ -3413,7 +3394,6 @@ 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 @@ -3572,7 +3552,6 @@ 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 @@ -3593,7 +3572,6 @@ 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 @@ -3608,7 +3586,6 @@ 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 @@ -3779,7 +3756,6 @@ 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 @@ -3964,7 +3940,6 @@ 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 @@ -3979,7 +3954,6 @@ 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 @@ -4142,7 +4116,6 @@ 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 @@ -5468,7 +5441,6 @@ 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 @@ -5482,7 +5454,6 @@ 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 @@ -5645,7 +5616,6 @@ 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 @@ -5670,7 +5640,6 @@ 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 @@ -5684,7 +5653,6 @@ 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 @@ -5845,7 +5813,6 @@ 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 @@ -6245,7 +6212,6 @@ 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 @@ -6467,7 +6433,6 @@ 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 @@ -6508,7 +6473,6 @@ 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 @@ -6522,7 +6486,6 @@ 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 @@ -6685,7 +6648,6 @@ 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 @@ -6918,7 +6880,6 @@ 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 @@ -6944,7 +6905,6 @@ 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 @@ -6960,13 +6920,11 @@ 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 @@ -7116,7 +7074,6 @@ 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 @@ -7142,7 +7099,6 @@ 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 @@ -7156,7 +7112,6 @@ 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 @@ -7321,7 +7276,6 @@ 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 @@ -7706,7 +7660,6 @@ 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 @@ -8109,7 +8062,6 @@ 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 @@ -8322,7 +8274,6 @@ 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 @@ -8841,7 +8792,6 @@ 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 @@ -8856,7 +8806,6 @@ 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 @@ -9018,7 +8967,6 @@ 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 @@ -9032,7 +8980,6 @@ 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 @@ -9046,7 +8993,6 @@ 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 @@ -9193,7 +9139,6 @@ 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 @@ -9375,7 +9320,6 @@ 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 @@ -9393,14 +9337,12 @@ 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 @@ -9574,7 +9516,6 @@ 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 @@ -9606,7 +9547,6 @@ 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 @@ -9801,7 +9741,6 @@ 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 @@ -10012,7 +9951,6 @@ 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 @@ -10202,7 +10140,6 @@ 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 @@ -10405,7 +10342,6 @@ 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 @@ -10607,7 +10543,6 @@ 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 @@ -11026,7 +10961,6 @@ 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 @@ -11041,7 +10975,6 @@ 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 @@ -11208,7 +11141,6 @@ 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 @@ -11428,7 +11360,6 @@ 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 @@ -11459,7 +11390,6 @@ 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 @@ -11475,7 +11405,6 @@ 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 @@ -11645,7 +11574,6 @@ 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 @@ -12043,7 +11971,6 @@ 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 @@ -12057,7 +11984,6 @@ 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 @@ -12206,7 +12132,6 @@ 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 @@ -12404,7 +12329,6 @@ 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 @@ -13602,7 +13526,6 @@ 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 @@ -13624,7 +13547,6 @@ 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 @@ -13639,12 +13561,10 @@ 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 @@ -13806,7 +13726,6 @@ 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 @@ -13972,208 +13891,6 @@ 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 @@ -14190,7 +13907,6 @@ 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 @@ -14205,7 +13921,6 @@ 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 @@ -14372,7 +14087,6 @@ 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 @@ -14397,7 +14111,6 @@ 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 @@ -14411,7 +14124,6 @@ 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 @@ -14573,7 +14285,6 @@ 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 @@ -14942,7 +14653,6 @@ 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 @@ -14957,7 +14667,6 @@ 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 @@ -15125,7 +14834,6 @@ 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 @@ -15182,7 +14890,6 @@ 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 @@ -15198,7 +14905,6 @@ 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 @@ -15361,7 +15067,6 @@ 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 @@ -15381,7 +15086,6 @@ 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 @@ -15395,7 +15099,6 @@ 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 @@ -15561,7 +15264,6 @@ 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 @@ -15592,7 +15294,6 @@ 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 @@ -15608,7 +15309,6 @@ 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 @@ -15782,7 +15482,6 @@ 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 @@ -16003,7 +15702,6 @@ 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 @@ -16016,7 +15714,6 @@ 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 @@ -16175,7 +15872,6 @@ 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 @@ -16725,7 +16421,6 @@ 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 @@ -16751,7 +16446,6 @@ 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 @@ -16768,11 +16462,9 @@ 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 @@ -16921,7 +16613,6 @@ 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 @@ -17157,7 +16848,6 @@ 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 @@ -17194,7 +16884,6 @@ 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 @@ -17209,13 +16898,11 @@ 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 @@ -17375,7 +17062,6 @@ 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 @@ -17566,7 +17252,6 @@ 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 @@ -17586,7 +17271,6 @@ 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 @@ -17601,7 +17285,6 @@ 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 @@ -17756,7 +17439,6 @@ 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 @@ -17780,7 +17462,6 @@ 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 @@ -17796,14 +17477,12 @@ 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 @@ -17965,7 +17644,6 @@ 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 9051ecfcd69ea1..a5da919c0ae4e9 100644 --- a/compile.c +++ b/compile.c @@ -2058,7 +2058,20 @@ cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr) static inline VALUE get_ivar_ic_value(rb_iseq_t *iseq,ID id) { - return INT2FIX(ISEQ_BODY(iseq)->ivc_size++); + 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; } static inline VALUE @@ -2459,13 +2472,9 @@ 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; @@ -11505,11 +11514,6 @@ 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 256633727fc86d..c6f4176e9752de 100644 --- a/debug_counter.h +++ b/debug_counter.h @@ -130,6 +130,7 @@ 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) @@ -139,17 +140,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 * @@ -320,7 +321,6 @@ RB_DEBUG_COUNTER(obj_imemo_parser_strterm) RB_DEBUG_COUNTER(obj_imemo_callinfo) RB_DEBUG_COUNTER(obj_imemo_callcache) RB_DEBUG_COUNTER(obj_imemo_constcache) -RB_DEBUG_COUNTER(obj_imemo_shape) /* ar_table */ RB_DEBUG_COUNTER(artable_hint_hit) diff --git a/ext/coverage/depend b/ext/coverage/depend index 719c6c6e791551..57d368d3f53660 100644 --- a/ext/coverage/depend +++ b/ext/coverage/depend @@ -165,9 +165,7 @@ 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 @@ -178,14 +176,12 @@ 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 88c66a232b7f57..c4da8031cc7087 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -350,7 +350,6 @@ 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 @@ -534,9 +533,7 @@ 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 @@ -547,14 +544,12 @@ 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/ext/objspace/objspace.c b/ext/objspace/objspace.c index 364d36e6963493..0b1b0943250387 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -644,7 +644,6 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self) INIT_IMEMO_TYPE_ID(imemo_callinfo); INIT_IMEMO_TYPE_ID(imemo_callcache); INIT_IMEMO_TYPE_ID(imemo_constcache); - INIT_IMEMO_TYPE_ID(imemo_shape); #undef INIT_IMEMO_TYPE_ID } diff --git a/gc.c b/gc.c index 77e2d1aeb8a06d..d026139d7bebff 100644 --- a/gc.c +++ b/gc.c @@ -2895,7 +2895,8 @@ 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); - uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count; + 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; size_t size; bool embed = true; @@ -2930,7 +2931,7 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) #endif } else { - rb_ensure_iv_list_size(obj, 0, index_tbl_num_entries); + rb_init_iv_list(obj); } return obj; @@ -2971,7 +2972,6 @@ rb_imemo_name(enum imemo_type type) IMEMO_NAME(callinfo); IMEMO_NAME(callcache); IMEMO_NAME(constcache); - IMEMO_NAME(shape); #undef IMEMO_NAME } return "unknown"; @@ -3018,14 +3018,6 @@ imemo_memsize(VALUE obj) case imemo_iseq: size += rb_iseq_memsize((rb_iseq_t *)obj); break; - case imemo_shape: - { - struct rb_id_table* edges = ((rb_shape_t *) obj)->edges; - if (edges) { - size += rb_id_table_memsize(edges); - } - break; - } case imemo_env: size += RANY(obj)->as.imemo.env.env_size * sizeof(VALUE); break; @@ -3214,6 +3206,20 @@ 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 @@ -3381,22 +3387,6 @@ obj_free_object_id(rb_objspace_t *objspace, VALUE obj) } } -static enum rb_id_table_iterator_result -remove_child_shapes_parent(VALUE value, void *ref) -{ - rb_shape_t * shape = (rb_shape_t *) value; - GC_ASSERT(IMEMO_TYPE_P(shape, imemo_shape)); - - // If both objects live on the same page and we're currently - // sweeping that page, then we need to assert that neither are marked - if (GET_HEAP_PAGE(shape) == GET_HEAP_PAGE(shape->parent)) { - GC_ASSERT(!MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(shape), shape)); - } - - shape->parent = NULL; - return ID_TABLE_CONTINUE; -} - static int obj_free(rb_objspace_t *objspace, VALUE obj) { @@ -3445,19 +3435,6 @@ obj_free(rb_objspace_t *objspace, VALUE obj) RB_DEBUG_COUNTER_INC(obj_obj_transient); } else { - // A shape can be collected before an object is collected (if both - // happened to be garbage at the same time), so when we look up the shape, _do not_ - // assert that the shape is an IMEMO because it could be null - rb_shape_t *shape = rb_shape_get_shape_by_id_without_assertion(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); } @@ -3472,6 +3449,9 @@ 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)); @@ -3748,37 +3728,6 @@ obj_free(rb_objspace_t *objspace, VALUE obj) case imemo_constcache: RB_DEBUG_COUNTER_INC(obj_imemo_constcache); break; - case imemo_shape: - { - rb_shape_t *shape = (rb_shape_t *)obj; - rb_shape_t *parent = shape->parent; - - if (parent) { - RUBY_ASSERT(IMEMO_TYPE_P(parent, imemo_shape)); - RUBY_ASSERT(parent->edges); - VALUE res; // Only used to temporarily store lookup value - if (rb_id_table_lookup(parent->edges, shape->edge_name, &res)) { - if ((rb_shape_t *)res == shape) { - rb_id_table_delete(parent->edges, shape->edge_name); - } - } - else { - rb_bug("Edge %s should exist", rb_id2name(shape->edge_name)); - } - } - if (shape->edges) { - rb_id_table_foreach_values(shape->edges, remove_child_shapes_parent, NULL); - rb_id_table_free(shape->edges); - shape->edges = NULL; - } - - shape->parent = NULL; - - rb_shape_set_shape_by_id(SHAPE_ID(shape), NULL); - - RB_DEBUG_COUNTER_INC(obj_imemo_shape); - break; - } } return TRUE; @@ -4924,6 +4873,10 @@ 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); } @@ -7201,21 +7154,6 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj) const struct rb_callcache *cc = (const struct rb_callcache *)obj; // should not mark klass here gc_mark(objspace, (VALUE)vm_cc_cme(cc)); - - // Check it's an attr_(reader|writer) - if (cc->cme_ && (cc->cme_->def->type == VM_METHOD_TYPE_ATTRSET || - cc->cme_->def->type == VM_METHOD_TYPE_IVAR)) { - shape_id_t source_shape_id = vm_cc_attr_index_source_shape_id(cc); - shape_id_t dest_shape_id = vm_cc_attr_index_dest_shape_id(cc); - if (source_shape_id != INVALID_SHAPE_ID) { - rb_shape_t *shape = rb_shape_get_shape_by_id(source_shape_id); - rb_gc_mark((VALUE)shape); - } - if (dest_shape_id != INVALID_SHAPE_ID) { - rb_shape_t *shape = rb_shape_get_shape_by_id(dest_shape_id); - rb_gc_mark((VALUE)shape); - } - } } return; case imemo_constcache: @@ -7224,14 +7162,6 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj) gc_mark(objspace, ice->value); } return; - case imemo_shape: - { - rb_shape_t *shape = (rb_shape_t *)obj; - if (shape->edges) { - mark_m_tbl(objspace, shape->edges); - } - } - return; #if VM_CHECK_MODE > 0 default: VM_UNREACHABLE(gc_mark_imemo); @@ -9835,10 +9765,6 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj) GC_ASSERT(!SPECIAL_CONST_P(obj)); switch (BUILTIN_TYPE(obj)) { - case T_IMEMO: - if (IMEMO_TYPE_P(obj, imemo_shape)) { - return FALSE; - } case T_NONE: case T_NIL: case T_MOVED: @@ -9852,6 +9778,7 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj) case T_STRING: case T_OBJECT: case T_FLOAT: + case T_IMEMO: case T_ARRAY: case T_BIGNUM: case T_ICLASS: @@ -10251,38 +10178,6 @@ gc_update_values(rb_objspace_t *objspace, long n, VALUE *values) } } -static enum rb_id_table_iterator_result -check_id_table_move(VALUE value, void *data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - if (gc_object_moved_p(objspace, (VALUE)value)) { - return ID_TABLE_REPLACE; - } - - return ID_TABLE_CONTINUE; -} - -static enum rb_id_table_iterator_result -update_id_table(VALUE *value, void *data, int existing) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - if (gc_object_moved_p(objspace, (VALUE)*value)) { - *value = rb_gc_location((VALUE)*value); - } - - return ID_TABLE_CONTINUE; -} - -static void -update_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl) -{ - if (tbl) { - rb_id_table_foreach_values_with_replace(tbl, check_id_table_move, update_id_table, objspace); - } -} - static void gc_ref_update_imemo(rb_objspace_t *objspace, VALUE obj) { @@ -10355,23 +10250,24 @@ gc_ref_update_imemo(rb_objspace_t *objspace, VALUE obj) case imemo_tmpbuf: case imemo_callinfo: break; - case imemo_shape: - { - rb_shape_t * shape = (rb_shape_t *)obj; - if(shape->edges) { - update_m_tbl(objspace, shape->edges); - } - if (shape->parent) { - shape->parent = (rb_shape_t *)rb_gc_location((VALUE)shape->parent); - } - } - break; default: rb_bug("not reachable %d", imemo_type(obj)); break; } } +static enum rb_id_table_iterator_result +check_id_table_move(VALUE value, void *data) +{ + rb_objspace_t *objspace = (rb_objspace_t *)data; + + if (gc_object_moved_p(objspace, (VALUE)value)) { + return ID_TABLE_REPLACE; + } + + return ID_TABLE_CONTINUE; +} + /* Returns the new location of an object, if it moved. Otherwise returns * the existing location. */ VALUE @@ -10404,6 +10300,26 @@ rb_gc_location(VALUE value) return destination; } +static enum rb_id_table_iterator_result +update_id_table(VALUE *value, void *data, int existing) +{ + rb_objspace_t *objspace = (rb_objspace_t *)data; + + if (gc_object_moved_p(objspace, (VALUE)*value)) { + *value = rb_gc_location((VALUE)*value); + } + + return ID_TABLE_CONTINUE; +} + +static void +update_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl) +{ + if (tbl) { + rb_id_table_foreach_values_with_replace(tbl, check_id_table_move, update_id_table, objspace); + } +} + static enum rb_id_table_iterator_result update_cc_tbl_i(VALUE ccs_ptr, void *data) { @@ -10491,6 +10407,15 @@ 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) { @@ -10498,6 +10423,11 @@ 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 @@ -10739,8 +10669,6 @@ gc_update_references(rb_objspace_t *objspace) struct heap_page *page = NULL; - rb_vm_update_references(vm); - for (int i = 0; i < SIZE_POOL_COUNT; i++) { bool should_set_mark_bits = TRUE; rb_size_pool_t *size_pool = &size_pools[i]; @@ -10759,6 +10687,7 @@ gc_update_references(rb_objspace_t *objspace) } } } + rb_vm_update_references(vm); rb_transient_heap_update_references(); rb_gc_update_global_tbl(); global_symbols.ids = rb_gc_location(global_symbols.ids); diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h index bec0b45fd4e566..7823061d8ffbf7 100644 --- a/include/ruby/internal/core/robject.h +++ b/include/ruby/internal/core/robject.h @@ -46,6 +46,7 @@ #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 */ /** @@ -131,7 +132,7 @@ struct RObject { * * This is a shortcut for `RCLASS_IV_INDEX_TBL(rb_obj_class(obj))`. */ - struct rb_id_table *iv_index_tbl; + struct st_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 7383426b2358a2..c51bd2e9d946f2 100644 --- a/include/ruby/internal/fl_type.h +++ b/include/ruby/internal/fl_type.h @@ -941,8 +941,21 @@ RB_OBJ_FREEZE_RAW(VALUE obj) RB_FL_SET_RAW(obj, RUBY_FL_FREEZE); } -RUBY_SYMBOL_EXPORT_BEGIN -void rb_obj_freeze_inline(VALUE obj); -RUBY_SYMBOL_EXPORT_END +/** + * 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); + } + } +} #endif /* RBIMPL_FL_TYPE_H */ diff --git a/inits.c b/inits.c index d1204c1324f73b..9decba3c11006a 100644 --- a/inits.c +++ b/inits.c @@ -77,7 +77,6 @@ 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 695c9cfb7e76f2..0740ae99e5e3d3 100644 --- a/internal.h +++ b/internal.h @@ -48,6 +48,9 @@ #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 36635d6eaa819a..ae680564a6c317 100644 --- a/internal/class.h +++ b/internal/class.h @@ -14,7 +14,6 @@ #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,9 +26,9 @@ struct rb_subclass_entry { }; struct rb_iv_index_tbl_entry { - uint32_t idx; - shape_id_t source_shape_id; - shape_id_t dest_shape_id; + uint32_t index; + rb_serial_t class_serial; + VALUE class_value; }; struct rb_cvar_class_tbl_entry { @@ -39,6 +38,7 @@ 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,8 +64,6 @@ struct rb_classext_struct { const VALUE refined_class; rb_alloc_func_t allocator; const VALUE includer; - uint32_t max_iv_count; - uint16_t shape_id; }; struct RClass { @@ -104,6 +102,7 @@ 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/imemo.h b/internal/imemo.h index 20bfff8d7c5bb5..91b524e0a6ae77 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -45,7 +45,6 @@ enum imemo_type { imemo_callinfo = 11, imemo_callcache = 12, imemo_constcache = 13, - imemo_shape = 14, }; /* CREF (Class REFerence) is defined in method.h */ diff --git a/internal/object.h b/internal/object.h index 7b54e13dd2ae32..88f3a44bc6f866 100644 --- a/internal/object.h +++ b/internal/object.h @@ -9,6 +9,11 @@ * @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); @@ -21,6 +26,7 @@ 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) */ @@ -58,4 +64,20 @@ 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 47037a33923265..1a19e8964b9f8e 100644 --- a/internal/variable.h +++ b/internal/variable.h @@ -37,9 +37,6 @@ 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); @@ -55,8 +52,6 @@ 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 7014162a1951d7..a4792d81fdb5b6 100644 --- a/iseq.c +++ b/iseq.c @@ -233,15 +233,13 @@ rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data) // IVC entries for (unsigned int i = 0; i < body->ivc_size; i++, is_entries++) { IVC ivc = (IVC)is_entries; - shape_id_t source_shape_id = vm_ic_attr_index_source_shape_id(ivc); - shape_id_t dest_shape_id = vm_ic_attr_index_dest_shape_id(ivc); - if (source_shape_id != INVALID_SHAPE_ID) { - rb_shape_t *shape = rb_shape_get_shape_by_id(source_shape_id); - func(data, (VALUE)shape); - } - if (dest_shape_id != INVALID_SHAPE_ID) { - rb_shape_t *shape = rb_shape_get_shape_by_id(dest_shape_id); - func(data, (VALUE)shape); + 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; + } } } diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 06f018c9346920..49f28ab6908612 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -73,6 +73,23 @@ 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 @@ -86,13 +103,6 @@ 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) @@ -353,37 +363,52 @@ 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.source_shape_id != C.INVALID_SHAPE_ID + if !status.compile_info.disable_ivar_cache && ic_copy.entry # 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 << " 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" + 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 else - 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 << " 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" src << " }\n" else - 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 << " 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 << " }\n" end end @@ -394,19 +419,20 @@ 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 && ic_copy.entry # 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 rb_serial_t ic_serial = (rb_serial_t)#{ic_copy.entry.class_serial};\n" + src << " const uint32_t index = #{ic_copy.entry.index};\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" - src << " stack[#{stack_size}] = ivtbl->ivptr[index];\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 << " }\n" src << " else {\n" src << " reg_cfp->pc = original_body_iseq + #{pos};\n" @@ -806,16 +832,35 @@ 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 - status.merge_ivar_guards_p = true - return + 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 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 59edbfe53a2a88..e4b40c0607b608 100644 --- a/marshal.c +++ b/marshal.c @@ -39,7 +39,6 @@ #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); @@ -717,7 +720,6 @@ 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); @@ -725,10 +727,6 @@ 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 da97e28b698729..595d54dfab3dde 100755 --- a/misc/lldb_cruby.py +++ b/misc/lldb_cruby.py @@ -418,13 +418,9 @@ 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) - if imemo_type == imemo_shape: - append_command_output(debugger, "p *(rb_shape_t *) %0#x" % val.GetValueAsUnsigned(), result) - else: - append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result) + append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result) elif flType == RUBY_T_STRUCT: tRTypedData = target.FindFirstType("struct RStruct").GetPointerType() val = val.Cast(tRTypedData) diff --git a/mjit_c.rb b/mjit_c.rb index fd8991af3f03e7..d8e5628bda57d4 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -5,10 +5,6 @@ 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 @@ -169,14 +165,6 @@ 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 @@ -193,10 +181,6 @@ 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)"), @@ -217,6 +201,7 @@ 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)")], @@ -255,9 +240,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)")], + entry: [CType::Pointer.new { self.rb_iv_index_tbl_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), entry)")], ) end @@ -330,11 +313,7 @@ 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: 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)")], - ), + attr_index: CType::Immediate.parse("unsigned int"), method_missing_reason: self.method_missing_reason, v: self.VALUE, ), Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)")], @@ -523,9 +502,9 @@ def C.rb_iseq_t 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)"), - idx: [CType::Immediate.parse("uint32_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), idx)")], - 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)")], + 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)")], ) end @@ -598,10 +577,6 @@ 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 b465be00fd678a..da7905442025f5 100644 --- a/mjit_compiler.h +++ b/mjit_compiler.h @@ -8,7 +8,6 @@ #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 @@ -49,6 +48,7 @@ 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 213f9760beebf4..2328b20757aae2 100644 --- a/object.c +++ b/object.c @@ -39,7 +39,6 @@ #include "ruby/util.h" #include "ruby/assert.h" #include "builtin.h" -#include "shape.h" /*! * \addtogroup object @@ -272,33 +271,9 @@ 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 max_len = dest_len < src_len ? src_len : dest_len; + uint32_t len = dest_len < src_len ? dest_len : src_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); + MEMCPY(dest_buf, src_buf, VALUE, len); } static void @@ -308,23 +283,10 @@ 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); } @@ -430,9 +392,6 @@ 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: { @@ -448,7 +407,6 @@ 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 9d4b8387c734b0..a065f5f809d0cf 100644 --- a/ractor_core.h +++ b/ractor_core.h @@ -289,13 +289,11 @@ 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 & 0xffff0000ffffffff; // 4B + VALUE flags = RBASIC(obj)->flags & 0xffffffff; // 4B RBASIC(obj)->flags = flags | ((VALUE)rid << 32); } @@ -312,7 +310,7 @@ rb_ractor_belonging(VALUE obj) return 0; } else { - return RBASIC(obj)->flags >> 32 & 0xFFFF; + return RBASIC(obj)->flags >> 32; } } diff --git a/shape.c b/shape.c deleted file mode 100644 index fa914663401d3e..00000000000000 --- a/shape.c +++ /dev/null @@ -1,571 +0,0 @@ -#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; -} - -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]; - RUBY_ASSERT(IMEMO_TYPE_P(shape, imemo_shape)); - 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; -} - -static inline shape_id_t -shape_set_shape_id(rb_shape_t *shape, shape_id_t id) { - VALUE flags = shape->flags & ~((uint64_t)SHAPE_MASK << 16); - return (shape_id_t)(shape->flags = (flags | ((VALUE)id << SHAPE_FLAG_SHIFT))); -} - -#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 SHAPE_ID(rb_shape_get_frozen_root_shape()); - } - -#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 shape_id_t -get_next_shape_id(void) -{ - rb_vm_t *vm = GET_VM(); - vm->max_shape_count++; - return vm->max_shape_count; -} - -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) || rb_objspace_garbage_object_p((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; - } - - shape_id_t next_shape_id = get_next_shape_id(); - - if (next_shape_id == MAX_SHAPE_ID) { - // TODO: Make an OutOfShapesError ?? - rb_bug("Out of shapes\n"); - } - else { - RUBY_ASSERT(next_shape_id < MAX_SHAPE_ID); - rb_shape_t * new_shape = rb_shape_alloc(next_shape_id, - id, - shape); - - new_shape->type = (uint8_t)shape_type; - - switch(shape_type) { - case SHAPE_FROZEN: - RB_OBJ_FREEZE_RAW((VALUE)new_shape); - break; - 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: - 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); - RB_OBJ_WRITTEN((VALUE)shape, Qundef, (VALUE)new_shape); - rb_shape_set_shape_by_id(next_shape_id, 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; - } - - RUBY_ASSERT(!rb_objspace_garbage_object_p((VALUE)next_shape)); - 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; - } - - RUBY_ASSERT(!rb_objspace_garbage_object_p((VALUE)next_shape)); - 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_shape_t *shape = (rb_shape_t *)rb_imemo_new(imemo_shape, 0, 0, 0, 0); - FL_SET_RAW((VALUE)shape, RUBY_FL_SHAREABLE); - FL_SET_RAW((VALUE)shape, RUBY_FL_PROMOTED1); - return shape; -} - -rb_shape_t * -rb_shape_alloc(shape_id_t shape_id, ID edge_name, rb_shape_t * parent) -{ - rb_shape_t * shape = shape_alloc(); - shape_set_shape_id(shape, shape_id); - - shape->edge_name = edge_name; - shape->iv_count = 0; - - RB_OBJ_WRITE(shape, &shape->parent, parent); - - RUBY_ASSERT(!parent || IMEMO_TYPE_P(parent, imemo_shape)); - - return shape; -} - -MJIT_FUNC_EXPORTED void -rb_shape_set_shape(VALUE obj, rb_shape_t* shape) -{ - RUBY_ASSERT(IMEMO_TYPE_P(shape, imemo_shape)); - RUBY_ASSERT(SHAPE_FROZEN == shape->type ? RB_OBJ_FROZEN(obj) : 1); - - if(rb_shape_set_shape_id(obj, SHAPE_ID(shape))) { - if (shape != rb_shape_get_frozen_root_shape()) { - RB_OBJ_WRITTEN(obj, Qundef, (VALUE)shape); - } - } -} - -void -rb_shape_set_shape_by_id(shape_id_t shape_id, rb_shape_t *shape) -{ - rb_vm_t *vm = GET_VM(); - - RUBY_ASSERT(shape == NULL || IMEMO_TYPE_P(shape, imemo_shape)); - vm->shape_list[shape_id] = shape; -} - -VALUE rb_cShape; - -static void -shape_mark(void *ptr) -{ - rb_gc_mark((VALUE)ptr); -} - -/* - * Exposing Shape to Ruby via RubyVM.debug_shape - */ -static const rb_data_type_t shape_data_type = { - "Shape", - {shape_mark, NULL, NULL,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED -}; - -static VALUE -rb_shape_id(VALUE self) { - rb_shape_t * shape; - TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); - return INT2NUM(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(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); - RB_OBJ_WRITTEN(res, Qundef, shape); - - 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(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(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->max_shape_count; 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()->max_shape_count); -} - -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_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 deleted file mode 100644 index b381b9e6ba3555..00000000000000 --- a/shape.h +++ /dev/null @@ -1,153 +0,0 @@ -#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 - -#define SHAPE_ID(shape) ((((rb_shape_t *)shape)->flags >> SHAPE_FLAG_SHIFT) & SHAPE_MASK) - -struct rb_shape { - VALUE flags; // Shape ID and frozen status encoded within flags - 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); -MJIT_SYMBOL_EXPORT_END - -rb_shape_t * rb_shape_alloc(shape_id_t shape_id, ID edge_name, rb_shape_t * parent); - -bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id); -void rb_shape_set_shape_by_id(shape_id_t, rb_shape_t *); - -VALUE rb_obj_debug_shape(VALUE self, VALUE obj); -VALUE rb_shape_flags_mask(void); - -#endif diff --git a/spec/ruby/library/objectspace/reachable_objects_from_spec.rb b/spec/ruby/library/objectspace/reachable_objects_from_spec.rb index 61ebc9f93d86b8..7e70bc85691989 100644 --- a/spec/ruby/library/objectspace/reachable_objects_from_spec.rb +++ b/spec/ruby/library/objectspace/reachable_objects_from_spec.rb @@ -17,7 +17,7 @@ it "enumerates objects directly reachable from a given object" do ObjectSpace.reachable_objects_from(['a', 'b', 'c']).should include(Array, 'a', 'b', 'c') - ObjectSpace.reachable_objects_from(Object.new).should include(Object) + ObjectSpace.reachable_objects_from(Object.new).should == [Object] end it "finds an object stored in an Array" do diff --git a/spec/ruby/optional/capi/shared/rbasic.rb b/spec/ruby/optional/capi/shared/rbasic.rb index 105408ec3d822d..99c2044bd7f375 100644 --- a/spec/ruby/optional/capi/shared/rbasic.rb +++ b/spec/ruby/optional/capi/shared/rbasic.rb @@ -8,9 +8,16 @@ it "reports the appropriate FREEZE flag for the object when reading" do obj, _ = @data.call - (@specs.get_flags(obj) & @freeze).should == 0 + initial = @specs.get_flags(obj) obj.freeze - (@specs.get_flags(obj) & @freeze).should == @freeze + @specs.get_flags(obj).should == @freeze | initial + end + + it "supports setting the FREEZE flag" do + obj, _ = @data.call + initial = @specs.get_flags(obj) + @specs.set_flags(obj, @freeze | initial).should == @freeze | initial + obj.should.frozen? end it "supports retrieving the (meta)class" do diff --git a/test/-ext-/marshal/test_internal_ivar.rb b/test/-ext-/marshal/test_internal_ivar.rb index 9359c7f11320c3..a32138f6e8f50d 100644 --- a/test/-ext-/marshal/test_internal_ivar.rb +++ b/test/-ext-/marshal/test_internal_ivar.rb @@ -7,7 +7,6 @@ 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/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 94d68dc0932d9b..3b9031985886c2 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -116,16 +116,12 @@ def test_reachable_objects_from opts = %w[--disable-gem --disable=frozen-string-literal -robjspace] assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" begin; - def assert_reachable_object_as_expected(expectation, reachable_objects_from_array) - reachable_objects = ObjectSpace.reachable_objects_from(reachable_objects_from_array) - assert_equal(expectation, reachable_objects) - end - assert_equal(nil, ObjectSpace.reachable_objects_from(nil)) - assert_reachable_object_as_expected([Array, 'a', 'b', 'c'], ['a', 'b', 'c']) - assert_reachable_object_as_expected([Array, 'a', 'a', 'a'], ['a', 'a', 'a']) - assert_reachable_object_as_expected([Array, 'a', 'a'], ['a', v = 'a', v]) - assert_reachable_object_as_expected([Array, 'a'], [v = 'a', v, v]) + assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c'])) + + assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a'])) + assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v])) + assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v])) long_ary = Array.new(1_000){''} max = 0 diff --git a/test/ruby/test_mjit.rb b/test/ruby/test_mjit.rb index 4c6cc6f39fdfd9..e49195f7631c67 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: 4, recompile_count: 2, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 3, recompile_count: 1, 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: 2, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 3, min_calls: 3) begin; class Foo def initialize diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb deleted file mode 100644 index cdca08dbb43c8d..00000000000000 --- a/test/ruby/test_shapes.rb +++ /dev/null @@ -1,171 +0,0 @@ -# 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?) - refute_equal(RubyVM.debug_shape(obj).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 8c21d424495c0c..d0f9bf527bf4c3 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -341,17 +341,12 @@ 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 @@ -365,10 +360,10 @@ def push_target(target) rb_callable_method_entry_struct rb_callcache rb_callinfo - rb_control_frame_t rb_cref_t - rb_execution_context_struct + rb_control_frame_t rb_execution_context_t + rb_execution_context_struct rb_iseq_constant_body rb_iseq_location_t rb_iseq_struct @@ -383,7 +378,6 @@ 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 abbe76eba7e356..056a1000b8edee 100644 --- a/variable.c +++ b/variable.c @@ -34,7 +34,6 @@ #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" @@ -64,9 +63,12 @@ static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int v static st_table *generic_iv_tbl_; struct ivar_update { - struct gen_ivtbl *ivtbl; - uint32_t iv_index; - rb_shape_t* shape; + union { + st_table *iv_index_tbl; + struct gen_ivtbl *ivtbl; + } u; + st_data_t index; + int iv_extended; }; void @@ -894,6 +896,30 @@ 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) { @@ -931,20 +957,7 @@ generic_ivtbl_no_ractor_check(VALUE obj) } static int -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) +gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl) { st_data_t data; int r = 0; @@ -964,7 +977,63 @@ rb_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 rb_gen_ivtbl_get(obj, 0, 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; } static size_t @@ -976,8 +1045,6 @@ 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)); @@ -1002,6 +1069,18 @@ 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) { @@ -1012,22 +1091,53 @@ 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->iv_index < ivtbl->numiv) { - ivup->ivtbl = ivtbl; + if (ivup->index < ivtbl->numiv) { + ivup->u.ivtbl = ivtbl; return ST_STOP; } } FL_SET((VALUE)*k, FL_EXIVAR); - 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 + uint32_t newsize = iv_index_tbl_newsize(ivup); + ivtbl = gen_ivtbl_resize(ivtbl, newsize); *v = (st_data_t)ivtbl; - ivup->ivtbl = ivtbl; -#if !SHAPE_IN_BASIC_FLAGS - ivtbl->shape_id = SHAPE_ID(ivup->shape); -#endif + ivup->u.ivtbl = ivtbl; 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) { @@ -1043,11 +1153,7 @@ rb_mark_generic_ivar(VALUE obj) { struct gen_ivtbl *ivtbl; - if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { - -#if !SHAPE_IN_BASIC_FLAGS - rb_gc_mark((VALUE)rb_shape_get_shape_by_id(ivtbl->shape_id)); -#endif + if (gen_ivtbl_get(obj, 0, &ivtbl)) { gen_ivtbl_mark(ivtbl); } } @@ -1076,35 +1182,11 @@ rb_generic_ivar_memsize(VALUE obj) { struct gen_ivtbl *ivtbl; - if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) + if (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) { @@ -1172,16 +1254,23 @@ 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: { @@ -1198,37 +1287,14 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) return val; } else { - return undef; + break; } } - 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_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; - } + if (FL_TEST(obj, FL_EXIVAR)) + return generic_ivar_get(obj, id, 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; } @@ -1249,12 +1315,26 @@ rb_attr_get(VALUE obj, ID id) static VALUE rb_ivar_delete(VALUE obj, ID id, VALUE undef) { - rb_check_frozen(obj); - - VALUE val = Qnil; - attr_index_t index; + 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; case T_CLASS: case T_MODULE: IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id); @@ -1265,33 +1345,11 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) } } break; - 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; - } - + default: + if (FL_TEST(obj, FL_EXIVAR)) + return generic_ivar_delete(obj, id, undef); break; - } } - return undef; } @@ -1301,34 +1359,67 @@ 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; - // 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; + ivup.iv_extended = 0; + ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass); RB_VM_LOCK_ENTER(); { - 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)); - } - - if (!st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update, - (st_data_t)&ivup)) { - RB_OBJ_WRITTEN(obj, Qundef, shape); - } + 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); } RB_VM_LOCK_LEAVE(); - ivup.ivtbl->ivptr[ivup.iv_index] = val; + ivup.u.ivtbl->ivptr[ivup.index] = val; - rb_shape_set_shape(obj, shape); RB_OBJ_WRITTEN(obj, Qundef, val); } @@ -1395,8 +1486,8 @@ rb_obj_transient_heap_evacuate(VALUE obj, int promote) } #endif -void -rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize) +static void +init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl) { VALUE *ptr = ROBJECT_IVPTR(obj); VALUE *newptr; @@ -1419,34 +1510,35 @@ rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize) #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); } -struct gen_ivtbl * -rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize) +// 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 * ivtbl = 0; + 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); RB_VM_LOCK_ENTER(); { - 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); - } + iv_index_tbl_extend(&ivup, id, klass); } RB_VM_LOCK_LEAVE(); - 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 ivup; } // Return the instance variable index for a given name and T_OBJECT object. The @@ -1460,114 +1552,26 @@ uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id) { 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)); - } - - 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; + // 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; } 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); - 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; -} - -bool -rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id) -{ - if (rb_shape_get_shape_id(obj) == shape_id) { - return false; - } + uint32_t len; + struct ivar_update ivup = obj_ensure_iv_index_mapping(obj, id); -#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; - } - case T_IMEMO: - if (imemo_type(obj) == imemo_shape) { - RBASIC(obj)->flags &= 0xffffffff0000ffff; - RBASIC(obj)->flags |= ((uint32_t)(shape_id) << 16); - } - 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(); - } - } + 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); } -#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); + RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[ivup.index], val); - if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) { - rb_freeze_singleton_class(x); - } - } + return val; } static void @@ -1577,14 +1581,10 @@ 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,86 +1614,161 @@ rb_ivar_set_internal(VALUE obj, ID id, VALUE val) VALUE rb_ivar_defined(VALUE obj, ID id) { - attr_index_t index; + VALUE val; + struct st_table *iv_index_tbl; + uint32_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; - } - else { - return Qfalse; - } + break; default: - return RBOOL(rb_shape_get_iv_index(rb_shape_get_shape(obj), id, &index)); + if (FL_TEST(obj, FL_EXIVAR)) + return generic_ivar_defined(obj, id); + break; } + 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 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; +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"); + } + } + return false; } static void obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) { - rb_shape_t* shape = rb_shape_get_shape(obj); - iterate_over_shapes_with_callback(shape, ROBJECT_IVPTR(obj), func, 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; + } + } } 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; - if (!rb_gen_ivtbl_get(obj, 0, &ivtbl)) return; + 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; + + 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); - iterate_over_shapes_with_callback(shape, ivtbl->ivptr, func, arg); + return ST_CONTINUE; } void rb_copy_generic_ivar(VALUE clone, VALUE obj) { - struct gen_ivtbl *obj_ivtbl; - struct gen_ivtbl *new_ivtbl; + struct gen_ivtbl *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 (rb_gen_ivtbl_get(obj, 0, &obj_ivtbl)) { - if (gen_ivtbl_count(obj_ivtbl) == 0) + if (gen_ivtbl_count(ivtbl) == 0) goto clear; - 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]); + 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); } + 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 @@ -1701,17 +1776,9 @@ 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)new_ivtbl); + st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)clone, (st_data_t)c.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; @@ -1779,7 +1846,7 @@ rb_ivar_count(VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - if (rb_shape_get_shape(obj)->iv_count > 0) { + if (ROBJECT_IV_INDEX_TBL(obj) != 0) { st_index_t i, count, num = ROBJECT_NUMIV(obj); const VALUE *const ivptr = ROBJECT_IVPTR(obj); for (i = count = 0; i < num; ++i) { @@ -1800,7 +1867,7 @@ rb_ivar_count(VALUE obj) if (FL_TEST(obj, FL_EXIVAR)) { struct gen_ivtbl *ivtbl; - if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { + if (gen_ivtbl_get(obj, 0, &ivtbl)) { return gen_ivtbl_count(ivtbl); } } @@ -1898,53 +1965,40 @@ 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); - 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; - 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; + n = id; + if (RCLASS_IV_TBL(obj) && lock_st_delete(RCLASS_IV_TBL(obj), &n, &v)) { + return (VALUE)v; } - 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; + default: + if (FL_TEST(obj, FL_EXIVAR)) { + if (generic_ivar_remove(obj, id, &val)) { + return val; + } } - break; - } } not_defined: diff --git a/variable.h b/variable.h index 314ac82df0dbc9..55596b00de6213 100644 --- a/variable.h +++ b/variable.h @@ -11,19 +11,11 @@ /* 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 **); - -#include "shape.h" -#if !SHAPE_IN_BASIC_FLAGS -shape_id_t rb_generic_shape_id(VALUE obj); -#endif - +VALUE rb_ivar_generic_lookup_with_index(VALUE obj, ID id, uint32_t index); #endif /* RUBY_TOPLEVEL_VARIABLE_H */ diff --git a/vm.c b/vm.c index 77393458c2a6c5..0de461392f398b 100644 --- a/vm.c +++ b/vm.c @@ -26,7 +26,6 @@ #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" @@ -2721,12 +2720,6 @@ rb_vm_update_references(void *ptr) vm->top_self = rb_gc_location(vm->top_self); vm->orig_progname = rb_gc_location(vm->orig_progname); - for (shape_id_t i = 0; i <= vm->max_shape_count; i++) { - if (vm->shape_list[i]) { - vm->shape_list[i] = (rb_shape_t *)rb_gc_location((VALUE)vm->shape_list[i]); - } - } - rb_gc_update_tbl_refs(vm->overloaded_cme_table); if (vm->coverages) { @@ -2808,8 +2801,6 @@ rb_vm_mark(void *ptr) obj_ary++; } - rb_gc_mark((VALUE)vm->root_shape); - rb_gc_mark((VALUE)vm->frozen_root_shape); rb_gc_mark_movable(vm->load_path); rb_gc_mark_movable(vm->load_path_snapshot); RUBY_MARK_MOVABLE_UNLESS_NULL(vm->load_path_check_cache); @@ -4030,11 +4021,6 @@ Init_BareVM(void) rb_native_cond_initialize(&vm->ractor.sync.terminate_cond); } -#ifndef _WIN32 -#include -#include -#endif - void Init_vm_objects(void) { @@ -4046,37 +4032,6 @@ 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(ROOT_SHAPE_ID, - 0, - 0); - rb_shape_set_shape_by_id(ROOT_SHAPE_ID, vm->root_shape); - RB_OBJ_WRITTEN(vm->root_shape, Qundef, (VALUE)vm); - - // Frozen root shape - vm->frozen_root_shape = rb_shape_alloc(FROZEN_ROOT_SHAPE_ID, - rb_make_internal_id(), - vm->root_shape); - vm->frozen_root_shape->type = (uint8_t)SHAPE_FROZEN; - RB_OBJ_FREEZE_RAW((VALUE)vm->frozen_root_shape); - rb_shape_set_shape_by_id(FROZEN_ROOT_SHAPE_ID, vm->frozen_root_shape); - RB_OBJ_WRITTEN(vm->frozen_root_shape, Qundef, (VALUE)vm); - vm->max_shape_count = 1; } /* Stub for builtin function when not building YJIT units*/ diff --git a/vm_callinfo.h b/vm_callinfo.h index e5b04c0709e883..fd2215be7dd7af 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -10,7 +10,6 @@ #include "debug_counter.h" #include "internal/class.h" -#include "shape.h" enum vm_call_flag_bits { VM_CALL_ARGS_SPLAT_bit, /* m(*args) */ @@ -285,32 +284,14 @@ struct rb_callcache { const vm_call_handler call_; union { - struct { - const attr_index_t index; - shape_id_t dest_shape_id; - } attr; + const unsigned int attr_index; const enum method_missing_reason method_missing_reason; /* used by method_missing */ VALUE v; } aux_; }; -#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; -} +#define VM_CALLCACHE_UNMARKABLE IMEMO_FL_USER0 +#define VM_CALLCACHE_ON_STACK IMEMO_FL_USER1 static inline const struct rb_callcache * vm_cc_new(VALUE klass, @@ -318,7 +299,6 @@ 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; } @@ -370,71 +350,30 @@ vm_cc_call(const struct rb_callcache *cc) return cc->call_; } -static inline attr_index_t +static inline unsigned int 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; -} - -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); + return cc->aux_.attr_index > 0; } -static inline shape_id_t -vm_cc_attr_shape_id(const struct rb_callcache *cc) +static inline uint32_t +vm_ic_entry_index(const struct iseq_inline_iv_cache_entry *ic) { - 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; + return ic->entry->index; } static inline bool -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) +vm_ic_entry_p(const struct iseq_inline_iv_cache_entry *ic) { - 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; + return ic->entry; } static inline unsigned int @@ -468,6 +407,10 @@ 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 @@ -479,29 +422,26 @@ 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, int index) { 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; + *(int *)&cc->aux_.attr_index = index + 1; } 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_entry_set(struct iseq_inline_iv_cache_entry *ic, struct rb_iv_index_tbl_entry *entry, const rb_iseq_t *iseq) { - *(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; + ic->entry = entry; + RB_OBJ_WRITTEN(iseq, Qundef, entry->class_value); } static inline void -vm_ic_attr_index_initialize(const struct iseq_inline_iv_cache_entry *ic, shape_id_t shape_id) +vm_cc_attr_index_initialize(const struct rb_callcache *cc) { - *(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; + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + VM_ASSERT(cc != vm_cc_empty()); + *(int *)&cc->aux_.attr_index = 0; } static inline void diff --git a/vm_core.h b/vm_core.h index b4768be13629b5..e11838a59a3aaa 100644 --- a/vm_core.h +++ b/vm_core.h @@ -99,7 +99,6 @@ 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" @@ -273,9 +272,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; + struct rb_iv_index_tbl_entry *entry; }; struct iseq_inline_cvar_cache_entry { @@ -690,12 +687,6 @@ 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 max_shape_count; - /* load */ VALUE top_self; VALUE load_path; diff --git a/vm_eval.c b/vm_eval.c index bfa812f3d34341..c0558fce2bcb0a 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -47,7 +47,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, @@ -89,7 +89,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 1cd66cf1eb387e..a662de468dda9a 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -50,11 +50,6 @@ 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; } @@ -1090,19 +1085,35 @@ vm_get_cvar_base(const rb_cref_t *cref, const rb_control_frame_t *cfp, int top_l return klass; } -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 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)); + 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) +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) { - if (is_attr) { - if (vm_cc_markable(cc)) { - vm_cc_attr_index_set(cc, index, shape_id, shape_id); - RB_OBJ_WRITTEN(cc, Qundef, rb_shape_get_shape_by_id(shape_id)); - } + // fill cache + if (!is_attr) { + vm_ic_entry_set(ic, ent, iseq); } else { - vm_ic_attr_index_set(iseq, ic, index, shape_id, shape_id); - RB_OBJ_WRITTEN(iseq, Qundef, rb_shape_get_shape_by_id(shape_id)); + vm_cc_attr_index_set(cc, ent->index); } } @@ -1112,120 +1123,68 @@ 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)) { - return Qnil; + // frozen? } + 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)); -#if SHAPE_IN_BASIC_FLAGS - shape_id = RBASIC_SHAPE_ID(obj); -#endif + RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); - 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); + if (LIKELY(BUILTIN_TYPE(obj) == T_OBJECT) && + LIKELY(index < ROBJECT_NUMIV(obj))) { + val = ROBJECT_IVPTR(obj)[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; + VM_ASSERT(rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); + } + else if (FL_TEST_RAW(obj, FL_EXIVAR)) { + val = rb_ivar_generic_lookup_with_index(obj, id, index); + } - if (is_attr) { - cached_id = vm_cc_attr_shape_id(cc); + goto ret; } else { - cached_id = vm_ic_attr_shape_id(ic); - } + struct rb_iv_index_tbl_entry *ent; - attr_index_t index; + if (BUILTIN_TYPE(obj) == T_OBJECT) { + struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (LIKELY(cached_id == shape_id)) { - RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); + if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &ent)) { + fill_ivar_cache(iseq, ic, cc, is_attr, ent); - 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; + // get value + if (ent->index < ROBJECT_NUMIV(obj)) { + val = ROBJECT_IVPTR(obj)[ent->index]; + + VM_ASSERT(rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); + } + } } + else if (FL_TEST_RAW(obj, FL_EXIVAR)) { + struct st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - val = ivar_list[index]; - VM_ASSERT(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); + 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); } } else { - 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); - } + // T_CLASS / T_MODULE + goto general_path; } -#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)) { - // 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]; + ret: + if (LIKELY(val != Qundef)) { + return val; } else { - 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; + return Qnil; } - } - - RUBY_ASSERT(val != Qundef); - - return val; - -general_path: + general_path: #endif /* OPT_IC_FOR_IVAR */ RB_DEBUG_COUNTER_INC(ivar_get_ic_miss); @@ -1237,24 +1196,6 @@ 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, rb_shape_t *shape, rb_shape_t *next_shape, 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(shape), SHAPE_ID(next_shape)); - RB_OBJ_WRITTEN(cc, Qundef, (VALUE)shape); - RB_OBJ_WRITTEN(cc, Qundef, (VALUE)next_shape); - } - } - else { - vm_ic_attr_index_set(iseq, ic, index, SHAPE_ID(shape), SHAPE_ID(next_shape)); - RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)shape); - RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)next_shape); - } -} - 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)); @@ -1262,66 +1203,35 @@ 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) { -#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); - rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); - if (shape != next_shape) { - rb_shape_set_shape(obj, next_shape); - } - - 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, shape, next_shape, 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); - } + rb_check_frozen_internal(obj); - VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); - RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); +#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; - return val; + 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)); } - case T_CLASS: - case T_MODULE: - break; - default: - { - rb_shape_t * shape = rb_shape_get_shape(obj); - rb_ivar_set(obj, id, val); - rb_shape_t * next_shape = rb_shape_get_shape(obj); - 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, shape, next_shape, id, iseq, ic, cc, is_attr); - } - else { - rb_bug("didn't find the id\n"); - } + uint32_t index = ent->index; - return val; + if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { + 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; + } } #endif RB_DEBUG_COUNTER_INC(ivar_set_ic_miss); @@ -1340,99 +1250,39 @@ 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 - RB_OBJ_WRITTEN(obj, Qundef, rb_shape_get_shape_by_id(dest_shape_id)); - } - else { - // Just get the IV table - RUBY_ASSERT(GET_VM()->shape_list[dest_shape_id]); - 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, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index) +vm_setivar(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr) { #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) { - 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); - } - else { - RUBY_ASSERT(GET_VM()->shape_list[dest_shape_id]); - } - - RUBY_ASSERT(index < ROBJECT_NUMIV(obj)); + if (LIKELY(RB_TYPE_P(obj, T_OBJECT)) && + LIKELY(!RB_OBJ_FROZEN_RAW(obj))) { - VALUE *ptr = ROBJECT_IVPTR(obj); + VM_ASSERT(!rb_ractor_shareable_p(obj)); - RB_OBJ_WRITE(obj, &ptr[index], val); + 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_DEBUG_COUNTER_INC(ivar_set_ic_hit); - - return val; - } + if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { + rb_init_iv_list(obj); } - break; - case T_CLASS: - case T_MODULE: + 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 { 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 @@ -1527,22 +1377,7 @@ 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)) { - 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); - } + vm_setivar(obj, id, val, iseq, ic, 0, 0); } void @@ -1551,6 +1386,28 @@ 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) { @@ -3243,45 +3100,17 @@ 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; - VALUE ivar = vm_getivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, NULL, NULL, cc, TRUE); - return ivar; + return vm_getivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, NULL, NULL, cc, TRUE); } static VALUE -vm_call_attrset_direct(rb_execution_context_t *ec, rb_control_frame_t *cfp, const struct rb_callcache *cc, VALUE obj) +vm_call_attrset(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) { + const struct rb_callcache *cc = calling->cc; 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); - 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); + return vm_setivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, val, NULL, NULL, cc, 1); } bool @@ -3390,7 +3219,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); @@ -3560,7 +3389,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); } @@ -3586,7 +3415,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); } @@ -3693,7 +3522,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)) { @@ -3873,45 +3702,18 @@ 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); - - 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))); - } + VM_CALL_METHOD_ATTR(v, + vm_call_attrset(ec, cfp, calling), + 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); - if (vm_cc_markable(cc)) { - vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); - } + vm_cc_attr_index_initialize(cc); 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 4b50d888de1f50..c3d4a39a2ba656 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -40,7 +40,6 @@ 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") @@ -82,12 +81,6 @@ 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 42d97b7e80b338..d310e3bf129137 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 (num_bits={})", num_bits); + panic!("immediate value too large"); } }, _ => unreachable!() diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 4018a314fc5754..c246c7b48febd8 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1938,12 +1938,14 @@ fn gen_set_ivar( let val_opnd = ctx.stack_pop(1); let recv_opnd = ctx.stack_pop(1); - // Call rb_vm_set_ivar_id with the receiver, the ivar name, and the value + 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 let val = asm.ccall( - rb_vm_set_ivar_id as *const u8, + rb_vm_set_ivar_idx as *const u8, vec![ recv_opnd, - Opnd::UImm(ivar_name.into()), + Opnd::Imm(ivar_index.into()), val_opnd, ], ); @@ -2021,82 +2023,81 @@ fn gen_get_ivar( return EndBlock; } - 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); - } + // 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(); // Pop receiver if it's on the temp stack if recv_opnd != SelfOpnd { ctx.stack_pop(1); } - // Guard heap object - if !recv_type.is_heap() { - guard_object_is_heap(asm, recv, side_exit); + 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()); } // Compile time self is embedded and the ivar index lands within the object - let embed_test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) }; - - 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 { + 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 + // 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, + ); + // Load the variable - let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index.unwrap() * SIZEOF_VALUE) as i32; + let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index * 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, ivar_opnd); + asm.mov(out_opnd, out_val); } 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.unwrap() as u64)); + asm.cmp(num_slots, Opnd::UImm(ivar_index as u64)); asm.jbe(counted_exit!(ocb, side_exit, getivar_idx_out_of_range).into()); } @@ -2104,10 +2105,15 @@ 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.unwrap()) as i32); + 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()); + // Push the ivar on the stack let out_opnd = ctx.stack_push(Type::Unknown); - asm.mov(out_opnd, ivar_opnd); + asm.mov(out_opnd, out_val); } // Jump to next instruction. This allows guard chains to share the same successor. @@ -2130,12 +2136,25 @@ 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 65f398f075e778..25149ab7304d45 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_id(obj: VALUE, idx: u32, val: VALUE) -> VALUE; + pub fn rb_vm_set_ivar_idx(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,26 +354,18 @@ 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(); - return flags_bits; + (flags_bits & (RUBY_T_MASK as usize)) as ruby_value_type } 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 84a778cbc530d4..f58bf1ca05879b 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -269,30 +269,6 @@ 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 flags: VALUE, - 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; @@ -537,7 +513,6 @@ pub const imemo_parser_strterm: imemo_type = 10; pub const imemo_callinfo: imemo_type = 11; pub const imemo_callcache: imemo_type = 12; pub const imemo_constcache: imemo_type = 13; -pub const imemo_shape: imemo_type = 14; pub type imemo_type = u32; pub const METHOD_VISI_UNDEF: rb_method_visibility_t = 0; pub const METHOD_VISI_PUBLIC: rb_method_visibility_t = 1; @@ -597,11 +572,6 @@ 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; } @@ -630,10 +600,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 entry: *mut rb_iv_index_tbl_entry, } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -729,6 +698,12 @@ 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 95d5b33ea0c99aae76ad148350cd353379a827c8 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 27 Sep 2022 12:44:30 +0900 Subject: [PATCH 253/269] syntax_suggest moved under the ruby organization from zombocom --- lib/syntax_suggest/capture_code_context.rb | 2 +- lib/syntax_suggest/pathname_from_message.rb | 2 +- lib/syntax_suggest/syntax_suggest.gemspec | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/syntax_suggest/capture_code_context.rb b/lib/syntax_suggest/capture_code_context.rb index c74a366a258d3f..6439af4ddeb8e8 100644 --- a/lib/syntax_suggest/capture_code_context.rb +++ b/lib/syntax_suggest/capture_code_context.rb @@ -137,7 +137,7 @@ def capture_before_after_kws(block) # puts "woof" # 3 # end # 4 # - # However due to https://github.com/zombocom/syntax_suggest/issues/32 + # However due to https://github.com/ruby/syntax_suggest/issues/32 # the problem line will be identified as: # # ❯ class Dog # 1 diff --git a/lib/syntax_suggest/pathname_from_message.rb b/lib/syntax_suggest/pathname_from_message.rb index ea1a90856e90ed..b6fe1617be0afa 100644 --- a/lib/syntax_suggest/pathname_from_message.rb +++ b/lib/syntax_suggest/pathname_from_message.rb @@ -4,7 +4,7 @@ module SyntaxSuggest # Converts a SyntaxError message to a path # # Handles the case where the filename has a colon in it - # such as on a windows file system: https://github.com/zombocom/syntax_suggest/issues/111 + # such as on a windows file system: https://github.com/ruby/syntax_suggest/issues/111 # # Example: # diff --git a/lib/syntax_suggest/syntax_suggest.gemspec b/lib/syntax_suggest/syntax_suggest.gemspec index acf9be7710150e..73b25c6a5faa39 100644 --- a/lib/syntax_suggest/syntax_suggest.gemspec +++ b/lib/syntax_suggest/syntax_suggest.gemspec @@ -14,12 +14,12 @@ Gem::Specification.new do |spec| spec.summary = "Find syntax errors in your source in a snap" spec.description = 'When you get an "unexpected end" in your syntax this gem helps you find it' - spec.homepage = "https://github.com/zombocom/syntax_suggest.git" + spec.homepage = "https://github.com/ruby/syntax_suggest.git" spec.license = "MIT" spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0") spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = "https://github.com/zombocom/syntax_suggest.git" + spec.metadata["source_code_uri"] = "https://github.com/ruby/syntax_suggest.git" # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. From c35e924f64f25343127ed209f949762eb0dcb58f Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Tue, 27 Sep 2022 08:50:53 -0500 Subject: [PATCH 254/269] [DOC] More on IO streams (#6445) Text is reorganized so that most of the previous text is now in these newly-created sections: Basic IO Line IO New text is added to form new sections: Character IO Byte IO Codepoint IO This gives the page a functional orientation, so that a reader can quickly find pertinent sections. The page retains its original mission: to provide good link targets for the doc for related classes. --- doc/io_streams.rdoc | 243 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 197 insertions(+), 46 deletions(-) diff --git a/doc/io_streams.rdoc b/doc/io_streams.rdoc index aab1b21b9c2950..aadc1afed0abd5 100644 --- a/doc/io_streams.rdoc +++ b/doc/io_streams.rdoc @@ -1,5 +1,29 @@ == \IO Streams +This page describes: + +- {Stream classes}[rdoc-ref:io_streams.rdoc@Stream+Classes]. +- {Pre-existing streams}[rdoc-ref:io_streams.rdoc@Pre-Existing+Streams]. +- {User-created streams}[rdoc-ref:io_streams.rdoc@User-Created+Streams]. +- {Basic \IO}[rdoc-ref:io_streams.rdoc@Basic+IO], including: + + - {Position}[rdoc-ref:io_streams.rdoc@Position]. + - {Open and closed streams}[rdoc-ref:io_streams.rdoc@Open+and+Closed+Streams]. + - {End-of-stream}[rdoc-ref:io_streams.rdoc@End-of-Stream]. + +- {Line \IO}[rdoc-ref:io_streams.rdoc@Line+IO], including: + + - {Line separator}[rdoc-ref:io_streams.rdoc@Line+Separator]. + - {Line limit}[rdoc-ref:io_streams.rdoc@Line+Limit]. + - {Line number}[rdoc-ref:io_streams.rdoc@Line+Number]. + - {Line options}[rdoc-ref:io_streams.rdoc@Line+Options]. + +- {Character \IO}[rdoc-ref:io_streams.rdoc@Character+IO]. +- {Byte \IO}[rdoc-ref:io_streams.rdoc@Byte+IO]. +- {Codepoint \IO}[rdoc-ref:io_streams.rdoc@Codepoint+IO]. + +=== Stream Classes + Ruby supports processing data as \IO streams; that is, as data that may be read, re-read, written, re-written, and traversed via iteration. @@ -10,14 +34,22 @@ Core classes with such support include: - {StringIO}[rdoc-ref:StringIO]: for processing a string. - {ARGF}[rdoc-ref:ARGF]: for processing files cited on the command line. -Pre-existing stream objects that are referenced by constants include: +Except as noted, the instance methods described on this page +are available in classes \ARGF, \File, \IO, and \StringIO. +A few, also noted, are available in class \Kernel. + +=== Pre-Existing Streams + +Pre-existing streams that are referenced by constants include: - $stdin: read-only instance of \IO. - $stdout: write-only instance of \IO. - $stderr: read-only instance of \IO. - \ARGF: read-only instance of \ARGF. -You can create stream objects: +=== User-Created Streams + +You can create streams: - \File: @@ -44,33 +76,34 @@ You can create stream objects: Many examples here use these variables: - # English text with newlines. - text = <<~EOT - First line - Second line + :include: doc/examples/files.rdoc - Fourth line - Fifth line - EOT +=== Basic \IO - # Russian text. - russian = "\u{442 435 441 442}" # => "тест" +You can perform basic stream \IO with these methods: - # Binary data. - data = "\u9990\u9991\u9992\u9993\u9994" +- IO#read: Returns all remaining or the next _n_ bytes read from the stream, + for a given _n_: - # Text file. - File.write('t.txt', text) + f = File.new('t.txt') + f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n" + f.rewind + f.read(30) # => "First line\r\nSecond line\r\n\r\nFou" + f.read(30) # => "rth line\r\nFifth line\r\n" + f.read(30) # => nil + f.close - # File with Russian text. - File.write('t.rus', russian) +- IO#write: Writes one or more given strings to the stream: - # File with binary data. - f = File.new('t.dat', 'wb:UTF-16') - f.write(data) - f.close + $stdout.write('Hello', ', ', 'World!', "\n") # => 14 + $stdout.write('foo', :bar, 2, "\n") -=== Position + Output: + + Hello, World! + foobar2 + +==== Position An \IO stream has a nonnegative integer _position_, which is the byte offset at which the next read or write is to occur; @@ -141,16 +174,44 @@ the relevant methods: f.tell # => 0 f.close -=== Lines +==== Open and Closed Streams + +A new \IO stream may be open for reading, open for writing, or both. + +You can close a stream using these methods: + +- IO#close: Closes the stream for both reading and writing. +- IO#close_read (not in \ARGF): Closes the stream for reading. +- IO#close_write (not in \ARGF): Closes the stream for writing. + +You can query whether a stream is closed using this method: + +- IO#closed?: Returns whether the stream is closed. + +==== End-of-Stream + +You can query whether a stream is positioned at its end using +method IO#eof? (also aliased as +#eof+). + +You can reposition to end-of-stream by reading all stream content: + + f = File.new('t.txt') + f.eof? # => false + f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n" + f.eof? # => true + +Or by using method IO#seek: + + f = File.new('t.txt') + f.eof? # => false + f.seek(0, :END) + f.eof? # => true -Some reader methods in \IO streams are line-oriented; -such a method reads one or more lines, -which are separated by an implicit or explicit line separator. +=== Line \IO -These methods are included (except as noted) in classes Kernel, IO, File, -and {ARGF}[rdoc-ref:ARGF]: +You can read an \IO stream line-by-line using these methods: -- IO#each_line: Passes each line to the block; not in Kernel: +- IO#each_line: Passes each line to the block: f = File.new('t.txt') f.each_line {|line| p line } @@ -174,7 +235,7 @@ and {ARGF}[rdoc-ref:ARGF]: "rth line\n" "Fifth line\n" -- IO#gets: Returns the next line (which may begin mid-line): +- IO#gets (also in Kernel): Returns the next line (which may begin mid-line): f = File.new('t.txt') f.gets # => "First line\n" @@ -184,10 +245,10 @@ and {ARGF}[rdoc-ref:ARGF]: f.readlines # => ["Fifth line\n"] f.gets # => nil -- IO#readline: Like #gets, but raises an exception at end-of-file; - not in StringIO. +- IO#readline (also in Kernel; not in StringIO): + Like #gets, but raises an exception at end-of-stream. -- IO#readlines: Returns all remaining lines in an array; +- IO#readlines (also in Kernel): Returns all remaining lines in an array; may begin mid-line: f = File.new('t.txt') @@ -195,12 +256,21 @@ and {ARGF}[rdoc-ref:ARGF]: f.readlines # => ["ine\n", "\n", "Fourth line\n", "Fifth line\n"] f.readlines # => [] -Each of these methods may be called with: +Each of these reader methods may be called with: - An optional line separator, +sep+. - An optional line-size limit, +limit+. - Both +sep+ and +limit+. +You can write to an \IO stream line-by-line using this method: + +- IO#puts (also in Kernel; not in \StringIO): Writes objects to the stream: + + f = File.new('t.tmp', 'w') + f.puts('foo', :bar, 1, 2.0, Complex(3, 0)) + f.flush + File.read('t.tmp') # => "foo\nbar\n1\n2.0\n3+0i\n" + ==== Line Separator The default line separator is the given by the global variable $/, @@ -326,23 +396,104 @@ that determine how lines in a stream are to be treated: - +:chomp+: If +true+, line separators are omitted; default is +false+. -=== Open and Closed \IO Streams +=== Character \IO -A new \IO stream may be open for reading, open for writing, or both. +You can process an \IO stream character-by-character using these methods: -You can close a stream using these methods: +- IO#getc: Reads and returns the next character from the stream: -- IO#close: Closes the stream for both reading and writing. -- IO#close_read (not available in \ARGF): Closes the stream for reading. -- IO#close_write (not available in \ARGF): Closes the stream for writing. + f = File.new('t.rus') + f.getc # => "т" + f.getc # => "е" + f.getc # => "с" + f.getc # => "т" + f.getc # => nil -You can query whether a stream is closed using these methods: +- IO#readchar (not in \StringIO): + Like #getc, but raises an exception at end-of-stream: -- IO#closed?: Returns whether the stream is closed. + f.readchar # Raises EOFError. + +- IO#ungetc (not in \ARGF): + Pushes back ("unshifts") a character or integer onto the stream: + + f = File.new('t.tmp', 'w') + f.putc("т") + f.putc("т") + f.close + File.read('t.tmp') # => "тт" + +- IO#putc (also in Kernel): Writes a character to the stream: + + c = File.new('t.rus').getc # => "т" + f = File.new('t.tmp', 'w') + f.putc(c) + f.putc(c) + f.close + File.read('t.tmp') # => "тт" + +- IO#each_char: Reads each remaining character in the stream, + passing the character to the given block: + + f = File.new('t.rus') + f.pos = 4 + f.each_char {|c| p c } + + Output: + + "с" + "т" + +=== Byte \IO + +You can process an \IO stream byte-by-byte using these methods: + +- IO#getbyte: Returns the next 8-bit byte as an integer in range 0..255: + + File.read('t.dat') + # => "\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94" + File.read('t.dat') + # => "\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94" + f = File.new('t.dat') + f.getbyte # => 254 + f.getbyte # => 255 + f.seek(-2, :END) + f.getbyte # => 153 + f.getbyte # => 148 + f.getbyte # => nil + +- IO#readbyte (not in \StringIO): + Like #getbyte, but raises an exception if at end-of-stream: + + f.readbyte # Raises EOFError. -=== Stream End-of-File +- IO#ungetbyte (not in \ARGF): + Pushes back ("unshifts") a byte back onto the stream: -You can query whether a stream is at end-of-file using this method: + f.ungetbyte(0) + f.ungetbyte(01) + f.read # => "\u0001\u0000" -- IO#eof? (also aliased as +#eof+): - Returns whether the stream is at end-of-file. +- IO#each_byte: Reads each remaining byte in the stream, + passing the byte to the given block: + + f.seek(-4, :END) + f.each_byte {|b| p b } + + Output: + + 153 + 147 + 153 + 148 + +=== Codepoint \IO + +You can process an \IO stream codepoint-by-codepoint using method ++#each_codepoint+: + + f = File.new('t.rus') + a = [] + f.each_codepoint {|c| a << c } + a # => [1090, 1077, 1089, 1090] + f.close From 1a06bc94d96ab4be4386b0ccd802d11380fa4e84 Mon Sep 17 00:00:00 2001 From: Chad Wilson Date: Tue, 27 Sep 2022 22:48:16 +0800 Subject: [PATCH 255/269] [ruby/psych] Bump snakeyaml from 1.31 to 1.33 https://github.com/ruby/psych/commit/8a761cdfb7 --- ext/psych/lib/psych/versions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb index f39d30ce5a1daa..a9585c887f65a6 100644 --- a/ext/psych/lib/psych/versions.rb +++ b/ext/psych/lib/psych/versions.rb @@ -5,6 +5,6 @@ module Psych VERSION = '5.0.0.dev' if RUBY_ENGINE == 'jruby' - DEFAULT_SNAKEYAML_VERSION = '1.31'.freeze + DEFAULT_SNAKEYAML_VERSION = '1.33'.freeze end end From 9d56d9975d867c94ab2a6d76e4482112ab6c3319 Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Sun, 25 Sep 2022 11:14:11 +0200 Subject: [PATCH 256/269] [ruby/timeout] Explicit add the timeout thread to default ThreadGroup Otherwise the timeout thread would be added to the ThreadGroup of the thread that makes the first call to Timeout.timeout . Fixes bug 19020: https://bugs.ruby-lang.org/issues/19020 Add a test case to make sure the common thread doesn't leak to another ThreadGroup https://github.com/ruby/timeout/commit/c4f1385c9a --- lib/timeout.rb | 1 + test/test_timeout.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/timeout.rb b/lib/timeout.rb index 2aad1d746543be..badba9a39797b6 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -120,6 +120,7 @@ def self.create_timeout_thread requests.reject!(&:done?) end end + ThreadGroup::Default.add(watcher) watcher.name = "Timeout stdlib thread" watcher.thread_variable_set(:"\0__detached_thread__", true) watcher diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 76de38949def1f..2d3dd162457759 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -159,4 +159,17 @@ def test_fork assert_equal 'timeout', r.read r.close end + + def test_threadgroup + assert_separately(%w[-rtimeout], <<-'end;') + tg = ThreadGroup.new + thr = Thread.new do + tg.add(Thread.current) + Timeout.timeout(10){} + end + thr.join + assert_equal [].to_s, tg.list.to_s + end; + end + end From bcd30fb96192993b629f24a5e551d22067220a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= Date: Fri, 16 Sep 2022 14:06:54 +0200 Subject: [PATCH 257/269] Re-enable TZ test missed due to merge conflict. This was disabled by b7577b4d9e, while properly fixed upstream by: https://github.com/ruby/spec/pull/939 --- spec/ruby/core/time/shared/local.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/ruby/core/time/shared/local.rb b/spec/ruby/core/time/shared/local.rb index 2dba23dbd7ea23..c4aa7a7ea9b4d2 100644 --- a/spec/ruby/core/time/shared/local.rb +++ b/spec/ruby/core/time/shared/local.rb @@ -6,7 +6,6 @@ end end -=begin platform_is_not :windows do describe "timezone changes" do it "correctly adjusts the timezone change to 'CET' on 'Europe/Amsterdam'" do @@ -17,7 +16,6 @@ end end end -=end end describe :time_local_10_arg, shared: true do From 5d4048e0bc84829ffcd6ff559515c198773476a2 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Tue, 27 Sep 2022 13:58:28 -0500 Subject: [PATCH 258/269] [DOC] More on IO streams (#6454) Adds remarks about .new and .open. Uses ..open where convenient (not convenient where output would be in a block). Fixed examples for #ungetc. --- doc/io_streams.rdoc | 66 ++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/doc/io_streams.rdoc b/doc/io_streams.rdoc index aadc1afed0abd5..96285e4f360bba 100644 --- a/doc/io_streams.rdoc +++ b/doc/io_streams.rdoc @@ -53,22 +53,29 @@ You can create streams: - \File: - - File.new: returns a new \File object. - - File.open: passes a new \File object to given the block. + - File.new: returns a new \File object; + the file should be closed when no longer needed. + - File.open: passes a new \File object to given the block; + the file is automatically closed on block exit. - \IO: - - IO.new: returns a new \IO object for the given integer file descriptor. - - IO.open: passes a new \IO object to the given block. + - IO.new: returns a new \IO object for the given integer file descriptor; + the \IO object should be closed when no longer needed. + - IO.open: passes a new \IO object to the given block; + the \IO object is automatically closed on block exit. - IO.popen: returns a new \IO object that is connected to the $stdin and $stdout of a newly-launched subprocess. - Kernel#open: returns a new \IO object connected to a given source: - stream, file, or subprocess. + stream, file, or subprocess; + the \IO object should be closed when no longer needed. - \StringIO: - - StringIO.new: returns a new \StringIO object. - - StringIO.open: passes a new \StringIO object to the given block. + - StringIO.new: returns a new \StringIO object; + the \StringIO object should be closed when no longer needed. + - StringIO.open: passes a new \StringIO object to the given block; + the \StringIO object is automatically closed on block exit. (You cannot create an \ARGF object, but one already exists.) @@ -375,11 +382,11 @@ Reading lines from a stream usually changes its line number: Iterating over lines in a stream usually changes its line number: - f = File.new('t.txt') - f.each_line do |line| - p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}" + File.open('t.txt') do |f| + f.each_line do |line| + p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}" + end end - f.close Output: @@ -417,27 +424,30 @@ You can process an \IO stream character-by-character using these methods: - IO#ungetc (not in \ARGF): Pushes back ("unshifts") a character or integer onto the stream: - f = File.new('t.tmp', 'w') - f.putc("т") - f.putc("т") - f.close - File.read('t.tmp') # => "тт" + path = 't.tmp' + File.write(path, 'foo') + File.open(path) do |f| + f.ungetc('т') + f.read # => "тfoo" + end - IO#putc (also in Kernel): Writes a character to the stream: - c = File.new('t.rus').getc # => "т" - f = File.new('t.tmp', 'w') - f.putc(c) - f.putc(c) - f.close - File.read('t.tmp') # => "тт" + File.open('t.tmp', 'w') do |f| + f.putc('т') + f.putc('е') + f.putc('с') + f.putc('т') + end + File.read('t.tmp') # => "тест" - IO#each_char: Reads each remaining character in the stream, passing the character to the given block: - f = File.new('t.rus') - f.pos = 4 - f.each_char {|c| p c } + File.open('t.rus') do |f| + f.pos = 4 + f.each_char {|c| p c } + end Output: @@ -492,8 +502,8 @@ You can process an \IO stream byte-by-byte using these methods: You can process an \IO stream codepoint-by-codepoint using method +#each_codepoint+: - f = File.new('t.rus') a = [] - f.each_codepoint {|c| a << c } + File.open('t.rus') do |f| + f.each_codepoint {|c| a << c } + end a # => [1090, 1077, 1089, 1090] - f.close From 8f7f12ad64c2f01e1fc8a75402337ceeb6607657 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 19 Sep 2022 15:39:32 -0400 Subject: [PATCH 259/269] [rubygems/rubygems] fix: Gem::Platform.match handles String argument properly Previously 9eead86 introduced non-commutativity of platforms, and later commit 1b9f7f50 changed the behavior of `Gem::Platform.match` to ensure the callee of `#=~` was the gem platform. However, when the platform argument is a String, then the callee and argument of `#=~` are flipped (see docs for `String#=~`), which works against the fix from 1b9f7f50. Closes #5938 https://github.com/rubygems/rubygems/commit/3b1fb562e8 --- lib/rubygems/platform.rb | 1 + test/rubygems/test_gem_platform.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index 06de5ded8da6c9..1dacc596c47be4 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -22,6 +22,7 @@ def self.match(platform) end def self.match_platforms?(platform, platforms) + platform = Gem::Platform.new(platform) unless platform.is_a?(Gem::Platform) platforms.any? do |local_platform| platform.nil? || local_platform == platform || diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index f683204a2fc209..3bd4a862c524a0 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -452,6 +452,13 @@ def test_inspect assert_equal 1, result.scan(/@version=/).size end + def test_gem_platform_match_with_string_argument + util_set_arch "x86_64-linux-musl" + + assert(Gem::Platform.match(Gem::Platform.new("x86_64-linux")), "should match Gem::Platform") + assert(Gem::Platform.match("x86_64-linux"), "should match String platform") + end + def assert_local_match(name) assert_match Gem::Platform.local, name end From 28433e9aa0c765c9d20bc6397439a1b12e66bcbd Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 27 Sep 2022 16:58:01 -0400 Subject: [PATCH 260/269] Change IncrCounter lowering on AArch64 (#6455) * Change IncrCounter lowering on AArch64 Previously we were using LDADDAL which is not available on Graviton 1 chips. Instead, we're going to use an exclusive load/store group through the LDAXR/STLXR instructions. * Update yjit/src/backend/arm64/mod.rs Co-authored-by: Maxime Chevalier-Boisvert --- .../asm/arm64/inst/load_store_exclusive.rs | 109 ++++++++++++++++++ yjit/src/asm/arm64/inst/mod.rs | 2 + yjit/src/asm/arm64/mod.rs | 39 +++++++ yjit/src/asm/arm64/opnd.rs | 10 ++ yjit/src/backend/arm64/mod.rs | 32 +++-- 5 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 yjit/src/asm/arm64/inst/load_store_exclusive.rs diff --git a/yjit/src/asm/arm64/inst/load_store_exclusive.rs b/yjit/src/asm/arm64/inst/load_store_exclusive.rs new file mode 100644 index 00000000000000..8216c2200af505 --- /dev/null +++ b/yjit/src/asm/arm64/inst/load_store_exclusive.rs @@ -0,0 +1,109 @@ +/// The operation being performed for this instruction. +enum Op { + Store = 0, + Load = 1 +} + +/// The size of the registers being operated on. +enum Size { + Size32 = 0b10, + Size64 = 0b11 +} + +/// A convenience function so that we can convert the number of bits of an +/// register operand directly into a Size enum variant. +impl From for Size { + fn from(num_bits: u8) -> Self { + match num_bits { + 64 => Size::Size64, + 32 => Size::Size32, + _ => panic!("Invalid number of bits: {}", num_bits) + } + } +} + +/// The struct that represents an A64 load or store exclusive instruction that +/// can be encoded. +/// +/// LDAXR/STLXR +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 | +/// | 1 0 0 1 0 0 0 0 0 1 1 1 1 1 1 | +/// | size. op rs.............. rn.............. rt.............. | +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// +pub struct LoadStoreExclusive { + /// The number of the register to be loaded. + rt: u8, + + /// The base register with which to form the address. + rn: u8, + + /// The register to be used for the status result if it applies to this + /// operation. Otherwise it's the zero register. + rs: u8, + + /// The operation being performed for this instruction. + op: Op, + + /// The size of the registers being operated on. + size: Size +} + +impl LoadStoreExclusive { + /// LDAXR + /// https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/LDAXR--Load-Acquire-Exclusive-Register- + pub fn ldaxr(rt: u8, rn: u8, num_bits: u8) -> Self { + Self { rt, rn, rs: 31, op: Op::Load, size: num_bits.into() } + } + + /// STLXR + /// https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/STLXR--Store-Release-Exclusive-Register- + pub fn stlxr(rs: u8, rt: u8, rn: u8, num_bits: u8) -> Self { + Self { rt, rn, rs, op: Op::Store, size: num_bits.into() } + } +} + +/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Loads-and-Stores?lang=en +const FAMILY: u32 = 0b0100; + +impl From for u32 { + /// Convert an instruction into a 32-bit value. + fn from(inst: LoadStoreExclusive) -> Self { + 0 + | ((inst.size as u32) << 30) + | (FAMILY << 25) + | ((inst.op as u32) << 22) + | ((inst.rs as u32) << 16) + | (0b111111 << 10) + | ((inst.rn as u32) << 5) + | (inst.rt as u32) + } +} + +impl From for [u8; 4] { + /// Convert an instruction into a 4 byte array. + fn from(inst: LoadStoreExclusive) -> [u8; 4] { + let result: u32 = inst.into(); + result.to_le_bytes() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ldaxr() { + let inst = LoadStoreExclusive::ldaxr(16, 0, 64); + let result: u32 = inst.into(); + assert_eq!(0xc85ffc10, result); + } + + #[test] + fn test_stlxr() { + let inst = LoadStoreExclusive::stlxr(17, 16, 0, 64); + let result: u32 = inst.into(); + assert_eq!(0xc811fc10, result); + } +} diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs index b3a77e73c98eb7..9821e6a334fd7c 100644 --- a/yjit/src/asm/arm64/inst/mod.rs +++ b/yjit/src/asm/arm64/inst/mod.rs @@ -13,6 +13,7 @@ mod halfword_imm; mod load_literal; mod load_register; mod load_store; +mod load_store_exclusive; mod logical_imm; mod logical_reg; mod mov; @@ -36,6 +37,7 @@ pub use halfword_imm::HalfwordImm; pub use load_literal::LoadLiteral; pub use load_register::LoadRegister; pub use load_store::LoadStore; +pub use load_store_exclusive::LoadStoreExclusive; pub use logical_imm::LogicalImm; pub use logical_reg::LogicalReg; pub use mov::Mov; diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs index d97452a0457538..88431ce30a9c41 100644 --- a/yjit/src/asm/arm64/mod.rs +++ b/yjit/src/asm/arm64/mod.rs @@ -331,6 +331,20 @@ pub fn ldaddal(cb: &mut CodeBlock, rs: A64Opnd, rt: A64Opnd, rn: A64Opnd) { cb.write_bytes(&bytes); } +/// LDAXR - atomic load with acquire semantics +pub fn ldaxr(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rt, rn) { + (A64Opnd::Reg(rt), A64Opnd::Reg(rn)) => { + assert_eq!(rn.num_bits, 64, "rn must be a 64-bit register."); + + LoadStoreExclusive::ldaxr(rt.reg_no, rn.reg_no, rt.num_bits).into() + }, + _ => panic!("Invalid operand combination to ldaxr instruction."), + }; + + cb.write_bytes(&bytes); +} + /// LDP (signed offset) - load a pair of registers from memory pub fn ldp(cb: &mut CodeBlock, rt1: A64Opnd, rt2: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt1, rt2, rn) { @@ -707,6 +721,21 @@ pub fn orr(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { cb.write_bytes(&bytes); } +/// STLXR - store a value to memory, release exclusive access +pub fn stlxr(cb: &mut CodeBlock, rs: A64Opnd, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rs, rt, rn) { + (A64Opnd::Reg(rs), A64Opnd::Reg(rt), A64Opnd::Reg(rn)) => { + assert_eq!(rs.num_bits, 32, "rs must be a 32-bit register."); + assert_eq!(rn.num_bits, 64, "rn must be a 64-bit register."); + + LoadStoreExclusive::stlxr(rs.reg_no, rt.reg_no, rn.reg_no, rn.num_bits).into() + }, + _ => panic!("Invalid operand combination to stlxr instruction.") + }; + + cb.write_bytes(&bytes); +} + /// STP (signed offset) - store a pair of registers to memory pub fn stp(cb: &mut CodeBlock, rt1: A64Opnd, rt2: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt1, rt2, rn) { @@ -1183,6 +1212,11 @@ mod tests { check_bytes("8b01eaf8", |cb| ldaddal(cb, X10, X11, X12)); } + #[test] + fn test_ldaxr() { + check_bytes("6afd5fc8", |cb| ldaxr(cb, X10, X11)); + } + #[test] fn test_ldp() { check_bytes("8a2d4da9", |cb| ldp(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); @@ -1333,6 +1367,11 @@ mod tests { check_bytes("80025fd6", |cb| ret(cb, X20)); } + #[test] + fn test_stlxr() { + check_bytes("8bfd0ac8", |cb| stlxr(cb, W10, X11, X12)); + } + #[test] fn test_stp() { check_bytes("8a2d0da9", |cb| stp(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); diff --git a/yjit/src/asm/arm64/opnd.rs b/yjit/src/asm/arm64/opnd.rs index 52b2a84637f166..0dc614ab4e08ce 100644 --- a/yjit/src/asm/arm64/opnd.rs +++ b/yjit/src/asm/arm64/opnd.rs @@ -84,6 +84,14 @@ impl A64Opnd { _ => false } } + + /// Unwrap a register from an operand. + pub fn unwrap_reg(&self) -> A64Reg { + match self { + A64Opnd::Reg(reg) => *reg, + _ => panic!("Expected register operand") + } + } } // argument registers @@ -102,6 +110,8 @@ pub const X12_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 12 }; pub const X13_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 13 }; pub const X14_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 14 }; pub const X15_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 15 }; +pub const X16_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 16 }; +pub const X17_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 17 }; // callee-save registers pub const X19_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 19 }; diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 0a5068be58a736..79dff530d16fd2 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -70,7 +70,8 @@ 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(X15_REG); + const SCRATCH0: A64Opnd = A64Opnd::Reg(X16_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 @@ -373,17 +374,12 @@ impl Assembler asm.csel_ge(opnd0, opnd1); }, Insn::IncrCounter { mem, value } => { - // We'll use LDADD later which only works with registers - // ... Load pointer into register - let counter_addr = split_lea_operand(asm, mem); - - // Load immediates into a register - let addend = match value { - opnd @ Opnd::Imm(_) | opnd @ Opnd::UImm(_) => asm.load(opnd), - opnd => opnd, + let counter_addr = match mem { + Opnd::Mem(_) => split_lea_operand(asm, mem), + _ => mem }; - asm.incr_counter(counter_addr, addend); + asm.incr_counter(counter_addr, value); }, Insn::JmpOpnd(opnd) => { if let Opnd::Mem(_) = opnd { @@ -936,7 +932,21 @@ impl Assembler emit_conditional_jump::<{Condition::VS}>(cb, *target); }, Insn::IncrCounter { mem, value } => { - ldaddal(cb, value.into(), value.into(), mem.into()); + let label = cb.new_label("incr_counter_loop".to_string()); + cb.write_label(label); + + ldaxr(cb, Self::SCRATCH0, mem.into()); + add(cb, Self::SCRATCH0, Self::SCRATCH0, value.into()); + + // The status register that gets used to track whether or + // not the store was successful must be 32 bytes. Since we + // store the SCRATCH registers as their 64-bit versions, we + // need to rewrap it here. + let status = A64Opnd::Reg(Self::SCRATCH1.unwrap_reg().with_num_bits(32)); + stlxr(cb, status, Self::SCRATCH0, mem.into()); + + cmp(cb, Self::SCRATCH1, A64Opnd::new_uimm(0)); + emit_conditional_jump::<{Condition::NE}>(cb, Target::Label(label)); }, Insn::Breakpoint => { brk(cb, A64Opnd::None); From 8fcbb79742b27619683f0a5f25497a402eeca56f Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert Date: Tue, 27 Sep 2022 19:27:19 -0400 Subject: [PATCH 261/269] YJIT: reverse configure.ac changes that disable `--yjit-stats` on Graviton1 (#6457) Reverse configure.ac changes that disable YJIT stats on Graviton1 --- configure.ac | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/configure.ac b/configure.ac index 206470ae0f5936..a1a1ea8eb8b9ec 100644 --- a/configure.ac +++ b/configure.ac @@ -3748,44 +3748,23 @@ AS_CASE(["${YJIT_SUPPORT}"], ], [dev], [ rb_rust_target_subdir=debug - CARGO_BUILD_ARGS='--features disasm,asm_comments' + CARGO_BUILD_ARGS='--features stats,disasm,asm_comments' AC_DEFINE(RUBY_DEBUG, 1) ], [dev_nodebug], [ rb_rust_target_subdir=dev_nodebug - CARGO_BUILD_ARGS='--profile dev_nodebug --features disasm,asm_comments' + CARGO_BUILD_ARGS='--profile dev_nodebug --features stats,disasm,asm_comments' ], [stats], [ rb_rust_target_subdir=stats - CARGO_BUILD_ARGS='--profile stats' + CARGO_BUILD_ARGS='--profile stats --features stats' ]) AS_IF([test -n "${CARGO_BUILD_ARGS}"], [ - AC_CHECK_TOOL(CARGO, [cargo], [no]) - AS_IF([test x"$CARGO" = "xno"], - AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install]) - ]) - - # Insn::IncrCounter uses ldaddal, which works only on ARMv8.1+. - AC_CACHE_CHECK(yjit stats are broken, rb_cv_broken_yjit_stats, [ - AC_RUN_IFELSE( - [AC_LANG_PROGRAM([[]], [[ - @%:@ifdef __aarch64__ - asm volatile(".arch armv8-a+lse\n" - "ldaddal xzr, xzr, @<:@sp@:>@"); - @%:@endif - ]])], - [rb_cv_broken_yjit_stats=no], - [rb_cv_broken_yjit_stats=yes], - [rb_cv_broken_yjit_stats=yes] - ) - ]) - # This won't enable stats in release builds because we don't use cargo - # for release builds, use rustc directly - AS_IF([test "$rb_cv_broken_yjit_stats" = no], [ - CARGO_BUILD_ARGS="${CARGO_BUILD_ARGS} --features stats" - ]) - ) + AC_CHECK_TOOL(CARGO, [cargo], [no]) + AS_IF([test x"$CARGO" = "xno"], + AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install]) + ])) YJIT_LIBS="yjit/target/${rb_rust_target_subdir}/libyjit.a" AS_CASE(["$target_os"],[openbsd*],[ From a58cbddd57152a3a4712c6b37b6efa3c108847e7 Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert Date: Tue, 27 Sep 2022 19:27:39 -0400 Subject: [PATCH 262/269] YJIT: add assertion wrt label names (#6459) Add assertion wrt label names --- yjit/src/asm/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs index 22b46e6acadc37..f5501a4bc767a7 100644 --- a/yjit/src/asm/mod.rs +++ b/yjit/src/asm/mod.rs @@ -220,6 +220,8 @@ impl CodeBlock { /// Allocate a new label with a given name pub fn new_label(&mut self, name: String) -> usize { + assert!(!name.contains(" "), "use underscores in label names, not spaces"); + // This label doesn't have an address yet self.label_addrs.push(0); self.label_names.push(name); From b8e804e410b9088fc7921fcf8b6a92a4fbb3006a Mon Sep 17 00:00:00 2001 From: Maciej Rzasa Date: Fri, 29 Jul 2022 11:33:23 +0200 Subject: [PATCH 263/269] [ruby/bigdecimal] Document precision=0 and ndigits=0 for converting from Float https://github.com/ruby/bigdecimal/commit/4f0894c6c0 --- ext/bigdecimal/bigdecimal.c | 3 +++ ext/bigdecimal/lib/bigdecimal/util.rb | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 61a8d4d8d5bd9a..1483f327a6de8c 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3500,6 +3500,9 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) * in the value, the result is rounded to that number of digits, * according to the current rounding mode; see BigDecimal.mode. * + * When +ndigits+ is 0, the number of digits to correctly represent a float number + * is determined automatically. + * * Returns +value+ converted to a \BigDecimal, depending on the type of +value+: * * - Integer, Float, Rational, Complex, or BigDecimal: converted directly: diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb index cb645d2a7128e2..ad92f7cfe64eef 100644 --- a/ext/bigdecimal/lib/bigdecimal/util.rb +++ b/ext/bigdecimal/lib/bigdecimal/util.rb @@ -33,12 +33,16 @@ class Float < Numeric # # Returns the value of +float+ as a BigDecimal. # The +precision+ parameter is used to determine the number of - # significant digits for the result (the default is Float::DIG). + # significant digits for the result. When +precision+ is set to +0+, + # the number of digits to represent the float being converted is determined + # automatically. + # The default +precision+ is +0+. # # require 'bigdecimal' # require 'bigdecimal/util' # # 0.5.to_d # => 0.5e0 + # 1.234.to_d # => 0.1234e1 # 1.234.to_d(2) # => 0.12e1 # # See also BigDecimal::new. From b58710e006e71359650240d9de467bd4212435c6 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 28 Sep 2022 17:06:12 +0900 Subject: [PATCH 264/269] dbm and gdbm have been extracted gems. --- test/test_extlibs.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_extlibs.rb b/test/test_extlibs.rb index 958c9ff73ef1cc..1d7f616424701a 100644 --- a/test/test_extlibs.rb +++ b/test/test_extlibs.rb @@ -45,7 +45,6 @@ def windows? check_existence "continuation" check_existence "coverage" check_existence "date" - #check_existence "dbm" # depend on libdbm check_existence "digest" check_existence "digest/bubblebabble" check_existence "digest/md5" @@ -56,7 +55,6 @@ def windows? check_existence "fcntl" check_existence "fiber" check_existence "fiddle" - #check_existence "gdbm" # depend on libgdbm check_existence "io/console" check_existence "io/nonblock" check_existence "io/wait" From cd77e71bbac9616a906f6823a8eba4922821e9ad Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Wed, 24 Aug 2022 11:38:17 -0700 Subject: [PATCH 265/269] [ruby/net-http] Remove ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE This list is out of date. At least OpenBSD since 2013 does not allow one user to read the environment variables of a process run by another user. While we could try to keep the list updated, I think it's a bad idea to not use the user/password from the environment, even if another user on the system could read it. If http_proxy exists in the environment, and other users can read it, it doesn't make it more secure for Ruby to ignore it. You could argue that it encourages poor security practices, but net/http should provide mechanism, not policy. Fixes [Bug #18908] https://github.com/ruby/net-http/commit/1e4585153d --- lib/net/http.rb | 11 ++--------- test/net/http/test_http.rb | 18 ++++-------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index a583441253c092..7e89409c1bb496 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1221,16 +1221,9 @@ def proxy_port end end - # [Bug #12921] - if /linux|freebsd|darwin/ =~ RUBY_PLATFORM - ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = true - else - ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = false - end - # The username of the proxy server, if one is configured. def proxy_user - if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env + if @proxy_from_env user = proxy_uri&.user unescape(user) if user else @@ -1240,7 +1233,7 @@ def proxy_user # The password of the proxy server, if one is configured. def proxy_pass - if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env + if @proxy_from_env pass = proxy_uri&.password unescape(pass) if pass else diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb index e9471273f472c5..0508645ac55b28 100644 --- a/test/net/http/test_http.rb +++ b/test/net/http/test_http.rb @@ -178,13 +178,8 @@ def test_proxy_eh_ENV_with_user http = Net::HTTP.new 'hostname.example' assert_equal true, http.proxy? - if Net::HTTP::ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE - assert_equal 'foo', http.proxy_user - assert_equal 'bar', http.proxy_pass - else - assert_nil http.proxy_user - assert_nil http.proxy_pass - end + assert_equal 'foo', http.proxy_user + assert_equal 'bar', http.proxy_pass end end @@ -195,13 +190,8 @@ def test_proxy_eh_ENV_with_urlencoded_user http = Net::HTTP.new 'hostname.example' assert_equal true, http.proxy? - if Net::HTTP::ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE - assert_equal "Y\\X", http.proxy_user - assert_equal "R%S] ?X", http.proxy_pass - else - assert_nil http.proxy_user - assert_nil http.proxy_pass - end + assert_equal "Y\\X", http.proxy_user + assert_equal "R%S] ?X", http.proxy_pass end end From d12fce7af3af27096b336f43700fffd51158e928 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 27 Sep 2022 22:46:06 +0900 Subject: [PATCH 266/269] [ruby/date] Check time zone offset elements Too big parts of fractional hour time zone offset can cause assertion failures. https://github.com/ruby/date/commit/06bcfb2729 --- ext/date/date_parse.c | 47 +++++++++++++++++++++++++-------- test/date/test_date_strptime.rb | 4 +++ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c index 95274d5baa8e0c..4890401894e12b 100644 --- a/ext/date/date_parse.c +++ b/ext/date/date_parse.c @@ -473,27 +473,53 @@ date_zone_to_diff(VALUE str) s++; l--; +#define out_of_range(v, min, max) ((v) < (min) || (max) < (v)) hour = STRTOUL(s, &p, 10); if (*p == ':') { + if (out_of_range(sec, 0, 59)) return Qnil; s = ++p; min = STRTOUL(s, &p, 10); + if (out_of_range(min, 0, 59)) return Qnil; if (*p == ':') { s = ++p; sec = STRTOUL(s, &p, 10); + if (out_of_range(hour, 0, 23)) return Qnil; } - goto num; } - if (*p == ',' || *p == '.') { - char *e = 0; - p++; - min = STRTOUL(p, &e, 10) * 3600; + else if (*p == ',' || *p == '.') { + /* fractional hour */ + size_t n; + int ov; + /* no over precision for offset; 10**-7 hour = 0.36 + * milliseconds should be enough. */ + const size_t max_digits = 7; /* 36 * 10**7 < 32-bit FIXNUM_MAX */ + + if (out_of_range(hour, 0, 23)) return Qnil; + + n = (s + l) - ++p; + if (n > max_digits) n = max_digits; + sec = ruby_scan_digits(p, n, 10, &n, &ov); + if ((p += n) < s + l && *p >= ('5' + !(sec & 1)) && *p <= '9') { + /* round half to even */ + sec++; + } + sec *= 36; if (sign) { hour = -hour; - min = -min; + sec = -sec; + } + if (n <= 2) { + /* HH.nn or HH.n */ + if (n == 1) sec *= 10; + offset = INT2FIX(sec + hour * 3600); + } + else { + VALUE denom = rb_int_positive_pow(10, (int)(n - 2)); + offset = f_add(rb_rational_new(INT2FIX(sec), denom), INT2FIX(hour * 3600)); + if (rb_rational_den(offset) == INT2FIX(1)) { + offset = rb_rational_num(offset); + } } - offset = rb_rational_new(INT2FIX(min), - rb_int_positive_pow(10, (int)(e - p))); - offset = f_add(INT2FIX(hour * 3600), offset); goto ok; } else if (l > 2) { @@ -506,12 +532,11 @@ date_zone_to_diff(VALUE str) min = ruby_scan_digits(&s[2 - l % 2], 2, 10, &n, &ov); if (l >= 5) sec = ruby_scan_digits(&s[4 - l % 2], 2, 10, &n, &ov); - goto num; } - num: sec += min * 60 + hour * 3600; if (sign) sec = -sec; offset = INT2FIX(sec); +#undef out_of_range } } } diff --git a/test/date/test_date_strptime.rb b/test/date/test_date_strptime.rb index fc42ebf7cda1ef..521bf92916ab8d 100644 --- a/test/date/test_date_strptime.rb +++ b/test/date/test_date_strptime.rb @@ -180,6 +180,10 @@ def test__strptime__3 [['fri1feb034pm+5', '%a%d%b%y%H%p%Z'], [2003,2,1,16,nil,nil,'+5',5*3600,5]], [['E. Australia Standard Time', '%Z'], [nil,nil,nil,nil,nil,nil,'E. Australia Standard Time',10*3600,nil], __LINE__], + + # out of range + [['+0.9999999999999999999999', '%Z'], [nil,nil,nil,nil,nil,nil,'+0.9999999999999999999999',+1*3600,nil], __LINE__], + [['+9999999999999999999999.0', '%Z'], [nil,nil,nil,nil,nil,nil,'+9999999999999999999999.0',nil,nil], __LINE__], ].each do |x, y| h = Date._strptime(*x) a = h.values_at(:year,:mon,:mday,:hour,:min,:sec,:zone,:offset,:wday) From 247d598477ba0c2424d1e42fa4b5af0dfe1ae1d1 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 28 Sep 2022 21:26:25 +0900 Subject: [PATCH 267/269] Install all file trees for lldb [ci skip] It is no longer single lldb_cruby.py only. --- tool/rbinstall.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tool/rbinstall.rb b/tool/rbinstall.rb index c944ef74da898f..e94445114bddf7 100755 --- a/tool/rbinstall.rb +++ b/tool/rbinstall.rb @@ -686,7 +686,13 @@ class << (w = []) RbConfig.expand(File.read(src), conf) } end - install File.join(srcdir, "misc/lldb_cruby.py"), File.join(rubylibdir, "lldb_cruby.py") + Dir.glob(File.join(srcdir, "misc/lldb_*")) do |src| + if File.directory?(src) + install_recursive src, File.join(rubylibdir, File.basename(src)) + else + install src, rubylibdir + end + end install File.join(srcdir, ".gdbinit"), File.join(rubylibdir, "gdbinit") if $debug_symbols { From 6f8d17e43c7b9b3b8bf42df0ef1ca085080b65f0 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Tue, 27 Sep 2022 14:50:40 -0400 Subject: [PATCH 268/269] Make string slices views rather than copies Just like commit 1c16645 for arrays, this commit changes string slices to be a view rather than a copy even if it can be allocated through VWA. --- string.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/string.c b/string.c index b9c567cbe250dd..bdab6fa1438f48 100644 --- a/string.c +++ b/string.c @@ -2802,18 +2802,20 @@ str_subseq(VALUE str, long beg, long len) { VALUE str2; - if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && - SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str))) { - str2 = rb_str_new_shared(str); + const long rstring_embed_capa_max = ((sizeof(struct RString) - offsetof(struct RString, as.embed.ary)) / sizeof(char)) - 1; + + if (!SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str)) || + len <= rstring_embed_capa_max) { + str2 = rb_str_new(RSTRING_PTR(str) + beg, len); + RB_GC_GUARD(str); + } + else { + str2 = str_new_shared(rb_cString, str); RSTRING(str2)->as.heap.ptr += beg; if (RSTRING(str2)->as.heap.len > len) { RSTRING(str2)->as.heap.len = len; } } - else { - str2 = rb_str_new(RSTRING_PTR(str) + beg, len); - RB_GC_GUARD(str); - } return str2; } From 28a572f8bffe54226a7ad3fae814c1c92b5036e6 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Tue, 27 Sep 2022 14:54:09 -0400 Subject: [PATCH 269/269] Fix bug when slicing a string with broken encoding Commit aa2a428 introduced a bug where non-embedded string slices copied the encoding of the original string. If the original string had a broken encoding but the slice has valid encoding, then the slice would be incorrectly marked as broken encoding. --- string.c | 1 + 1 file changed, 1 insertion(+) diff --git a/string.c b/string.c index bdab6fa1438f48..78ac5591cc1359 100644 --- a/string.c +++ b/string.c @@ -2811,6 +2811,7 @@ str_subseq(VALUE str, long beg, long len) } else { str2 = str_new_shared(rb_cString, str); + ENC_CODERANGE_CLEAR(str2); RSTRING(str2)->as.heap.ptr += beg; if (RSTRING(str2)->as.heap.len > len) { RSTRING(str2)->as.heap.len = len;