From 3c16d93cd3a7c4d1362e07070c9ed9826a7272a8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 12 May 2024 23:12:02 +0900 Subject: [PATCH 001/242] Constify encoding type in universal parser Fixed warning about discarding modifiers. ``` ../src/ruby_parser.c:677:48: warning: passing 'rb_encoding *' (aka 'const struct OnigEncodingTypeST *') to parameter of type 'void *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers] 677 | ast = rb_parser_compile(p, gets, ptr, len, enc, input, line); | ^~~ ../src/internal/parse.h:58:128: note: passing argument to parameter 'fname_enc' here 58 | rb_ast_t *rb_parser_compile(rb_parser_t *p, rb_parser_lex_gets_func *gets, const char *fname_ptr, long fname_len, rb_encoding *fname_enc, rb_parser_input_data input, int line); | ^ ``` --- internal/parse.h | 2 +- ruby_parser.c | 98 +++++++++++++++++++++++----------------------- rubyparser.h | 2 +- universal_parser.c | 2 +- 4 files changed, 53 insertions(+), 51 deletions(-) diff --git a/internal/parse.h b/internal/parse.h index a7e8e08912315f..5f52e8a8e3aacf 100644 --- a/internal/parse.h +++ b/internal/parse.h @@ -13,7 +13,7 @@ #include "internal/static_assert.h" #ifdef UNIVERSAL_PARSER -#define rb_encoding void +#define rb_encoding const void #endif struct rb_iseq_struct; /* in vm_core.h */ diff --git a/ruby_parser.c b/ruby_parser.c index f9d4e6d59ce0ac..1dcdfd8e79ecd8 100644 --- a/ruby_parser.c +++ b/ruby_parser.c @@ -32,6 +32,8 @@ #include "vm_core.h" #include "symbol.h" +#define parser_encoding const void + static int is_ascii_string2(VALUE str) { @@ -41,9 +43,9 @@ is_ascii_string2(VALUE str) RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 6, 0) static VALUE syntax_error_append(VALUE exc, VALUE file, int line, int column, - void *enc, const char *fmt, va_list args) + parser_encoding *enc, const char *fmt, va_list args) { - return rb_syntax_error_append(exc, file, line, column, (rb_encoding *)enc, fmt, args); + return rb_syntax_error_append(exc, file, line, column, enc, fmt, args); } static int @@ -59,9 +61,9 @@ dvar_defined(ID id, const void *p) } static int -is_usascii_enc(void *enc) +is_usascii_enc(parser_encoding *enc) { - return rb_is_usascii_enc((rb_encoding *)enc); + return rb_is_usascii_enc(enc); } static int @@ -83,21 +85,21 @@ is_notop_id2(ID id) } static VALUE -enc_str_new(const char *ptr, long len, void *enc) +enc_str_new(const char *ptr, long len, parser_encoding *enc) { - return rb_enc_str_new(ptr, len, (rb_encoding *)enc); + return rb_enc_str_new(ptr, len, enc); } static int -enc_isalnum(OnigCodePoint c, void *enc) +enc_isalnum(OnigCodePoint c, parser_encoding *enc) { - return rb_enc_isalnum(c, (rb_encoding *)enc); + return rb_enc_isalnum(c, enc); } static int -enc_precise_mbclen(const char *p, const char *e, void *enc) +enc_precise_mbclen(const char *p, const char *e, parser_encoding *enc) { - return rb_enc_precise_mbclen(p, e, (rb_encoding *)enc); + return rb_enc_precise_mbclen(p, e, enc); } static int @@ -113,93 +115,93 @@ mbclen_charfound_len(int len) } static const char * -enc_name(void *enc) +enc_name(parser_encoding *enc) { - return rb_enc_name((rb_encoding *)enc); + return rb_enc_name(enc); } static char * -enc_prev_char(const char *s, const char *p, const char *e, void *enc) +enc_prev_char(const char *s, const char *p, const char *e, parser_encoding *enc) { - return rb_enc_prev_char(s, p, e, (rb_encoding *)enc); + return rb_enc_prev_char(s, p, e, enc); } -static void * +static parser_encoding * enc_get(VALUE obj) { - return (void *)rb_enc_get(obj); + return rb_enc_get(obj); } static int -enc_asciicompat(void *enc) +enc_asciicompat(parser_encoding *enc) { - return rb_enc_asciicompat((rb_encoding *)enc); + return rb_enc_asciicompat(enc); } -static void * +static parser_encoding * utf8_encoding(void) { - return (void *)rb_utf8_encoding(); + return rb_utf8_encoding(); } static VALUE -enc_associate(VALUE obj, void *enc) +enc_associate(VALUE obj, parser_encoding *enc) { - return rb_enc_associate(obj, (rb_encoding *)enc); + return rb_enc_associate(obj, enc); } -static void * +static parser_encoding * ascii8bit_encoding(void) { - return (void *)rb_ascii8bit_encoding(); + return rb_ascii8bit_encoding(); } static int -enc_codelen(int c, void *enc) +enc_codelen(int c, parser_encoding *enc) { - return rb_enc_codelen(c, (rb_encoding *)enc); + return rb_enc_codelen(c, enc); } static int -enc_mbcput(unsigned int c, void *buf, void *enc) +enc_mbcput(unsigned int c, void *buf, parser_encoding *enc) { - return rb_enc_mbcput(c, buf, (rb_encoding *)enc); + return rb_enc_mbcput(c, buf, enc); } static int -enc_mbclen(const char *p, const char *e, void *enc) +enc_mbclen(const char *p, const char *e, parser_encoding *enc) { - return rb_enc_mbclen(p, e, (rb_encoding *)enc); + return rb_enc_mbclen(p, e, enc); } -static void * +static parser_encoding * enc_from_index(int idx) { - return (void *)rb_enc_from_index(idx); + return rb_enc_from_index(idx); } static int -enc_isspace(OnigCodePoint c, void *enc) +enc_isspace(OnigCodePoint c, parser_encoding *enc) { - return rb_enc_isspace(c, (rb_encoding *)enc); + return rb_enc_isspace(c, enc); } static ID -intern3(const char *name, long len, void *enc) +intern3(const char *name, long len, parser_encoding *enc) { - return rb_intern3(name, len, (rb_encoding *)enc); + return rb_intern3(name, len, enc); } -static void * +static parser_encoding * usascii_encoding(void) { - return (void *)rb_usascii_encoding(); + return rb_usascii_encoding(); } static int -enc_symname_type(const char *name, long len, void *enc, unsigned int allowed_attrset) +enc_symname_type(const char *name, long len, parser_encoding *enc, unsigned int allowed_attrset) { - return rb_enc_symname_type(name, len, (rb_encoding *)enc, allowed_attrset); + return rb_enc_symname_type(name, len, enc, allowed_attrset); } typedef struct { @@ -220,7 +222,7 @@ reg_named_capture_assign_iter(const OnigUChar *name, const OnigUChar *name_end, long len = name_end - name; const char *s = (const char *)name; - return rb_reg_named_capture_assign_iter_impl(p, s, len, (void *)enc, &arg->succ_block, loc); + return rb_reg_named_capture_assign_iter_impl(p, s, len, enc, &arg->succ_block, loc); } static NODE * @@ -305,25 +307,25 @@ static_id2sym(ID id) } static long -str_coderange_scan_restartable(const char *s, const char *e, void *enc, int *cr) +str_coderange_scan_restartable(const char *s, const char *e, parser_encoding *enc, int *cr) { - return rb_str_coderange_scan_restartable(s, e, (rb_encoding *)enc, cr); + return rb_str_coderange_scan_restartable(s, e, enc, cr); } static int -enc_mbminlen(void *enc) +enc_mbminlen(parser_encoding *enc) { - return rb_enc_mbminlen((rb_encoding *)enc); + return rb_enc_mbminlen(enc); } static bool -enc_isascii(OnigCodePoint c, void *enc) +enc_isascii(OnigCodePoint c, parser_encoding *enc) { - return rb_enc_isascii(c, (rb_encoding *)enc); + return rb_enc_isascii(c, enc); } static OnigCodePoint -enc_mbc_to_codepoint(const char *p, const char *e, void *enc) +enc_mbc_to_codepoint(const char *p, const char *e, parser_encoding *enc) { const OnigUChar *up = RBIMPL_CAST((const OnigUChar *)p); const OnigUChar *ue = RBIMPL_CAST((const OnigUChar *)e); diff --git a/rubyparser.h b/rubyparser.h index cb90b847b66769..fc8be9633aa2ae 100644 --- a/rubyparser.h +++ b/rubyparser.h @@ -9,7 +9,7 @@ #ifdef UNIVERSAL_PARSER -#define rb_encoding void +#define rb_encoding const void #define OnigCodePoint unsigned int #include "parser_st.h" #ifndef RUBY_RUBY_H diff --git a/universal_parser.c b/universal_parser.c index 05445587ba0bc9..c5e557ca30370a 100644 --- a/universal_parser.c +++ b/universal_parser.c @@ -59,7 +59,7 @@ #undef st_lookup #define st_lookup rb_parser_st_lookup -#define rb_encoding void +#define rb_encoding const void #undef xmalloc #define xmalloc p->config->malloc From e9262983430e7a758a4151e39151062ac00d67bf Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Fri, 10 May 2024 16:11:51 +0200 Subject: [PATCH 002/242] [rubygems/rubygems] Require things right before they're needed https://github.com/rubygems/rubygems/commit/07022e3f2f --- lib/bundler/rubygems_ext.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index a7539f4adb7d46..0cd94d60ce6e9c 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -4,8 +4,6 @@ require "rubygems" unless defined?(Gem) -require "rubygems/specification" - # We can't let `Gem::Source` be autoloaded in the `Gem::Specification#source` # redefinition below, so we need to load it upfront. The reason is that if # Bundler monkeypatches are loaded before RubyGems activates an executable (for @@ -17,10 +15,6 @@ # `Gem::Source` from the redefined `Gem::Specification#source`. require "rubygems/source" -require_relative "match_metadata" -require_relative "force_platform" -require_relative "match_platform" - # Cherry-pick fixes to `Gem.ruby_version` to be useful for modern Bundler # versions and ignore patchlevels # (https://github.com/rubygems/rubygems/pull/5472, @@ -31,7 +25,12 @@ end module Gem + require "rubygems/specification" + class Specification + require_relative "match_metadata" + require_relative "match_platform" + include ::Bundler::MatchMetadata include ::Bundler::MatchPlatform @@ -148,17 +147,23 @@ def dependencies_to_gemfile(dependencies, group = nil) module BetterPermissionError def data + require_relative "shared_helpers" + Bundler::SharedHelpers.filesystem_access(loaded_from, :read) do super end end end + require "rubygems/stub_specification" + class StubSpecification prepend BetterPermissionError end class Dependency + require_relative "force_platform" + include ::Bundler::ForcePlatform attr_accessor :source, :groups From 69c87619bd7a69936c44cc0d323b11148400e894 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Fri, 10 May 2024 16:14:26 +0200 Subject: [PATCH 003/242] [rubygems/rubygems] Avoid standard requires while loading shared helpers We should make sure Bundler does not trigger RubyGems require logic for gem activation until it had the chance to register its own monkeypatches to RubyGems. https://github.com/rubygems/rubygems/commit/fbd2ff86b9 --- lib/bundler/constants.rb | 2 ++ lib/bundler/rubygems_integration.rb | 6 ++---- lib/bundler/shared_helpers.rb | 10 ++++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/bundler/constants.rb b/lib/bundler/constants.rb index de9698b577e41b..bcbd228b187650 100644 --- a/lib/bundler/constants.rb +++ b/lib/bundler/constants.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "rbconfig" + module Bundler WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/ FREEBSD = RbConfig::CONFIG["host_os"].to_s.include?("bsd") diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 494030eab22846..6980b32236e503 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -4,9 +4,7 @@ module Bundler class RubygemsIntegration - require "monitor" - - EXT_LOCK = Monitor.new + autoload :Monitor, "monitor" def initialize @replaced_methods = {} @@ -173,7 +171,7 @@ def ui=(obj) end def ext_lock - EXT_LOCK + @ext_lock ||= Monitor.new end def spec_from_gem(path) diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index 78760e6fa48c64..28f0cdff192c66 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -1,15 +1,17 @@ # frozen_string_literal: true -require "pathname" -require "rbconfig" - require_relative "version" -require_relative "constants" require_relative "rubygems_integration" require_relative "current_ruby" module Bundler + autoload :WINDOWS, File.expand_path("constants", __dir__) + autoload :FREEBSD, File.expand_path("constants", __dir__) + autoload :NULL, File.expand_path("constants", __dir__) + module SharedHelpers + autoload :Pathname, "pathname" + def root gemfile = find_gemfile raise GemfileNotFound, "Could not locate Gemfile" unless gemfile From 899568cce9f2929c765738bf52fcadaead953187 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Thu, 9 May 2024 15:25:26 +0200 Subject: [PATCH 004/242] [rubygems/rubygems] Fix error when Bundler installation is corrupted If one upgrades the default copy of Bundler through `gem update --system`, and then reinstalls Ruby without removing the previous copy. Then the new installation will have a correct default bundler gemspec, but a higher copy installed in site_dir. This causes a crash when running Bundler and prints the bug report template. This could probably be fixed in Ruby install script, by removing any previous Bundler default copies, but if the problem is already there, I think it's best to print a proper user error. https://github.com/rubygems/rubygems/commit/ada6de765d --- lib/bundler/errors.rb | 14 +++++++++++++ lib/bundler/source/metadata.rb | 2 ++ spec/bundler/commands/install_spec.rb | 29 +++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb index b6a11cc7219518..c29b1bfed89122 100644 --- a/lib/bundler/errors.rb +++ b/lib/bundler/errors.rb @@ -230,4 +230,18 @@ def message status_code(38) end + + class CorruptBundlerInstallError < BundlerError + def initialize(loaded_spec) + @loaded_spec = loaded_spec + end + + def message + "The running version of Bundler (#{Bundler::VERSION}) does not match the version of the specification installed for it (#{@loaded_spec.version}). " \ + "This can be caused by reinstalling Ruby without removing previous installation, leaving around an upgraded default version of Bundler. " \ + "Reinstalling Ruby from scratch should fix the problem." + end + + status_code(39) + end end diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index 4d27761365385e..6b05e177271be1 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -11,6 +11,8 @@ def specs end if local_spec = Gem.loaded_specs["bundler"] + raise CorruptBundlerInstallError.new(local_spec) if local_spec.version.to_s != Bundler::VERSION + idx << local_spec else idx << Gem::Specification.new do |s| diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 1e57414377e76f..edc5887d7bceda 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -1407,4 +1407,33 @@ def run expect(bundled_app(".bundle/config")).not_to exist end end + + context "when bundler installation is corrupt" do + before do + system_gems "bundler-9.99.8" + + replace_version_file("9.99.9", dir: system_gem_path("gems/bundler-9.99.8")) + end + + it "shows a proper error" do + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + + BUNDLED WITH + 9.99.8 + L + + install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", env: { "BUNDLER_VERSION" => "9.99.8" }, raise_on_error: false + + expect(err).not_to include("ERROR REPORT TEMPLATE") + expect(err).to include("The running version of Bundler (9.99.9) does not match the version of the specification installed for it (9.99.8)") + end + end end From a86ad47c940dee19bfe74a374c5ac61752026b7c Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 6 May 2024 15:56:37 +0200 Subject: [PATCH 005/242] [rubygems/rubygems] Improve plugin removal test Instead of unit testing the `remove_plugins` method, test the whole removal process. https://github.com/rubygems/rubygems/commit/bfdc60af98 --- test/rubygems/test_gem_uninstaller.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index 9e0c1aa3d89397..cd6d560ece1c86 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -188,20 +188,29 @@ def test_remove_plugins refute File.exist?(plugin_path), "plugin not removed" end - def test_remove_plugins_with_install_dir + def test_uninstall_with_install_dir_removes_plugins write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io| io.write "# do nothing" end @spec.files += %w[lib/rubygems_plugin.rb] - Gem::Installer.at(Gem::Package.build(@spec), force: true).install + package = Gem::Package.build(@spec) + + Gem::Installer.at(package, force: true).install plugin_path = File.join Gem.plugindir, "a_plugin.rb" assert File.exist?(plugin_path), "plugin not written" - Dir.mkdir "#{@gemhome}2" - Gem::Uninstaller.new(nil, install_dir: "#{@gemhome}2").remove_plugins @spec + install_dir = "#{@gemhome}2" + + Gem::Installer.at(package, force: true, install_dir: install_dir).install + + install_dir_plugin_path = File.join install_dir, "plugins/a_plugin.rb" + assert File.exist?(install_dir_plugin_path), "plugin not written" + + Gem::Specification.dirs = [install_dir] + Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall assert File.exist?(plugin_path), "plugin unintentionally removed" end From 5880103450faf26da193ac914421957e37d71604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= Date: Mon, 13 Mar 2023 14:32:19 +0100 Subject: [PATCH 006/242] [rubygems/rubygems] Use spec.base_dir to remove plugins The plugin loader from `@gem_home` was removed during uninstallation. However, this could leave behind the plugins for `--user-install` installed gems. Use `Gem::Specifictaions#base_dir` instead. This ensures that the plugin loader for associated .gemspec is uninstalled. https://github.com/rubygems/rubygems/commit/6047f78210 --- lib/rubygems/uninstaller.rb | 9 ++++++--- test/rubygems/test_gem_uninstaller.rb | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb index c96df2a08536bb..f4202caa0a5a0f 100644 --- a/lib/rubygems/uninstaller.rb +++ b/lib/rubygems/uninstaller.rb @@ -50,7 +50,6 @@ def initialize(gem, options = {}) @gem = gem @version = options[:version] || Gem::Requirement.default @gem_home = File.realpath(options[:install_dir] || Gem.dir) - @plugins_dir = Gem.plugindir(@gem_home) @force_executables = options[:executables] @force_all = options[:all] @force_ignore = options[:ignore] @@ -284,7 +283,7 @@ def remove(spec) def remove_plugins(spec) # :nodoc: return if spec.plugins.empty? - remove_plugins_for(spec, @plugins_dir) + remove_plugins_for(spec, plugin_dir_for(spec)) end ## @@ -294,7 +293,7 @@ def regenerate_plugins latest = Gem::Specification.latest_spec_for(@spec.name) return if latest.nil? - regenerate_plugins_for(latest, @plugins_dir) + regenerate_plugins_for(latest, plugin_dir_for(@spec)) end ## @@ -406,4 +405,8 @@ def warn_cannot_uninstall_default_gems(specs) say "Gem #{spec.full_name} cannot be uninstalled because it is a default gem" end end + + def plugin_dir_for(spec) + Gem.plugindir(spec.base_dir) + end end diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index cd6d560ece1c86..20a0005341475c 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -215,6 +215,25 @@ def test_uninstall_with_install_dir_removes_plugins assert File.exist?(plugin_path), "plugin unintentionally removed" end + def test_remove_plugins_user_installed + write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io| + io.write "# do nothing" + end + + @spec.files += %w[lib/rubygems_plugin.rb] + + Gem::Installer.at(Gem::Package.build(@spec), force: true, user_install: true).install + + plugin_path = File.join Gem.user_dir, "plugins/a_plugin.rb" + assert File.exist?(plugin_path), "plugin not written" + + Gem::Specification.dirs = [Gem.dir, Gem.user_dir] + + Gem::Uninstaller.new(@spec.name, executables: true, force: true, user_install: true).uninstall + + refute File.exist?(plugin_path), "plugin not removed" + end + def test_regenerate_plugins_for write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io| io.write "# do nothing" From 02c8e6583b33c62b4050420234167a467f32fefa Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Tue, 20 Feb 2024 11:05:30 -0800 Subject: [PATCH 007/242] [rubygems/rubygems] Use a constant empty tar header to avoid extra allocations https://github.com/rubygems/rubygems/commit/716666f65f --- lib/rubygems/package/tar_header.rb | 24 +++++++++++++++---- test/rubygems/test_gem_package_tar_header.rb | 25 ++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb index 087f13f6c9e117..dd5e835a1e59b1 100644 --- a/lib/rubygems/package/tar_header.rb +++ b/lib/rubygems/package/tar_header.rb @@ -95,14 +95,14 @@ class Gem::Package::TarHeader attr_reader(*FIELDS) - EMPTY_HEADER = ("\0" * 512).freeze # :nodoc: + EMPTY_HEADER = ("\0" * 512).b.freeze # :nodoc: ## # Creates a tar header from IO +stream+ def self.from(stream) header = stream.read 512 - empty = (header == EMPTY_HEADER) + return EMPTY if header == EMPTY_HEADER fields = header.unpack UNPACK_FORMAT @@ -123,7 +123,7 @@ def self.from(stream) devminor: strict_oct(fields.shift), prefix: fields.shift, - empty: empty + empty: false end def self.strict_oct(str) @@ -172,6 +172,22 @@ def initialize(vals) @empty = vals[:empty] end + EMPTY = new({ # :nodoc: + checksum: 0, + gname: "", + linkname: "", + magic: "", + mode: 0, + name: "", + prefix: "", + size: 0, + uname: "", + version: 0, + + empty: true, + }).freeze + private_constant :EMPTY + ## # Is the tar entry empty? @@ -241,7 +257,7 @@ def header(checksum = @checksum) header = header.pack PACK_FORMAT - header << ("\0" * ((512 - header.size) % 512)) + header.ljust 512, "\0" end def oct(num, len) diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb index 4469750f9a5657..a3f95bb7704f91 100644 --- a/test/rubygems/test_gem_package_tar_header.rb +++ b/test/rubygems/test_gem_package_tar_header.rb @@ -99,6 +99,31 @@ def test_empty_eh assert_empty @tar_header end + def test_empty + @tar_header = Gem::Package::TarHeader.from(StringIO.new(Gem::Package::TarHeader::EMPTY_HEADER)) + + assert_empty @tar_header + assert_equal Gem::Package::TarHeader.new( + checksum: 0, + devmajor: 0, + devminor: 0, + empty: true, + gid: 0, + gname: "", + linkname: "", + magic: "", + mode: 0, + mtime: 0, + name: "", + prefix: "", + size: 0, + typeflag: "0", + uid: 0, + uname: "", + version: 0, + ), @tar_header + end + def test_equals2 assert_equal @tar_header, @tar_header assert_equal @tar_header, @tar_header.dup From e6340258f88064cabba5150cfef1f8898f6aa9d8 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 13 May 2024 11:26:39 -0400 Subject: [PATCH 008/242] [ruby/prism] Autoload newlines and comment visitors Having the @newline instance variable in every node adds up, and since it is so rarely used, we only want to add it when necessary. Moving this into an autoloaded file and moving the instance variable out of the default initializers reduces allocated memory because the nodes are now smaller and some fit into the compact list. On my machine, I'm seeing about an 8% drop. https://github.com/ruby/prism/commit/eea92c07d2 --- lib/prism.rb | 2 - lib/prism/parse_result.rb | 17 +++ lib/prism/parse_result/comments.rb | 7 -- lib/prism/parse_result/newlines.rb | 111 ++++++++++++++++-- .../lib/prism/inspect_visitor.rb.erb | 7 +- prism/templates/lib/prism/node.rb.erb | 32 ----- 6 files changed, 118 insertions(+), 58 deletions(-) diff --git a/lib/prism.rb b/lib/prism.rb index 19774538e7a9e1..2bb7f79bf644d0 100644 --- a/lib/prism.rb +++ b/lib/prism.rb @@ -71,8 +71,6 @@ def self.load(source, serialized) require_relative "prism/node" require_relative "prism/node_ext" require_relative "prism/parse_result" -require_relative "prism/parse_result/comments" -require_relative "prism/parse_result/newlines" # This is a Ruby implementation of the prism parser. If we're running on CRuby # and we haven't explicitly set the PRISM_FFI_BACKEND environment variable, then diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb index 63cc72a96640b2..798fde09e5a7c8 100644 --- a/lib/prism/parse_result.rb +++ b/lib/prism/parse_result.rb @@ -574,6 +574,12 @@ def failure? # This is a result specific to the `parse` and `parse_file` methods. class ParseResult < Result + autoload :Comments, "prism/parse_result/comments" + autoload :Newlines, "prism/parse_result/newlines" + + private_constant :Comments + private_constant :Newlines + # The syntax tree that was parsed from the source code. attr_reader :value @@ -587,6 +593,17 @@ def initialize(value, comments, magic_comments, data_loc, errors, warnings, sour def deconstruct_keys(keys) super.merge!(value: value) end + + # Attach the list of comments to their respective locations in the tree. + def attach_comments! + Comments.new(self).attach! # steep:ignore + end + + # Walk the tree and mark nodes that are on a new line, loosely emulating + # the behavior of CRuby's `:line` tracepoint event. + def mark_newlines! + value.accept(Newlines.new(source.offsets.size)) # steep:ignore + end end # This is a result specific to the `lex` and `lex_file` methods. diff --git a/lib/prism/parse_result/comments.rb b/lib/prism/parse_result/comments.rb index 3fa0603d74ce71..22c4148b2c168c 100644 --- a/lib/prism/parse_result/comments.rb +++ b/lib/prism/parse_result/comments.rb @@ -183,12 +183,5 @@ def nearest_targets(node, comment) [preceding, NodeTarget.new(node), following] end end - - private_constant :Comments - - # Attach the list of comments to their respective locations in the tree. - def attach_comments! - Comments.new(self).attach! # steep:ignore - end end end diff --git a/lib/prism/parse_result/newlines.rb b/lib/prism/parse_result/newlines.rb index 4a8151cc09a090..cc1343dfdacc28 100644 --- a/lib/prism/parse_result/newlines.rb +++ b/lib/prism/parse_result/newlines.rb @@ -17,21 +17,26 @@ class ParseResult < Result # Note that the logic in this file should be kept in sync with the Java # MarkNewlinesVisitor, since that visitor is responsible for marking the # newlines for JRuby/TruffleRuby. + # + # This file is autoloaded only when `mark_newlines!` is called, so the + # re-opening of the various nodes in this file will only be performed in + # that case. We do that to avoid storing the extra `@newline` instance + # variable on every node if we don't need it. class Newlines < Visitor # Create a new Newlines visitor with the given newline offsets. - def initialize(newline_marked) - @newline_marked = newline_marked + def initialize(lines) + @lines = Array.new(1 + lines, false) end # Permit block/lambda nodes to mark newlines within themselves. def visit_block_node(node) - old_newline_marked = @newline_marked - @newline_marked = Array.new(old_newline_marked.size, false) + old_lines = @lines + @lines = Array.new(old_lines.size, false) begin super(node) ensure - @newline_marked = old_newline_marked + @lines = old_lines end end @@ -39,7 +44,7 @@ def visit_block_node(node) # Mark if/unless nodes as newlines. def visit_if_node(node) - node.set_newline_flag(@newline_marked) + node.newline!(@lines) super(node) end @@ -48,17 +53,101 @@ def visit_if_node(node) # Permit statements lists to mark newlines within themselves. def visit_statements_node(node) node.body.each do |child| - child.set_newline_flag(@newline_marked) + child.newline!(@lines) end super(node) end end + end + + class Node + def newline? # :nodoc: + @newline ? true : false + end + + def newline!(lines) # :nodoc: + line = location.start_line + unless lines[line] + lines[line] = true + @newline = true + end + end + end + + class BeginNode < Node + def newline!(lines) # :nodoc: + # Never mark BeginNode with a newline flag, mark children instead. + end + end + + class ParenthesesNode < Node + def newline!(lines) # :nodoc: + # Never mark ParenthesesNode with a newline flag, mark children instead. + end + end + + class IfNode < Node + def newline!(lines) # :nodoc: + predicate.newline!(lines) + end + end + + class UnlessNode < Node + def newline!(lines) # :nodoc: + predicate.newline!(lines) + end + end + + class UntilNode < Node + def newline!(lines) # :nodoc: + predicate.newline!(lines) + end + end + + class WhileNode < Node + def newline!(lines) # :nodoc: + predicate.newline!(lines) + end + end + + class RescueModifierNode < Node + def newline!(lines) # :nodoc: + expression.newline!(lines) + end + end + + class InterpolatedMatchLastLineNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first + end + end + + class InterpolatedRegularExpressionNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first + end + end + + class InterpolatedStringNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first + end + end - private_constant :Newlines + class InterpolatedSymbolNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first + end + end - # Walk the tree and mark nodes that are on a new line. - def mark_newlines! - value.accept(Newlines.new(Array.new(1 + source.offsets.size, false))) # steep:ignore + class InterpolatedXStringNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first end end end diff --git a/prism/templates/lib/prism/inspect_visitor.rb.erb b/prism/templates/lib/prism/inspect_visitor.rb.erb index 8e7902f0f1ff2d..9328da636b0739 100644 --- a/prism/templates/lib/prism/inspect_visitor.rb.erb +++ b/prism/templates/lib/prism/inspect_visitor.rb.erb @@ -116,13 +116,8 @@ module Prism # Compose a header for the given node. def inspect_node(name, node) - result = +"@ #{name} (" - location = node.location - result << "location: (#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column})" - result << ", newline: true" if node.newline? - - result << ")\n" + "@ #{name} (location: (#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}))\n" end # Compose a string representing the given inner location field. diff --git a/prism/templates/lib/prism/node.rb.erb b/prism/templates/lib/prism/node.rb.erb index 50e622a8d12ca8..f0ce226defcff9 100644 --- a/prism/templates/lib/prism/node.rb.erb +++ b/prism/templates/lib/prism/node.rb.erb @@ -28,18 +28,6 @@ module Prism location.is_a?(Location) ? location.end_offset : ((location >> 32) + (location & 0xFFFFFFFF)) end - def newline? # :nodoc: - @newline ? true : false - end - - def set_newline_flag(newline_marked) # :nodoc: - line = location.start_line - unless newline_marked[line] - newline_marked[line] = true - @newline = true - end - end - # Returns all of the lines of the source code associated with this node. def source_lines location.source_lines @@ -181,7 +169,6 @@ module Prism # def initialize: (<%= (node.fields.map { |field| "#{field.rbs_class} #{field.name}" } + ["Location location"]).join(", ") %>) -> void def initialize(source, <%= (node.fields.map(&:name) + ["location"]).join(", ") %>) @source = source - @newline = false @location = location <%- node.fields.each do |field| -%> <%- if Prism::Template::CHECK_FIELD_KIND && field.respond_to?(:check_field_kind) -%> @@ -195,25 +182,6 @@ module Prism def accept(visitor) visitor.visit_<%= node.human %>(self) end - <%- if node.newline == false -%> - - def set_newline_flag(newline_marked) # :nodoc: - # Never mark <%= node.name %> with a newline flag, mark children instead - end - <%- elsif node.newline.is_a?(String) -%> - - def set_newline_flag(newline_marked) # :nodoc: - <%- field = node.fields.find { |f| f.name == node.newline } or raise node.newline -%> - <%- case field -%> - <%- when Prism::Template::NodeField -%> - <%= field.name %>.set_newline_flag(newline_marked) - <%- when Prism::Template::NodeListField -%> - first = <%= field.name %>.first - first.set_newline_flag(newline_marked) if first - <%- else raise field.class.name -%> - <%- end -%> - end - <%- end -%> # def child_nodes: () -> Array[nil | Node] def child_nodes From 5931f857abf704c6fc62a93d2fc933b13950392d Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 13 May 2024 16:56:01 +0900 Subject: [PATCH 009/242] [ruby/prism] Add error handling for missing `parser` gem in `Prism::Translation` Resolves https://github.com/ruby/prism/pull/2803. This PR adds error handling for missing `parser` gem in `Prism::Translation`. The `parser` gem is a required runtime dependency when using `Prism::Translation::Parser`. But it is not required for other uses of Prism. To avoid unnecessary dependencies, it is not added as a `runtime_dependency` in the prism.gemspec. Instead, if the dependency is missing, instructions are given to add it to Gemfile. ## Before ```console $ bundle exec ruby -e 'require "prism"; require "prism/translation/parser33"' /Users/koic/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `require': cannot load such file -- parser (LoadError) from /Users/koic/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `block (2 levels) in replace_require' from /Users/koic/src/github.com/ruby/prism/lib/prism/translation/parser.rb:3:in `' from /Users/koic/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `require' from /Users/koic/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `block (2 levels) in replace_require' from /Users/koic/src/github.com/ruby/prism/lib/prism/translation/parser33.rb:6:in `' from /Users/koic/src/github.com/ruby/prism/lib/prism/translation/parser33.rb:4:in `' from /Users/koic/src/github.com/ruby/prism/lib/prism/translation/parser33.rb:3:in `' from /Users/koic/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `require' from /Users/koic/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `block (2 levels) in replace_require' from -e:1:in `
' ``` ## After ```console $ bundle exec ruby -e 'require "prism"; require "prism/translation/parser33"' Error: Unable to load parser. Add `gem "parser"` to your Gemfile. ``` https://github.com/ruby/prism/commit/4880aec22d --- lib/prism/translation/parser.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb index 193bbae406e536..8052e38570c8f8 100644 --- a/lib/prism/translation/parser.rb +++ b/lib/prism/translation/parser.rb @@ -1,6 +1,11 @@ # frozen_string_literal: true -require "parser" +begin + require "parser" +rescue LoadError => e + warn 'Error: Unable to load parser. Add `gem "parser"` to your Gemfile.' + exit! +end module Prism module Translation From b5e53e2f32e19e826eacbf9e2e301b1deae146df Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 13 May 2024 12:31:47 -0400 Subject: [PATCH 010/242] [ruby/prism] Rescue LoadError for ruby_parser as well https://github.com/ruby/prism/commit/d4eb13e703 --- lib/prism/translation/parser.rb | 6 +++--- lib/prism/translation/ruby_parser.rb | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb index 8052e38570c8f8..3748fc896e9826 100644 --- a/lib/prism/translation/parser.rb +++ b/lib/prism/translation/parser.rb @@ -2,9 +2,9 @@ begin require "parser" -rescue LoadError => e - warn 'Error: Unable to load parser. Add `gem "parser"` to your Gemfile.' - exit! +rescue LoadError + warn(%q{Error: Unable to load parser. Add `gem "parser"` to your Gemfile.}) + exit(1) end module Prism diff --git a/lib/prism/translation/ruby_parser.rb b/lib/prism/translation/ruby_parser.rb index d01a762a033b24..43aac23be7b7dd 100644 --- a/lib/prism/translation/ruby_parser.rb +++ b/lib/prism/translation/ruby_parser.rb @@ -1,6 +1,11 @@ # frozen_string_literal: true -require "ruby_parser" +begin + require "ruby_parser" +rescue LoadError + warn(%q{Error: Unable to load ruby_parser. Add `gem "ruby_parser"` to your Gemfile.}) + exit(1) +end module Prism module Translation From 22dab739714208bde73a7c86ebcc2667e534b39e Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 13 May 2024 15:02:43 +0200 Subject: [PATCH 011/242] [rubygems/rubygems] Don't depend on Pathname unnecessarily https://github.com/rubygems/rubygems/commit/8c8aaecc48 --- lib/bundler/rubygems_ext.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index 0cd94d60ce6e9c..fa61664a90bc67 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "pathname" - require "rubygems" unless defined?(Gem) # We can't let `Gem::Source` be autoloaded in the `Gem::Specification#source` @@ -47,7 +45,7 @@ def source def full_gem_path if source.respond_to?(:root) - Pathname.new(loaded_from).dirname.expand_path(source.root).to_s + File.expand_path(File.dirname(loaded_from), source.root) else rg_full_gem_path end From d1c172dfd0296efda404e85bfc42cd8720b65cea Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 13 May 2024 15:03:13 +0200 Subject: [PATCH 012/242] [rubygems/rubygems] Don't load `SharedHelpers` unnecessarily https://github.com/rubygems/rubygems/commit/de4650f629 --- lib/bundler/rubygems_ext.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index fa61664a90bc67..14b870321313c0 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -145,11 +145,9 @@ def dependencies_to_gemfile(dependencies, group = nil) module BetterPermissionError def data - require_relative "shared_helpers" - - Bundler::SharedHelpers.filesystem_access(loaded_from, :read) do - super - end + super + rescue Errno::EACCES + raise Bundler::PermissionError.new(loaded_from, :read) end end From 5319587ac32e168dbaba452585e3f3861082ee8d Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 13 May 2024 15:03:32 +0200 Subject: [PATCH 013/242] [rubygems/rubygems] Restore previous way of loading monitor I'm not sure why but this particular change seems to have introduced some flaky test failures. So I'll revert it. https://github.com/rubygems/rubygems/commit/668488014b --- lib/bundler/rubygems_integration.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 6980b32236e503..494030eab22846 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -4,7 +4,9 @@ module Bundler class RubygemsIntegration - autoload :Monitor, "monitor" + require "monitor" + + EXT_LOCK = Monitor.new def initialize @replaced_methods = {} @@ -171,7 +173,7 @@ def ui=(obj) end def ext_lock - @ext_lock ||= Monitor.new + EXT_LOCK end def spec_from_gem(path) From 9d01f657b30f51f60ac6dec7fb47d13549d26bcd Mon Sep 17 00:00:00 2001 From: Mari Imaizumi Date: Tue, 14 May 2024 10:58:27 +0900 Subject: [PATCH 014/242] [ruby/reline] Implement the undo command (https://github.com/ruby/reline/pull/701) * Refactor send * Implement the undo command * Fix @past_lines initialization * Improve assertion * Hide to save buffer in insert_pasted_text * Replace @using_delete_command with @undoing * Refactor `@past_lines` https://github.com/ruby/reline/commit/4ab72f9cbd --- lib/reline/key_actor/emacs.rb | 2 +- lib/reline/line_editor.rb | 61 ++++++++++++++++-- test/reline/test_key_actor_emacs.rb | 68 +++++++++++++++++++++ test/reline/yamatanooroti/test_rendering.rb | 13 ++++ 4 files changed, 138 insertions(+), 6 deletions(-) diff --git a/lib/reline/key_actor/emacs.rb b/lib/reline/key_actor/emacs.rb index 9c797ba43e97bc..edd88289a3c940 100644 --- a/lib/reline/key_actor/emacs.rb +++ b/lib/reline/key_actor/emacs.rb @@ -63,7 +63,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base # 30 ^^ :ed_unassigned, # 31 ^_ - :ed_unassigned, + :undo, # 32 SPACE :ed_insert, # 33 ! diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 4c76932c1097a5..23ece602203432 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -4,7 +4,6 @@ require 'tempfile' class Reline::LineEditor - # TODO: undo # TODO: Use "private alias_method" idiom after drop Ruby 2.5. attr_reader :byte_pointer attr_accessor :confirm_multiline_termination_proc @@ -251,6 +250,8 @@ def reset_variables(prompt = '', encoding:) @resized = false @cache = {} @rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0) + @past_lines = [] + @undoing = false reset_line end @@ -948,7 +949,8 @@ def dialog_proc_scope_completion_journey_data unless @waiting_proc byte_pointer_diff = @byte_pointer - old_byte_pointer @byte_pointer = old_byte_pointer - send(@vi_waiting_operator, byte_pointer_diff) + method_obj = method(@vi_waiting_operator) + wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff) cleanup_waiting end else @@ -1009,7 +1011,8 @@ def wrap_method_call(method_symbol, method_obj, key, with_operator = false) if @vi_waiting_operator byte_pointer_diff = @byte_pointer - old_byte_pointer @byte_pointer = old_byte_pointer - send(@vi_waiting_operator, byte_pointer_diff) + method_obj = method(@vi_waiting_operator) + wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff) cleanup_waiting end @kill_ring.process @@ -1106,6 +1109,7 @@ def update(key) end def input_key(key) + save_old_buffer @config.reset_oneshot_key_bindings @dialogs.each do |dialog| if key.char.instance_of?(Symbol) and key.char == dialog.name @@ -1120,7 +1124,6 @@ def input_key(key) finish return end - old_lines = @buffer_of_lines.dup @first_char = false @completion_occurs = false @@ -1134,12 +1137,15 @@ def input_key(key) @completion_journey_state = nil end + push_past_lines unless @undoing + @undoing = false + if @in_pasting clear_dialogs return end - modified = old_lines != @buffer_of_lines + modified = @old_buffer_of_lines != @buffer_of_lines if !@completion_occurs && modified && !@config.disable_completion && @config.autocompletion # Auto complete starts only when edited process_insert(force: true) @@ -1148,6 +1154,26 @@ def input_key(key) modified end + def save_old_buffer + @old_buffer_of_lines = @buffer_of_lines.dup + @old_byte_pointer = @byte_pointer.dup + @old_line_index = @line_index.dup + end + + def push_past_lines + if @old_buffer_of_lines != @buffer_of_lines + @past_lines.push([@old_buffer_of_lines, @old_byte_pointer, @old_line_index]) + end + trim_past_lines + end + + MAX_PAST_LINES = 100 + def trim_past_lines + if @past_lines.size > MAX_PAST_LINES + @past_lines.shift + end + end + def scroll_into_view _wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position if wrapped_cursor_y < screen_scroll_top @@ -1224,6 +1250,18 @@ def set_current_line(line, byte_pointer = nil) process_auto_indent end + def set_current_lines(lines, byte_pointer = nil, line_index = 0) + cursor = current_byte_pointer_cursor + @buffer_of_lines = lines + @line_index = line_index + if byte_pointer + @byte_pointer = byte_pointer + else + calculate_nearest_cursor(cursor) + end + process_auto_indent + end + def retrieve_completion_block(set_completion_quote_character = false) if Reline.completer_word_break_characters.empty? word_break_regexp = nil @@ -1306,6 +1344,7 @@ def confirm_multiline_termination end def insert_pasted_text(text) + save_old_buffer pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer) post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..) lines = (pre + text.gsub(/\r\n?/, "\n") + post).split("\n", -1) @@ -1313,6 +1352,7 @@ def insert_pasted_text(text) @buffer_of_lines[@line_index, 1] = lines @line_index += lines.size - 1 @byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize + push_past_lines end def insert_text(text) @@ -2487,4 +2527,15 @@ def finish private def vi_editing_mode(key) @config.editing_mode = :vi_insert end + + private def undo(_key) + return if @past_lines.empty? + + @undoing = true + + target_lines, target_cursor_x, target_cursor_y = @past_lines.last + set_current_lines(target_lines, target_cursor_x, target_cursor_y) + + @past_lines.pop + end end diff --git a/test/reline/test_key_actor_emacs.rb b/test/reline/test_key_actor_emacs.rb index 409a7334cbbe9d..013ca2f7b36040 100644 --- a/test/reline/test_key_actor_emacs.rb +++ b/test/reline/test_key_actor_emacs.rb @@ -1437,4 +1437,72 @@ def test_vi_editing_mode @line_editor.__send__(:vi_editing_mode, nil) assert(@config.editing_mode_is?(:vi_insert)) end + + def test_undo + input_keys("\C-_", false) + assert_line_around_cursor('', '') + input_keys("aあb\C-h\C-h\C-h", false) + assert_line_around_cursor('', '') + input_keys("\C-_", false) + assert_line_around_cursor('a', '') + input_keys("\C-_", false) + assert_line_around_cursor('aあ', '') + input_keys("\C-_", false) + assert_line_around_cursor('aあb', '') + input_keys("\C-_", false) + assert_line_around_cursor('aあ', '') + input_keys("\C-_", false) + assert_line_around_cursor('a', '') + input_keys("\C-_", false) + assert_line_around_cursor('', '') + end + + def test_undo_with_cursor_position + input_keys("abc\C-b\C-h", false) + assert_line_around_cursor('a', 'c') + input_keys("\C-_", false) + assert_line_around_cursor('ab', 'c') + input_keys("あいう\C-b\C-h", false) + assert_line_around_cursor('abあ', 'うc') + input_keys("\C-_", false) + assert_line_around_cursor('abあい', 'うc') + end + + def test_undo_with_multiline + @line_editor.multiline_on + @line_editor.confirm_multiline_termination_proc = proc {} + input_keys("1\n2\n3", false) + assert_whole_lines(["1", "2", "3"]) + assert_line_index(2) + assert_line_around_cursor('3', '') + input_keys("\C-p\C-h\C-h", false) + assert_whole_lines(["1", "3"]) + assert_line_index(0) + assert_line_around_cursor('1', '') + input_keys("\C-_", false) + assert_whole_lines(["1", "", "3"]) + assert_line_index(1) + assert_line_around_cursor('', '') + input_keys("\C-_", false) + assert_whole_lines(["1", "2", "3"]) + assert_line_index(1) + assert_line_around_cursor('2', '') + input_keys("\C-_", false) + assert_whole_lines(["1", "2", ""]) + assert_line_index(2) + assert_line_around_cursor('', '') + input_keys("\C-_", false) + assert_whole_lines(["1", "2"]) + assert_line_index(1) + assert_line_around_cursor('2', '') + end + + def test_undo_with_many_times + str = "a" + "b" * 100 + input_keys(str, false) + 100.times { input_keys("\C-_", false) } + assert_line_around_cursor('a', '') + input_keys("\C-_", false) + assert_line_around_cursor('a', '') + end end diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb index 9e8d7da78fdfe6..37a1c1a1930907 100644 --- a/test/reline/yamatanooroti/test_rendering.rb +++ b/test/reline/yamatanooroti/test_rendering.rb @@ -556,6 +556,19 @@ def test_bracketed_paste EOC end + def test_bracketed_paste_with_undo + omit if Reline.core.io_gate.win? + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write("abc") + write("\e[200~def hoge\r\t3\rend\e[201~") + write("\C-_") + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> abc + EOC + end + def test_backspace_until_returns_to_initial start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("ABC") From b60f09442a0220e2fc053de020fdc58b642f851e Mon Sep 17 00:00:00 2001 From: Andrew Konchin Date: Fri, 10 May 2024 12:28:59 +0300 Subject: [PATCH 015/242] Fix TestPatternMatching#test_deconstruct_keys test Before the change `C.keys` returned keys captured in some previous test case that by chance captured `nil` value what made this test passed successfully. Now it returns keys captured in this test case. --- test/ruby/test_pattern_matching.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index db6ad06b82d12a..cfe3bd1e1939a3 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -1331,7 +1331,7 @@ def test_deconstruct_keys end assert_block do - case {} + case C.new({}) in {} C.keys == nil end From 4a1e4436a99c5794b022f03e8c0589cf8bae1c71 Mon Sep 17 00:00:00 2001 From: Mari Imaizumi Date: Tue, 14 May 2024 17:18:28 +0900 Subject: [PATCH 016/242] [ruby/reline] Bump version to 0.5.7 (https://github.com/ruby/reline/pull/704) https://github.com/ruby/reline/commit/b9b3d392ff --- 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 ef7d617a4504fe..46613a59520d21 100644 --- a/lib/reline/version.rb +++ b/lib/reline/version.rb @@ -1,3 +1,3 @@ module Reline - VERSION = '0.5.6' + VERSION = '0.5.7' end From 44f50f41796ee24fbd955fb768ca17f24b99b0ed Mon Sep 17 00:00:00 2001 From: git Date: Tue, 14 May 2024 08:19:43 +0000 Subject: [PATCH 017/242] Update default gems list at 4a1e4436a99c5794b022f03e8c0589 [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 4c950e884f56f1..ce626248a566ad 100644 --- a/NEWS.md +++ b/NEWS.md @@ -51,7 +51,7 @@ The following default gems are updated. * optparse 0.5.0 * prism 0.29.0 * rdoc 6.6.3.1 -* reline 0.5.6 +* reline 0.5.7 * resolv 0.4.0 * stringio 3.1.1 * strscan 3.1.1 From 41e6fd066e8ea82c460ba392a1dd55d624a19763 Mon Sep 17 00:00:00 2001 From: Alexander Ross Date: Tue, 14 May 2024 10:22:36 +0200 Subject: [PATCH 018/242] [DOC] Fix typo in example code for `String#encode` method The example code in the documentation for the `String#encode` method has a typo in the `fallback` option. The example code uses `h` as the fallback option, but it should use `hash` instead to match the variable name in the example code. --- doc/encodings.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/encodings.rdoc b/doc/encodings.rdoc index 97c0d2261600ff..d85099cdbccbba 100644 --- a/doc/encodings.rdoc +++ b/doc/encodings.rdoc @@ -419,7 +419,7 @@ These keyword-value pairs specify encoding options: hash = {"\u3042" => 'xyzzy'} hash.default = 'XYZZY' - s.encode('ASCII', fallback: h) # => "xyzzyfooXYZZY" + s.encode('ASCII', fallback: hash) # => "xyzzyfooXYZZY" def (fallback = "U+%.4X").escape(x) self % x.unpack("U") From 4dc0b2301806cc5214b4cfc40e300b03e893726f Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Fri, 3 May 2024 23:38:05 +0200 Subject: [PATCH 019/242] [rubygems/rubygems] Remove redundant receivers https://github.com/rubygems/rubygems/commit/7b71965a70 --- lib/rubygems/specification.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index a1eaf1248ed6a2..5fedc8bc7963be 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -859,7 +859,7 @@ def self.stubs_for(name) # optionally filtering out specs not matching the current platform # def self.stubs_for_pattern(pattern, match_platform = true) # :nodoc: - installed_stubs = installed_stubs(Gem::Specification.dirs, pattern) + installed_stubs = installed_stubs(dirs, pattern) installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform stubs = installed_stubs + default_stubs(pattern) stubs = stubs.uniq(&:full_name) @@ -1125,7 +1125,7 @@ def self.from_yaml(input) # +prerelease+ is true. def self.latest_specs(prerelease = false) - _latest_specs Gem::Specification.stubs, prerelease + _latest_specs stubs, prerelease end ## From 8d28e635662a44b8f4a0e6dd6d655e0734f99271 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 6 May 2024 17:13:48 +0200 Subject: [PATCH 020/242] [rubygems/rubygems] Simplify test teardown If we move test directory removal to the very end, I think we should not leak anything. https://github.com/rubygems/rubygems/commit/fb3d8944b5 --- test/rubygems/helper.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb index b90b7f28ebaa3c..771faf6488f3bd 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -76,8 +76,6 @@ class Gem::TestCase < Test::Unit::TestCase attr_accessor :uri # :nodoc: - @@tempdirs = [] - def assert_activate(expected, *specs) specs.each do |spec| case spec @@ -451,8 +449,6 @@ def teardown Dir.chdir @current_dir - FileUtils.rm_rf @tempdir - ENV.replace(@orig_env) Gem::ConfigFile.send :remove_const, :SYSTEM_WIDE_CONFIG_FILE @@ -481,12 +477,9 @@ def teardown @back_ui.close - refute_directory_exists @tempdir, "may be still in use" - ghosts = @@tempdirs.filter_map do |test_name, tempdir| - test_name if File.exist?(tempdir) - end - @@tempdirs << [method_name, @tempdir] - assert_empty ghosts + FileUtils.rm_rf @tempdir + + refute_directory_exists @tempdir, "#{@tempdir} used by test #{method_name} is still in use" end def credential_setup From 64542380728b2f0cb0a76a09d08735538350cd9f Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 6 May 2024 12:15:44 +0200 Subject: [PATCH 021/242] [rubygems/rubygems] Make `Gem.clear_paths` test less implementation dependent https://github.com/rubygems/rubygems/commit/b545daa95d --- test/rubygems/helper.rb | 10 ++++++++++ test/rubygems/test_gem.rb | 5 ++++- test/rubygems/test_gem_ci_detector.rb | 14 +------------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb index 771faf6488f3bd..856dc535882435 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -534,6 +534,16 @@ def without_any_upwards_gemfiles ENV["BUNDLE_GEMFILE"] = File.join(@tempdir, "Gemfile") end + def with_env(overrides, &block) + @orig_env = ENV.to_h + ENV.replace(overrides) + begin + block.call + ensure + ENV.replace(@orig_env) + end + end + ## # A git_gem is used with a gem dependencies file. The gem created here # has no files, just a gem specification for the given +name+ and +version+. diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index 3fb66e14072c54..40a473f8d69f8f 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -516,7 +516,10 @@ def test_self_clear_paths Gem.clear_paths - assert_nil Gem::Specification.send(:class_variable_get, :@@all) + with_env("GEM_HOME" => "foo", "GEM_PATH" => "bar") do + assert_equal("foo", Gem.dir) + assert_equal("bar", Gem.path.first) + end end def test_self_configuration diff --git a/test/rubygems/test_gem_ci_detector.rb b/test/rubygems/test_gem_ci_detector.rb index 3caefce97d72b1..a28ee49f4b2e55 100644 --- a/test/rubygems/test_gem_ci_detector.rb +++ b/test/rubygems/test_gem_ci_detector.rb @@ -3,7 +3,7 @@ require_relative "helper" require "rubygems" -class TestCiDetector < Test::Unit::TestCase +class TestCiDetector < Gem::TestCase def test_ci? with_env("FOO" => "bar") { assert_equal(false, Gem::CIDetector.ci?) } with_env("CI" => "true") { assert_equal(true, Gem::CIDetector.ci?) } @@ -29,16 +29,4 @@ def test_ci_strings assert_equal(["dsari", "taskcluster"], Gem::CIDetector.ci_strings) end end - - private - - def with_env(overrides, &block) - @orig_env = ENV.to_h - ENV.replace(overrides) - begin - block.call - ensure - ENV.replace(@orig_env) - end - end end From 91e387197010586b0fc815f2a83c77832a0f0aa3 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 6 May 2024 15:33:44 +0200 Subject: [PATCH 022/242] [rubygems/rubygems] Simplify documentation I don't think this method is any worse than others, let's only document what it does. https://github.com/rubygems/rubygems/commit/dec722187f --- lib/rubygems/specification.rb | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 5fedc8bc7963be..ca418cd394f371 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -923,16 +923,7 @@ def self.all end ## - # Sets the known specs to +specs+. Not guaranteed to work for you in - # the future. Use at your own risk. Caveat emptor. Doomy doom doom. - # Etc etc. - # - #-- - # Makes +specs+ the known specs - # Listen, time is a river - # Winter comes, code breaks - # - # -- wilsonb + # Sets the known specs to +specs+. def self.all=(specs) @@stubs_by_name = specs.group_by(&:name) From 5628cc1229ea1f53788a263cc0a6ae8df1ad428f Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 6 May 2024 15:34:23 +0200 Subject: [PATCH 023/242] [rubygems/rubygems] Add missing docs Other analog methods are documented, so document this one too. https://github.com/rubygems/rubygems/commit/76da34d44d --- lib/rubygems/specification.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index ca418cd394f371..e369516bdd6060 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -1044,6 +1044,10 @@ def self.find_inactive_by_path(path) stub&.to_spec end + ## + # Return the best specification that contains the file matching +path+, among + # those already activated. + def self.find_active_stub_by_path(path) stub = @@active_stub_with_requirable_file[path] ||= stubs.find do |s| s.activated? && s.contains_requirable_file?(path) From c219ee00b446fb22657031aa4a47b9daf72d514c Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 6 May 2024 15:36:57 +0200 Subject: [PATCH 024/242] [rubygems/rubygems] Fix typo in attribute documentation https://github.com/rubygems/rubygems/commit/8c9807092c --- lib/rubygems/uninstaller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb index f4202caa0a5a0f..7dad521a93b9df 100644 --- a/lib/rubygems/uninstaller.rb +++ b/lib/rubygems/uninstaller.rb @@ -32,7 +32,7 @@ class Gem::Uninstaller attr_reader :bin_dir ## - # The gem repository the gem will be installed into + # The gem repository the gem will be uninstalled from attr_reader :gem_home From bd8423616947738d2efb3602791db91587b822bb Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 6 May 2024 15:41:42 +0200 Subject: [PATCH 025/242] [rubygems/rubygems] Extract a `Gem::SpecificationRecord` class This class handles all logic to handle the list of specifications, given a set of GEM_PATH directories. Makes `Gem::Specification` has less responsibilities and will help with fixing some bugs next. https://github.com/rubygems/rubygems/commit/df280dbbed --- lib/rubygems/specification.rb | 107 ++++----------- lib/rubygems/specification_record.rb | 171 ++++++++++++++++++++++++ test/rubygems/test_gem_specification.rb | 11 +- 3 files changed, 202 insertions(+), 87 deletions(-) create mode 100644 lib/rubygems/specification_record.rb diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index e369516bdd6060..e27901923e772d 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -11,6 +11,7 @@ require_relative "basic_specification" require_relative "stub_specification" require_relative "platform" +require_relative "specification_record" require_relative "util/list" require "rbconfig" @@ -179,19 +180,9 @@ class Gem::Specification < Gem::BasicSpecification @@default_value[k].nil? end - def self.clear_specs # :nodoc: - @@all = nil - @@stubs = nil - @@stubs_by_name = {} - @@spec_with_requirable_file = {} - @@active_stub_with_requirable_file = {} - end - private_class_method :clear_specs - - clear_specs - # Sentinel object to represent "not found" stubs NOT_FOUND = Struct.new(:to_spec, :this).new # :nodoc: + deprecate_constant :NOT_FOUND # Tracking removed method calls to warn users during build time. REMOVED_METHODS = [:rubyforge_project=, :mark_version].freeze # :nodoc: @@ -770,7 +761,7 @@ def extensions_dir attr_accessor :specification_version def self._all # :nodoc: - @@all ||= Gem.loaded_specs.values | stubs.map(&:to_spec) + specification_record.all end def self.clear_load_cache # :nodoc: @@ -788,26 +779,9 @@ def self.each_gemspec(dirs) # :nodoc: end end - def self.gemspec_stubs_in(dir, pattern) + def self.gemspec_stubs_in(dir, pattern) # :nodoc: Gem::Util.glob_files_in_dir(pattern, dir).map {|path| yield path }.select(&:valid?) end - private_class_method :gemspec_stubs_in - - def self.installed_stubs(dirs, pattern) - map_stubs(dirs, pattern) do |path, base_dir, gems_dir| - Gem::StubSpecification.gemspec_stub(path, base_dir, gems_dir) - end - end - private_class_method :installed_stubs - - def self.map_stubs(dirs, pattern) # :nodoc: - dirs.flat_map do |dir| - base_dir = File.dirname dir - gems_dir = File.join base_dir, "gems" - gemspec_stubs_in(dir, pattern) {|path| yield path, base_dir, gems_dir } - end - end - private_class_method :map_stubs def self.each_spec(dirs) # :nodoc: each_gemspec(dirs) do |path| @@ -820,13 +794,7 @@ def self.each_spec(dirs) # :nodoc: # Returns a Gem::StubSpecification for every installed gem def self.stubs - @@stubs ||= begin - pattern = "*.gemspec" - stubs = stubs_for_pattern(pattern, false) - - @@stubs_by_name = stubs.select {|s| Gem::Platform.match_spec? s }.group_by(&:name) - stubs - end + specification_record.stubs end ## @@ -845,13 +813,7 @@ def self.default_stubs(pattern = "*.gemspec") # only returns stubs that match Gem.platforms def self.stubs_for(name) - if @@stubs - @@stubs_by_name[name] || [] - else - @@stubs_by_name[name] ||= stubs_for_pattern("#{name}-*.gemspec").select do |s| - s.name == name - end - end + specification_record.stubs_for(name) end ## @@ -859,12 +821,7 @@ def self.stubs_for(name) # optionally filtering out specs not matching the current platform # def self.stubs_for_pattern(pattern, match_platform = true) # :nodoc: - installed_stubs = installed_stubs(dirs, pattern) - installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform - stubs = installed_stubs + default_stubs(pattern) - stubs = stubs.uniq(&:full_name) - _resort!(stubs) - stubs + specification_record.stubs_for_pattern(pattern, match_platform) end def self._resort!(specs) # :nodoc: @@ -893,23 +850,14 @@ def self.load_defaults # properly sorted. def self.add_spec(spec) - return if _all.include? spec - - _all << spec - stubs << spec - (@@stubs_by_name[spec.name] ||= []) << spec - - _resort!(@@stubs_by_name[spec.name]) - _resort!(stubs) + specification_record.add_spec(spec) end ## # Removes +spec+ from the known specs. def self.remove_spec(spec) - _all.delete spec.to_spec - stubs.delete spec - (@@stubs_by_name[spec.name] || []).delete spec + specification_record.remove_spec(spec) end ## @@ -926,15 +874,14 @@ def self.all # Sets the known specs to +specs+. def self.all=(specs) - @@stubs_by_name = specs.group_by(&:name) - @@all = @@stubs = specs + specification_record.all = specs end ## # Return full names of all specs in sorted order. def self.all_names - _all.map(&:full_name) + specification_record.all_names end ## @@ -1024,12 +971,7 @@ def self.find_by_full_name(full_name) # Return the best specification that contains the file matching +path+. def self.find_by_path(path) - path = path.dup.freeze - spec = @@spec_with_requirable_file[path] ||= stubs.find do |s| - s.contains_requirable_file? path - end || NOT_FOUND - - spec.to_spec + specification_record.find_by_path(path) end ## @@ -1037,11 +979,7 @@ def self.find_by_path(path) # amongst the specs that are not activated. def self.find_inactive_by_path(path) - stub = stubs.find do |s| - next if s.activated? - s.contains_requirable_file? path - end - stub&.to_spec + specification_record.find_inactive_by_path(path) end ## @@ -1049,11 +987,7 @@ def self.find_inactive_by_path(path) # those already activated. def self.find_active_stub_by_path(path) - stub = @@active_stub_with_requirable_file[path] ||= stubs.find do |s| - s.activated? && s.contains_requirable_file?(path) - end || NOT_FOUND - - stub.this + specification_record.find_active_stub_by_path(path) end ## @@ -1120,14 +1054,14 @@ def self.from_yaml(input) # +prerelease+ is true. def self.latest_specs(prerelease = false) - _latest_specs stubs, prerelease + specification_record.latest_specs(prerelease) end ## # Return the latest installed spec for gem +name+. def self.latest_spec_for(name) - latest_specs(true).find {|installed_spec| installed_spec.name == name } + specification_record.latest_spec_for(name) end def self._latest_specs(specs, prerelease = false) # :nodoc: @@ -1265,7 +1199,7 @@ def self.required_attributes def self.reset @@dirs = nil Gem.pre_reset_hooks.each(&:call) - clear_specs + @specification_record = nil clear_load_cache unresolved = unresolved_deps unless unresolved.empty? @@ -1286,6 +1220,13 @@ def self.reset Gem.post_reset_hooks.each(&:call) end + ## + # Keeps track of all currently known specifications + + def self.specification_record + @specification_record ||= Gem::SpecificationRecord.new(dirs) + end + # DOC: This method needs documented or nodoc'd def self.unresolved_deps @unresolved_deps ||= Hash.new {|h, n| h[n] = Gem::Dependency.new n } diff --git a/lib/rubygems/specification_record.rb b/lib/rubygems/specification_record.rb new file mode 100644 index 00000000000000..bdce0b3a099184 --- /dev/null +++ b/lib/rubygems/specification_record.rb @@ -0,0 +1,171 @@ +# frozen_string_literal: true + +module Gem + class SpecificationRecord + def initialize(dirs) + @all = nil + @stubs = nil + @stubs_by_name = {} + @spec_with_requirable_file = {} + @active_stub_with_requirable_file = {} + + @dirs = dirs + end + + # Sentinel object to represent "not found" stubs + NOT_FOUND = Struct.new(:to_spec, :this).new + private_constant :NOT_FOUND + + ## + # Returns the list of all specifications in the record + + def all + @all ||= Gem.loaded_specs.values | stubs.map(&:to_spec) + end + + ## + # Returns a Gem::StubSpecification for every specification in the record + + def stubs + @stubs ||= begin + pattern = "*.gemspec" + stubs = stubs_for_pattern(pattern, false) + + @stubs_by_name = stubs.select {|s| Gem::Platform.match_spec? s }.group_by(&:name) + stubs + end + end + + ## + # Returns a Gem::StubSpecification for every specification in the record + # named +name+ only returns stubs that match Gem.platforms + + def stubs_for(name) + if @stubs + @stubs_by_name[name] || [] + else + @stubs_by_name[name] ||= stubs_for_pattern("#{name}-*.gemspec").select do |s| + s.name == name + end + end + end + + ## + # Finds stub specifications matching a pattern in the record, optionally + # filtering out specs not matching the current platform + + def stubs_for_pattern(pattern, match_platform = true) + installed_stubs = installed_stubs(pattern) + installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform + stubs = installed_stubs + Gem::Specification.default_stubs(pattern) + stubs = stubs.uniq(&:full_name) + Gem::Specification._resort!(stubs) + stubs + end + + ## + # Adds +spec+ to the the record, keeping the collection properly sorted. + + def add_spec(spec) + return if all.include? spec + + all << spec + stubs << spec + (@stubs_by_name[spec.name] ||= []) << spec + + Gem::Specification._resort!(@stubs_by_name[spec.name]) + Gem::Specification._resort!(stubs) + end + + ## + # Removes +spec+ from the record. + + def remove_spec(spec) + all.delete spec.to_spec + stubs.delete spec + (@stubs_by_name[spec.name] || []).delete spec + end + + ## + # Sets the specs known by the record to +specs+. + + def all=(specs) + @stubs_by_name = specs.group_by(&:name) + @all = @stubs = specs + end + + ## + # Return full names of all specs in the record in sorted order. + + def all_names + all.map(&:full_name) + end + + ## + # Return the best specification in the record that contains the file matching +path+. + + def find_by_path(path) + path = path.dup.freeze + spec = @spec_with_requirable_file[path] ||= stubs.find do |s| + s.contains_requirable_file? path + end || NOT_FOUND + + spec.to_spec + end + + ## + # Return the best specification in the record that contains the file + # matching +path+ amongst the specs that are not activated. + + def find_inactive_by_path(path) + stub = stubs.find do |s| + next if s.activated? + s.contains_requirable_file? path + end + stub&.to_spec + end + + ## + # Return the best specification in the record that contains the file + # matching +path+, among those already activated. + + def find_active_stub_by_path(path) + stub = @active_stub_with_requirable_file[path] ||= stubs.find do |s| + s.activated? && s.contains_requirable_file?(path) + end || NOT_FOUND + + stub.this + end + + ## + # Return the latest specs in the record, optionally including prerelease + # specs if +prerelease+ is true. + + def latest_specs(prerelease) + Gem::Specification._latest_specs stubs, prerelease + end + + ## + # Return the latest installed spec in the record for gem +name+. + + def latest_spec_for(name) + latest_specs(true).find {|installed_spec| installed_spec.name == name } + end + + private + + def installed_stubs(pattern) + map_stubs(pattern) do |path, base_dir, gems_dir| + Gem::StubSpecification.gemspec_stub(path, base_dir, gems_dir) + end + end + + def map_stubs(pattern) + @dirs.flat_map do |dir| + base_dir = File.dirname dir + gems_dir = File.join base_dir, "gems" + Gem::Specification.gemspec_stubs_in(dir, pattern) {|path| yield path, base_dir, gems_dir } + end + end + end +end diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index f9f063c7976071..9395e34f75188b 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -967,7 +967,10 @@ def test_self_remove_spec_removed def test_self_stubs_for_lazy_loading Gem.loaded_specs.clear - Gem::Specification.class_variable_set(:@@stubs, nil) + + specification_record = Gem::Specification.specification_record + + specification_record.instance_variable_set(:@stubs, nil) dir_standard_specs = File.join Gem.dir, "specifications" @@ -975,9 +978,9 @@ def test_self_stubs_for_lazy_loading save_gemspec("b-1", "1", dir_standard_specs) {|s| s.name = "b" } assert_equal ["a-1"], Gem::Specification.stubs_for("a").map(&:full_name) - assert_equal 1, Gem::Specification.class_variable_get(:@@stubs_by_name).length + assert_equal 1, specification_record.instance_variable_get(:@stubs_by_name).length assert_equal ["b-1"], Gem::Specification.stubs_for("b").map(&:full_name) - assert_equal 2, Gem::Specification.class_variable_get(:@@stubs_by_name).length + assert_equal 2, specification_record.instance_variable_get(:@stubs_by_name).length assert_equal( Gem::Specification.stubs_for("a").map(&:object_id), @@ -986,7 +989,7 @@ def test_self_stubs_for_lazy_loading Gem.loaded_specs.delete "a" Gem.loaded_specs.delete "b" - Gem::Specification.class_variable_set(:@@stubs, nil) + specification_record.instance_variable_set(:@stubs, nil) end def test_self_stubs_for_no_lazy_loading_after_all_specs_setup From 965cb3ab4c659a1be96b6f26df68b75d85585978 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 6 May 2024 21:00:14 +0200 Subject: [PATCH 026/242] [rubygems/rubygems] Fix issue when cleaning up plugin stubs When `gem uninstall --install-dir ` is run, if the version removed had a plugin, and that same version happened to also be installed globally, then the plugin stub would fail to be removed. https://github.com/rubygems/rubygems/commit/4e2fa0be77 --- lib/rubygems/specification.rb | 6 ++---- lib/rubygems/specification_record.rb | 10 +++++++++ lib/rubygems/uninstaller.rb | 8 ++++--- test/rubygems/test_gem_uninstaller.rb | 31 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 7 deletions(-) diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index e27901923e772d..eb846095293ef7 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -906,9 +906,7 @@ def self.attribute_names # Return the directories that Specification uses to find specs. def self.dirs - @@dirs ||= Gem.path.collect do |dir| - File.join dir, "specifications" - end + @@dirs ||= Gem::SpecificationRecord.dirs_from(Gem.path) end ## @@ -918,7 +916,7 @@ def self.dirs def self.dirs=(dirs) reset - @@dirs = Array(dirs).map {|dir| File.join dir, "specifications" } + @@dirs = Gem::SpecificationRecord.dirs_from(Array(dirs)) end extend Enumerable diff --git a/lib/rubygems/specification_record.rb b/lib/rubygems/specification_record.rb index bdce0b3a099184..812431fa3297a7 100644 --- a/lib/rubygems/specification_record.rb +++ b/lib/rubygems/specification_record.rb @@ -2,6 +2,16 @@ module Gem class SpecificationRecord + def self.dirs_from(paths) + paths.map do |path| + File.join(path, "specifications") + end + end + + def self.from_path(path) + new(dirs_from([path])) + end + def initialize(dirs) @all = nil @stubs = nil diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb index 7dad521a93b9df..e1f82e6a21bdbb 100644 --- a/lib/rubygems/uninstaller.rb +++ b/lib/rubygems/uninstaller.rb @@ -49,7 +49,8 @@ def initialize(gem, options = {}) # TODO: document the valid options @gem = gem @version = options[:version] || Gem::Requirement.default - @gem_home = File.realpath(options[:install_dir] || Gem.dir) + @install_dir = options[:install_dir] + @gem_home = File.realpath(@install_dir || Gem.dir) @force_executables = options[:executables] @force_all = options[:all] @force_ignore = options[:ignore] @@ -69,7 +70,7 @@ def initialize(gem, options = {}) # only add user directory if install_dir is not set @user_install = false - @user_install = options[:user_install] unless options[:install_dir] + @user_install = options[:user_install] unless @install_dir # Optimization: populated during #uninstall @default_specs_matching_uninstall_params = [] @@ -290,7 +291,8 @@ def remove_plugins(spec) # :nodoc: # Regenerates plugin wrappers after removal. def regenerate_plugins - latest = Gem::Specification.latest_spec_for(@spec.name) + specification_record = @install_dir ? Gem::SpecificationRecord.from_path(@install_dir) : Gem::Specification.specification_record + latest = specification_record.latest_spec_for(@spec.name) return if latest.nil? regenerate_plugins_for(latest, plugin_dir_for(@spec)) diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index 20a0005341475c..eb25b505e6cbd3 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -213,6 +213,37 @@ def test_uninstall_with_install_dir_removes_plugins Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall assert File.exist?(plugin_path), "plugin unintentionally removed" + refute File.exist?(install_dir_plugin_path), "plugin not removed" + end + + def test_uninstall_with_install_dir_regenerates_plugins + write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io| + io.write "# do nothing" + end + + @spec.files += %w[lib/rubygems_plugin.rb] + + install_dir = "#{@gemhome}2" + + package = Gem::Package.build(@spec) + + spec_v9 = @spec.dup + spec_v9.version = "9" + package_v9 = Gem::Package.build(spec_v9) + + Gem::Installer.at(package, force: true, install_dir: install_dir).install + Gem::Installer.at(package_v9, force: true, install_dir: install_dir).install + + install_dir_plugin_path = File.join install_dir, "plugins/a_plugin.rb" + assert File.exist?(install_dir_plugin_path), "plugin not written" + + Gem::Specification.dirs = [install_dir] + Gem::Uninstaller.new(@spec.name, version: "9", executables: true, install_dir: install_dir).uninstall + assert File.exist?(install_dir_plugin_path), "plugin unintentionally removed" + + Gem::Specification.dirs = [install_dir] + Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall + refute File.exist?(install_dir_plugin_path), "plugin not removed" end def test_remove_plugins_user_installed From cb1a574e46f38b9598e98a2648969ac818cf2aa0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 02:42:23 +0000 Subject: [PATCH 027/242] Bump github/codeql-action from 3.25.4 to 3.25.5 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.4 to 3.25.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/ccf74c947955fd1cf117aef6a0e4e66191ef6f61...b7cec7526559c32f1616476ff32d17ba4c59b2d6) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yml | 8 ++++---- .github/workflows/scorecards.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8191d6beb29852..5fa6da51700ef7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -80,15 +80,15 @@ jobs: run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb - name: Initialize CodeQL - uses: github/codeql-action/init@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4 + uses: github/codeql-action/init@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4 + uses: github/codeql-action/autobuild@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4 + uses: github/codeql-action/analyze@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: category: '/language:${{ matrix.language }}' upload: False @@ -118,7 +118,7 @@ jobs: continue-on-error: true - name: Upload SARIF - uses: github/codeql-action/upload-sarif@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: sarif-results/${{ matrix.language }}.sarif continue-on-error: true diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 602902e6a18082..bfc7b958635227 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v2.1.27 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v2.1.27 with: sarif_file: results.sarif From e33336cb37b47834c0448921c6c4942e3fa63996 Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Wed, 20 Dec 2023 18:30:06 -0800 Subject: [PATCH 028/242] [ruby/zlib] Avoid allocating intermediary strings when read/readpartial are passed an outbuf This accounts for a significant number of string allocations when reading rubygems, but we can avoid that in many places by only copying into the outbuf when present https://github.com/ruby/zlib/commit/d25ef406c1 --- ext/zlib/zlib.c | 105 ++++++++++++++++++++++++++--------------- test/zlib/test_zlib.rb | 25 +++++++++- 2 files changed, 90 insertions(+), 40 deletions(-) diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index fe030725760aea..b1623546e674b0 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -90,7 +90,7 @@ static void zstream_expand_buffer_into(struct zstream*, unsigned long); static int zstream_expand_buffer_non_stream(struct zstream *z); static void zstream_append_buffer(struct zstream*, const Bytef*, long); static VALUE zstream_detach_buffer(struct zstream*); -static VALUE zstream_shift_buffer(struct zstream*, long); +static VALUE zstream_shift_buffer(struct zstream*, long, VALUE); static void zstream_buffer_ungets(struct zstream*, const Bytef*, unsigned long); static void zstream_buffer_ungetbyte(struct zstream*, int); static void zstream_append_input(struct zstream*, const Bytef*, long); @@ -170,8 +170,8 @@ static void gzfile_check_footer(struct gzfile*, VALUE outbuf); static void gzfile_write(struct gzfile*, Bytef*, long); static long gzfile_read_more(struct gzfile*, VALUE outbuf); static void gzfile_calc_crc(struct gzfile*, VALUE); -static VALUE gzfile_read(struct gzfile*, long); -static VALUE gzfile_read_all(struct gzfile*); +static VALUE gzfile_read(struct gzfile*, long, VALUE); +static VALUE gzfile_read_all(struct gzfile*, VALUE); static void gzfile_ungets(struct gzfile*, const Bytef*, long); static void gzfile_ungetbyte(struct gzfile*, int); static VALUE gzfile_writer_end_run(VALUE); @@ -820,19 +820,31 @@ zstream_detach_buffer(struct zstream *z) } static VALUE -zstream_shift_buffer(struct zstream *z, long len) +zstream_shift_buffer(struct zstream *z, long len, VALUE dst) { - VALUE dst; char *bufptr; long buflen = ZSTREAM_BUF_FILLED(z); if (buflen <= len) { - return zstream_detach_buffer(z); + if (NIL_P(dst) || (!ZSTREAM_IS_FINISHED(z) && !ZSTREAM_IS_GZFILE(z) && + rb_block_given_p())) { + return zstream_detach_buffer(z); + } else { + bufptr = RSTRING_PTR(z->buf); + rb_str_resize(dst, buflen); + memcpy(RSTRING_PTR(dst), bufptr, buflen); + } + buflen = 0; + } else { + bufptr = RSTRING_PTR(z->buf); + if (NIL_P(dst)) { + dst = rb_str_new(bufptr, len); + } else { + rb_str_resize(dst, len); + memcpy(RSTRING_PTR(dst), bufptr, len); + } + buflen -= len; } - - bufptr = RSTRING_PTR(z->buf); - dst = rb_str_new(bufptr, len); - buflen -= len; memmove(bufptr, bufptr + len, buflen); rb_str_set_len(z->buf, buflen); z->stream.next_out = (Bytef*)RSTRING_END(z->buf); @@ -2874,18 +2886,18 @@ gzfile_newstr(struct gzfile *gz, VALUE str) } static long -gzfile_fill(struct gzfile *gz, long len) +gzfile_fill(struct gzfile *gz, long len, VALUE outbuf) { if (len < 0) rb_raise(rb_eArgError, "negative length %ld given", len); if (len == 0) return 0; while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) < len) { - gzfile_read_more(gz, Qnil); + gzfile_read_more(gz, outbuf); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz, Qnil); + gzfile_check_footer(gz, outbuf); } return -1; } @@ -2893,14 +2905,27 @@ gzfile_fill(struct gzfile *gz, long len) } static VALUE -gzfile_read(struct gzfile *gz, long len) +gzfile_read(struct gzfile *gz, long len, VALUE outbuf) { VALUE dst; - len = gzfile_fill(gz, len); - if (len == 0) return rb_str_new(0, 0); - if (len < 0) return Qnil; - dst = zstream_shift_buffer(&gz->z, len); + len = gzfile_fill(gz, len, outbuf); + + if (len < 0) { + if (!NIL_P(outbuf)) + rb_str_resize(outbuf, 0); + return Qnil; + } + if (len == 0) { + if (NIL_P(outbuf)) + return rb_str_new(0, 0); + else { + rb_str_resize(outbuf, 0); + return outbuf; + } + } + + dst = zstream_shift_buffer(&gz->z, len, outbuf); if (!NIL_P(dst)) gzfile_calc_crc(gz, dst); return dst; } @@ -2933,7 +2958,7 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf) rb_raise(rb_eEOFError, "end of file reached"); } - dst = zstream_shift_buffer(&gz->z, len); + dst = zstream_shift_buffer(&gz->z, len, outbuf); gzfile_calc_crc(gz, dst); if (!NIL_P(outbuf)) { @@ -2945,17 +2970,19 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf) } static VALUE -gzfile_read_all(struct gzfile *gz) +gzfile_read_all(struct gzfile *gz, VALUE dst) { - VALUE dst; - while (!ZSTREAM_IS_FINISHED(&gz->z)) { - gzfile_read_more(gz, Qnil); + gzfile_read_more(gz, dst); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz, Qnil); + gzfile_check_footer(gz, dst); } + if (!NIL_P(dst)) { + rb_str_resize(dst, 0); + return dst; + } return rb_str_new(0, 0); } @@ -2993,7 +3020,7 @@ gzfile_getc(struct gzfile *gz) de = (unsigned char *)ds + GZFILE_CBUF_CAPA; (void)rb_econv_convert(gz->ec, &sp, se, &dp, de, ECONV_PARTIAL_INPUT|ECONV_AFTER_OUTPUT); rb_econv_check_error(gz->ec); - dst = zstream_shift_buffer(&gz->z, sp - ss); + dst = zstream_shift_buffer(&gz->z, sp - ss, Qnil); gzfile_calc_crc(gz, dst); rb_str_resize(cbuf, dp - ds); return cbuf; @@ -3001,7 +3028,7 @@ gzfile_getc(struct gzfile *gz) else { buf = gz->z.buf; len = rb_enc_mbclen(RSTRING_PTR(buf), RSTRING_END(buf), gz->enc); - dst = gzfile_read(gz, len); + dst = gzfile_read(gz, len, Qnil); if (NIL_P(dst)) return dst; return gzfile_newstr(gz, dst); } @@ -3909,7 +3936,7 @@ rb_gzreader_s_zcat(int argc, VALUE *argv, VALUE klass) if (!buf) { buf = rb_str_new(0, 0); } - tmpbuf = gzfile_read_all(get_gzfile(obj)); + tmpbuf = gzfile_read_all(get_gzfile(obj), Qnil); rb_str_cat(buf, RSTRING_PTR(tmpbuf), RSTRING_LEN(tmpbuf)); } @@ -4011,19 +4038,19 @@ static VALUE rb_gzreader_read(int argc, VALUE *argv, VALUE obj) { struct gzfile *gz = get_gzfile(obj); - VALUE vlen; + VALUE vlen, outbuf; long len; - rb_scan_args(argc, argv, "01", &vlen); + rb_scan_args(argc, argv, "02", &vlen, &outbuf); if (NIL_P(vlen)) { - return gzfile_read_all(gz); + return gzfile_read_all(gz, outbuf); } len = NUM2INT(vlen); if (len < 0) { rb_raise(rb_eArgError, "negative length %ld given", len); } - return gzfile_read(gz, len); + return gzfile_read(gz, len, outbuf); } /* @@ -4096,7 +4123,7 @@ rb_gzreader_getbyte(VALUE obj) struct gzfile *gz = get_gzfile(obj); VALUE dst; - dst = gzfile_read(gz, 1); + dst = gzfile_read(gz, 1, Qnil); if (!NIL_P(dst)) { dst = INT2FIX((unsigned int)(RSTRING_PTR(dst)[0]) & 0xff); } @@ -4217,7 +4244,7 @@ gzreader_skip_linebreaks(struct gzfile *gz) } } - str = zstream_shift_buffer(&gz->z, n - 1); + str = zstream_shift_buffer(&gz->z, n - 1, Qnil); gzfile_calc_crc(gz, str); } @@ -4238,7 +4265,7 @@ gzreader_charboundary(struct gzfile *gz, long n) if (l < n) { int n_bytes = rb_enc_precise_mbclen(p, e, gz->enc); if (MBCLEN_NEEDMORE_P(n_bytes)) { - if ((l = gzfile_fill(gz, n + MBCLEN_NEEDMORE_LEN(n_bytes))) > 0) { + if ((l = gzfile_fill(gz, n + MBCLEN_NEEDMORE_LEN(n_bytes), Qnil)) > 0) { return l; } } @@ -4290,10 +4317,10 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) if (NIL_P(rs)) { if (limit < 0) { - dst = gzfile_read_all(gz); + dst = gzfile_read_all(gz, Qnil); if (RSTRING_LEN(dst) == 0) return Qnil; } - else if ((n = gzfile_fill(gz, limit)) <= 0) { + else if ((n = gzfile_fill(gz, limit, Qnil)) <= 0) { return Qnil; } else { @@ -4303,7 +4330,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) else { n = limit; } - dst = zstream_shift_buffer(&gz->z, n); + dst = zstream_shift_buffer(&gz->z, n, Qnil); if (NIL_P(dst)) return dst; gzfile_calc_crc(gz, dst); dst = gzfile_newstr(gz, dst); @@ -4330,7 +4357,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) while (ZSTREAM_BUF_FILLED(&gz->z) < rslen) { if (ZSTREAM_IS_FINISHED(&gz->z)) { if (ZSTREAM_BUF_FILLED(&gz->z) > 0) gz->lineno++; - return gzfile_read(gz, rslen); + return gzfile_read(gz, rslen, Qnil); } gzfile_read_more(gz, Qnil); } @@ -4367,7 +4394,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) } gz->lineno++; - dst = gzfile_read(gz, n); + dst = gzfile_read(gz, n, Qnil); if (NIL_P(dst)) return dst; if (rspara) { gzreader_skip_linebreaks(gz); diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index ae4adc21fe2cf8..15e5bd852fe5d3 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -991,6 +991,25 @@ def test_read assert_raise(ArgumentError) { f.read(-1) } assert_equal(str, f.read) end + + Zlib::GzipReader.open(t.path) do |f| + s = "".b + + assert_raise(ArgumentError) { f.read(-1, s) } + + assert_same s, f.read(1, s) + assert_equal "\xE3".b, s + + assert_same s, f.read(2, s) + assert_equal "\x81\x82".b, s + + assert_same s, f.read(6, s) + assert_equal "\u3044\u3046".b, s + + assert_nil f.read(1, s) + assert_equal "".b, s + assert_predicate f, :eof? + end } end @@ -1005,10 +1024,14 @@ def test_readpartial Zlib::GzipReader.open(t.path) do |f| s = "".dup - f.readpartial(3, s) + assert_same s, f.readpartial(3, s) assert("foo".start_with?(s)) assert_raise(ArgumentError) { f.readpartial(-1) } + + assert_same s, f.readpartial(3, s) + + assert_predicate f, :eof? end } end From e3e1e53098f3214ef12849b3441791465ad7c0a5 Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Thu, 11 Apr 2024 14:28:07 -0500 Subject: [PATCH 029/242] [ruby/zlib] Avoid double copying into provided outbuf https://github.com/ruby/zlib/commit/07f44b7546 --- ext/zlib/zlib.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index b1623546e674b0..aad9f8d28a4374 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -2961,11 +2961,6 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf) dst = zstream_shift_buffer(&gz->z, len, outbuf); gzfile_calc_crc(gz, dst); - if (!NIL_P(outbuf)) { - rb_str_resize(outbuf, RSTRING_LEN(dst)); - memcpy(RSTRING_PTR(outbuf), RSTRING_PTR(dst), RSTRING_LEN(dst)); - dst = outbuf; - } return dst; } From e73f6618d5d7a36e0599df14d2423544007bdf7c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 15 May 2024 16:53:27 +0900 Subject: [PATCH 030/242] [rubygems/rubygems] Do not override `@orig_env` It is saved in `setup` and restored in `teardown`. https://github.com/rubygems/rubygems/commit/880c5de667 --- test/rubygems/helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb index 856dc535882435..7014843bba0fdd 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -535,12 +535,12 @@ def without_any_upwards_gemfiles end def with_env(overrides, &block) - @orig_env = ENV.to_h + orig_env = ENV.to_h ENV.replace(overrides) begin block.call ensure - ENV.replace(@orig_env) + ENV.replace(orig_env) end end From c4a5866798589fb78393b694fcd3359f0ec52b16 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 15 May 2024 11:14:57 -0400 Subject: [PATCH 031/242] [ruby/prism] Replace end of file with end-of-input https://github.com/ruby/prism/commit/dd532ded95 --- prism/prism.c | 49 +++++++++++++++------------- prism/templates/src/token_type.c.erb | 2 +- test/prism/errors_test.rb | 14 ++++---- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 4c8b42f8020e8d..480252cc056d7f 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -13552,8 +13552,8 @@ parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_ */ static pm_statements_node_t * parse_statements(pm_parser_t *parser, pm_context_t context) { - // First, skip past any optional terminators that might be at the beginning of - // the statements. + // First, skip past any optional terminators that might be at the beginning + // of the statements. while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)); // If we have a terminator, then we can just return NULL. @@ -13569,20 +13569,20 @@ parse_statements(pm_parser_t *parser, pm_context_t context) { pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION); pm_statements_node_body_append(parser, statements, node); - // If we're recovering from a syntax error, then we need to stop parsing the - // statements now. + // If we're recovering from a syntax error, then we need to stop parsing + // the statements now. if (parser->recovering) { - // If this is the level of context where the recovery has happened, then - // we can mark the parser as done recovering. + // If this is the level of context where the recovery has happened, + // then we can mark the parser as done recovering. if (context_terminator(context, &parser->current)) parser->recovering = false; break; } - // If we have a terminator, then we will parse all consecutive terminators - // and then continue parsing the statements list. + // If we have a terminator, then we will parse all consecutive + // terminators and then continue parsing the statements list. if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { - // If we have a terminator, then we will continue parsing the statements - // list. + // If we have a terminator, then we will continue parsing the + // statements list. while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); if (context_terminator(context, &parser->current)) break; @@ -13590,27 +13590,28 @@ parse_statements(pm_parser_t *parser, pm_context_t context) { continue; } - // At this point we have a list of statements that are not terminated by a - // newline or semicolon. At this point we need to check if we're at the end - // of the statements list. If we are, then we should break out of the loop. + // At this point we have a list of statements that are not terminated by + // a newline or semicolon. At this point we need to check if we're at + // the end of the statements list. If we are, then we should break out + // of the loop. if (context_terminator(context, &parser->current)) break; // At this point, we have a syntax error, because the statement was not // terminated by a newline or semicolon, and we're not at the end of the - // statements list. Ideally we should scan forward to determine if we should - // insert a missing terminator or break out of parsing the statements list - // at this point. + // statements list. Ideally we should scan forward to determine if we + // should insert a missing terminator or break out of parsing the + // statements list at this point. // - // We don't have that yet, so instead we'll do a more naive approach. If we - // were unable to parse an expression, then we will skip past this token and - // continue parsing the statements list. Otherwise we'll add an error and - // continue parsing the statements list. + // We don't have that yet, so instead we'll do a more naive approach. If + // we were unable to parse an expression, then we will skip past this + // token and continue parsing the statements list. Otherwise we'll add + // an error and continue parsing the statements list. if (PM_NODE_TYPE_P(node, PM_MISSING_NODE)) { parser_lex(parser); while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); if (context_terminator(context, &parser->current)) break; - } else if (!accept1(parser, PM_TOKEN_NEWLINE)) { + } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) { // This is an inlined version of accept1 because the error that we // want to add has varargs. If this happens again, we should // probably extract a helper function. @@ -17428,7 +17429,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we didn't find a terminator and we didn't find a right // parenthesis, then this is a syntax error. - if (!terminator_found) { + if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) { PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type)); } @@ -17457,7 +17458,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break; } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { break; - } else { + } else if (!match1(parser, PM_TOKEN_EOF)) { + // If we're at the end of the file, then we're going to add + // an error after this for the ) anyway. PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type)); } } diff --git a/prism/templates/src/token_type.c.erb b/prism/templates/src/token_type.c.erb index a3095eeab8543f..af6a2ad6fe2a86 100644 --- a/prism/templates/src/token_type.c.erb +++ b/prism/templates/src/token_type.c.erb @@ -30,7 +30,7 @@ const char * pm_token_type_human(pm_token_type_t token_type) { switch (token_type) { case PM_TOKEN_EOF: - return "end of file"; + return "end-of-input"; case PM_TOKEN_MISSING: return "missing token"; case PM_TOKEN_NOT_PROVIDED: diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 5d18780dddb564..c6991519c50a12 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -195,8 +195,7 @@ def test_unterminated_s_symbol def test_unterminated_parenthesized_expression assert_errors expression('(1 + 2'), '(1 + 2', [ - ["unexpected end of file, expecting end-of-input", 6..6], - ["unexpected end of file, assuming it is closing the parent top level context", 6..6], + ["unexpected end-of-input, assuming it is closing the parent top level context", 6..6], ["expected a matching `)`", 6..6] ] end @@ -210,8 +209,8 @@ def test_missing_terminator_in_parentheses def test_unterminated_argument_expression assert_errors expression('a %'), 'a %', [ ["invalid `%` token", 2..3], - ["unexpected end of file; expected an expression after the operator", 3..3], - ["unexpected end of file, assuming it is closing the parent top level context", 3..3] + ["unexpected end-of-input; expected an expression after the operator", 3..3], + ["unexpected end-of-input, assuming it is closing the parent top level context", 3..3] ] end @@ -365,7 +364,7 @@ def test_block_beginning_with_brace_and_ending_with_end assert_error_messages "x.each { x end", [ "unexpected 'end', expecting end-of-input", "unexpected 'end', ignoring it", - "unexpected end of file, assuming it is closing the parent top level context", + "unexpected end-of-input, assuming it is closing the parent top level context", "expected a block beginning with `{` to end with `}`" ] end @@ -1482,8 +1481,7 @@ def test_shadow_args_in_lambda assert_errors expression(source), source, [ ["expected a `do` keyword or a `{` to open the lambda block", 3..3], - ["unexpected end of file, expecting end-of-input", 7..7], - ["unexpected end of file, assuming it is closing the parent top level context", 7..7], + ["unexpected end-of-input, assuming it is closing the parent top level context", 7..7], ["expected a lambda block beginning with `do` to end with `end`", 7..7] ] end @@ -1541,7 +1539,7 @@ def test_while_endless_method assert_errors expression(source), source, [ ["expected a predicate expression for the `while` statement", 22..22], - ["unexpected end of file, assuming it is closing the parent top level context", 22..22], + ["unexpected end-of-input, assuming it is closing the parent top level context", 22..22], ["expected an `end` to close the `while` statement", 22..22] ] end From cbd1553800c173113f5ea8f1c5d52b00b283d677 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 15 May 2024 11:47:01 -0400 Subject: [PATCH 032/242] [ruby/prism] Extend the bounds of the invalid global variable name error https://github.com/ruby/prism/commit/89d0d9851a --- prism/prism.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 480252cc056d7f..97d47c8985f109 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -8995,8 +8995,8 @@ lex_global_variable(pm_parser_t *parser) { // If we get here, then we have a $ followed by something that // isn't recognized as a global variable. pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; - size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); - PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start); + const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start); } return PM_TOKEN_GLOBAL_VARIABLE; From 901f4c3fb77968cd9cb2f1dc2d860cf936a2457d Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 15 May 2024 12:49:03 -0400 Subject: [PATCH 033/242] [PRISM] Temporarily exclude IRB test --- test/.excludes-prism/TestIRB/RubyLexTest.rb | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/.excludes-prism/TestIRB/RubyLexTest.rb diff --git a/test/.excludes-prism/TestIRB/RubyLexTest.rb b/test/.excludes-prism/TestIRB/RubyLexTest.rb new file mode 100644 index 00000000000000..a2d0a574525d8e --- /dev/null +++ b/test/.excludes-prism/TestIRB/RubyLexTest.rb @@ -0,0 +1 @@ +exclude(:test_code_block_open_with_should_continue, "https://github.com/ruby/irb/pull/956") From cabc0e093cca984cd6edda0d070b70dc1c125363 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 15 May 2024 11:25:13 -0400 Subject: [PATCH 034/242] [PRISM] Enable TestParse#test_unexpected_token_after_numeric --- test/.excludes-prism/TestParse.rb | 1 - test/ruby/test_parse.rb | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index 7cf5f97dd26631..3b390dbedde8bc 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -6,5 +6,4 @@ exclude(:test_string, "error message format") exclude(:test_truncated_source_line, "truncate error message") exclude(:test_unexpected_eof, "error message format") -exclude(:test_unexpected_token_after_numeric, "error message format") exclude(:test_void_value_in_rhs, "missing raising error for some void value expressions") diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index bd6dabf2c978d4..1e39fbf72f201e 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1336,9 +1336,9 @@ def test_unexpected_token_error end def test_unexpected_token_after_numeric - assert_syntax_error('0000xyz', /^ \^~~\Z/) - assert_syntax_error('1.2i1.1', /^ \^~~\Z/) - assert_syntax_error('1.2.3', /^ \^~\Z/) + assert_syntax_error('0000xyz', /(^|\| ) \^~~(?!~)/) + assert_syntax_error('1.2i1.1', /(^|\| ) \^~~(?!~)/) + assert_syntax_error('1.2.3', /(^|\| ) \^~(?!~)/) assert_syntax_error('1.', /unexpected end-of-input/) assert_syntax_error('1e', /expecting end-of-input/) end From be239cc7bc50d3882bc3dec2bbe1f4f0c1e797fa Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 15 May 2024 11:27:03 -0400 Subject: [PATCH 035/242] [PRISM] Enable TestParse#test_unexpected_eof --- test/ruby/test_parse.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 1e39fbf72f201e..c77cdc56739b04 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1390,7 +1390,7 @@ def test_eof_in_def end def test_unexpected_eof - assert_syntax_error('unless', /^ \^\Z/) + assert_syntax_error('unless', /(^|\| ) \^(?!~)/) end def test_location_of_invalid_token From cd8b28f0e7661498bbae259982809fc7f50e0581 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 15 May 2024 11:40:52 -0400 Subject: [PATCH 036/242] [PRISM] Enable test_location_of_invalid_token --- test/.excludes-prism/TestParse.rb | 2 -- test/ruby/test_parse.rb | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index 3b390dbedde8bc..1c2349cfe51681 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,9 +1,7 @@ exclude(:test_global_variable, "error message format") exclude(:test_invalid_char, "error message format") -exclude(:test_location_of_invalid_token, "error message format") exclude(:test_percent, "error message format") exclude(:test_question, "error message format") exclude(:test_string, "error message format") exclude(:test_truncated_source_line, "truncate error message") -exclude(:test_unexpected_eof, "error message format") exclude(:test_void_value_in_rhs, "missing raising error for some void value expressions") diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index c77cdc56739b04..0035f779570e6d 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1394,7 +1394,7 @@ def test_unexpected_eof end def test_location_of_invalid_token - assert_syntax_error('class xxx end', /^ \^~~\Z/) + assert_syntax_error('class xxx end', /(^|\| ) \^~~(?!~)/) end def test_whitespace_warning From 2aed14d65b6240d03fd9084792855e868e793b2d Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 15 May 2024 11:48:20 -0400 Subject: [PATCH 037/242] [PRISM] Enable TestParse#test_global_variable --- test/.excludes-prism/TestParse.rb | 1 - test/ruby/test_parse.rb | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index 1c2349cfe51681..8ff7dba1f4cdcd 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,4 +1,3 @@ -exclude(:test_global_variable, "error message format") exclude(:test_invalid_char, "error message format") exclude(:test_percent, "error message format") exclude(:test_question, "error message format") diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 0035f779570e6d..63539270fc0793 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -875,7 +875,8 @@ def test_global_variable $test_parse_foobarbazqux = nil assert_equal(nil, $&) assert_equal(nil, eval('alias $& $preserve_last_match')) - assert_syntax_error('a = $#', /as a global variable name\na = \$\#\n \^~$/) + assert_syntax_error('a = $#', /as a global variable name/) + assert_syntax_error('a = $#', /a = \$\#\n(^|.+?\| ) \^~(?!~)/) end def test_invalid_instance_variable From 9d69619623ec6b86c464b7cac911b7201f74dab7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 20 Apr 2024 19:56:58 +0900 Subject: [PATCH 038/242] [rubygems/rubygems] Clean up generated and copied files > leaving the files after gem rebuild was intentional, for local > inspection, but the test suite should be made to clean up after > itself independently of that. https://github.com/rubygems/rubygems/pull/4913#issuecomment-2081710691 https://github.com/rubygems/rubygems/commit/185a2091fb --- test/rubygems/test_gem_commands_rebuild_command.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/rubygems/test_gem_commands_rebuild_command.rb b/test/rubygems/test_gem_commands_rebuild_command.rb index 5e8c797e2d11ad..3b7927c44e0308 100644 --- a/test/rubygems/test_gem_commands_rebuild_command.rb +++ b/test/rubygems/test_gem_commands_rebuild_command.rb @@ -105,7 +105,7 @@ def util_test_rebuild_gem(gem, args, original_gem_file, gemspec_file, timestamp) assert_equal old_spec.name, new_spec.name assert_equal old_spec.summary, new_spec.summary - reproduced + [reproduced, original] end def test_build_is_reproducible @@ -134,12 +134,21 @@ def test_build_is_reproducible # also testing that `gem rebuild` overrides the value. ENV["SOURCE_DATE_EPOCH"] = Time.new(2007, 8, 9, 10, 11, 12).to_s - rebuild_gem_file = util_test_rebuild_gem(@gem, [@gem_name, @gem_version], original_gem_file, gemspec_file, timestamp) + rebuild_gem_file, saved_gem_file = + util_test_rebuild_gem(@gem, [@gem_name, @gem_version], original_gem_file, gemspec_file, timestamp) rebuild_contents = File.read(rebuild_gem_file) assert_equal build_contents, rebuild_contents ensure ENV["SOURCE_DATE_EPOCH"] = epoch + if rebuild_gem_file + File.unlink(rebuild_gem_file) + dir = File.dirname(rebuild_gem_file) + Dir.rmdir(dir) + File.unlink(saved_gem_file) + Dir.rmdir(File.dirname(saved_gem_file)) + Dir.rmdir(File.dirname(dir)) + end end end From 2dd46bb82ffc4dff01d7ea70922f0e407acafb4e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 16 May 2024 16:22:17 +0900 Subject: [PATCH 039/242] [Bug #20468] Fix safe navigation in `for` variable --- compile.c | 21 +++++++++++++++------ test/ruby/test_syntax.rb | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/compile.c b/compile.c index c319b72b524d78..5167f4d61c3b75 100644 --- a/compile.c +++ b/compile.c @@ -5380,12 +5380,17 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node)); + bool safenav_call = false; LINK_ELEMENT *insn_element = LAST_ELEMENT(pre); iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */ ASSUME(iobj); - ELEM_REMOVE(LAST_ELEMENT(pre)); - ELEM_REMOVE((LINK_ELEMENT *)iobj); - pre->last = iobj->link.prev; + ELEM_REMOVE(insn_element); + if (!IS_INSN_ID(iobj, send)) { + safenav_call = true; + iobj = (INSN *)get_prev_insn(iobj); + ELEM_INSERT_NEXT(&iobj->link, insn_element); + } + (pre->last = iobj->link.prev)->next = 0; const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0); int argc = vm_ci_argc(ci) + 1; @@ -5404,7 +5409,9 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const return COMPILE_NG; } - ADD_ELEM(lhs, (LINK_ELEMENT *)iobj); + iobj->link.prev = lhs->last; + lhs->last->next = &iobj->link; + for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next); if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) { int argc = vm_ci_argc(ci); bool dupsplat = false; @@ -5437,9 +5444,11 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const } INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1)); } - ADD_INSN(lhs, line_node, pop); - if (argc != 1) { + if (!safenav_call) { ADD_INSN(lhs, line_node, pop); + if (argc != 1) { + ADD_INSN(lhs, line_node, pop); + } } for (int i=0; i < argc; i++) { ADD_INSN(post, line_node, pop); diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 7b894eee79ec27..edc3a3cec3fb9d 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -1248,6 +1248,20 @@ def test_safe_call_in_massign_lhs assert_syntax_error("a&.x,=0", /multiple assignment destination/) end + def test_safe_call_in_for_variable + assert_valid_syntax("for x&.bar in []; end") + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + foo = nil + for foo&.bar in [1]; end + assert_nil(foo) + + foo = Struct.new(:bar).new + for foo&.bar in [1]; end + assert_equal(1, foo.bar) + end; + end + def test_no_warning_logop_literal assert_warning("") do eval("true||raise;nil") From 4db76d198b4a152c416d657e97959ade3e50a185 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 13 May 2024 11:59:55 +0200 Subject: [PATCH 040/242] [rubygems/rubygems] Add a FreeBSD platform helper https://github.com/rubygems/rubygems/commit/535c83d5be --- lib/rubygems.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/rubygems.rb b/lib/rubygems.rb index ad7ab1075636a8..ac225ca70ae6a6 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -1012,6 +1012,13 @@ def self.solaris_platform? RUBY_PLATFORM.include?("solaris") end + ## + # Is this platform FreeBSD + + def self.freebsd_platform? + RbConfig::CONFIG["host_os"].to_s.include?("bsd") + end + ## # Load +plugins+ as Ruby files From 9fd41480fbdf7c137b2417851b517aeda33393a0 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 13 May 2024 12:04:59 +0200 Subject: [PATCH 041/242] [rubygems/rubygems] Use RubyGems freebsd helper https://github.com/rubygems/rubygems/commit/5d03a346ab --- lib/bundler/cli/install.rb | 2 +- lib/bundler/rubygems_ext.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index 6c102d537dfd0f..a233d5d2e5a183 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -14,7 +14,7 @@ def run Bundler.self_manager.install_locked_bundler_and_restart_with_it_if_needed - Bundler::SharedHelpers.set_env "RB_USER_INSTALL", "1" if Bundler::FREEBSD + Bundler::SharedHelpers.set_env "RB_USER_INSTALL", "1" if Gem.freebsd_platform? # Disable color in deployment mode Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment] diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index 14b870321313c0..18180a81a1c3f9 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -23,6 +23,13 @@ end module Gem + # Can be removed once RubyGems 3.5.11 support is dropped + unless Gem.respond_to?(:freebsd_platform?) + def self.freebsd_platform? + RbConfig::CONFIG["host_os"].to_s.include?("bsd") + end + end + require "rubygems/specification" class Specification From 29a9ab0ba674f3739e453b2c7a68c138c8802b99 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Mon, 13 May 2024 12:05:24 +0200 Subject: [PATCH 042/242] [rubygems/rubygems] Deprecate Bundler constants https://github.com/rubygems/rubygems/commit/6166e4bce1 --- lib/bundler/constants.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/bundler/constants.rb b/lib/bundler/constants.rb index bcbd228b187650..9564771e78afbb 100644 --- a/lib/bundler/constants.rb +++ b/lib/bundler/constants.rb @@ -4,6 +4,11 @@ module Bundler WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/ + deprecate_constant :WINDOWS + FREEBSD = RbConfig::CONFIG["host_os"].to_s.include?("bsd") - NULL = File::NULL + deprecate_constant :FREEBSD + + NULL = File::NULL + deprecate_constant :NULL end From 7857061ecd740ef88d8451c5119827e91d57a4fd Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Tue, 14 May 2024 20:07:15 +0200 Subject: [PATCH 043/242] [rubygems/rubygems] Fix regression when caching gems from secondary sources If `cache_all_platforms` setting is enabled, the secondary source was no longer considering cached gems. That means that if the remote secondary source has removed its gems, then this was now resulting in an error while before the previously cached gem from the source would still be used. This commit restores previous behavior. https://github.com/rubygems/rubygems/commit/2d2cd00255 --- lib/bundler/spec_set.rb | 1 + spec/bundler/commands/cache_spec.rb | 60 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index 2933d284500668..8e1130e40ed81f 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -129,6 +129,7 @@ def materialize(deps) def materialized_for_all_platforms @specs.map do |s| next s unless s.is_a?(LazySpecification) + s.source.cached! s.source.remote! spec = s.materialize_for_installation raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb index 70e2c84961d377..37d8b3ac1a47ff 100644 --- a/spec/bundler/commands/cache_spec.rb +++ b/spec/bundler/commands/cache_spec.rb @@ -386,6 +386,66 @@ expect(the_bundle).to include_gems "rack 1.0.0" end + it "uses cached gems for secondary sources when cache_all_platforms configured" do + build_repo4 do + build_gem "foo", "1.0.0" do |s| + s.platform = "x86_64-linux" + end + + build_gem "foo", "1.0.0" do |s| + s.platform = "arm64-darwin" + end + end + + gemfile <<~G + source "https://gems.repo2" + + source "https://gems.repo4" do + gem "foo" + end + G + + lockfile <<~L + GEM + remote: https://gems.repo2/ + specs: + + GEM + remote: https://gems.repo4/ + specs: + foo (1.0.0-x86_64-linux) + foo (1.0.0-arm64-darwin) + + PLATFORMS + arm64-darwin + ruby + x86_64-linux + + DEPENDENCIES + foo + + BUNDLED WITH + #{Bundler::VERSION} + L + + simulate_platform "x86_64-linux" do + bundle "config set cache_all_platforms true" + bundle "config set path vendor/bundle" + bundle :cache, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + build_repo4 do + # simulate removal of all remote gems + end + + # delete compact index cache + FileUtils.rm_rf home(".bundle/cache/compact_index") + + bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + expect(the_bundle).to include_gems "foo 1.0.0 x86_64-linux" + end + end + it "does not reinstall already-installed gems" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" From 847fc9af1805d8d2277827eb19c0cb4df8509973 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Thu, 9 May 2024 20:16:32 +0200 Subject: [PATCH 044/242] [rubygems/rubygems] Fix error message calling method removed a long time ago https://github.com/rubygems/rubygems/commit/5fbe5e43d6 --- lib/rubygems/commands/uninstall_command.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb index 2a77ec72cf1033..283bc96ce3e8cd 100644 --- a/lib/rubygems/commands/uninstall_command.rb +++ b/lib/rubygems/commands/uninstall_command.rb @@ -184,7 +184,7 @@ def uninstall_gem(gem_name) rescue Gem::GemNotInHomeException => e spec = e.spec alert("In order to remove #{spec.name}, please execute:\n" \ - "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}") + "\tgem uninstall #{spec.name} --install-dir=#{spec.base_dir}") rescue Gem::UninstallError => e spec = e.spec alert_error("Error: unable to successfully uninstall '#{spec.name}' which is " \ From b19693f0ae5077c074ab5a1799cae91e1ef5d7e6 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Thu, 9 May 2024 20:17:44 +0200 Subject: [PATCH 045/242] [rubygems/rubygems] Improve uninstaller tests Tests have two copies of b-2 installed, one in default home and another in user home. But only the one in default home was visible because of not reloading paths. The user install test was working by chance. https://github.com/rubygems/rubygems/commit/667537a08f --- test/rubygems/test_gem_uninstaller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index eb25b505e6cbd3..2e4fca011216fb 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -429,7 +429,7 @@ def test_uninstall_not_ok end def test_uninstall_user_install - @user_spec = Gem::Specification.find_by_name "b" + Gem::Specification.dirs = [Gem.user_dir] uninstaller = Gem::Uninstaller.new(@user_spec.name, executables: true, From 5111b3d479c413c3c14df1c4b4dc5d22c9270edb Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Thu, 9 May 2024 20:51:23 +0200 Subject: [PATCH 046/242] [rubygems/rubygems] Fix `gem uninstall --user-install` for symlinked HOME's https://github.com/rubygems/rubygems/commit/7f0706a897 --- lib/rubygems/uninstaller.rb | 5 +++-- test/rubygems/test_gem_uninstaller.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb index e1f82e6a21bdbb..4d72f6fd0a2cfd 100644 --- a/lib/rubygems/uninstaller.rb +++ b/lib/rubygems/uninstaller.rb @@ -51,6 +51,7 @@ def initialize(gem, options = {}) @version = options[:version] || Gem::Requirement.default @install_dir = options[:install_dir] @gem_home = File.realpath(@install_dir || Gem.dir) + @user_dir = File.exist?(Gem.user_dir) ? File.realpath(Gem.user_dir) : Gem.user_dir @force_executables = options[:executables] @force_all = options[:all] @force_ignore = options[:ignore] @@ -105,7 +106,7 @@ def uninstall list, other_repo_specs = list.partition do |spec| @gem_home == spec.base_dir || - (@user_install && spec.base_dir == Gem.user_dir) + (@user_install && spec.base_dir == @user_dir) end list.sort! @@ -239,7 +240,7 @@ def remove_all(list) def remove(spec) unless path_ok?(@gem_home, spec) || - (@user_install && path_ok?(Gem.user_dir, spec)) + (@user_install && path_ok?(@user_dir, spec)) e = Gem::GemNotInHomeException.new \ "Gem '#{spec.full_name}' is not installed in directory #{@gem_home}" e.spec = spec diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index 2e4fca011216fb..aa5ab0ed67684d 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -453,6 +453,32 @@ def test_uninstall_user_install assert_same uninstaller, @post_uninstall_hook_arg end + def test_uninstall_user_install_with_symlinked_home + pend "Symlinks not supported or not enabled" unless symlink_supported? + + Gem::Specification.dirs = [Gem.user_dir] + + symlinked_home = File.join(@tempdir, "new-home") + FileUtils.ln_s(Gem.user_home, symlinked_home) + + ENV["HOME"] = symlinked_home + Gem.instance_variable_set(:@user_home, nil) + Gem.instance_variable_set(:@data_home, nil) + + uninstaller = Gem::Uninstaller.new(@user_spec.name, + executables: true, + user_install: true, + force: true) + + gem_dir = File.join @user_spec.gem_dir + + assert_path_exist gem_dir + + uninstaller.uninstall + + assert_path_not_exist gem_dir + end + def test_uninstall_wrong_repo Dir.mkdir "#{@gemhome}2" Gem.use_paths "#{@gemhome}2", [@gemhome] From d076101af93b28beff15eaa1fcb92ce4f070e9cc Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 16 May 2024 21:25:52 +0900 Subject: [PATCH 047/242] Fix dump of NODE [ci skip] --- misc/lldb_rb/utils.py | 451 +++++++++++++++++++++--------------------- 1 file changed, 230 insertions(+), 221 deletions(-) diff --git a/misc/lldb_rb/utils.py b/misc/lldb_rb/utils.py index 86b5bdda2d8a13..1b0e4f9f2ab0bb 100644 --- a/misc/lldb_rb/utils.py +++ b/misc/lldb_rb/utils.py @@ -60,6 +60,9 @@ def inspect(self, val): rbUndef = self.ruby_globals["RUBY_Qundef"] rbImmediateMask = self.ruby_globals["RUBY_IMMEDIATE_MASK"] + if self.inspect_node(val): + return + num = val.GetValueAsSigned() if num == rbFalse: print('false', file=self.result) @@ -245,227 +248,6 @@ def inspect(self, val): print("T_DATA:", file=self.result) self._append_expression("*(struct RData *) %0#x" % val.GetValueAsUnsigned()) - elif rval.is_type("RUBY_T_NODE"): - tRNode = self.target.FindFirstType("struct RNode").GetPointerType() - rbNodeTypeMask = self.ruby_globals["RUBY_NODE_TYPEMASK"] - rbNodeTypeShift = self.ruby_globals["RUBY_NODE_TYPESHIFT"] - - nd_type = (rval.flags & rbNodeTypeMask) >> rbNodeTypeShift - val = val.Cast(tRNode) - - self._append_expression("(node_type) %d" % nd_type) - - if nd_type == self.ruby_globals["NODE_SCOPE"]: - self._append_expression("*(struct RNode_SCOPE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_BLOCK"]: - self._append_expression("*(struct RNode_BLOCK *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_IF"]: - self._append_expression("*(struct RNode_IF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_UNLESS"]: - self._append_expression("*(struct RNode_UNLESS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CASE"]: - self._append_expression("*(struct RNode_CASE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CASE2"]: - self._append_expression("*(struct RNode_CASE2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CASE3"]: - self._append_expression("*(struct RNode_CASE3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_WHEN"]: - self._append_expression("*(struct RNode_WHEN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_IN"]: - self._append_expression("*(struct RNode_IN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_WHILE"]: - self._append_expression("*(struct RNode_WHILE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_UNTIL"]: - self._append_expression("*(struct RNode_UNTIL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ITER"]: - self._append_expression("*(struct RNode_ITER *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FOR"]: - self._append_expression("*(struct RNode_FOR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FOR_MASGN"]: - self._append_expression("*(struct RNode_FOR_MASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_BREAK"]: - self._append_expression("*(struct RNode_BREAK *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_NEXT"]: - self._append_expression("*(struct RNode_NEXT *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_REDO"]: - self._append_expression("*(struct RNode_REDO *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_RETRY"]: - self._append_expression("*(struct RNode_RETRY *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_BEGIN"]: - self._append_expression("*(struct RNode_BEGIN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_RESCUE"]: - self._append_expression("*(struct RNode_RESCUE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_RESBODY"]: - self._append_expression("*(struct RNode_RESBODY *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ENSURE"]: - self._append_expression("*(struct RNode_ENSURE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_AND"]: - self._append_expression("*(struct RNode_AND *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OR"]: - self._append_expression("*(struct RNode_OR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MASGN"]: - self._append_expression("*(struct RNode_MASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LASGN"]: - self._append_expression("*(struct RNode_LASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DASGN"]: - self._append_expression("*(struct RNode_DASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_GASGN"]: - self._append_expression("*(struct RNode_GASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_IASGN"]: - self._append_expression("*(struct RNode_IASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CDECL"]: - self._append_expression("*(struct RNode_CDECL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CVASGN"]: - self._append_expression("*(struct RNode_CVASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_ASGN1"]: - self._append_expression("*(struct RNode_OP_ASGN1 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_ASGN2"]: - self._append_expression("*(struct RNode_OP_ASGN2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_ASGN_AND"]: - self._append_expression("*(struct RNode_OP_ASGN_AND *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_ASGN_OR"]: - self._append_expression("*(struct RNode_OP_ASGN_OR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_CDECL"]: - self._append_expression("*(struct RNode_OP_CDECL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CALL"]: - self._append_expression("*(struct RNode_CALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OPCALL"]: - self._append_expression("*(struct RNode_OPCALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FCALL"]: - self._append_expression("*(struct RNode_FCALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_VCALL"]: - self._append_expression("*(struct RNode_VCALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_QCALL"]: - self._append_expression("*(struct RNode_QCALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_SUPER"]: - self._append_expression("*(struct RNode_SUPER *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ZSUPER"]: - self._append_expression("*(struct RNode_ZSUPER *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LIST"]: - self._append_expression("*(struct RNode_LIST *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ZLIST"]: - self._append_expression("*(struct RNode_ZLIST *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_HASH"]: - self._append_expression("*(struct RNode_HASH *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_RETURN"]: - self._append_expression("*(struct RNode_RETURN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_YIELD"]: - self._append_expression("*(struct RNode_YIELD *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LVAR"]: - self._append_expression("*(struct RNode_LVAR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DVAR"]: - self._append_expression("*(struct RNode_DVAR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_GVAR"]: - self._append_expression("*(struct RNode_GVAR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CONST"]: - self._append_expression("*(struct RNode_CONST *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CVAR"]: - self._append_expression("*(struct RNode_CVAR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_NTH_REF"]: - self._append_expression("*(struct RNode_NTH_REF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_BACK_REF"]: - self._append_expression("*(struct RNode_BACK_REF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MATCH"]: - self._append_expression("*(struct RNode_MATCH *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MATCH2"]: - self._append_expression("*(struct RNode_MATCH2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MATCH3"]: - self._append_expression("*(struct RNode_MATCH3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_STR"]: - self._append_expression("*(struct RNode_STR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DSTR"]: - self._append_expression("*(struct RNode_DSTR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_XSTR"]: - self._append_expression("*(struct RNode_XSTR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DXSTR"]: - self._append_expression("*(struct RNode_DXSTR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_EVSTR"]: - self._append_expression("*(struct RNode_EVSTR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_REGX"]: - self._append_expression("*(struct RNode_REGX *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DREGX"]: - self._append_expression("*(struct RNode_DREGX *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ONCE"]: - self._append_expression("*(struct RNode_ONCE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARGS"]: - self._append_expression("*(struct RNode_ARGS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARGS_AUX"]: - self._append_expression("*(struct RNode_ARGS_AUX *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OPT_ARG"]: - self._append_expression("*(struct RNode_OPT_ARG *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_KW_ARG"]: - self._append_expression("*(struct RNode_KW_ARG *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_POSTARG"]: - self._append_expression("*(struct RNode_POSTARG *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARGSCAT"]: - self._append_expression("*(struct RNode_ARGSCAT *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARGSPUSH"]: - self._append_expression("*(struct RNode_ARGSPUSH *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_SPLAT"]: - self._append_expression("*(struct RNode_SPLAT *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DEFN"]: - self._append_expression("*(struct RNode_DEFN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DEFS"]: - self._append_expression("*(struct RNode_DEFS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ALIAS"]: - self._append_expression("*(struct RNode_ALIAS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_VALIAS"]: - self._append_expression("*(struct RNode_VALIAS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_UNDEF"]: - self._append_expression("*(struct RNode_UNDEF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CLASS"]: - self._append_expression("*(struct RNode_CLASS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MODULE"]: - self._append_expression("*(struct RNode_MODULE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_SCLASS"]: - self._append_expression("*(struct RNode_SCLASS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_COLON2"]: - self._append_expression("*(struct RNode_COLON2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_COLON3"]: - self._append_expression("*(struct RNode_COLON3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DOT2"]: - self._append_expression("*(struct RNode_DOT2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DOT3"]: - self._append_expression("*(struct RNode_DOT3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FLIP2"]: - self._append_expression("*(struct RNode_FLIP2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FLIP3"]: - self._append_expression("*(struct RNode_FLIP3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_SELF"]: - self._append_expression("*(struct RNode_SELF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_NIL"]: - self._append_expression("*(struct RNode_NIL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_TRUE"]: - self._append_expression("*(struct RNode_TRUE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FALSE"]: - self._append_expression("*(struct RNode_FALSE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ERRINFO"]: - self._append_expression("*(struct RNode_ERRINFO *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DEFINED"]: - self._append_expression("*(struct RNode_DEFINED *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_POSTEXE"]: - self._append_expression("*(struct RNode_POSTEXE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DSYM"]: - self._append_expression("*(struct RNode_DSYM *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ATTRASGN"]: - self._append_expression("*(struct RNode_ATTRASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LAMBDA"]: - self._append_expression("*(struct RNode_LAMBDA *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARYPTN"]: - self._append_expression("*(struct RNode_ARYPTN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_HSHPTN"]: - self._append_expression("*(struct RNode_HSHPTN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FNDPTN"]: - self._append_expression("*(struct RNode_FNDPTN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ERROR"]: - self._append_expression("*(struct RNode_ERROR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LINE"]: - self._append_expression("*(struct RNode_LINE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FILE"]: - self._append_expression("*(struct RNode_FILE *) %0#x" % val.GetValueAsUnsigned()) - else: - self._append_expression("*(struct RNode *) %0#x" % val.GetValueAsUnsigned()) - elif rval.is_type("RUBY_T_IMEMO"): imemo_type = ((rval.flags >> self.ruby_globals["RUBY_FL_USHIFT"]) & IMEMO_MASK) @@ -492,3 +274,230 @@ def inspect(self, val): else: print("Not-handled type %0#x" % rval.type, file=self.result) print(val, file=self.result) + + def inspect_node(self, val): + tRNode = self.target.FindFirstType("struct RNode").GetPointerType() + + # if val.GetType() != tRNode: does not work for unknown reason + + if val.GetType().GetPointeeType().name != "NODE": + return False + + rbNodeTypeMask = self.ruby_globals["RUBY_NODE_TYPEMASK"] + rbNodeTypeShift = self.ruby_globals["RUBY_NODE_TYPESHIFT"] + flags = val.Cast(tRNode).GetChildMemberWithName("flags").GetValueAsUnsigned() + nd_type = (flags & rbNodeTypeMask) >> rbNodeTypeShift + + self._append_expression("(node_type) %d" % nd_type) + + if nd_type == self.ruby_globals["NODE_SCOPE"]: + self._append_expression("*(struct RNode_SCOPE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_BLOCK"]: + self._append_expression("*(struct RNode_BLOCK *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_IF"]: + self._append_expression("*(struct RNode_IF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_UNLESS"]: + self._append_expression("*(struct RNode_UNLESS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CASE"]: + self._append_expression("*(struct RNode_CASE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CASE2"]: + self._append_expression("*(struct RNode_CASE2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CASE3"]: + self._append_expression("*(struct RNode_CASE3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_WHEN"]: + self._append_expression("*(struct RNode_WHEN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_IN"]: + self._append_expression("*(struct RNode_IN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_WHILE"]: + self._append_expression("*(struct RNode_WHILE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_UNTIL"]: + self._append_expression("*(struct RNode_UNTIL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ITER"]: + self._append_expression("*(struct RNode_ITER *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FOR"]: + self._append_expression("*(struct RNode_FOR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FOR_MASGN"]: + self._append_expression("*(struct RNode_FOR_MASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_BREAK"]: + self._append_expression("*(struct RNode_BREAK *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_NEXT"]: + self._append_expression("*(struct RNode_NEXT *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_REDO"]: + self._append_expression("*(struct RNode_REDO *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_RETRY"]: + self._append_expression("*(struct RNode_RETRY *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_BEGIN"]: + self._append_expression("*(struct RNode_BEGIN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_RESCUE"]: + self._append_expression("*(struct RNode_RESCUE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_RESBODY"]: + self._append_expression("*(struct RNode_RESBODY *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ENSURE"]: + self._append_expression("*(struct RNode_ENSURE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_AND"]: + self._append_expression("*(struct RNode_AND *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OR"]: + self._append_expression("*(struct RNode_OR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MASGN"]: + self._append_expression("*(struct RNode_MASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LASGN"]: + self._append_expression("*(struct RNode_LASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DASGN"]: + self._append_expression("*(struct RNode_DASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_GASGN"]: + self._append_expression("*(struct RNode_GASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_IASGN"]: + self._append_expression("*(struct RNode_IASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CDECL"]: + self._append_expression("*(struct RNode_CDECL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CVASGN"]: + self._append_expression("*(struct RNode_CVASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_ASGN1"]: + self._append_expression("*(struct RNode_OP_ASGN1 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_ASGN2"]: + self._append_expression("*(struct RNode_OP_ASGN2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_ASGN_AND"]: + self._append_expression("*(struct RNode_OP_ASGN_AND *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_ASGN_OR"]: + self._append_expression("*(struct RNode_OP_ASGN_OR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_CDECL"]: + self._append_expression("*(struct RNode_OP_CDECL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CALL"]: + self._append_expression("*(struct RNode_CALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OPCALL"]: + self._append_expression("*(struct RNode_OPCALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FCALL"]: + self._append_expression("*(struct RNode_FCALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_VCALL"]: + self._append_expression("*(struct RNode_VCALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_QCALL"]: + self._append_expression("*(struct RNode_QCALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_SUPER"]: + self._append_expression("*(struct RNode_SUPER *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ZSUPER"]: + self._append_expression("*(struct RNode_ZSUPER *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LIST"]: + self._append_expression("*(struct RNode_LIST *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ZLIST"]: + self._append_expression("*(struct RNode_ZLIST *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_HASH"]: + self._append_expression("*(struct RNode_HASH *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_RETURN"]: + self._append_expression("*(struct RNode_RETURN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_YIELD"]: + self._append_expression("*(struct RNode_YIELD *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LVAR"]: + self._append_expression("*(struct RNode_LVAR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DVAR"]: + self._append_expression("*(struct RNode_DVAR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_GVAR"]: + self._append_expression("*(struct RNode_GVAR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CONST"]: + self._append_expression("*(struct RNode_CONST *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CVAR"]: + self._append_expression("*(struct RNode_CVAR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_NTH_REF"]: + self._append_expression("*(struct RNode_NTH_REF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_BACK_REF"]: + self._append_expression("*(struct RNode_BACK_REF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MATCH"]: + self._append_expression("*(struct RNode_MATCH *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MATCH2"]: + self._append_expression("*(struct RNode_MATCH2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MATCH3"]: + self._append_expression("*(struct RNode_MATCH3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_STR"]: + self._append_expression("*(struct RNode_STR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DSTR"]: + self._append_expression("*(struct RNode_DSTR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_XSTR"]: + self._append_expression("*(struct RNode_XSTR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DXSTR"]: + self._append_expression("*(struct RNode_DXSTR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_EVSTR"]: + self._append_expression("*(struct RNode_EVSTR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_REGX"]: + self._append_expression("*(struct RNode_REGX *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DREGX"]: + self._append_expression("*(struct RNode_DREGX *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ONCE"]: + self._append_expression("*(struct RNode_ONCE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARGS"]: + self._append_expression("*(struct RNode_ARGS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARGS_AUX"]: + self._append_expression("*(struct RNode_ARGS_AUX *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OPT_ARG"]: + self._append_expression("*(struct RNode_OPT_ARG *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_KW_ARG"]: + self._append_expression("*(struct RNode_KW_ARG *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_POSTARG"]: + self._append_expression("*(struct RNode_POSTARG *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARGSCAT"]: + self._append_expression("*(struct RNode_ARGSCAT *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARGSPUSH"]: + self._append_expression("*(struct RNode_ARGSPUSH *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_SPLAT"]: + self._append_expression("*(struct RNode_SPLAT *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DEFN"]: + self._append_expression("*(struct RNode_DEFN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DEFS"]: + self._append_expression("*(struct RNode_DEFS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ALIAS"]: + self._append_expression("*(struct RNode_ALIAS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_VALIAS"]: + self._append_expression("*(struct RNode_VALIAS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_UNDEF"]: + self._append_expression("*(struct RNode_UNDEF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CLASS"]: + self._append_expression("*(struct RNode_CLASS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MODULE"]: + self._append_expression("*(struct RNode_MODULE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_SCLASS"]: + self._append_expression("*(struct RNode_SCLASS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_COLON2"]: + self._append_expression("*(struct RNode_COLON2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_COLON3"]: + self._append_expression("*(struct RNode_COLON3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DOT2"]: + self._append_expression("*(struct RNode_DOT2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DOT3"]: + self._append_expression("*(struct RNode_DOT3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FLIP2"]: + self._append_expression("*(struct RNode_FLIP2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FLIP3"]: + self._append_expression("*(struct RNode_FLIP3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_SELF"]: + self._append_expression("*(struct RNode_SELF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_NIL"]: + self._append_expression("*(struct RNode_NIL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_TRUE"]: + self._append_expression("*(struct RNode_TRUE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FALSE"]: + self._append_expression("*(struct RNode_FALSE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ERRINFO"]: + self._append_expression("*(struct RNode_ERRINFO *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DEFINED"]: + self._append_expression("*(struct RNode_DEFINED *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_POSTEXE"]: + self._append_expression("*(struct RNode_POSTEXE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DSYM"]: + self._append_expression("*(struct RNode_DSYM *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ATTRASGN"]: + self._append_expression("*(struct RNode_ATTRASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LAMBDA"]: + self._append_expression("*(struct RNode_LAMBDA *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARYPTN"]: + self._append_expression("*(struct RNode_ARYPTN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_HSHPTN"]: + self._append_expression("*(struct RNode_HSHPTN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FNDPTN"]: + self._append_expression("*(struct RNode_FNDPTN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ERROR"]: + self._append_expression("*(struct RNode_ERROR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LINE"]: + self._append_expression("*(struct RNode_LINE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FILE"]: + self._append_expression("*(struct RNode_FILE *) %0#x" % val.GetValueAsUnsigned()) + else: + self._append_expression("*(struct RNode *) %0#x" % val.GetValueAsUnsigned()) + return True From 35c5c7edb9c77398de65cef0636a3cdd2e539bb5 Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Thu, 9 May 2024 16:00:17 +0200 Subject: [PATCH 048/242] [rubygems/rubygems] Refactor `Gem::Specification#find_all_by_name` So that it can also be delegated to `Gem::SpecificationRecord`. https://github.com/rubygems/rubygems/commit/1407807a99 --- lib/rubygems/dependency.rb | 14 +------------- lib/rubygems/specification.rb | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index d1bf074441e5e9..5ce9c5e84030a7 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -271,15 +271,7 @@ def merge(other) end def matching_specs(platform_only = false) - env_req = Gem.env_requirement(name) - matches = Gem::Specification.stubs_for(name).find_all do |spec| - requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) - end.map(&:to_spec) - - if prioritizes_bundler? - require_relative "bundler_version_finder" - Gem::BundlerVersionFinder.prioritize!(matches) - end + matches = Gem::Specification.find_all_by_name(name, requirement) if platform_only matches.reject! do |spec| @@ -297,10 +289,6 @@ def specific? @requirement.specific? end - def prioritizes_bundler? - name == "bundler" && !specific? - end - def to_specs matches = matching_specs true diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index eb846095293ef7..00ed1cd5810a97 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -937,9 +937,19 @@ def self.each # Returns every spec that matches +name+ and optional +requirements+. def self.find_all_by_name(name, *requirements) - requirements = Gem::Requirement.default if requirements.empty? + req = Gem::Requirement.create(*requirements) + env_req = Gem.env_requirement(name) + + matches = stubs_for(name).find_all do |spec| + req.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) + end.map(&:to_spec) + + if name == "bundler" && !req.specific? + require_relative "bundler_version_finder" + Gem::BundlerVersionFinder.prioritize!(matches) + end - Gem::Dependency.new(name, *requirements).matching_specs + matches end ## From c55c11d7d5813683dde3eb7791bc2a9d5d39556d Mon Sep 17 00:00:00 2001 From: David Rodriguez Date: Thu, 9 May 2024 17:51:44 +0200 Subject: [PATCH 049/242] [rubygems/rubygems] Fix binstubs sometimes not getting regenerated when `--destdir` is given This was only working for gems also installed in the default gem home. https://github.com/rubygems/rubygems/commit/47df02dbd9 --- lib/rubygems/commands/pristine_command.rb | 15 +++++---- lib/rubygems/commands/setup_command.rb | 2 ++ lib/rubygems/installer.rb | 2 +- lib/rubygems/specification.rb | 22 ++----------- lib/rubygems/specification_record.rb | 32 +++++++++++++++++++ .../test_gem_commands_setup_command.rb | 23 +++++++++++-- 6 files changed, 67 insertions(+), 29 deletions(-) diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb index 456d897df23d58..b272a15b6c9dbb 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -57,7 +57,7 @@ def initialize end add_option("-i", "--install-dir DIR", - "Gem repository to get binstubs and plugins installed") do |value, options| + "Gem repository to get gems restored") do |value, options| options[:install_dir] = File.expand_path(value) end @@ -103,21 +103,25 @@ def usage # :nodoc: end def execute + install_dir = options[:install_dir] + + specification_record = install_dir ? Gem::SpecificationRecord.from_path(install_dir) : Gem::Specification.specification_record + specs = if options[:all] - Gem::Specification.map + specification_record.map # `--extensions` must be explicitly given to pristine only gems # with extensions. elsif options[:extensions_set] && options[:extensions] && options[:args].empty? - Gem::Specification.select do |spec| + specification_record.select do |spec| spec.extensions && !spec.extensions.empty? end elsif options[:only_missing_extensions] - Gem::Specification.select(&:missing_extensions?) + specification_record.select(&:missing_extensions?) else get_all_gem_names.sort.map do |gem_name| - Gem::Specification.find_all_by_name(gem_name, options[:version]).reverse + specification_record.find_all_by_name(gem_name, options[:version]).reverse end.flatten end @@ -176,7 +180,6 @@ def execute end bin_dir = options[:bin_dir] if options[:bin_dir] - install_dir = options[:install_dir] if options[:install_dir] installer_options = { wrappers: true, diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index 3f38074280538e..9c633d6ef73408 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -585,6 +585,8 @@ def regenerate_binstubs(bindir) args = %w[--all --only-executables --silent] args << "--bindir=#{bindir}" + args << "--install-dir=#{default_dir}" + if options[:env_shebang] args << "--env-shebang" end diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 8f6f9a5aa8e894..844f292ba25d3d 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -344,7 +344,7 @@ def install say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil? - Gem::Specification.add_spec(spec) + Gem::Specification.add_spec(spec) unless @install_dir load_plugin diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 00ed1cd5810a97..57f9b45cf7ab07 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -925,31 +925,15 @@ def self.dirs=(dirs) # Enumerate every known spec. See ::dirs= and ::add_spec to set the list of # specs. - def self.each - return enum_for(:each) unless block_given? - - _all.each do |x| - yield x - end + def self.each(&block) + specification_record.each(&block) end ## # Returns every spec that matches +name+ and optional +requirements+. def self.find_all_by_name(name, *requirements) - req = Gem::Requirement.create(*requirements) - env_req = Gem.env_requirement(name) - - matches = stubs_for(name).find_all do |spec| - req.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) - end.map(&:to_spec) - - if name == "bundler" && !req.specific? - require_relative "bundler_version_finder" - Gem::BundlerVersionFinder.prioritize!(matches) - end - - matches + specification_record.find_all_by_name(name, *requirements) end ## diff --git a/lib/rubygems/specification_record.rb b/lib/rubygems/specification_record.rb index 812431fa3297a7..dd6aa7eafaee3e 100644 --- a/lib/rubygems/specification_record.rb +++ b/lib/rubygems/specification_record.rb @@ -111,6 +111,38 @@ def all_names all.map(&:full_name) end + include Enumerable + + ## + # Enumerate every known spec. + + def each + return enum_for(:each) unless block_given? + + all.each do |x| + yield x + end + end + + ## + # Returns every spec in the record that matches +name+ and optional +requirements+. + + def find_all_by_name(name, *requirements) + req = Gem::Requirement.create(*requirements) + env_req = Gem.env_requirement(name) + + matches = stubs_for(name).find_all do |spec| + req.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) + end.map(&:to_spec) + + if name == "bundler" && !req.specific? + require_relative "bundler_version_finder" + Gem::BundlerVersionFinder.prioritize!(matches) + end + + matches + end + ## # Return the best specification in the record that contains the file matching +path+. diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb index 43f695f147a7d3..8eedb6c03aa48d 100644 --- a/test/rubygems/test_gem_commands_setup_command.rb +++ b/test/rubygems/test_gem_commands_setup_command.rb @@ -159,6 +159,23 @@ def test_destdir_flag_does_not_try_to_write_to_the_default_gem_home end end + def test_destdir_flag_regenerates_binstubs + # install to destdir + destdir = File.join(@tempdir, "foo") + gem_bin_path = gem_install "destdir-only-gem", install_dir: destdir + + # change binstub manually + write_file gem_bin_path do |io| + io.puts "I changed it!" + end + + @cmd.options[:destdir] = destdir + @cmd.options[:prefix] = "/" + @cmd.execute + + assert_match(/\A#!/, File.read(gem_bin_path)) + end + def test_files_in assert_equal %w[rubygems.rb rubygems/requirement.rb rubygems/ssl_certs/rubygems.org/foo.pem], @cmd.files_in("lib").sort @@ -412,7 +429,7 @@ def create_dummy_files(list) end end - def gem_install(name) + def gem_install(name, **options) gem = util_spec name do |s| s.executables = [name] s.files = %W[bin/#{name}] @@ -420,8 +437,8 @@ def gem_install(name) write_file File.join @tempdir, "bin", name do |f| f.puts "#!/usr/bin/ruby" end - install_gem gem - File.join @gemhome, "bin", name + install_gem gem, **options + File.join options[:install_dir] || @gemhome, "bin", name end def gem_install_with_plugin(name) From 477b13ba17d595cda5c8d38da145ef2600702af5 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 09:41:52 -0400 Subject: [PATCH 050/242] [ruby/prism] Update unterminated string error location https://github.com/ruby/prism/commit/33d12afa60 --- prism/prism.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/prism/prism.c b/prism/prism.c index 97d47c8985f109..41075502233727 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -16878,6 +16878,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { // start with a single string content node. pm_string_t unescaped; pm_token_t content; + if (match1(parser, PM_TOKEN_EOF)) { unescaped = PM_STRING_EMPTY; content = not_provided(parser); @@ -16940,7 +16941,19 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); pm_node_flag_set(node, parse_unescaped_encoding(parser)); - expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF); + + // Kind of odd behavior, but basically if we have an + // unterminated string and it ends in a newline, we back up one + // character so that the error message is on the last line of + // content in the string. + if (!accept1(parser, PM_TOKEN_STRING_END)) { + const uint8_t *location = parser->previous.end; + if (location > parser->start && location[-1] == '\n') location--; + pm_parser_err(parser, location, location, PM_ERR_STRING_LITERAL_EOF); + + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } } else if (accept1(parser, PM_TOKEN_LABEL_END)) { node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)); } else { From 011668883ca62bc727f372fa8abb6a64e3a33aae Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 10:01:30 -0400 Subject: [PATCH 051/242] [ruby/prism] Always go to string lex mode even if EOF https://github.com/ruby/prism/commit/57d5c9be2c --- prism/prism.c | 5 +---- test/prism/errors_test.rb | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 41075502233727..e6f10f0810aab0 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -11375,10 +11375,7 @@ parser_lex(pm_parser_t *parser) { const uint8_t delimiter = pm_lex_percent_delimiter(parser); lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter)); - - if (parser->current.end < parser->end) { - LEX(PM_TOKEN_STRING_BEGIN); - } + LEX(PM_TOKEN_STRING_BEGIN); } // Delimiters for %-literals cannot be alphanumeric. We diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index c6991519c50a12..65b913fc3242ef 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -222,7 +222,7 @@ def test_unterminated_interpolated_symbol def test_cr_without_lf_in_percent_expression assert_errors expression("%\r"), "%\r", [ - ["invalid `%` token", 0..2], + ["unterminated string meets end of file", 2..2], ] end From 7a4b9d1614bfea5277deaab842c30e19a842a827 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 10:23:31 -0400 Subject: [PATCH 052/242] [ruby/prism] Match CRuby error message for unknown % string https://github.com/ruby/prism/commit/863197629c --- prism/templates/src/diagnostic.c.erb | 2 +- test/prism/errors_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index 451f9c3f7cd80c..8e63dd337b65c1 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -235,7 +235,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_INVALID_MULTIBYTE_CHARACTER] = { "invalid multibyte character 0x%X", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_MULTIBYTE_ESCAPE] = { "invalid multibyte escape: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_SYNTAX }, // TODO WHAT? + [PM_ERR_INVALID_PERCENT] = { "unknown type of %string", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_AFTER_ELSE] = { "Invalid retry after else", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_AFTER_ENSURE] = { "Invalid retry after ensure", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_WITHOUT_RESCUE] = { "Invalid retry without rescue", PM_ERROR_LEVEL_SYNTAX }, diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 65b913fc3242ef..0cf1c6925bb3fe 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -208,7 +208,7 @@ def test_missing_terminator_in_parentheses def test_unterminated_argument_expression assert_errors expression('a %'), 'a %', [ - ["invalid `%` token", 2..3], + ["unknown type of %string", 2..3], ["unexpected end-of-input; expected an expression after the operator", 3..3], ["unexpected end-of-input, assuming it is closing the parent top level context", 3..3] ] @@ -1400,7 +1400,7 @@ def test_invalid_number_underscores end def test_alnum_delimiters - error_messages = ["invalid `%` token"] + error_messages = ["unknown type of %string"] assert_error_messages "%qXfooX", error_messages assert_error_messages "%QXfooX", error_messages From 910bb4923066b29b42692a82c7310a7fbc36ebb9 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 10:38:16 -0400 Subject: [PATCH 053/242] [ruby/prism] Update error message for invalid % at EOF https://github.com/ruby/prism/commit/178d4f66fd --- prism/config.yml | 1 + prism/prism.c | 2 +- prism/templates/src/diagnostic.c.erb | 1 + test/prism/errors_test.rb | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/prism/config.yml b/prism/config.yml index c48e5f28dc7992..0652cc4a9fa2bf 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -153,6 +153,7 @@ errors: - INVALID_NUMBER_UNDERSCORE_INNER - INVALID_NUMBER_UNDERSCORE_TRAILING - INVALID_PERCENT + - INVALID_PERCENT_EOF - INVALID_PRINTABLE_CHARACTER - INVALID_RETRY_AFTER_ELSE - INVALID_RETRY_AFTER_ENSURE diff --git a/prism/prism.c b/prism/prism.c index e6f10f0810aab0..6befa6bab14590 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -11356,7 +11356,7 @@ parser_lex(pm_parser_t *parser) { // operator because we don't want to move into the string // lex mode unnecessarily. if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) { - pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT); + pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT_EOF); LEX(PM_TOKEN_PERCENT); } diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index 8e63dd337b65c1..989283fef0892b 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -236,6 +236,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_INVALID_MULTIBYTE_ESCAPE] = { "invalid multibyte escape: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_PERCENT] = { "unknown type of %string", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_PERCENT_EOF] = { "unterminated quoted string meets end of file", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_AFTER_ELSE] = { "Invalid retry after else", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_AFTER_ENSURE] = { "Invalid retry after ensure", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_WITHOUT_RESCUE] = { "Invalid retry without rescue", PM_ERROR_LEVEL_SYNTAX }, diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 0cf1c6925bb3fe..1357c97cf97ee5 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -208,7 +208,7 @@ def test_missing_terminator_in_parentheses def test_unterminated_argument_expression assert_errors expression('a %'), 'a %', [ - ["unknown type of %string", 2..3], + ["unterminated quoted string meets end of file", 2..3], ["unexpected end-of-input; expected an expression after the operator", 3..3], ["unexpected end-of-input, assuming it is closing the parent top level context", 3..3] ] From 0e11781f216c0dbf5f5f3dfbfbb43e14bdfd82ab Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 11:04:01 -0400 Subject: [PATCH 054/242] [ruby/prism] Match CRuby invalid character error message https://github.com/ruby/prism/commit/8d4c5c2bca --- prism/templates/src/diagnostic.c.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index 989283fef0892b..16472bb9cf5b53 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -230,7 +230,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_INVALID_NUMBER_OCTAL] = { "invalid octal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER] = { "invalid underscore placement in number", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING] = { "trailing '_' in number", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_CHARACTER] = { "invalid character 0x%X", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_CHARACTER] = { "Invalid char '\\x%02X' in expression", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_MULTIBYTE_CHAR] = { "invalid multibyte char (%s)", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_MULTIBYTE_CHARACTER] = { "invalid multibyte character 0x%X", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_MULTIBYTE_ESCAPE] = { "invalid multibyte escape: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, From d314d976d4542f4facbacb8f644014fcbdbcc34d Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 11:10:43 -0400 Subject: [PATCH 055/242] [ruby/prism] Match CRuby invalid escape character syntax error message https://github.com/ruby/prism/commit/0ef5658dcc --- prism/templates/src/diagnostic.c.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index 16472bb9cf5b53..eab3dd26bff00c 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -158,10 +158,10 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_EMBVAR_INVALID] = { "invalid embedded variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_END_UPCASE_BRACE] = { "expected a `{` after `END`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_END_UPCASE_TERM] = { "expected a `}` to close the `END` statement", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ESCAPE_INVALID_CONTROL] = { "invalid control escape sequence", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_CONTROL] = { "Invalid escape character syntax", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT] = { "invalid control escape sequence; control cannot be repeated", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_HEXADECIMAL] = { "invalid hex escape sequence", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ESCAPE_INVALID_META] = { "invalid meta escape sequence", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_META] = { "Invalid escape character syntax", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_META_REPEAT] = { "invalid meta escape sequence; meta cannot be repeated", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_UNICODE] = { "invalid Unicode escape sequence", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS] = { "invalid Unicode escape sequence; Unicode cannot be combined with control or meta flags", PM_ERROR_LEVEL_SYNTAX }, From 14debd13210a9a187241a24360bad640e5cd2aa4 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 09:24:38 -0400 Subject: [PATCH 056/242] [PRISM] Enable TestSyntax#test__END___cr --- test/.excludes-prism/TestSyntax.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/.excludes-prism/TestSyntax.rb b/test/.excludes-prism/TestSyntax.rb index 0d312618c72836..c65ed92823387e 100644 --- a/test/.excludes-prism/TestSyntax.rb +++ b/test/.excludes-prism/TestSyntax.rb @@ -1,6 +1,4 @@ -exclude(:test__END___cr, "error message format") exclude(:test_dedented_heredoc_continued_line, "heredoc line continuation dedent calculation") -exclude(:test_duplicated_when, "error message format") exclude(:test_error_message_encoding, "syntax error message encoding") exclude(:test_it, "https://github.com/ruby/prism/issues/2323") exclude(:test_numbered_parameter, "should raise syntax error for numbered parameters in inner blocks") @@ -8,5 +6,6 @@ exclude(:test_unterminated_heredoc_cr, "quoted \r heredoc terminators should not match \r\n") exclude(:test_warn_balanced, "missing warning for ** being interpreted as a binary operator") +exclude(:test_duplicated_when, "https://bugs.ruby-lang.org/issues/20401") exclude(:test_optional_self_reference, "https://bugs.ruby-lang.org/issues/20478") exclude(:test_keyword_self_reference, "https://bugs.ruby-lang.org/issues/20478") From 52401a32e5699870d2eef9a492532a0729be204a Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 09:43:09 -0400 Subject: [PATCH 057/242] [PRISM] Enable TestSyntax#test_syntax_error_at_newline --- test/.excludes-prism/TestSyntax.rb | 1 - test/ruby/test_syntax.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/.excludes-prism/TestSyntax.rb b/test/.excludes-prism/TestSyntax.rb index c65ed92823387e..6e9f51604a370e 100644 --- a/test/.excludes-prism/TestSyntax.rb +++ b/test/.excludes-prism/TestSyntax.rb @@ -2,7 +2,6 @@ exclude(:test_error_message_encoding, "syntax error message encoding") exclude(:test_it, "https://github.com/ruby/prism/issues/2323") exclude(:test_numbered_parameter, "should raise syntax error for numbered parameters in inner blocks") -exclude(:test_syntax_error_at_newline, "error message format") exclude(:test_unterminated_heredoc_cr, "quoted \r heredoc terminators should not match \r\n") exclude(:test_warn_balanced, "missing warning for ** being interpreted as a binary operator") diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index edc3a3cec3fb9d..56b97789e531ac 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -1601,7 +1601,7 @@ def test_syntax_error_in_rescue end def test_syntax_error_at_newline - expected = "\n ^" + expected = /(\n|\| ) \^/ assert_syntax_error("%[abcdef", expected) assert_syntax_error("%[abcdef\n", expected) end From 58f993c500ae3f1051fbaa76d21e85bec93fa1f7 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 10:51:04 -0400 Subject: [PATCH 058/242] [PRISM] Enable TestParse#test_percent --- test/.excludes-prism/TestParse.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index 8ff7dba1f4cdcd..317b09250336c9 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,5 +1,4 @@ exclude(:test_invalid_char, "error message format") -exclude(:test_percent, "error message format") exclude(:test_question, "error message format") exclude(:test_string, "error message format") exclude(:test_truncated_source_line, "truncate error message") From 7eb45a40fdc8d3e077089b13cef90f2d350509e1 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 11:05:47 -0400 Subject: [PATCH 059/242] [PRISM] Enable TestParse#test_invalid_char --- test/.excludes-prism/TestParse.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index 317b09250336c9..cf5f1a873547cc 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,4 +1,3 @@ -exclude(:test_invalid_char, "error message format") exclude(:test_question, "error message format") exclude(:test_string, "error message format") exclude(:test_truncated_source_line, "truncate error message") From 07241dfac8b64c16c0fc35da55fdb7da8a10dfe4 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 11:11:42 -0400 Subject: [PATCH 060/242] [PRISM] Enable TestParse#test_question --- test/.excludes-prism/TestParse.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index cf5f1a873547cc..ba718d063c0800 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,4 +1,3 @@ -exclude(:test_question, "error message format") exclude(:test_string, "error message format") exclude(:test_truncated_source_line, "truncate error message") exclude(:test_void_value_in_rhs, "missing raising error for some void value expressions") From 286d3032a794500a81bcac9f10363f953d368ef6 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 12:29:14 -0400 Subject: [PATCH 061/242] [ruby/prism] More mixed encoding errors https://github.com/ruby/prism/commit/2a43b4f55c --- prism/prism.c | 8 ++++++++ prism/util/pm_strpbrk.c | 38 ++++++++++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 6befa6bab14590..be6b52f5b1bc26 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -15382,6 +15382,10 @@ parse_string_part(pm_parser_t *parser) { // "aaa #{bbb} #@ccc ddd" // ^^^^^^ case PM_TOKEN_EMBEXPR_BEGIN: { + // Ruby disallows seeing encoding around interpolation in strings, + // even though it is known at parse time. + parser->explicit_encoding = NULL; + pm_lex_state_t state = parser->lex_state; int brace_nesting = parser->brace_nesting; @@ -15414,6 +15418,10 @@ parse_string_part(pm_parser_t *parser) { // "aaa #{bbb} #@ccc ddd" // ^^^^^ case PM_TOKEN_EMBVAR: { + // Ruby disallows seeing encoding around interpolation in strings, + // even though it is known at parse time. + parser->explicit_encoding = NULL; + lex_state_set(parser, PM_LEX_STATE_BEG); parser_lex(parser); diff --git a/prism/util/pm_strpbrk.c b/prism/util/pm_strpbrk.c index 6c8dea183679c6..916a4cc3fd3c16 100644 --- a/prism/util/pm_strpbrk.c +++ b/prism/util/pm_strpbrk.c @@ -8,6 +8,27 @@ pm_strpbrk_invalid_multibyte_character(pm_parser_t *parser, const uint8_t *start pm_diagnostic_list_append_format(&parser->error_list, start, end, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *start); } +/** + * Set the explicit encoding for the parser to the current encoding. + */ +static inline void +pm_strpbrk_explicit_encoding_set(pm_parser_t *parser, const uint8_t *source, size_t width) { + if (parser->explicit_encoding != NULL) { + if (parser->explicit_encoding == parser->encoding) { + // Okay, we already locked to this encoding. + } else if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + // Not okay, we already found a Unicode escape sequence and this + // conflicts. + pm_diagnostic_list_append_format(&parser->error_list, source, source + width, PM_ERR_MIXED_ENCODING, parser->encoding->name); + } else { + // Should not be anything else. + assert(false && "unreachable"); + } + } + + parser->explicit_encoding = parser->encoding; +} + /** * This is the default path. */ @@ -52,7 +73,7 @@ pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *chars * This is the path when the encoding is ASCII-8BIT. */ static inline const uint8_t * -pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maximum) { +pm_strpbrk_ascii_8bit(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { size_t index = 0; while (index < maximum) { @@ -60,6 +81,7 @@ pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maxi return source + index; } + if (validate && source[index] >= 0x80) pm_strpbrk_explicit_encoding_set(parser, source, 1); index++; } @@ -72,6 +94,7 @@ pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maxi static inline const uint8_t * pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { size_t index = 0; + const pm_encoding_t *encoding = parser->encoding; while (index < maximum) { if (strchr((const char *) charset, source[index]) != NULL) { @@ -81,7 +104,8 @@ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t if (source[index] < 0x80) { index++; } else { - size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + if (validate) pm_strpbrk_explicit_encoding_set(parser, source, width); if (width > 0) { index += width; @@ -96,7 +120,7 @@ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t do { index++; - } while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); + } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index); } @@ -113,6 +137,7 @@ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t static inline const uint8_t * pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { size_t index = 0; + const pm_encoding_t *encoding = parser->encoding; while (index < maximum) { if (strchr((const char *) charset, source[index]) != NULL) { @@ -122,7 +147,8 @@ pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t if (source[index] < 0x80 || !validate) { index++; } else { - size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + pm_strpbrk_explicit_encoding_set(parser, source, width); if (width > 0) { index += width; @@ -135,7 +161,7 @@ pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t do { index++; - } while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); + } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index); } @@ -171,7 +197,7 @@ pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, p } else if (!parser->encoding_changed) { return pm_strpbrk_utf8(parser, source, charset, (size_t) length, validate); } else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) { - return pm_strpbrk_ascii_8bit(source, charset, (size_t) length); + return pm_strpbrk_ascii_8bit(parser, source, charset, (size_t) length, validate); } else if (parser->encoding->multibyte) { return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length, validate); } else { From 0b4cf461e02b84c5ede91ff46b88d844353e1c3d Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 13:17:04 -0400 Subject: [PATCH 062/242] [PRISM] Enable TestM17N#test_string_mixed_unicode --- test/.excludes-prism/TestM17N.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/.excludes-prism/TestM17N.rb b/test/.excludes-prism/TestM17N.rb index f10c9632d96dda..0367ffa9f2da88 100644 --- a/test/.excludes-prism/TestM17N.rb +++ b/test/.excludes-prism/TestM17N.rb @@ -1,2 +1 @@ exclude(:test_regexp_usascii, "x80 should raise syntax error") -exclude(:test_string_mixed_unicode, "should raise mixed encoding error") From 012a80d768affcfb4e747a358f4941647381776b Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 13:28:27 -0400 Subject: [PATCH 063/242] [PRISM] Enable TestISeq#test_syntax_error_message --- test/.excludes-prism/TestISeq.rb | 2 -- test/ruby/test_iseq.rb | 16 +++++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/test/.excludes-prism/TestISeq.rb b/test/.excludes-prism/TestISeq.rb index ee3d92494e440e..499ae2e53dbe33 100644 --- a/test/.excludes-prism/TestISeq.rb +++ b/test/.excludes-prism/TestISeq.rb @@ -1,4 +1,2 @@ -exclude(:test_syntax_error_message, "error message format") - exclude(:test_each_child, "https://bugs.ruby-lang.org/issues/20479") exclude(:test_trace_points, "https://bugs.ruby-lang.org/issues/20479") diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index d2a39e673f3ae6..df4b6651b0216e 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -347,11 +347,17 @@ def y@;end end end assert_equal([m1, e1.message], [m2, e2.message], feature11951) - message = e1.message.each_line - message.with_index(1) do |line, i| - next if /^ / =~ line - assert_send([line, :start_with?, __FILE__], - proc {message.map {|l, j| (i == j ? ">" : " ") + l}.join("")}) + + if e1.message.lines[0] == "#{__FILE__}:#{line}: syntax errors found\n" + # Prism lays out the error messages in line with the source, so the + # following assertions do not make sense in that context. + else + message = e1.message.each_line + message.with_index(1) do |line, i| + next if /^ / =~ line + assert_send([line, :start_with?, __FILE__], + proc {message.map {|l, j| (i == j ? ">" : " ") + l}.join("")}) + end end end From 98e1e610f6df95aa89481fd92a31a75a808ce2d1 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 14:28:48 -0400 Subject: [PATCH 064/242] [ruby/prism] Update more escape error messages to match CRuby https://github.com/ruby/prism/commit/ab43b3ab66 --- prism/prism.c | 37 +++++++++++++++++++++------- prism/templates/src/diagnostic.c.erb | 6 ++--- test/prism/errors_test.rb | 5 ++-- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index be6b52f5b1bc26..75f143216122b5 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -9658,7 +9658,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE_LONG); } else if (hexadecimal_length == 0) { // there are not hexadecimal characters - pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE); + pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE); + pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_TERM); return; } @@ -9707,10 +9708,6 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre } } - if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) { - pm_parser_err(parser, start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); - } - return; } case 'c': { @@ -9733,6 +9730,12 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL); return; case ' ': @@ -9760,7 +9763,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre case 'C': { parser->current.end++; if (peek(parser) != '-') { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL); return; } @@ -9783,6 +9787,12 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL); return; case ' ': @@ -9797,7 +9807,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; default: { if (!char_is_ascii_printable(peeked)) { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL); return; } @@ -9810,7 +9821,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre case 'M': { parser->current.end++; if (peek(parser) != '-') { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META); + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META); return; } @@ -9828,6 +9840,12 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META); return; case ' ': @@ -9842,7 +9860,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; default: if (!char_is_ascii_printable(peeked)) { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META); + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META); return; } diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index eab3dd26bff00c..d9e195e08f3f0e 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -167,7 +167,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS] = { "invalid Unicode escape sequence; Unicode cannot be combined with control or meta flags", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL] = { "invalid Unicode escape sequence; Multiple codepoints at single character literal are disallowed", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_UNICODE_LONG] = { "invalid Unicode escape sequence; maximum length is 6 digits", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = { "invalid Unicode escape sequence; needs closing `}`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = { "unterminated Unicode escape", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_ARGUMENT] = { "expected an argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EOL_AFTER_STATEMENT] = { "unexpected %s, expecting end-of-input", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ] = { "expected an expression after `&&=`", PM_ERROR_LEVEL_SYNTAX }, @@ -330,12 +330,12 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_STATEMENT_PREEXE_BEGIN] = { "unexpected a `BEGIN` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STATEMENT_UNDEF] = { "unexpected an `undef` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STRING_CONCATENATION] = { "expected a string for concatenation", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_STRING_INTERPOLATED_TERM] = { "expected a closing delimiter for the interpolated string", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STRING_INTERPOLATED_TERM] = { "unterminated string; expected a closing delimiter for the interpolated string", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STRING_LITERAL_EOF] = { "unterminated string meets end of file", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STRING_LITERAL_TERM] = { "unexpected %s, expected a string literal terminator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_SYMBOL_INVALID] = { "invalid symbol", PM_ERROR_LEVEL_SYNTAX }, // TODO expected symbol? prism.c ~9719 [PM_ERR_SYMBOL_TERM_DYNAMIC] = { "unterminated quoted string; expected a closing delimiter for the dynamic symbol", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_SYMBOL_TERM_INTERPOLATED] = { "expected a closing delimiter for the interpolated symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_SYMBOL_TERM_INTERPOLATED] = { "unterminated symbol; expected a closing delimiter for the interpolated symbol", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_TERNARY_COLON] = { "expected a `:` after the true expression of a ternary operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_TERNARY_EXPRESSION_FALSE] = { "expected an expression after `:` in the ternary operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_TERNARY_EXPRESSION_TRUE] = { "expected an expression after `?` in the ternary operator", PM_ERROR_LEVEL_SYNTAX }, diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 1357c97cf97ee5..3670b90dd76e16 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -216,7 +216,7 @@ def test_unterminated_argument_expression def test_unterminated_interpolated_symbol assert_error_messages ":\"#", [ - "expected a closing delimiter for the interpolated symbol" + "unterminated symbol; expected a closing delimiter for the interpolated symbol" ] end @@ -715,12 +715,13 @@ def test_do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_charact assert_errors expected, '"\u{000z}"', [ ["invalid Unicode escape sequence", 7..7], + ["unterminated Unicode escape", 7..7] ] end def test_unterminated_unicode_brackets_should_be_a_syntax_error assert_errors expression('?\\u{3'), '?\\u{3', [ - ["invalid Unicode escape sequence; needs closing `}`", 1..5], + ["unterminated Unicode escape", 1..5], ] end From 92af7054989e6bb605482178f97cee5e59ec9326 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 15:07:03 -0400 Subject: [PATCH 065/242] [ruby/prism] Truncate source lines in errors messages when too long https://github.com/ruby/prism/commit/72518f5716 --- prism/prism.c | 48 +++++++++++++++++++++++++++----- test/prism/format_errors_test.rb | 42 +++++++++++++++++++++++++--- test/prism/newline_test.rb | 1 + 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 75f143216122b5..d203b4b7202065 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -21592,6 +21592,7 @@ typedef struct { #define PM_COLOR_GRAY "\033[38;5;102m" #define PM_COLOR_RED "\033[1;31m" #define PM_COLOR_RESET "\033[m" +#define PM_ERROR_TRUNCATE 30 static inline pm_error_t * pm_parser_errors_format_sort(const pm_parser_t *parser, const pm_list_t *error_list, const pm_newline_list_t *newline_list) { @@ -21646,7 +21647,7 @@ pm_parser_errors_format_sort(const pm_parser_t *parser, const pm_list_t *error_l } static inline void -pm_parser_errors_format_line(const pm_parser_t *parser, const pm_newline_list_t *newline_list, const char *number_prefix, int32_t line, pm_buffer_t *buffer) { +pm_parser_errors_format_line(const pm_parser_t *parser, const pm_newline_list_t *newline_list, const char *number_prefix, int32_t line, uint32_t column_start, uint32_t column_end, pm_buffer_t *buffer) { int32_t line_delta = line - parser->start_line; assert(line_delta >= 0); @@ -21663,9 +21664,25 @@ pm_parser_errors_format_line(const pm_parser_t *parser, const pm_newline_list_t } pm_buffer_append_format(buffer, number_prefix, line); + + // Here we determine if we should truncate the end of the line. + bool truncate_end = false; + if ((column_end != 0) && ((end - (start + column_end)) >= PM_ERROR_TRUNCATE)) { + end = start + column_end + PM_ERROR_TRUNCATE; + truncate_end = true; + } + + // Here we determine if we should truncate the start of the line. + if (column_start >= PM_ERROR_TRUNCATE) { + pm_buffer_append_string(buffer, "... ", 4); + start += column_start; + } + pm_buffer_append_string(buffer, (const char *) start, (size_t) (end - start)); - if (end == parser->end && end[-1] != '\n') { + if (truncate_end) { + pm_buffer_append_string(buffer, " ...\n", 5); + } else if (end == parser->end && end[-1] != '\n') { pm_buffer_append_string(buffer, "\n", 1); } } @@ -21780,6 +21797,7 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, // display the same line twice in case the errors are close enough in the // source. int32_t last_line = parser->start_line - 1; + uint32_t last_column_start = 0; const pm_encoding_t *encoding = parser->encoding; for (size_t index = 0; index < error_list->size; index++) { @@ -21794,11 +21812,11 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, } pm_buffer_append_string(buffer, " ", 2); - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 2, buffer); + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 2, 0, 0, buffer); } pm_buffer_append_string(buffer, " ", 2); - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 1, buffer); + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 1, 0, 0, buffer); } // If this is the first error or we're on a new line, then we'll display @@ -21809,7 +21827,17 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, } else { pm_buffer_append_string(buffer, "> ", 2); } - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line, buffer); + + last_column_start = error->column_start; + + // Find the maximum column end of all the errors on this line. + uint32_t column_end = error->column_end; + for (size_t next_index = index + 1; next_index < error_list->size; next_index++) { + if (errors[next_index].line != error->line) break; + if (errors[next_index].column_end > column_end) column_end = errors[next_index].column_end; + } + + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line, error->column_start, column_end, buffer); } const uint8_t *start = &parser->start[newline_list->offsets[error->line - start_line]]; @@ -21828,6 +21856,11 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, pm_buffer_append_string(buffer, error_format.blank_prefix, error_format.blank_prefix_length); size_t column = 0; + if (last_column_start >= PM_ERROR_TRUNCATE) { + pm_buffer_append_string(buffer, " ", 4); + column = last_column_start; + } + while (column < error->column_start) { pm_buffer_append_byte(buffer, ' '); @@ -21867,12 +21900,12 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, if (next_line - last_line > 1) { pm_buffer_append_string(buffer, " ", 2); - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, buffer); + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer); } if (next_line - last_line > 1) { pm_buffer_append_string(buffer, " ", 2); - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, buffer); + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer); } } @@ -21880,6 +21913,7 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, xfree(errors); } +#undef PM_ERROR_TRUNCATE #undef PM_COLOR_GRAY #undef PM_COLOR_RED #undef PM_COLOR_RESET diff --git a/test/prism/format_errors_test.rb b/test/prism/format_errors_test.rb index a1edbef2e86bd1..63206d5765c775 100644 --- a/test/prism/format_errors_test.rb +++ b/test/prism/format_errors_test.rb @@ -6,19 +6,53 @@ module Prism class FormatErrorsTest < TestCase - def test_format_errors - assert_equal <<~ERROR, Debug.format_errors("<>", false) + def test_basic + expected = <<~ERROR > 1 | <> | ^ unexpected '<', ignoring it | ^ unexpected '>', ignoring it ERROR - assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false) - > 1 | "%W"\u" + assert_equal expected, Debug.format_errors("<>", false) + end + + def test_multiple + expected = <<~ERROR + > 1 | "%W"\\u" | ^ unexpected backslash, ignoring it | ^ unexpected local variable or method, expecting end-of-input | ^ unterminated string meets end of file ERROR + + assert_equal expected, Debug.format_errors('"%W"\u"', false) + end + + def test_truncate_start + expected = <<~ERROR + > 1 | ... <> + | ^ unexpected '<', ignoring it + | ^ unexpected '>', ignoring it + ERROR + + assert_equal expected, Debug.format_errors("#{" " * 30}<>", false) + end + + def test_truncate_end + expected = <<~ERROR + > 1 | <#{" " * 30} ... + | ^ unexpected '<', ignoring it + ERROR + + assert_equal expected, Debug.format_errors("<#{" " * 30}a", false) + end + + def test_truncate_both + expected = <<~ERROR + > 1 | ... <#{" " * 30} ... + | ^ unexpected '<', ignoring it + ERROR + + assert_equal expected, Debug.format_errors("#{" " * 30}<#{" " * 30}a", false) end end end diff --git a/test/prism/newline_test.rb b/test/prism/newline_test.rb index d31fb89bc6f1c4..f7511f665cdfea 100644 --- a/test/prism/newline_test.rb +++ b/test/prism/newline_test.rb @@ -10,6 +10,7 @@ class NewlineTest < TestCase filepaths = Dir["*.rb", base: base] - %w[ encoding_test.rb errors_test.rb + format_errors_test.rb parser_test.rb regexp_test.rb static_literals_test.rb From 854cbbd5a9f031c74f9185c2cd8f3dd72784e2d1 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 14:41:11 -0400 Subject: [PATCH 066/242] [PRISM] Enable TestParse#test_string --- test/.excludes-prism/TestParse.rb | 1 - test/ruby/test_parse.rb | 54 ++++++++++++++++++------------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index ba718d063c0800..513a7ed9429a8f 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,3 +1,2 @@ -exclude(:test_string, "error message format") exclude(:test_truncated_source_line, "truncate error message") exclude(:test_void_value_in_rhs, "missing raising error for some void value expressions") diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 63539270fc0793..aa484b5e0b31e7 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -555,34 +555,42 @@ def test_string mesg = 'from the backslash through the invalid char' e = assert_syntax_error('"\xg1"', /hex escape/) - assert_equal(' ^~'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^~(?!~)/, e.message.lines.last, mesg) e = assert_syntax_error('"\u{1234"', 'unterminated Unicode escape') - assert_equal(' ^'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^(?!~)/, e.message.lines.last, mesg) e = assert_syntax_error('"\u{xxxx}"', 'invalid Unicode escape') - assert_equal(' ^'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^(?!~)/, e.message.lines.last, mesg) e = assert_syntax_error('"\u{xxxx', 'Unicode escape') - assert_pattern_list([ - /.*: invalid Unicode escape\n.*\n/, - / \^/, - /\n/, - /.*: unterminated Unicode escape\n.*\n/, - / \^/, - /\n/, - /.*: unterminated string.*\n.*\n/, - / \^\n/, - ], e.message) + if e.message.lines.first == "#{__FILE__}:#{__LINE__ - 1}: syntax errors found\n" + assert_pattern_list([ + /\s+\| \^ unterminated string;.+\n/, + /\s+\| \^ unterminated Unicode escape\n/, + /\s+\| \^ invalid Unicode escape sequence\n/, + ], e.message.lines[2..-1].join) + else + assert_pattern_list([ + /.*: invalid Unicode escape\n.*\n/, + / \^/, + /\n/, + /.*: unterminated Unicode escape\n.*\n/, + / \^/, + /\n/, + /.*: unterminated string.*\n.*\n/, + / \^\n/, + ], e.message) + end e = assert_syntax_error('"\M1"', /escape character syntax/) - assert_equal(' ^~~'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^~~(?!~)/, e.message.lines.last, mesg) e = assert_syntax_error('"\C1"', /escape character syntax/) - assert_equal(' ^~~'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^~~(?!~)/, e.message.lines.last, mesg) src = '"\xD0\u{90'"\n""000000000000000000000000" - assert_syntax_error(src, /:#{__LINE__}: unterminated/o) + assert_syntax_error(src, /(:#{__LINE__}:|> #{__LINE__} \|.+) unterminated/om) assert_syntax_error('"\u{100000000}"', /invalid Unicode escape/) assert_equal("", eval('"\u{}"')) @@ -605,22 +613,22 @@ def test_string assert_syntax_error("\"\\C-\\M-\x01\"", 'Invalid escape character syntax') e = assert_syntax_error('"\c\u0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\c\U0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\C-\u0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\C-\U0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\M-\u0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\M-\U0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last) e = assert_syntax_error(%["\\C-\u3042"], 'Invalid escape character syntax') - assert_match(/^\s \^(?# \\ ) ~(?# C ) ~(?# - ) ~+(?# U+3042 )$/x, e.message.lines.last) + assert_match(/(^|\|\s)\s \^(?# \\ ) ~(?# C ) ~(?# - ) ~+(?# U+3042 )($|\s)/x, e.message.lines.last) assert_not_include(e.message, "invalid multibyte char") end From b90ea8ae52d0213900cd5b8b2a47ff6a2a9442df Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Thu, 16 May 2024 07:56:58 +0200 Subject: [PATCH 067/242] [ruby/find] find.gemspec: Drop executables dir config This gem contains no executables. https://github.com/ruby/find/commit/7bd452155e --- lib/find.gemspec | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/find.gemspec b/lib/find.gemspec index cb845e940985e2..aef24a502844ca 100644 --- a/lib/find.gemspec +++ b/lib/find.gemspec @@ -25,7 +25,5 @@ Gem::Specification.new do |spec| spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] end From 761b90702492cd18f6746b59e77826e0302ca547 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 15:36:04 -0400 Subject: [PATCH 068/242] [PRISM] Enable TestParse#test_truncated_source_line --- test/ruby/test_parse.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index aa484b5e0b31e7..3857f3cc172b2c 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1353,9 +1353,13 @@ def test_unexpected_token_after_numeric end def test_truncated_source_line - e = assert_syntax_error("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 0123456789012345678901234567890123456789", + lineno = __LINE__ + 1 + e = assert_syntax_error("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 123456789012345678901234567890123456789", /unexpected local variable or method/) + line = e.message.lines[1] + line.delete_prefix!("> #{lineno} | ") if line.start_with?(">") + assert_operator(line, :start_with?, "...") assert_operator(line, :end_with?, "...\n") end From 84d2bacb424094f1b57cff0f2dd4381e9f8bd9ed Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 16 May 2024 22:44:52 -0400 Subject: [PATCH 069/242] [ruby/irb] Reorder ruby lex clauses for unrecoverable first (https://github.com/ruby/irb/pull/956) When a syntax error includes multiple error messages, we want to check for unrecoverable messages first so that we do not accidentally match a recoverable error later in the message. https://github.com/ruby/irb/commit/2f42b2360d --- lib/irb/ruby-lex.rb | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb index 86e340eb05e568..f6ac7f0f5f51f6 100644 --- a/lib/irb/ruby-lex.rb +++ b/lib/irb/ruby-lex.rb @@ -219,27 +219,6 @@ def check_code_syntax(code, local_variables:) :unrecoverable_error rescue SyntaxError => e case e.message - when /unterminated (?:string|regexp) meets end of file/ - # "unterminated regexp meets end of file" - # - # example: - # / - # - # "unterminated string meets end of file" - # - # example: - # ' - return :recoverable_error - when /unexpected end-of-input/ - # "syntax error, unexpected end-of-input, expecting keyword_end" - # - # example: - # if true - # hoge - # if false - # fuga - # end - return :recoverable_error when /unexpected keyword_end/ # "syntax error, unexpected keyword_end" # @@ -262,6 +241,27 @@ def check_code_syntax(code, local_variables:) # example: # method / f / return :unrecoverable_error + when /unterminated (?:string|regexp) meets end of file/ + # "unterminated regexp meets end of file" + # + # example: + # / + # + # "unterminated string meets end of file" + # + # example: + # ' + return :recoverable_error + when /unexpected end-of-input/ + # "syntax error, unexpected end-of-input, expecting keyword_end" + # + # example: + # if true + # hoge + # if false + # fuga + # end + return :recoverable_error else return :other_error end From 0f8f0386a8c32812cc5738bdf9c8e74eec34440b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 May 2024 02:18:31 +0000 Subject: [PATCH 070/242] Bump actions/checkout from 4.1.5 to 4.1.6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.5 to 4.1.6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/44c2b7a8a4ea60a981eaca3cf939b5f4305c123b...a5ac7e51b41094c92402da3b24376905380afc29) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/annocheck.yml | 2 +- .github/workflows/baseruby.yml | 2 +- .github/workflows/bundled_gems.yml | 2 +- .github/workflows/check_dependencies.yml | 2 +- .github/workflows/check_misc.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/compilers.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/mingw.yml | 2 +- .github/workflows/prism.yml | 2 +- .github/workflows/rjit-bindgen.yml | 2 +- .github/workflows/rjit.yml | 2 +- .github/workflows/scorecards.yml | 2 +- .github/workflows/spec_guards.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- .github/workflows/wasm.yml | 2 +- .github/workflows/windows.yml | 2 +- .github/workflows/yjit-macos.yml | 4 ++-- .github/workflows/yjit-ubuntu.yml | 6 +++--- 19 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/annocheck.yml b/.github/workflows/annocheck.yml index 0590d8c75e1d7d..6c03f52289199f 100644 --- a/.github/workflows/annocheck.yml +++ b/.github/workflows/annocheck.yml @@ -63,7 +63,7 @@ jobs: - run: id working-directory: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml index 4876251b82b4fd..ce979c9e7a8c1a 100644 --- a/.github/workflows/baseruby.yml +++ b/.github/workflows/baseruby.yml @@ -56,7 +56,7 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler: none - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: ./.github/actions/setup/ubuntu diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml index c509967c351845..6f06451a99e7c2 100644 --- a/.github/workflows/bundled_gems.yml +++ b/.github/workflows/bundled_gems.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index fbad01a8f47a4d..8ecae4f2236443 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -45,7 +45,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: ./.github/actions/setup/ubuntu if: ${{ contains(matrix.os, 'ubuntu') }} diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml index c9a182c57b163d..f26319f4480196 100644 --- a/.github/workflows/check_misc.yml +++ b/.github/workflows/check_misc.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5fa6da51700ef7..d6a2e297be3d59 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Install libraries if: ${{ contains(matrix.os, 'macos') }} diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index d4b047b4e93c85..8937a5b15f28c2 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -233,7 +233,7 @@ jobs: - run: id working-directory: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9d0ff5ae373bf1..e71706b186070d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -52,7 +52,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index f4d399751a6574..4170369def04de 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -97,7 +97,7 @@ jobs: $result working-directory: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/prism.yml b/.github/workflows/prism.yml index 7d4cdcb47ac147..78b6469db19a54 100644 --- a/.github/workflows/prism.yml +++ b/.github/workflows/prism.yml @@ -55,7 +55,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/rjit-bindgen.yml b/.github/workflows/rjit-bindgen.yml index 799be7a847f4df..c4cfe9ed83c0de 100644 --- a/.github/workflows/rjit-bindgen.yml +++ b/.github/workflows/rjit-bindgen.yml @@ -51,7 +51,7 @@ jobs: with: ruby-version: '3.1' - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/rjit.yml b/.github/workflows/rjit.yml index f1cd70afa4210f..509088db7d54ff 100644 --- a/.github/workflows/rjit.yml +++ b/.github/workflows/rjit.yml @@ -55,7 +55,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index bfc7b958635227..85ee3e2a48c007 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -32,7 +32,7 @@ jobs: steps: - name: 'Checkout code' - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: persist-credentials: false diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml index 2dc4a7b39a6392..14669a13b3c848 100644 --- a/.github/workflows/spec_guards.yml +++ b/.github/workflows/spec_guards.yml @@ -45,7 +45,7 @@ jobs: - ruby-3.3 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index b808ac5e9e2baa..697056df604751 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -58,7 +58,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 09f8e4bbccdff4..764ca2d21ce8f3 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -61,7 +61,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d4b9bd644aaf96..fcf600263bb84c 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -123,7 +123,7 @@ jobs: Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH shell: pwsh - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml index c6acd3c614b2d0..bd17b302ae72a1 100644 --- a/.github/workflows/yjit-macos.yml +++ b/.github/workflows/yjit-macos.yml @@ -37,7 +37,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: RUST_BACKTRACE=1 cargo test working-directory: yjit @@ -81,7 +81,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 2b0c79db28436d..bd7e25a9cd62a2 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -36,7 +36,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 # For now we can't run cargo test --offline because it complains about the # capstone dependency, even though the dependency is optional @@ -68,7 +68,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 # Check that we don't have linting errors in release mode, too - run: cargo clippy --all-targets --all-features @@ -127,7 +127,7 @@ jobs: )}} steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github From 534f1c37e3d7527080921fcc0dd3a42a83ac311d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 May 2024 02:24:44 +0000 Subject: [PATCH 071/242] Bump actions/checkout in /.github/actions/setup/directories Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.5 to 4.1.6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/44c2b7a8a4ea60a981eaca3cf939b5f4305c123b...a5ac7e51b41094c92402da3b24376905380afc29) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/actions/setup/directories/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup/directories/action.yml b/.github/actions/setup/directories/action.yml index db1ae87fffefc5..5264e0e9692b75 100644 --- a/.github/actions/setup/directories/action.yml +++ b/.github/actions/setup/directories/action.yml @@ -88,7 +88,7 @@ runs: git config --global init.defaultBranch garbage - if: inputs.checkout - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: path: ${{ inputs.srcdir }} fetch-depth: ${{ inputs.fetch-depth }} From 10d02e71d1f57b68f530c0e669e4441fbcdc48ba Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 17 May 2024 17:17:13 +0900 Subject: [PATCH 072/242] Update bundled_gems --- gems/bundled_gems | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gems/bundled_gems b/gems/bundled_gems index c6bb0f9f545c67..16e16fe5a83830 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -6,11 +6,11 @@ # - revision: revision in repository-url to test # if `revision` is not given, "v"+`version` or `version` will be used. -minitest 5.22.3 https://github.com/minitest/minitest ea9caafc0754b1d6236a490d59e624b53209734a +minitest 5.23.0 https://github.com/minitest/minitest power_assert 2.0.3 https://github.com/ruby/power_assert 84e85124c5014a139af39161d484156cfe87a9ed rake 13.2.1 https://github.com/ruby/rake test-unit 3.6.2 https://github.com/test-unit/test-unit -rexml 3.2.6 https://github.com/ruby/rexml +rexml 3.2.8 https://github.com/ruby/rexml rss 0.3.0 https://github.com/ruby/rss net-ftp 0.3.4 https://github.com/ruby/net-ftp net-imap 0.4.11 https://github.com/ruby/net-imap From 8acec5b6e8a8ed102885da467d47c30657a9a73f Mon Sep 17 00:00:00 2001 From: git Date: Fri, 17 May 2024 08:40:30 +0000 Subject: [PATCH 073/242] Update bundled gems list at 10d02e71d1f57b68f530c0e669e444 [ci skip] --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index ce626248a566ad..5972eac924ca40 100644 --- a/NEWS.md +++ b/NEWS.md @@ -58,9 +58,10 @@ The following default gems are updated. The following bundled gems are updated. -* minitest 5.22.3 +* minitest 5.23.0 * rake 13.2.1 * test-unit 3.6.2 +* rexml 3.2.8 * net-ftp 0.3.4 * net-imap 0.4.11 * net-smtp 0.5.0 From 2d825ca7d0d8d57b49327c3760ec650776ebe60e Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 17 May 2024 22:02:40 +0900 Subject: [PATCH 074/242] REXML 3.2.8 is not working with rss and test-bundled-gems yet. https://github.com/ruby/ruby/actions/runs/9125437794/job/25091614796#step:11:272 --- gems/bundled_gems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/bundled_gems b/gems/bundled_gems index 16e16fe5a83830..bb6dcb5447b992 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -10,7 +10,7 @@ minitest 5.23.0 https://github.com/minitest/minitest power_assert 2.0.3 https://github.com/ruby/power_assert 84e85124c5014a139af39161d484156cfe87a9ed rake 13.2.1 https://github.com/ruby/rake test-unit 3.6.2 https://github.com/test-unit/test-unit -rexml 3.2.8 https://github.com/ruby/rexml +rexml 3.2.6 https://github.com/ruby/rexml rss 0.3.0 https://github.com/ruby/rss net-ftp 0.3.4 https://github.com/ruby/net-ftp net-imap 0.4.11 https://github.com/ruby/net-imap From 4ba0579da651ef3373fce59034f0fcb8b0ad9e5f Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 17 May 2024 09:45:08 -0400 Subject: [PATCH 075/242] [PRISM] Enable TestSyntax#test_error_message_encoding --- prism_compile.c | 3 ++- test/.excludes-prism/TestSyntax.rb | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prism_compile.c b/prism_compile.c index 5ecc69470f9b1c..a53d94f5c2cec3 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -9269,7 +9269,8 @@ pm_parse_process_error(const pm_parse_result_t *result) } } - VALUE error = rb_exc_new(rb_eSyntaxError, pm_buffer_value(&buffer), pm_buffer_length(&buffer)); + VALUE message = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), result->node.encoding); + VALUE error = rb_exc_new_str(rb_eSyntaxError, message); rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding(); VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding); diff --git a/test/.excludes-prism/TestSyntax.rb b/test/.excludes-prism/TestSyntax.rb index 6e9f51604a370e..0505330b1cc399 100644 --- a/test/.excludes-prism/TestSyntax.rb +++ b/test/.excludes-prism/TestSyntax.rb @@ -1,5 +1,4 @@ exclude(:test_dedented_heredoc_continued_line, "heredoc line continuation dedent calculation") -exclude(:test_error_message_encoding, "syntax error message encoding") exclude(:test_it, "https://github.com/ruby/prism/issues/2323") exclude(:test_numbered_parameter, "should raise syntax error for numbered parameters in inner blocks") exclude(:test_unterminated_heredoc_cr, "quoted \r heredoc terminators should not match \r\n") From aed93ba19ead1186974e1d88ca20a73c5b8b95e7 Mon Sep 17 00:00:00 2001 From: S-H-GAMELINKS Date: Fri, 17 May 2024 11:59:35 +0900 Subject: [PATCH 076/242] [ruby/prism] Replace and Use PM_LOCATION_NULL_VALUE macro https://github.com/ruby/prism/commit/50e188b168 --- prism/prism.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index d203b4b7202065..b22330e1d6d41e 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -1630,7 +1630,7 @@ not_provided(pm_parser_t *parser) { return (pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }; } -#define PM_LOCATION_NULL_VALUE(parser) ((pm_location_t) { .start = parser->start, .end = parser->start }) +#define PM_LOCATION_NULL_VALUE(parser) ((pm_location_t) { .start = (parser)->start, .end = (parser)->start }) #define PM_LOCATION_TOKEN_VALUE(token) ((pm_location_t) { .start = (token)->start, .end = (token)->end }) #define PM_LOCATION_NODE_VALUE(node) ((pm_location_t) { .start = (node)->location.start, .end = (node)->location.end }) #define PM_LOCATION_NODE_BASE_VALUE(node) ((pm_location_t) { .start = (node)->base.location.start, .end = (node)->base.location.end }) @@ -2827,8 +2827,7 @@ static pm_call_node_t * pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) { pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY); - node->base.location.start = parser->start; - node->base.location.end = parser->start; + node->base.location = PM_LOCATION_NULL_VALUE(parser); node->arguments = arguments; node->name = name; @@ -4621,7 +4620,7 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant *node = (pm_global_variable_read_node_t) { { .type = PM_GLOBAL_VARIABLE_READ_NODE, - .location = { .start = parser->start, .end = parser->start } + .location = PM_LOCATION_NULL_VALUE(parser) }, .name = name }; @@ -4663,11 +4662,11 @@ pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constan *node = (pm_global_variable_write_node_t) { { .type = PM_GLOBAL_VARIABLE_WRITE_NODE, - .location = { .start = parser->start, .end = parser->start } + .location = PM_LOCATION_NULL_VALUE(parser) }, .name = name, - .name_loc = { .start = parser->start, .end = parser->start }, - .operator_loc = { .start = parser->start, .end = parser->start }, + .name_loc = PM_LOCATION_NULL_VALUE(parser), + .operator_loc = PM_LOCATION_NULL_VALUE(parser), .value = value }; @@ -7355,9 +7354,9 @@ pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) { { .type = PM_SYMBOL_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, - .location = { .start = parser->start, .end = parser->start } + .location = PM_LOCATION_NULL_VALUE(parser) }, - .value_loc = { .start = parser->start, .end = parser->start }, + .value_loc = PM_LOCATION_NULL_VALUE(parser), .unescaped = { 0 } }; @@ -7483,7 +7482,7 @@ pm_true_node_synthesized_create(pm_parser_t *parser) { *node = (pm_true_node_t) {{ .type = PM_TRUE_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, - .location = { .start = parser->start, .end = parser->end } + .location = PM_LOCATION_NULL_VALUE(parser) }}; return node; @@ -7758,10 +7757,10 @@ pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_s *node = (pm_while_node_t) { { .type = PM_WHILE_NODE, - .location = { .start = parser->start, .end = parser->start } + .location = PM_LOCATION_NULL_VALUE(parser) }, - .keyword_loc = { .start = parser->start, .end = parser->start }, - .closing_loc = { .start = parser->start, .end = parser->start }, + .keyword_loc = PM_LOCATION_NULL_VALUE(parser), + .closing_loc = PM_LOCATION_NULL_VALUE(parser), .predicate = predicate, .statements = statements }; From 0649c1e3bc6d20034aac352046953a3a275d3745 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 17 May 2024 10:47:13 -0400 Subject: [PATCH 077/242] [ruby/prism] Revert incorrect synthesized true node change https://github.com/ruby/prism/commit/31ef81fd06 --- prism/prism.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prism/prism.c b/prism/prism.c index b22330e1d6d41e..02c208808988e9 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -7482,7 +7482,7 @@ pm_true_node_synthesized_create(pm_parser_t *parser) { *node = (pm_true_node_t) {{ .type = PM_TRUE_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, - .location = PM_LOCATION_NULL_VALUE(parser) + .location = { .start = parser->start, .end = parser->end } }}; return node; From c60cdbdc9818a8aee091dc4c07aeb78096828ffc Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 17 May 2024 10:35:30 -0400 Subject: [PATCH 078/242] [PRISM] Emit END event for modules --- prism_compile.c | 2 +- spec/prism.mspec | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/prism_compile.c b/prism_compile.c index a53d94f5c2cec3..9207f7a5f556bd 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -8745,7 +8745,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, break; } - if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE)) { + if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) { const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, scope_node->ast_node); ADD_TRACE(ret, RUBY_EVENT_END); ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line; diff --git a/spec/prism.mspec b/spec/prism.mspec index 3014b9788feac1..42956c6e7bc353 100644 --- a/spec/prism.mspec +++ b/spec/prism.mspec @@ -1,8 +1,4 @@ # frozen_string_literal: true -# We are missing emitting some :end event inside eval; we need more -# investigation here. -MSpec.register(:exclude, "TracePoint#path equals \"(eval at __FILE__:__LINE__)\" inside an eval for :end event") - # We need to respect the eval coverage setting. MSpec.register(:exclude, "Coverage.result returns the correct results when eval coverage is disabled") From e61d24d77e53a4114da64948c072380c7270d309 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 17 May 2024 10:43:31 -0400 Subject: [PATCH 079/242] [PRISM] Enable passing IRB test --- test/.excludes-prism/TestIRB/RubyLexTest.rb | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test/.excludes-prism/TestIRB/RubyLexTest.rb diff --git a/test/.excludes-prism/TestIRB/RubyLexTest.rb b/test/.excludes-prism/TestIRB/RubyLexTest.rb deleted file mode 100644 index a2d0a574525d8e..00000000000000 --- a/test/.excludes-prism/TestIRB/RubyLexTest.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_code_block_open_with_should_continue, "https://github.com/ruby/irb/pull/956") From cce7c25a42cbe46b9daea7c1e0accda00fe91c43 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 17 May 2024 11:33:08 -0400 Subject: [PATCH 080/242] [PRISM] Enable TestRequire --- prism_compile.c | 1 + test/.excludes-prism/TestRequire.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 test/.excludes-prism/TestRequire.rb diff --git a/prism_compile.c b/prism_compile.c index 9207f7a5f556bd..44ac70d2fa276c 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -9454,6 +9454,7 @@ pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error) VALUE pm_parse_file(pm_parse_result_t *result, VALUE filepath) { + result->node.filepath_encoding = rb_enc_get(filepath); pm_options_filepath_set(&result->options, RSTRING_PTR(filepath)); RB_GC_GUARD(filepath); diff --git a/test/.excludes-prism/TestRequire.rb b/test/.excludes-prism/TestRequire.rb deleted file mode 100644 index cb35d59c6008be..00000000000000 --- a/test/.excludes-prism/TestRequire.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_require_nonascii_path_shift_jis, "requiring non-ascii paths") From 55c62e676f3b30b47978684ab871e9d6e906fc27 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Wed, 15 May 2024 09:32:36 +0900 Subject: [PATCH 081/242] No need to specify tags anymore In the past, these codes were used by both parser and ripper. On ripper, the type of LHS is `` then type cast was needed. However currently these are only used by parser then no need to cast. --- parse.y | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/parse.y b/parse.y index 5fe9babefaea05..6365f4c89ba6c0 100644 --- a/parse.y +++ b/parse.y @@ -3613,14 +3613,14 @@ command : fcall command_args %prec tLOWEST { NODE *args = 0; args = ret_args(p, $2); - $$ = add_block_exit(p, NEW_BREAK(args, &@$)); + $$ = add_block_exit(p, NEW_BREAK(args, &@$)); /*% ripper: break!($:2) %*/ } | keyword_next call_args { NODE *args = 0; args = ret_args(p, $2); - $$ = add_block_exit(p, NEW_NEXT(args, &@$)); + $$ = add_block_exit(p, NEW_NEXT(args, &@$)); /*% ripper: next!($:2) %*/ } ; @@ -4790,17 +4790,17 @@ primary : literal } | keyword_break { - $$ = add_block_exit(p, NEW_BREAK(0, &@$)); + $$ = add_block_exit(p, NEW_BREAK(0, &@$)); /*% ripper: break!(args_new!) %*/ } | keyword_next { - $$ = add_block_exit(p, NEW_NEXT(0, &@$)); + $$ = add_block_exit(p, NEW_NEXT(0, &@$)); /*% ripper: next!(args_new!) %*/ } | keyword_redo { - $$ = add_block_exit(p, NEW_REDO(&@$)); + $$ = add_block_exit(p, NEW_REDO(&@$)); /*% ripper: redo! %*/ } | keyword_retry From ac85eef18ba0f5afa088c6a463f2ed8163692766 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 17 May 2024 23:15:42 +0100 Subject: [PATCH 082/242] Fix links --- complex.c | 4 ++-- string.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/complex.c b/complex.c index ff278f80fae3a6..f562ed316123c7 100644 --- a/complex.c +++ b/complex.c @@ -2470,7 +2470,7 @@ float_arg(VALUE self) * * The rectangular coordinates of a complex number * are called the _real_ and _imaginary_ parts; - * see {Complex number definition}[https://en.wikipedia.org/wiki/Complex_number#Definition]. + * see {Complex number definition}[https://en.wikipedia.org/wiki/Complex_number#Definition_and_basic_operations]. * * You can create a \Complex object from rectangular coordinates with: * @@ -2495,7 +2495,7 @@ float_arg(VALUE self) * * The polar coordinates of a complex number * are called the _absolute_ and _argument_ parts; - * see {Complex polar plane}[https://en.wikipedia.org/wiki/Complex_number#Polar_complex_plane]. + * see {Complex polar plane}[https://en.wikipedia.org/wiki/Complex_number#Polar_form]. * * In this class, the argument part * in expressed {radians}[https://en.wikipedia.org/wiki/Radian] diff --git a/string.rb b/string.rb index 00d76cbe03f0b3..bec4e8cecfbf2c 100644 --- a/string.rb +++ b/string.rb @@ -311,7 +311,7 @@ # - {Comparing}[rdoc-ref:String@Methods+for+Comparing] # - {Modifying a String}[rdoc-ref:String@Methods+for+Modifying+a+String] # - {Converting to New String}[rdoc-ref:String@Methods+for+Converting+to+New+String] -# - {Converting to Non-String}[rdoc-ref:String@Methods+for+Converting+to+Non--5CString] +# - {Converting to Non-String}[rdoc-ref:String@Methods+for+Converting+to+Non-String] # - {Iterating}[rdoc-ref:String@Methods+for+Iterating] # # === Methods for Creating a +String+ From 232f7b37cf1e13d0fff6c8ffc8a342f1c126cd98 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 18 May 2024 19:46:05 +0900 Subject: [PATCH 083/242] Replace cast tags with typed midrule actions * Add types to `tLAMBDA` and `tSTRING_DBEG` to store corresponding information when returning these tokens. * Add `enum lex_state_e state` to `%union` for `tSTRING_DBEG`. --- parse.y | 64 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/parse.y b/parse.y index 6365f4c89ba6c0..de52170cb4e041 100644 --- a/parse.y +++ b/parse.y @@ -2770,6 +2770,7 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) const struct vtable *vars; struct rb_strterm_struct *strterm; struct lex_context ctxt; + enum lex_state_e state; } %token @@ -2934,7 +2935,7 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) %token tSTAR "*" %token tDSTAR "**arg" %token tAMPER "&" -%token tLAMBDA "->" +%token tLAMBDA "->" %token tSYMBEG "symbol literal" %token tSTRING_BEG "string literal" %token tXSTRING_BEG "backtick literal" @@ -2945,7 +2946,8 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) %token tQSYMBOLS_BEG "verbatim symbol list" %token tSTRING_END "terminator" %token tSTRING_DEND "'}'" -%token tSTRING_DBEG tSTRING_DVAR tLAMBEG tLABEL_END +%token tSTRING_DBEG "'#{'" +%token tSTRING_DVAR tLAMBEG tLABEL_END %token tIGNORED_NL tCOMMENT tEMBDOC_BEG tEMBDOC tEMBDOC_END %token tHEREDOC_BEG tHEREDOC_END k__END__ @@ -4641,7 +4643,7 @@ primary : literal k_end { if (CASE_LABELS_ENABLED_P(p->case_labels)) st_free_table(p->case_labels); - p->case_labels = $4; + p->case_labels = $4; $$ = NEW_CASE($2, $5, &@$); fixpos($$, $2); /*% ripper: case!($:2, $:5) %*/ @@ -4655,7 +4657,7 @@ primary : literal k_end { if (p->case_labels) st_free_table(p->case_labels); - p->case_labels = $3; + p->case_labels = $3; $$ = NEW_CASE2($4, &@$); /*% ripper: case!(Qnil, $:4) %*/ } @@ -5288,13 +5290,12 @@ it_id : { } ; -lambda : tLAMBDA[dyna] +lambda : tLAMBDA[lpar] { token_info_push(p, "->", &@1); - $dyna = dyna_push(p); - $$ = p->lex.lpar_beg; + $$ = dyna_push(p); p->lex.lpar_beg = p->lex.paren_nest; - }[lpar] + }[dyna] max_numparam numparam it_id allow_exits f_larglist[args] { @@ -5304,7 +5305,7 @@ lambda : tLAMBDA[dyna] { int max_numparam = p->max_numparam; ID it_id = p->it_id; - p->lex.lpar_beg = $lpar; + p->lex.lpar_beg = $lpar; p->max_numparam = $max_numparam; p->it_id = $it_id; restore_block_exit(p, $allow_exits); @@ -5319,7 +5320,7 @@ lambda : tLAMBDA[dyna] } /*% ripper: lambda!($:args, $:body) %*/ numparam_pop(p, $numparam); - dyna_pop(p, $dyna); + dyna_pop(p, $dyna); } ; @@ -5461,7 +5462,7 @@ brace_block : '{' brace_body '}' } ; -brace_body : {$$ = dyna_push(p);}[dyna] +brace_body : {$$ = dyna_push(p);}[dyna] max_numparam numparam it_id allow_exits opt_block_param[args] compstmt { @@ -5474,28 +5475,28 @@ brace_body : {$$ = dyna_push(p);}[dyna] /*% ripper: brace_block!($:args, $:compstmt) %*/ restore_block_exit(p, $allow_exits); numparam_pop(p, $numparam); - dyna_pop(p, $dyna); + dyna_pop(p, $dyna); } ; do_body : { - $$ = dyna_push(p); + $$ = dyna_push(p); CMDARG_PUSH(0); - }[dyna] + }[dyna] max_numparam numparam it_id allow_exits opt_block_param[args] bodystmt { int max_numparam = p->max_numparam; ID it_id = p->it_id; p->max_numparam = $max_numparam; - p->it_id = $it_id; + p->it_id = $it_id; $args = args_with_numbered(p, $args, max_numparam, it_id); $$ = NEW_ITER($args, $bodystmt, &@$); /*% ripper: do_block!($:args, $:bodystmt) %*/ CMDARG_POP(); restore_block_exit(p, $allow_exits); numparam_pop(p, $numparam); - dyna_pop(p, $dyna); + dyna_pop(p, $dyna); } ; @@ -6318,32 +6319,31 @@ string_content : tSTRING_CONTENT nd_set_line($$, @3.end_pos.lineno); /*% ripper: string_dvar!($:3) %*/ } - | tSTRING_DBEG[term] + | tSTRING_DBEG[state] { CMDARG_PUSH(0); COND_PUSH(0); /* need to backup p->lex.strterm so that a string literal `%!foo,#{ !0 },bar!` can be parsed */ - $term = p->lex.strterm; + $$ = p->lex.strterm; p->lex.strterm = 0; - $$ = p->lex.state; SET_LEX_STATE(EXPR_BEG); - }[state] + }[term] { - $$ = p->lex.brace_nest; + $$ = p->lex.brace_nest; p->lex.brace_nest = 0; - }[brace] + }[brace] { - $$ = p->heredoc_indent; + $$ = p->heredoc_indent; p->heredoc_indent = 0; - }[indent] + }[indent] compstmt string_dend { COND_POP(); CMDARG_POP(); - p->lex.strterm = $term; - SET_LEX_STATE($state); - p->lex.brace_nest = $brace; - p->heredoc_indent = $indent; + p->lex.strterm = $term; + SET_LEX_STATE($state); + p->lex.brace_nest = $brace; + p->heredoc_indent = $indent; p->heredoc_line_indent = -1; if ($compstmt) nd_unset_fl_newline($compstmt); $$ = new_evstr(p, $compstmt, &@$); @@ -6506,14 +6506,14 @@ f_paren_args : '(' f_args rparen f_arglist : f_paren_args | { - $$ = p->ctxt; + $$ = p->ctxt; p->ctxt.in_kwarg = 1; p->ctxt.in_argdef = 1; SET_LEX_STATE(p->lex.state|EXPR_LABEL); /* force for args */ - } + } f_args term { - p->ctxt.in_kwarg = $1.in_kwarg; + p->ctxt.in_kwarg = $1.in_kwarg; p->ctxt.in_argdef = 0; $$ = $2; SET_LEX_STATE(EXPR_BEG); @@ -8837,6 +8837,7 @@ parser_peek_variable_name(struct parser_params *p) case '{': p->lex.pcur = ptr; p->command_start = TRUE; + yylval.state = p->lex.state; return tSTRING_DBEG; default: return 0; @@ -11219,6 +11220,7 @@ parser_yylex(struct parser_params *p) } if (c == '>') { SET_LEX_STATE(EXPR_ENDFN); + yylval.num = p->lex.lpar_beg; return tLAMBDA; } if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p, '-'))) { From fd8e6e8c54708c2cfde689afe5762b4e206ffe31 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 19 May 2024 00:27:34 +0900 Subject: [PATCH 084/242] Replace cast tags for `tSTRING_DVAR` with typed midrule actions --- parse.y | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parse.y b/parse.y index de52170cb4e041..38b8a74e4f5b9e 100644 --- a/parse.y +++ b/parse.y @@ -6308,13 +6308,13 @@ string_content : tSTRING_CONTENT | tSTRING_DVAR { /* need to backup p->lex.strterm so that a string literal `%&foo,#$&,bar&` can be parsed */ - $$ = p->lex.strterm; + $$ = p->lex.strterm; p->lex.strterm = 0; SET_LEX_STATE(EXPR_BEG); - } + } string_dvar { - p->lex.strterm = $2; + p->lex.strterm = $2; $$ = NEW_EVSTR($3, &@$); nd_set_line($$, @3.end_pos.lineno); /*% ripper: string_dvar!($:3) %*/ From dbbaf871de70243b78e0d2f3d3575351317b5c5c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 19 May 2024 00:29:00 +0900 Subject: [PATCH 085/242] [DOC] Fix `$<` comment --- parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse.y b/parse.y index 38b8a74e4f5b9e..9b363171ed4572 100644 --- a/parse.y +++ b/parse.y @@ -10490,7 +10490,7 @@ parse_gvar(struct parser_params *p, const enum lex_state_e last_state) case '.': /* $.: last read line number */ case '=': /* $=: ignorecase */ case ':': /* $:: load path */ - case '<': /* $<: reading filename */ + case '<': /* $<: default input handle */ case '>': /* $>: default output handle */ case '\"': /* $": already loaded files */ tokadd(p, '$'); From ad636033e2fdafb417873a3cb8667351033307b1 Mon Sep 17 00:00:00 2001 From: Andrew Konchin Date: Thu, 9 May 2024 20:35:01 +0300 Subject: [PATCH 086/242] Update to ruby/spec@6b04c1d --- spec/ruby/core/binding/dup_spec.rb | 17 +++++++ spec/ruby/core/enumerator/next_values_spec.rb | 8 +++- spec/ruby/core/enumerator/peek_values_spec.rb | 8 +++- spec/ruby/core/io/pread_spec.rb | 9 +++- spec/ruby/core/io/read_spec.rb | 15 ++++++ spec/ruby/core/io/readpartial_spec.rb | 3 +- spec/ruby/core/io/sysread_spec.rb | 9 +++- spec/ruby/core/module/include_spec.rb | 28 +++++++++++ spec/ruby/core/module/prepend_spec.rb | 48 +++++++++++++++++++ .../core/warning/performance_warning_spec.rb | 28 +++++++++++ spec/ruby/language/break_spec.rb | 19 ++++++++ spec/ruby/language/execution_spec.rb | 4 +- spec/ruby/language/hash_spec.rb | 32 +++++++++++++ spec/ruby/language/keyword_arguments_spec.rb | 28 +++++++++++ .../language/regexp/back-references_spec.rb | 9 ++++ spec/ruby/language/retry_spec.rb | 5 +- spec/ruby/language/yield_spec.rb | 12 +++++ .../library/{ => date}/time/to_date_spec.rb | 2 +- .../{ => datetime}/time/to_datetime_spec.rb | 2 +- .../socket/basicsocket/recv_nonblock_spec.rb | 16 +++++-- .../library/socket/basicsocket/recv_spec.rb | 22 +++++++-- .../socket/socket/recvfrom_nonblock_spec.rb | 21 ++++++++ .../udpsocket/recvfrom_nonblock_spec.rb | 9 ++++ .../socket/unixsocket/recvfrom_spec.rb | 23 +++++++++ spec/ruby/library/stringio/shared/read.rb | 20 +++++++- spec/ruby/optional/capi/class_spec.rb | 12 +++++ spec/ruby/optional/capi/ext/class_spec.c | 5 ++ spec/ruby/optional/capi/ext/gc_spec.c | 10 ++++ spec/ruby/optional/capi/ext/object_spec.c | 16 +++---- spec/ruby/optional/capi/gc_spec.rb | 4 ++ spec/ruby/shared/kernel/at_exit.rb | 6 +++ 31 files changed, 425 insertions(+), 25 deletions(-) create mode 100644 spec/ruby/core/warning/performance_warning_spec.rb rename spec/ruby/library/{ => date}/time/to_date_spec.rb (97%) rename spec/ruby/library/{ => datetime}/time/to_datetime_spec.rb (97%) diff --git a/spec/ruby/core/binding/dup_spec.rb b/spec/ruby/core/binding/dup_spec.rb index 55fac6e3332a84..4eff66bd9a4cc9 100644 --- a/spec/ruby/core/binding/dup_spec.rb +++ b/spec/ruby/core/binding/dup_spec.rb @@ -10,4 +10,21 @@ bind.frozen?.should == true bind.dup.frozen?.should == false end + + it "retains original binding variables but the list is distinct" do + bind1 = binding + eval "a = 1", bind1 + + bind2 = bind1.dup + eval("a = 2", bind2) + eval("a", bind1).should == 2 + eval("a", bind2).should == 2 + + eval("b = 2", bind2) + -> { eval("b", bind1) }.should raise_error(NameError) + eval("b", bind2).should == 2 + + bind1.local_variables.sort.should == [:a, :bind1, :bind2] + bind2.local_variables.sort.should == [:a, :b, :bind1, :bind2] + end end diff --git a/spec/ruby/core/enumerator/next_values_spec.rb b/spec/ruby/core/enumerator/next_values_spec.rb index 201b5d323ffbf8..2202700c588832 100644 --- a/spec/ruby/core/enumerator/next_values_spec.rb +++ b/spec/ruby/core/enumerator/next_values_spec.rb @@ -11,6 +11,7 @@ def o.each yield :e1, :e2, :e3 yield nil yield + yield [:f1, :f2] end @e = o.to_enum @@ -48,8 +49,13 @@ def o.each @e.next_values.should == [] end - it "raises StopIteration if called on a finished enumerator" do + it "returns an array of array if yield is called with an array" do 7.times { @e.next } + @e.next_values.should == [[:f1, :f2]] + end + + it "raises StopIteration if called on a finished enumerator" do + 8.times { @e.next } -> { @e.next_values }.should raise_error(StopIteration) end end diff --git a/spec/ruby/core/enumerator/peek_values_spec.rb b/spec/ruby/core/enumerator/peek_values_spec.rb index 7865546515d460..8b84fc8afcde8f 100644 --- a/spec/ruby/core/enumerator/peek_values_spec.rb +++ b/spec/ruby/core/enumerator/peek_values_spec.rb @@ -11,6 +11,7 @@ def o.each yield :e1, :e2, :e3 yield nil yield + yield [:f1, :f2] end @e = o.to_enum @@ -50,8 +51,13 @@ def o.each @e.peek_values.should == [] end - it "raises StopIteration if called on a finished enumerator" do + it "returns an array of array if yield is called with an array" do 7.times { @e.next } + @e.peek_values.should == [[:f1, :f2]] + end + + it "raises StopIteration if called on a finished enumerator" do + 8.times { @e.next } -> { @e.peek_values }.should raise_error(StopIteration) end end diff --git a/spec/ruby/core/io/pread_spec.rb b/spec/ruby/core/io/pread_spec.rb index 28afc80e5c614f..6d93b432c2636d 100644 --- a/spec/ruby/core/io/pread_spec.rb +++ b/spec/ruby/core/io/pread_spec.rb @@ -22,7 +22,7 @@ it "accepts a length, an offset, and an output buffer" do buffer = +"foo" - @file.pread(3, 4, buffer) + @file.pread(3, 4, buffer).should.equal?(buffer) buffer.should == "567" end @@ -38,6 +38,13 @@ buffer.should == "12345" end + it "preserves the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + @file.pread(10, 0, buffer) + + buffer.encoding.should == Encoding::ISO_8859_1 + end + it "does not advance the file pointer" do @file.pread(4, 0).should == "1234" @file.read.should == "1234567890" diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb index eb3652e692a581..8741d9f0179a85 100644 --- a/spec/ruby/core/io/read_spec.rb +++ b/spec/ruby/core/io/read_spec.rb @@ -376,6 +376,21 @@ buf.should == @contents[0..4] end + it "preserves the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + @io.read(10, buffer) + + buffer.encoding.should == Encoding::ISO_8859_1 + end + + # https://bugs.ruby-lang.org/issues/20416 + it "does not preserve the encoding of the given buffer when max length is not provided" do + buffer = ''.encode(Encoding::ISO_8859_1) + @io.read(nil, buffer) + + buffer.encoding.should_not == Encoding::ISO_8859_1 + end + it "returns the given buffer" do buf = +"" diff --git a/spec/ruby/core/io/readpartial_spec.rb b/spec/ruby/core/io/readpartial_spec.rb index 0060beb54504a5..0852f20b2da83b 100644 --- a/spec/ruby/core/io/readpartial_spec.rb +++ b/spec/ruby/core/io/readpartial_spec.rb @@ -62,7 +62,7 @@ buffer = +"existing content" @wr.write("hello world") @wr.close - @rd.readpartial(11, buffer) + @rd.readpartial(11, buffer).should.equal?(buffer) buffer.should == "hello world" end @@ -106,6 +106,7 @@ @wr.write("abc") @wr.close @rd.readpartial(10, buffer) + buffer.encoding.should == Encoding::ISO_8859_1 end end diff --git a/spec/ruby/core/io/sysread_spec.rb b/spec/ruby/core/io/sysread_spec.rb index 003bb9eb94911a..8851214283ca54 100644 --- a/spec/ruby/core/io/sysread_spec.rb +++ b/spec/ruby/core/io/sysread_spec.rb @@ -97,7 +97,7 @@ it "discards the existing buffer content upon successful read" do buffer = +"existing content" - @file.sysread(11, buffer) + @file.sysread(11, buffer).should.equal?(buffer) buffer.should == "01234567890" end @@ -107,6 +107,13 @@ -> { @file.sysread(1, buffer) }.should raise_error(EOFError) buffer.should be_empty end + + it "preserves the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + string = @file.sysread(10, buffer) + + buffer.encoding.should == Encoding::ISO_8859_1 + end end describe "IO#sysread" do diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb index c073bc31ca0eea..78f6b410317142 100644 --- a/spec/ruby/core/module/include_spec.rb +++ b/spec/ruby/core/module/include_spec.rb @@ -47,6 +47,34 @@ def self.append_features(mod) -> { ModuleSpecs::SubclassSpec.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) end + ruby_version_is ""..."3.2" do + it "raises ArgumentError when the argument is a refinement" do + refinement = nil + + Module.new do + refine String do + refinement = self + end + end + + -> { ModuleSpecs::Basic.include(refinement) }.should raise_error(ArgumentError, "refinement module is not allowed") + end + end + + ruby_version_is "3.2" do + it "raises a TypeError when the argument is a refinement" do + refinement = nil + + Module.new do + refine String do + refinement = self + end + end + + -> { ModuleSpecs::Basic.include(refinement) }.should raise_error(TypeError, "Cannot include refinement") + end + end + it "imports constants to modules and classes" do ModuleSpecs::A.constants.should include(:CONSTANT_A) ModuleSpecs::B.constants.should include(:CONSTANT_A, :CONSTANT_B) diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb index c90fa9700ec3b4..b40d12f0deba2d 100644 --- a/spec/ruby/core/module/prepend_spec.rb +++ b/spec/ruby/core/module/prepend_spec.rb @@ -75,6 +75,26 @@ def foo foo.call.should == 'm' end + it "updates the optimized method when a prepended module is updated" do + out = ruby_exe(<<~RUBY) + module M; end + class Integer + prepend M + end + l = -> { 1 + 2 } + p l.call + M.module_eval do + def +(o) + $called = true + super(o) + end + end + p l.call + p $called + RUBY + out.should == "3\n3\ntrue\n" + end + it "updates the method when there is a base included method and the prepended module overrides it" do base_module = Module.new do def foo @@ -415,6 +435,34 @@ module M -> { ModuleSpecs::SubclassSpec.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) end + ruby_version_is ""..."3.2" do + it "raises ArgumentError when the argument is a refinement" do + refinement = nil + + Module.new do + refine String do + refinement = self + end + end + + -> { ModuleSpecs::Basic.prepend(refinement) }.should raise_error(ArgumentError, "refinement module is not allowed") + end + end + + ruby_version_is "3.2" do + it "raises a TypeError when the argument is a refinement" do + refinement = nil + + Module.new do + refine String do + refinement = self + end + end + + -> { ModuleSpecs::Basic.prepend(refinement) }.should raise_error(TypeError, "Cannot prepend refinement") + end + end + it "imports constants" do m1 = Module.new m1::MY_CONSTANT = 1 diff --git a/spec/ruby/core/warning/performance_warning_spec.rb b/spec/ruby/core/warning/performance_warning_spec.rb new file mode 100644 index 00000000000000..ab0badcd3d7f33 --- /dev/null +++ b/spec/ruby/core/warning/performance_warning_spec.rb @@ -0,0 +1,28 @@ +require_relative '../../spec_helper' + + +describe "Performance warnings" do + guard -> { ruby_version_is("3.4") || RUBY_ENGINE == "truffleruby" } do + # Optimising Integer, Float or Symbol methods is kind of implementation detail + # but multiple implementations do so. So it seems reasonable to have a test case + # for at least one such common method. + # See https://bugs.ruby-lang.org/issues/20429 + context "when redefined optimised methods" do + it "emits performance warning for redefining Integer#+" do + code = <<~CODE + Warning[:performance] = true + + class Integer + ORIG_METHOD = instance_method(:+) + + def +(...) + ORIG_METHOD.bind(self).call(...) + end + end + CODE + + ruby_exe(code, args: "2>&1").should.include?("warning: Redefining 'Integer#+' disables interpreter and JIT optimizations") + end + end + end +end diff --git a/spec/ruby/language/break_spec.rb b/spec/ruby/language/break_spec.rb index e725e77e80d0b3..7e5b6fb32859c2 100644 --- a/spec/ruby/language/break_spec.rb +++ b/spec/ruby/language/break_spec.rb @@ -252,6 +252,25 @@ def mid(&b) end end +describe "The break statement in a method" do + it "is invalid and raises a SyntaxError" do + -> { + eval("def m; break; end") + }.should raise_error(SyntaxError) + end +end + +describe "The break statement in a module literal" do + it "is invalid and raises a SyntaxError" do + code = <<~RUBY + module BreakSpecs:ModuleWithBreak + break + end + RUBY + + -> { eval(code) }.should raise_error(SyntaxError) + end +end # TODO: Rewrite all the specs from here to the end of the file in the style # above. diff --git a/spec/ruby/language/execution_spec.rb b/spec/ruby/language/execution_spec.rb index ef1de38899809d..51bcde62e8c745 100644 --- a/spec/ruby/language/execution_spec.rb +++ b/spec/ruby/language/execution_spec.rb @@ -38,7 +38,7 @@ 2.times do runner.instance_exec do - `test #{:command}` + `test #{:command}` # rubocop:disable Lint/LiteralInInterpolation end end @@ -84,7 +84,7 @@ 2.times do runner.instance_exec do - %x{test #{:command}} + %x{test #{:command}} # rubocop:disable Lint/LiteralInInterpolation end end diff --git a/spec/ruby/language/hash_spec.rb b/spec/ruby/language/hash_spec.rb index a7631fb0d6b6fb..068ac0f39c6a5f 100644 --- a/spec/ruby/language/hash_spec.rb +++ b/spec/ruby/language/hash_spec.rb @@ -86,6 +86,30 @@ -> { eval("{:a ==> 1}") }.should raise_error(SyntaxError) end + it "recognizes '!' at the end of the key" do + eval("{:a! =>1}").should == {:"a!" => 1} + eval("{:a! => 1}").should == {:"a!" => 1} + + eval("{a!:1}").should == {:"a!" => 1} + eval("{a!: 1}").should == {:"a!" => 1} + end + + it "raises a SyntaxError if there is no space between `!` and `=>`" do + -> { eval("{:a!=> 1}") }.should raise_error(SyntaxError) + end + + it "recognizes '?' at the end of the key" do + eval("{:a? =>1}").should == {:"a?" => 1} + eval("{:a? => 1}").should == {:"a?" => 1} + + eval("{a?:1}").should == {:"a?" => 1} + eval("{a?: 1}").should == {:"a?" => 1} + end + + it "raises a SyntaxError if there is no space between `?` and `=>`" do + -> { eval("{:a?=> 1}") }.should raise_error(SyntaxError) + end + it "constructs a new hash with the given elements" do {foo: 123}.should == {foo: 123} h = {rbx: :cool, specs: 'fail_sometimes'} @@ -271,6 +295,14 @@ def foo(val) a.new.foo(1).should == {bar: "baz", val: 1} end + + it "raises a SyntaxError when the hash key ends with `!`" do + -> { eval("{a!:}") }.should raise_error(SyntaxError, /identifier a! is not valid to get/) + end + + it "raises a SyntaxError when the hash key ends with `?`" do + -> { eval("{a?:}") }.should raise_error(SyntaxError, /identifier a\? is not valid to get/) + end end end end diff --git a/spec/ruby/language/keyword_arguments_spec.rb b/spec/ruby/language/keyword_arguments_spec.rb index ffb5b1fab06cc4..8668799d26cd1e 100644 --- a/spec/ruby/language/keyword_arguments_spec.rb +++ b/spec/ruby/language/keyword_arguments_spec.rb @@ -395,4 +395,32 @@ def n(*args) # Note the missing ruby2_keywords here end end end + + context "in define_method(name, &proc)" do + # This tests that a free-standing proc used in define_method and converted to ruby2_keywords adopts that logic. + # See jruby/jruby#8119 for a case where aggressive JIT optimization broke later ruby2_keywords changes. + it "works with ruby2_keywords" do + m = Class.new do + def bar(a, foo: nil) + [a, foo] + end + + # define_method and ruby2_keywords using send to avoid peephole optimizations + def self.setup + pr = make_proc + send :define_method, :foo, &pr + send :ruby2_keywords, :foo + end + + # create proc in isolated method to force jit compilation on some implementations + def self.make_proc + proc { |a, *args| bar(a, *args) } + end + end + + m.setup + + m.new.foo(1, foo:2).should == [1, 2] + end + end end diff --git a/spec/ruby/language/regexp/back-references_spec.rb b/spec/ruby/language/regexp/back-references_spec.rb index 26750c20c59a86..627c8daacefa42 100644 --- a/spec/ruby/language/regexp/back-references_spec.rb +++ b/spec/ruby/language/regexp/back-references_spec.rb @@ -22,6 +22,15 @@ $10.should == "0" end + it "returns nil for numbered variable with too large index" do + -> { + eval(<<~CODE).should == nil + "a" =~ /(.)/ + eval('$4294967296') + CODE + }.should complain(/warning: ('|`)\$4294967296' is too big for a number variable, always nil/) + end + it "will not clobber capture variables across threads" do cap1, cap2, cap3 = nil "foo" =~ /(o+)/ diff --git a/spec/ruby/language/retry_spec.rb b/spec/ruby/language/retry_spec.rb index ee5377946f7ef9..669d5f0ff51904 100644 --- a/spec/ruby/language/retry_spec.rb +++ b/spec/ruby/language/retry_spec.rb @@ -31,8 +31,11 @@ results.should == [1, 2, 3, 1, 2, 4, 5, 6, 4, 5] end - it "raises a SyntaxError when used outside of a begin statement" do + it "raises a SyntaxError when used outside of a rescue statement" do -> { eval 'retry' }.should raise_error(SyntaxError) + -> { eval 'begin; retry; end' }.should raise_error(SyntaxError) + -> { eval 'def m; retry; end' }.should raise_error(SyntaxError) + -> { eval 'module RetrySpecs; retry; end' }.should raise_error(SyntaxError) end end diff --git a/spec/ruby/language/yield_spec.rb b/spec/ruby/language/yield_spec.rb index 5283517636d202..e125cf8e738bea 100644 --- a/spec/ruby/language/yield_spec.rb +++ b/spec/ruby/language/yield_spec.rb @@ -206,3 +206,15 @@ class << Object.new -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/) end end + +describe "Using yield in a module literal" do + it 'raises a SyntaxError' do + code = <<~RUBY + module YieldSpecs::ModuleWithYield + yield + end + RUBY + + -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/) + end +end diff --git a/spec/ruby/library/time/to_date_spec.rb b/spec/ruby/library/date/time/to_date_spec.rb similarity index 97% rename from spec/ruby/library/time/to_date_spec.rb rename to spec/ruby/library/date/time/to_date_spec.rb index baeafe0847a7d2..f9132da2890e92 100644 --- a/spec/ruby/library/time/to_date_spec.rb +++ b/spec/ruby/library/date/time/to_date_spec.rb @@ -1,5 +1,5 @@ -require_relative '../../spec_helper' +require_relative '../../../spec_helper' require 'time' describe "Time#to_date" do diff --git a/spec/ruby/library/time/to_datetime_spec.rb b/spec/ruby/library/datetime/time/to_datetime_spec.rb similarity index 97% rename from spec/ruby/library/time/to_datetime_spec.rb rename to spec/ruby/library/datetime/time/to_datetime_spec.rb index 9c44f38e5c2ff1..1125dbe851c8fa 100644 --- a/spec/ruby/library/time/to_datetime_spec.rb +++ b/spec/ruby/library/datetime/time/to_datetime_spec.rb @@ -1,4 +1,4 @@ -require_relative '../../spec_helper' +require_relative '../../../spec_helper' require 'time' require 'date' date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0' diff --git a/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb index 17c846054df17a..df42c116fb33f1 100644 --- a/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb +++ b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb @@ -52,9 +52,19 @@ @s2.send("data", 0, @s1.getsockname) IO.select([@s1], nil, nil, 2) - buf = +"foo" - @s1.recv_nonblock(5, 0, buf) - buf.should == "data" + buffer = +"foo" + @s1.recv_nonblock(5, 0, buffer).should.equal?(buffer) + buffer.should == "data" + end + + it "preserves the encoding of the given buffer" do + @s1.bind(Socket.pack_sockaddr_in(0, ip_address)) + @s2.send("data", 0, @s1.getsockname) + IO.select([@s1], nil, nil, 2) + + buffer = ''.encode(Encoding::ISO_8859_1) + @s1.recv_nonblock(5, 0, buffer) + buffer.encoding.should == Encoding::ISO_8859_1 end it "does not block if there's no data available" do diff --git a/spec/ruby/library/socket/basicsocket/recv_spec.rb b/spec/ruby/library/socket/basicsocket/recv_spec.rb index 9fe8c52f9a5233..e82a357d3dfd35 100644 --- a/spec/ruby/library/socket/basicsocket/recv_spec.rb +++ b/spec/ruby/library/socket/basicsocket/recv_spec.rb @@ -100,13 +100,29 @@ socket.write("data") client = @server.accept - buf = +"foo" + buffer = +"foo" begin - client.recv(4, 0, buf) + client.recv(4, 0, buffer).should.equal?(buffer) ensure client.close end - buf.should == "data" + buffer.should == "data" + + socket.close + end + + it "preserves the encoding of the given buffer" do + socket = TCPSocket.new('127.0.0.1', @port) + socket.write("data") + + client = @server.accept + buffer = ''.encode(Encoding::ISO_8859_1) + begin + client.recv(4, 0, buffer) + ensure + client.close + end + buffer.encoding.should == Encoding::ISO_8859_1 socket.close end diff --git a/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb b/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb index 94f58ac49f4ac6..5596f91bb86b07 100644 --- a/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb +++ b/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb @@ -52,6 +52,27 @@ end end + it "allows an output buffer as third argument" do + @client.write('hello') + + IO.select([@server]) + buffer = +'' + message, = @server.recvfrom_nonblock(5, 0, buffer) + + message.should.equal?(buffer) + buffer.should == 'hello' + end + + it "preserves the encoding of the given buffer" do + @client.write('hello') + + IO.select([@server]) + buffer = ''.encode(Encoding::ISO_8859_1) + @server.recvfrom_nonblock(5, 0, buffer) + + buffer.encoding.should == Encoding::ISO_8859_1 + end + describe 'the returned data' do it 'is the same as the sent data' do 5.times do diff --git a/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb b/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb index 650a061221b8c1..b8040995897c34 100644 --- a/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb +++ b/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb @@ -58,6 +58,15 @@ buffer.should == 'h' end + it "preserves the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + IO.select([@server]) + message, = @server.recvfrom_nonblock(1, 0, buffer) + + message.should.equal?(buffer) + buffer.encoding.should == Encoding::ISO_8859_1 + end + describe 'the returned Array' do before do IO.select([@server]) diff --git a/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb index fedf74bb2fa2ac..d849fdc302646b 100644 --- a/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb +++ b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb @@ -31,6 +31,29 @@ sock.close end + it "allows an output buffer as third argument" do + buffer = +'' + + @client.send("foobar", 0) + sock = @server.accept + message, = sock.recvfrom(6, 0, buffer) + sock.close + + message.should.equal?(buffer) + buffer.should == "foobar" + end + + it "preserves the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + + @client.send("foobar", 0) + sock = @server.accept + sock.recvfrom(6, 0, buffer) + sock.close + + buffer.encoding.should == Encoding::ISO_8859_1 + end + it "uses different message options" do @client.send("foobar", Socket::MSG_PEEK) sock = @server.accept diff --git a/spec/ruby/library/stringio/shared/read.rb b/spec/ruby/library/stringio/shared/read.rb index e3840786d902fa..8ef6ec273427a1 100644 --- a/spec/ruby/library/stringio/shared/read.rb +++ b/spec/ruby/library/stringio/shared/read.rb @@ -11,10 +11,28 @@ end it "reads length bytes and writes them to the buffer String" do - @io.send(@method, 7, buffer = +"") + @io.send(@method, 7, buffer = +"").should.equal?(buffer) buffer.should == "example" end + ruby_version_is ""..."3.4" do + it "does not preserve the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + @io.send(@method, 7, buffer) + + buffer.encoding.should_not == Encoding::ISO_8859_1 + end + end + + ruby_version_is "3.4" do + it "preserves the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + @io.send(@method, 7, buffer) + + buffer.encoding.should == Encoding::ISO_8859_1 + end + end + it "tries to convert the passed buffer Object to a String using #to_str" do obj = mock("to_str") obj.should_receive(:to_str).and_return(buffer = +"") diff --git a/spec/ruby/optional/capi/class_spec.rb b/spec/ruby/optional/capi/class_spec.rb index d0a9913570ddb2..a231245ebe8158 100644 --- a/spec/ruby/optional/capi/class_spec.rb +++ b/spec/ruby/optional/capi/class_spec.rb @@ -487,4 +487,16 @@ def obj.some_method() end @s.rb_class_real(0).should == 0 end end + + describe "rb_class_get_superclass" do + it "returns parent class for a provided class" do + a = Class.new + @s.rb_class_get_superclass(Class.new(a)).should == a + end + + it "returns false when there is no parent class" do + @s.rb_class_get_superclass(BasicObject).should == false + @s.rb_class_get_superclass(Module.new).should == false + end + end end diff --git a/spec/ruby/optional/capi/ext/class_spec.c b/spec/ruby/optional/capi/ext/class_spec.c index f376534924cf1c..c13f02ecf23c79 100644 --- a/spec/ruby/optional/capi/ext/class_spec.c +++ b/spec/ruby/optional/capi/ext/class_spec.c @@ -79,6 +79,10 @@ static VALUE class_spec_rb_class_real(VALUE self, VALUE object) { } } +static VALUE class_spec_rb_class_get_superclass(VALUE self, VALUE klass) { + return rb_class_get_superclass(klass); +} + static VALUE class_spec_rb_class_superclass(VALUE self, VALUE klass) { return rb_class_superclass(klass); } @@ -160,6 +164,7 @@ void Init_class_spec(void) { rb_define_method(cls, "rb_class_new_instance_kw", class_spec_rb_class_new_instance_kw, 2); #endif rb_define_method(cls, "rb_class_real", class_spec_rb_class_real, 1); + rb_define_method(cls, "rb_class_get_superclass", class_spec_rb_class_get_superclass, 1); rb_define_method(cls, "rb_class_superclass", class_spec_rb_class_superclass, 1); rb_define_method(cls, "rb_cvar_defined", class_spec_cvar_defined, 2); rb_define_method(cls, "rb_cvar_get", class_spec_cvar_get, 2); diff --git a/spec/ruby/optional/capi/ext/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c index 1392bc6ee668da..2637ad27ac570a 100644 --- a/spec/ruby/optional/capi/ext/gc_spec.c +++ b/spec/ruby/optional/capi/ext/gc_spec.c @@ -16,6 +16,8 @@ VALUE registered_after_rb_global_variable_bignum; VALUE registered_after_rb_global_variable_float; VALUE rb_gc_register_address_outside_init; +VALUE rb_gc_register_mark_object_not_referenced_float; + static VALUE registered_tagged_address(VALUE self) { return registered_tagged_value; } @@ -90,6 +92,10 @@ static VALUE gc_spec_rb_gc_register_mark_object(VALUE self, VALUE obj) { return Qnil; } +static VALUE gc_spec_rb_gc_register_mark_object_not_referenced_float(VALUE self) { + return rb_gc_register_mark_object_not_referenced_float; +} + void Init_gc_spec(void) { VALUE cls = rb_define_class("CApiGCSpecs", rb_cObject); @@ -115,6 +121,9 @@ void Init_gc_spec(void) { registered_after_rb_global_variable_float = DBL2NUM(6.28); rb_global_variable(®istered_after_rb_global_variable_float); + rb_gc_register_mark_object_not_referenced_float = DBL2NUM(1.61); + rb_gc_register_mark_object(rb_gc_register_mark_object_not_referenced_float); + rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0); rb_define_method(cls, "registered_reference_address", registered_reference_address, 0); rb_define_method(cls, "registered_before_rb_gc_register_address", get_registered_before_rb_gc_register_address, 0); @@ -131,6 +140,7 @@ void Init_gc_spec(void) { rb_define_method(cls, "rb_gc", gc_spec_rb_gc, 0); rb_define_method(cls, "rb_gc_adjust_memory_usage", gc_spec_rb_gc_adjust_memory_usage, 1); rb_define_method(cls, "rb_gc_register_mark_object", gc_spec_rb_gc_register_mark_object, 1); + rb_define_method(cls, "rb_gc_register_mark_object_not_referenced_float", gc_spec_rb_gc_register_mark_object_not_referenced_float, 0); rb_define_method(cls, "rb_gc_latest_gc_info", gc_spec_rb_gc_latest_gc_info, 1); } diff --git a/spec/ruby/optional/capi/ext/object_spec.c b/spec/ruby/optional/capi/ext/object_spec.c index 8aa98cc5ce24c1..aa60662e1e3bb2 100644 --- a/spec/ruby/optional/capi/ext/object_spec.c +++ b/spec/ruby/optional/capi/ext/object_spec.c @@ -390,22 +390,22 @@ static VALUE speced_allocator(VALUE klass) { return instance; } -static VALUE define_alloc_func(VALUE self, VALUE klass) { +static VALUE object_spec_rb_define_alloc_func(VALUE self, VALUE klass) { rb_define_alloc_func(klass, speced_allocator); return Qnil; } -static VALUE undef_alloc_func(VALUE self, VALUE klass) { +static VALUE object_spec_rb_undef_alloc_func(VALUE self, VALUE klass) { rb_undef_alloc_func(klass); return Qnil; } -static VALUE speced_allocator_p(VALUE self, VALUE klass) { +static VALUE object_spec_speced_allocator_p(VALUE self, VALUE klass) { rb_alloc_func_t allocator = rb_get_alloc_func(klass); return (allocator == speced_allocator) ? Qtrue : Qfalse; } -static VALUE custom_alloc_func_p(VALUE self, VALUE klass) { +static VALUE object_spec_custom_alloc_func_p(VALUE self, VALUE klass) { rb_alloc_func_t allocator = rb_get_alloc_func(klass); return allocator ? Qtrue : Qfalse; } @@ -485,10 +485,10 @@ void Init_object_spec(void) { rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2); rb_define_method(cls, "rb_copy_generic_ivar", object_spec_rb_copy_generic_ivar, 2); rb_define_method(cls, "rb_free_generic_ivar", object_spec_rb_free_generic_ivar, 1); - rb_define_method(cls, "rb_define_alloc_func", define_alloc_func, 1); - rb_define_method(cls, "rb_undef_alloc_func", undef_alloc_func, 1); - rb_define_method(cls, "speced_allocator?", speced_allocator_p, 1); - rb_define_method(cls, "custom_alloc_func?", custom_alloc_func_p, 1); + rb_define_method(cls, "rb_define_alloc_func", object_spec_rb_define_alloc_func, 1); + rb_define_method(cls, "rb_undef_alloc_func", object_spec_rb_undef_alloc_func, 1); + rb_define_method(cls, "speced_allocator?", object_spec_speced_allocator_p, 1); + rb_define_method(cls, "custom_alloc_func?", object_spec_custom_alloc_func_p, 1); rb_define_method(cls, "not_implemented_method", rb_f_notimplement, -1); rb_define_method(cls, "rb_ivar_foreach", object_spec_rb_ivar_foreach, 1); } diff --git a/spec/ruby/optional/capi/gc_spec.rb b/spec/ruby/optional/capi/gc_spec.rb index aaced56483a0cc..d9661328ab39a0 100644 --- a/spec/ruby/optional/capi/gc_spec.rb +++ b/spec/ruby/optional/capi/gc_spec.rb @@ -108,6 +108,10 @@ it "can be called with an object" do @f.rb_gc_register_mark_object(Object.new).should be_nil end + + it "keeps the value alive even if the value is not referenced by any Ruby object" do + @f.rb_gc_register_mark_object_not_referenced_float.should == 1.61 + end end describe "rb_gc_latest_gc_info" do diff --git a/spec/ruby/shared/kernel/at_exit.rb b/spec/ruby/shared/kernel/at_exit.rb index 16d41cb01c93b1..29db79bb391428 100644 --- a/spec/ruby/shared/kernel/at_exit.rb +++ b/spec/ruby/shared/kernel/at_exit.rb @@ -30,6 +30,12 @@ result.lines.should.include?("The exception matches: true (message=foo)\n") end + it "gives access to an exception raised in a previous handler" do + code = "#{@method} { print '$!.message = ' + $!.message }; #{@method} { raise 'foo' }" + result = ruby_exe(code, args: "2>&1", exit_status: 1) + result.lines.should.include?("$!.message = foo") + end + it "both exceptions in a handler and in the main script are printed" do code = "#{@method} { raise 'at_exit_error' }; raise 'main_script_error'" result = ruby_exe(code, args: "2>&1", exit_status: 1) From 7afc16aa48beb093b06eb978bc430f90dd771690 Mon Sep 17 00:00:00 2001 From: KJ Tsanaktsidis Date: Sat, 18 May 2024 18:37:49 +0900 Subject: [PATCH 087/242] Inline RB_VM_SAVE_MACHINE_CONTEXT into BLOCKING_REGION There's an exhaustive explanation of this in the linked redmine bug, but the short version is as follows: blocking_region_begin can spill callee-saved registers to the stack for its own use. That means they're not saved to ec->machine by the call to setjmp, since by that point they're already on the stack and new, different values are in the real registers. ec->machine's end-of-stack pointer is also bumped to accomodate this, BUT, after blocking_region_begin returns, that points past the end of the stack! As far as C is concerned, that's fine; the callee-saved registers are restored when blocking_region_begin returns. But, if another thread triggers GC, it is relying on finding references to Ruby objects by walking the stack region pointed to by ec->machine. If the C code in exec; subsequently does things that use that stack memory, then the value will be overwritten and the GC might prematurely collect something it shouldn't. [Bug #20493] --- thread.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/thread.c b/thread.c index b8ba61e188bb2e..80537fd41dfee9 100644 --- a/thread.c +++ b/thread.c @@ -197,6 +197,10 @@ static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_regio if (blocking_region_begin(th, &__region, (ubf), (ubfarg), fail_if_interrupted) || \ /* always return true unless fail_if_interrupted */ \ !only_if_constant(fail_if_interrupted, TRUE)) { \ + /* Important that this is inlined into the macro, and not part of \ + * blocking_region_begin - see bug #20493 */ \ + RB_VM_SAVE_MACHINE_CONTEXT(th); \ + thread_sched_to_waiting(TH_SCHED(th), th); \ exec; \ blocking_region_end(th, &__region); \ }; \ @@ -1482,9 +1486,6 @@ blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region, rb_ractor_blocking_threads_inc(th->ractor, __FILE__, __LINE__); RUBY_DEBUG_LOG("thread_id:%p", (void *)th->nt->thread_id); - - RB_VM_SAVE_MACHINE_CONTEXT(th); - thread_sched_to_waiting(TH_SCHED(th), th); return TRUE; } else { @@ -1893,6 +1894,8 @@ rb_thread_call_with_gvl(void *(*func)(void *), void *data1) /* leave from Ruby world: You can not access Ruby values, etc. */ int released = blocking_region_begin(th, brb, prev_unblock.func, prev_unblock.arg, FALSE); RUBY_ASSERT_ALWAYS(released); + RB_VM_SAVE_MACHINE_CONTEXT(th); + thread_sched_to_waiting(TH_SCHED(th), th); return r; } From b47533f67b5c4c758b05cdce0fcd0f978fbfe8b9 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 19 May 2024 20:44:02 +0900 Subject: [PATCH 088/242] Remove `rb_bug` after COMPILE_ERROR Fix test failures since 7afc16aa48beb093b06eb978bc430f90dd771690. Why crash after reported compile error properly. --- prism_compile.c | 1 - 1 file changed, 1 deletion(-) diff --git a/prism_compile.c b/prism_compile.c index 44ac70d2fa276c..05dab99dc21ca0 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -5406,7 +5406,6 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } COMPILE_ERROR(ERROR_ARGS "Invalid break"); - rb_bug("Invalid break"); } return; } From 8c0b57d3ee68caea093cb73b5f014a303f37bbb4 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 19 May 2024 20:22:20 +0900 Subject: [PATCH 089/242] `rb_enc_compile_warn` and `rb_enc_compile_warning` are printf format --- internal/error.h | 2 ++ prism_compile.c | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/error.h b/internal/error.h index 7e41f134d7c78c..7a4daca6b36bfd 100644 --- a/internal/error.h +++ b/internal/error.h @@ -142,6 +142,8 @@ VALUE rb_syntax_error_append(VALUE, VALUE, int, int, rb_encoding*, const char*, PRINTF_ARGS(void rb_enc_warn(rb_encoding *enc, const char *fmt, ...), 2, 3); PRINTF_ARGS(void rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...), 2, 3); PRINTF_ARGS(void rb_syserr_enc_warning(int err, rb_encoding *enc, const char *fmt, ...), 3, 4); +PRINTF_ARGS(void rb_enc_compile_warning(rb_encoding *enc, const char *file, int line, const char *fmt, ...), 4, 5); +PRINTF_ARGS(void rb_enc_compile_warn(rb_encoding *enc, const char *file, int line, const char *fmt, ...), 4, 5); rb_warning_category_t rb_warning_category_from_name(VALUE category); bool rb_warning_category_enabled_p(rb_warning_category_t category); VALUE rb_name_err_new(VALUE mesg, VALUE recv, VALUE method); diff --git a/prism_compile.c b/prism_compile.c index 05dab99dc21ca0..675b4cf86a3122 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -9280,9 +9280,6 @@ pm_parse_process_error(const pm_parse_result_t *result) return error; } -void rb_enc_compile_warning(rb_encoding *enc, const char *file, int line, const char *fmt, ...); -void rb_enc_compile_warn(rb_encoding *enc, const char *file, int line, const char *fmt, ...); - /** * Parse the parse result and raise a Ruby error if there are any syntax errors. * It returns an error if one should be raised. It is assumed that the parse From cb43540d536e378bcac388c730dfdebb404989e7 Mon Sep 17 00:00:00 2001 From: TOMITA Masahiro Date: Sun, 19 May 2024 18:21:51 +0900 Subject: [PATCH 090/242] Fix IO::Buffer comment --- io_buffer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/io_buffer.c b/io_buffer.c index af884f14739a41..5210d4732b6e21 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -2441,13 +2441,13 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source) * * #copy can be used to put buffer into strings associated with buffer: * - * string= "buffer: " - * # => "buffer: " + * string= "data: " + * # => "data: " * buffer = IO::Buffer.for(string) * buffer.copy(IO::Buffer.for("test"), 5) * # => 4 * string - * # => "buffer:test" + * # => "data:test" * * Attempt to copy into a read-only buffer will fail: * @@ -3571,7 +3571,7 @@ io_buffer_not_inplace(VALUE self) * * \Buffer from string: * - * string = 'buffer' + * string = 'data' * buffer = IO::Buffer.for(string) * # => * # # @@ -3579,7 +3579,7 @@ io_buffer_not_inplace(VALUE self) * buffer * # => * # # - * # 0x00000000 64 61 74 61 buffer + * # 0x00000000 64 61 74 61 data * * buffer.get_string(2) # read content starting from offset 2 * # => "ta" @@ -3594,7 +3594,7 @@ io_buffer_not_inplace(VALUE self) * * \Buffer from file: * - * File.write('test.txt', 'test buffer') + * File.write('test.txt', 'test data') * # => 9 * buffer = IO::Buffer.map(File.open('test.txt')) * # => @@ -3611,7 +3611,7 @@ io_buffer_not_inplace(VALUE self) * buffer.set_string('---', 1) * # => 3 -- bytes written * File.read('test.txt') - * # => "t--- buffer" + * # => "t--- data" * * The class is experimental and the interface is subject to change, this * is especially true of file mappings which may be removed entirely in From 48ebd77e595d569b225151ef281aae537d95d6f5 Mon Sep 17 00:00:00 2001 From: TOMITA Masahiro Date: Sun, 19 May 2024 20:21:15 +0900 Subject: [PATCH 091/242] Fix comment: Buffer.for(string) without block returns readonly buffer --- io_buffer.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/io_buffer.c b/io_buffer.c index 5210d4732b6e21..e6ca61bdae0136 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -3572,25 +3572,23 @@ io_buffer_not_inplace(VALUE self) * \Buffer from string: * * string = 'data' - * buffer = IO::Buffer.for(string) - * # => - * # # - * # ... - * buffer - * # => - * # # - * # 0x00000000 64 61 74 61 data - * - * buffer.get_string(2) # read content starting from offset 2 - * # => "ta" - * buffer.set_string('---', 1) # write content, starting from offset 1 - * # => 3 - * buffer - * # => - * # # - * # 0x00000000 64 2d 2d 2d d--- - * string # original string changed, too - * # => "d---" + * IO::Buffer.for(string) do |buffer| + * buffer + * # => + * # # + * # 0x00000000 64 61 74 61 data + * + * buffer.get_string(2) # read content starting from offset 2 + * # => "ta" + * buffer.set_string('---', 1) # write content, starting from offset 1 + * # => 3 + * buffer + * # => + * # # + * # 0x00000000 64 2d 2d 2d d--- + * string # original string changed, too + * # => "d---" + * end * * \Buffer from file: * From d037c5196a14c03e72746ccdf0437b5dd4f80a69 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 20 May 2024 01:30:49 +0900 Subject: [PATCH 092/242] Suppress -Wclobbered warnings At 7afc16aa48beb093b06eb978bc430f90dd771690, now `BLOCKING_REGION` contains `setjmp` call in `RB_VM_SAVE_MACHINE_CONTEXT`. By this change, variables in blocks for this macro may be clobbered. --- thread.c | 54 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/thread.c b/thread.c index 80537fd41dfee9..25e4cf1b748791 100644 --- a/thread.c +++ b/thread.c @@ -1768,7 +1768,7 @@ rb_thread_mn_schedulable(VALUE thval) VALUE rb_thread_io_blocking_call(rb_blocking_function_t *func, void *data1, int fd, int events) { - rb_execution_context_t * volatile ec = GET_EC(); + rb_execution_context_t *volatile ec = GET_EC(); rb_thread_t *th = rb_ec_thread_ptr(ec); RUBY_DEBUG_LOG("th:%u fd:%d ev:%d", rb_th_serial(th), fd, events); @@ -1796,6 +1796,7 @@ rb_thread_io_blocking_call(rb_blocking_function_t *func, void *data1, int fd, in saved_errno = errno; }, ubf_select, waiting_fd.th, FALSE); + th = rb_ec_thread_ptr(ec); if (events && blocking_call_retryable_p((int)val, saved_errno) && thread_io_wait_events(th, fd, events, NULL)) { @@ -1805,6 +1806,7 @@ rb_thread_io_blocking_call(rb_blocking_function_t *func, void *data1, int fd, in } EC_POP_TAG(); + th = rb_ec_thread_ptr(ec); th->mn_schedulable = prev_mn_schedulable; } /* @@ -4208,9 +4210,10 @@ rb_fd_set(int fd, rb_fdset_t *set) #endif static int -wait_retryable(int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end) +wait_retryable(volatile int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end) { - if (*result < 0) { + int r = *result; + if (r < 0) { switch (errnum) { case EINTR: #ifdef ERESTART @@ -4224,7 +4227,7 @@ wait_retryable(int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end) } return FALSE; } - else if (*result == 0) { + else if (r == 0) { /* check for spurious wakeup */ if (rel) { return !hrtime_update_expire(rel, end); @@ -4262,11 +4265,12 @@ static VALUE do_select(VALUE p) { struct select_set *set = (struct select_set *)p; - int result = 0; + volatile int result = 0; int lerrno; rb_hrtime_t *to, rel, end = 0; timeout_prepare(&to, &rel, &end, set->timeout); + volatile rb_hrtime_t endtime = end; #define restore_fdset(dst, src) \ ((dst) ? rb_fd_dup(dst, src) : (void)0) #define do_select_update() \ @@ -4282,15 +4286,15 @@ do_select(VALUE p) struct timeval tv; if (!RUBY_VM_INTERRUPTED(set->th->ec)) { - result = native_fd_select(set->max, - set->rset, set->wset, set->eset, - rb_hrtime2timeval(&tv, to), set->th); + result = native_fd_select(set->max, + set->rset, set->wset, set->eset, + rb_hrtime2timeval(&tv, to), set->th); if (result < 0) lerrno = errno; } }, ubf_select, set->th, TRUE); RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */ - } while (wait_retryable(&result, lerrno, to, end) && do_select_update()); + } while (wait_retryable(&result, lerrno, to, endtime) && do_select_update()); if (result < 0) { errno = lerrno; @@ -4352,6 +4356,23 @@ rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * # define POLLERR_SET (0) #endif +static int +wait_for_single_fd_blocking_region(rb_thread_t *th, struct pollfd *fds, nfds_t nfds, + rb_hrtime_t *const to, volatile int *lerrno) +{ + struct timespec ts; + volatile int result = 0; + + *lerrno = 0; + BLOCKING_REGION(th, { + if (!RUBY_VM_INTERRUPTED(th->ec)) { + result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0); + if (result < 0) *lerrno = errno; + } + }, ubf_select, th, TRUE); + return result; +} + /* * returns a mask of events */ @@ -4363,7 +4384,7 @@ rb_thread_wait_for_single_fd(int fd, int events, struct timeval *timeout) .events = (short)events, .revents = 0, }}; - int result = 0; + volatile int result = 0; nfds_t nfds; struct waiting_fd wfd; enum ruby_tag_type state; @@ -4387,17 +4408,8 @@ rb_thread_wait_for_single_fd(int fd, int events, struct timeval *timeout) RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec); timeout_prepare(&to, &rel, &end, timeout); do { - nfds = 1; - - lerrno = 0; - BLOCKING_REGION(wfd.th, { - struct timespec ts; - - if (!RUBY_VM_INTERRUPTED(wfd.th->ec)) { - result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0); - if (result < 0) lerrno = errno; - } - }, ubf_select, wfd.th, TRUE); + nfds = numberof(fds); + result = wait_for_single_fd_blocking_region(wfd.th, fds, nfds, to, &lerrno); RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec); } while (wait_retryable(&result, lerrno, to, end)); From a10a483af9786702152670e85b198dfc9156aea5 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 18 May 2024 15:34:49 +0900 Subject: [PATCH 093/242] [rubygems/rubygems] Should rescue vendored net-http exception https://github.com/rubygems/rubygems/commit/7d2c4cf364 --- test/rubygems/test_bundled_ca.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb index 50e621f22b490b..a737185681ee25 100644 --- a/test/rubygems/test_bundled_ca.rb +++ b/test/rubygems/test_bundled_ca.rb @@ -33,7 +33,7 @@ def assert_https(host) http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.cert_store = bundled_certificate_store http.get("/") - rescue Errno::ENOENT, Errno::ETIMEDOUT, SocketError, Net::OpenTimeout + rescue Errno::ENOENT, Errno::ETIMEDOUT, SocketError, Gem::Net::OpenTimeout pend "#{host} seems offline, I can't tell whether ssl would work." rescue OpenSSL::SSL::SSLError => e # Only fail for certificate verification errors From eb410c9fde271886709873fe0eff3bb2e621eb2b Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Mon, 20 May 2024 12:44:28 +0900 Subject: [PATCH 094/242] [ruby/error_highlight] Load "did_you_mean" explicitly in test I'm not sure how it works, but I seem to get an error `undefined method 'formatter' for module DidYouMean` in parallel mode of `make test-all`. https://app.launchableinc.com/organizations/ruby/workspaces/ruby/data/test-paths/file%3Dtest%2Ferror_highlight%2Ftest_error_highlight.rb%23class%3DErrorHighlightTest%23testcase%3Dtest_explicit_raise_name_error?organizationId=ruby&workspaceId=ruby&testPathId=file%3Dtest%2Ferror_highlight%2Ftest_error_highlight.rb%23class%3DErrorHighlightTest%23testcase%3Dtest_explicit_raise_name_error&testSessionStatus=flake https://github.com/ruby/error_highlight/commit/f931b42430 --- test/error_highlight/test_error_highlight.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/error_highlight/test_error_highlight.rb b/test/error_highlight/test_error_highlight.rb index 2095970af17968..69aef313391bc7 100644 --- a/test/error_highlight/test_error_highlight.rb +++ b/test/error_highlight/test_error_highlight.rb @@ -1,6 +1,7 @@ require "test/unit" require "error_highlight" +require "did_you_mean" require "tempfile" class ErrorHighlightTest < Test::Unit::TestCase From 18eaf0be905e3e251423b42d6f4e56b7cae1bc3b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 20 May 2024 11:44:27 +0900 Subject: [PATCH 095/242] [Bug #20494] Search non-default directories for GMP Co-Authored-by: lish82 (Hiroki Katagiri) --- configure.ac | 3 ++- tool/m4/ruby_check_header.m4 | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tool/m4/ruby_check_header.m4 diff --git a/configure.ac b/configure.ac index e9a452ebee2326..09f95b2beceb91 100644 --- a/configure.ac +++ b/configure.ac @@ -16,6 +16,7 @@ m4_include([tool/m4/ruby_append_option.m4])dnl m4_include([tool/m4/ruby_append_options.m4])dnl m4_include([tool/m4/ruby_check_builtin_func.m4])dnl m4_include([tool/m4/ruby_check_builtin_setjmp.m4])dnl +m4_include([tool/m4/ruby_check_header.m4])dnl m4_include([tool/m4/ruby_check_printf_prefix.m4])dnl m4_include([tool/m4/ruby_check_setjmp.m4])dnl m4_include([tool/m4/ruby_check_signedness.m4])dnl @@ -1375,7 +1376,7 @@ AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [ RUBY_UNIVERSAL_CHECK_HEADER([x86_64, i386], x86intrin.h) AS_IF([test "x$with_gmp" != xno], - [AC_CHECK_HEADERS(gmp.h) + [RUBY_CHECK_HEADER(gmp.h) AS_IF([test "x$ac_cv_header_gmp_h" != xno], AC_SEARCH_LIBS([__gmpz_init], [gmp], [AC_DEFINE(HAVE_LIBGMP, 1)]))]) diff --git a/tool/m4/ruby_check_header.m4 b/tool/m4/ruby_check_header.m4 new file mode 100644 index 00000000000000..171455549e3362 --- /dev/null +++ b/tool/m4/ruby_check_header.m4 @@ -0,0 +1,8 @@ +dnl -*- Autoconf -*- +AC_DEFUN([RUBY_CHECK_HEADER], + [# RUBY_CHECK_HEADER($@) + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS m4_if([$5], [], [$INCFLAGS], [$5])" + AC_CHECK_HEADER([$1], [$2], [$3], [$4]) + CPPFLAGS="$save_CPPFLAGS" + unset save_CPPFLAGS]) From 14da90f26294b0ccba872b433dbb726544f486f7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 20 May 2024 12:56:33 +0900 Subject: [PATCH 096/242] --with-gmp-dir option is for ruby itself, not extensions --- .github/actions/setup/macos/action.yml | 6 +++++- .github/workflows/macos.yml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup/macos/action.yml b/.github/actions/setup/macos/action.yml index 896930de84d161..b96e959aa69147 100644 --- a/.github/actions/setup/macos/action.yml +++ b/.github/actions/setup/macos/action.yml @@ -18,7 +18,11 @@ runs: - name: Set ENV shell: bash run: | - for lib in openssl@1.1 gmp; do + for lib in gmp; do + ruby_configure_args="${ruby_configure_args:+$ruby_configure_args }--with-${lib%@*}-dir=$(brew --prefix $lib)" + done + for lib in openssl@1.1; do CONFIGURE_ARGS="${CONFIGURE_ARGS:+$CONFIGURE_ARGS }--with-${lib%@*}-dir=$(brew --prefix $lib)" done + echo ruby_configure_args="${ruby_configure_args}" >> $GITHUB_ENV echo CONFIGURE_ARGS="${CONFIGURE_ARGS}" >> $GITHUB_ENV diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e71706b186070d..cc4e66cf16e1c1 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -71,7 +71,7 @@ jobs: fetch-depth: 10 - name: Run configure - run: ../src/configure -C --disable-install-doc + run: ../src/configure -C --disable-install-doc ${ruby_configure_args} - run: make prepare-gems if: ${{ matrix.test_task == 'test-bundled-gems' }} From fa26ef5bf715c7eb0bd80dd587a3f86f808f56af Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 20 May 2024 12:58:56 +0900 Subject: [PATCH 097/242] Fix the end of "compiler section" --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 09f95b2beceb91..d787bc3502eb71 100644 --- a/configure.ac +++ b/configure.ac @@ -989,7 +989,6 @@ AC_SUBST(incflags, "$INCFLAGS") 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\"" -} AC_CACHE_CHECK([whether compiler has statement and declarations in expressions], rb_cv_have_stmt_and_decl_in_expr, @@ -999,6 +998,7 @@ AC_CACHE_CHECK([whether compiler has statement and declarations in expressions], AS_IF([test "$rb_cv_have_stmt_and_decl_in_expr" = yes], [ AC_DEFINE(HAVE_STMT_AND_DECL_IN_EXPR) ]) +} [begin]_group "header and library section" && { AC_ARG_WITH(winnt-ver, From 0b38403328f02f2b103e15103b2ace69d123c9f3 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 20 May 2024 11:42:34 +0900 Subject: [PATCH 098/242] Fix incorrect assertion in TestThreadInstrumentation The test meant to assert the thread is suspended at least once, but was actually asserting to it to be suspected at least twice. --- test/-ext-/thread/test_instrumentation_api.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/-ext-/thread/test_instrumentation_api.rb b/test/-ext-/thread/test_instrumentation_api.rb index 9a3b67fa108512..663e41be537f22 100644 --- a/test/-ext-/thread/test_instrumentation_api.rb +++ b/test/-ext-/thread/test_instrumentation_api.rb @@ -54,7 +54,7 @@ def test_thread_pass_multi_thread thread&.join end - def test_muti_thread_timeline + def test_multi_thread_timeline threads = nil full_timeline = record do threads = threaded_cpu_bound_work(1.0) @@ -68,7 +68,7 @@ def test_muti_thread_timeline threads.each do |thread| timeline = timeline_for(thread, full_timeline) assert_consistent_timeline(timeline) - assert timeline.count(:suspended) > 1, "Expected threads to yield suspended at least once: #{timeline.inspect}" + assert_operator timeline.count(:suspended), :>=, 1, "Expected threads to yield suspended at least once: #{timeline.inspect}" end timeline = timeline_for(Thread.current, full_timeline) From 70ad58cb62b195ba86a5ef07a565b22b02a040ea Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 20 May 2024 17:09:41 +0900 Subject: [PATCH 099/242] Update bundled_gems --- gems/bundled_gems | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gems/bundled_gems b/gems/bundled_gems index bb6dcb5447b992..1ff6fbc314f024 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -10,7 +10,7 @@ minitest 5.23.0 https://github.com/minitest/minitest power_assert 2.0.3 https://github.com/ruby/power_assert 84e85124c5014a139af39161d484156cfe87a9ed rake 13.2.1 https://github.com/ruby/rake test-unit 3.6.2 https://github.com/test-unit/test-unit -rexml 3.2.6 https://github.com/ruby/rexml +rexml 3.2.8 https://github.com/ruby/rexml rss 0.3.0 https://github.com/ruby/rss net-ftp 0.3.4 https://github.com/ruby/net-ftp net-imap 0.4.11 https://github.com/ruby/net-imap @@ -21,7 +21,7 @@ prime 0.1.2 https://github.com/ruby/prime rbs 3.4.4 https://github.com/ruby/rbs ba7872795d5de04adb8ff500c0e6afdc81a041dd typeprof 0.21.11 https://github.com/ruby/typeprof b19a6416da3a05d57fadd6ffdadb382b6d236ca5 debug 1.9.2 https://github.com/ruby/debug -racc 1.7.3 https://github.com/ruby/racc +racc 1.8.0 https://github.com/ruby/racc mutex_m 0.2.0 https://github.com/ruby/mutex_m getoptlong 0.2.1 https://github.com/ruby/getoptlong base64 0.2.0 https://github.com/ruby/base64 From 7f0e26b7f99bf76408569892ce20318501f74729 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 20 May 2024 17:10:26 +0900 Subject: [PATCH 100/242] Re-use strscan with ruby repo --- tool/lib/bundled_gem.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tool/lib/bundled_gem.rb b/tool/lib/bundled_gem.rb index 3ba27f6d645e67..8730f0fb3ac535 100644 --- a/tool/lib/bundled_gem.rb +++ b/tool/lib/bundled_gem.rb @@ -11,7 +11,8 @@ module BundledGem "time", # net-ftp "singleton", # prime "ipaddr", # rinda - "forwardable" # prime, rinda + "forwardable", # prime, rinda + "strscan" # rexml ] module_function From 8248268434db7523093b2ac55ebf0e03e102ada9 Mon Sep 17 00:00:00 2001 From: git Date: Mon, 20 May 2024 09:10:55 +0000 Subject: [PATCH 101/242] Update bundled gems list at 7f0e26b7f99bf76408569892ce2031 [ci skip] --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 5972eac924ca40..558da7ebb50beb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -68,6 +68,7 @@ The following bundled gems are updated. * rbs 3.4.4 * typeprof 0.21.11 * debug 1.9.2 +* racc 1.8.0 The following bundled gems are promoted from default gems. From e90e8f8bd35391233f43d196d23b46ba8c20d56e Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 09:54:14 -0400 Subject: [PATCH 102/242] [ruby/prism] Implement ambiguous binary operator warning https://github.com/ruby/prism/commit/6258c3695f --- prism/config.yml | 1 + prism/prism.c | 36 +++++++++++++++++++++++++++- prism/templates/src/diagnostic.c.erb | 1 + test/prism/newline_test.rb | 1 + test/prism/warnings_test.rb | 17 +++++++++++++ 5 files changed, 55 insertions(+), 1 deletion(-) diff --git a/prism/config.yml b/prism/config.yml index 0652cc4a9fa2bf..6b4b1aa9c0e1dc 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -274,6 +274,7 @@ errors: - WRITE_TARGET_UNEXPECTED - XSTRING_TERM warnings: + - AMBIGUOUS_BINARY_OPERATOR - AMBIGUOUS_FIRST_ARGUMENT_MINUS - AMBIGUOUS_FIRST_ARGUMENT_PLUS - AMBIGUOUS_PREFIX_AMPERSAND diff --git a/prism/prism.c b/prism/prism.c index 02c208808988e9..b1d584d0ab9356 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -423,10 +423,18 @@ lex_mode_pop(pm_parser_t *parser) { * This is the equivalent of IS_lex_state is CRuby. */ static inline bool -lex_state_p(pm_parser_t *parser, pm_lex_state_t state) { +lex_state_p(const pm_parser_t *parser, pm_lex_state_t state) { return parser->lex_state & state; } +/** + * This is equivalent to the predicate of warn_balanced in CRuby. + */ +static inline bool +ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) { + return !lex_state_p(parser, PM_LEX_STATE_CLASS | PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME | PM_LEX_STATE_ENDFN) && space_seen && !pm_char_is_whitespace(*parser->current.end); +} + typedef enum { PM_IGNORED_NEWLINE_NONE = 0, PM_IGNORED_NEWLINE_ALL, @@ -10821,6 +10829,8 @@ parser_lex(pm_parser_t *parser) { type = PM_TOKEN_USTAR_STAR; } else if (lex_state_beg_p(parser)) { type = PM_TOKEN_USTAR_STAR; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix"); } if (lex_state_operator_p(parser)) { @@ -10844,6 +10854,8 @@ parser_lex(pm_parser_t *parser) { type = PM_TOKEN_USTAR; } else if (lex_state_beg_p(parser)) { type = PM_TOKEN_USTAR; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix"); } if (lex_state_operator_p(parser)) { @@ -11017,6 +11029,10 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_LESS_LESS_EQUAL); } + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document"); + } + if (lex_state_operator_p(parser)) { lex_state_set(parser, PM_LEX_STATE_ARG); } else { @@ -11130,6 +11146,8 @@ parser_lex(pm_parser_t *parser) { type = PM_TOKEN_UAMPERSAND; } else if (lex_state_beg_p(parser)) { type = PM_TOKEN_UAMPERSAND; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix"); } if (lex_state_operator_p(parser)) { @@ -11204,6 +11222,10 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_UPLUS); } + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator"); + } + lex_state_set(parser, PM_LEX_STATE_BEG); LEX(PM_TOKEN_PLUS); } @@ -11241,6 +11263,10 @@ parser_lex(pm_parser_t *parser) { LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS); } + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator"); + } + lex_state_set(parser, PM_LEX_STATE_BEG); LEX(PM_TOKEN_MINUS); } @@ -11339,6 +11365,10 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_REGEXP_BEGIN); } + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal"); + } + if (lex_state_operator_p(parser)) { lex_state_set(parser, PM_LEX_STATE_ARG); } else { @@ -11520,6 +11550,10 @@ parser_lex(pm_parser_t *parser) { } } + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal"); + } + lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG); LEX(PM_TOKEN_PERCENT); } diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index d9e195e08f3f0e..57c52602f42689 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -356,6 +356,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_XSTRING_TERM] = { "expected a closing delimiter for the `%x` or backtick string", PM_ERROR_LEVEL_SYNTAX }, // Warnings + [PM_WARN_AMBIGUOUS_BINARY_OPERATOR] = { "'%s' after local variable or literal is interpreted as binary operator even though it seems like %s", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS] = { "ambiguous first argument; put parentheses or a space even after `-` operator", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS] = { "ambiguous first argument; put parentheses or a space even after `+` operator", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND] = { "ambiguous `&` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE }, diff --git a/test/prism/newline_test.rb b/test/prism/newline_test.rb index f7511f665cdfea..96374ce6a70283 100644 --- a/test/prism/newline_test.rb +++ b/test/prism/newline_test.rb @@ -15,6 +15,7 @@ class NewlineTest < TestCase regexp_test.rb static_literals_test.rb unescape_test.rb + warnings_test.rb ] filepaths.each do |relative| diff --git a/test/prism/warnings_test.rb b/test/prism/warnings_test.rb index b138a3cb431921..3e26cd2227ded4 100644 --- a/test/prism/warnings_test.rb +++ b/test/prism/warnings_test.rb @@ -23,6 +23,23 @@ def test_ambiguous_regexp assert_warning("a /b/", "wrap regexp in parentheses") end + def test_binary_operator + [ + [:**, "argument prefix"], + [:*, "argument prefix"], + [:<<, "here document"], + [:&, "argument prefix"], + [:+, "unary operator"], + [:-, "unary operator"], + [:/, "regexp literal"], + [:%, "string literal"] + ].each do |(operator, warning)| + assert_warning("puts 1 #{operator}0", warning) + assert_warning("puts :a #{operator}0", warning) + assert_warning("m = 1; puts m #{operator}0", warning) + end + end + def test_equal_in_conditional assert_warning("if a = 1; end; a = a", "should be ==") end From 2e8ae139748f137a1585b5f464e505ac479a1332 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 10:42:52 -0400 Subject: [PATCH 103/242] [ruby/prism] Fix invalid read at EOF https://github.com/ruby/prism/commit/dddaf67c34 --- prism/prism.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index b1d584d0ab9356..22c19f56b57519 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -427,14 +427,6 @@ lex_state_p(const pm_parser_t *parser, pm_lex_state_t state) { return parser->lex_state & state; } -/** - * This is equivalent to the predicate of warn_balanced in CRuby. - */ -static inline bool -ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) { - return !lex_state_p(parser, PM_LEX_STATE_CLASS | PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME | PM_LEX_STATE_ENDFN) && space_seen && !pm_char_is_whitespace(*parser->current.end); -} - typedef enum { PM_IGNORED_NEWLINE_NONE = 0, PM_IGNORED_NEWLINE_ALL, @@ -8074,7 +8066,7 @@ pm_do_loop_stack_p(pm_parser_t *parser) { * is beyond the end of the source then return '\0'. */ static inline uint8_t -peek_at(pm_parser_t *parser, const uint8_t *cursor) { +peek_at(const pm_parser_t *parser, const uint8_t *cursor) { if (cursor < parser->end) { return *cursor; } else { @@ -8097,7 +8089,7 @@ peek_offset(pm_parser_t *parser, ptrdiff_t offset) { * that position is beyond the end of the source then return '\0'. */ static inline uint8_t -peek(pm_parser_t *parser) { +peek(const pm_parser_t *parser) { return peek_at(parser, parser->current.end); } @@ -8162,6 +8154,14 @@ next_newline(const uint8_t *cursor, ptrdiff_t length) { return memchr(cursor, '\n', (size_t) length); } +/** + * This is equivalent to the predicate of warn_balanced in CRuby. + */ +static inline bool +ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) { + return !lex_state_p(parser, PM_LEX_STATE_CLASS | PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME | PM_LEX_STATE_ENDFN) && space_seen && !pm_char_is_whitespace(peek(parser)); +} + /** * Here we're going to check if this is a "magic" comment, and perform whatever * actions are necessary for it here. From 785fba3b161bc0a54e2fc9c5447526499ef93a74 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 10:00:53 -0400 Subject: [PATCH 104/242] [PRISM] Enable TestSyntax#test_warn_balanced --- test/ruby/test_syntax.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 56b97789e531ac..2f311c859ee862 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -506,10 +506,6 @@ def test_warn_unreachable end def test_warn_balanced - warning = < Date: Mon, 20 May 2024 15:25:22 +0900 Subject: [PATCH 105/242] Make sure that kern.coredump=1 --- .github/workflows/macos.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index cc4e66cf16e1c1..f07990b3da4cbf 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -70,6 +70,11 @@ jobs: # Set fetch-depth: 0 so that Launchable can receive commits information. fetch-depth: 10 + - name: make sure that kern.coredump=1 + run: | + sysctl -n kern.coredump + sudo sysctl -w kern.coredump=1 + - name: Run configure run: ../src/configure -C --disable-install-doc ${ruby_configure_args} From 1ba93c2c4d4fa89f5fda490b6e82caafb0d5abc6 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Mon, 20 May 2024 14:53:36 +0900 Subject: [PATCH 106/242] Upload cores to AWS S3 (if any) --- .github/workflows/macos.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f07990b3da4cbf..151df8126bec59 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -129,6 +129,36 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot if: ${{ failure() }} + - name: Resolve job ID + id: job_id + uses: actions/github-script@main + env: + matrix: ${{ toJson(matrix) }} + with: + script: | + const { data: workflow_run } = await github.rest.actions.listJobsForWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + const matrix = JSON.parse(process.env.matrix); + const job_name = `${context.job}${matrix ? ` (${Object.values(matrix).join(", ")})` : ""}`; + return workflow_run.jobs.find((job) => job.name === job_name).id; + + - name: upload /cores + run: | + ls -l /cores + CORES_TAR_GZ="cores-${{ github.run_id }}-${{ steps.job_id.outputs.result }}.tar.gz" + echo "test: $CORES_TAR_GZ" + if [ -n "$(ls /cores)" ]; then + tar czf /cores "$CORES_TAR_GZ" + aws s3 cp "$CORES_TAR_GZ" "s3://ruby-core-files/$CORES_TAR_GZ" + fi + env: + AWS_ACCESS_KEY_ID: ${{ secrets.RUBY_CORE_FILES_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.RUBY_CORE_FILES_AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: ap-northeast-1 + result: if: ${{ always() }} name: ${{ github.workflow }} result From ca5b4580442eac3ee8ad3f98b692d204d7b92f03 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 10:44:13 -0400 Subject: [PATCH 107/242] [PRISM] Match CRuby line semantics for evstr --- prism/prism.c | 7 +++++++ prism_compile.c | 17 +++++++++++------ test/.excludes-prism/TestCoverage.rb | 1 - 3 files changed, 18 insertions(+), 7 deletions(-) delete mode 100644 test/.excludes-prism/TestCoverage.rb diff --git a/prism/prism.c b/prism/prism.c index 22c19f56b57519..bad6c7ff2ba621 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -15460,6 +15460,13 @@ parse_string_part(pm_parser_t *parser) { expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END); pm_token_t closing = parser->previous; + // If this set of embedded statements only contains a single + // statement, then Ruby does not consider it as a possible statement + // that could emit a line event. + if (statements != NULL && statements->body.size == 1) { + pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE); + } + return (pm_node_t *) pm_embedded_statements_node_create(parser, &opening, statements, &closing); } diff --git a/prism_compile.c b/prism_compile.c index 675b4cf86a3122..c9f2fed9ca6741 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -570,6 +570,7 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const if (parts_size > 0) { VALUE current_string = Qnil; + pm_line_column_t current_location = *node_location; for (size_t index = 0; index < parts_size; index++) { const pm_node_t *part = parts->nodes[index]; @@ -590,6 +591,7 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const } else { current_string = string_value; + if (index != 0) current_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, part); } } else { @@ -616,6 +618,7 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const } else { current_string = string_value; + current_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, part); } } else { @@ -640,11 +643,13 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const current_string = rb_enc_str_new(NULL, 0, encoding); } - PUSH_INSN1(ret, *node_location, putobject, rb_fstring(current_string)); + PUSH_INSN1(ret, current_location, putobject, rb_fstring(current_string)); PM_COMPILE_NOT_POPPED(part); - PUSH_INSN(ret, *node_location, dup); - PUSH_INSN1(ret, *node_location, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE , NULL, FALSE)); - PUSH_INSN(ret, *node_location, anytostring); + + const pm_line_column_t current_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, part); + PUSH_INSN(ret, current_location, dup); + PUSH_INSN1(ret, current_location, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE, NULL, FALSE)); + PUSH_INSN(ret, current_location, anytostring); current_string = Qnil; stack_size += 2; @@ -656,10 +661,10 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const current_string = rb_fstring(current_string); if (stack_size == 0 && interpolated) { - PUSH_INSN1(ret, *node_location, putstring, current_string); + PUSH_INSN1(ret, current_location, putstring, current_string); } else { - PUSH_INSN1(ret, *node_location, putobject, current_string); + PUSH_INSN1(ret, current_location, putobject, current_string); } current_string = Qnil; diff --git a/test/.excludes-prism/TestCoverage.rb b/test/.excludes-prism/TestCoverage.rb deleted file mode 100644 index f122d6edbc768f..00000000000000 --- a/test/.excludes-prism/TestCoverage.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_eval, "respect eval coverage setting") From 78e504f21d3148b7b6fa78ea70d10426170f4112 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 11:36:15 -0400 Subject: [PATCH 108/242] [PRISM] Enable TestPrism#test_truncated_source_line --- test/.excludes-prism/TestParse.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index 513a7ed9429a8f..f0b7118624f506 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,2 +1 @@ -exclude(:test_truncated_source_line, "truncate error message") exclude(:test_void_value_in_rhs, "missing raising error for some void value expressions") From a708b6aa6589b06ed059ad40e7d4e71ebe0e16e3 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 11:29:50 -0400 Subject: [PATCH 109/242] [PRISM] Respect eval coverage setting --- iseq.c | 7 +++++++ load.c | 1 + prism_compile.c | 4 ++++ prism_compile.h | 8 +++++--- ruby.c | 1 + vm_eval.c | 7 +++++++ 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/iseq.c b/iseq.c index aab58e3760f35e..9631488411c6c7 100644 --- a/iseq.c +++ b/iseq.c @@ -1029,8 +1029,13 @@ pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpa ISEQ_BODY(iseq)->prism = true; ISEQ_BODY(iseq)->param.flags.use_block = true; // unused block warning is not supported yet + rb_compile_option_t next_option; if (!option) option = &COMPILE_OPTION_DEFAULT; + next_option = *option; + next_option.coverage_enabled = node->coverage_enabled < 0 ? 0 : node->coverage_enabled > 0; + option = &next_option; + pm_location_t *location = &node->base.location; int32_t start_line = node->parser->start_line; @@ -1273,6 +1278,7 @@ pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V pm_parse_result_t result = { 0 }; pm_options_line_set(&result.options, NUM2INT(line)); + result.node.coverage_enabled = 1; switch (option.frozen_string_literal) { case ISEQ_FROZEN_STRING_LITERAL_UNSET: @@ -1708,6 +1714,7 @@ iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self) pm_parse_result_t result = { 0 }; result.options.line = 1; + result.node.coverage_enabled = 1; VALUE error = pm_load_parse_file(&result, file); diff --git a/load.c b/load.c index 37f7248b7dc263..af2475f999b29e 100644 --- a/load.c +++ b/load.c @@ -746,6 +746,7 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname) if (*rb_ruby_prism_ptr()) { pm_parse_result_t result = { 0 }; result.options.line = 1; + result.node.coverage_enabled = 1; VALUE error = pm_load_parse_file(&result, fname); diff --git a/prism_compile.c b/prism_compile.c index c9f2fed9ca6741..58c70748e1c37a 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -2868,6 +2868,7 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_ scope->encoding = previous->encoding; scope->filepath_encoding = previous->filepath_encoding; scope->constants = previous->constants; + scope->coverage_enabled = previous->coverage_enabled; } switch (PM_NODE_TYPE(node)) { @@ -9299,6 +9300,7 @@ pm_parse_process(pm_parse_result_t *result, pm_node_t *node) // freed regardless of whether or we return an error. pm_scope_node_t *scope_node = &result->node; rb_encoding *filepath_encoding = scope_node->filepath_encoding; + int coverage_enabled = scope_node->coverage_enabled; pm_scope_node_init(node, scope_node, NULL); scope_node->filepath_encoding = filepath_encoding; @@ -9306,6 +9308,8 @@ pm_parse_process(pm_parse_result_t *result, pm_node_t *node) scope_node->encoding = rb_enc_find(parser->encoding->name); if (!scope_node->encoding) rb_bug("Encoding not found %s!", parser->encoding->name); + scope_node->coverage_enabled = coverage_enabled; + // Emit all of the various warnings from the parse. const pm_diagnostic_t *warning; const char *warning_filepath = (const char *) pm_string_source(&parser->filepath); diff --git a/prism_compile.h b/prism_compile.h index 0f82782ec02407..8df82c5c7caa2f 100644 --- a/prism_compile.h +++ b/prism_compile.h @@ -36,14 +36,16 @@ typedef struct pm_scope_node { */ rb_encoding *filepath_encoding; - // The size of the local table - // on the iseq which includes - // locals and hidden variables + // The size of the local table on the iseq which includes locals and hidden + // variables. int local_table_for_iseq_size; ID *constants; st_table *index_lookup_table; + // The current coverage setting, passed down through the various scopes. + int coverage_enabled; + /** * This will only be set on the top-level scope node. It will contain all of * the instructions pertaining to BEGIN{} nodes. diff --git a/ruby.c b/ruby.c index cd7f9ad4b8c371..a8de48d2a60897 100644 --- a/ruby.c +++ b/ruby.c @@ -2155,6 +2155,7 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result) pm_options_command_line_set(options, command_line); prism_opt_init(opt); + result->node.coverage_enabled = 0; error = pm_parse_string(result, opt->e_script, rb_str_new2("-e")); } else { diff --git a/vm_eval.c b/vm_eval.c index 7eafb2d5f7f1a4..8da3f3c5293e3c 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1629,6 +1629,10 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, const rb_iseq_t *const parent = vm_block_iseq(base_block); const rb_iseq_t *iseq = parent; VALUE name = rb_fstring_lit(""); + + // Conditionally enable coverage depending on the current mode: + int coverage_enabled = ((rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0) ? 1 : 0; + if (!fname) { fname = rb_source_location(&line); } @@ -1638,10 +1642,12 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, } else { fname = get_eval_default_path(); + coverage_enabled = 0; } pm_parse_result_t result = { 0 }; pm_options_line_set(&result.options, line); + result.node.coverage_enabled = coverage_enabled; // Cout scopes, one for each parent iseq, plus one for our local scope int scopes_count = 0; @@ -1703,6 +1709,7 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, RUBY_ASSERT(parent_scope != NULL); pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1]; + parent_scope->coverage_enabled = coverage_enabled; parent_scope->parser = &result.parser; parent_scope->index_lookup_table = st_init_numtable(); From 6c07aa364d26c686418843de75234e00a4b9b41d Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 11:33:55 -0400 Subject: [PATCH 110/242] [PRISM] Remove old prism spec workflow, just use default.mspec now --- .github/workflows/prism.yml | 4 ++-- common.mk | 9 --------- spec/prism.mspec | 4 ---- 3 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 spec/prism.mspec diff --git a/.github/workflows/prism.yml b/.github/workflows/prism.yml index 78b6469db19a54..63abd09cb8c350 100644 --- a/.github/workflows/prism.yml +++ b/.github/workflows/prism.yml @@ -95,9 +95,9 @@ jobs: RUBY_TESTOPTS: '-q --tty=no --excludes-dir="../src/test/.excludes-prism" --exclude="error_highlight/test_error_highlight.rb" --exclude="prism/encoding_test.rb"' RUN_OPTS: ${{ matrix.run_opts }} - - name: make test-prism-spec + - name: make test-spec run: | - $SETARCH make -s test-prism-spec SPECOPTS="$SPECOPTS" + $SETARCH make -s test-spec SPECOPTS="$SPECOPTS" timeout-minutes: 10 env: GNUMAKEFLAGS: '' diff --git a/common.mk b/common.mk index 18641efd7ffbbd..65e785c5fb8e3d 100644 --- a/common.mk +++ b/common.mk @@ -1018,15 +1018,6 @@ yes-test-spec: yes-test-spec-precheck $(ACTIONS_ENDGROUP) no-test-spec: -test-prism-spec: $(TEST_RUNNABLE)-test-prism-spec -yes-test-prism-spec: yes-test-spec-precheck - $(ACTIONS_GROUP) - $(gnumake_recursive)$(Q) \ - $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/lib/_tmpdir \ - $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/default.mspec -B $(srcdir)/spec/prism.mspec $(MSPECOPT) $(SPECOPTS) - $(ACTIONS_ENDGROUP) -no-test-prism-spec: - check: $(DOT_WAIT) test-spec RUNNABLE = $(LIBRUBY_RELATIVE:no=un)-runnable diff --git a/spec/prism.mspec b/spec/prism.mspec deleted file mode 100644 index 42956c6e7bc353..00000000000000 --- a/spec/prism.mspec +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -# We need to respect the eval coverage setting. -MSpec.register(:exclude, "Coverage.result returns the correct results when eval coverage is disabled") From 012b764f7c13d786b7a9340f93a2aaf24c87dc95 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 15:20:44 -0400 Subject: [PATCH 111/242] [ruby/prism] Fix up regexp escapes with control/meta and x https://github.com/ruby/prism/commit/1dbbbedf55 --- prism/prism.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index bad6c7ff2ba621..3bd761928f8a00 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -9396,7 +9396,7 @@ escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length) { */ static inline uint8_t escape_byte(uint8_t value, const uint8_t flags) { - if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x1f; + if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x9f; if (flags & PM_ESCAPE_FLAG_META) value |= 0x80; return value; } @@ -9632,11 +9632,16 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre parser->current.end++; } + value = escape_byte(value, flags); if (flags & PM_ESCAPE_FLAG_REGEXP) { - pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) { + pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value); + } else { + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } } - escape_write_byte_encoded(parser, buffer, escape_byte(value, flags)); + escape_write_byte_encoded(parser, buffer, value); } else { pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL); } From 7c2c528d350cb5497a2358bdb5684c4822b0f239 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 15:29:06 -0400 Subject: [PATCH 112/242] [ruby/prism] Fix up regexp escapes with control/meta https://github.com/ruby/prism/commit/8a7afa6988 --- prism/prism.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 3bd761928f8a00..cf8ce7c8301a40 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -9496,22 +9496,7 @@ escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer) { static inline void escape_write_byte(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) { if (flags & PM_ESCAPE_FLAG_REGEXP) { - pm_buffer_append_bytes(regular_expression_buffer, (const uint8_t *) "\\x", 2); - - uint8_t byte1 = (uint8_t) ((byte >> 4) & 0xF); - uint8_t byte2 = (uint8_t) (byte & 0xF); - - if (byte1 >= 0xA) { - pm_buffer_append_byte(regular_expression_buffer, (uint8_t) ((byte1 - 0xA) + 'A')); - } else { - pm_buffer_append_byte(regular_expression_buffer, (uint8_t) (byte1 + '0')); - } - - if (byte2 >= 0xA) { - pm_buffer_append_byte(regular_expression_buffer, (uint8_t) (byte2 - 0xA + 'A')); - } else { - pm_buffer_append_byte(regular_expression_buffer, (uint8_t) (byte2 + '0')); - } + pm_buffer_append_format(regular_expression_buffer, "\\x%02X", byte); } escape_write_byte_encoded(parser, buffer, byte); @@ -9546,57 +9531,57 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre switch (peek(parser)) { case '\\': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\\', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags)); return; } case '\'': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\'', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags)); return; } case 'a': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\a', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags)); return; } case 'b': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\b', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags)); return; } case 'e': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\033', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags)); return; } case 'f': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\f', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags)); return; } case 'n': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\n', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags)); return; } case 'r': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\r', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags)); return; } case 's': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte(' ', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags)); return; } case 't': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\t', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags)); return; } case 'v': { parser->current.end++; - escape_write_byte_encoded(parser, buffer, escape_byte('\v', flags)); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\v', flags)); return; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { @@ -9613,7 +9598,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre } } - escape_write_byte_encoded(parser, buffer, value); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, value); return; } case 'x': { From f021bcdbbe69d324b36c03dab4d2347608658b86 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 10 May 2024 12:51:30 -0700 Subject: [PATCH 113/242] Add allocation tests for ruby2_keywords This tests ruby2_keywords flagged methods, as well as passing ruby2_keywords flagged hashes to other methods. Some of the behavior here is questionable, such as allocating different numbers of objects depending on whether a block is passed or whether YJIT is enabled. I think there are likely ways to eliminate allocations in certain cases. However, this gives us a baseline and shows us where it is possible to make improvements. --- test/ruby/test_allocation.rb | 121 +++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb index 48348c0fbd514d..8f6858f1cf7bdd 100644 --- a/test/ruby/test_allocation.rb +++ b/test/ruby/test_allocation.rb @@ -25,6 +25,10 @@ def self.num_allocations empty_array = empty_array = [] empty_hash = empty_hash = {} array1 = array1 = [1] + r2k_array = r2k_array = [Hash.ruby2_keywords_hash(a: 3)] + r2k_array1 = r2k_array1 = [1, Hash.ruby2_keywords_hash(a: 3)] + r2k_empty_array = r2k_empty_array = [Hash.ruby2_keywords_hash({})] + r2k_empty_array1 = r2k_empty_array1 = [1, Hash.ruby2_keywords_hash({})] hash1 = hash1 = {a: 2} nill = nill = nil block = block = lambda{} @@ -95,6 +99,8 @@ def self.none(#{only_block}); end check_allocations(1, 0, "none(*empty_array, *empty_array#{block})") check_allocations(0, 1, "none(**empty_hash, **empty_hash#{block})") check_allocations(1, 1, "none(*empty_array, *empty_array, **empty_hash, **empty_hash#{block})") + + check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "none(*r2k_empty_array#{block})") RUBY end @@ -114,6 +120,9 @@ def self.required(x#{block}); end check_allocations(0, 1, "required(**hash1, **empty_hash#{block})") check_allocations(1, 0, "required(*array1, *empty_array, **empty_hash#{block})") + check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "required(*r2k_empty_array1#{block})") + check_allocations(0, 1, "required(*r2k_array#{block})") + # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "required(*empty_array, **hash1, **empty_hash#{block})") RUBY @@ -135,6 +144,10 @@ def self.optional(x=nil#{block}); end check_allocations(0, 1, "optional(**hash1, **empty_hash#{block})") check_allocations(1, 0, "optional(*array1, *empty_array, **empty_hash#{block})") + check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "optional(*r2k_empty_array#{block})") + check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "optional(*r2k_empty_array1#{block})") + check_allocations(0, 1, "optional(*r2k_array#{block})") + # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "optional(*empty_array, **hash1, **empty_hash#{block})") RUBY @@ -166,6 +179,11 @@ def self.splat(*x#{block}); end check_allocations(1, 1, "splat(**hash1, **empty_hash#{block})") check_allocations(1, 0, "splat(*array1, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "splat(*empty_array, **hash1, **empty_hash#{block})") + + check_allocations(1, 1, "splat(*r2k_empty_array#{block})") + check_allocations(1, 1, "splat(*r2k_empty_array1#{block})") + check_allocations(1, 1, "splat(*r2k_array#{block})") + check_allocations(1, 1, "splat(*r2k_array1#{block})") RUBY end @@ -195,6 +213,10 @@ def self.req_splat(x, *y#{block}); end check_allocations(1, 1, "req_splat(**hash1, **empty_hash#{block})") check_allocations(1, 0, "req_splat(*array1, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "req_splat(*empty_array, **hash1, **empty_hash#{block})") + + check_allocations(1, 1, "req_splat(*r2k_empty_array1#{block})") + check_allocations(1, 1, "req_splat(*r2k_array#{block})") + check_allocations(1, 1, "req_splat(*r2k_array1#{block})") RUBY end @@ -224,6 +246,10 @@ def self.splat_post(*x, y#{block}); end check_allocations(1, 1, "splat_post(**hash1, **empty_hash#{block})") check_allocations(1, 0, "splat_post(*array1, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "splat_post(*empty_array, **hash1, **empty_hash#{block})") + + check_allocations(1, 1, "splat_post(*r2k_empty_array1#{block})") + check_allocations(1, 1, "splat_post(*r2k_array#{block})") + check_allocations(1, 1, "splat_post(*r2k_array1#{block})") RUBY end @@ -251,6 +277,9 @@ def self.keyword(a: nil#{block}); end check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})") check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})") + check_allocations(1, 1, "keyword(*r2k_empty_array#{block})") + check_allocations(1, 1, "keyword(*r2k_array#{block})") + # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "keyword(*empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "keyword(*empty_array, **hash1, **empty_hash#{block})") @@ -281,6 +310,9 @@ def self.keyword_splat(**kw#{block}); end check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})") check_allocations(1, 1, "keyword_splat(*empty_array, *empty_array, **empty_hash#{block})") + check_allocations(1, 1, "keyword_splat(*r2k_empty_array#{block})") + check_allocations(1, 1, "keyword_splat(*r2k_array#{block})") + # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "keyword_splat(*empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "keyword_splat(*empty_array, **hash1, **empty_hash#{block})") @@ -311,6 +343,9 @@ def self.keyword_and_keyword_splat(a: 1, **kw#{block}); end check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})") check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, *empty_array, **empty_hash#{block})") + check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_empty_array#{block})") + check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_array#{block})") + # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, **hash1, **empty_hash#{block})") @@ -350,6 +385,9 @@ def self.required_and_keyword(b, a: nil#{block}); end check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "required_and_keyword(*r2k_empty_array1#{block})") + check_allocations(1, 1, "required_and_keyword(*r2k_array1#{block})") + # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "required_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "required_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})") @@ -397,6 +435,11 @@ def self.splat_and_keyword(*b, a: nil#{block}); end check_allocations(1, 1, "splat_and_keyword(*array1, **empty_hash, a: 2#{block})") check_allocations(1, 1, "splat_and_keyword(*array1, **hash1, **empty_hash#{block})") check_allocations(1, 0, "splat_and_keyword(*array1, **nil#{block})") + + check_allocations(1, 1, "splat_and_keyword(*r2k_empty_array#{block})") + check_allocations(1, 1, "splat_and_keyword(*r2k_array#{block})") + check_allocations(1, 1, "splat_and_keyword(*r2k_empty_array1#{block})") + check_allocations(1, 1, "splat_and_keyword(*r2k_array1#{block})") RUBY end @@ -433,6 +476,9 @@ def self.required_and_keyword_splat(b, **kw#{block}); end check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "required_and_keyword_splat(*r2k_empty_array1#{block})") + check_allocations(1, 1, "required_and_keyword_splat(*r2k_array1#{block})") + # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})") @@ -480,6 +526,11 @@ def self.splat_and_keyword_splat(*b, **kw#{block}); end check_allocations(1, 1, "splat_and_keyword_splat(*array1, **empty_hash, a: 2#{block})") check_allocations(1, 1, "splat_and_keyword_splat(*array1, **hash1, **empty_hash#{block})") check_allocations(1, 1, "splat_and_keyword_splat(*array1, **nil#{block})") + + check_allocations(1, 1, "splat_and_keyword_splat(*r2k_empty_array#{block})") + check_allocations(1, 1, "splat_and_keyword_splat(*r2k_array#{block})") + check_allocations(1, 1, "splat_and_keyword_splat(*r2k_empty_array1#{block})") + check_allocations(1, 1, "splat_and_keyword_splat(*r2k_array1#{block})") RUBY end @@ -521,6 +572,11 @@ def self.anon_splat_and_anon_keyword_splat(*, **#{block}); end check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})") check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})") + + check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})") + check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})") + check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array1#{block})") + check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array1#{block})") RUBY end @@ -562,6 +618,11 @@ def self.anon_splat_and_anon_keyword_splat(*, **#{block}); t(*, **) end; def sel check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})") check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})") + + check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})") + check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})") + check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array1#{block})") + check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array1#{block})") RUBY end @@ -603,6 +664,11 @@ def self.argument_forwarding(...); end check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})") check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})") check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})") + + check_allocations(1, 1, "argument_forwarding(*r2k_empty_array#{block})") + check_allocations(1, 1, "argument_forwarding(*r2k_array#{block})") + check_allocations(1, 1, "argument_forwarding(*r2k_empty_array1#{block})") + check_allocations(1, 1, "argument_forwarding(*r2k_array1#{block})") RUBY end @@ -644,6 +710,61 @@ def self.argument_forwarding(...); t(...) end; def self.t(...) end check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})") check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})") check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})") + + check_allocations(1, 1, "argument_forwarding(*r2k_empty_array#{block})") + check_allocations(1, 1, "argument_forwarding(*r2k_array#{block})") + check_allocations(1, 1, "argument_forwarding(*r2k_empty_array1#{block})") + check_allocations(1, 1, "argument_forwarding(*r2k_array1#{block})") + RUBY + end + + def test_ruby2_keywords + check_allocations(<<~RUBY) + def self.r2k(*a#{block}); end + singleton_class.send(:ruby2_keywords, :r2k) + + check_allocations(1, 1, "r2k(1, a: 2#{block})") + check_allocations(1, 1, "r2k(1, *empty_array, a: 2#{block})") + check_allocations(1, 1, "r2k(1, a:2, **empty_hash#{block})") + check_allocations(1, 1, "r2k(1, **empty_hash, a: 2#{block})") + + check_allocations(1, 0, "r2k(1, **nil#{block})") + check_allocations(1, 1, "r2k(1, **empty_hash#{block})") + check_allocations(1, 1, "r2k(1, **hash1#{block})") + check_allocations(1, 1, "r2k(1, *empty_array, **hash1#{block})") + check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "r2k(1, **empty_hash, **hash1#{block})") + + check_allocations(1, 0, "r2k(1, *empty_array#{block})") + check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "r2k(1, *empty_array, *empty_array, **empty_hash#{block})") + + check_allocations(1, 1, "r2k(*array1, a: 2#{block})") + + check_allocations(1, 0, "r2k(*array1, **nill#{block})") + check_allocations(1, 1, "r2k(*array1, **empty_hash#{block})") + check_allocations(1, 1, "r2k(*array1, **hash1#{block})") + check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1#{block})") + + check_allocations(1, 0, "r2k(*array1, *empty_array#{block})") + check_allocations(1, 1, "r2k(*array1, *empty_array, **empty_hash#{block})") + + check_allocations(1, 1, "r2k(*array1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1, **empty_hash#{block})") + + check_allocations(1, 1, "r2k(1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(1, 1, "r2k(1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "r2k(*array1, **empty_hash, a: 2#{block})") + check_allocations(1, 1, "r2k(*array1, **hash1, **empty_hash#{block})") + check_allocations(1, 0, "r2k(*array1, **nil#{block})") + + check_allocations(1, 1, "r2k(*r2k_empty_array#{block})") + check_allocations(1, 1, "r2k(*r2k_array#{block})") + unless defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled? + # YJIT may or may not allocate depending on arch? + check_allocations(1, 1, "r2k(*r2k_empty_array1#{block})") + check_allocations(1, 1, "r2k(*r2k_array1#{block})") + end RUBY end From 2433b57b6a3a6f8e65f61c27d707e1c7f5407986 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sat, 11 May 2024 12:12:52 -0700 Subject: [PATCH 114/242] Avoid hash allocation for empty ruby2_keywords flagged keyword hash If the method being called does not have a keyword splat parameter, there is no point in allocating the hash, because the hash will be unused (as empty keyword hashes are ignored). --- test/ruby/test_allocation.rb | 28 ++++++++++++++-------------- vm_args.c | 4 +++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb index 8f6858f1cf7bdd..ab3ae15a9f05bc 100644 --- a/test/ruby/test_allocation.rb +++ b/test/ruby/test_allocation.rb @@ -100,7 +100,7 @@ def self.none(#{only_block}); end check_allocations(0, 1, "none(**empty_hash, **empty_hash#{block})") check_allocations(1, 1, "none(*empty_array, *empty_array, **empty_hash, **empty_hash#{block})") - check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "none(*r2k_empty_array#{block})") + check_allocations(#{block.empty?} ? 0 : 1, 0, "none(*r2k_empty_array#{block})") RUBY end @@ -120,7 +120,7 @@ def self.required(x#{block}); end check_allocations(0, 1, "required(**hash1, **empty_hash#{block})") check_allocations(1, 0, "required(*array1, *empty_array, **empty_hash#{block})") - check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "required(*r2k_empty_array1#{block})") + check_allocations(#{block.empty?} ? 0 : 1, 0, "required(*r2k_empty_array1#{block})") check_allocations(0, 1, "required(*r2k_array#{block})") # Currently allocates 1 array unnecessarily due to splatarray true @@ -144,8 +144,8 @@ def self.optional(x=nil#{block}); end check_allocations(0, 1, "optional(**hash1, **empty_hash#{block})") check_allocations(1, 0, "optional(*array1, *empty_array, **empty_hash#{block})") - check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "optional(*r2k_empty_array#{block})") - check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "optional(*r2k_empty_array1#{block})") + check_allocations(#{block.empty?} ? 0 : 1, 0, "optional(*r2k_empty_array#{block})") + check_allocations(#{block.empty?} ? 0 : 1, 0, "optional(*r2k_empty_array1#{block})") check_allocations(0, 1, "optional(*r2k_array#{block})") # Currently allocates 1 array unnecessarily due to splatarray true @@ -180,8 +180,8 @@ def self.splat(*x#{block}); end check_allocations(1, 0, "splat(*array1, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "splat(*empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "splat(*r2k_empty_array#{block})") - check_allocations(1, 1, "splat(*r2k_empty_array1#{block})") + check_allocations(1, 0, "splat(*r2k_empty_array#{block})") + check_allocations(1, 0, "splat(*r2k_empty_array1#{block})") check_allocations(1, 1, "splat(*r2k_array#{block})") check_allocations(1, 1, "splat(*r2k_array1#{block})") RUBY @@ -214,7 +214,7 @@ def self.req_splat(x, *y#{block}); end check_allocations(1, 0, "req_splat(*array1, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "req_splat(*empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "req_splat(*r2k_empty_array1#{block})") + check_allocations(1, 0, "req_splat(*r2k_empty_array1#{block})") check_allocations(1, 1, "req_splat(*r2k_array#{block})") check_allocations(1, 1, "req_splat(*r2k_array1#{block})") RUBY @@ -247,7 +247,7 @@ def self.splat_post(*x, y#{block}); end check_allocations(1, 0, "splat_post(*array1, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "splat_post(*empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "splat_post(*r2k_empty_array1#{block})") + check_allocations(1, 0, "splat_post(*r2k_empty_array1#{block})") check_allocations(1, 1, "splat_post(*r2k_array#{block})") check_allocations(1, 1, "splat_post(*r2k_array1#{block})") RUBY @@ -277,7 +277,7 @@ def self.keyword(a: nil#{block}); end check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})") check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})") - check_allocations(1, 1, "keyword(*r2k_empty_array#{block})") + check_allocations(1, 0, "keyword(*r2k_empty_array#{block})") check_allocations(1, 1, "keyword(*r2k_array#{block})") # Currently allocates 1 array unnecessarily due to splatarray true @@ -385,7 +385,7 @@ def self.required_and_keyword(b, a: nil#{block}); end check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "required_and_keyword(*r2k_empty_array1#{block})") + check_allocations(1, 0, "required_and_keyword(*r2k_empty_array1#{block})") check_allocations(1, 1, "required_and_keyword(*r2k_array1#{block})") # Currently allocates 1 array unnecessarily due to splatarray true @@ -436,9 +436,9 @@ def self.splat_and_keyword(*b, a: nil#{block}); end check_allocations(1, 1, "splat_and_keyword(*array1, **hash1, **empty_hash#{block})") check_allocations(1, 0, "splat_and_keyword(*array1, **nil#{block})") - check_allocations(1, 1, "splat_and_keyword(*r2k_empty_array#{block})") + check_allocations(1, 0, "splat_and_keyword(*r2k_empty_array#{block})") check_allocations(1, 1, "splat_and_keyword(*r2k_array#{block})") - check_allocations(1, 1, "splat_and_keyword(*r2k_empty_array1#{block})") + check_allocations(1, 0, "splat_and_keyword(*r2k_empty_array1#{block})") check_allocations(1, 1, "splat_and_keyword(*r2k_array1#{block})") RUBY end @@ -758,11 +758,11 @@ def self.r2k(*a#{block}); end check_allocations(1, 1, "r2k(*array1, **hash1, **empty_hash#{block})") check_allocations(1, 0, "r2k(*array1, **nil#{block})") - check_allocations(1, 1, "r2k(*r2k_empty_array#{block})") + check_allocations(1, 0, "r2k(*r2k_empty_array#{block})") check_allocations(1, 1, "r2k(*r2k_array#{block})") unless defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled? # YJIT may or may not allocate depending on arch? - check_allocations(1, 1, "r2k(*r2k_empty_array1#{block})") + check_allocations(1, 0, "r2k(*r2k_empty_array1#{block})") check_allocations(1, 1, "r2k(*r2k_array1#{block})") end RUBY diff --git a/vm_args.c b/vm_args.c index 1a78e96776d53b..1db62dc7e28340 100644 --- a/vm_args.c +++ b/vm_args.c @@ -684,7 +684,9 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co if (RB_TYPE_P(rest_last, T_HASH) && FL_TEST_RAW(rest_last, RHASH_PASS_AS_KEYWORDS)) { // def f(**kw); a = [..., kw]; g(*a) splat_flagged_keyword_hash = rest_last; - rest_last = rb_hash_dup(rest_last); + if (!RHASH_EMPTY_P(rest_last) || (ISEQ_BODY(iseq)->param.flags.has_kwrest)) { + rest_last = rb_hash_dup(rest_last); + } kw_flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT; // Unset rest_dupped set by anon_rest as we may need to modify splat in this case From 86cf074fa1dcf73846e094775414d661e47dfc76 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Tue, 14 May 2024 17:03:46 -0700 Subject: [PATCH 115/242] Avoid array allocation for empty ruby2_keywords flagged keyword hash If the method being called does not have a positional splat parameter, there is no point in allocating the array, as decrementing given_argc is sufficient to ensure the empty keyword hash is not considered an argument, assuming that we are calling a method/lambda and not a regular proc. --- test/ruby/test_allocation.rb | 8 ++++---- vm_args.c | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb index ab3ae15a9f05bc..fbe0548899dc86 100644 --- a/test/ruby/test_allocation.rb +++ b/test/ruby/test_allocation.rb @@ -100,7 +100,7 @@ def self.none(#{only_block}); end check_allocations(0, 1, "none(**empty_hash, **empty_hash#{block})") check_allocations(1, 1, "none(*empty_array, *empty_array, **empty_hash, **empty_hash#{block})") - check_allocations(#{block.empty?} ? 0 : 1, 0, "none(*r2k_empty_array#{block})") + check_allocations(0, 0, "none(*r2k_empty_array#{block})") RUBY end @@ -120,7 +120,7 @@ def self.required(x#{block}); end check_allocations(0, 1, "required(**hash1, **empty_hash#{block})") check_allocations(1, 0, "required(*array1, *empty_array, **empty_hash#{block})") - check_allocations(#{block.empty?} ? 0 : 1, 0, "required(*r2k_empty_array1#{block})") + check_allocations(0, 0, "required(*r2k_empty_array1#{block})") check_allocations(0, 1, "required(*r2k_array#{block})") # Currently allocates 1 array unnecessarily due to splatarray true @@ -144,8 +144,8 @@ def self.optional(x=nil#{block}); end check_allocations(0, 1, "optional(**hash1, **empty_hash#{block})") check_allocations(1, 0, "optional(*array1, *empty_array, **empty_hash#{block})") - check_allocations(#{block.empty?} ? 0 : 1, 0, "optional(*r2k_empty_array#{block})") - check_allocations(#{block.empty?} ? 0 : 1, 0, "optional(*r2k_empty_array1#{block})") + check_allocations(0, 0, "optional(*r2k_empty_array#{block})") + check_allocations(0, 0, "optional(*r2k_empty_array1#{block})") check_allocations(0, 1, "optional(*r2k_array#{block})") # Currently allocates 1 array unnecessarily due to splatarray true diff --git a/vm_args.c b/vm_args.c index 1db62dc7e28340..0c1a26cfadc6aa 100644 --- a/vm_args.c +++ b/vm_args.c @@ -693,8 +693,11 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co args->rest_dupped = false; if (ignore_keyword_hash_p(rest_last, iseq, &kw_flag, &converted_keyword_hash)) { - arg_rest_dup(args); - rb_ary_pop(args->rest); + if (ISEQ_BODY(iseq)->param.flags.has_rest || arg_setup_type != arg_setup_method) { + // Only duplicate/modify splat array if it will be used + arg_rest_dup(args); + rb_ary_pop(args->rest); + } given_argc--; kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT); } From e82c7a0577cb9574fbe748a381c9d6fa75e9211d Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 15:22:45 -0400 Subject: [PATCH 116/242] [PRISM] Enable TestRegexp#test_match_control_meta_escape --- test/.excludes-prism/TestRegexp.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/.excludes-prism/TestRegexp.rb b/test/.excludes-prism/TestRegexp.rb index 5852d870efed6e..9d514236c73eee 100644 --- a/test/.excludes-prism/TestRegexp.rb +++ b/test/.excludes-prism/TestRegexp.rb @@ -1,2 +1 @@ exclude(:test_unescape, "unescapes in regexp missing some bytes") -exclude(:test_match_control_meta_escape, "unescapes in regexp missing some bytes") From 6ddc0a8044929751146c667be4d777c02ed52667 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 20 May 2024 15:40:40 -0400 Subject: [PATCH 117/242] [PRISM] Enable TestRegexp#test_unescape --- test/.excludes-prism/TestRegexp.rb | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test/.excludes-prism/TestRegexp.rb diff --git a/test/.excludes-prism/TestRegexp.rb b/test/.excludes-prism/TestRegexp.rb deleted file mode 100644 index 9d514236c73eee..00000000000000 --- a/test/.excludes-prism/TestRegexp.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_unescape, "unescapes in regexp missing some bytes") From 30aa300f84388c78e951a1314c80e575e68754b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 02:13:47 +0000 Subject: [PATCH 118/242] --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yml | 8 ++++---- .github/workflows/scorecards.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d6a2e297be3d59..7d12c1fe811b8b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -80,15 +80,15 @@ jobs: run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb - name: Initialize CodeQL - uses: github/codeql-action/init@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 + uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 + uses: github/codeql-action/autobuild@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 + uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 with: category: '/language:${{ matrix.language }}' upload: False @@ -118,7 +118,7 @@ jobs: continue-on-error: true - name: Upload SARIF - uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 + uses: github/codeql-action/upload-sarif@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 with: sarif_file: sarif-results/${{ matrix.language }}.sarif continue-on-error: true diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 85ee3e2a48c007..eeff61d558bfb5 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v2.1.27 + uses: github/codeql-action/upload-sarif@9fdb3e49720b44c48891d036bb502feb25684276 # v2.1.27 with: sarif_file: results.sarif From 5fed63f7b09d7697a34fcc38efc2985d6e805c40 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 9 May 2024 12:00:29 +0900 Subject: [PATCH 119/242] ripper: Use ripper DSL in simple dispatch chain cases --- parse.y | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/parse.y b/parse.y index 9b363171ed4572..2080110c036233 100644 --- a/parse.y +++ b/parse.y @@ -4056,12 +4056,7 @@ arg : lhs '=' lex_ctxt arg_rhs | tUMINUS_NUM simple_numeric tPOW arg { $$ = call_uni_op(p, call_bin_op(p, $2, idPow, $4, &@2, &@$), idUMinus, &@1, &@$); - /*%%%*/ - /*% - VALUE val = dispatch3(binary, get_value($:2), ID2VAL(idPow), get_value($:4)); - val = dispatch2(unary, ID2VAL(idUMinus), val); - set_value(val); - %*/ + /*% ripper: unary!(ID2VAL(idUMinus), binary!($:2, ID2VAL(idPow), $:4)) %*/ } | tUPLUS arg { @@ -5226,12 +5221,7 @@ block_param_def : '|' opt_bv_decl '|' p->max_numparam = ORDINAL_PARAM; p->ctxt.in_argdef = 0; $$ = 0; - /*%%%*/ - /*% - VALUE val = dispatch7(params, Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil); - val = dispatch2(block_var, val, get_value($:2)); - set_value(val); - %*/ + /*% ripper: block_var!(params!(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil), $:2) %*/ } | '|' block_param opt_bv_decl '|' { From 2d92a4afba36034f3d92ab75dfae4dbf5a445c7e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 10 May 2024 20:02:16 +0900 Subject: [PATCH 120/242] ripper: Make `$:n` to refer each grammar values Ripper DSL uses these values for callbacks, but does not need indexes. --- ext/ripper/tools/dsl.rb | 16 +- parse.y | 423 +++++++++++++++++----------------------- 2 files changed, 186 insertions(+), 253 deletions(-) diff --git a/ext/ripper/tools/dsl.rb b/ext/ripper/tools/dsl.rb index d0002d1ec3a184..d090b15c0bf3b2 100644 --- a/ext/ripper/tools/dsl.rb +++ b/ext/ripper/tools/dsl.rb @@ -42,7 +42,15 @@ def initialize(code, options, lineno = nil) p = p = "p" @code = +"" - code = code.gsub(%r[\G#{NOT_REF_PATTERN}\K(\$|\$:|@)#{TAG_PATTERN}?#{NAME_PATTERN}]o, '"\&"') + code = code.gsub(%r[\G#{NOT_REF_PATTERN}\K(\$|\$:|@)#{TAG_PATTERN}?#{NAME_PATTERN}]o) { + if (arg = $&) == "$:$" + '"p->s_lvalue"' + elsif arg.start_with?("$:") + %["get_value(#{arg})"] + else + arg.dump + end + } @last_value = eval(code) rescue SyntaxError $stderr.puts "error on line #{@lineno}" if @lineno @@ -77,11 +85,7 @@ def add_event(event, args, qundef_check = false) vars = [] args.each do |arg| vars << v = new_var - if arg =~ /\A\$:#{NAME_PATTERN}\z/ - @code << "#{ v }=get_value(#{arg});" - else - @code << "#{ v }=#{ arg };" - end + @code << "#{ v }=#{ arg };" end v = new_var d = "dispatch#{ args.size }(#{ [event, *vars].join(",") })" diff --git a/parse.y b/parse.y index 2080110c036233..4f295eb12fef4e 100644 --- a/parse.y +++ b/parse.y @@ -1488,14 +1488,12 @@ static NODE *heredoc_dedent(struct parser_params*,NODE*); static void check_literal_when(struct parser_params *p, NODE *args, const YYLTYPE *loc); #ifdef RIPPER -static VALUE var_field(struct parser_params *p, VALUE a); #define get_value(idx) (rb_ary_entry(p->s_value_stack, idx)) #define set_value(val) (p->s_lvalue = val) static VALUE defs(struct parser_params *p, VALUE head, VALUE args, VALUE bodystmt); static VALUE backref_error(struct parser_params*, NODE *, VALUE); static VALUE ripper_assignable(struct parser_params *p, ID id, VALUE lhs); static VALUE ripper_const_decl(struct parser_params *p, VALUE path); -static VALUE ripper_heredoc_dedent(struct parser_params *p, int indent, VALUE array); static VALUE assign_error(struct parser_params *p, const char *mesg, VALUE a); static int id_is_var(struct parser_params *p, ID id); #endif @@ -1611,7 +1609,7 @@ void ripper_error(struct parser_params *p); #define yyparse ripper_yyparse -static void ripper_formal_argument(struct parser_params *p, ID id, VALUE lhs); +static VALUE ripper_formal_argument(struct parser_params *p, ID id, VALUE lhs); static VALUE ripper_new_args(struct parser_params *p, VALUE pre_args, VALUE opt_args, VALUE rest_arg, VALUE post_args, VALUE tail) @@ -2990,43 +2988,43 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) p->cur_arg = 0; p->ctxt.in_argdef = 1; $$ = NEW_OPT_ARG(assignable(p, $1, $3, &@$), &@$); - /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:3)) %*/ + /*% ripper: rb_assoc_new(ripper_assignable(p, $1, $:1), $:3) %*/ } ; %rule f_optarg(value) : f_opt(value) { $$ = $1; - /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ + /*% ripper: rb_ary_new3(1, $:1) %*/ } | f_optarg(value) ',' f_opt(value) { $$ = opt_arg_append($1, $3); - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ + /*% ripper: rb_ary_push($:1, $:3) %*/ } ; %rule f_kwarg(kw) : kw { $$ = $1; - /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ + /*% ripper: rb_ary_new3(1, $:1) %*/ } | f_kwarg(kw) ',' kw { $$ = kwd_append($1, $3); - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ + /*% ripper: rb_ary_push($:1, $:3) %*/ } ; %rule opt_args_tail(tail) : ',' tail { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } | /* none */ { $$ = new_args_tail(p, 0, 0, 0, &@0); - /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, Qnil); %*/ + /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, Qnil) %*/ } ; @@ -3095,7 +3093,7 @@ top_stmt : stmt | keyword_BEGIN begin_block { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; @@ -3184,7 +3182,7 @@ k_END : keyword_END lex_ctxt { $$ = $2; p->ctxt.in_rescue = before_rescue; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ }; stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem @@ -3355,12 +3353,8 @@ command_asgn : lhs '=' lex_ctxt command_rhs $bodystmt = new_scope_body(p, $args, $bodystmt, &@$); ($$ = $head->nd_def)->nd_loc = @$; RNODE_DEFN($$)->nd_defn = $bodystmt; - /*%%%*/ - /*% - VALUE val = dispatch4(bodystmt, get_value($:bodystmt), Qnil, Qnil, Qnil); - val = dispatch3(def, get_value($:head), get_value($:args), val); - set_value(val); - %*/ + /*% ripper: bodystmt!($:bodystmt, Qnil, Qnil, Qnil) %*/ + /*% ripper: def!($:head, $:args, $:$) %*/ local_pop(p); } | defs_head[head] f_opt_paren_args[args] '=' endless_command[bodystmt] @@ -3370,12 +3364,8 @@ command_asgn : lhs '=' lex_ctxt command_rhs $bodystmt = new_scope_body(p, $args, $bodystmt, &@$); ($$ = $head->nd_def)->nd_loc = @$; RNODE_DEFS($$)->nd_defn = $bodystmt; - /*%%%*/ - /*% - VALUE val = dispatch4(bodystmt, get_value($:bodystmt), Qnil, Qnil, Qnil); - val = defs(p, get_value($:head), get_value($:args), val); - set_value(val); - %*/ + /*% ripper: bodystmt!($:bodystmt, Qnil, Qnil, Qnil) %*/ + /*% ripper: defs(p, $:head, $:args, $:$) %*/ local_pop(p); } | backref tOP_ASGN lex_ctxt command_rhs @@ -3384,7 +3374,7 @@ command_asgn : lhs '=' lex_ctxt command_rhs rb_backref_error(p, $1); /*% %*/ $$ = NEW_ERROR(&@$); - /*% ripper[error]: backref_error(p, $1, opassign!(var_field(p, get_value($:1)), $:2, $:4)) %*/ + /*% ripper[error]: backref_error(p, $1, opassign!(var_field!($:1), $:2, $:4)) %*/ } ; @@ -3485,7 +3475,7 @@ defn_head : k_def def_name $$ = def_head_save(p, $k_def); $$->nd_mid = $def_name; $$->nd_def = NEW_DEFN($def_name, 0, &@$); - /*% ripper: get_value($:def_name); %*/ + /*% ripper: $:def_name %*/ } ; @@ -3500,10 +3490,7 @@ defs_head : k_def singleton dot_or_colon $$ = def_head_save(p, $k_def); $$->nd_mid = $def_name; $$->nd_def = NEW_DEFS($singleton, $def_name, 0, &@$); - /*%%%*/ - /*% - set_value(rb_ary_new_from_args(3, get_value($:singleton), get_value($:dot_or_colon), get_value($:def_name))); - %*/ + /*% ripper: rb_ary_new_from_args(3, $:singleton, $:dot_or_colon, $:def_name) %*/ } ; @@ -3521,7 +3508,7 @@ expr_value : expr expr_value_do : {COND_PUSH(1);} expr_value do {COND_POP();} { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; @@ -3541,14 +3528,14 @@ cmd_brace_block : tLBRACE_ARG brace_body '}' { $$ = $2; set_embraced_location($$, &@1, &@3); - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; fcall : operation { $$ = NEW_FCALL($1, 0, &@$); - /*% ripper: get_value($:1); %*/ + /*% ripper: $:1 %*/ } ; @@ -3646,7 +3633,7 @@ mlhs_inner : mlhs_basic mlhs_basic : mlhs_head { $$ = NEW_MASGN($1, 0, &@$); - /*% ripper: get_value($:1) %*/ + /*% ripper: $:1 %*/ } | mlhs_head mlhs_item { @@ -3730,12 +3717,12 @@ mlhs_post : mlhs_item mlhs_node : user_variable { $$ = assignable(p, $1, 0, &@$); - /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper: ripper_assignable(p, $1, var_field!($:1)) %*/ } | keyword_variable { $$ = assignable(p, $1, 0, &@$); - /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper: ripper_assignable(p, $1, var_field!($:1)) %*/ } | primary_value '[' opt_call_args rbracket { @@ -3775,19 +3762,19 @@ mlhs_node : user_variable rb_backref_error(p, $1); /*% %*/ $$ = NEW_ERROR(&@$); - /*% ripper[error]: backref_error(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper[error]: backref_error(p, $1, var_field!($:1)) %*/ } ; lhs : user_variable { $$ = assignable(p, $1, 0, &@$); - /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper: ripper_assignable(p, $1, var_field!($:1)) %*/ } | keyword_variable { $$ = assignable(p, $1, 0, &@$); - /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper: ripper_assignable(p, $1, var_field!($:1)) %*/ } | primary_value '[' opt_call_args rbracket { @@ -3825,7 +3812,7 @@ lhs : user_variable rb_backref_error(p, $1); /*% %*/ $$ = NEW_ERROR(&@$); - /*% ripper[error]: backref_error(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper[error]: backref_error(p, $1, var_field!($:1)) %*/ } ; @@ -3879,13 +3866,13 @@ fitem : fname undef_list : fitem { $$ = NEW_UNDEF($1, &@$); - /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ + /*% ripper: rb_ary_new3(1, $:1) %*/ } | undef_list ',' {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem { NODE *undef = NEW_UNDEF($4, &@4); $$ = block_append(p, $1, undef); - /*% ripper: rb_ary_push(get_value($:1), get_value($:4)) %*/ + /*% ripper: rb_ary_push($:1, $:4) %*/ } ; @@ -3983,7 +3970,7 @@ arg : lhs '=' lex_ctxt arg_rhs /*%%%*/ $$ = NEW_ERROR(&@$); /*% %*/ - /*% ripper[error]: backref_error(p, $1, opassign!(var_field(p, get_value($:1)), $:2, $:4)) %*/ + /*% ripper[error]: backref_error(p, $1, opassign!(var_field!($:1), $:2, $:4)) %*/ } | arg tDOT2 arg { @@ -4164,12 +4151,8 @@ arg : lhs '=' lex_ctxt arg_rhs $bodystmt = new_scope_body(p, $args, $bodystmt, &@$); ($$ = $head->nd_def)->nd_loc = @$; RNODE_DEFN($$)->nd_defn = $bodystmt; - /*%%%*/ - /*% - VALUE val = dispatch4(bodystmt, get_value($:bodystmt), Qnil, Qnil, Qnil); - val = dispatch3(def, get_value($:head), get_value($:args), val); - set_value(val); - %*/ + /*% ripper: bodystmt!($:bodystmt, Qnil, Qnil, Qnil) %*/ + /*% ripper: def!($:head, $:args, $:$) %*/ local_pop(p); } | defs_head[head] f_opt_paren_args[args] '=' endless_arg[bodystmt] @@ -4179,12 +4162,8 @@ arg : lhs '=' lex_ctxt arg_rhs $bodystmt = new_scope_body(p, $args, $bodystmt, &@$); ($$ = $head->nd_def)->nd_loc = @$; RNODE_DEFS($$)->nd_defn = $bodystmt; - /*%%%*/ - /*% - VALUE val = dispatch4(bodystmt, get_value($:bodystmt), Qnil, Qnil, Qnil); - val = defs(p, get_value($:head), get_value($:args), val); - set_value(val); - %*/ + /*% ripper: bodystmt!($:bodystmt, Qnil, Qnil, Qnil) %*/ + /*% ripper: defs(p, $:head, $:args, $:$) %*/ local_pop(p); } | primary @@ -4394,14 +4373,14 @@ command_args : { CMDARG_POP(); if (lookahead) CMDARG_PUSH(0); $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; block_arg : tAMPER arg_value { $$ = NEW_BLOCK_PASS($2, &@$); - /*% ripper: get_value($:2) %*/ + /*% ripper: $:2 %*/ } | tAMPER { @@ -4414,7 +4393,7 @@ block_arg : tAMPER arg_value opt_block_arg : ',' block_arg { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } | none { @@ -4450,7 +4429,7 @@ args : arg_value arg_splat : tSTAR arg_value { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } | tSTAR /* none */ { @@ -4782,7 +4761,7 @@ primary : literal $bodystmt = new_scope_body(p, $args, $bodystmt, &@$); ($$ = $head->nd_def)->nd_loc = @$; RNODE_DEFS($$)->nd_defn = $bodystmt; - /*% ripper: defs(p, get_value($:head), get_value($:args), get_value($:bodystmt)) %*/ + /*% ripper: defs(p, $:head, $:args, $:bodystmt) %*/ local_pop(p); } | keyword_break @@ -5029,7 +5008,7 @@ f_marg : f_norm_arg { $$ = assignable(p, $1, 0, &@$); mark_lvar_used(p, $$); - /*% ripper: ripper_assignable(p, $1, get_value($:1)) %*/ + /*% ripper: ripper_assignable(p, $1, $:1) %*/ } | tLPAREN f_margs rparen { @@ -5053,7 +5032,7 @@ f_marg_list : f_marg f_margs : f_marg_list { $$ = NEW_MASGN($1, 0, &@$); - /*% ripper: get_value($:1) %*/ + /*% ripper: $:1 %*/ } | f_marg_list ',' f_rest_marg { @@ -5081,7 +5060,7 @@ f_rest_marg : tSTAR f_norm_arg { $$ = assignable(p, $2, 0, &@$); mark_lvar_used(p, $$); - /*% ripper: ripper_assignable(p, $2, get_value($:2)) %*/ + /*% ripper: ripper_assignable(p, $2, $:2) %*/ } | tSTAR { @@ -5103,22 +5082,22 @@ f_eq : {p->ctxt.in_argdef = 0;} '='; block_args_tail : f_kwarg(f_block_kw) ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4, &@3); - /*% ripper: rb_ary_new_from_args(3, get_value($:1), get_value($:3), get_value($:4)); %*/ + /*% ripper: rb_ary_new_from_args(3, $:1, $:3, $:4) %*/ } | f_kwarg(f_block_kw) opt_f_block_arg { $$ = new_args_tail(p, $1, 0, $2, &@1); - /*% ripper: rb_ary_new_from_args(3, get_value($:1), Qnil, get_value($:2)); %*/ + /*% ripper: rb_ary_new_from_args(3, $:1, Qnil, $:2) %*/ } | f_any_kwrest opt_f_block_arg { $$ = new_args_tail(p, 0, $1, $2, &@1); - /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), get_value($:2)); %*/ + /*% ripper: rb_ary_new_from_args(3, Qnil, $:1, $:2) %*/ } | f_block_arg { $$ = new_args_tail(p, 0, 0, $1, &@1); - /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, get_value($:1)); %*/ + /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, $:1) %*/ } ; @@ -5133,78 +5112,78 @@ excessed_comma : ',' block_param : f_arg ',' f_optarg(primary_value) ',' f_rest_arg opt_args_tail(block_args_tail) { $$ = new_args(p, $1, $3, $5, 0, $6, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), Qnil, get_value($:6)) %*/ + /*% ripper: ripper_new_args(p, $:1, $:3, $:5, Qnil, $:6) %*/ } | f_arg ',' f_optarg(primary_value) ',' f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, $1, $3, $5, $7, $8, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), get_value($:7), get_value($:8)) %*/ + /*% ripper: ripper_new_args(p, $:1, $:3, $:5, $:7, $:8) %*/ } | f_arg ',' f_optarg(primary_value) opt_args_tail(block_args_tail) { $$ = new_args(p, $1, $3, 0, 0, $4, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, Qnil, get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, $:1, $:3, Qnil, Qnil, $:4) %*/ } | f_arg ',' f_optarg(primary_value) ',' f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, $1, $3, 0, $5, $6, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, get_value($:5), get_value($:6)) %*/ + /*% ripper: ripper_new_args(p, $:1, $:3, Qnil, $:5, $:6) %*/ } | f_arg ',' f_rest_arg opt_args_tail(block_args_tail) { $$ = new_args(p, $1, 0, $3, 0, $4, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), Qnil, get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, $:1, Qnil, $:3, Qnil, $:4) %*/ } | f_arg excessed_comma { $$ = new_args_tail(p, 0, 0, 0, &@2); $$ = new_args(p, $1, 0, $2, 0, $$, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:2), Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/ + /*% ripper: ripper_new_args(p, $:1, Qnil, $:2, Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/ } | f_arg ',' f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, $1, 0, $3, $5, $6, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), get_value($:5), get_value($:6)) %*/ + /*% ripper: ripper_new_args(p, $:1, Qnil, $:3, $:5, $:6) %*/ } | f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, $1, 0, 0, 0, $2, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), Qnil, Qnil, Qnil, get_value($:2)) %*/ + /*% ripper: ripper_new_args(p, $:1, Qnil, Qnil, Qnil, $:2) %*/ } | f_optarg(primary_value) ',' f_rest_arg opt_args_tail(block_args_tail) { $$ = new_args(p, 0, $1, $3, 0, $4, &@$); - /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), Qnil, get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, Qnil, $:1, $:3, Qnil, $:4) %*/ } | f_optarg(primary_value) ',' f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, 0, $1, $3, $5, $6, &@$); - /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), get_value($:5), get_value($:6)) %*/ + /*% ripper: ripper_new_args(p, Qnil, $:1, $:3, $:5, $:6) %*/ } | f_optarg(primary_value) opt_args_tail(block_args_tail) { $$ = new_args(p, 0, $1, 0, 0, $2, &@$); - /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, Qnil, get_value($:2)) %*/ + /*% ripper: ripper_new_args(p, Qnil, $:1, Qnil, Qnil, $:2) %*/ } | f_optarg(primary_value) ',' f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, 0, $1, 0, $3, $4, &@$); - /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, get_value($:3), get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, Qnil, $:1, Qnil, $:3, $:4) %*/ } | f_rest_arg opt_args_tail(block_args_tail) { $$ = new_args(p, 0, 0, $1, 0, $2, &@$); - /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), Qnil, get_value($:2)) %*/ + /*% ripper: ripper_new_args(p, Qnil, Qnil, $:1, Qnil, $:2) %*/ } | f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, 0, 0, $1, $3, $4, &@$); - /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), get_value($:3), get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, Qnil, Qnil, $:1, $:3, $:4) %*/ } | block_args_tail { $$ = new_args(p, 0, 0, 0, 0, $1, &@$); - /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, get_value($:1)) %*/ + /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, $:1) %*/ } ; @@ -5242,20 +5221,20 @@ opt_bv_decl : '\n'? | '\n'? ';' bv_decls '\n'? { $$ = 0; - /*% ripper: get_value($:3) %*/ + /*% ripper: $:3 %*/ } ; bv_decls : bvar - /*% ripper[brace]: rb_ary_new3(1, get_value($:1)) %*/ + /*% ripper[brace]: rb_ary_new3(1, $:1) %*/ | bv_decls ',' bvar - /*% ripper[brace]: rb_ary_push(get_value($:1), get_value($:3)) %*/ + /*% ripper[brace]: rb_ary_push($:1, $:3) %*/ ; bvar : tIDENTIFIER { new_bv(p, $1); - /*% ripper: get_value($:1) %*/ + /*% ripper: $:1 %*/ } | f_bad_arg { @@ -5334,7 +5313,7 @@ lambda_body : tLAMBEG compstmt '}' { token_info_pop(p, "}", &@3); $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } | keyword_do_LAMBDA { @@ -5343,7 +5322,7 @@ lambda_body : tLAMBEG compstmt '}' bodystmt k_end { $$ = $3; - /*% ripper: get_value($:3); %*/ + /*% ripper: $:3 %*/ } ; @@ -5351,7 +5330,7 @@ do_block : k_do_block do_body k_end { $$ = $2; set_embraced_location($$, &@1, &@3); - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; @@ -5442,13 +5421,13 @@ brace_block : '{' brace_body '}' { $$ = $2; set_embraced_location($$, &@1, &@3); - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } | k_do do_body k_end { $$ = $2; set_embraced_location($$, &@1, &@3); - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; @@ -5579,28 +5558,28 @@ p_top_expr_body : p_expr { $$ = new_array_pattern_tail(p, 0, 1, 0, 0, &@$); $$ = new_array_pattern(p, 0, $1, $$, &@$); - /*% ripper: ripper_new_array_pattern(p, Qnil, get_value($:1), rb_ary_new()); %*/ + /*% ripper: ripper_new_array_pattern(p, Qnil, $:1, rb_ary_new()) %*/ } | p_expr ',' p_args { $$ = new_array_pattern(p, 0, $1, $3, &@$); nd_set_first_loc($$, @1.beg_pos); - /*% ripper: ripper_new_array_pattern(p, Qnil, get_value($:1), get_value($:3)); %*/ + /*% ripper: ripper_new_array_pattern(p, Qnil, $:1, $:3) %*/ } | p_find { $$ = new_find_pattern(p, 0, $1, &@$); - /*% ripper: ripper_new_find_pattern(p, Qnil, get_value($:1)); %*/ + /*% ripper: ripper_new_find_pattern(p, Qnil, $:1) %*/ } | p_args_tail { $$ = new_array_pattern(p, 0, 0, $1, &@$); - /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, get_value($:1)); %*/ + /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, $:1) %*/ } | p_kwargs { $$ = new_hash_pattern(p, 0, $1, &@$); - /*% ripper: ripper_new_hash_pattern(p, Qnil, get_value($:1)); %*/ + /*% ripper: ripper_new_hash_pattern(p, Qnil, $:1) %*/ } ; @@ -5628,14 +5607,14 @@ p_alt : p_alt '|' p_expr_basic p_lparen : '(' p_pktbl { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; p_lbracket : '[' p_pktbl { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; @@ -5646,70 +5625,70 @@ p_expr_basic : p_value pop_pktbl(p, $p_pktbl); $$ = new_array_pattern(p, $p_const, 0, $p_args, &@$); nd_set_first_loc($$, @p_const.beg_pos); - /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, get_value($:p_args)); %*/ + /*% ripper: ripper_new_array_pattern(p, $:p_const, Qnil, $:p_args) %*/ } | p_const p_lparen[p_pktbl] p_find rparen { pop_pktbl(p, $p_pktbl); $$ = new_find_pattern(p, $p_const, $p_find, &@$); nd_set_first_loc($$, @p_const.beg_pos); - /*% ripper: ripper_new_find_pattern(p, get_value($:p_const), get_value($:p_find)); %*/ + /*% ripper: ripper_new_find_pattern(p, $:p_const, $:p_find) %*/ } | p_const p_lparen[p_pktbl] p_kwargs rparen { pop_pktbl(p, $p_pktbl); $$ = new_hash_pattern(p, $p_const, $p_kwargs, &@$); nd_set_first_loc($$, @p_const.beg_pos); - /*% ripper: ripper_new_hash_pattern(p, get_value($:p_const), get_value($:p_kwargs)); %*/ + /*% ripper: ripper_new_hash_pattern(p, $:p_const, $:p_kwargs) %*/ } | p_const '(' rparen { $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$); $$ = new_array_pattern(p, $p_const, 0, $$, &@$); - /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, rb_ary_new()); %*/ + /*% ripper: ripper_new_array_pattern(p, $:p_const, Qnil, rb_ary_new()) %*/ } | p_const p_lbracket[p_pktbl] p_args rbracket { pop_pktbl(p, $p_pktbl); $$ = new_array_pattern(p, $p_const, 0, $p_args, &@$); nd_set_first_loc($$, @p_const.beg_pos); - /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, get_value($:p_args)); %*/ + /*% ripper: ripper_new_array_pattern(p, $:p_const, Qnil, $:p_args) %*/ } | p_const p_lbracket[p_pktbl] p_find rbracket { pop_pktbl(p, $p_pktbl); $$ = new_find_pattern(p, $p_const, $p_find, &@$); nd_set_first_loc($$, @p_const.beg_pos); - /*% ripper: ripper_new_find_pattern(p, get_value($:p_const), get_value($:p_find)); %*/ + /*% ripper: ripper_new_find_pattern(p, $:p_const, $:p_find) %*/ } | p_const p_lbracket[p_pktbl] p_kwargs rbracket { pop_pktbl(p, $p_pktbl); $$ = new_hash_pattern(p, $p_const, $p_kwargs, &@$); nd_set_first_loc($$, @p_const.beg_pos); - /*% ripper: ripper_new_hash_pattern(p, get_value($:p_const), get_value($:p_kwargs)); %*/ + /*% ripper: ripper_new_hash_pattern(p, $:p_const, $:p_kwargs) %*/ } | p_const '[' rbracket { $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$); $$ = new_array_pattern(p, $1, 0, $$, &@$); - /*% ripper: ripper_new_array_pattern(p, get_value($:1), Qnil, rb_ary_new()); %*/ + /*% ripper: ripper_new_array_pattern(p, $:1, Qnil, rb_ary_new()) %*/ } | tLBRACK p_args rbracket { $$ = new_array_pattern(p, 0, 0, $p_args, &@$); - /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, get_value($:p_args)); %*/ + /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, $:p_args) %*/ } | tLBRACK p_find rbracket { $$ = new_find_pattern(p, 0, $p_find, &@$); - /*% ripper: ripper_new_find_pattern(p, Qnil, get_value($:p_find)); %*/ + /*% ripper: ripper_new_find_pattern(p, Qnil, $:p_find) %*/ } | tLBRACK rbracket { $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$); $$ = new_array_pattern(p, 0, 0, $$, &@$); - /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, rb_ary_new()); %*/ + /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, rb_ary_new()) %*/ } | tLBRACE p_pktbl lex_ctxt[ctxt] { @@ -5720,24 +5699,19 @@ p_expr_basic : p_value pop_pktbl(p, $p_pktbl); p->ctxt.in_kwarg = $ctxt.in_kwarg; $$ = new_hash_pattern(p, 0, $p_kwargs, &@$); - /*% ripper: ripper_new_hash_pattern(p, Qnil, get_value($:p_kwargs)); %*/ + /*% ripper: ripper_new_hash_pattern(p, Qnil, $:p_kwargs) %*/ } | tLBRACE rbrace { $$ = new_hash_pattern_tail(p, 0, 0, &@$); $$ = new_hash_pattern(p, 0, $$, &@$); - /*%%%*/ - /*% - VALUE val = ripper_new_hash_pattern_tail(p, Qnil, 0); - val = ripper_new_hash_pattern(p, Qnil, val); - set_value(val); - %*/ + /*% ripper: ripper_new_hash_pattern(p, Qnil, ripper_new_hash_pattern_tail(p, Qnil, 0)) %*/ } | tLPAREN p_pktbl p_expr rparen { pop_pktbl(p, $p_pktbl); $$ = $p_expr; - /*% ripper: get_value($:p_expr); %*/ + /*% ripper: $:p_expr %*/ } ; @@ -5745,44 +5719,28 @@ p_args : p_expr { NODE *pre_args = NEW_LIST($1, &@$); $$ = new_array_pattern_tail(p, pre_args, 0, 0, 0, &@$); - /*%%%*/ - /*% - VALUE ary = rb_ary_new_from_args(1, get_value($:1)); - set_value(rb_ary_new_from_args(3, ary, Qnil, Qnil)); - %*/ + /*% ripper: rb_ary_new_from_args(1, $:1) %*/ + /*% ripper: rb_ary_new_from_args(3, $:$, Qnil, Qnil) %*/ } | p_args_head { $$ = new_array_pattern_tail(p, $1, 1, 0, 0, &@$); - /*%%%*/ - /*% - set_value(rb_ary_new_from_args(3, get_value($:1), Qnil, Qnil)); - %*/ + /*% ripper: rb_ary_new_from_args(3, $:1, Qnil, Qnil) %*/ } | p_args_head p_arg { $$ = new_array_pattern_tail(p, list_concat($1, $2), 0, 0, 0, &@$); - /*%%%*/ - /*% - VALUE pre_args = rb_ary_concat(get_value($:1), get_value($:2)); - set_value(rb_ary_new_from_args(3, pre_args, Qnil, Qnil)); - %*/ + /*% ripper: rb_ary_new_from_args(3, rb_ary_concat($:1, $:2), Qnil, Qnil) %*/ } | p_args_head p_rest { $$ = new_array_pattern_tail(p, $1, 1, $2, 0, &@$); - /*%%%*/ - /*% - set_value(rb_ary_new_from_args(3, get_value($:1), get_value($:2), Qnil)); - %*/ + /*% ripper: rb_ary_new_from_args(3, $:1, $:2, Qnil) %*/ } | p_args_head p_rest ',' p_args_post { $$ = new_array_pattern_tail(p, $1, 1, $2, $4, &@$); - /*%%%*/ - /*% - set_value(rb_ary_new_from_args(3, get_value($:1), get_value($:2), get_value($:4))); - %*/ + /*% ripper: rb_ary_new_from_args(3, $:1, $:2, $:4) %*/ } | p_args_tail ; @@ -5794,26 +5752,26 @@ p_args_head : p_arg ',' | p_args_head p_arg ',' { $$ = list_concat($1, $2); - /*% ripper: rb_ary_concat(get_value($:1), get_value($:2)) %*/ + /*% ripper: rb_ary_concat($:1, $:2) %*/ } ; p_args_tail : p_rest { $$ = new_array_pattern_tail(p, 0, 1, $1, 0, &@$); - /*% ripper: ripper_new_array_pattern_tail(p, Qnil, get_value($:1), Qnil); %*/ + /*% ripper: ripper_new_array_pattern_tail(p, Qnil, $:1, Qnil) %*/ } | p_rest ',' p_args_post { $$ = new_array_pattern_tail(p, 0, 1, $1, $3, &@$); - /*% ripper: ripper_new_array_pattern_tail(p, Qnil, get_value($:1), get_value($:3)); %*/ + /*% ripper: ripper_new_array_pattern_tail(p, Qnil, $:1, $:3) %*/ } ; p_find : p_rest ',' p_args_post ',' p_rest { $$ = new_find_pattern_tail(p, $1, $3, $5, &@$); - /*% ripper: ripper_new_find_pattern_tail(p, get_value($:1), get_value($:3), get_value($:5)) %*/ + /*% ripper: ripper_new_find_pattern_tail(p, $:1, $:3, $:5) %*/ } ; @@ -5822,12 +5780,12 @@ p_rest : tSTAR tIDENTIFIER { error_duplicate_pattern_variable(p, $2, &@2); $$ = assignable(p, $2, 0, &@$); - /*% ripper: ripper_assignable(p, $2, var_field(p, get_value($:2))) %*/ + /*% ripper: ripper_assignable(p, $2, var_field!($:2)) %*/ } | tSTAR { $$ = 0; - /*% ripper: var_field(p, Qnil) %*/ + /*% ripper: var_field!(Qnil) %*/ } ; @@ -5835,45 +5793,45 @@ p_args_post : p_arg | p_args_post ',' p_arg { $$ = list_concat($1, $3); - /*% ripper: rb_ary_concat(get_value($:1), get_value($:3)) %*/ + /*% ripper: rb_ary_concat($:1, $:3) %*/ } ; p_arg : p_expr { $$ = NEW_LIST($1, &@$); - /*% ripper: rb_ary_new_from_args(1, get_value($:1)) %*/ + /*% ripper: rb_ary_new_from_args(1, $:1) %*/ } ; p_kwargs : p_kwarg ',' p_any_kwrest { $$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), $3, &@$); - /*% ripper: ripper_new_hash_pattern_tail(p, get_value($:1), get_value($:3)) %*/ + /*% ripper: ripper_new_hash_pattern_tail(p, $:1, $:3) %*/ } | p_kwarg { $$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), 0, &@$); - /*% ripper: ripper_new_hash_pattern_tail(p, get_value($:1), 0) %*/ + /*% ripper: ripper_new_hash_pattern_tail(p, $:1, 0) %*/ } | p_kwarg ',' { $$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), 0, &@$); - /*% ripper: ripper_new_hash_pattern_tail(p, get_value($:1), 0) %*/ + /*% ripper: ripper_new_hash_pattern_tail(p, $:1, 0) %*/ } | p_any_kwrest { $$ = new_hash_pattern_tail(p, new_hash(p, 0, &@$), $1, &@$); - /*% ripper: ripper_new_hash_pattern_tail(p, rb_ary_new(), get_value($:1)) %*/ + /*% ripper: ripper_new_hash_pattern_tail(p, rb_ary_new(), $:1) %*/ } ; p_kwarg : p_kw - /*% ripper[brace]: rb_ary_new_from_args(1, get_value($:1)) %*/ + /*% ripper[brace]: rb_ary_new_from_args(1, $:1) %*/ | p_kwarg ',' p_kw { $$ = list_concat($1, $3); - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ + /*% ripper: rb_ary_push($:1, $:3) %*/ } ; @@ -5881,7 +5839,7 @@ p_kw : p_kw_label p_expr { error_duplicate_pattern_key(p, $1, &@1); $$ = list_append(p, NEW_LIST(NEW_SYM(rb_id2str($1), &@1), &@$), $2); - /*% ripper: rb_ary_new_from_args(2, get_value($:1), get_value($:2)) %*/ + /*% ripper: rb_ary_new_from_args(2, $:1, $:2) %*/ } | p_kw_label { @@ -5891,7 +5849,7 @@ p_kw : p_kw_label p_expr } error_duplicate_pattern_variable(p, $1, &@1); $$ = list_append(p, NEW_LIST(NEW_SYM(rb_id2str($1), &@$), &@$), assignable(p, $1, 0, &@$)); - /*% ripper: rb_ary_new_from_args(2, ripper_assignable(p, $1, get_value($:1)), Qnil) %*/ + /*% ripper: rb_ary_new_from_args(2, ripper_assignable(p, $1, $:1), Qnil) %*/ } ; @@ -5907,19 +5865,19 @@ p_kw_label : tLABEL yyerror1(&loc, "symbol literal with interpolation is not allowed"); $$ = rb_intern_str(STR_NEW0()); } - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; p_kwrest : kwrest_mark tIDENTIFIER { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } | kwrest_mark { $$ = 0; - /*% ripper: 0; %*/ + /*% ripper: 0 %*/ } ; @@ -6001,7 +5959,7 @@ p_variable : tIDENTIFIER { error_duplicate_pattern_variable(p, $1, &@1); $$ = assignable(p, $1, 0, &@$); - /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper: ripper_assignable(p, $1, var_field!($:1)) %*/ } ; @@ -6076,7 +6034,7 @@ opt_rescue : k_rescue exc_list exc_var then exc_list : arg_value { $$ = NEW_LIST($1, &@$); - /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ + /*% ripper: rb_ary_new3(1, $:1) %*/ } | mrhs { @@ -6088,7 +6046,7 @@ exc_list : arg_value exc_var : tASSOC lhs { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } | none ; @@ -6116,7 +6074,7 @@ strings : string node = evstr2dstr(p, node); } $$ = node; - /*% ripper: get_value($:1); %*/ + /*% ripper: $:1 %*/ } ; @@ -6131,32 +6089,26 @@ string : tCHAR string1 : tSTRING_BEG string_contents tSTRING_END { - /*%%%*/ - /*% - int indent = p->heredoc_indent; - %*/ $$ = heredoc_dedent(p, $2); if ($$) nd_set_loc($$, &@$); - /*%%%*/ - /*% - VALUE val = dispatch1(string_literal, ripper_heredoc_dedent(p, indent, get_value($:2))); - set_value(val); - %*/ + /*% ripper: $:2 %*/ + if (p->heredoc_indent > 0) { + /*% ripper: heredoc_dedent!($:$, INT2NUM(%{p->heredoc_indent})) %*/ + p->heredoc_indent = 0; + } + /*% ripper: string_literal!($:$) %*/ } ; xstring : tXSTRING_BEG xstring_contents tSTRING_END { - /*%%%*/ - /*% - int indent = p->heredoc_indent; - %*/ $$ = new_xstring(p, heredoc_dedent(p, $2), &@$); - /*%%%*/ - /*% - VALUE val = dispatch1(xstring_literal, ripper_heredoc_dedent(p, indent, get_value($:2))); - set_value(val); - %*/ + /*% ripper: $:2 %*/ + if (p->heredoc_indent > 0) { + /*% ripper: heredoc_dedent!($:$, INT2NUM(%{p->heredoc_indent})) %*/ + p->heredoc_indent = 0; + } + /*% ripper: xstring_literal!($:$) %*/ } ; @@ -6294,7 +6246,7 @@ regexp_contents: /* none */ ; string_content : tSTRING_CONTENT - /*% ripper[brace]: get_value($:1); %*/ + /*% ripper[brace]: $:1 %*/ | tSTRING_DVAR { /* need to backup p->lex.strterm so that a string literal `%&foo,#$&,bar&` can be parsed */ @@ -6444,12 +6396,12 @@ var_ref : user_variable var_lhs : user_variable { $$ = assignable(p, $1, 0, &@$); - /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper: ripper_assignable(p, $1, var_field!($:1)) %*/ } | keyword_variable { $$ = assignable(p, $1, 0, &@$); - /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/ + /*% ripper: ripper_assignable(p, $1, var_field!($:1)) %*/ } ; @@ -6465,7 +6417,7 @@ superclass : '<' expr_value term { $$ = $3; - /*% ripper: get_value($:3); %*/ + /*% ripper: $:3 %*/ } | /* none */ { @@ -6508,108 +6460,108 @@ f_arglist : f_paren_args $$ = $2; SET_LEX_STATE(EXPR_BEG); p->command_start = TRUE; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } ; args_tail : f_kwarg(f_kw) ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4, &@3); - /*% ripper: rb_ary_new_from_args(3, get_value($:1), get_value($:3), get_value($:4)); %*/ + /*% ripper: rb_ary_new_from_args(3, $:1, $:3, $:4) %*/ } | f_kwarg(f_kw) opt_f_block_arg { $$ = new_args_tail(p, $1, 0, $2, &@1); - /*% ripper: rb_ary_new_from_args(3, get_value($:1), Qnil, get_value($:2)); %*/ + /*% ripper: rb_ary_new_from_args(3, $:1, Qnil, $:2) %*/ } | f_any_kwrest opt_f_block_arg { $$ = new_args_tail(p, 0, $1, $2, &@1); - /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), get_value($:2)); %*/ + /*% ripper: rb_ary_new_from_args(3, Qnil, $:1, $:2) %*/ } | f_block_arg { $$ = new_args_tail(p, 0, 0, $1, &@1); - /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, get_value($:1)); %*/ + /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, $:1) %*/ } | args_forward { add_forwarding_args(p); $$ = new_args_tail(p, 0, $1, arg_FWD_BLOCK, &@1); $$->nd_ainfo.forwarding = 1; - /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), Qnil); %*/ + /*% ripper: rb_ary_new_from_args(3, Qnil, $:1, Qnil) %*/ } ; f_args : f_arg ',' f_optarg(arg_value) ',' f_rest_arg opt_args_tail(args_tail) { $$ = new_args(p, $1, $3, $5, 0, $6, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), Qnil, get_value($:6)) %*/ + /*% ripper: ripper_new_args(p, $:1, $:3, $:5, Qnil, $:6) %*/ } | f_arg ',' f_optarg(arg_value) ',' f_rest_arg ',' f_arg opt_args_tail(args_tail) { $$ = new_args(p, $1, $3, $5, $7, $8, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), get_value($:7), get_value($:8)) %*/ + /*% ripper: ripper_new_args(p, $:1, $:3, $:5, $:7, $:8) %*/ } | f_arg ',' f_optarg(arg_value) opt_args_tail(args_tail) { $$ = new_args(p, $1, $3, 0, 0, $4, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, Qnil, get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, $:1, $:3, Qnil, Qnil, $:4) %*/ } | f_arg ',' f_optarg(arg_value) ',' f_arg opt_args_tail(args_tail) { $$ = new_args(p, $1, $3, 0, $5, $6, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, get_value($:5), get_value($:6)) %*/ + /*% ripper: ripper_new_args(p, $:1, $:3, Qnil, $:5, $:6) %*/ } | f_arg ',' f_rest_arg opt_args_tail(args_tail) { $$ = new_args(p, $1, 0, $3, 0, $4, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), Qnil, get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, $:1, Qnil, $:3, Qnil, $:4) %*/ } | f_arg ',' f_rest_arg ',' f_arg opt_args_tail(args_tail) { $$ = new_args(p, $1, 0, $3, $5, $6, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), get_value($:5), get_value($:6)) %*/ + /*% ripper: ripper_new_args(p, $:1, Qnil, $:3, $:5, $:6) %*/ } | f_arg opt_args_tail(args_tail) { $$ = new_args(p, $1, 0, 0, 0, $2, &@$); - /*% ripper: ripper_new_args(p, get_value($:1), Qnil, Qnil, Qnil, get_value($:2)) %*/ + /*% ripper: ripper_new_args(p, $:1, Qnil, Qnil, Qnil, $:2) %*/ } | f_optarg(arg_value) ',' f_rest_arg opt_args_tail(args_tail) { $$ = new_args(p, 0, $1, $3, 0, $4, &@$); - /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), Qnil, get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, Qnil, $:1, $:3, Qnil, $:4) %*/ } | f_optarg(arg_value) ',' f_rest_arg ',' f_arg opt_args_tail(args_tail) { $$ = new_args(p, 0, $1, $3, $5, $6, &@$); - /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), get_value($:5), get_value($:6)) %*/ + /*% ripper: ripper_new_args(p, Qnil, $:1, $:3, $:5, $:6) %*/ } | f_optarg(arg_value) opt_args_tail(args_tail) { $$ = new_args(p, 0, $1, 0, 0, $2, &@$); - /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, Qnil, get_value($:2)) %*/ + /*% ripper: ripper_new_args(p, Qnil, $:1, Qnil, Qnil, $:2) %*/ } | f_optarg(arg_value) ',' f_arg opt_args_tail(args_tail) { $$ = new_args(p, 0, $1, 0, $3, $4, &@$); - /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, get_value($:3), get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, Qnil, $:1, Qnil, $:3, $:4) %*/ } | f_rest_arg opt_args_tail(args_tail) { $$ = new_args(p, 0, 0, $1, 0, $2, &@$); - /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), Qnil, get_value($:2)) %*/ + /*% ripper: ripper_new_args(p, Qnil, Qnil, $:1, Qnil, $:2) %*/ } | f_rest_arg ',' f_arg opt_args_tail(args_tail) { $$ = new_args(p, 0, 0, $1, $3, $4, &@$); - /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), get_value($:3), get_value($:4)) %*/ + /*% ripper: ripper_new_args(p, Qnil, Qnil, $:1, $:3, $:4) %*/ } | args_tail { $$ = new_args(p, 0, 0, 0, 0, $1, &@$); - /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, get_value($:1)) %*/ + /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, $:1) %*/ } | /* none */ { @@ -6674,10 +6626,7 @@ f_norm_arg : f_bad_arg formal_argument(p, $1); p->max_numparam = ORDINAL_PARAM; $$ = $1; - /*%%%*/ - /*% - ripper_formal_argument(p, $1, get_value($:1)); - %*/ + /*% ripper: ripper_formal_argument(p, $1, $:1) %*/ } ; @@ -6694,7 +6643,7 @@ f_arg_item : f_arg_asgn { p->cur_arg = 0; $$ = NEW_ARGS_AUX($1, 1, &NULL_LOC); - /*% ripper: get_value($:1) %*/ + /*% ripper: $:1 %*/ } | tLPAREN f_margs rparen { @@ -6716,14 +6665,14 @@ f_arg_item : f_arg_asgn ; f_arg : f_arg_item - /*% ripper[brace]: rb_ary_new3(1, get_value($:1)) %*/ + /*% ripper[brace]: rb_ary_new3(1, $:1) %*/ | f_arg ',' f_arg_item { $$ = $1; $$->nd_plen++; $$->nd_next = block_append(p, $$->nd_next, $3->nd_next); rb_discard_node(p, (NODE *)$3); - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ + /*% ripper: rb_ary_push($:1, $:3) %*/ } ; @@ -6735,10 +6684,7 @@ f_label : tLABEL p->max_numparam = ORDINAL_PARAM; p->ctxt.in_argdef = 0; $$ = $1; - /*%%%*/ - /*% - ripper_formal_argument(p, $1, get_value($:1)); - %*/ + /*% ripper: ripper_formal_argument(p, $1, $:1) %*/ } ; @@ -6747,14 +6693,14 @@ f_kw : f_label arg_value p->cur_arg = 0; p->ctxt.in_argdef = 1; $$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$); - /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:2)) %*/ + /*% ripper: rb_assoc_new(ripper_assignable(p, $1, $:1), $:2) %*/ } | f_label { p->cur_arg = 0; p->ctxt.in_argdef = 1; $$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$); - /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), 0) %*/ + /*% ripper: rb_assoc_new(ripper_assignable(p, $1, $:1), 0) %*/ } ; @@ -6762,13 +6708,13 @@ f_block_kw : f_label primary_value { p->ctxt.in_argdef = 1; $$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$); - /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:2)) %*/ + /*% ripper: rb_assoc_new(ripper_assignable(p, $1, $:1), $:2) %*/ } | f_label { p->ctxt.in_argdef = 1; $$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$); - /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), 0) %*/ + /*% ripper: rb_assoc_new(ripper_assignable(p, $1, $:1), 0) %*/ } ; @@ -6835,12 +6781,12 @@ f_block_arg : blkarg_mark tIDENTIFIER opt_f_block_arg : ',' f_block_arg { $$ = $2; - /*% ripper: get_value($:2); %*/ + /*% ripper: $:2 %*/ } | none { $$ = 0; - /*% ripper: Qnil; %*/ + /*% ripper: Qnil %*/ } ; @@ -6890,7 +6836,7 @@ assoc_list : none ; assocs : assoc - /*% ripper[brace]: rb_ary_new3(1, get_value($:1)) %*/ + /*% ripper[brace]: rb_ary_new3(1, $:1) %*/ | assocs ',' assoc { NODE *assocs = $1; @@ -6912,7 +6858,7 @@ assocs : assoc } } $$ = assocs; - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ + /*% ripper: rb_ary_push($:1, $:3) %*/ } ; @@ -9106,7 +9052,6 @@ heredoc_dedent(struct parser_params *p, NODE *root) rb_parser_string_t *prev_lit = 0; if (indent <= 0) return root; - p->heredoc_indent = 0; if (!root) return root; prev_node = node = str_node = root; @@ -9150,17 +9095,6 @@ heredoc_dedent(struct parser_params *p, NODE *root) return root; } -#ifdef RIPPER -static VALUE -ripper_heredoc_dedent(struct parser_params *p, int indent, VALUE array) -{ - if (indent <= 0) return array; - p->heredoc_indent = 0; - dispatch2(heredoc_dedent, array, INT2NUM(indent)); - return array; -} -#endif - static int whole_match_p(struct parser_params *p, const char *eos, long len, int indent) { @@ -9526,7 +9460,7 @@ formal_argument(struct parser_params *p, ID id) } #ifdef RIPPER -static void +static VALUE ripper_formal_argument(struct parser_params *p, ID id, VALUE lhs) { switch (id_type(id)) { @@ -9535,21 +9469,22 @@ ripper_formal_argument(struct parser_params *p, ID id, VALUE lhs) #define ERR(mesg) (dispatch2(param_error, WARN_S(mesg), lhs), ripper_error(p)) case ID_CONST: ERR("formal argument cannot be a constant"); - return; + break; case ID_INSTANCE: ERR("formal argument cannot be an instance variable"); - return; + break; case ID_GLOBAL: ERR("formal argument cannot be a global variable"); - return; + break; case ID_CLASS: ERR("formal argument cannot be a class variable"); - return; + break; default: ERR("formal argument must be local variable"); - return; + break; #undef ERR } + return lhs; } #endif @@ -15064,12 +14999,6 @@ assign_error(struct parser_params *p, const char *mesg, VALUE a) ripper_error(p); return a; } - -static VALUE -var_field(struct parser_params *p, VALUE a) -{ - return dispatch1(var_field, a); -} #endif static NODE * From 2e765c20dbd542d43155749910421b0f7963524f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 10 May 2024 23:08:25 +0900 Subject: [PATCH 121/242] ripper: Short hand for `rb_ary_new_from_args` --- ext/ripper/tools/dsl.rb | 14 ++++++- parse.y | 93 +++++++++++++++-------------------------- 2 files changed, 47 insertions(+), 60 deletions(-) diff --git a/ext/ripper/tools/dsl.rb b/ext/ripper/tools/dsl.rb index d090b15c0bf3b2..68641f4da7e1ea 100644 --- a/ext/ripper/tools/dsl.rb +++ b/ext/ripper/tools/dsl.rb @@ -26,6 +26,18 @@ def self.line?(line, lineno = nil) end end + using Module.new { + refine Array do + def to_s + if empty? + "rb_ary_new()" + else + "rb_ary_new_from_args(#{size}, #{map(&:to_s).join(', ')})" + end + end + end + } + def initialize(code, options, lineno = nil) @lineno = lineno @events = {} @@ -100,7 +112,7 @@ def method_missing(event, *args) elsif args.empty? and /\Aid[A-Z_]/ =~ event.to_s event else - "#{ event }(#{ args.join(", ") })" + "#{ event }(#{ args.map(&:to_s).join(", ") })" end end diff --git a/parse.y b/parse.y index 4f295eb12fef4e..7a679435675120 100644 --- a/parse.y +++ b/parse.y @@ -1638,12 +1638,6 @@ ripper_new_array_pattern(struct parser_params *p, VALUE constant, VALUE pre_arg, return dispatch4(aryptn, constant, pre_args, rest_arg, post_args); } -static VALUE -ripper_new_array_pattern_tail(struct parser_params *p, VALUE pre_args, VALUE rest_arg, VALUE post_args) -{ - return rb_ary_new_from_args(3, pre_args, rest_arg, post_args); -} - static VALUE ripper_new_hash_pattern(struct parser_params *p, VALUE constant, VALUE hshptn) { @@ -1653,18 +1647,6 @@ ripper_new_hash_pattern(struct parser_params *p, VALUE constant, VALUE hshptn) return dispatch3(hshptn, constant, kw_args, kw_rest_arg); } -static VALUE -ripper_new_hash_pattern_tail(struct parser_params *p, VALUE kw_args, VALUE kw_rest_arg) -{ - if (kw_rest_arg) { - kw_rest_arg = dispatch1(var_field, kw_rest_arg); - } - else { - kw_rest_arg = Qnil; - } - return rb_ary_new_from_args(2, kw_args, kw_rest_arg); -} - static VALUE ripper_new_find_pattern(struct parser_params *p, VALUE constant, VALUE fndptn) { @@ -1675,12 +1657,6 @@ ripper_new_find_pattern(struct parser_params *p, VALUE constant, VALUE fndptn) return dispatch4(fndptn, constant, pre_rest_arg, args, post_rest_arg); } -static VALUE -ripper_new_find_pattern_tail(struct parser_params *p, VALUE pre_rest_arg, VALUE args, VALUE post_rest_arg) -{ - return rb_ary_new_from_args(3, pre_rest_arg, args, post_rest_arg); -} - #define ID2VAL(id) STATIC_ID2SYM(id) #define TOKEN2VAL(t) ID2VAL(TOKEN2ID(t)) #endif /* RIPPER */ @@ -3024,7 +3000,7 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) | /* none */ { $$ = new_args_tail(p, 0, 0, 0, &@0); - /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, Qnil) %*/ + /*% ripper: [Qnil, Qnil, Qnil] %*/ } ; @@ -3490,7 +3466,7 @@ defs_head : k_def singleton dot_or_colon $$ = def_head_save(p, $k_def); $$->nd_mid = $def_name; $$->nd_def = NEW_DEFS($singleton, $def_name, 0, &@$); - /*% ripper: rb_ary_new_from_args(3, $:singleton, $:dot_or_colon, $:def_name) %*/ + /*% ripper: [$:singleton, $:dot_or_colon, $:def_name] %*/ } ; @@ -5082,22 +5058,22 @@ f_eq : {p->ctxt.in_argdef = 0;} '='; block_args_tail : f_kwarg(f_block_kw) ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4, &@3); - /*% ripper: rb_ary_new_from_args(3, $:1, $:3, $:4) %*/ + /*% ripper: [$:1, $:3, $:4] %*/ } | f_kwarg(f_block_kw) opt_f_block_arg { $$ = new_args_tail(p, $1, 0, $2, &@1); - /*% ripper: rb_ary_new_from_args(3, $:1, Qnil, $:2) %*/ + /*% ripper: [$:1, Qnil, $:2] %*/ } | f_any_kwrest opt_f_block_arg { $$ = new_args_tail(p, 0, $1, $2, &@1); - /*% ripper: rb_ary_new_from_args(3, Qnil, $:1, $:2) %*/ + /*% ripper: [Qnil, $:1, $:2] %*/ } | f_block_arg { $$ = new_args_tail(p, 0, 0, $1, &@1); - /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, $:1) %*/ + /*% ripper: [Qnil, Qnil, $:1] %*/ } ; @@ -5138,7 +5114,7 @@ block_param : f_arg ',' f_optarg(primary_value) ',' f_rest_arg opt_args_tail(blo { $$ = new_args_tail(p, 0, 0, 0, &@2); $$ = new_args(p, $1, 0, $2, 0, $$, &@$); - /*% ripper: ripper_new_args(p, $:1, Qnil, $:2, Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/ + /*% ripper: params!($:1, Qnil, $:2, Qnil, Qnil, Qnil, Qnil) %*/ } | f_arg ',' f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { @@ -5705,7 +5681,7 @@ p_expr_basic : p_value { $$ = new_hash_pattern_tail(p, 0, 0, &@$); $$ = new_hash_pattern(p, 0, $$, &@$); - /*% ripper: ripper_new_hash_pattern(p, Qnil, ripper_new_hash_pattern_tail(p, Qnil, 0)) %*/ + /*% ripper: ripper_new_hash_pattern(p, Qnil, [Qnil, Qnil]) %*/ } | tLPAREN p_pktbl p_expr rparen { @@ -5719,28 +5695,27 @@ p_args : p_expr { NODE *pre_args = NEW_LIST($1, &@$); $$ = new_array_pattern_tail(p, pre_args, 0, 0, 0, &@$); - /*% ripper: rb_ary_new_from_args(1, $:1) %*/ - /*% ripper: rb_ary_new_from_args(3, $:$, Qnil, Qnil) %*/ + /*% ripper: [[$:1], Qnil, Qnil] %*/ } | p_args_head { $$ = new_array_pattern_tail(p, $1, 1, 0, 0, &@$); - /*% ripper: rb_ary_new_from_args(3, $:1, Qnil, Qnil) %*/ + /*% ripper: [$:1, Qnil, Qnil] %*/ } | p_args_head p_arg { $$ = new_array_pattern_tail(p, list_concat($1, $2), 0, 0, 0, &@$); - /*% ripper: rb_ary_new_from_args(3, rb_ary_concat($:1, $:2), Qnil, Qnil) %*/ + /*% ripper: [rb_ary_concat($:1, $:2), Qnil, Qnil] %*/ } | p_args_head p_rest { $$ = new_array_pattern_tail(p, $1, 1, $2, 0, &@$); - /*% ripper: rb_ary_new_from_args(3, $:1, $:2, Qnil) %*/ + /*% ripper: [$:1, $:2, Qnil] %*/ } | p_args_head p_rest ',' p_args_post { $$ = new_array_pattern_tail(p, $1, 1, $2, $4, &@$); - /*% ripper: rb_ary_new_from_args(3, $:1, $:2, $:4) %*/ + /*% ripper: [$:1, $:2, $:4] %*/ } | p_args_tail ; @@ -5759,19 +5734,19 @@ p_args_head : p_arg ',' p_args_tail : p_rest { $$ = new_array_pattern_tail(p, 0, 1, $1, 0, &@$); - /*% ripper: ripper_new_array_pattern_tail(p, Qnil, $:1, Qnil) %*/ + /*% ripper: [Qnil, $:1, Qnil] %*/ } | p_rest ',' p_args_post { $$ = new_array_pattern_tail(p, 0, 1, $1, $3, &@$); - /*% ripper: ripper_new_array_pattern_tail(p, Qnil, $:1, $:3) %*/ + /*% ripper: [Qnil, $:1, $:3] %*/ } ; p_find : p_rest ',' p_args_post ',' p_rest { $$ = new_find_pattern_tail(p, $1, $3, $5, &@$); - /*% ripper: ripper_new_find_pattern_tail(p, $:1, $:3, $:5) %*/ + /*% ripper: [$:1, $:3, $:5] %*/ } ; @@ -5800,34 +5775,34 @@ p_args_post : p_arg p_arg : p_expr { $$ = NEW_LIST($1, &@$); - /*% ripper: rb_ary_new_from_args(1, $:1) %*/ + /*% ripper: [$:1] %*/ } ; p_kwargs : p_kwarg ',' p_any_kwrest { $$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), $3, &@$); - /*% ripper: ripper_new_hash_pattern_tail(p, $:1, $:3) %*/ + /*% ripper: [$:1, $:3] %*/ } | p_kwarg { $$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), 0, &@$); - /*% ripper: ripper_new_hash_pattern_tail(p, $:1, 0) %*/ + /*% ripper: [$:1, Qnil] %*/ } | p_kwarg ',' { $$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), 0, &@$); - /*% ripper: ripper_new_hash_pattern_tail(p, $:1, 0) %*/ + /*% ripper: [$:1, Qnil] %*/ } | p_any_kwrest { $$ = new_hash_pattern_tail(p, new_hash(p, 0, &@$), $1, &@$); - /*% ripper: ripper_new_hash_pattern_tail(p, rb_ary_new(), $:1) %*/ + /*% ripper: [[], $:1] %*/ } ; p_kwarg : p_kw - /*% ripper[brace]: rb_ary_new_from_args(1, $:1) %*/ + /*% ripper[brace]: [$:1] %*/ | p_kwarg ',' p_kw { $$ = list_concat($1, $3); @@ -5839,7 +5814,7 @@ p_kw : p_kw_label p_expr { error_duplicate_pattern_key(p, $1, &@1); $$ = list_append(p, NEW_LIST(NEW_SYM(rb_id2str($1), &@1), &@$), $2); - /*% ripper: rb_ary_new_from_args(2, $:1, $:2) %*/ + /*% ripper: [$:1, $:2] %*/ } | p_kw_label { @@ -5849,7 +5824,7 @@ p_kw : p_kw_label p_expr } error_duplicate_pattern_variable(p, $1, &@1); $$ = list_append(p, NEW_LIST(NEW_SYM(rb_id2str($1), &@$), &@$), assignable(p, $1, 0, &@$)); - /*% ripper: rb_ary_new_from_args(2, ripper_assignable(p, $1, $:1), Qnil) %*/ + /*% ripper: [ripper_assignable(p, $1, $:1), Qnil] %*/ } ; @@ -5872,12 +5847,12 @@ p_kw_label : tLABEL p_kwrest : kwrest_mark tIDENTIFIER { $$ = $2; - /*% ripper: $:2 %*/ + /*% ripper: var_field!($:2) %*/ } | kwrest_mark { $$ = 0; - /*% ripper: 0 %*/ + /*% ripper: Qnil %*/ } ; @@ -5891,7 +5866,7 @@ p_any_kwrest : p_kwrest | p_kwnorest { $$ = idNil; - /*% ripper: ID2VAL(idNil) %*/ + /*% ripper: var_field!(ID2VAL(idNil)) %*/ } ; @@ -6432,7 +6407,7 @@ f_opt_paren_args: f_paren_args p->ctxt.in_argdef = 0; $$ = new_args_tail(p, 0, 0, 0, &@0); $$ = new_args(p, 0, 0, 0, 0, $$, &@0); - /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/ + /*% ripper: params!(Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil) %*/ } ; @@ -6467,29 +6442,29 @@ f_arglist : f_paren_args args_tail : f_kwarg(f_kw) ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4, &@3); - /*% ripper: rb_ary_new_from_args(3, $:1, $:3, $:4) %*/ + /*% ripper: [$:1, $:3, $:4] %*/ } | f_kwarg(f_kw) opt_f_block_arg { $$ = new_args_tail(p, $1, 0, $2, &@1); - /*% ripper: rb_ary_new_from_args(3, $:1, Qnil, $:2) %*/ + /*% ripper: [$:1, Qnil, $:2] %*/ } | f_any_kwrest opt_f_block_arg { $$ = new_args_tail(p, 0, $1, $2, &@1); - /*% ripper: rb_ary_new_from_args(3, Qnil, $:1, $:2) %*/ + /*% ripper: [Qnil, $:1, $:2] %*/ } | f_block_arg { $$ = new_args_tail(p, 0, 0, $1, &@1); - /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, $:1) %*/ + /*% ripper: [Qnil, Qnil, $:1] %*/ } | args_forward { add_forwarding_args(p); $$ = new_args_tail(p, 0, $1, arg_FWD_BLOCK, &@1); $$->nd_ainfo.forwarding = 1; - /*% ripper: rb_ary_new_from_args(3, Qnil, $:1, Qnil) %*/ + /*% ripper: [Qnil, $:1, Qnil] %*/ } ; @@ -6567,7 +6542,7 @@ f_args : f_arg ',' f_optarg(arg_value) ',' f_rest_arg opt_args_tail(args_tail) { $$ = new_args_tail(p, 0, 0, 0, &@0); $$ = new_args(p, 0, 0, 0, 0, $$, &@0); - /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/ + /*% ripper: params!(Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil) %*/ } ; From ee8bbbabe549d1ff8a37509e3a5dc9e648aced3a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 12 May 2024 11:34:01 +0900 Subject: [PATCH 122/242] ripper: Show popped TOS in debug mode --- parse.y | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/parse.y b/parse.y index 7a679435675120..b467c4a15488c9 100644 --- a/parse.y +++ b/parse.y @@ -634,10 +634,10 @@ static void after_reduce(int len, struct parser_params *p) { for (int i = 0; i < len; i++) { + VALUE tos = rb_ary_pop(p->s_value_stack); if (p->debug) { - rb_parser_printf(p, "after-reduce pop: %+"PRIsVALUE"\n", rb_ary_entry(p->s_value_stack, -1)); + rb_parser_printf(p, "after-reduce pop: %+"PRIsVALUE"\n", tos); } - rb_ary_pop(p->s_value_stack); } if (p->debug) { rb_parser_printf(p, "after-reduce push: %+"PRIsVALUE"\n", p->s_lvalue); @@ -659,10 +659,10 @@ static void after_pop_stack(int len, struct parser_params *p) { for (int i = 0; i < len; i++) { + VALUE tos = rb_ary_pop(p->s_value_stack); if (p->debug) { - rb_parser_printf(p, "after-pop-stack pop: %+"PRIsVALUE"\n", rb_ary_entry(p->s_value_stack, -1)); + rb_parser_printf(p, "after-pop-stack pop: %+"PRIsVALUE"\n", tos); } - rb_ary_pop(p->s_value_stack); } } #else From 147134b474fc74b68537750a2f63c81a87bd0e86 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 12 May 2024 11:44:45 +0900 Subject: [PATCH 123/242] ripper: Remove rb_ripper_none Now it is used only for wheter `opt_paren_args` is `none`. Introduce a new special node to distinguish an empty parentheses from it . --- ext/ripper/ripper_init.c.tmpl | 4 ---- ext/ripper/ripper_init.h | 1 - ext/ripper/tools/dsl.rb | 7 +------ parse.y | 36 +++++++++++++++++++++++++---------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/ext/ripper/ripper_init.c.tmpl b/ext/ripper/ripper_init.c.tmpl index 21f29bd79a80f1..fc98c067b87f21 100644 --- a/ext/ripper/ripper_init.c.tmpl +++ b/ext/ripper/ripper_init.c.tmpl @@ -665,8 +665,4 @@ InitVM_ripper(void) */ rb_define_global_const("SCRIPT_LINES__", Qnil); #endif - rb_ripper_none = rb_obj_alloc(rb_cObject); - rb_obj_freeze(rb_ripper_none); - rb_gc_register_mark_object(rb_ripper_none); - } diff --git a/ext/ripper/ripper_init.h b/ext/ripper/ripper_init.h index 664bb7bce368fc..9d228107d1b172 100644 --- a/ext/ripper/ripper_init.h +++ b/ext/ripper/ripper_init.h @@ -1,7 +1,6 @@ #ifndef RIPPER_INIT_H #define RIPPER_INIT_H -extern VALUE rb_ripper_none; PRINTF_ARGS(void ripper_compile_error(struct parser_params*, const char *fmt, ...), 2, 3); #endif /* RIPPER_INIT_H */ diff --git a/ext/ripper/tools/dsl.rb b/ext/ripper/tools/dsl.rb index 68641f4da7e1ea..5ef1d693188143 100644 --- a/ext/ripper/tools/dsl.rb +++ b/ext/ripper/tools/dsl.rb @@ -87,11 +87,7 @@ def new_var "v#{ @vars += 1 }" end - def opt_event(event, default, addend) - add_event(event, [default, addend], true) - end - - def add_event(event, args, qundef_check = false) + def add_event(event, args) event = event.to_s.sub(/!\z/, "") @events[event] = args.size vars = [] @@ -101,7 +97,6 @@ def add_event(event, args, qundef_check = false) end v = new_var d = "dispatch#{ args.size }(#{ [event, *vars].join(",") })" - d = "#{ vars.last }==rb_ripper_none ? #{ vars.first } : #{ d }" if qundef_check @code << "#{ v }=#{ d };" v end diff --git a/parse.y b/parse.y index b467c4a15488c9..3e6be1db2c47da 100644 --- a/parse.y +++ b/parse.y @@ -83,6 +83,9 @@ static NODE *reg_named_capture_assign(struct parser_params* p, VALUE regexp, con #define compile_callback rb_suppress_tracing #endif /* !UNIVERSAL_PARSER */ +#define NODE_SPECIAL_EMPTY_ARGS ((NODE *)-1) +#define NODE_EMPTY_ARGS_P(node) ((node) == NODE_SPECIAL_EMPTY_ARGS) + static int rb_parser_string_hash_cmp(rb_parser_string_t *str1, rb_parser_string_t *str2); static rb_parser_string_t *rb_parser_string_deep_copy(struct parser_params *p, const rb_parser_string_t *original); @@ -294,7 +297,6 @@ parse_isxdigit(int c) #define STRNCASECMP rb_parser_st_locale_insensitive_strncasecmp #ifdef RIPPER -VALUE rb_ripper_none; #include "ripper_init.h" #endif @@ -4268,13 +4270,16 @@ paren_args : '(' opt_call_args rparen opt_paren_args : none | paren_args + { + $$ = $1 ? $1 : NODE_SPECIAL_EMPTY_ARGS; + } ; opt_call_args : none | call_args | args ',' { - $$ = $1; + $$ = $1; } | args ',' assocs ',' { @@ -5324,13 +5329,23 @@ block_call : command do_block } | block_call call_op2 operation2 opt_paren_args { + bool has_args = $4 != 0; + if (NODE_EMPTY_ARGS_P($4)) $4 = 0; $$ = new_qcall(p, $2, $1, $3, $4, &@3, &@$); - /*% ripper: opt_event(:method_add_arg!, call!($:1, $:2, $:3), $:4) %*/ + /*% ripper: call!($:1, $:2, $:3) %*/ + if (has_args) { + /*% ripper: method_add_arg!($:$, $:4) %*/ + } } | block_call call_op2 operation2 opt_paren_args brace_block { + bool has_args = $5 != 0; + if (NODE_EMPTY_ARGS_P($5)) $5 = 0; $$ = new_command_qcall(p, $2, $1, $3, $4, $5, &@3, &@$); - /*% ripper: opt_event(:method_add_block!, command_call!($:1, $:2, $:3, $:4), $:5) %*/ + /*% ripper: command_call!($:1, $:2, $:3, $:4) %*/ + if (has_args) { + /*% ripper: method_add_block!($:$, $:5) %*/ + } } | block_call call_op2 operation2 command_args do_block { @@ -5348,9 +5363,14 @@ method_call : fcall paren_args } | primary_value call_op operation2 opt_paren_args { + bool has_args = $4 != 0; + if (NODE_EMPTY_ARGS_P($4)) $4 = 0; $$ = new_qcall(p, $2, $1, $3, $4, &@3, &@$); nd_set_line($$, @3.end_pos.lineno); - /*% ripper: opt_event(:method_add_arg!, call!($:1, $:2, $:3), $:4) %*/ + /*% ripper: call!($:1, $:2, $:3) %*/ + if (has_args) { + /*% ripper: method_add_arg!($:$, $:4) %*/ + } } | primary_value tCOLON2 operation2 paren_args { @@ -6928,10 +6948,6 @@ terms : term none : /* none */ { $$ = 0; - /*%%%*/ - /*% - set_value(rb_ripper_none); - %*/ } ; %% @@ -16160,7 +16176,7 @@ rb_yytnamerr(struct parser_params *p, char *yyres, const char *yystr) #endif #ifdef RIPPER -#define validate(x) ((x) = (x) == rb_ripper_none ? Qnil : x) +#define validate(x) (void)(x) static VALUE ripper_dispatch0(struct parser_params *p, ID mid) From 47efdae6028e39bbe8f35d325e26e6bc206251ef Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 12 May 2024 11:59:44 +0900 Subject: [PATCH 124/242] ripper: Preserve indentation --- ext/ripper/tools/dsl.rb | 11 ++++++----- ext/ripper/tools/preproc.rb | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ext/ripper/tools/dsl.rb b/ext/ripper/tools/dsl.rb index 5ef1d693188143..4815364c9290f9 100644 --- a/ext/ripper/tools/dsl.rb +++ b/ext/ripper/tools/dsl.rb @@ -20,9 +20,9 @@ class DSL NAME_PATTERN = /(?>\$|\d+|[a-zA-Z_][a-zA-Z0-9_]*|\[[a-zA-Z_.][-a-zA-Z0-9_.]*\])(?>(?:\.|->)[a-zA-Z_][a-zA-Z0-9_]*)*/.source NOT_REF_PATTERN = /(?>\#.*|[^\"$@]*|"(?>\\.|[^\"])*")/.source - def self.line?(line, lineno = nil) - if %r =~ line - new($2, $1&.split(",") || [], lineno) + def self.line?(line, lineno = nil, indent: nil) + if %r<(?\s*)/\*% *ripper(?:\[(?