summaryrefslogtreecommitdiff
path: root/ext/digest/sha1/extconf.rb
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2021-08-23 14:02:42 -0400
committerPeter Zhu <peter@peterzhu.ca>2021-08-23 14:57:52 -0400
commit6648b411f7350711417936865331cf5066ef35aa (patch)
tree070a1b58956f4fc1e1bfb48a5d4182a90824659b /ext/digest/sha1/extconf.rb
parenteddd369e7378ec762cf81a226aec1a2b8719bf7a (diff)
Replace intptr_t with uintptr_t in gc.c
Pointers may be large to the point where intptr_t would be negative. This is problematic when doing comparisons of pointers.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/4765
Diffstat (limited to 'ext/digest/sha1/extconf.rb')
0 files changed, 0 insertions, 0 deletions
r--r--.github/workflows/bundled_gems.yml2
-rw-r--r--.github/workflows/check_dependencies.yml9
-rw-r--r--.github/workflows/check_misc.yml2
-rw-r--r--.github/workflows/codeql-analysis.yml7
-rw-r--r--.github/workflows/compilers.yml14
-rw-r--r--.github/workflows/dependabot_automerge.yml4
-rw-r--r--.github/workflows/macos.yml8
-rw-r--r--.github/workflows/mingw.yml10
-rw-r--r--.github/workflows/publish.yml18
-rw-r--r--.github/workflows/rjit-bindgen.yml9
-rw-r--r--.github/workflows/rjit.yml5
-rw-r--r--.github/workflows/scorecards.yml6
-rw-r--r--.github/workflows/spec_guards.yml6
-rw-r--r--.github/workflows/ubuntu.yml6
-rw-r--r--.github/workflows/wasm.yml15
-rw-r--r--.github/workflows/windows.yml50
-rw-r--r--.github/workflows/yjit-macos.yml12
-rw-r--r--.github/workflows/yjit-ubuntu.yml18
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml9
-rw-r--r--array.c3
-rw-r--r--bignum.c2
-rw-r--r--bootstraptest/test_fork.rb26
-rw-r--r--bootstraptest/test_ractor.rb14
-rw-r--r--bootstraptest/test_yjit.rb255
-rw-r--r--class.c25
-rw-r--r--common.mk20
-rw-r--r--compile.c64
-rw-r--r--complex.c7
-rw-r--r--configure.ac41
-rw-r--r--cont.c10
-rw-r--r--coroutine/asyncify/Context.h8
-rw-r--r--cygwin/GNUmakefile.in5
-rw-r--r--defs/gmake.mk3
-rw-r--r--doc/_timezones.rdoc50
-rw-r--r--doc/irb/indexes.md3
-rw-r--r--doc/string/new.rdoc4
-rw-r--r--enc/Makefile.in2
-rw-r--r--enc/depend1
-rw-r--r--enc/trans/iso2022.trans3
-rw-r--r--encoding.c15
-rw-r--r--enum.c2
-rw-r--r--error.c6
-rw-r--r--eval.c18
-rw-r--r--eval_error.c4
-rw-r--r--ext/-test-/string/fstring.c4
-rw-r--r--ext/date/date_core.c21
-rw-r--r--ext/etc/extconf.rb2
-rwxr-xr-xext/extmk.rb22
-rw-r--r--ext/io/console/extconf.rb10
-rw-r--r--ext/io/nonblock/extconf.rb2
-rw-r--r--ext/io/wait/extconf.rb4
-rw-r--r--ext/json/generator/generator.c42
-rw-r--r--ext/json/lib/json/add/ostruct.rb7
-rw-r--r--ext/json/lib/json/common.rb3
-rw-r--r--ext/json/lib/json/generic_object.rb8
-rw-r--r--ext/json/lib/json/version.rb2
-rw-r--r--ext/json/parser/extconf.rb4
-rw-r--r--ext/objspace/objspace_dump.c5
-rw-r--r--ext/openssl/extconf.rb2
-rw-r--r--ext/ripper/lib/ripper/lexer.rb7
-rw-r--r--ext/socket/extconf.rb2
-rw-r--r--ext/socket/ipsocket.c11
-rw-r--r--ext/socket/raddrinfo.c36
-rw-r--r--ext/socket/rubysocket.h4
-rw-r--r--ext/stringio/stringio.c2
-rw-r--r--ext/strscan/extconf.rb4
-rw-r--r--ext/strscan/strscan.c20
-rw-r--r--ext/win32/lib/win32/registry.rb13
-rw-r--r--ext/zlib/zlib.c5
-rw-r--r--gc.c7
-rw-r--r--gems/bundled_gems10
-rw-r--r--hash.c143
-rw-r--r--include/ruby/internal/anyargs.h22
-rw-r--r--include/ruby/internal/attr/nonstring.h32
-rw-r--r--include/ruby/internal/stdbool.h12
-rw-r--r--include/ruby/io/buffer.h8
-rw-r--r--include/ruby/onigmo.h1
-rw-r--r--include/ruby/win32.h7
-rw-r--r--internal/class.h3
-rw-r--r--internal/eval.h1
-rw-r--r--internal/object.h2
-rw-r--r--internal/proc.h1
-rw-r--r--internal/string.h1
-rw-r--r--internal/thread.h1
-rw-r--r--internal/time.h5
-rw-r--r--io.c88
-rw-r--r--io_buffer.c4
-rw-r--r--iseq.c25
-rw-r--r--iseq.h1
-rw-r--r--lib/bundled_gems.rb133
-rw-r--r--lib/bundler.rb84
-rw-r--r--lib/bundler/cli.rb81
-rw-r--r--lib/bundler/cli/add.rb2
-rw-r--r--lib/bundler/cli/binstubs.rb2
-rw-r--r--lib/bundler/cli/check.rb2
-rw-r--r--lib/bundler/cli/fund.rb2
-rw-r--r--lib/bundler/cli/gem.rb30
-rw-r--r--lib/bundler/cli/install.rb15
-rw-r--r--lib/bundler/cli/lock.rb19
-rw-r--r--lib/bundler/cli/outdated.rb34
-rw-r--r--lib/bundler/cli/plugin.rb5
-rw-r--r--lib/bundler/compact_index_client.rb131
-rw-r--r--lib/bundler/compact_index_client/cache.rb119
-rw-r--r--lib/bundler/compact_index_client/parser.rb84
-rw-r--r--lib/bundler/constants.rb9
-rw-r--r--lib/bundler/definition.rb297
-rw-r--r--lib/bundler/dependency.rb3
-rw-r--r--lib/bundler/dsl.rb73
-rw-r--r--lib/bundler/endpoint_specification.rb11
-rw-r--r--lib/bundler/env.rb2
-rw-r--r--lib/bundler/environment_preserver.rb28
-rw-r--r--lib/bundler/errors.rb26
-rw-r--r--lib/bundler/fetcher.rb8
-rw-r--r--lib/bundler/fetcher/compact_index.rb39
-rw-r--r--lib/bundler/fetcher/downloader.rb2
-rw-r--r--lib/bundler/force_platform.rb2
-rw-r--r--lib/bundler/gem_helper.rb2
-rw-r--r--lib/bundler/gem_helpers.rb21
-rw-r--r--lib/bundler/gem_version_promoter.rb80
-rw-r--r--lib/bundler/injector.rb10
-rw-r--r--lib/bundler/inline.rb39
-rw-r--r--lib/bundler/installer.rb42
-rw-r--r--lib/bundler/installer/gem_installer.rb7
-rw-r--r--lib/bundler/installer/parallel_installer.rb5
-rw-r--r--lib/bundler/installer/standalone.rb3
-rw-r--r--lib/bundler/lazy_specification.rb1
-rw-r--r--lib/bundler/lockfile_parser.rb2
-rw-r--r--lib/bundler/man/bundle-add.143
-rw-r--r--lib/bundler/man/bundle-add.1.ronn51
-rw-r--r--lib/bundler/man/bundle-binstubs.12
-rw-r--r--lib/bundler/man/bundle-cache.12
-rw-r--r--lib/bundler/man/bundle-check.14
-rw-r--r--lib/bundler/man/bundle-check.1.ronn3
-rw-r--r--lib/bundler/man/bundle-clean.12
-rw-r--r--lib/bundler/man/bundle-config.16
-rw-r--r--lib/bundler/man/bundle-config.1.ronn5
-rw-r--r--lib/bundler/man/bundle-console.12
-rw-r--r--lib/bundler/man/bundle-doctor.12
-rw-r--r--lib/bundler/man/bundle-exec.12
-rw-r--r--lib/bundler/man/bundle-gem.18
-rw-r--r--lib/bundler/man/bundle-gem.1.ronn11
-rw-r--r--lib/bundler/man/bundle-help.12
-rw-r--r--lib/bundler/man/bundle-info.12
-rw-r--r--lib/bundler/man/bundle-init.12
-rw-r--r--lib/bundler/man/bundle-inject.12
-rw-r--r--lib/bundler/man/bundle-install.16
-rw-r--r--lib/bundler/man/bundle-install.1.ronn4
-rw-r--r--lib/bundler/man/bundle-list.12
-rw-r--r--lib/bundler/man/bundle-lock.12
-rw-r--r--lib/bundler/man/bundle-open.12
-rw-r--r--lib/bundler/man/bundle-outdated.12
-rw-r--r--lib/bundler/man/bundle-platform.12
-rw-r--r--lib/bundler/man/bundle-plugin.111
-rw-r--r--lib/bundler/man/bundle-plugin.1.ronn10
-rw-r--r--lib/bundler/man/bundle-pristine.12
-rw-r--r--lib/bundler/man/bundle-remove.12
-rw-r--r--lib/bundler/man/bundle-show.12
-rw-r--r--lib/bundler/man/bundle-update.12
-rw-r--r--lib/bundler/man/bundle-version.12
-rw-r--r--lib/bundler/man/bundle-viz.12
-rw-r--r--lib/bundler/man/bundle.12
-rw-r--r--lib/bundler/man/gemfile.58
-rw-r--r--lib/bundler/man/gemfile.5.ronn10
-rw-r--r--lib/bundler/mirror.rb6
-rw-r--r--lib/bundler/plugin/api/source.rb7
-rw-r--r--lib/bundler/plugin/installer.rb52
-rw-r--r--lib/bundler/plugin/installer/path.rb18
-rw-r--r--lib/bundler/plugin/source_list.rb8
-rw-r--r--lib/bundler/resolver.rb143
-rw-r--r--lib/bundler/resolver/base.rb10
-rw-r--r--lib/bundler/resolver/candidate.rb22
-rw-r--r--lib/bundler/resolver/package.rb15
-rw-r--r--lib/bundler/resolver/spec_group.rb22
-rw-r--r--lib/bundler/retry.rb2
-rw-r--r--lib/bundler/ruby_version.rb8
-rw-r--r--lib/bundler/rubygems_ext.rb173
-rw-r--r--lib/bundler/rubygems_gem_installer.rb40
-rw-r--r--lib/bundler/rubygems_integration.rb40
-rw-r--r--lib/bundler/runtime.rb9
-rw-r--r--lib/bundler/self_manager.rb32
-rw-r--r--lib/bundler/settings.rb46
-rw-r--r--lib/bundler/setup.rb6
-rw-r--r--lib/bundler/shared_helpers.rb10
-rw-r--r--lib/bundler/source/git.rb90
-rw-r--r--lib/bundler/source/git/git_proxy.rb29
-rw-r--r--lib/bundler/source/metadata.rb2
-rw-r--r--lib/bundler/source/path.rb17
-rw-r--r--lib/bundler/source/rubygems.rb85
-rw-r--r--lib/bundler/source/rubygems/remote.rb2
-rw-r--r--lib/bundler/source_list.rb28
-rw-r--r--lib/bundler/spec_set.rb30
-rw-r--r--lib/bundler/stub_specification.rb23
-rw-r--r--lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt106
-rw-r--r--lib/bundler/templates/newgem/README.md.tt8
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt7
-rw-r--r--lib/bundler/templates/newgem/rubocop.yml.tt5
-rw-r--r--lib/bundler/ui/shell.rb26
-rw-r--r--lib/bundler/ui/silent.rb13
-rw-r--r--lib/bundler/uri_credentials_filter.rb4
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb98
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb1
-rw-r--r--lib/bundler/vendor/securerandom/.document (renamed from lib/rubygems/net-http/.document)0
-rw-r--r--lib/bundler/vendor/securerandom/lib/random/formatter.rb373
-rw-r--r--lib/bundler/vendor/securerandom/lib/securerandom.rb96
-rw-r--r--lib/bundler/vendor/uri/lib/uri/common.rb2
-rw-r--r--lib/bundler/vendor/uri/lib/uri/version.rb2
-rw-r--r--lib/bundler/vendored_net_http.rb25
-rw-r--r--lib/bundler/vendored_securerandom.rb14
-rw-r--r--lib/bundler/vendored_timeout.rb10
-rw-r--r--lib/bundler/vendored_uri.rb19
-rw-r--r--lib/bundler/version.rb2
-rw-r--r--lib/bundler/yaml_serializer.rb19
-rw-r--r--lib/cgi.rb2
-rw-r--r--lib/cgi/cgi.gemspec3
-rw-r--r--lib/cgi/cookie.rb5
-rw-r--r--lib/cgi/session/pstore.rb7
-rw-r--r--lib/cgi/util.rb4
-rw-r--r--lib/irb.rb1377
-rw-r--r--lib/irb/cmd/backtrace.rb21
-rw-r--r--lib/irb/cmd/break.rb21
-rw-r--r--lib/irb/cmd/catch.rb21
-rw-r--r--lib/irb/cmd/chws.rb36
-rw-r--r--lib/irb/cmd/edit.rb60
-rw-r--r--lib/irb/cmd/help.rb23
-rw-r--r--lib/irb/cmd/info.rb21
-rw-r--r--lib/irb/cmd/nop.rb55
-rw-r--r--lib/irb/cmd/pushws.rb45
-rw-r--r--lib/irb/cmd/show_cmds.rb59
-rw-r--r--lib/irb/cmd/show_doc.rb48
-rw-r--r--lib/irb/cmd/show_source.rb65
-rw-r--r--lib/irb/color.rb4
-rw-r--r--lib/irb/command.rb23
-rw-r--r--lib/irb/command/backtrace.rb17
-rw-r--r--lib/irb/command/base.rb62
-rw-r--r--lib/irb/command/break.rb17
-rw-r--r--lib/irb/command/catch.rb17
-rw-r--r--lib/irb/command/chws.rb40
-rw-r--r--lib/irb/command/context.rb16
-rw-r--r--lib/irb/command/continue.rb (renamed from lib/irb/cmd/continue.rb)6
-rw-r--r--lib/irb/command/debug.rb (renamed from lib/irb/cmd/debug.rb)29
-rw-r--r--lib/irb/command/delete.rb (renamed from lib/irb/cmd/delete.rb)6
-rw-r--r--lib/irb/command/disable_irb.rb19
-rw-r--r--lib/irb/command/edit.rb63
-rw-r--r--lib/irb/command/exit.rb18
-rw-r--r--lib/irb/command/finish.rb (renamed from lib/irb/cmd/finish.rb)6
-rw-r--r--lib/irb/command/force_exit.rb18
-rw-r--r--lib/irb/command/help.rb83
-rw-r--r--lib/irb/command/history.rb (renamed from lib/irb/cmd/history.rb)16
-rw-r--r--lib/irb/command/info.rb17
-rw-r--r--lib/irb/command/internal_helpers.rb27
-rw-r--r--lib/irb/command/irb_info.rb (renamed from lib/irb/cmd/irb_info.rb)13
-rw-r--r--lib/irb/command/load.rb (renamed from lib/irb/cmd/load.rb)31
-rw-r--r--lib/irb/command/ls.rb (renamed from lib/irb/cmd/ls.rb)44
-rw-r--r--lib/irb/command/measure.rb (renamed from lib/irb/cmd/measure.rb)24
-rw-r--r--lib/irb/command/next.rb (renamed from lib/irb/cmd/next.rb)6
-rw-r--r--lib/irb/command/pushws.rb65
-rw-r--r--lib/irb/command/show_doc.rb51
-rw-r--r--lib/irb/command/show_source.rb74
-rw-r--r--lib/irb/command/step.rb (renamed from lib/irb/cmd/step.rb)6
-rw-r--r--lib/irb/command/subirb.rb (renamed from lib/irb/cmd/subirb.rb)48
-rw-r--r--lib/irb/command/whereami.rb (renamed from lib/irb/cmd/whereami.rb)8
-rw-r--r--lib/irb/completion.rb31
-rw-r--r--lib/irb/context.rb140
-rw-r--r--lib/irb/default_commands.rb260
-rw-r--r--lib/irb/ext/change-ws.rb14
-rw-r--r--lib/irb/ext/eval_history.rb6
-rw-r--r--lib/irb/ext/loader.rb8
-rw-r--r--lib/irb/ext/multi-irb.rb10
-rw-r--r--lib/irb/ext/tracer.rb63
-rw-r--r--lib/irb/ext/use-loader.rb14
-rw-r--r--lib/irb/ext/workspaces.rb44
-rw-r--r--lib/irb/extend-command.rb360
-rw-r--r--lib/irb/frame.rb2
-rw-r--r--lib/irb/help.rb4
-rw-r--r--lib/irb/helper_method.rb29
-rw-r--r--lib/irb/helper_method/base.rb16
-rw-r--r--lib/irb/helper_method/conf.rb11
-rw-r--r--lib/irb/history.rb17
-rw-r--r--lib/irb/init.rb115
-rw-r--r--lib/irb/input-method.rb64
-rw-r--r--lib/irb/inspector.rb6
-rw-r--r--lib/irb/irb.gemspec4
-rw-r--r--lib/irb/lc/error.rb12
-rw-r--r--lib/irb/lc/ja/error.rb12
-rw-r--r--lib/irb/lc/ja/help-message10
-rw-r--r--lib/irb/locale.rb4
-rw-r--r--lib/irb/nesting_parser.rb16
-rw-r--r--lib/irb/notifier.rb2
-rw-r--r--lib/irb/output-method.rb10
-rw-r--r--lib/irb/ruby-lex.rb4
-rw-r--r--lib/irb/source_finder.rb135
-rw-r--r--lib/irb/statement.rb48
-rw-r--r--lib/irb/version.rb6
-rw-r--r--lib/irb/workspace.rb28
-rw-r--r--lib/irb/ws-for-case-2.rb2
-rw-r--r--lib/irb/xmp.rb2
-rw-r--r--lib/mkmf.rb2
-rw-r--r--lib/rdoc/store.rb45
-rw-r--r--lib/rdoc/version.rb2
-rw-r--r--lib/reline.rb298
-rw-r--r--lib/reline/config.rb174
-rw-r--r--lib/reline/face.rb8
-rw-r--r--lib/reline/general_io.rb116
-rw-r--r--lib/reline/history.rb2
-rw-r--r--lib/reline/io.rb41
-rw-r--r--lib/reline/io/ansi.rb (renamed from lib/reline/ansi.rb)237
-rw-r--r--lib/reline/io/dumb.rb106
-rw-r--r--lib/reline/io/windows.rb (renamed from lib/reline/windows.rb)218
-rw-r--r--lib/reline/key_actor.rb1
-rw-r--r--lib/reline/key_actor/base.rb28
-rw-r--r--lib/reline/key_actor/composite.rb17
-rw-r--r--lib/reline/key_actor/emacs.rb30
-rw-r--r--lib/reline/key_actor/vi_command.rb50
-rw-r--r--lib/reline/key_actor/vi_insert.rb14
-rw-r--r--lib/reline/key_stroke.rb169
-rw-r--r--lib/reline/kill_ring.rb4
-rw-r--r--lib/reline/line_editor.rb2827
-rw-r--r--lib/reline/reline.gemspec5
-rw-r--r--lib/reline/terminfo.rb28
-rw-r--r--lib/reline/unicode.rb131
-rw-r--r--lib/reline/unicode/east_asian_width.rb2453
-rw-r--r--lib/reline/version.rb2
-rw-r--r--lib/resolv.rb8
-rw-r--r--lib/ruby_vm/rjit/insn_compiler.rb2
-rw-r--r--lib/rubygems.rb61
-rw-r--r--lib/rubygems/basic_specification.rb44
-rw-r--r--lib/rubygems/bundler_version_finder.rb2
-rw-r--r--lib/rubygems/command.rb2
-rw-r--r--lib/rubygems/command_manager.rb11
-rw-r--r--lib/rubygems/commands/build_command.rb13
-rw-r--r--lib/rubygems/commands/cleanup_command.rb12
-rw-r--r--lib/rubygems/commands/contents_command.rb23
-rw-r--r--lib/rubygems/commands/exec_command.rb7
-rw-r--r--lib/rubygems/commands/fetch_command.rb14
-rw-r--r--lib/rubygems/commands/help_command.rb4
-rw-r--r--lib/rubygems/commands/install_command.rb4
-rw-r--r--lib/rubygems/commands/pristine_command.rb43
-rw-r--r--lib/rubygems/commands/rdoc_command.rb9
-rw-r--r--lib/rubygems/commands/rebuild_command.rb264
-rw-r--r--lib/rubygems/commands/setup_command.rb4
-rw-r--r--lib/rubygems/commands/sources_command.rb4
-rw-r--r--lib/rubygems/commands/uninstall_command.rb13
-rw-r--r--lib/rubygems/commands/update_command.rb17
-rw-r--r--lib/rubygems/config_file.rb49
-rw-r--r--lib/rubygems/defaults.rb8
-rw-r--r--lib/rubygems/dependency.rb28
-rw-r--r--lib/rubygems/dependency_list.rb2
-rw-r--r--lib/rubygems/deprecate.rb156
-rw-r--r--lib/rubygems/errors.rb3
-rw-r--r--lib/rubygems/exceptions.rb5
-rw-r--r--lib/rubygems/ext/cargo_builder.rb19
-rw-r--r--lib/rubygems/gemcutter_utilities.rb63
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_listener.rb2
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_poller.rb4
-rw-r--r--lib/rubygems/gemspec_helpers.rb19
-rw-r--r--lib/rubygems/installer.rb54
-rw-r--r--lib/rubygems/local_remote_options.rb12
-rw-r--r--lib/rubygems/net/http.rb3
-rw-r--r--lib/rubygems/optparse.rb3
-rw-r--r--lib/rubygems/optparse/lib/optparse/uri.rb7
-rw-r--r--lib/rubygems/package.rb21
-rw-r--r--lib/rubygems/package/tar_header.rb24
-rw-r--r--lib/rubygems/platform.rb5
-rw-r--r--lib/rubygems/query_utils.rb2
-rw-r--r--lib/rubygems/remote_fetcher.rb7
-rw-r--r--lib/rubygems/request.rb10
-rw-r--r--lib/rubygems/request_set.rb2
-rw-r--r--lib/rubygems/requirement.rb9
-rw-r--r--lib/rubygems/resolver.rb10
-rw-r--r--lib/rubygems/resolver/activation_request.rb2
-rw-r--r--lib/rubygems/resolver/api_set.rb21
-rw-r--r--lib/rubygems/resolver/best_set.rb28
-rw-r--r--lib/rubygems/resolver/molinillo.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb57
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb6
-rw-r--r--lib/rubygems/resolver/spec_specification.rb7
-rw-r--r--lib/rubygems/s3_uri_signer.rb6
-rw-r--r--lib/rubygems/safe_yaml.rb11
-rw-r--r--lib/rubygems/security.rb2
-rw-r--r--lib/rubygems/source.rb18
-rw-r--r--lib/rubygems/source/git.rb16
-rw-r--r--lib/rubygems/source/installed.rb4
-rw-r--r--lib/rubygems/source/local.rb12
-rw-r--r--lib/rubygems/source/specific_file.rb8
-rw-r--r--lib/rubygems/source_list.rb2
-rw-r--r--lib/rubygems/spec_fetcher.rb15
-rw-r--r--lib/rubygems/specification.rb197
-rw-r--r--lib/rubygems/specification_policy.rb46
-rw-r--r--lib/rubygems/specification_record.rb212
-rw-r--r--lib/rubygems/stub_specification.rb42
-rw-r--r--lib/rubygems/timeout.rb3
-rw-r--r--lib/rubygems/tsort.rb3
-rw-r--r--lib/rubygems/uninstaller.rb63
-rw-r--r--lib/rubygems/uri.rb12
-rw-r--r--lib/rubygems/util.rb2
-rw-r--r--lib/rubygems/util/licenses.rb68
-rw-r--r--lib/rubygems/vendor/molinillo/.document (renamed from lib/rubygems/net-protocol/.document)0
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo.rb)4
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb57
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb)22
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb)4
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/action.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/log.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/errors.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/gem_metadata.rb6
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/modules/specification_provider.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb)4
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/modules/ui.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb)8
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/resolver.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb)2
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/state.rb (renamed from lib/rubygems/resolver/molinillo/lib/molinillo/state.rb)2
-rw-r--r--lib/rubygems/vendor/net-http/.document (renamed from lib/rubygems/optparse/.document)0
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http.rb (renamed from lib/rubygems/net-http/lib/net/http.rb)32
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/backward.rb (renamed from lib/rubygems/net-http/lib/net/http/backward.rb)0
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb (renamed from lib/rubygems/net-http/lib/net/http/exceptions.rb)0
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb (renamed from lib/rubygems/net-http/lib/net/http/generic_request.rb)18
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/header.rb (renamed from lib/rubygems/net-http/lib/net/http/header.rb)4
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/proxy_delta.rb (renamed from lib/rubygems/net-http/lib/net/http/proxy_delta.rb)0
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/request.rb (renamed from lib/rubygems/net-http/lib/net/http/request.rb)6
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/requests.rb (renamed from lib/rubygems/net-http/lib/net/http/requests.rb)60
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/response.rb (renamed from lib/rubygems/net-http/lib/net/http/response.rb)4
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/responses.rb (renamed from lib/rubygems/net-http/lib/net/http/responses.rb)12
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/status.rb (renamed from lib/rubygems/net-http/lib/net/http/status.rb)2
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/https.rb (renamed from lib/rubygems/net-http/lib/net/https.rb)2
-rw-r--r--lib/rubygems/vendor/net-protocol/.document (renamed from lib/rubygems/resolv/.document)0
-rw-r--r--lib/rubygems/vendor/net-protocol/lib/net/protocol.rb (renamed from lib/rubygems/net-protocol/lib/net/protocol.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/.document (renamed from lib/rubygems/resolver/molinillo/.document)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optionparser.rb (renamed from lib/rubygems/optparse/lib/optionparser.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse.rb (renamed from lib/rubygems/optparse/lib/optparse.rb)22
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/ac.rb (renamed from lib/rubygems/optparse/lib/optparse/ac.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/date.rb (renamed from lib/rubygems/optparse/lib/optparse/date.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/kwargs.rb (renamed from lib/rubygems/optparse/lib/optparse/kwargs.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/shellwords.rb (renamed from lib/rubygems/optparse/lib/optparse/shellwords.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/time.rb (renamed from lib/rubygems/optparse/lib/optparse/time.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/uri.rb7
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/version.rb (renamed from lib/rubygems/optparse/lib/optparse/version.rb)0
-rw-r--r--lib/rubygems/vendor/resolv/.document (renamed from lib/rubygems/timeout/.document)0
-rw-r--r--lib/rubygems/vendor/resolv/lib/resolv.rb (renamed from lib/rubygems/resolv/lib/resolv.rb)87
-rw-r--r--lib/rubygems/vendor/securerandom/.document (renamed from lib/rubygems/tsort/.document)0
-rw-r--r--lib/rubygems/vendor/securerandom/lib/random/formatter.rb373
-rw-r--r--lib/rubygems/vendor/securerandom/lib/securerandom.rb96
-rw-r--r--lib/rubygems/vendor/timeout/.document1
-rw-r--r--lib/rubygems/vendor/timeout/lib/timeout.rb (renamed from lib/rubygems/timeout/lib/timeout.rb)2
-rw-r--r--lib/rubygems/vendor/tsort/.document1
-rw-r--r--lib/rubygems/vendor/tsort/lib/tsort.rb (renamed from lib/rubygems/tsort/lib/tsort.rb)4
-rw-r--r--lib/rubygems/vendor/uri/.document1
-rw-r--r--lib/rubygems/vendor/uri/lib/uri.rb104
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/common.rb855
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/file.rb100
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/ftp.rb267
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/generic.rb1588
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/http.rb125
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/https.rb23
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/ldap.rb261
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/ldaps.rb22
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/mailto.rb293
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/rfc2396_parser.rb539
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/rfc3986_parser.rb183
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/version.rb6
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/ws.rb83
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/wss.rb23
-rw-r--r--lib/rubygems/vendored_molinillo.rb3
-rw-r--r--lib/rubygems/vendored_net_http.rb5
-rw-r--r--lib/rubygems/vendored_optparse.rb3
-rw-r--r--lib/rubygems/vendored_securerandom.rb4
-rw-r--r--lib/rubygems/vendored_timeout.rb5
-rw-r--r--lib/rubygems/vendored_tsort.rb3
-rw-r--r--lib/rubygems/yaml_serializer.rb19
-rw-r--r--lib/syntax_suggest/around_block_scan.rb4
-rw-r--r--lib/syntax_suggest/block_expand.rb2
-rw-r--r--lib/syntax_suggest/capture_code_context.rb2
-rw-r--r--lib/syntax_suggest/clean_document.rb2
-rw-r--r--lib/syntax_suggest/lex_value.rb2
-rw-r--r--lib/syntax_suggest/parse_blocks_from_indent_line.rb2
-rw-r--r--lib/syntax_suggest/version.rb2
-rw-r--r--lib/uri/common.rb2
-rw-r--r--lib/uri/generic.rb15
-rw-r--r--lib/uri/version.rb2
-rwxr-xr-xlibexec/syntax_suggest2
-rw-r--r--load.c2
-rw-r--r--main.c6
-rw-r--r--marshal.c109
-rw-r--r--numeric.c41
-rw-r--r--object.c33
-rw-r--r--parse.y119
-rw-r--r--prism/prism.c5
-rw-r--r--prism_compile.c2
-rw-r--r--proc.c162
-rw-r--r--process.c1
-rw-r--r--ractor.c16
-rw-r--r--ractor_core.h2
-rw-r--r--random.c88
-rw-r--r--range.c12
-rw-r--r--re.c26
-rw-r--r--regcomp.c2
-rw-r--r--regenc.c2
-rw-r--r--regenc.h5
-rw-r--r--regexec.c367
-rw-r--r--regint.h16
-rw-r--r--regparse.c3
-rw-r--r--rjit_c.rb4
-rw-r--r--ruby.c255
-rw-r--r--ruby_parser.c2
-rw-r--r--rubyparser.h2
-rw-r--r--scheduler.c28
-rw-r--r--shape.c26
-rw-r--r--shape.h1
-rw-r--r--signal.c3
-rw-r--r--siphash.c3
-rw-r--r--spec/bundler/bundler/bundler_spec.rb11
-rw-r--r--spec/bundler/bundler/cli_spec.rb24
-rw-r--r--spec/bundler/bundler/compact_index_client/parser_spec.rb237
-rw-r--r--spec/bundler/bundler/definition_spec.rb77
-rw-r--r--spec/bundler/bundler/digest_spec.rb2
-rw-r--r--spec/bundler/bundler/dsl_spec.rb70
-rw-r--r--spec/bundler/bundler/endpoint_specification_spec.rb2
-rw-r--r--spec/bundler/bundler/env_spec.rb28
-rw-r--r--spec/bundler/bundler/environment_preserver_spec.rb16
-rw-r--r--spec/bundler/bundler/fetcher/base_spec.rb6
-rw-r--r--spec/bundler/bundler/fetcher/compact_index_spec.rb13
-rw-r--r--spec/bundler/bundler/fetcher/dependency_spec.rb4
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb28
-rw-r--r--spec/bundler/bundler/fetcher/index_spec.rb2
-rw-r--r--spec/bundler/bundler/fetcher_spec.rb4
-rw-r--r--spec/bundler/bundler/friendly_errors_spec.rb4
-rw-r--r--spec/bundler/bundler/gem_helper_spec.rb40
-rw-r--r--spec/bundler/bundler/gem_version_promoter_spec.rb32
-rw-r--r--spec/bundler/bundler/installer/gem_installer_spec.rb7
-rw-r--r--spec/bundler/bundler/mirror_spec.rb16
-rw-r--r--spec/bundler/bundler/plugin/index_spec.rb2
-rw-r--r--spec/bundler/bundler/plugin/installer_spec.rb8
-rw-r--r--spec/bundler/bundler/plugin_spec.rb4
-rw-r--r--spec/bundler/bundler/resolver/candidate_spec.rb19
-rw-r--r--spec/bundler/bundler/retry_spec.rb2
-rw-r--r--spec/bundler/bundler/ruby_dsl_spec.rb4
-rw-r--r--spec/bundler/bundler/rubygems_integration_spec.rb10
-rw-r--r--spec/bundler/bundler/settings_spec.rb37
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb14
-rw-r--r--spec/bundler/bundler/source/rubygems/remote_spec.rb20
-rw-r--r--spec/bundler/bundler/source_list_spec.rb4
-rw-r--r--spec/bundler/bundler/stub_specification_spec.rb20
-rw-r--r--spec/bundler/bundler/ui/shell_spec.rb26
-rw-r--r--spec/bundler/bundler/uri_credentials_filter_spec.rb10
-rw-r--r--spec/bundler/bundler/yaml_serializer_spec.rb15
-rw-r--r--spec/bundler/cache/cache_path_spec.rb12
-rw-r--r--spec/bundler/cache/gems_spec.rb296
-rw-r--r--spec/bundler/cache/git_spec.rb201
-rw-r--r--spec/bundler/cache/path_spec.rb24
-rw-r--r--spec/bundler/cache/platform_spec.rb16
-rw-r--r--spec/bundler/commands/add_spec.rb114
-rw-r--r--spec/bundler/commands/binstubs_spec.rb241
-rw-r--r--spec/bundler/commands/cache_spec.rb213
-rw-r--r--spec/bundler/commands/check_spec.rb187
-rw-r--r--spec/bundler/commands/clean_spec.rb220
-rw-r--r--spec/bundler/commands/config_spec.rb50
-rw-r--r--spec/bundler/commands/console_spec.rb20
-rw-r--r--spec/bundler/commands/doctor_spec.rb10
-rw-r--r--spec/bundler/commands/exec_spec.rb282
-rw-r--r--spec/bundler/commands/fund_spec.rb50
-rw-r--r--spec/bundler/commands/help_spec.rb7
-rw-r--r--spec/bundler/commands/info_spec.rb26
-rw-r--r--spec/bundler/commands/init_spec.rb16
-rw-r--r--spec/bundler/commands/inject_spec.rb54
-rw-r--r--spec/bundler/commands/install_spec.rb598
-rw-r--r--spec/bundler/commands/issue_spec.rb2
-rw-r--r--spec/bundler/commands/licenses_spec.rb4
-rw-r--r--spec/bundler/commands/list_spec.rb48
-rw-r--r--spec/bundler/commands/lock_spec.rb914
-rw-r--r--spec/bundler/commands/newgem_spec.rb287
-rw-r--r--spec/bundler/commands/open_spec.rb11
-rw-r--r--spec/bundler/commands/outdated_spec.rb133
-rw-r--r--spec/bundler/commands/platform_spec.rb378
-rw-r--r--spec/bundler/commands/post_bundle_message_spec.rb22
-rw-r--r--spec/bundler/commands/pristine_spec.rb2
-rw-r--r--spec/bundler/commands/remove_spec.rb334
-rw-r--r--spec/bundler/commands/show_spec.rb16
-rw-r--r--spec/bundler/commands/update_spec.rb685
-rw-r--r--spec/bundler/commands/viz_spec.rb60
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb26
-rw-r--r--spec/bundler/install/binstubs_spec.rb20
-rw-r--r--spec/bundler/install/bundler_spec.rb48
-rw-r--r--spec/bundler/install/deploy_spec.rb230
-rw-r--r--spec/bundler/install/failure_spec.rb4
-rw-r--r--spec/bundler/install/gemfile/eval_gemfile_spec.rb34
-rw-r--r--spec/bundler/install/gemfile/force_ruby_platform_spec.rb70
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb216
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb442
-rw-r--r--spec/bundler/install/gemfile/groups_spec.rb70
-rw-r--r--spec/bundler/install/gemfile/install_if_spec.rb22
-rw-r--r--spec/bundler/install/gemfile/lockfile_spec.rb8
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb204
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb474
-rw-r--r--spec/bundler/install/gemfile/ruby_spec.rb68
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb538
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb629
-rw-r--r--spec/bundler/install/gemfile_spec.rb59
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb265
-rw-r--r--spec/bundler/install/gems/dependency_api_fallback_spec.rb53
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb160
-rw-r--r--spec/bundler/install/gems/env_spec.rb36
-rw-r--r--spec/bundler/install/gems/flex_spec.rb192
-rw-r--r--spec/bundler/install/gems/fund_spec.rb28
-rw-r--r--spec/bundler/install/gems/mirror_spec.rb22
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb14
-rw-r--r--spec/bundler/install/gems/post_install_spec.rb44
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb187
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb111
-rw-r--r--spec/bundler/install/gems/win32_spec.rb10
-rw-r--r--spec/bundler/install/gemspecs_spec.rb48
-rw-r--r--spec/bundler/install/git_spec.rb61
-rw-r--r--spec/bundler/install/global_cache_spec.rb120
-rw-r--r--spec/bundler/install/path_spec.rb62
-rw-r--r--spec/bundler/install/prereleases_spec.rb16
-rw-r--r--spec/bundler/install/process_lock_spec.rb8
-rw-r--r--spec/bundler/install/redownload_spec.rb20
-rw-r--r--spec/bundler/install/security_policy_spec.rb8
-rw-r--r--spec/bundler/install/yanked_spec.rb63
-rw-r--r--spec/bundler/lock/git_spec.rb14
-rw-r--r--spec/bundler/lock/lockfile_spec.rb747
-rw-r--r--spec/bundler/other/ext_spec.rb4
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb110
-rw-r--r--spec/bundler/plugins/command_spec.rb10
-rw-r--r--spec/bundler/plugins/hook_spec.rb34
-rw-r--r--spec/bundler/plugins/install_spec.rb141
-rw-r--r--spec/bundler/plugins/list_spec.rb4
-rw-r--r--spec/bundler/plugins/source/example_spec.rb30
-rw-r--r--spec/bundler/plugins/source_spec.rb16
-rw-r--r--spec/bundler/plugins/uninstall_spec.rb8
-rw-r--r--spec/bundler/quality_spec.rb15
-rw-r--r--spec/bundler/realworld/dependency_api_spec.rb46
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb15
-rw-r--r--spec/bundler/realworld/fixtures/warbler/Gemfile2
-rw-r--r--spec/bundler/realworld/fixtures/warbler/Gemfile.lock6
-rw-r--r--spec/bundler/realworld/gemfile_source_header_spec.rb14
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb14
-rw-r--r--spec/bundler/realworld/slow_perf_spec.rb18
-rw-r--r--spec/bundler/resolver/basic_spec.rb38
-rw-r--r--spec/bundler/runtime/env_helpers_spec.rb (renamed from spec/bundler/runtime/with_unbundled_env_spec.rb)27
-rw-r--r--spec/bundler/runtime/executable_spec.rb84
-rw-r--r--spec/bundler/runtime/gem_tasks_spec.rb67
-rw-r--r--spec/bundler/runtime/inline_spec.rb271
-rw-r--r--spec/bundler/runtime/load_spec.rb26
-rw-r--r--spec/bundler/runtime/platform_spec.rb115
-rw-r--r--spec/bundler/runtime/require_spec.rb68
-rw-r--r--spec/bundler/runtime/self_management_spec.rb84
-rw-r--r--spec/bundler/runtime/setup_spec.rb434
-rw-r--r--spec/bundler/spec_helper.rb7
-rw-r--r--spec/bundler/support/activate.rb2
-rw-r--r--spec/bundler/support/artifice/fail.rb2
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index.rb7
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint.rb2
-rw-r--r--spec/bundler/support/artifice/helpers/rack_request.rb2
-rw-r--r--spec/bundler/support/artifice/vcr.rb2
-rw-r--r--spec/bundler/support/build_metadata.rb2
-rw-r--r--spec/bundler/support/builders.rb107
-rw-r--r--spec/bundler/support/checksums.rb18
-rw-r--r--spec/bundler/support/command_execution.rb48
-rw-r--r--spec/bundler/support/env.rb9
-rw-r--r--spec/bundler/support/filters.rb2
-rw-r--r--spec/bundler/support/hax.rb14
-rw-r--r--spec/bundler/support/helpers.rb244
-rw-r--r--spec/bundler/support/indexes.rb14
-rw-r--r--spec/bundler/support/matchers.rb30
-rw-r--r--spec/bundler/support/options.rb15
-rw-r--r--spec/bundler/support/path.rb50
-rw-r--r--spec/bundler/support/rubygems_ext.rb27
-rw-r--r--spec/bundler/support/rubygems_version_manager.rb35
-rw-r--r--spec/bundler/support/subprocess.rb108
-rw-r--r--spec/bundler/support/vendored_net_http.rb23
-rw-r--r--spec/bundler/update/gemfile_spec.rb14
-rw-r--r--spec/bundler/update/gems/fund_spec.rb4
-rw-r--r--spec/bundler/update/gems/post_install_spec.rb20
-rw-r--r--spec/bundler/update/git_spec.rb84
-rw-r--r--spec/bundler/update/path_spec.rb2
-rw-r--r--spec/bundler/update/redownload_spec.rb22
-rw-r--r--spec/ruby/core/binding/clone_spec.rb6
-rw-r--r--spec/ruby/core/binding/dup_spec.rb6
-rw-r--r--spec/ruby/core/binding/shared/clone.rb22
-rw-r--r--spec/ruby/core/fiber/raise_spec.rb34
-rw-r--r--spec/ruby/core/file/atime_spec.rb3
-rw-r--r--spec/ruby/core/file/ctime_spec.rb3
-rw-r--r--spec/ruby/core/file/mtime_spec.rb3
-rw-r--r--spec/ruby/core/kernel/shared/require.rb4
-rw-r--r--spec/ruby/core/marshal/dump_spec.rb27
-rw-r--r--spec/ruby/core/method/clone_spec.rb15
-rw-r--r--spec/ruby/core/method/dup_spec.rb15
-rw-r--r--spec/ruby/core/method/shared/dup.rb32
-rw-r--r--spec/ruby/core/module/set_temporary_name_spec.rb45
-rw-r--r--spec/ruby/core/proc/clone_spec.rb9
-rw-r--r--spec/ruby/core/proc/dup_spec.rb7
-rw-r--r--spec/ruby/core/proc/shared/dup.rb23
-rw-r--r--spec/ruby/core/unboundmethod/clone_spec.rb13
-rw-r--r--spec/ruby/core/unboundmethod/dup_spec.rb15
-rw-r--r--spec/ruby/core/unboundmethod/shared/dup.rb32
-rw-r--r--spec/ruby/language/hash_spec.rb28
-rw-r--r--spec/ruby/language/predefined_spec.rb2
-rw-r--r--spec/ruby/language/symbol_spec.rb14
-rw-r--r--spec/ruby/library/shellwords/shellwords_spec.rb15
-rw-r--r--spec/ruby/library/socket/socket/gethostname_spec.rb10
-rw-r--r--spec/syntax_suggest/unit/block_expand_spec.rb4
-rw-r--r--spec/syntax_suggest/unit/clean_document_spec.rb6
-rw-r--r--sprintf.c5
-rw-r--r--st.c32
-rw-r--r--string.c187
-rw-r--r--struct.c20
-rw-r--r--symbol.c18
-rw-r--r--template/Makefile.in3
-rw-r--r--template/extinit.c.tmpl2
-rw-r--r--template/id.c.tmpl3
-rw-r--r--template/prelude.c.tmpl5
-rw-r--r--test/-ext-/bug_reporter/test_bug_reporter.rb1
-rw-r--r--test/-ext-/debug/test_debug.rb15
-rw-r--r--test/-ext-/string/test_capacity.rb2
-rw-r--r--test/-ext-/string/test_fstring.rb8
-rw-r--r--test/cgi/test_cgi_session.rb2
-rw-r--r--test/cgi/test_cgi_util.rb18
-rw-r--r--test/coverage/test_coverage.rb17
-rw-r--r--test/date/test_date.rb4
-rw-r--r--test/fiber/test_enumerator.rb8
-rw-r--r--test/fiber/test_io.rb43
-rw-r--r--test/fiber/test_scheduler.rb41
-rw-r--r--test/irb/command/test_custom_command.rb149
-rw-r--r--test/irb/command/test_force_exit.rb51
-rw-r--r--test/irb/command/test_help.rb75
-rw-r--r--test/irb/command/test_multi_irb_commands.rb50
-rw-r--r--test/irb/command/test_show_source.rb (renamed from test/irb/cmd/test_show_source.rb)121
-rw-r--r--test/irb/helper.rb8
-rw-r--r--test/irb/test_color.rb3
-rw-r--r--test/irb/test_color_printer.rb1
-rw-r--r--test/irb/test_command.rb (renamed from test/irb/test_cmd.rb)186
-rw-r--r--test/irb/test_completion.rb19
-rw-r--r--test/irb/test_context.rb257
-rw-r--r--test/irb/test_debugger_integration.rb (renamed from test/irb/test_debug_cmd.rb)76
-rw-r--r--test/irb/test_eval_history.rb4
-rw-r--r--test/irb/test_helper_method.rb134
-rw-r--r--test/irb/test_history.rb233
-rw-r--r--test/irb/test_init.rb97
-rw-r--r--test/irb/test_input_method.rb18
-rw-r--r--test/irb/test_irb.rb176
-rw-r--r--test/irb/test_nesting_parser.rb38
-rw-r--r--test/irb/test_raise_exception.rb2
-rw-r--r--test/irb/test_ruby_lex.rb4
-rw-r--r--test/irb/test_tracer.rb90
-rw-r--r--test/irb/test_type_completor.rb7
-rw-r--r--test/irb/test_workspace.rb3
-rw-r--r--test/irb/yamatanooroti/test_rendering.rb81
-rw-r--r--test/json/json_addition_test.rb2
-rw-r--r--test/json/json_generic_object_test.rb2
-rw-r--r--test/json/json_parser_test.rb71
-rw-r--r--test/net/http/test_http.rb12
-rw-r--r--test/net/http/test_https.rb7
-rw-r--r--test/objspace/test_objspace.rb6
-rw-r--r--test/openssl/test_asn1.rb8
-rw-r--r--test/openssl/test_provider.rb2
-rw-r--r--test/openssl/test_x509req.rb7
-rw-r--r--test/prism/snapshots/not.txt6
-rw-r--r--test/prism/snapshots/seattlerb/bug_not_parens.txt2
-rw-r--r--test/prism/snapshots/whitequark/not.txt12
-rw-r--r--test/reline/helper.rb55
-rw-r--r--test/reline/test_ansi_with_terminfo.rb6
-rw-r--r--test/reline/test_ansi_without_terminfo.rb4
-rw-r--r--test/reline/test_config.rb239
-rw-r--r--test/reline/test_key_actor_emacs.rb2044
-rw-r--r--test/reline/test_key_actor_vi.rb1346
-rw-r--r--test/reline/test_key_stroke.rb56
-rw-r--r--test/reline/test_line_editor.rb186
-rw-r--r--test/reline/test_macro.rb1
-rw-r--r--test/reline/test_reline.rb62
-rw-r--r--test/reline/test_reline_key.rb51
-rw-r--r--test/reline/test_string_processing.rb12
-rw-r--r--test/reline/test_terminfo.rb2
-rw-r--r--test/reline/test_unicode.rb31
-rw-r--r--test/reline/test_within_pipe.rb1
-rwxr-xr-xtest/reline/yamatanooroti/multiline_repl19
-rw-r--r--test/reline/yamatanooroti/test_rendering.rb325
-rw-r--r--test/resolv/test_dns.rb7
-rw-r--r--test/ripper/test_lexer.rb81
-rw-r--r--test/ripper/test_scanner_events.rb2
-rw-r--r--test/ruby/test_array.rb10
-rw-r--r--test/ruby/test_assignment.rb10
-rw-r--r--test/ruby/test_bignum.rb6
-rw-r--r--test/ruby/test_call.rb12
-rw-r--r--test/ruby/test_clone.rb7
-rw-r--r--test/ruby/test_complex.rb40
-rw-r--r--test/ruby/test_data.rb6
-rw-r--r--test/ruby/test_enumerator.rb17
-rw-r--r--test/ruby/test_env.rb16
-rw-r--r--test/ruby/test_exception.rb9
-rw-r--r--test/ruby/test_float.rb14
-rw-r--r--test/ruby/test_hash.rb55
-rw-r--r--test/ruby/test_integer.rb12
-rw-r--r--test/ruby/test_io.rb92
-rw-r--r--test/ruby/test_io_buffer.rb37
-rw-r--r--test/ruby/test_iseq.rb34
-rw-r--r--test/ruby/test_keyword.rb18
-rw-r--r--test/ruby/test_m17n.rb17
-rw-r--r--test/ruby/test_marshal.rb21
-rw-r--r--test/ruby/test_method.rb30
-rw-r--r--test/ruby/test_module.rb67
-rw-r--r--test/ruby/test_optimization.rb11
-rw-r--r--test/ruby/test_parse.rb17
-rw-r--r--test/ruby/test_proc.rb30
-rw-r--r--test/ruby/test_process.rb57
-rw-r--r--test/ruby/test_range.rb2
-rw-r--r--test/ruby/test_refinement.rb17
-rw-r--r--test/ruby/test_regexp.rb79
-rw-r--r--test/ruby/test_require.rb20
-rw-r--r--test/ruby/test_rubyoptions.rb40
-rw-r--r--test/ruby/test_settracefunc.rb77
-rw-r--r--test/ruby/test_sprintf.rb12
-rw-r--r--test/ruby/test_string.rb58
-rw-r--r--test/ruby/test_struct.rb20
-rw-r--r--test/ruby/test_super.rb41
-rw-r--r--test/ruby/test_symbol.rb1
-rw-r--r--test/ruby/test_syntax.rb40
-rw-r--r--test/ruby/test_time_tz.rb8
-rw-r--r--test/ruby/test_variable.rb22
-rw-r--r--test/ruby/test_weakkeymap.rb15
-rw-r--r--test/ruby/test_weakmap.rb8
-rw-r--r--test/ruby/test_whileuntil.rb18
-rw-r--r--test/ruby/test_yjit.rb22
-rw-r--r--test/rubygems/helper.rb70
-rw-r--r--test/rubygems/installer_test_case.rb6
-rw-r--r--test/rubygems/rubygems/commands/ins_command.rb7
-rw-r--r--test/rubygems/rubygems/commands/interrupt_command.rb11
-rw-r--r--test/rubygems/rubygems_plugin.rb19
-rw-r--r--test/rubygems/specifications/rubyforge-0.0.1.gemspec21
-rw-r--r--test/rubygems/test_bundled_ca.rb4
-rw-r--r--test/rubygems/test_gem.rb52
-rw-r--r--test/rubygems/test_gem_bundler_version_finder.rb2
-rw-r--r--test/rubygems/test_gem_ci_detector.rb14
-rw-r--r--test/rubygems/test_gem_command_manager.rb9
-rw-r--r--test/rubygems/test_gem_commands_build_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb6
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_exec_command.rb21
-rw-r--r--test/rubygems/test_gem_commands_fetch_command.rb82
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_list_command.rb29
-rw-r--r--test/rubygems/test_gem_commands_lock_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb27
-rw-r--r--test/rubygems/test_gem_commands_push_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_rebuild_command.rb145
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb25
-rw-r--r--test/rubygems/test_gem_commands_signin_command.rb75
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb57
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb12
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb2
-rw-r--r--test/rubygems/test_gem_config_file.rb91
-rw-r--r--test/rubygems/test_gem_dependency.rb12
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb50
-rw-r--r--test/rubygems/test_gem_ext_builder.rb16
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder.rb4
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock12
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock12
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml2
-rw-r--r--test/rubygems/test_gem_ext_cmake_builder.rb2
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb2
-rw-r--r--test/rubygems/test_gem_installer.rb71
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb12
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb25
-rw-r--r--test/rubygems/test_gem_package_task.rb4
-rw-r--r--test/rubygems/test_gem_platform.rb70
-rw-r--r--test/rubygems/test_gem_rdoc.rb2
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb652
-rw-r--r--test/rubygems/test_gem_remote_fetcher_local_server.rb220
-rw-r--r--test/rubygems/test_gem_remote_fetcher_local_ssl_server.rb195
-rw-r--r--test/rubygems/test_gem_remote_fetcher_s3.rb233
-rw-r--r--test/rubygems/test_gem_request.rb32
-rw-r--r--test/rubygems/test_gem_request_connection_pools.rb16
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb2
-rw-r--r--test/rubygems/test_gem_requirement.rb8
-rw-r--r--test/rubygems/test_gem_resolver.rb4
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb37
-rw-r--r--test/rubygems/test_gem_resolver_api_specification.rb4
-rw-r--r--test/rubygems/test_gem_resolver_best_set.rb82
-rw-r--r--test/rubygems/test_gem_safe_marshal.rb31
-rw-r--r--test/rubygems/test_gem_safe_yaml.rb24
-rw-r--r--test/rubygems/test_gem_source.rb14
-rw-r--r--test/rubygems/test_gem_source_git.rb8
-rw-r--r--test/rubygems/test_gem_source_installed.rb5
-rw-r--r--test/rubygems/test_gem_source_list.rb4
-rw-r--r--test/rubygems/test_gem_source_local.rb5
-rw-r--r--test/rubygems/test_gem_source_lock.rb2
-rw-r--r--test/rubygems/test_gem_source_specific_file.rb4
-rw-r--r--test/rubygems/test_gem_source_subpath_problem.rb4
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb18
-rw-r--r--test/rubygems/test_gem_specification.rb223
-rw-r--r--test/rubygems/test_gem_stream_ui.rb2
-rw-r--r--test/rubygems/test_gem_stub_specification.rb95
-rw-r--r--test/rubygems/test_gem_uninstaller.rb99
-rw-r--r--test/rubygems/test_kernel.rb16
-rw-r--r--test/rubygems/test_require.rb56
-rw-r--r--test/rubygems/test_rubygems.rb2
-rw-r--r--test/rubygems/test_webauthn_listener.rb20
-rw-r--r--test/rubygems/test_webauthn_poller.rb12
-rw-r--r--test/rubygems/utilities.rb12
-rw-r--r--test/socket/test_tcp.rb164
-rw-r--r--test/strscan/test_stringscanner.rb4
-rw-r--r--test/test_bundled_gems.rb35
-rw-r--r--test/uri/test_generic.rb18
-rw-r--r--test/zlib/test_zlib.rb32
-rw-r--r--thread.c68
-rw-r--r--thread_none.c2
-rw-r--r--thread_pthread.c6
-rw-r--r--thread_pthread.h4
-rw-r--r--thread_sync.c60
-rw-r--r--thread_win32.c2
-rw-r--r--time.c76
-rw-r--r--tool/bundler/dev_gems.rb4
-rw-r--r--tool/bundler/test_gems.rb9
-rw-r--r--tool/downloader.rb3
-rw-r--r--tool/lib/bundled_gem.rb12
-rw-r--r--tool/lib/core_assertions.rb12
-rw-r--r--tool/lib/envutil.rb1
-rw-r--r--tool/lib/test/unit/testcase.rb3
-rw-r--r--tool/m4/ruby_check_header.m48
-rw-r--r--tool/m4/ruby_default_arch.m412
-rw-r--r--tool/m4/ruby_try_cflags.m413
-rw-r--r--tool/m4/ruby_universal_arch.m42
-rwxr-xr-xtool/make-snapshot2
-rwxr-xr-xtool/merger.rb183
-rwxr-xr-xtool/rbinstall.rb109
-rw-r--r--tool/rbs_skip_tests2
-rwxr-xr-xtool/redmine-backporter.rb133
-rwxr-xr-xtool/test_for_warn_bundled_gems/test.sh32
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_bootsnap.rb (renamed from tool/test_for_warn_bundled_gems/test_no_warn_bootsnap.rb)0
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_bootsnap_rubyarchdir_gem.rb11
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_redefined.rb18
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_zeitwerk.rb12
-rw-r--r--universal_parser.c2
-rw-r--r--variable.c183
-rw-r--r--version.c3
-rw-r--r--version.h4
-rw-r--r--vm.c52
-rw-r--r--vm_args.c21
-rw-r--r--vm_callinfo.h15
-rw-r--r--vm_core.h4
-rw-r--r--vm_exec.h4
-rw-r--r--vm_insnhelper.c44
-rw-r--r--vm_method.c123
-rw-r--r--vm_trace.c4
-rw-r--r--weakmap.c274
-rw-r--r--win32/Makefile.sub16
-rwxr-xr-xwin32/mkexports.rb1
-rw-r--r--win32/win32.c21
-rw-r--r--win32/winmain.c4
-rw-r--r--yjit.c15
-rw-r--r--yjit/bindgen/Cargo.lock4
-rw-r--r--yjit/bindgen/src/main.rs6
-rw-r--r--yjit/src/codegen.rs159
-rw-r--r--yjit/src/core.rs39
-rw-r--r--yjit/src/cruby.rs5
-rw-r--r--yjit/src/cruby_bindings.inc.rs10
-rw-r--r--yjit/src/lib.rs3
-rw-r--r--yjit/src/stats.rs6
-rw-r--r--yjit/src/yjit.rs7
972 files changed, 33781 insertions, 19525 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 0a25dceab4..0000000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,133 +0,0 @@
----
-version: '{build}'
-init:
- - git config --global user.name git
- - git config --global user.email svn-admin@ruby-lang.org
- - git config --global core.autocrlf false
- - git config --global core.eol lf
- - git config --global advice.detachedHead 0
-shallow_clone: true
-clone_depth: 10
-platform:
- - x64
-skip_commits:
- message: /\[DOC\]/
- files:
- - doc/*
- - '**/*.md'
- - '**/*.rdoc'
- - '**/.document'
- - '**/*.[1-8]'
- - '**/*.ronn'
-environment:
- ruby_version: "25-%Platform%"
- matrix:
- # Test only the oldest supported version because AppVeyor is unstable, its concurrency
- # is limited, and compatibility issues that happen only in newer versions are rare.
- # You may test some other stuff on GitHub Actions instead.
- - build: vs
- vs: 120 # Visual Studio 2013
- ssl: OpenSSL-v111
- # The worker image name. This is NOT the Visual Studio version we're using here.
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- GEMS_FOR_TEST: ""
- RELINE_TEST_ENCODING: "UTF-8"
-cache:
- - c:\Tools\vcpkg\installed\
-for:
--
- matrix:
- only:
- - build: vs
- install:
- - ver
- - chcp
- - SET BITS=%Platform:x86=32%
- - SET BITS=%BITS:x=%
- - SET OPENSSL_DIR=C:\%ssl%-Win%BITS%
- - cd C:\Tools\vcpkg
- - git pull -q
- - .\bootstrap-vcpkg.bat
- - cd %APPVEYOR_BUILD_FOLDER%
- - vcpkg --triplet %Platform%-windows install --x-use-aria2 libffi libyaml readline zlib
- - CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat
- - SET vcvars
- - '"%vcvars%" %Platform:x64=amd64%'
- - SET ruby_path=C:\Ruby%ruby_version:-x86=%
- - SET PATH=\usr\local\bin;%ruby_path%\bin;%PATH%;C:\msys64\mingw64\bin;C:\msys64\usr\bin
- - ruby --version
- - 'cl'
- - echo> Makefile srcdir=.
- - echo>> Makefile MSC_VER=0
- - echo>> Makefile RT=none
- - echo>> Makefile RT_VER=0
- - echo>> Makefile BUILTIN_ENCOBJS=nul
- - type win32\Makefile.sub >> Makefile
- - nmake %mflags% up VCSUP="echo Update OK"
- - nmake %mflags% extract-extlibs
- - del Makefile
- - mkdir \usr\local\bin
- - mkdir \usr\local\include
- - mkdir \usr\local\lib
- - for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
- - for %%I in (c:\Tools\vcpkg\installed\%Platform%-windows\bin\*.dll) do (
- if not %%~nI == readline mklink \usr\local\bin\%%~nxI %%I
- )
- - attrib +r /s /d
- - mkdir %Platform%-mswin_%vs%
- build_script:
- - set HAVE_GIT=no
- - cd %APPVEYOR_BUILD_FOLDER%
- - cd %Platform%-mswin_%vs%
- - >-
- ..\win32\configure.bat
- --with-opt-dir="/usr/local;c:/Tools/vcpkg/installed/%Platform%-windows"
- --with-openssl-dir=%OPENSSL_DIR:\=/%
- - nmake -l
- - nmake install-nodoc
- - \usr\bin\ruby -v -e "p :locale => Encoding.find('locale'), :filesystem => Encoding.find('filesystem')"
- - if not "%GEMS_FOR_TEST%" == "" \usr\bin\gem install --no-document %GEMS_FOR_TEST%
- - \usr\bin\ruby -ropenssl -e "puts 'Build ' + OpenSSL::OPENSSL_VERSION, 'Runtime ' + OpenSSL::OPENSSL_LIBRARY_VERSION"
- test_script:
- - set /a JOBS=%NUMBER_OF_PROCESSORS%
- - nmake -l "TESTOPTS=-v -q" btest
- - nmake -l "TESTOPTS=-v -q" test-basic
- - >-
- nmake -l "TESTOPTS=--timeout-scale=3.0
- --excludes=../test/.excludes/_appveyor -j%JOBS%
- --exclude win32ole
- --exclude test_bignum
- --exclude test_syntax
- --exclude test_open-uri
- --exclude test_bundled_ca
- " test-all
- # separately execute tests without -j which may crash worker with -j.
- - >-
- nmake -l
- "TESTOPTS=--timeout-scale=3.0 --excludes=../test/.excludes/_appveyor"
- TESTS="
- ../test/win32ole
- ../test/ruby/test_bignum.rb
- ../test/ruby/test_syntax.rb
- ../test/open-uri/test_open-uri.rb
- ../test/rubygems/test_bundled_ca.rb
- " test-all
- - nmake -l test-spec # not using `-j` because sometimes `mspec -j` silently dies on Windows
-notifications:
- - provider: Webhook
- method: POST
- url:
- secure: CcFlJNDJ/a6to7u3Z4Fnz6dScEPNx7hTha2GkSRlV+1U6dqmxY/7uBcLXYb9gR3jfQk6w+2o/HrjNAyXMNGU/JOka3s2WRI4VKitzM+lQ08owvJIh0R7LxrGH0J2e81U # ruby-lang slack: ruby/simpler-alerts-bot
- body: >-
- {{^isPullRequest}}
- {
- "ci": "AppVeyor CI",
- "env": "Visual Studio 2013",
- "url": "{{buildUrl}}",
- "commit": "{{commitId}}",
- "branch": "{{branch}}"
- }
- {{/isPullRequest}}
- on_build_success: false
- on_build_failure: true
- on_build_status_changed: false
diff --git a/.github/actions/setup/directories/action.yml b/.github/actions/setup/directories/action.yml
index 359e5c0d37..4150288ed7 100644
--- a/.github/actions/setup/directories/action.yml
+++ b/.github/actions/setup/directories/action.yml
@@ -80,7 +80,7 @@ runs:
with:
path: ${{ inputs.srcdir }}
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+ - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
with:
path: ${{ inputs.srcdir }}/.downloaded-cache
key: downloaded-cache
diff --git a/.github/workflows/annocheck.yml b/.github/workflows/annocheck.yml
index 582285afa8..6f10fef9dd 100644
--- a/.github/workflows/annocheck.yml
+++ b/.github/workflows/annocheck.yml
@@ -8,6 +8,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -15,6 +16,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -22,6 +24,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -65,7 +68,7 @@ jobs:
- run: id
working-directory:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
@@ -112,6 +115,7 @@ jobs:
# CHECK_LEAKS: true
- run: make test-annocheck
+ continue-on-error: true # toomanyrequests: You have reached your pull rate limit.
- uses: ./.github/actions/slack
with:
diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml
index 998a6ea58a..ca27244b46 100644
--- a/.github/workflows/auto_request_review.yml
+++ b/.github/workflows/auto_request_review.yml
@@ -13,7 +13,7 @@ jobs:
if: ${{ github.repository == 'ruby/ruby' && github.base_ref == 'master' }}
steps:
- name: Request review based on files changes and/or groups the author belongs to
- uses: necojackarc/auto-request-review@6a51cebffe2c084705d9a7b394abd802e0119633 # v0.12.0
+ uses: necojackarc/auto-request-review@e89da1a8cd7c8c16d9de9c6e763290b6b0e3d424 # v0.13.0
with:
# scope: public_repo
token: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml
index 6102c61ad0..73294b86c0 100644
--- a/.github/workflows/baseruby.yml
+++ b/.github/workflows/baseruby.yml
@@ -8,6 +8,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -15,6 +16,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -22,6 +24,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -34,7 +37,7 @@ jobs:
baseruby:
name: BASERUBY
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: >-
${{!(false
@@ -55,12 +58,12 @@ jobs:
- ruby-3.2
steps:
- - uses: ruby/setup-ruby@036ef458ddccddb148a2b9fb67e95a22fdbf728b # v1.160.0
+ - uses: ruby/setup-ruby@f41e084df884422b269f4c01c3748a9df4431a75 # v1.236.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: ./.github/actions/setup/ubuntu
diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml
index 943140e9ef..a40e204977 100644
--- a/.github/workflows/bundled_gems.yml
+++ b/.github/workflows/bundled_gems.yml
@@ -35,7 +35,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
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 4da0b3696c..8fe60b6d22 100644
--- a/.github/workflows/check_dependencies.yml
+++ b/.github/workflows/check_dependencies.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,6 +15,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -21,6 +23,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -31,9 +34,11 @@ permissions:
jobs:
update-deps:
+ name: Dependency checks
+
strategy:
matrix:
- os: [ubuntu-20.04]
+ os: [ubuntu-latest]
fail-fast: true
runs-on: ${{ matrix.os }}
@@ -47,7 +52,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- 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 ce4397b35a..18b174d47c 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
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 56cfd8abce..2183e046b6 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -9,6 +9,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -16,6 +17,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
schedule:
- cron: '0 12 * * *'
workflow_dispatch:
@@ -54,7 +56,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install libraries
uses: ./.github/actions/setup/ubuntu
@@ -68,6 +70,7 @@ jobs:
uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
with:
languages: ${{ matrix.language }}
+ trap-caching: false
- name: Autobuild
uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
@@ -84,7 +87,7 @@ jobs:
continue-on-error: true
- name: filter-sarif
- uses: advanced-security/filter-sarif@f3b8118a9349d88f7b1c0c488476411145b6270d # v1.0
+ uses: advanced-security/filter-sarif@bc96d9fb9338c5b48cc440b1b4d0a350b26a20db # v1.0.0
with:
patterns: |
+**/*.rb
diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml
index 0262c70ef8..da44e4b21d 100644
--- a/.github/workflows/compilers.yml
+++ b/.github/workflows/compilers.yml
@@ -1,3 +1,4 @@
+# Some tests depending on this name 'Compilations' via $GITHUB_WORKFLOW. Make sure to update such tests when renaming this workflow.
name: Compilations
on:
@@ -8,6 +9,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -15,6 +17,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -22,6 +25,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -71,6 +75,8 @@ jobs:
env:
- {}
entry:
+ - { name: gcc-15, env: { default_cc: gcc-15 } }
+ - { name: gcc-14, env: { default_cc: gcc-14 } }
- { name: gcc-13, env: { default_cc: gcc-13 } }
- { name: gcc-12, env: { default_cc: gcc-12 } }
- { name: gcc-11, env: { default_cc: gcc-11 } }
@@ -139,7 +145,7 @@ jobs:
- { name: '-O0', env: { optflags: '-O0 -march=x86-64 -mtune=generic' } }
# - { name: '-O3', env: { optflags: '-O3 -march=x86-64 -mtune=generic' }, check: true }
- - { name: gmp, env: { append_configure: '--with-gmp' } }
+ - { name: gmp, env: { append_configure: '--with-gmp' }, check: 'ruby/test_bignum.rb' }
- { name: jemalloc, env: { append_configure: '--with-jemalloc' } }
- { name: valgrind, env: { append_configure: '--with-valgrind' } }
- { name: 'coroutine=ucontext', env: { append_configure: '--with-coroutine=ucontext' } }
@@ -234,7 +240,7 @@ jobs:
- run: id
working-directory:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
@@ -271,8 +277,10 @@ jobs:
- run: make test-tool
if: ${{ matrix.entry.check }}
- - run: make test-all TESTS='-- ruby -ext-'
+ - run: make test-all TESTS="-- $tests"
if: ${{ matrix.entry.check }}
+ env:
+ tests: ${{ matrix.entry.check == true && 'ruby -ext-' || matrix.entry.check }}
- run: make test-spec
env:
diff --git a/.github/workflows/dependabot_automerge.yml b/.github/workflows/dependabot_automerge.yml
index 6259199b11..40e39d6924 100644
--- a/.github/workflows/dependabot_automerge.yml
+++ b/.github/workflows/dependabot_automerge.yml
@@ -11,11 +11,11 @@ jobs:
steps:
- name: Dependabot metadata
- uses: dependabot/fetch-metadata@c9c4182bf1b97f5224aee3906fd373f6b61b4526 # v1.6.0
+ uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7 # v2.3.0
id: metadata
- name: Wait for status checks
- uses: lewagon/wait-on-check-action@e106e5c43e8ca1edea6383a39a01c5ca495fd812 # v1.3.1
+ uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc # v1.3.4
with:
repo-token: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
ref: ${{ github.event.pull_request.head.sha || github.sha }}
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 2eba0823ba..ff4bb203fa 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
# Do not use paths-ignore for required status checks
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
@@ -17,6 +18,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -31,10 +33,10 @@ jobs:
matrix:
test_task: ['check']
configure: ['']
- os: ${{ fromJSON(format('["macos-11","macos-12"{0}]', (github.repository == 'ruby/ruby' && ',"macos-arm-oss"' || ''))) }}
+ os: ['macos-13', 'macos-14']
include:
- test_task: test-all TESTS=--repeat-count=2
- os: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-12' }}
+ os: macos-14
fail-fast: false
env:
@@ -51,7 +53,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml
index 597fc44a67..92f24992e0 100644
--- a/.github/workflows/mingw.yml
+++ b/.github/workflows/mingw.yml
@@ -7,6 +7,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,6 +16,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -21,6 +25,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -68,7 +74,7 @@ jobs:
steps:
- name: Set up Ruby & MSYS2
- uses: ruby/setup-ruby@036ef458ddccddb148a2b9fb67e95a22fdbf728b # v1.160.0
+ uses: ruby/setup-ruby@f41e084df884422b269f4c01c3748a9df4431a75 # v1.236.0
with:
ruby-version: ${{ matrix.base_ruby }}
@@ -99,7 +105,7 @@ jobs:
$result
working-directory:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000000..5d4474d978
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,18 @@
+name: Start release workflow
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+ notify:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build release package
+ run: |
+ curl -L -X POST \
+ -H "Authorization: Bearer ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }}" \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ https://api.github.com/repos/ruby/actions/dispatches \
+ -d '{"event_type": "${{ github.ref }}"}'
diff --git a/.github/workflows/rjit-bindgen.yml b/.github/workflows/rjit-bindgen.yml
index 8da2815b06..79b191580f 100644
--- a/.github/workflows/rjit-bindgen.yml
+++ b/.github/workflows/rjit-bindgen.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,6 +15,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -21,6 +23,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -37,7 +40,7 @@ jobs:
- task: rjit-bindgen
fail-fast: false
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: >-
${{!(false
@@ -49,11 +52,11 @@ jobs:
steps:
- name: Set up Ruby
- uses: ruby/setup-ruby@036ef458ddccddb148a2b9fb67e95a22fdbf728b # v1.160.0
+ uses: ruby/setup-ruby@f41e084df884422b269f4c01c3748a9df4431a75 # v1.236.0
with:
ruby-version: '3.1'
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
diff --git a/.github/workflows/rjit.yml b/.github/workflows/rjit.yml
index 53c29959eb..7f2e82996e 100644
--- a/.github/workflows/rjit.yml
+++ b/.github/workflows/rjit.yml
@@ -8,6 +8,7 @@ on:
- '**/.document'
- '**.[1-8]'
- '**.ronn'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -16,6 +17,7 @@ on:
- '**/.document'
- '**.[1-8]'
- '**.ronn'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -24,6 +26,7 @@ on:
- '**/.document'
- '**.[1-8]'
- '**.ronn'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -58,7 +61,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 51ce54a518..54694b0d4d 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -32,12 +32,12 @@ jobs:
steps:
- name: 'Checkout code'
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: 'Run analysis'
- uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
+ uses: ossf/scorecard-action@ea651e62978af7915d09fe2e282747c798bf2dab # v2.4.1
with:
results_file: results.sarif
results_format: sarif
@@ -59,7 +59,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
# - name: "Upload artifact"
- # uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ # uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
# with:
# name: SARIF file
# path: results.sarif
diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml
index e14e7818a6..ecf2264d10 100644
--- a/.github/workflows/spec_guards.yml
+++ b/.github/workflows/spec_guards.yml
@@ -25,7 +25,7 @@ jobs:
rubyspec:
name: Rubyspec
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: >-
${{!(false
@@ -45,9 +45,9 @@ jobs:
- ruby-3.2
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: ruby/setup-ruby@036ef458ddccddb148a2b9fb67e95a22fdbf728b # v1.160.0
+ - uses: ruby/setup-ruby@f41e084df884422b269f4c01c3748a9df4431a75 # v1.236.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index 91f970009e..599cbf1133 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
# Do not use paths-ignore for required status checks
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
@@ -17,6 +18,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -48,7 +50,7 @@ jobs:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUBY_DEBUG: ci
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: >-
${{!(false
@@ -59,7 +61,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index a47a30ca65..dd5493528c 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,6 +15,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -21,6 +23,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -52,7 +55,7 @@ jobs:
BINARYEN_VERSION: 113
WASMTIME_VERSION: v15.0.0
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: >-
${{!(false
@@ -63,7 +66,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
@@ -111,6 +114,12 @@ jobs:
make
make install
+ - name: Download config.guess with wasi version
+ run: |
+ rm tool/config.guess tool/config.sub
+ ruby tool/downloader.rb -d tool -e gnu config.guess config.sub
+ working-directory: src
+
- name: Run configure
run: |
../src/configure \
@@ -133,7 +142,7 @@ jobs:
- run: tar cfz ../install.tar.gz -C ../install .
- name: Upload artifacts
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ruby-wasm-install
path: ${{ github.workspace }}/install.tar.gz
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 982f855345..164c8c8624 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,6 +15,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
paths-ignore:
- 'doc/**'
@@ -21,6 +23,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -34,11 +37,11 @@ jobs:
strategy:
matrix:
include:
- - vs: 2019
- vs: 2022
+ vcvers: -vcvars_ver=14.2
fail-fast: false
- runs-on: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
+ runs-on: windows-${{ matrix.vs }}
if: >-
${{!(false
@@ -52,7 +55,7 @@ jobs:
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
- OS_VER: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
+ OS_VER: windows-${{ matrix.vs }}
# FIXME: This is a workaround for the vcpkg's issue present as of openssl 3.1.1
# where OpenSSL's default modules directory is incorrectly set to C:\vcpkg\packages\openssl_x64-windows\bin
# cf. https://github.com/ruby/openssl/pull/635#issuecomment-1596833720
@@ -82,7 +85,7 @@ jobs:
NEEDED_TOOLS: >-
patch.exe
- - uses: msys2/setup-msys2@d40200dc2db4c351366b048a9565ad82919e1c24 # v2
+ - uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2.27.0
id: setup-msys2
with:
update: true
@@ -90,15 +93,7 @@ jobs:
${{ steps.find-tools.outputs.needs }}
if: ${{ steps.find-tools.outputs.needs != '' }}
- - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- with:
- path: C:\vcpkg\downloads
- key: ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-
- ${{ runner.os }}-vcpkg-download-
-
- - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: C:\vcpkg\installed
key: ${{ runner.os }}-vcpkg-installed-${{ env.OS_VER }}-${{ github.sha }}
@@ -106,17 +101,18 @@ jobs:
${{ runner.os }}-vcpkg-installed-${{ env.OS_VER }}-
${{ runner.os }}-vcpkg-installed-
- - name: Install libraries with vcpkg
- run: |
- vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib
-
- name: Install libraries with scoop
run: |
iex "& {$(irm get.scoop.sh)} -RunAsAdmin"
Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
+ scoop install cmake@3.31.6
shell: pwsh
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - name: Install libraries with vcpkg
+ run: |
+ vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib
+
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
@@ -130,14 +126,12 @@ jobs:
# %TEMP% is inconsistent with %TMP% and test-all expects they are consistent.
# https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302
run: |
- set VS=${{ matrix.vs }}
- set VCVARS=${{ matrix.vcvars || '' }}
if not "%VCVARS%" == "" goto :vcset
- set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
- if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
:vcset
set | C:\msys64\usr\bin\sort > old.env
- call %VCVARS%
+ call %VCVARS% ${{ matrix.vcvers || ''}}
nmake -f nul
set TMP=%USERPROFILE%\AppData\Local\Temp
set TEMP=%USERPROFILE%\AppData\Local\Temp
@@ -192,6 +186,16 @@ jobs:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
if: ${{ failure() }}
+ result:
+ if: ${{ always() }}
+ name: ${{ github.workflow }} result
+ runs-on: windows-latest
+ needs: [make]
+ steps:
+ - run: exit 1
+ working-directory:
+ if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
+
defaults:
run:
working-directory: build
diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml
index c8f21cfa7e..0718fe2783 100644
--- a/.github/workflows/yjit-macos.yml
+++ b/.github/workflows/yjit-macos.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
# Do not use paths-ignore for required status checks
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
@@ -17,6 +18,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -29,7 +31,7 @@ jobs:
cargo:
name: cargo test
- runs-on: macos-arm-oss
+ runs-on: macos-14
if: >-
${{github.repository == 'ruby/ruby' &&
@@ -41,7 +43,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: RUST_BACKTRACE=1 cargo test
working-directory: yjit
@@ -70,7 +72,7 @@ jobs:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUN_OPTS: ${{ matrix.yjit_opts }}
- runs-on: macos-arm-oss
+ runs-on: macos-14
if: >-
${{github.repository == 'ruby/ruby' &&
@@ -82,7 +84,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
@@ -138,7 +140,7 @@ jobs:
result:
if: ${{ always() && github.repository == 'ruby/ruby' }}
name: ${{ github.workflow }} result
- runs-on: macos-arm-oss
+ runs-on: macos-14
needs: [make]
steps:
- run: exit 1
diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml
index d29aaccdfb..b9ed80a8dc 100644
--- a/.github/workflows/yjit-ubuntu.yml
+++ b/.github/workflows/yjit-ubuntu.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
# Do not use paths-ignore for required status checks
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
@@ -17,6 +18,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -30,7 +32,7 @@ jobs:
name: cargo test
# GitHub Action's image seems to already contain a Rust 1.58.0.
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: >-
${{!(false
@@ -41,7 +43,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# For now we can't run cargo test --offline because it complains about the
# capstone dependency, even though the dependency is optional
@@ -62,7 +64,7 @@ jobs:
name: cargo clippy
# GitHub Action's image seems to already contain a Rust 1.58.0.
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: >-
${{!(false
@@ -73,7 +75,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Check that we don't have linting errors in release mode, too
- run: cargo clippy --all-targets --all-features
@@ -86,7 +88,8 @@ jobs:
include:
- test_task: 'yjit-bindgen'
hint: 'To fix: use patch in logs'
- configure: '--with-gcc=clang-12 --enable-yjit=dev'
+ configure: '--with-gcc=clang-14 --enable-yjit=dev'
+ libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1'
- test_task: 'check'
# YJIT should be automatically built in release mode on x86-64 Linux with rustc present
@@ -116,7 +119,7 @@ jobs:
BUNDLE_JOBS: 8 # for yjit-bench
RUST_BACKTRACE: 1
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
if: >-
${{!(false
@@ -127,7 +130,7 @@ jobs:
)}}
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
@@ -171,6 +174,7 @@ jobs:
PRECHECK_BUNDLED_GEMS: 'no'
SYNTAX_SUGGEST_TIMEOUT: '5'
YJIT_BINDGEN_DIFF_OPTS: '--exit-code'
+ LIBCLANG_PATH: ${{ matrix.libclang_path }}
continue-on-error: ${{ matrix.test_task == 'yjit-bench' }}
- name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison
diff --git a/.gitignore b/.gitignore
index f402bf2155..b6beba3b3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -138,6 +138,7 @@ lcov*.info
/test.rb
/test-coverage.dat
/tmp
+/vcpkg_installed
/transdb.h
/uncommon.mk
/verconf.h
@@ -166,6 +167,7 @@ lcov*.info
# /coroutine/
!/coroutine/**/*.s
+!/coroutine/**/*.S
# /enc/trans/
/enc/trans/*.c
@@ -260,12 +262,16 @@ lcov*.info
/lib/prism/dispatcher.rb
/lib/prism/dot_visitor.rb
/lib/prism/dsl.rb
+/lib/prism/inspect_visitor.rb
/lib/prism/mutation_compiler.rb
/lib/prism/node.rb
+/lib/prism/reflection.rb
/lib/prism/serialize.rb
/lib/prism/visitor.rb
/prism/api_node.c
/prism/ast.h
+/prism/diagnostic.c
+/prism/diagnostic.h
/prism/node.c
/prism/prettyprint.c
/prism/serialize.c
diff --git a/.travis.yml b/.travis.yml
index 741c0a32ef..2ac2372584 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -113,11 +113,10 @@ matrix:
- <<: *arm32-linux
allow_failures:
# Allow failures for the unstable jobs.
- # - name: arm64-linux
- # - name: ppc64le-linux
- # - name: s390x-linux
- # The 2nd arm64 pipeline may be unstable.
- # - name: arm32-linux
+ - name: arm64-linux
+ - name: ppc64le-linux
+ - name: s390x-linux
+ - name: arm32-linux
fast_finish: true
before_script:
diff --git a/array.c b/array.c
index d029d44b39..e1b8553568 100644
--- a/array.c
+++ b/array.c
@@ -3387,6 +3387,9 @@ rb_ary_sort_bang(VALUE ary)
rb_ary_unshare(ary);
FL_SET_EMBED(ary);
}
+ if (ARY_EMBED_LEN(tmp) > ARY_CAPA(ary)) {
+ ary_resize_capa(ary, ARY_EMBED_LEN(tmp));
+ }
ary_memcpy(ary, 0, ARY_EMBED_LEN(tmp), ARY_EMBED_PTR(tmp));
ARY_SET_LEN(ary, ARY_EMBED_LEN(tmp));
}
diff --git a/bignum.c b/bignum.c
index e9bf37d206..bc48697bf4 100644
--- a/bignum.c
+++ b/bignum.c
@@ -6333,7 +6333,7 @@ bigand_int(VALUE x, long xn, BDIGIT hibitsx, long y)
BDIGIT hibitsy;
if (y == 0) return INT2FIX(0);
- if (xn == 0) return hibitsx ? LONG2NUM(y) : 0;
+ if (xn == 0) return hibitsx ? LONG2NUM(y) : INT2FIX(0);
hibitsy = 0 <= y ? 0 : BDIGMAX;
xds = BDIGITS(x);
#if SIZEOF_BDIGIT >= SIZEOF_LONG
diff --git a/bootstraptest/test_fork.rb b/bootstraptest/test_fork.rb
index 83923dad97..0cdfb5ab24 100644
--- a/bootstraptest/test_fork.rb
+++ b/bootstraptest/test_fork.rb
@@ -75,3 +75,29 @@ assert_equal '[1, 2]', %q{
end
}, '[ruby-dev:44005] [Ruby 1.9 - Bug #4950]'
+assert_equal 'ok', %q{
+ # This test is very unstable. It fails a Ractor assertion:
+ # Assertion Failed: ../src/thread_pthread.c:451:ractor_sched_set_locked:vm->ractor.sched.lock_owner == NULL
+ skip :ok # too unstable
+
+ def now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ Thread.new do
+ loop { sleep 0.0001 }
+ end
+
+ 10.times do
+ pid = fork{ exit!(0) }
+ deadline = now + 1
+ until Process.waitpid(pid, Process::WNOHANG)
+ if now > deadline
+ Process.kill(:KILL, pid)
+ raise "failed"
+ end
+ sleep 0.001
+ end
+ rescue NotImplementedError
+ end
+ :ok
+}, '[Bug #20670]'
+
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb
index 4383795dc1..c03fbb07d7 100644
--- a/bootstraptest/test_ractor.rb
+++ b/bootstraptest/test_ractor.rb
@@ -1532,7 +1532,7 @@ assert_equal "ok", %q{
1_000.times { idle_worker, tmp_reporter = Ractor.select(*workers) }
"ok"
-} unless ENV['RUN_OPTS'] =~ /rjit/ # flaky
+} unless ENV['RUN_OPTS'] =~ /rjit/ || ENV.key?('CI') # flaky
assert_equal "ok", %q{
def foo(*); ->{ super }; end
@@ -1773,3 +1773,15 @@ assert_equal 'ok', %q{
}
end # if !ENV['GITHUB_WORKFLOW']
+
+# Using Symbol#to_proc inside ractors
+# [Bug #21354]
+assert_equal 'ok', %q{
+ :inspect.to_proc
+ Ractor.new do
+ # It should not use this cached proc, it should create a new one. If it used
+ # the cached proc, we would get a ractor_confirm_belonging error here.
+ :inspect.to_proc
+ end.take
+ 'ok'
+}
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index aaf70e7b93..02767a53bd 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -1,3 +1,77 @@
+# regression test for popping before side exit
+assert_equal "ok", %q{
+ def foo(a, *) = a
+
+ def call(args, &)
+ foo(1) # spill at where the block arg will be
+ foo(*args, &)
+ end
+
+ call([1, 2])
+
+ begin
+ call([])
+ rescue ArgumentError
+ :ok
+ end
+}
+
+# regression test for send processing before side exit
+assert_equal "ok", %q{
+ def foo(a, *) = :foo
+
+ def call(args)
+ send(:foo, *args)
+ end
+
+ call([1, 2])
+
+ begin
+ call([])
+ rescue ArgumentError
+ :ok
+ end
+}
+
+# test discarding extra yield arguments
+assert_equal "2210150001501015", %q{
+ def splat_kw(ary) = yield *ary, a: 1
+
+ def splat(ary) = yield *ary
+
+ def kw = yield 1, 2, a: 0
+
+ def simple = yield 0, 1
+
+ def calls
+ [
+ splat([1, 1, 2]) { |x, y| x + y },
+ splat([1, 1, 2]) { |y, opt = raise| opt + y},
+ splat_kw([0, 1]) { |a:| a },
+ kw { |a:| a },
+ kw { |a| a },
+ simple { 5.itself },
+ simple { |a| a },
+ simple { |opt = raise| opt },
+ simple { |*rest| rest },
+ simple { |opt_kw: 5| opt_kw },
+ # autosplat ineractions
+ [0, 1, 2].yield_self { |a, b| [a, b] },
+ [0, 1, 2].yield_self { |a, opt = raise| [a, opt] },
+ [1].yield_self { |a, opt = 4| a + opt },
+ ]
+ end
+
+ calls.join
+}
+
+# test autosplat with empty splat
+assert_equal "ok", %q{
+ def m(pos, splat) = yield pos, *splat
+
+ m([:ok], []) {|v0,| v0 }
+}
+
# regression test for send stack shifting
assert_normal_exit %q{
def foo(a, b)
@@ -11,6 +85,13 @@ assert_normal_exit %q{
call_foo
}
+# regression test for keyword splat with yield
+assert_equal 'nil', %q{
+ def splat_kw(kwargs) = yield(**kwargs)
+
+ splat_kw({}) { _1 }.inspect
+} unless rjit_enabled? # Not yet working on RJIT
+
# regression test for arity check with splat
assert_equal '[:ae, :ae]', %q{
def req_one(a_, b_ = 1) = raise
@@ -508,6 +589,8 @@ assert_equal 'string', %q{
# Check that exceptions work when getting global variable
assert_equal 'rescued', %q{
+ Warning[:deprecated] = true
+
module Warning
def warn(message)
raise
@@ -2440,6 +2523,18 @@ assert_equal '[1, 2, 3, 4, 5]', %q{
splatarray
}
+# splatkw
+assert_equal '[1, 2]', %q{
+ def foo(a:) = [a, yield]
+
+ def entry(&block)
+ a = { a: 1 }
+ foo(**a, &block)
+ end
+
+ entry { 2 }
+}
+
assert_equal '[1, 1, 2, 1, 2, 3]', %q{
def expandarray
arr = [1, 2, 3]
@@ -3396,6 +3491,74 @@ assert_equal 'new', %q{
test
}
+# Bug #21257 (infinite jmp)
+assert_equal 'ok', %q{
+ Good = :ok
+
+ def first
+ second
+ end
+
+ def second
+ ::Good
+ end
+
+ # Make `second` side exit on its first instruction
+ trace = TracePoint.new(:line) { }
+ trace.enable(target: method(:second))
+
+ first
+ # Recompile now that the constant cache is populated, so we get a fallthrough from `first` to `second`
+ # (this is need to reproduce with --yjit-call-threshold=1)
+ RubyVM::YJIT.code_gc if defined?(RubyVM::YJIT)
+ first
+
+ # Trigger a constant cache miss in rb_vm_opt_getconstant_path (in `second`) next time it's called
+ module InvalidateConstantCache
+ Good = nil
+ end
+
+ RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
+
+ first
+ first
+}
+
+assert_equal 'ok', %q{
+ # Multiple incoming branches into second
+ Good = :ok
+
+ def incoming_one
+ second
+ end
+
+ def incoming_two
+ second
+ end
+
+ def second
+ ::Good
+ end
+
+ # Make `second` side exit on its first instruction
+ trace = TracePoint.new(:line) { }
+ trace.enable(target: method(:second))
+
+ incoming_one
+ # Recompile now that the constant cache is populated, so we get a fallthrough from `incoming_one` to `second`
+ # (this is need to reproduce with --yjit-call-threshold=1)
+ RubyVM::YJIT.code_gc if defined?(RubyVM::YJIT)
+ incoming_one
+ incoming_two
+
+ # Trigger a constant cache miss in rb_vm_opt_getconstant_path (in `second`) next time it's called
+ module InvalidateConstantCache
+ Good = nil
+ end
+
+ incoming_one
+}
+
assert_equal 'ok', %q{
# Try to compile new method while OOM
def foo
@@ -4330,3 +4493,95 @@ assert_equal '0', %q{
entry
}
+
+# Integer succ and overflow
+assert_equal '[2, 4611686018427387904]', %q{
+ [1.succ, 4611686018427387903.succ]
+}
+
+# Integer right shift
+assert_equal '[0, 1, -4]', %q{
+ [0 >> 1, 2 >> 1, -7 >> 1]
+}
+
+assert_equal '[nil, "yield"]', %q{
+ def defined_yield = defined?(yield)
+ [defined_yield, defined_yield {}]
+}
+
+# splat with ruby2_keywords into rest parameter
+assert_equal '[[{:a=>1}], {}]', %q{
+ ruby2_keywords def foo(*args) = args
+
+ def bar(*args, **kw) = [args, kw]
+
+ def pass_bar(*args) = bar(*args)
+
+ def body
+ args = foo(a: 1)
+ pass_bar(*args)
+ end
+
+ body
+}
+
+# regression test for splatting empty array to cfunc
+assert_normal_exit %q{
+ def test_body(args) = Array(1, *args)
+ test_body([])
+ 0x100.times do
+ array = Array.new(100)
+ array.clear
+ test_body(array)
+ end
+}
+
+# compiling code shouldn't emit warnings as it may call into more Ruby code
+assert_equal 'ok', <<~'RUBY'
+ # [Bug #20522]
+ $VERBOSE = true
+ Warning[:performance] = true
+
+ module StrictWarnings
+ def warn(msg, **)
+ raise msg
+ end
+ end
+ Warning.singleton_class.prepend(StrictWarnings)
+
+ class A
+ def compiled_method(is_private)
+ @some_ivar = is_private
+ end
+ end
+
+ shape_max_variations = 8
+ if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS) && RubyVM::Shape::SHAPE_MAX_VARIATIONS != shape_max_variations
+ raise "Expected SHAPE_MAX_VARIATIONS to be #{shape_max_variations}, got: #{RubyVM::Shape::SHAPE_MAX_VARIATIONS}"
+ end
+
+ 100.times do |i|
+ klass = Class.new(A)
+ (shape_max_variations - 1).times do |j|
+ obj = klass.new
+ obj.instance_variable_set("@base_#{i}", 42)
+ obj.instance_variable_set("@ivar_#{j}", 42)
+ end
+ obj = klass.new
+ obj.instance_variable_set("@base_#{i}", 42)
+ begin
+ obj.compiled_method(true)
+ rescue
+ # expected
+ end
+ end
+
+ :ok
+RUBY
+
+assert_normal_exit %{
+ class Bug20997
+ def foo(&) = self.class.name(&)
+ new.foo
+ end
+}
diff --git a/class.c b/class.c
index 5e9dbca3a3..b2a219100c 100644
--- a/class.c
+++ b/class.c
@@ -1007,7 +1007,7 @@ rb_define_class_under(VALUE outer, const char *name, VALUE super)
}
VALUE
-rb_define_class_id_under(VALUE outer, ID id, VALUE super)
+rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super)
{
VALUE klass;
@@ -1024,8 +1024,6 @@ rb_define_class_id_under(VALUE outer, ID id, VALUE super)
" (%"PRIsVALUE" is given but was %"PRIsVALUE")",
outer, rb_id2str(id), RCLASS_SUPER(klass), super);
}
- /* Class may have been defined in Ruby and not pin-rooted */
- rb_vm_add_root_module(klass);
return klass;
}
@@ -1037,12 +1035,19 @@ rb_define_class_id_under(VALUE outer, ID id, VALUE super)
rb_set_class_path_string(klass, outer, rb_id2str(id));
rb_const_set(outer, id, klass);
rb_class_inherited(super, klass);
- rb_vm_add_root_module(klass);
return klass;
}
VALUE
+rb_define_class_id_under(VALUE outer, ID id, VALUE super)
+{
+ VALUE klass = rb_define_class_id_under_no_pin(outer, id, super);
+ rb_vm_add_root_module(klass);
+ return klass;
+}
+
+VALUE
rb_module_s_alloc(VALUE klass)
{
VALUE mod = class_alloc(T_MODULE, klass);
@@ -1189,8 +1194,8 @@ rb_include_module(VALUE klass, VALUE module)
iclass = iclass->next;
}
- int do_include = 1;
while (iclass) {
+ int do_include = 1;
VALUE check_class = iclass->klass;
/* During lazy sweeping, iclass->klass could be a dead object that
* has not yet been swept. */
@@ -1321,7 +1326,6 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
iclass = rb_include_class_new(module, super_class);
c = RCLASS_SET_SUPER(c, iclass);
RCLASS_SET_INCLUDER(iclass, klass);
- add_subclass = TRUE;
if (module != RCLASS_ORIGIN(module)) {
if (!origin_stack) origin_stack = rb_ary_hidden_new(2);
VALUE origin[2] = {iclass, RCLASS_ORIGIN(module)};
@@ -1332,14 +1336,11 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
RCLASS_SET_ORIGIN(RARRAY_AREF(origin_stack, (origin_len -= 2)), iclass);
RICLASS_SET_ORIGIN_SHARED_MTBL(iclass);
rb_ary_resize(origin_stack, origin_len);
- add_subclass = FALSE;
}
- if (add_subclass) {
- VALUE m = module;
- if (BUILTIN_TYPE(m) == T_ICLASS) m = METACLASS_OF(m);
- rb_module_add_to_subclasses_list(m, iclass);
- }
+ VALUE m = module;
+ if (BUILTIN_TYPE(m) == T_ICLASS) m = METACLASS_OF(m);
+ rb_module_add_to_subclasses_list(m, iclass);
if (BUILTIN_TYPE(klass) == T_MODULE && FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
VALUE refined_class =
diff --git a/common.mk b/common.mk
index 83f10f7a58..8075d6a95e 100644
--- a/common.mk
+++ b/common.mk
@@ -46,7 +46,7 @@ RUN_OPTS = --disable-gems
# GITPULLOPTIONS = --no-tags
PRISM_SRCDIR = $(srcdir)/prism
-INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(PRISM_SRCDIR) -I$(UNICODE_HDR_DIR) $(incflags)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(ext_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(PRISM_SRCDIR) -I$(UNICODE_HDR_DIR) $(incflags)
GEM_HOME =
GEM_PATH =
@@ -835,6 +835,12 @@ clean-platform distclean-platform realclean-platform:
-$(Q) $(RMDIR) $(PLATFORM_DIR) 2> $(NULL) || $(NULLCMD)
RUBYSPEC_CAPIEXT = spec/ruby/optional/capi/ext
+RUBYSPEC_CAPIEXT_SRCDIR = $(srcdir)/$(RUBYSPEC_CAPIEXT)
+RUBYSPEC_CAPIEXT_DEPS = $(RUBYSPEC_CAPIEXT_SRCDIR)/rubyspec.h $(RUBY_H_INCLUDES) $(LIBRUBY)
+
+rubyspec-capiext: build-ext $(DOT_WAIT)
+# make-dependent rules should be included after this and built after build-ext.
+
clean-spec: PHONY
-$(Q) $(RM) $(RUBYSPEC_CAPIEXT)/*.$(OBJEXT) $(RUBYSPEC_CAPIEXT)/*.$(DLEXT)
-$(Q) $(RMDIRS) $(RUBYSPEC_CAPIEXT) 2> $(NULL) || $(NULLCMD)
@@ -1560,7 +1566,7 @@ no-test-bundled-gems-prepare: no-test-bundled-gems-precheck
yes-test-bundled-gems-prepare: yes-test-bundled-gems-precheck
$(ACTIONS_GROUP)
$(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
- --install-dir .bundle --conservative "hoe" "json-schema" "test-unit-rr"
+ --install-dir .bundle --conservative "hoe" "json-schema:5.1.0" "test-unit-rr"
$(ACTIONS_ENDGROUP)
PREPARE_BUNDLED_GEMS = test-bundled-gems-prepare
@@ -7503,6 +7509,7 @@ goruby.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+goruby.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
goruby.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
@@ -9523,6 +9530,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
marshal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
@@ -10132,6 +10140,7 @@ miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
miniinit.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
@@ -12590,6 +12599,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/compilers.h
proc.$(OBJEXT): $(top_srcdir)/internal/error.h
proc.$(OBJEXT): $(top_srcdir)/internal/eval.h
proc.$(OBJEXT): $(top_srcdir)/internal/gc.h
+proc.$(OBJEXT): $(top_srcdir)/internal/hash.h
proc.$(OBJEXT): $(top_srcdir)/internal/imemo.h
proc.$(OBJEXT): $(top_srcdir)/internal/object.h
proc.$(OBJEXT): $(top_srcdir)/internal/proc.h
@@ -12800,6 +12810,8 @@ proc.$(OBJEXT): {$(VPATH)}prism/ast.h
proc.$(OBJEXT): {$(VPATH)}prism/version.h
proc.$(OBJEXT): {$(VPATH)}prism_compile.h
proc.$(OBJEXT): {$(VPATH)}proc.c
+proc.$(OBJEXT): {$(VPATH)}ractor.h
+proc.$(OBJEXT): {$(VPATH)}ractor_core.h
proc.$(OBJEXT): {$(VPATH)}ruby_assert.h
proc.$(OBJEXT): {$(VPATH)}ruby_atomic.h
proc.$(OBJEXT): {$(VPATH)}rubyparser.h
@@ -16466,6 +16478,7 @@ signal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+signal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
signal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
@@ -16955,6 +16968,7 @@ st.$(OBJEXT): {$(VPATH)}internal/variable.h
st.$(OBJEXT): {$(VPATH)}internal/warning_push.h
st.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
st.$(OBJEXT): {$(VPATH)}missing.h
+st.$(OBJEXT): {$(VPATH)}ruby_assert.h
st.$(OBJEXT): {$(VPATH)}st.c
st.$(OBJEXT): {$(VPATH)}st.h
st.$(OBJEXT): {$(VPATH)}subst.h
@@ -17224,6 +17238,7 @@ string.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
string.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+string.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
string.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
string.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
string.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
@@ -17682,6 +17697,7 @@ symbol.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
symbol.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
diff --git a/compile.c b/compile.c
index afc2061b12..22eadc8d25 100644
--- a/compile.c
+++ b/compile.c
@@ -2975,7 +2975,6 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
break;
}
else if ((lab = find_destination((INSN *)i)) != 0) {
- if (lab->unremovable) break;
unref_counts[lab->label_no]++;
}
}
@@ -2992,8 +2991,7 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
/* do nothing */
}
else if (IS_ADJUST(i)) {
- LABEL *dest = ((ADJUST *)i)->label;
- if (dest && dest->unremovable) return 0;
+ return 0;
}
end = i;
} while ((i = i->next) != 0);
@@ -4826,7 +4824,12 @@ compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node)
}
else {
CHECK(COMPILE_(ret, "array element", node, FALSE));
- ADD_INSN1(ret, node, newarray, INT2FIX(1));
+ if (keyword_node_p(node)) {
+ ADD_INSN1(ret, node, newarraykwsplat, INT2FIX(1));
+ }
+ else {
+ ADD_INSN1(ret, node, newarray, INT2FIX(1));
+ }
}
return 1;
@@ -5277,12 +5280,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;
@@ -5301,7 +5309,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);
ci = ci_argc_set(iseq, ci, argc - 1);
@@ -5310,9 +5320,11 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1));
INSERT_BEFORE_INSN(iobj, line_node, concatarray);
}
- 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);
@@ -6093,7 +6105,6 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
if (kwnode) {
// kwsplat
*flag_ptr |= VM_CALL_KW_SPLAT;
- *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
compile_hash(iseq, args, kwnode, TRUE, FALSE);
argc += 1;
}
@@ -7097,6 +7108,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
case NODE_COLON2:
case NODE_COLON3:
case NODE_BEGIN:
+ case NODE_BLOCK:
CHECK(COMPILE(ret, "case in literal", node)); // (1)
if (in_single_pattern) {
ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
@@ -7878,7 +7890,6 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
add_ensure_iseq(ret, iseq, 0);
ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
ADD_ADJUST_RESTORE(ret, splabel);
- splabel->unremovable = FALSE;
if (!popped) {
ADD_INSN(ret, line_node, putnil);
@@ -9371,14 +9382,11 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
if (local_body->param.flags.has_kwrest) {
int idx = local_body->local_table_size - local_kwd->rest_start;
ADD_GETLOCAL(args, node, idx, lvar_level);
- if (local_kwd->num > 0) {
- ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
- flag |= VM_CALL_KW_SPLAT_MUT;
- }
+ assert(local_kwd->num > 0);
+ ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
}
else {
ADD_INSN1(args, node, newhash, INT2FIX(0));
- flag |= VM_CALL_KW_SPLAT_MUT;
}
for (i = 0; i < local_kwd->num; ++i) {
ID id = local_kwd->table[i];
@@ -9387,13 +9395,13 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_GETLOCAL(args, node, idx, lvar_level);
}
ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
- flag |= VM_CALL_KW_SPLAT;
+ flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
}
else if (local_body->param.flags.has_kwrest) {
int idx = local_body->local_table_size - local_kwd->rest_start;
ADD_GETLOCAL(args, node, idx, lvar_level);
argc++;
- flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT;
+ flag |= VM_CALL_KW_SPLAT;
}
}
@@ -9658,11 +9666,13 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
VALUE argc;
LABEL *else_label = NULL;
VALUE branches = Qfalse;
+ struct rb_callinfo_kwarg *keywords = NULL;
/* optimization shortcut
* obj["literal"] = value -> opt_aset_with(obj, "literal", value)
*/
- if (mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args &&
+ if (!ISEQ_COMPILE_DATA(iseq)->in_masgn &&
+ mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args &&
nd_type_p(RNODE_ATTRASGN(node)->nd_args, NODE_LIST) && RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->as.nd_alen == 2 &&
nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_STR) &&
ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
@@ -9685,7 +9695,7 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
INIT_ANCHOR(recv);
INIT_ANCHOR(args);
- argc = setup_args(iseq, args, RNODE_ATTRASGN(node)->nd_args, &flag, NULL);
+ argc = setup_args(iseq, args, RNODE_ATTRASGN(node)->nd_args, &flag, &keywords);
CHECK(!NIL_P(argc));
int asgnflag = COMPILE_RECV(recv, "recv", node, RNODE_ATTRASGN(node)->nd_recv);
@@ -9773,7 +9783,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
if (nd_fl_newline(node)) {
int event = RUBY_EVENT_LINE;
ISEQ_COMPILE_DATA(iseq)->last_line = line;
- if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
+ if (line > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
event |= RUBY_EVENT_COVERAGE_LINE;
}
ADD_TRACE(ret, event);
@@ -9860,7 +9870,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_MASGN:{
+ bool prev_in_masgn = ISEQ_COMPILE_DATA(iseq)->in_masgn;
+ ISEQ_COMPILE_DATA(iseq)->in_masgn = true;
compile_massign(iseq, ret, node, popped);
+ ISEQ_COMPILE_DATA(iseq)->in_masgn = prev_in_masgn;
break;
}
@@ -10090,7 +10103,12 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
case NODE_LIT:{
debugp_param("lit", RNODE_LIT(node)->nd_lit);
if (!popped) {
- ADD_INSN1(ret, node, putobject, RNODE_LIT(node)->nd_lit);
+ if (UNLIKELY(RNODE_LIT(node)->nd_lit == rb_mRubyVMFrozenCore)) {
+ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); // [Bug #20569]
+ }
+ else {
+ ADD_INSN1(ret, node, putobject, RNODE_LIT(node)->nd_lit);
+ }
RB_OBJ_WRITTEN(iseq, Qundef, RNODE_LIT(node)->nd_lit);
}
break;
diff --git a/complex.c b/complex.c
index d29189e367..b6fbc856f1 100644
--- a/complex.c
+++ b/complex.c
@@ -2321,8 +2321,11 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise)
return a1;
/* should raise exception for consistency */
if (!k_numeric_p(a1)) {
- if (!raise)
- return rb_protect(to_complex, a1, NULL);
+ if (!raise) {
+ a1 = rb_protect(to_complex, a1, NULL);
+ rb_set_errinfo(Qnil);
+ return a1;
+ }
return to_complex(a1);
}
}
diff --git a/configure.ac b/configure.ac
index 18b4247991..0f9553ee42 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
@@ -225,15 +226,23 @@ AS_CASE(["/${rb_CC} "],
[*clang*], [
# Ditto for LLVM. Note however that llvm-as is a LLVM-IR to LLVM bitcode
# assembler that does not target your machine native binary.
+
+ # Xcode has its own version tools that may be incompatible with
+ # genuine LLVM tools, use the tools in the same directory.
+
+ AS_IF([$rb_CC -E -dM -xc - < /dev/null | grep -F __apple_build_version__ > /dev/null],
+ [llvm_prefix=], [llvm_prefix=llvm-])
+ # AC_PREPROC_IFELSE cannot be used before AC_USE_SYSTEM_EXTENSIONS
+
RUBY_CHECK_PROG_FOR_CC([LD], [s/clang/ld/]) # ... maybe try lld ?
- RUBY_CHECK_PROG_FOR_CC([AR], [s/clang/llvm-ar/])
-# RUBY_CHECK_PROG_FOR_CC([AS], [s/clang/llvm-as/])
+ RUBY_CHECK_PROG_FOR_CC([AR], [s/clang/${llvm_prefix}ar/])
+# RUBY_CHECK_PROG_FOR_CC([AS], [s/clang/${llvm_prefix}as/])
RUBY_CHECK_PROG_FOR_CC([CXX], [s/clang/clang++/])
- RUBY_CHECK_PROG_FOR_CC([NM], [s/clang/llvm-nm/])
- RUBY_CHECK_PROG_FOR_CC([OBJCOPY], [s/clang/llvm-objcopy/])
- RUBY_CHECK_PROG_FOR_CC([OBJDUMP], [s/clang/llvm-objdump/])
- RUBY_CHECK_PROG_FOR_CC([RANLIB], [s/clang/llvm-ranlib/])
- RUBY_CHECK_PROG_FOR_CC([STRIP], [s/clang/llvm-strip/])
+ RUBY_CHECK_PROG_FOR_CC([NM], [s/clang/${llvm_prefix}nm/])
+ RUBY_CHECK_PROG_FOR_CC([OBJCOPY], [s/clang/${llvm_prefix}objcopy/])
+ RUBY_CHECK_PROG_FOR_CC([OBJDUMP], [s/clang/${llvm_prefix}objdump/])
+ RUBY_CHECK_PROG_FOR_CC([RANLIB], [s/clang/${llvm_prefix}ranlib/])
+ RUBY_CHECK_PROG_FOR_CC([STRIP], [s/clang/${llvm_prefix}strip/])
])
AS_UNSET(rb_CC)
AS_UNSET(rb_dummy)
@@ -244,12 +253,6 @@ AS_CASE(["${build_os}"],
],
[aix*], [
AC_PATH_TOOL([NM], [nm], [/usr/ccs/bin/nm], [/usr/ccs/bin:$PATH])
-],
-[darwin*], [
- # For Apple clang version 14.0.3 (clang-1403.0.22.14.1)
- ac_cv_prog_ac_ct_AR=`$CC -print-prog-name=ar`
- ac_cv_prog_ac_ct_LD=`$CC -print-prog-name=ld`
- ac_cv_prog_ac_ct_NM=`$CC -print-prog-name=nm`
])
AS_CASE(["${target_os}"],
[cygwin*|msys*|mingw*|darwin*], [
@@ -462,7 +465,7 @@ AC_SUBST(CC_VERSION_MESSAGE, $cc_version_message)
RUBY_UNIVERSAL_ARCH
AS_IF([test "$target_cpu" != "$host_cpu" -a "$GCC" = yes -a "${universal_binary:-no}" = no], [
- RUBY_DEFAULT_ARCH("$target_cpu")
+ RUBY_DEFAULT_ARCH($target_cpu)
])
host_os=$target_os
host_vendor=$target_vendor
@@ -1207,8 +1210,6 @@ main()
ac_cv_func_gmtime_r=yes
rb_cv_large_fd_select=yes
ac_cv_type_struct_timeval=yes
- ac_cv_func_clock_gettime=yes
- ac_cv_func_clock_getres=yes
ac_cv_func_malloc_usable_size=no
ac_cv_type_off_t=yes
ac_cv_sizeof_off_t=8
@@ -1356,6 +1357,7 @@ AC_CHECK_HEADERS(ucontext.h)
AC_CHECK_HEADERS(utime.h)
AC_CHECK_HEADERS(sys/epoll.h)
AC_CHECK_HEADERS(sys/event.h)
+AC_CHECK_HEADERS(stdatomic.h)
AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [
AC_CHECK_HEADERS(x86intrin.h)
@@ -1363,7 +1365,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)]))])
@@ -1373,6 +1375,8 @@ AC_ARG_WITH([jemalloc],
[with_jemalloc=$withval], [with_jemalloc=no])
AS_IF([test "x$with_jemalloc" != xno],[
# find jemalloc header first
+ save_CPPFLAGS="${CPPFLAGS}"
+ CPPFLAGS="${INCFLAGS} ${CPPFLAGS}"
malloc_header=
AC_CHECK_HEADER(jemalloc/jemalloc.h, [malloc_header=jemalloc/jemalloc.h], [
AC_CHECK_HEADER(jemalloc.h, [malloc_header=jemalloc.h])
@@ -1404,6 +1408,8 @@ AS_IF([test "x$with_jemalloc" != xno],[
done
done
])
+ CPPFLAGS="${save_CPPFLAGS}"
+ unset save_CPPFLAGS
with_jemalloc=${rb_cv_jemalloc_library}
AS_CASE(["$with_jemalloc"],
[no],
@@ -2032,6 +2038,7 @@ AC_CHECK_FUNCS(_longjmp) # used for AC_ARG_WITH(setjmp-type)
test x$ac_cv_func__longjmp = xno && ac_cv_func__setjmp=no
AC_CHECK_FUNCS(arc4random_buf)
AC_CHECK_FUNCS(atan2l atan2f)
+AC_CHECK_DECLS(atomic_signal_fence, [], [], [#include <stdatomic.h>])
AC_CHECK_FUNCS(chmod)
AC_CHECK_FUNCS(chown)
AC_CHECK_FUNCS(chroot)
diff --git a/cont.c b/cont.c
index 2b60860edd..55040d3d38 100644
--- a/cont.c
+++ b/cont.c
@@ -2378,7 +2378,7 @@ rb_fiber_initialize(int argc, VALUE* argv, VALUE self)
VALUE
rb_fiber_new_storage(rb_block_call_func_t func, VALUE obj, VALUE storage)
{
- return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 1, storage);
+ return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 0, storage);
}
VALUE
@@ -3223,7 +3223,13 @@ rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
static VALUE
fiber_raise(rb_fiber_t *fiber, VALUE exception)
{
- if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
+ if (fiber == fiber_current()) {
+ rb_exc_raise(exception);
+ }
+ else if (fiber->resuming_fiber) {
+ return fiber_raise(fiber->resuming_fiber, exception);
+ }
+ else if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
return fiber_transfer_kw(fiber, -1, &exception, RB_NO_KEYWORDS);
}
else {
diff --git a/coroutine/asyncify/Context.h b/coroutine/asyncify/Context.h
index 7dba829a1d..71791a4004 100644
--- a/coroutine/asyncify/Context.h
+++ b/coroutine/asyncify/Context.h
@@ -13,6 +13,7 @@
#include <stddef.h>
#include <stdio.h>
+#include <stdint.h>
#include "wasm/asyncify.h"
#include "wasm/machine.h"
#include "wasm/fiber.h"
@@ -47,10 +48,13 @@ static inline void coroutine_initialize_main(struct coroutine_context * context)
static inline void coroutine_initialize(struct coroutine_context *context, coroutine_start start, void *stack, size_t size)
{
- if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)stack + size);
+ // Linear stack pointer must be always aligned down to 16 bytes.
+ // https://github.com/WebAssembly/tool-conventions/blob/c74267a5897c1bdc9aa60adeaf41816387d3cd12/BasicCABI.md#the-linear-stack
+ uintptr_t sp = ((uintptr_t)stack + size) & ~0xF;
+ if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)sp);
rb_wasm_init_context(&context->fc, coroutine_trampoline, start, context);
// record the initial stack pointer position to restore it after resumption
- context->current_sp = (char *)stack + size;
+ context->current_sp = (char *)sp;
context->stack_base = stack;
context->size = size;
}
diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in
index 192a8cc711..7bcf1ada30 100644
--- a/cygwin/GNUmakefile.in
+++ b/cygwin/GNUmakefile.in
@@ -2,6 +2,9 @@ gnumake = yes
include Makefile
+MUNICODE_FLAG := $(if $(filter mingw%,$(target_os)),-municode)
+override EXE_LDFLAGS += $(MUNICODE_FLAG)
+
DLLWRAP = @DLLWRAP@ --target=$(target_os) --driver-name="$(CC)"
ifeq (@USE_LLVM_WINDRES@,yes) # USE_LLVM_WINDRES
# llvm-windres fails when preprocessor options are added
@@ -69,7 +72,7 @@ $(PROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT)
$(WPROGRAM): $(RUBYW_INSTALL_NAME).res.$(OBJEXT)
@rm -f $@
$(ECHO) linking $@
- $(Q) $(PURIFY) $(CC) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \
+ $(Q) $(PURIFY) $(CC) $(MUNICODE_FLAG) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \
$(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(LIBS) -o $@
$(STUBPROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT)
diff --git a/defs/gmake.mk b/defs/gmake.mk
index 5489b017b3..c3201e2502 100644
--- a/defs/gmake.mk
+++ b/defs/gmake.mk
@@ -500,7 +500,8 @@ update-deps:
# order-only-prerequisites doesn't work for $(RUBYSPEC_CAPIEXT)
# because the same named directory exists in the source tree.
-$(RUBYSPEC_CAPIEXT)/%.$(DLEXT): $(srcdir)/$(RUBYSPEC_CAPIEXT)/%.c $(srcdir)/$(RUBYSPEC_CAPIEXT)/rubyspec.h $(RUBY_H_INCLUDES) $(LIBRUBY)
+$(RUBYSPEC_CAPIEXT)/%.$(DLEXT): $(srcdir)/$(RUBYSPEC_CAPIEXT)/%.c $(RUBYSPEC_CAPIEXT_DEPS) \
+ | build-ext
$(ECHO) building $@
$(Q) $(MAKEDIRS) $(@D)
$(Q) $(DLDSHARED) -L. $(XDLDFLAGS) $(XLDFLAGS) $(LDFLAGS) $(INCFLAGS) $(CPPFLAGS) $(OUTFLAG)$@ $< $(LIBRUBYARG)
diff --git a/doc/_timezones.rdoc b/doc/_timezones.rdoc
index c5230ea67d..e355d699b4 100644
--- a/doc/_timezones.rdoc
+++ b/doc/_timezones.rdoc
@@ -64,43 +64,49 @@ The timezone methods are:
- +local_to_utc+:
- - Called when Time.new is invoked with +tz+
- as the value of positional argument +zone+ or keyword argument +in:+.
- - Argument: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
- - Returns: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects] in the UTC timezone.
+ Called when Time.new is invoked with +tz+ as the value of positional
+ argument +zone+ or keyword argument +in:+.
+
+ Argument:: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
+ Returns:: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects] in the UTC timezone.
- +utc_to_local+:
- - Called when Time.at or Time.now is invoked with +tz+
- as the value for keyword argument +in:+,
- and when Time#getlocal or Time#localtime is called with +tz+
- as the value for positional argument +zone+.
- - Argument: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
- - Returns: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects] in the local timezone.
+ Called when Time.at or Time.now is invoked with +tz+ as the value for
+ keyword argument +in:+, and when Time#getlocal or Time#localtime is called
+ with +tz+ as the value for positional argument +zone+.
+
+ The UTC offset will be calculated as the difference between the
+ original time and the returned object as an +Integer+.
+
+ Argument:: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
+ Returns:: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects] in the local timezone.
A custom timezone class may have these instance methods,
which will be called if defined:
- +abbr+:
- - Called when Time#strftime is invoked with a format involving <tt>%Z</tt>.
- - Argument: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
- - Returns: a string abbreviation for the timezone name.
+ Called when Time#strftime is invoked with a format involving <tt>%Z</tt>.
+
+ Argument:: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
+ Returns:: a string abbreviation for the timezone name.
- +dst?+:
- - Called when Time.at or Time.now is invoked with +tz+
- as the value for keyword argument +in:+,
- and when Time#getlocal or Time#localtime is called with +tz+
- as the value for positional argument +zone+.
- - Argument: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
- - Returns: whether the time is daylight saving time.
+ Called when Time.at or Time.now is invoked with +tz+ as the value for
+ keyword argument +in:+, and when Time#getlocal or Time#localtime is
+ called with +tz+ as the value for positional argument +zone+.
+
+ Argument:: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
+ Returns:: whether the time is daylight saving time.
- +name+:
- - Called when <tt>Marshal.dump(t)</tt> is invoked
- - Argument: none.
- - Returns: the string name of the timezone.
+ Called when <tt>Marshal.dump(t)</tt> is invoked
+
+ Argument:: none.
+ Returns:: the string name of the timezone.
==== +Time+-Like Objects
diff --git a/doc/irb/indexes.md b/doc/irb/indexes.md
index 9659db8c0b..24a67b9698 100644
--- a/doc/irb/indexes.md
+++ b/doc/irb/indexes.md
@@ -165,9 +165,6 @@ for each entry that is pre-defined, the initial value is given:
- `:RC`: Whether a {configuration file}[rdoc-ref:IRB@Configuration+File]
was found and interpreted;
initial value: `true` if a configuration file was found, `false` otherwise.
-- `:RC_NAME_GENERATOR`: \Proc to generate paths of potential
- {configuration files}[rdoc-ref:IRB@Configuration+File];
- initial value: `=> #<Proc:0x000055f9bebfed80 /var/lib/gems/3.0.0/gems/irb-1.8.3/lib/irb/init.rb:401>`.
- `:SAVE_HISTORY`: Number of commands to save in
{input command history}[rdoc-ref:IRB@Input+Command+History];
initial value: `1000`.
diff --git a/doc/string/new.rdoc b/doc/string/new.rdoc
index d955e61c87..1d44291f76 100644
--- a/doc/string/new.rdoc
+++ b/doc/string/new.rdoc
@@ -46,6 +46,10 @@ which may in turn affect performance:
String.new(capacity: 1)
String.new('foo', capacity: 4096)
+Note that Ruby strings are null-terminated internally, so the internal
+buffer size will be one or more bytes larger than the requested capacity
+depending on the encoding.
+
The +string+, +encoding+, and +capacity+ arguments may all be used together:
String.new('hello', encoding: 'UTF-8', capacity: 25)
diff --git a/enc/Makefile.in b/enc/Makefile.in
index dd8ca1b528..9d0c367134 100644
--- a/enc/Makefile.in
+++ b/enc/Makefile.in
@@ -51,7 +51,7 @@ optflags = @optflags@
debugflags = @debugflags@
warnflags = @warnflags@
CCDLFLAGS = @CCDLFLAGS@
-INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(top_srcdir)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(top_srcdir) @incflags@
DEFS = @DEFS@
CPPFLAGS = @CPPFLAGS@ -DONIG_ENC_REGISTER=rb_enc_register
LDFLAGS = @LDFLAGS@
diff --git a/enc/depend b/enc/depend
index 12ddbc223a..cf2ca2d9da 100644
--- a/enc/depend
+++ b/enc/depend
@@ -6972,6 +6972,7 @@ enc/trans/iso2022.$(OBJEXT): internal/attr/nodiscard.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noexcept.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noinline.h
enc/trans/iso2022.$(OBJEXT): internal/attr/nonnull.h
+enc/trans/iso2022.$(OBJEXT): internal/attr/nonstring.h
enc/trans/iso2022.$(OBJEXT): internal/attr/noreturn.h
enc/trans/iso2022.$(OBJEXT): internal/attr/packed_struct.h
enc/trans/iso2022.$(OBJEXT): internal/attr/pure.h
diff --git a/enc/trans/iso2022.trans b/enc/trans/iso2022.trans
index a441f1596d..ea7d2c8aec 100644
--- a/enc/trans/iso2022.trans
+++ b/enc/trans/iso2022.trans
@@ -1,4 +1,5 @@
#include "transcode_data.h"
+#include "ruby/internal/attr/nonstring.h"
<%
map = {
@@ -443,7 +444,7 @@ rb_cp50221_encoder = {
iso2022jp_encoder_reset_sequence_size, finish_iso2022jp_encoder
};
-static const char *tbl0208 =
+RBIMPL_ATTR_NONSTRING() static const char *tbl0208 =
"\x21\x23\x21\x56\x21\x57\x21\x22\x21\x26\x25\x72\x25\x21\x25\x23" \
"\x25\x25\x25\x27\x25\x29\x25\x63\x25\x65\x25\x67\x25\x43\x21\x3C" \
"\x25\x22\x25\x24\x25\x26\x25\x28\x25\x2A\x25\x2B\x25\x2D\x25\x2F" \
diff --git a/encoding.c b/encoding.c
index 1ede6a677c..3613fcc9f0 100644
--- a/encoding.c
+++ b/encoding.c
@@ -1291,7 +1291,7 @@ enc_names_i(st_data_t name, st_data_t idx, st_data_t args)
VALUE *arg = (VALUE *)args;
if ((int)idx == (int)arg[0]) {
- VALUE str = rb_fstring_cstr((char *)name);
+ VALUE str = rb_interned_str_cstr((char *)name);
rb_ary_push(arg[1], str);
}
return ST_CONTINUE;
@@ -1543,7 +1543,14 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha
if (NIL_P(encoding)) {
def->index = -1;
def->enc = 0;
- st_insert(enc_table->names, (st_data_t)strdup(name),
+ char *name_dup = strdup(name);
+
+ st_data_t existing_name = (st_data_t)name_dup;
+ if (st_delete(enc_table->names, &existing_name, NULL)) {
+ xfree((void *)existing_name);
+ }
+
+ st_insert(enc_table->names, (st_data_t)name_dup,
(st_data_t)UNSPECIFIED_ENCODING);
}
else {
@@ -1781,7 +1788,7 @@ static int
rb_enc_name_list_i(st_data_t name, st_data_t idx, st_data_t arg)
{
VALUE ary = (VALUE)arg;
- VALUE str = rb_fstring_cstr((char *)name);
+ VALUE str = rb_interned_str_cstr((char *)name);
rb_ary_push(ary, str);
return ST_CONTINUE;
}
@@ -1826,7 +1833,7 @@ rb_enc_aliases_enc_i(st_data_t name, st_data_t orig, st_data_t arg)
str = rb_fstring_cstr(rb_enc_name(enc));
rb_ary_store(ary, idx, str);
}
- key = rb_fstring_cstr((char *)name);
+ key = rb_interned_str_cstr((char *)name);
rb_hash_aset(aliases, key, str);
return ST_CONTINUE;
}
diff --git a/enum.c b/enum.c
index 7f15836ba8..cd48e80866 100644
--- a/enum.c
+++ b/enum.c
@@ -4650,7 +4650,7 @@ sum_iter(VALUE i, struct enum_sum_memo *memo)
}
else switch (TYPE(memo->v)) {
default: sum_iter_some_value(i, memo); return;
- case T_FLOAT: sum_iter_Kahan_Babuska(i, memo); return;
+ case T_FLOAT:
case T_FIXNUM:
case T_BIGNUM:
case T_RATIONAL:
diff --git a/error.c b/error.c
index 29f0f27ab9..32409e1e41 100644
--- a/error.c
+++ b/error.c
@@ -432,7 +432,7 @@ rb_warn(const char *fmt, ...)
void
rb_category_warn(rb_warning_category_t category, const char *fmt, ...)
{
- if (!NIL_P(ruby_verbose)) {
+ if (!NIL_P(ruby_verbose) && rb_warning_category_enabled_p(category)) {
with_warning_string(mesg, 0, fmt) {
rb_warn_category(mesg, rb_warning_category_to_name(category));
}
@@ -464,7 +464,7 @@ rb_warning(const char *fmt, ...)
void
rb_category_warning(rb_warning_category_t category, const char *fmt, ...)
{
- if (RTEST(ruby_verbose)) {
+ if (RTEST(ruby_verbose) && rb_warning_category_enabled_p(category)) {
with_warning_string(mesg, 0, fmt) {
rb_warn_category(mesg, rb_warning_category_to_name(category));
}
@@ -1662,7 +1662,7 @@ exc_detailed_message(int argc, VALUE *argv, VALUE exc)
VALUE highlight = check_highlight_keyword(opt, 0);
- extern VALUE rb_decorate_message(const VALUE eclass, const VALUE emesg, int highlight);
+ extern VALUE rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight);
return rb_decorate_message(CLASS_OF(exc), rb_get_message(exc), RTEST(highlight));
}
diff --git a/eval.c b/eval.c
index 15b6567aff..a8fc48daf5 100644
--- a/eval.c
+++ b/eval.c
@@ -1797,6 +1797,16 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
return obj;
}
+VALUE
+rb_top_main_class(const char *method)
+{
+ VALUE klass = GET_THREAD()->top_wrapper;
+
+ if (!klass) return rb_cObject;
+ rb_warning("main.%s in the wrapped load is effective only in wrapper module", method);
+ return klass;
+}
+
/*
* call-seq:
* include(module, ...) -> self
@@ -1809,13 +1819,7 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
static VALUE
top_include(int argc, VALUE *argv, VALUE self)
{
- rb_thread_t *th = GET_THREAD();
-
- if (th->top_wrapper) {
- rb_warning("main.include in the wrapped load is effective only in wrapper module");
- return rb_mod_include(argc, argv, th->top_wrapper);
- }
- return rb_mod_include(argc, argv, rb_cObject);
+ return rb_mod_include(argc, argv, rb_top_main_class("include"));
}
/*
diff --git a/eval_error.c b/eval_error.c
index bdce295f6e..d58df5a737 100644
--- a/eval_error.c
+++ b/eval_error.c
@@ -125,7 +125,7 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VA
}
VALUE
-rb_decorate_message(const VALUE eclass, const VALUE emesg, int highlight)
+rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight)
{
const char *einfo = "";
long elen = 0;
@@ -210,6 +210,8 @@ rb_decorate_message(const VALUE eclass, const VALUE emesg, int highlight)
}
}
+ RB_GC_GUARD(emesg);
+
return str;
}
diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c
index 7ee14a8570..8dfa36e345 100644
--- a/ext/-test-/string/fstring.c
+++ b/ext/-test-/string/fstring.c
@@ -21,13 +21,13 @@ bug_s_fstring_fake_str(VALUE self)
VALUE
bug_s_rb_enc_interned_str(VALUE self, VALUE encoding)
{
- return rb_enc_interned_str("foo", 3, RDATA(encoding)->data);
+ return rb_enc_interned_str("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data);
}
VALUE
bug_s_rb_enc_str_new(VALUE self, VALUE encoding)
{
- return rb_enc_str_new("foo", 3, RDATA(encoding)->data);
+ return rb_enc_str_new("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data);
}
void
diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index f4b390584b..55706088f4 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -6941,13 +6941,24 @@ d_lite_eql_p(VALUE self, VALUE other)
static VALUE
d_lite_hash(VALUE self)
{
- st_index_t v, h[4];
+ st_index_t v, h[5];
+ VALUE nth;
get_d1(self);
- h[0] = m_nth(dat);
- h[1] = m_jd(dat);
- h[2] = m_df(dat);
- h[3] = m_sf(dat);
+ nth = m_nth(dat);
+
+ if (FIXNUM_P(nth)) {
+ h[0] = 0;
+ h[1] = (st_index_t)nth;
+ } else {
+ h[0] = 1;
+ h[1] = (st_index_t)FIX2LONG(rb_hash(nth));
+ }
+
+ h[2] = m_jd(dat);
+ h[3] = m_df(dat);
+ h[4] = m_sf(dat);
+
v = rb_memhash(h, sizeof(h));
return ST2FIX(v);
}
diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb
index 2e28d58037..c4929c02d2 100644
--- a/ext/etc/extconf.rb
+++ b/ext/etc/extconf.rb
@@ -58,7 +58,7 @@ end
# TODO: remove when dropping 2.7 support, as exported since 3.0
have_func('rb_deprecate_constant(Qnil, "None")')
-have_func("rb_io_descriptor")
+have_func("rb_io_descriptor", "ruby/io.h")
$distcleanfiles << "constdefs.h"
diff --git a/ext/extmk.rb b/ext/extmk.rb
index 428ffc91a6..da0b06aaa5 100755
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -132,6 +132,14 @@ def extract_makefile(makefile, keep = true)
true
end
+def create_makefile(target, srcprefix = nil)
+ if $static and target.include?("/")
+ base = File.basename(target)
+ $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}"
+ end
+ super
+end
+
def extmake(target, basedir = 'ext', maybestatic = true)
FileUtils.mkpath target unless File.directory?(target)
begin
@@ -159,8 +167,6 @@ def extmake(target, basedir = 'ext', maybestatic = true)
$mdir = target
$srcdir = File.join($top_srcdir, basedir, $mdir)
$preload = nil
- $objs = []
- $srcs = []
$extso = []
makefile = "./Makefile"
static = $static
@@ -194,7 +200,7 @@ def extmake(target, basedir = 'ext', maybestatic = true)
begin
$extconf_h = nil
ok &&= extract_makefile(makefile)
- old_objs = $objs
+ old_objs = $objs || []
old_cleanfiles = $distcleanfiles | $cleanfiles
conf = ["#{$srcdir}/makefile.rb", "#{$srcdir}/extconf.rb"].find {|f| File.exist?(f)}
if (!ok || ($extconf_h && !File.exist?($extconf_h)) ||
@@ -257,6 +263,8 @@ def extmake(target, basedir = 'ext', maybestatic = true)
unless $destdir.to_s.empty? or $mflags.defined?("DESTDIR")
args += ["DESTDIR=" + relative_from($destdir, "../"+prefix)]
end
+ $objs ||= []
+ $srcs ||= []
if $static and ok and !$objs.empty? and !noinstall
args += ["static"]
$extlist.push [(maybestatic ? $static : false), target, $target, $preload]
@@ -545,7 +553,13 @@ extend Module.new {
end
def create_makefile(*args, &block)
- return super unless @gemname
+ unless @gemname
+ if $static and (target = args.first).include?("/")
+ base = File.basename(target)
+ $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}"
+ end
+ return super
+ end
super(*args) do |conf|
conf.find do |s|
s.sub!(%r(^(srcdir *= *)\$\(top_srcdir\)/\.bundle/gems/[^/]+(?=/))) {
diff --git a/ext/io/console/extconf.rb b/ext/io/console/extconf.rb
index aa0b6c6cfb..c9d37a48d7 100644
--- a/ext/io/console/extconf.rb
+++ b/ext/io/console/extconf.rb
@@ -6,11 +6,11 @@ version = ["../../..", "."].find do |dir|
rescue
end
-have_func("rb_io_path")
-have_func("rb_io_descriptor")
-have_func("rb_io_get_write_io")
-have_func("rb_io_closed_p")
-have_func("rb_io_open_descriptor")
+have_func("rb_io_path", "ruby/io.h")
+have_func("rb_io_descriptor", "ruby/io.h")
+have_func("rb_io_get_write_io", "ruby/io.h")
+have_func("rb_io_closed_p", "ruby/io.h")
+have_func("rb_io_open_descriptor", "ruby/io.h")
ok = true if RUBY_ENGINE == "ruby" || RUBY_ENGINE == "truffleruby"
hdr = nil
diff --git a/ext/io/nonblock/extconf.rb b/ext/io/nonblock/extconf.rb
index a1e6075c9b..505c9e6252 100644
--- a/ext/io/nonblock/extconf.rb
+++ b/ext/io/nonblock/extconf.rb
@@ -7,7 +7,7 @@ unless RUBY_ENGINE == 'ruby'
return
end
-have_func("rb_io_descriptor")
+have_func("rb_io_descriptor", "ruby/io.h")
hdr = %w"fcntl.h"
if have_macro("O_NONBLOCK", hdr) and
diff --git a/ext/io/wait/extconf.rb b/ext/io/wait/extconf.rb
index e63c046187..ba223f0ac2 100644
--- a/ext/io/wait/extconf.rb
+++ b/ext/io/wait/extconf.rb
@@ -5,8 +5,8 @@ if RUBY_VERSION < "2.6"
File.write("Makefile", dummy_makefile($srcdir).join(""))
else
target = "io/wait"
- have_func("rb_io_wait")
- have_func("rb_io_descriptor")
+ have_func("rb_io_wait", "ruby/io.h")
+ have_func("rb_io_descriptor", "ruby/io.h")
unless macro_defined?("DOSISH", "#include <ruby.h>")
have_header(ioctl_h = "sys/ioctl.h") or ioctl_h = nil
fionread = %w[sys/ioctl.h sys/filio.h sys/socket.h].find do |h|
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index a71acfbb76..6d78284bc4 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -867,7 +867,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
if (klass == rb_cString) {
key_to_s = key;
} else if (klass == rb_cSymbol) {
- key_to_s = rb_id2str(SYM2ID(key));
+ key_to_s = rb_sym2str(key);
} else {
key_to_s = rb_funcall(key, i_to_s, 0);
}
@@ -892,7 +892,6 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
struct hash_foreach_arg arg;
if (max_nesting != 0 && depth > max_nesting) {
- fbuffer_free(buffer);
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
}
fbuffer_append_char(buffer, '{');
@@ -927,7 +926,6 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
long depth = ++state->depth;
int i, j;
if (max_nesting != 0 && depth > max_nesting) {
- fbuffer_free(buffer);
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
}
fbuffer_append_char(buffer, '[');
@@ -1020,10 +1018,8 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
VALUE tmp = rb_funcall(obj, i_to_s, 0);
if (!allow_nan) {
if (isinf(value)) {
- fbuffer_free(buffer);
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
} else if (isnan(value)) {
- fbuffer_free(buffer);
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
}
}
@@ -1096,11 +1092,45 @@ static FBuffer *cState_prepare_buffer(VALUE self)
return buffer;
}
+struct generate_json_data {
+ FBuffer *buffer;
+ VALUE vstate;
+ JSON_Generator_State *state;
+ VALUE obj;
+};
+
+static VALUE generate_json_try(VALUE d)
+{
+ struct generate_json_data *data = (struct generate_json_data *)d;
+
+ generate_json(data->buffer, data->vstate, data->state, data->obj);
+
+ return Qnil;
+}
+
+static VALUE generate_json_rescue(VALUE d, VALUE exc)
+{
+ struct generate_json_data *data = (struct generate_json_data *)d;
+ fbuffer_free(data->buffer);
+
+ rb_exc_raise(exc);
+
+ return Qundef;
+}
+
static VALUE cState_partial_generate(VALUE self, VALUE obj)
{
FBuffer *buffer = cState_prepare_buffer(self);
GET_STATE(self);
- generate_json(buffer, self, state, obj);
+
+ struct generate_json_data data = {
+ .buffer = buffer,
+ .vstate = self,
+ .state = state,
+ .obj = obj
+ };
+ rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
+
return fbuffer_to_s(buffer);
}
diff --git a/ext/json/lib/json/add/ostruct.rb b/ext/json/lib/json/add/ostruct.rb
index 498de17178..1e6f408248 100644
--- a/ext/json/lib/json/add/ostruct.rb
+++ b/ext/json/lib/json/add/ostruct.rb
@@ -2,7 +2,10 @@
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
-require 'ostruct'
+begin
+ require 'ostruct'
+rescue LoadError
+end
class OpenStruct
@@ -48,4 +51,4 @@ class OpenStruct
def to_json(*args)
as_json.to_json(*args)
end
-end
+end if defined?(::OpenStruct)
diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb
index 090066012d..95098d3bb4 100644
--- a/ext/json/lib/json/common.rb
+++ b/ext/json/lib/json/common.rb
@@ -1,8 +1,9 @@
#frozen_string_literal: false
require 'json/version'
-require 'json/generic_object'
module JSON
+ autoload :GenericObject, 'json/generic_object'
+
NOT_SET = Object.new.freeze
private_constant :NOT_SET
diff --git a/ext/json/lib/json/generic_object.rb b/ext/json/lib/json/generic_object.rb
index 108309db26..56efda6495 100644
--- a/ext/json/lib/json/generic_object.rb
+++ b/ext/json/lib/json/generic_object.rb
@@ -1,5 +1,9 @@
#frozen_string_literal: false
-require 'ostruct'
+begin
+ require 'ostruct'
+rescue LoadError
+ warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`."
+end
module JSON
class GenericObject < OpenStruct
@@ -67,5 +71,5 @@ module JSON
def to_json(*a)
as_json.to_json(*a)
end
- end
+ end if defined?(::OpenStruct)
end
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
index b43ceecdcd..836f47edf4 100644
--- a/ext/json/lib/json/version.rb
+++ b/ext/json/lib/json/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module JSON
# JSON version
- VERSION = '2.7.1'
+ VERSION = '2.7.2'
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
diff --git a/ext/json/parser/extconf.rb b/ext/json/parser/extconf.rb
index feb586e1b4..4723a02aee 100644
--- a/ext/json/parser/extconf.rb
+++ b/ext/json/parser/extconf.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: false
require 'mkmf'
-have_func("rb_enc_raise", "ruby.h")
-have_func("rb_enc_interned_str", "ruby.h")
+have_func("rb_enc_raise", "ruby/encoding.h")
+have_func("rb_enc_interned_str", "ruby/encoding.h")
# checking if String#-@ (str_uminus) dedupes... '
begin
diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c
index 866a49eff4..1812ba1d08 100644
--- a/ext/objspace/objspace_dump.c
+++ b/ext/objspace/objspace_dump.c
@@ -547,9 +547,8 @@ dump_object(VALUE obj, struct dump_config *dc)
if (dc->cur_obj_klass) {
VALUE mod_name = rb_mod_name(obj);
if (!NIL_P(mod_name)) {
- dump_append(dc, ", \"name\":\"");
- dump_append(dc, RSTRING_PTR(mod_name));
- dump_append(dc, "\"");
+ dump_append(dc, ", \"name\":");
+ dump_append_string_value(dc, mod_name);
}
else {
VALUE real_mod_name = rb_mod_name(rb_class_real(obj));
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 56f4a1c3ab..7a768867f8 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -47,7 +47,7 @@ Logging::message "=== OpenSSL for Ruby configurator ===\n"
$defs.push("-D""OPENSSL_SUPPRESS_DEPRECATED")
-have_func("rb_io_descriptor")
+have_func("rb_io_descriptor", "ruby/io.h")
have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1
Logging::message "=== Checking for system dependent stuff... ===\n"
diff --git a/ext/ripper/lib/ripper/lexer.rb b/ext/ripper/lib/ripper/lexer.rb
index 6a3c04af30..a0f1cbeaa8 100644
--- a/ext/ripper/lib/ripper/lexer.rb
+++ b/ext/ripper/lib/ripper/lexer.rb
@@ -242,7 +242,12 @@ class Ripper
end
def on_error2(mesg, elem)
- @errors.push Elem.new(elem.pos, __callee__, elem.tok, elem.state, mesg)
+ if elem
+ elem = Elem.new(elem.pos, __callee__, elem.tok, elem.state, mesg)
+ else
+ elem = Elem.new([lineno(), column()], __callee__, token(), state(), mesg)
+ end
+ @errors.push elem
end
PARSER_EVENTS.grep(/_error\z/) do |e|
arity = PARSER_EVENT_TABLE.fetch(e)
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index 4e8536fc60..d44ce31b0a 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -607,8 +607,6 @@ You can try --enable-wide-getaddrinfo.
EOS
end
- have_const('AI_ADDRCONFIG', headers)
-
case with_config("lookup-order-hack", "UNSPEC")
when "INET"
$defs << "-DLOOKUP_ORDER_HACK_INET"
diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c
index 0a693655b4..0c13620258 100644
--- a/ext/socket/ipsocket.c
+++ b/ext/socket/ipsocket.c
@@ -54,22 +54,15 @@ init_inetsock_internal(VALUE v)
VALUE connect_timeout = arg->connect_timeout;
struct timeval tv_storage;
struct timeval *tv = NULL;
- int remote_addrinfo_hints = 0;
if (!NIL_P(connect_timeout)) {
tv_storage = rb_time_interval(connect_timeout);
tv = &tv_storage;
}
- if (type == INET_SERVER) {
- remote_addrinfo_hints |= AI_PASSIVE;
- }
-#ifdef HAVE_CONST_AI_ADDRCONFIG
- remote_addrinfo_hints |= AI_ADDRCONFIG;
-#endif
-
arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv,
- family, SOCK_STREAM, remote_addrinfo_hints);
+ family, SOCK_STREAM,
+ (type == INET_SERVER) ? AI_PASSIVE : 0);
/*
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index 560312741f..909c02752c 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -277,8 +277,9 @@ numeric_getaddrinfo(const char *node, const char *service,
void
rb_freeaddrinfo(struct rb_addrinfo *ai)
{
- if (!ai->allocated_by_malloc)
- freeaddrinfo(ai->ai);
+ if (!ai->allocated_by_malloc) {
+ if (ai->ai) freeaddrinfo(ai->ai);
+ }
else {
struct addrinfo *ai1, *ai2;
ai1 = ai->ai;
@@ -345,7 +346,7 @@ struct getaddrinfo_arg
char *node, *service;
struct addrinfo hints;
struct addrinfo *ai;
- int err, refcount, done, cancelled;
+ int err, gai_errno, refcount, done, cancelled;
rb_nativethread_lock_t lock;
rb_nativethread_cond_t cond;
};
@@ -406,8 +407,9 @@ do_getaddrinfo(void *ptr)
{
struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr;
- int err;
+ int err, gai_errno;
err = getaddrinfo(arg->node, arg->service, &arg->hints, &arg->ai);
+ gai_errno = errno;
#ifdef __linux__
/* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and
* it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420]
@@ -420,8 +422,9 @@ do_getaddrinfo(void *ptr)
rb_nativethread_lock_lock(&arg->lock);
{
arg->err = err;
+ arg->gai_errno = gai_errno;
if (arg->cancelled) {
- freeaddrinfo(arg->ai);
+ if (arg->ai) freeaddrinfo(arg->ai);
}
else {
arg->done = 1;
@@ -479,7 +482,7 @@ rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hint
{
int retry;
struct getaddrinfo_arg *arg;
- int err;
+ int err, gai_errno = 0;
start:
retry = 0;
@@ -503,10 +506,11 @@ start:
{
if (arg->done) {
err = arg->err;
+ gai_errno = arg->gai_errno;
if (err == 0) *ai = arg->ai;
}
else if (arg->cancelled) {
- err = EAI_AGAIN;
+ retry = 1;
}
else {
// If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getaddrinfo.
@@ -525,6 +529,10 @@ start:
rb_thread_check_ints();
if (retry) goto start;
+ /* Because errno is threadlocal, the errno value we got from the call to getaddrinfo() in the thread
+ * (in case of EAI_SYSTEM return value) is not propagated to the caller of _this_ function. Set errno
+ * explicitly, as round-tripped through struct getaddrinfo_arg, to deal with that */
+ if (gai_errno) errno = gai_errno;
return err;
}
@@ -591,7 +599,7 @@ struct getnameinfo_arg
size_t hostlen;
char *serv;
size_t servlen;
- int err, refcount, done, cancelled;
+ int err, gni_errno, refcount, done, cancelled;
rb_nativethread_lock_t lock;
rb_nativethread_cond_t cond;
};
@@ -644,12 +652,14 @@ do_getnameinfo(void *ptr)
{
struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr;
- int err;
+ int err, gni_errno;
err = getnameinfo(arg->sa, arg->salen, arg->host, (socklen_t)arg->hostlen, arg->serv, (socklen_t)arg->servlen, arg->flags);
+ gni_errno = errno;
int need_free = 0;
rb_nativethread_lock_lock(&arg->lock);
arg->err = err;
+ arg->gni_errno = gni_errno;
if (!arg->cancelled) {
arg->done = 1;
rb_native_cond_signal(&arg->cond);
@@ -691,7 +701,7 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
{
int retry;
struct getnameinfo_arg *arg;
- int err;
+ int err, gni_errno = 0;
start:
retry = 0;
@@ -714,13 +724,14 @@ start:
rb_nativethread_lock_lock(&arg->lock);
if (arg->done) {
err = arg->err;
+ gni_errno = arg->gni_errno;
if (err == 0) {
if (host) memcpy(host, arg->host, hostlen);
if (serv) memcpy(serv, arg->serv, servlen);
}
}
else if (arg->cancelled) {
- err = EAI_AGAIN;
+ retry = 1;
}
else {
// If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getnameinfo.
@@ -738,6 +749,9 @@ start:
rb_thread_check_ints();
if (retry) goto start;
+ /* Make sure we copy the thread-local errno value from the getnameinfo thread back to this thread, so
+ * calling code sees the correct errno */
+ if (gni_errno) errno = gni_errno;
return err;
}
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 283735b12c..920e276578 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -290,8 +290,8 @@ extern VALUE rb_eResolution;
#ifdef SOCKS
extern VALUE rb_cSOCKSSocket;
# ifndef SOCKS5
-void SOCKSinit();
-int Rconnect();
+void SOCKSinit(char *);
+int Rconnect(int, const struct sockaddr *, socklen_t);
# endif
#endif
diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c
index 7eade5bcba..74e2b95c99 100644
--- a/ext/stringio/stringio.c
+++ b/ext/stringio/stringio.c
@@ -13,7 +13,7 @@
**********************************************************************/
static const char *const
-STRINGIO_VERSION = "3.1.0";
+STRINGIO_VERSION = "3.1.1";
#include "ruby.h"
#include "ruby/io.h"
diff --git a/ext/strscan/extconf.rb b/ext/strscan/extconf.rb
index bd65606a4e..3c311d2364 100644
--- a/ext/strscan/extconf.rb
+++ b/ext/strscan/extconf.rb
@@ -2,8 +2,8 @@
require 'mkmf'
if RUBY_ENGINE == 'ruby'
$INCFLAGS << " -I$(top_srcdir)" if $extmk
- have_func("onig_region_memsize", "ruby.h")
- have_func("rb_reg_onig_match", "ruby.h")
+ have_func("onig_region_memsize(NULL)")
+ have_func("rb_reg_onig_match", "ruby/re.h")
create_makefile 'strscan'
else
File.write('Makefile', dummy_makefile("").join)
diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c
index 16d669d8a5..4598d13c90 100644
--- a/ext/strscan/strscan.c
+++ b/ext/strscan/strscan.c
@@ -22,7 +22,7 @@ extern size_t onig_region_memsize(const struct re_registers *regs);
#include <stdbool.h>
-#define STRSCAN_VERSION "3.0.7"
+#define STRSCAN_VERSION "3.0.9"
/* =======================================================================
Data Type Definitions
@@ -1243,10 +1243,10 @@ strscan_size(VALUE self)
* If nothing was priorly matched, it returns nil.
*
* s = StringScanner.new("Fri Dec 12 1975 14:39")
- * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 "
- * s.captures # -> ["Fri", "Dec", "12"]
- * s.scan(/(\w+) (\w+) (\d+) /) # -> nil
- * s.captures # -> nil
+ * s.scan(/(\w+) (\w+) (\d+) (1980)?/) # -> "Fri Dec 12 "
+ * s.captures # -> ["Fri", "Dec", "12", nil]
+ * s.scan(/(\w+) (\w+) (\d+) (1980)?/) # -> nil
+ * s.captures # -> nil
*/
static VALUE
strscan_captures(VALUE self)
@@ -1262,9 +1262,13 @@ strscan_captures(VALUE self)
new_ary = rb_ary_new2(num_regs);
for (i = 1; i < num_regs; i++) {
- VALUE str = extract_range(p,
- adjust_register_position(p, p->regs.beg[i]),
- adjust_register_position(p, p->regs.end[i]));
+ VALUE str;
+ if (p->regs.beg[i] == -1)
+ str = Qnil;
+ else
+ str = extract_range(p,
+ adjust_register_position(p, p->regs.beg[i]),
+ adjust_register_position(p, p->regs.end[i]));
rb_ary_push(new_ary, str);
}
diff --git a/ext/win32/lib/win32/registry.rb b/ext/win32/lib/win32/registry.rb
index b5b99ff684..bda8bb012f 100644
--- a/ext/win32/lib/win32/registry.rb
+++ b/ext/win32/lib/win32/registry.rb
@@ -69,7 +69,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
WCHAR_NUL = "\0".encode(WCHAR).freeze
WCHAR_CR = "\r".encode(WCHAR).freeze
WCHAR_SIZE = WCHAR_NUL.bytesize
- LOCALE = Encoding.find(Encoding.locale_charmap)
+ begin
+ LOCALE = Encoding.find(Encoding.locale_charmap)
+ rescue ArgumentError
+ LOCALE = Encoding::UTF_8
+ end
class Registry
@@ -740,14 +744,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
# method returns.
#
def write(name, type, data)
- termsize = 0
case type
when REG_SZ, REG_EXPAND_SZ
- data = data.encode(WCHAR)
- termsize = WCHAR_SIZE
+ data = data.encode(WCHAR) << WCHAR_NUL
when REG_MULTI_SZ
data = data.to_a.map {|s| s.encode(WCHAR)}.join(WCHAR_NUL) << WCHAR_NUL
- termsize = WCHAR_SIZE
when REG_BINARY, REG_NONE
data = data.to_s
when REG_DWORD
@@ -759,7 +760,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
else
raise TypeError, "Unsupported type #{Registry.type2name(type)}"
end
- API.SetValue(@hkey, name, type, data, data.bytesize + termsize)
+ API.SetValue(@hkey, name, type, data, data.bytesize)
end
#
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index dc608ee460..3db4d25271 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -25,7 +25,7 @@
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
#endif
-#define RUBY_ZLIB_VERSION "3.1.0"
+#define RUBY_ZLIB_VERSION "3.1.1"
#ifndef RB_PASS_CALLED_KEYWORDS
# define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass)
@@ -3500,6 +3500,9 @@ static VALUE
rb_gzfile_eof_p(VALUE obj)
{
struct gzfile *gz = get_gzfile(obj);
+ while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) == 0) {
+ gzfile_read_more(gz, Qnil);
+ }
return GZFILE_IS_FINISHED(gz) ? Qtrue : Qfalse;
}
diff --git a/gc.c b/gc.c
index 6419f8ff25..0346812f42 100644
--- a/gc.c
+++ b/gc.c
@@ -213,7 +213,7 @@ rb_malloc_grow_capa(size_t current, size_t type_size)
new_capacity -= malloc_offset;
new_capacity /= type_size;
if (current > new_capacity) {
- rb_bug("rb_malloc_grow_capa: current_capacity=%zu, new_capacity=%zu, malloc_offset=%zu", current, new_capacity, malloc_offset);
+ rb_bug("rb_malloc_grow_capa: current_capacity=%"PRIuSIZE", new_capacity=%"PRIuSIZE", malloc_offset=%"PRIuSIZE"", current, new_capacity, malloc_offset);
}
RUBY_ASSERT(new_capacity > current);
return new_capacity;
@@ -3491,7 +3491,7 @@ rb_data_free(rb_objspace_t *objspace, VALUE obj)
if (dfree) {
if (dfree == RUBY_DEFAULT_FREE) {
- if (!RTYPEDDATA_EMBEDDED_P(obj)) {
+ if (!RTYPEDDATA_P(obj) || !RTYPEDDATA_EMBEDDED_P(obj)) {
xfree(data);
RB_DEBUG_COUNTER_INC(obj_data_xfree);
}
@@ -3791,6 +3791,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
case imemo_callinfo:
{
const struct rb_callinfo * ci = ((const struct rb_callinfo *)obj);
+ rb_vm_ci_free(ci);
if (ci->kwarg) {
((struct rb_callinfo_kwarg *)ci->kwarg)->references--;
if (ci->kwarg->references == 0) xfree((void *)ci->kwarg);
@@ -13962,7 +13963,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
{
if (rb_shape_obj_too_complex(obj)) {
size_t hash_len = rb_st_table_size(ROBJECT_IV_HASH(obj));
- APPEND_F("(too_complex) len:%zu", hash_len);
+ APPEND_F("(too_complex) len:%"PRIuSIZE"", hash_len);
}
else {
uint32_t len = ROBJECT_IV_CAPACITY(obj);
diff --git a/gems/bundled_gems b/gems/bundled_gems
index ae0a1acc3d..42a167f771 100644
--- a/gems/bundled_gems
+++ b/gems/bundled_gems
@@ -9,15 +9,15 @@ minitest 5.20.0 https://github.com/minitest/minitest
power_assert 2.0.3 https://github.com/ruby/power_assert
rake 13.1.0 https://github.com/ruby/rake
test-unit 3.6.1 https://github.com/test-unit/test-unit
-rexml 3.2.6 https://github.com/ruby/rexml
-rss 0.3.0 https://github.com/ruby/rss
+rexml 3.3.9 https://github.com/ruby/rexml
+rss 0.3.1 https://github.com/ruby/rss
net-ftp 0.3.4 https://github.com/ruby/net-ftp
-net-imap 0.4.9.1 https://github.com/ruby/net-imap
+net-imap 0.4.21 https://github.com/ruby/net-imap
net-pop 0.1.2 https://github.com/ruby/net-pop
-net-smtp 0.4.0.1 https://github.com/ruby/net-smtp
+net-smtp 0.5.1 https://github.com/ruby/net-smtp
matrix 0.4.2 https://github.com/ruby/matrix
prime 0.1.2 https://github.com/ruby/prime
rbs 3.4.0 https://github.com/ruby/rbs
typeprof 0.21.9 https://github.com/ruby/typeprof
-debug 1.9.1 https://github.com/ruby/debug
+debug 1.9.2 https://github.com/ruby/debug
racc 1.7.3 https://github.com/ruby/racc
diff --git a/hash.c b/hash.c
index b15d856ee1..2142132597 100644
--- a/hash.c
+++ b/hash.c
@@ -377,6 +377,9 @@ const struct st_hash_type rb_hashtype_ident = {
rb_ident_hash,
};
+#define RHASH_IDENTHASH_P(hash) (RHASH_TYPE(hash) == &identhash)
+#define RHASH_STRING_KEY_P(hash, key) (!RHASH_IDENTHASH_P(hash) && (rb_obj_class(key) == rb_cString))
+
typedef st_index_t st_hash_t;
/*
@@ -857,7 +860,7 @@ ar_general_foreach(VALUE hash, st_foreach_check_callback_func *func, st_update_c
if (replace) {
VALUE key = pair->key;
VALUE val = pair->val;
- retval = (*replace)(&key, &val, arg, TRUE);
+ (*replace)(&key, &val, arg, TRUE);
// TODO: pair should be same as pair before.
ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
@@ -928,7 +931,7 @@ ar_foreach_check(VALUE hash, st_foreach_check_callback_func *func, st_data_t arg
if (pair->key == never) break;
ret = ar_find_entry_hint(hash, hint, key);
if (ret == RHASH_AR_TABLE_MAX_BOUND) {
- retval = (*func)(0, 0, arg, 1);
+ (*func)(0, 0, arg, 1);
return 2;
}
}
@@ -1388,6 +1391,7 @@ hash_foreach_ensure(VALUE hash)
return 0;
}
+/* This does not manage iteration level */
int
rb_hash_stlike_foreach(VALUE hash, st_foreach_callback_func *func, st_data_t arg)
{
@@ -1399,6 +1403,7 @@ rb_hash_stlike_foreach(VALUE hash, st_foreach_callback_func *func, st_data_t arg
}
}
+/* This does not manage iteration level */
int
rb_hash_stlike_foreach_with_replace(VALUE hash, st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg)
{
@@ -1705,14 +1710,14 @@ tbl_update(VALUE hash, VALUE key, tbl_update_func func, st_data_t optional_arg)
.func = func,
.hash = hash,
.key = key,
- .value = (VALUE)optional_arg,
+ .value = 0
};
int ret = rb_hash_stlike_update(hash, key, tbl_update_modify, (st_data_t)&arg);
/* write barrier */
RB_OBJ_WRITTEN(hash, Qundef, arg.key);
- RB_OBJ_WRITTEN(hash, Qundef, arg.value);
+ if (arg.value) RB_OBJ_WRITTEN(hash, Qundef, arg.value);
return ret;
}
@@ -2939,7 +2944,7 @@ rb_hash_aset(VALUE hash, VALUE key, VALUE val)
rb_hash_modify(hash);
- if (RHASH_TYPE(hash) == &identhash || rb_obj_class(key) != rb_cString) {
+ if (!RHASH_STRING_KEY_P(hash, key)) {
RHASH_UPDATE_ITER(hash, iter_p, key, hash_aset, val);
}
else {
@@ -3323,6 +3328,20 @@ transform_values_foreach_replace(st_data_t *key, st_data_t *value, st_data_t arg
return ST_CONTINUE;
}
+static VALUE
+transform_values_call(VALUE hash)
+{
+ rb_hash_stlike_foreach_with_replace(hash, transform_values_foreach_func, transform_values_foreach_replace, hash);
+ return hash;
+}
+
+static void
+transform_values(VALUE hash)
+{
+ hash_iter_lev_inc(hash);
+ rb_ensure(transform_values_call, hash, hash_foreach_ensure, hash);
+}
+
/*
* call-seq:
* hash.transform_values {|value| ... } -> new_hash
@@ -3353,7 +3372,7 @@ rb_hash_transform_values(VALUE hash)
SET_DEFAULT(result, Qnil);
if (!RHASH_EMPTY_P(hash)) {
- rb_hash_stlike_foreach_with_replace(result, transform_values_foreach_func, transform_values_foreach_replace, result);
+ transform_values(result);
compact_after_delete(result);
}
@@ -3382,7 +3401,7 @@ rb_hash_transform_values_bang(VALUE hash)
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
- rb_hash_stlike_foreach_with_replace(hash, transform_values_foreach_func, transform_values_foreach_replace, hash);
+ transform_values(hash);
}
return hash;
@@ -3887,42 +3906,76 @@ rb_hash_invert(VALUE hash)
}
static int
-rb_hash_update_callback(st_data_t *key, st_data_t *value, struct update_arg *arg, int existing)
-{
- *value = arg->arg;
- return ST_CONTINUE;
-}
-
-NOINSERT_UPDATE_CALLBACK(rb_hash_update_callback)
-
-static int
rb_hash_update_i(VALUE key, VALUE value, VALUE hash)
{
- RHASH_UPDATE(hash, key, rb_hash_update_callback, value);
+ rb_hash_aset(hash, key, value);
return ST_CONTINUE;
}
+struct update_call_args {
+ VALUE hash, newvalue, *argv;
+ int argc;
+ bool block_given;
+ bool iterating;
+};
+
static int
rb_hash_update_block_callback(st_data_t *key, st_data_t *value, struct update_arg *arg, int existing)
{
- st_data_t newvalue = arg->arg;
+ VALUE k = (VALUE)*key, v = (VALUE)*value;
+ struct update_call_args *ua = (void *)arg->arg;
+ VALUE newvalue = ua->newvalue, hash = arg->hash;
if (existing) {
- newvalue = (st_data_t)rb_yield_values(3, (VALUE)*key, (VALUE)*value, (VALUE)newvalue);
+ hash_iter_lev_inc(hash);
+ ua->iterating = true;
+ newvalue = rb_yield_values(3, k, v, newvalue);
+ hash_iter_lev_dec(hash);
+ ua->iterating = false;
}
- *value = newvalue;
+ else if (RHASH_STRING_KEY_P(hash, k) && !RB_OBJ_FROZEN(k)) {
+ *key = (st_data_t)rb_hash_key_str(k);
+ }
+ *value = (st_data_t)newvalue;
return ST_CONTINUE;
}
NOINSERT_UPDATE_CALLBACK(rb_hash_update_block_callback)
static int
-rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash)
+rb_hash_update_block_i(VALUE key, VALUE value, VALUE args)
{
- RHASH_UPDATE(hash, key, rb_hash_update_block_callback, value);
+ struct update_call_args *ua = (void *)args;
+ ua->newvalue = value;
+ RHASH_UPDATE(ua->hash, key, rb_hash_update_block_callback, args);
return ST_CONTINUE;
}
+static VALUE
+rb_hash_update_call(VALUE args)
+{
+ struct update_call_args *arg = (void *)args;
+
+ for (int i = 0; i < arg->argc; i++){
+ VALUE hash = to_hash(arg->argv[i]);
+ if (arg->block_given) {
+ rb_hash_foreach(hash, rb_hash_update_block_i, args);
+ }
+ else {
+ rb_hash_foreach(hash, rb_hash_update_i, arg->hash);
+ }
+ }
+ return arg->hash;
+}
+
+static VALUE
+rb_hash_update_ensure(VALUE args)
+{
+ struct update_call_args *ua = (void *)args;
+ if (ua->iterating) hash_iter_lev_dec(ua->hash);
+ return Qnil;
+}
+
/*
* call-seq:
* hash.merge! -> self
@@ -3974,20 +4027,17 @@ rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash)
static VALUE
rb_hash_update(int argc, VALUE *argv, VALUE self)
{
- int i;
- bool block_given = rb_block_given_p();
+ struct update_call_args args = {
+ .hash = self,
+ .argv = argv,
+ .argc = argc,
+ .block_given = rb_block_given_p(),
+ .iterating = false,
+ };
+ VALUE arg = (VALUE)&args;
rb_hash_modify(self);
- for (i = 0; i < argc; i++){
- VALUE hash = to_hash(argv[i]);
- if (block_given) {
- rb_hash_foreach(hash, rb_hash_update_block_i, self);
- }
- else {
- rb_hash_foreach(hash, rb_hash_update_i, self);
- }
- }
- return self;
+ return rb_ensure(rb_hash_update_call, arg, rb_hash_update_ensure, arg);
}
struct update_func_arg {
@@ -4146,7 +4196,7 @@ rb_hash_assoc(VALUE hash, VALUE key)
if (RHASH_EMPTY_P(hash)) return Qnil;
- if (RHASH_ST_TABLE_P(hash) && RHASH_ST_TABLE(hash)->type != &identhash) {
+ if (RHASH_ST_TABLE_P(hash) && !RHASH_IDENTHASH_P(hash)) {
VALUE value = Qundef;
st_table assoctable = *RHASH_ST_TABLE(hash);
assoctable.type = &(struct st_hash_type){
@@ -4423,7 +4473,7 @@ rb_hash_compare_by_id(VALUE hash)
VALUE
rb_hash_compare_by_id_p(VALUE hash)
{
- return RBOOL(RHASH_ST_TABLE_P(hash) && RHASH_ST_TABLE(hash)->type == &identhash);
+ return RBOOL(RHASH_IDENTHASH_P(hash));
}
VALUE
@@ -4908,7 +4958,7 @@ env_name(volatile VALUE *s)
static VALUE env_aset(VALUE nm, VALUE val);
static void
-reset_by_modified_env(const char *nam)
+reset_by_modified_env(const char *nam, const char *val)
{
/*
* ENV['TZ'] = nil has a special meaning.
@@ -4917,7 +4967,7 @@ reset_by_modified_env(const char *nam)
* This hack might works only on Linux glibc.
*/
if (ENVMATCH(nam, TZ_ENV)) {
- ruby_reset_timezone();
+ ruby_reset_timezone(val);
}
}
@@ -4925,7 +4975,7 @@ static VALUE
env_delete(VALUE name)
{
const char *nam = env_name(name);
- reset_by_modified_env(nam);
+ reset_by_modified_env(nam, NULL);
VALUE val = getenv_with_lock(nam);
if (!NIL_P(val)) {
@@ -5383,7 +5433,7 @@ env_aset(VALUE nm, VALUE val)
get_env_ptr(value, val);
ruby_setenv(name, value);
- reset_by_modified_env(name);
+ reset_by_modified_env(name, value);
return val;
}
@@ -5934,24 +5984,23 @@ env_to_s(VALUE _)
static VALUE
env_inspect(VALUE _)
{
- VALUE i;
VALUE str = rb_str_buf_new2("{");
+ rb_encoding *enc = env_encoding();
ENV_LOCK();
{
char **env = GET_ENVIRON(environ);
while (*env) {
- char *s = strchr(*env, '=');
+ const char *s = strchr(*env, '=');
if (env != environ) {
rb_str_buf_cat2(str, ", ");
}
if (s) {
- rb_str_buf_cat2(str, "\"");
- rb_str_buf_cat(str, *env, s-*env);
- rb_str_buf_cat2(str, "\"=>");
- i = rb_inspect(rb_str_new2(s+1));
- rb_str_buf_append(str, i);
+ rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(*env, s-*env, enc)));
+ rb_str_buf_cat2(str, "=>");
+ s++;
+ rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(s, strlen(s), enc)));
}
env++;
}
diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h
index e3e1b6166d..e4c6d155cc 100644
--- a/include/ruby/internal/anyargs.h
+++ b/include/ruby/internal/anyargs.h
@@ -84,12 +84,15 @@
#elif defined(_WIN32) || defined(__CYGWIN__)
# /* Skip due to [Bug #16134] */
+# define RBIMPL_CAST_FN_PTR 1
#elif ! RBIMPL_HAS_ATTRIBUTE(transparent_union)
# /* :TODO: improve here, please find a way to support. */
+# define RBIMPL_CAST_FN_PTR 1
#elif ! defined(HAVE_VA_ARGS_MACRO)
# /* :TODO: improve here, please find a way to support. */
+# define RBIMPL_CAST_FN_PTR 1
#else
# /** @cond INTERNAL_MACRO */
@@ -348,6 +351,25 @@ RBIMPL_ANYARGS_DECL(rb_define_method, VALUE, const char *)
#endif /* __cplusplus */
+#if defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus)
+/* In C23, K&R style prototypes are gone and so `void foo(ANYARGS)` became
+ * equivalent to `void foo(void)` unlike in earlier versions. This is a problem
+ * for rb_define_* functions since that makes all valid functions one can pass
+ * trip -Wincompatible-pointer-types, which we treat as errors. This is mostly
+ * not a problem for the __builtin_choose_expr path, but outside of that we
+ * need to add a cast for compatibility.
+ */
+#define rb_define_method(klass, mid, func, arity) rb_define_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_method_id(klass, mid, func, arity) rb_define_method_id((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_singleton_method(obj, mid, func, arity) rb_define_singleton_method((obj), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_protected_method(klass, mid, func, arity) rb_define_protected_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_private_method(klass, mid, func, arity) rb_define_private_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_module_function(mod, mid, func, arity) rb_define_module_function((mod), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_global_function(mid, func, arity) rb_define_global_function((mid), (VALUE (*)(ANYARGS))(func), (arity))
+
+#undef RBIMPL_CAST_FN_PTR
+#endif /* defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus) */
+
/**
* This macro is to properly cast a function parameter of *_define_method
* family. It has been around since 1.x era so you can maximise backwards
diff --git a/include/ruby/internal/attr/nonstring.h b/include/ruby/internal/attr/nonstring.h
new file mode 100644
index 0000000000..de26e926d4
--- /dev/null
+++ b/include/ruby/internal/attr/nonstring.h
@@ -0,0 +1,32 @@
+#ifndef RBIMPL_ATTR_NONSTRING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NONSTRING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NONSTRING.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((nonstring))` */
+#if RBIMPL_HAS_ATTRIBUTE(nonstring)
+# define RBIMPL_ATTR_NONSTRING() __attribute__((nonstring))
+#else
+# define RBIMPL_ATTR_NONSTRING() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NONSTRING_H */
diff --git a/include/ruby/internal/stdbool.h b/include/ruby/internal/stdbool.h
index 1ca61136ba..cfe73437a2 100644
--- a/include/ruby/internal/stdbool.h
+++ b/include/ruby/internal/stdbool.h
@@ -35,17 +35,9 @@
# define __bool_true_false_are_defined
# endif
-#elif defined(HAVE_STDBOOL_H)
-# /* Take stdbool.h definition. */
+#else
+# /* Take stdbool.h definition. It exists since GCC 3.0 and VS 2015. */
# include <stdbool.h>
-
-#elif !defined(HAVE__BOOL)
-typedef unsigned char _Bool;
-# /* See also http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2229.htm */
-# define bool _Bool
-# define true ((_Bool)+1)
-# define false ((_Bool)+0)
-# define __bool_true_false_are_defined
#endif
#endif /* RBIMPL_STDBOOL_H */
diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h
index b044db0539..e4d98bf051 100644
--- a/include/ruby/io/buffer.h
+++ b/include/ruby/io/buffer.h
@@ -69,14 +69,10 @@ enum rb_io_buffer_endian {
RB_IO_BUFFER_LITTLE_ENDIAN = 4,
RB_IO_BUFFER_BIG_ENDIAN = 8,
-#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
-#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#if defined(WORDS_BIGENDIAN)
RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
-#elif defined(REG_DWORD) && REG_DWORD == REG_DWORD_LITTLE_ENDIAN
+#else
RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
-#elif defined(REG_DWORD) && REG_DWORD == REG_DWORD_BIG_ENDIAN
- RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
#endif
RB_IO_BUFFER_NETWORK_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN
diff --git a/include/ruby/onigmo.h b/include/ruby/onigmo.h
index d233336316..db290cd47a 100644
--- a/include/ruby/onigmo.h
+++ b/include/ruby/onigmo.h
@@ -636,6 +636,7 @@ ONIG_EXTERN const OnigSyntaxType* OnigDefaultSyntax;
#define ONIGERR_PARSE_DEPTH_LIMIT_OVER -16
#define ONIGERR_DEFAULT_ENCODING_IS_NOT_SET -21
#define ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR -22
+#define ONIGERR_TIMEOUT -23
/* general error */
#define ONIGERR_INVALID_ARGUMENT -30
/* syntax error */
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index dfb56f4182..cfa31db130 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -125,8 +125,15 @@ typedef unsigned int uintptr_t;
#define O_SHARE_DELETE 0x20000000 /* for rb_w32_open(), rb_w32_wopen() */
typedef int clockid_t;
+#if defined(__MINGW32__)
+#undef CLOCK_PROCESS_CPUTIME_ID
+#undef CLOCK_THREAD_CPUTIME_ID
+#undef CLOCK_REALTIME_COARSE
+#endif
+#if defined(HAVE_CLOCK_GETTIME) && !defined(CLOCK_REALTIME)
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
+#endif
#undef utime
#undef lseek
diff --git a/internal/class.h b/internal/class.h
index fcdface028..594f1daea7 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -44,7 +44,7 @@ struct rb_classext_struct {
VALUE *iv_ptr;
struct rb_id_table *const_tbl;
struct rb_id_table *callable_m_tbl;
- struct rb_id_table *cc_tbl; /* ID -> [[ci, cc1], cc2, ...] */
+ struct rb_id_table *cc_tbl; /* ID -> [[ci1, cc1], [ci2, cc2] ...] */
struct rb_id_table *cvc_tbl;
size_t superclass_depth;
VALUE *superclasses;
@@ -175,6 +175,7 @@ void rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE);
void rb_class_detach_subclasses(VALUE);
void rb_class_detach_module_subclasses(VALUE);
void rb_class_remove_from_module_subclasses(VALUE);
+VALUE rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super);
VALUE rb_obj_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_protected_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_private_methods(int argc, const VALUE *argv, VALUE obj);
diff --git a/internal/eval.h b/internal/eval.h
index 73bb656d96..e594d8516d 100644
--- a/internal/eval.h
+++ b/internal/eval.h
@@ -21,6 +21,7 @@ extern ID ruby_static_id_status;
VALUE rb_refinement_module_get_refined_class(VALUE module);
void rb_class_modify_check(VALUE);
NORETURN(VALUE rb_f_raise(int argc, VALUE *argv));
+VALUE rb_top_main_class(const char *method);
/* eval_error.c */
VALUE rb_get_backtrace(VALUE info);
diff --git a/internal/object.h b/internal/object.h
index 06595bdd91..903e2d29a5 100644
--- a/internal/object.h
+++ b/internal/object.h
@@ -16,6 +16,8 @@ VALUE rb_class_search_ancestor(VALUE klass, VALUE super);
NORETURN(void rb_undefined_alloc(VALUE klass));
double rb_num_to_dbl(VALUE val);
VALUE rb_obj_dig(int argc, VALUE *argv, VALUE self, VALUE notfound);
+VALUE rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze);
+VALUE rb_obj_dup_setup(VALUE obj, VALUE dup);
VALUE rb_immutable_obj_clone(int, VALUE *, VALUE);
VALUE rb_check_convert_type_with_id(VALUE,int,const char*,ID);
int rb_bool_expected(VALUE, const char *, int raise);
diff --git a/internal/proc.h b/internal/proc.h
index c75f15b283..c6a9e38bfa 100644
--- a/internal/proc.h
+++ b/internal/proc.h
@@ -23,6 +23,7 @@ VALUE rb_block_to_s(VALUE self, const struct rb_block *block, const char *additi
VALUE rb_callable_receiver(VALUE);
VALUE rb_func_proc_new(rb_block_call_func_t func, VALUE val);
+VALUE rb_func_proc_dup(VALUE src_obj);
VALUE rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_argc);
VALUE rb_iseq_location(const struct rb_iseq_struct *iseq);
VALUE rb_sym_to_proc(VALUE sym);
diff --git a/internal/string.h b/internal/string.h
index 8c481f979e..ba2af25877 100644
--- a/internal/string.h
+++ b/internal/string.h
@@ -57,6 +57,7 @@ static inline VALUE rb_str_eql_internal(const VALUE str1, const VALUE str2);
RUBY_SYMBOL_EXPORT_BEGIN
/* string.c (export) */
VALUE rb_str_tmp_frozen_acquire(VALUE str);
+VALUE rb_str_tmp_frozen_no_embed_acquire(VALUE str);
void rb_str_tmp_frozen_release(VALUE str, VALUE tmp);
VALUE rb_setup_fake_str(struct RString *fake_str, const char *name, long len, rb_encoding *enc);
VALUE rb_str_upto_each(VALUE, VALUE, int, int (*each)(VALUE, VALUE), VALUE);
diff --git a/internal/thread.h b/internal/thread.h
index cf25975d8b..4ed542382b 100644
--- a/internal/thread.h
+++ b/internal/thread.h
@@ -57,6 +57,7 @@ int rb_thread_wait_for_single_fd(int fd, int events, struct timeval * timeout);
struct rb_io_close_wait_list {
struct ccan_list_head pending_fd_users;
VALUE closing_thread;
+ VALUE closing_fiber;
VALUE wakeup_mutex;
};
int rb_notify_fd_close(int fd, struct rb_io_close_wait_list *busy);
diff --git a/internal/time.h b/internal/time.h
index a3bf0587ec..e21b3574f6 100644
--- a/internal/time.h
+++ b/internal/time.h
@@ -28,7 +28,10 @@ struct timeval rb_time_timeval(VALUE);
RUBY_SYMBOL_EXPORT_BEGIN
/* time.c (export) */
void ruby_reset_leap_second_info(void);
-void ruby_reset_timezone(void);
+#ifdef RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY
+RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY()
+#endif
+void ruby_reset_timezone(const char *);
RUBY_SYMBOL_EXPORT_END
#endif /* INTERNAL_TIME_H */
diff --git a/io.c b/io.c
index ca2cb904ee..b4b262b6ac 100644
--- a/io.c
+++ b/io.c
@@ -1156,8 +1156,14 @@ io_internal_wait(VALUE thread, rb_io_t *fptr, int error, int events, struct time
return -1;
}
- errno = error;
- return -1;
+ // If there was an error BEFORE we started waiting, return it:
+ if (error) {
+ errno = error;
+ return -1;
+ } else {
+ // Otherwise, whatever error was generated by `nogvl_wait_for` is the one we want:
+ return ready;
+ }
}
static VALUE
@@ -1970,7 +1976,7 @@ io_fwrite(VALUE str, rb_io_t *fptr, int nosync)
if (converted)
OBJ_FREEZE(str);
- tmp = rb_str_tmp_frozen_acquire(str);
+ tmp = rb_str_tmp_frozen_no_embed_acquire(str);
RSTRING_GETMEM(tmp, ptr, len);
n = io_binwrite(tmp, ptr, len, fptr, nosync);
rb_str_tmp_frozen_release(str, tmp);
@@ -3820,8 +3826,33 @@ rscheck(const char *rsptr, long rslen, VALUE rs)
rb_raise(rb_eRuntimeError, "rs modified");
}
+static const char *
+search_delim(const char *p, long len, int delim, rb_encoding *enc)
+{
+ if (rb_enc_mbminlen(enc) == 1) {
+ p = memchr(p, delim, len);
+ if (p) return p + 1;
+ }
+ else {
+ const char *end = p + len;
+ while (p < end) {
+ int r = rb_enc_precise_mbclen(p, end, enc);
+ if (!MBCLEN_CHARFOUND_P(r)) {
+ p += rb_enc_mbminlen(enc);
+ continue;
+ }
+ int n = MBCLEN_CHARFOUND_LEN(r);
+ if (rb_enc_mbc_to_codepoint(p, end, enc) == (unsigned int)delim) {
+ return p + n;
+ }
+ p += n;
+ }
+ }
+ return NULL;
+}
+
static int
-appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
+appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp, rb_encoding *enc)
{
VALUE str = *strp;
long limit = *lp;
@@ -3836,9 +3867,9 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
p = READ_CHAR_PENDING_PTR(fptr);
if (0 < limit && limit < searchlen)
searchlen = (int)limit;
- e = memchr(p, delim, searchlen);
+ e = search_delim(p, searchlen, delim, enc);
if (e) {
- int len = (int)(e-p+1);
+ int len = (int)(e-p);
if (NIL_P(str))
*strp = str = rb_str_new(p, len);
else
@@ -3878,8 +3909,8 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp)
long last;
if (limit > 0 && pending > limit) pending = limit;
- e = memchr(p, delim, pending);
- if (e) pending = e - p + 1;
+ e = search_delim(p, pending, delim, enc);
+ if (e) pending = e - p;
if (!NIL_P(str)) {
last = RSTRING_LEN(str);
rb_str_resize(str, last + pending);
@@ -4139,16 +4170,26 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr)
rsptr = RSTRING_PTR(rs);
rslen = RSTRING_LEN(rs);
}
+ newline = '\n';
+ }
+ else if (rb_enc_mbminlen(enc) == 1) {
+ rsptr = RSTRING_PTR(rs);
+ newline = (unsigned char)rsptr[rslen - 1];
}
else {
+ rs = rb_str_encode(rs, rb_enc_from_encoding(enc), 0, Qnil);
rsptr = RSTRING_PTR(rs);
+ const char *e = rsptr + rslen;
+ const char *last = rb_enc_prev_char(rsptr, e, e, enc);
+ int n;
+ newline = rb_enc_codepoint_len(last, e, &n, enc);
+ if (last + n != e) rb_raise(rb_eArgError, "broken separator");
}
- newline = (unsigned char)rsptr[rslen - 1];
- chomp_cr = chomp && rslen == 1 && newline == '\n';
+ chomp_cr = chomp && newline == '\n' && rslen == rb_enc_mbminlen(enc);
}
/* MS - Optimization */
- while ((c = appendline(fptr, newline, &str, &limit)) != EOF) {
+ while ((c = appendline(fptr, newline, &str, &limit, enc)) != EOF) {
const char *s, *p, *pp, *e;
if (c == newline) {
@@ -4169,8 +4210,8 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr)
if (limit == 0) {
s = RSTRING_PTR(str);
p = RSTRING_END(str);
- pp = rb_enc_left_char_head(s, p-1, p, enc);
- if (extra_limit &&
+ pp = rb_enc_prev_char(s, p, p, enc);
+ if (extra_limit && pp &&
MBCLEN_NEEDMORE_P(rb_enc_precise_mbclen(pp, p, enc))) {
/* relax the limit while incomplete character.
* extra_limit limits the relax length */
@@ -4372,23 +4413,31 @@ rb_io_set_lineno(VALUE io, VALUE lineno)
static VALUE
io_readline(rb_execution_context_t *ec, VALUE io, VALUE sep, VALUE lim, VALUE chomp)
{
+ long limit = -1;
if (NIL_P(lim)) {
+ VALUE tmp = Qnil;
// If sep is specified, but it's not a string and not nil, then assume
// it's the limit (it should be an integer)
- if (!NIL_P(sep) && NIL_P(rb_check_string_type(sep))) {
+ if (!NIL_P(sep) && NIL_P(tmp = rb_check_string_type(sep))) {
// If the user has specified a non-nil / non-string value
// for the separator, we assume it's the limit and set the
// separator to default: rb_rs.
lim = sep;
+ limit = NUM2LONG(lim);
sep = rb_rs;
}
+ else {
+ sep = tmp;
+ }
}
-
- if (!NIL_P(sep)) {
- StringValue(sep);
+ else {
+ if (!NIL_P(sep)) StringValue(sep);
+ limit = NUM2LONG(lim);
}
- VALUE line = rb_io_getline_1(sep, NIL_P(lim) ? -1L : NUM2LONG(lim), RTEST(chomp), io);
+ check_getline_args(&sep, &limit, io);
+
+ VALUE line = rb_io_getline_1(sep, limit, RTEST(chomp), io);
rb_lastline_set_up(line, 1);
if (NIL_P(line)) {
@@ -7959,7 +8008,7 @@ popen_finish(VALUE port, VALUE klass)
if (NIL_P(port)) {
/* child */
if (rb_block_given_p()) {
- rb_yield(Qnil);
+ rb_protect(rb_yield, Qnil, NULL);
rb_io_flush(rb_ractor_stdout());
rb_io_flush(rb_ractor_stderr());
_exit(0);
@@ -13055,6 +13104,7 @@ copy_stream_fallback_body(VALUE arg)
while (1) {
long numwrote;
long l;
+ rb_str_make_independent(buf);
if (stp->copy_length < (rb_off_t)0) {
l = buflen;
}
diff --git a/io_buffer.c b/io_buffer.c
index 7715aa0d37..ea46b149f6 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -843,7 +843,8 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
static inline void
io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
{
- if (buffer->flags & RB_IO_BUFFER_READONLY) {
+ if (buffer->flags & RB_IO_BUFFER_READONLY ||
+ (!NIL_P(buffer->source) && OBJ_FROZEN(buffer->source))) {
rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
}
@@ -1532,6 +1533,7 @@ rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_
struct rb_io_buffer *slice = NULL;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
+ slice->flags |= (buffer->flags & RB_IO_BUFFER_READONLY);
slice->base = (char*)buffer->base + offset;
slice->size = length;
diff --git a/iseq.c b/iseq.c
index 27c5bb5d82..d28d68206b 100644
--- a/iseq.c
+++ b/iseq.c
@@ -113,7 +113,9 @@ remove_from_constant_cache(ID id, IC ic)
st_table *ics = (st_table *)lookup_result;
st_delete(ics, &ic_data, NULL);
- if (ics->num_entries == 0) {
+ if (ics->num_entries == 0 &&
+ // See comment in vm_track_constant_cache on why we need this check
+ id != vm->inserting_constant_cache_id) {
rb_id_table_delete(vm->constant_cache, id);
st_free_table(ics);
}
@@ -531,6 +533,19 @@ rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
rb_iseq_pathobj_new(path, realpath));
}
+// Make a dummy iseq for a dummy frame that exposes a path for profilers to inspect
+rb_iseq_t *
+rb_iseq_alloc_with_dummy_path(VALUE fname)
+{
+ rb_iseq_t *dummy_iseq = iseq_alloc();
+
+ ISEQ_BODY(dummy_iseq)->type = ISEQ_TYPE_TOP;
+ RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.pathobj, fname);
+ RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.label, fname);
+
+ return dummy_iseq;
+}
+
static rb_iseq_location_t *
iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id)
{
@@ -1670,7 +1685,11 @@ rb_iseqw_to_iseq(VALUE iseqw)
static VALUE
iseqw_eval(VALUE self)
{
- return rb_iseq_eval(iseqw_check(self));
+ const rb_iseq_t *iseq = iseqw_check(self);
+ if (0 == ISEQ_BODY(iseq)->iseq_size) {
+ rb_raise(rb_eTypeError, "attempt to evaluate dummy InstructionSequence");
+ }
+ return rb_iseq_eval(iseq);
}
/*
@@ -3465,7 +3484,7 @@ rb_vm_encoded_insn_data_table_init(void)
const void * const *table = rb_vm_get_insns_address_table();
#define INSN_CODE(insn) ((VALUE)table[insn])
#else
-#define INSN_CODE(insn) (insn)
+#define INSN_CODE(insn) ((VALUE)(insn))
#endif
st_data_t insn;
encoded_insn_data = st_init_numtable_with_size(VM_INSTRUCTION_SIZE / 2);
diff --git a/iseq.h b/iseq.h
index d71f37ca13..478f02afaf 100644
--- a/iseq.h
+++ b/iseq.h
@@ -117,6 +117,7 @@ struct iseq_compile_data {
struct iseq_compile_data_storage *storage_current;
} insn;
bool in_rescue;
+ bool in_masgn;
int loopval_popped; /* used by NODE_BREAK */
int last_line;
int label_no;
diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb
index 55286725c0..0a96b44965 100644
--- a/lib/bundled_gems.rb
+++ b/lib/bundled_gems.rb
@@ -28,19 +28,10 @@ module Gem::BUNDLED_GEMS
"syslog" => "3.4.0",
}.freeze
+ SINCE_FAST_PATH = SINCE.transform_keys { |g| g.sub(/\A.*\-/, "") }.freeze
+
EXACT = {
- "abbrev" => true,
- "base64" => true,
- "bigdecimal" => true,
- "csv" => true,
- "drb" => true,
- "getoptlong" => true,
- "mutex_m" => true,
- "nkf" => true, "kconv" => "nkf",
- "observer" => true,
- "resolv-replace" => true,
- "rinda" => true,
- "syslog" => true,
+ "kconv" => "nkf",
}.freeze
PREFIXED = {
@@ -68,8 +59,12 @@ module Gem::BUNDLED_GEMS
[::Kernel.singleton_class, ::Kernel].each do |kernel_class|
kernel_class.send(:alias_method, :no_warning_require, :require)
kernel_class.send(:define_method, :require) do |name|
- if message = ::Gem::BUNDLED_GEMS.warning?(name, specs: spec_names) # rubocop:disable Style/HashSyntax
- warn message, :uplevel => 1
+ if message = ::Gem::BUNDLED_GEMS.warning?(name, specs: spec_names)
+ if ::Gem::BUNDLED_GEMS.uplevel > 0
+ Kernel.warn message, uplevel: ::Gem::BUNDLED_GEMS.uplevel
+ else
+ Kernel.warn message
+ end
end
kernel_class.send(:no_warning_require, name)
end
@@ -81,6 +76,36 @@ module Gem::BUNDLED_GEMS
end
end
+ def self.uplevel
+ frame_count = 0
+ frames_to_skip = 3
+ uplevel = 0
+ require_found = false
+ Thread.each_caller_location do |cl|
+ frame_count += 1
+ if frames_to_skip >= 1
+ frames_to_skip -= 1
+ next
+ end
+ uplevel += 1
+ if require_found
+ if cl.base_label != "require"
+ return uplevel
+ end
+ else
+ if cl.base_label == "require"
+ require_found = true
+ end
+ end
+ # Don't show script name when bundle exec and call ruby script directly.
+ if cl.path.end_with?("bundle")
+ frame_count = 0
+ break
+ end
+ end
+ require_found ? 1 : frame_count - 1
+ end
+
def self.find_gem(path)
if !path
return
@@ -91,30 +116,46 @@ module Gem::BUNDLED_GEMS
else
return
end
- EXACT[n] or PREFIXED[n = n[%r[\A[^/]+(?=/)]]] && n
+ (EXACT[n] || !!SINCE[n]) or PREFIXED[n = n[%r[\A[^/]+(?=/)]]] && n
end
def self.warning?(name, specs: nil)
- feature = File.path(name) # name can be a feature name or a file path with String or Pathname
- name = feature.tr("/", "-")
+ # name can be a feature name or a file path with String or Pathname
+ feature = File.path(name)
+
+ # The actual checks needed to properly identify the gem being required
+ # are costly (see [Bug #20641]), so we first do a much cheaper check
+ # to exclude the vast majority of candidates.
+ if feature.include?("/")
+ # If requiring $LIBDIR/mutex_m.rb, we check SINCE_FAST_PATH["mutex_m"]
+ # We'll fail to warn requires for files that are not the entry point
+ # of the gem, e.g. require "logger/formatter.rb" won't warn.
+ # But that's acceptable because this warning is best effort,
+ # and in the overwhelming majority of cases logger.rb will end
+ # up required.
+ return unless SINCE_FAST_PATH[File.basename(feature, ".*")]
+ else
+ return unless SINCE_FAST_PATH[feature]
+ end
+
+ # bootsnap expands `require "csv"` to `require "#{LIBDIR}/csv.rb"`,
+ # and `require "syslog"` to `require "#{ARCHDIR}/syslog.so"`.
+ name = feature.delete_prefix(ARCHDIR)
+ name.delete_prefix!(LIBDIR)
+ name.tr!("/", "-")
name.sub!(LIBEXT, "")
return if specs.include?(name)
_t, path = $:.resolve_feature_path(feature)
if gem = find_gem(path)
return if specs.include?(gem)
- caller = caller_locations(3, 3).find {|c| c&.absolute_path}
+ caller = caller_locations(3, 3)&.find {|c| c&.absolute_path}
return if find_gem(caller&.absolute_path)
- elsif SINCE[name]
+ elsif SINCE[name] && !path
gem = true
else
return
end
- # Warning feature is not working correctly with Bootsnap.
- # caller_locations returns:
- # lib/ruby/3.3.0+0/bundled_gems.rb:65:in `block (2 levels) in replace_require'
- # $GEM_HOME/gems/bootsnap-1.17.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'"
- # ...
- return if caller_locations(2).find {|c| c&.path.match?(/bootsnap/) }
+
return if WARNED[name]
WARNED[name] = true
if gem == true
@@ -130,15 +171,39 @@ module Gem::BUNDLED_GEMS
end
def self.build_message(gem)
- msg = " #{RUBY_VERSION < SINCE[gem] ? "will no longer be" : "is not"} part of the default gems since Ruby #{SINCE[gem]}."
+ msg = " #{RUBY_VERSION < SINCE[gem] ? "will no longer be" : "is not"} part of the default gems starting from Ruby #{SINCE[gem]}."
if defined?(Bundler)
- msg += " Add #{gem} to your Gemfile or gemspec."
- # We detect the gem name from caller_locations. We need to skip 2 frames like:
- # lib/ruby/3.3.0+0/bundled_gems.rb:90:in `warning?'",
- # lib/ruby/3.3.0+0/bundler/rubygems_integration.rb:247:in `block (2 levels) in replace_require'",
- location = caller_locations(3,1)[0]&.path
- if File.file?(location) && !location.start_with?(Gem::BUNDLED_GEMS::LIBDIR)
+ msg += "\nYou can add #{gem} to your Gemfile or gemspec to silence this warning."
+
+ # We detect the gem name from caller_locations. First we walk until we find `require`
+ # then take the first frame that's not from `require`.
+ #
+ # Additionally, we need to skip Bootsnap and Zeitwerk if present, these
+ # gems decorate Kernel#require, so they are not really the ones issuing
+ # the require call users should be warned about. Those are upwards.
+ frames_to_skip = 3
+ location = nil
+ require_found = false
+ Thread.each_caller_location do |cl|
+ if frames_to_skip >= 1
+ frames_to_skip -= 1
+ next
+ end
+
+ if require_found
+ if cl.base_label != "require"
+ location = cl.path
+ break
+ end
+ else
+ if cl.base_label == "require"
+ require_found = true
+ end
+ end
+ end
+
+ if location && File.file?(location) && !location.start_with?(Gem::BUNDLED_GEMS::LIBDIR)
caller_gem = nil
Gem.path.each do |path|
if location =~ %r{#{path}/gems/([\w\-\.]+)}
@@ -147,7 +212,7 @@ module Gem::BUNDLED_GEMS
end
end
if caller_gem
- msg += " Also contact author of #{caller_gem} to add #{gem} into its gemspec."
+ msg += "\nAlso please contact the author of #{caller_gem} to request adding #{gem} into its gemspec."
end
end
else
@@ -168,7 +233,7 @@ class LoadError
name = path.tr("/", "-")
if !defined?(Bundler) && Gem::BUNDLED_GEMS::SINCE[name] && !Gem::BUNDLED_GEMS::WARNED[name]
- warn name + Gem::BUNDLED_GEMS.build_message(name)
+ warn name + Gem::BUNDLED_GEMS.build_message(name), uplevel: Gem::BUNDLED_GEMS.uplevel
end
super
end
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 7bb6690e52..85532f4ac2 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -40,7 +40,9 @@ module Bundler
SUDO_MUTEX = Thread::Mutex.new
autoload :Checksum, File.expand_path("bundler/checksum", __dir__)
+ autoload :CLI, File.expand_path("bundler/cli", __dir__)
autoload :CIDetector, File.expand_path("bundler/ci_detector", __dir__)
+ autoload :CompactIndexClient, File.expand_path("bundler/compact_index_client", __dir__)
autoload :Definition, File.expand_path("bundler/definition", __dir__)
autoload :Dependency, File.expand_path("bundler/dependency", __dir__)
autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__)
@@ -165,6 +167,29 @@ module Bundler
end
end
+ def auto_switch
+ self_manager.restart_with_locked_bundler_if_needed
+ end
+
+ # Automatically install dependencies if Bundler.settings[:auto_install] exists.
+ # This is set through config cmd `bundle config set --global auto_install 1`.
+ #
+ # Note that this method `nil`s out the global Definition object, so it
+ # should be called first, before you instantiate anything like an
+ # `Installer` that'll keep a reference to the old one instead.
+ def auto_install
+ return unless settings[:auto_install]
+
+ begin
+ definition.specs
+ rescue GemNotFound, GitError
+ ui.info "Automatically installing missing gems."
+ reset!
+ CLI::Install.new({}).run
+ reset!
+ end
+ end
+
# Setups Bundler environment (see Bundler.setup) if it is not already set,
# and loads all gems from groups specified. Unlike ::setup, can be called
# multiple times with different groups (if they were allowed by setup).
@@ -200,12 +225,13 @@ module Bundler
#
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
+ # @param lockfile [Pathname] Path to Gemfile.lock
# @return [Bundler::Definition]
- def definition(unlock = nil)
+ def definition(unlock = nil, lockfile = default_lockfile)
@definition = nil if unlock
@definition ||= begin
configure
- Definition.build(default_gemfile, default_lockfile, unlock)
+ Definition.build(default_gemfile, lockfile, unlock)
end
end
@@ -335,7 +361,7 @@ module Bundler
def settings
@settings ||= Settings.new(app_config_path)
rescue GemfileNotFound
- @settings = Settings.new(Pathname.new(".bundle").expand_path)
+ @settings = Settings.new
end
# @return [Hash] Environment present before Bundler was activated
@@ -357,28 +383,12 @@ module Bundler
# @return [Hash] Environment with all bundler-related variables removed
def unbundled_env
- env = original_env
-
- if env.key?("BUNDLER_ORIG_MANPATH")
- env["MANPATH"] = env["BUNDLER_ORIG_MANPATH"]
- end
-
- env.delete_if {|k, _| k[0, 7] == "BUNDLE_" }
-
- if env.key?("RUBYOPT")
- rubyopt = env["RUBYOPT"].split(" ")
- rubyopt.delete("-r#{File.expand_path("bundler/setup", __dir__)}")
- rubyopt.delete("-rbundler/setup")
- env["RUBYOPT"] = rubyopt.join(" ")
- end
-
- if env.key?("RUBYLIB")
- rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR)
- rubylib.delete(__dir__)
- env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR)
- end
+ unbundle_env(original_env)
+ end
- env
+ # Remove all bundler-related variables from ENV
+ def unbundle_env!
+ ENV.replace(unbundle_env(ENV))
end
# Run block with environment present before Bundler was activated
@@ -607,6 +617,30 @@ module Bundler
private
+ def unbundle_env(env)
+ if env.key?("BUNDLER_ORIG_MANPATH")
+ env["MANPATH"] = env["BUNDLER_ORIG_MANPATH"]
+ end
+
+ env.delete_if {|k, _| k[0, 7] == "BUNDLE_" }
+ env.delete("BUNDLER_SETUP")
+
+ if env.key?("RUBYOPT")
+ rubyopt = env["RUBYOPT"].split(" ")
+ rubyopt.delete("-r#{File.expand_path("bundler/setup", __dir__)}")
+ rubyopt.delete("-rbundler/setup")
+ env["RUBYOPT"] = rubyopt.join(" ")
+ end
+
+ if env.key?("RUBYLIB")
+ rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR)
+ rubylib.delete(__dir__)
+ env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR)
+ end
+
+ env
+ end
+
def load_marshal(data, marshal_proc: nil)
Marshal.load(data, marshal_proc)
rescue TypeError => e
@@ -626,7 +660,7 @@ module Bundler
rescue ScriptError, StandardError => e
msg = "There was an error while loading `#{path.basename}`: #{e.message}"
- raise GemspecError, Dsl::DSLError.new(msg, path, e.backtrace, contents)
+ raise GemspecError, Dsl::DSLError.new(msg, path.to_s, e.backtrace, contents)
end
def configure_gem_path
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index d93feb7b55..743f32d014 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -5,6 +5,7 @@ require_relative "vendored_thor"
module Bundler
class CLI < Thor
require_relative "cli/common"
+ require_relative "cli/install"
package_name "Bundler"
@@ -64,12 +65,12 @@ module Bundler
Bundler.reset_settings_and_root!
end
- Bundler.self_manager.restart_with_locked_bundler_if_needed
+ Bundler.auto_switch
Bundler.settings.set_command_option_if_given :retry, options[:retry]
current_cmd = args.last[:current_command].name
- auto_install if AUTO_INSTALL_CMDS.include?(current_cmd)
+ Bundler.auto_install if AUTO_INSTALL_CMDS.include?(current_cmd)
rescue UnknownArgumentError => e
raise InvalidOption, e.message
ensure
@@ -109,11 +110,13 @@ module Bundler
default_task(Bundler.feature_flag.default_cli_command)
class_option "no-color", type: :boolean, desc: "Disable colorization in output"
- class_option "retry", type: :numeric, aliases: "-r", banner: "NUM",
- desc: "Specify the number of times you wish to attempt network commands"
+ class_option "retry", type: :numeric, aliases: "-r", banner: "NUM",
+ desc: "Specify the number of times you wish to attempt network commands"
class_option "verbose", type: :boolean, desc: "Enable verbose output mode", aliases: "-V"
def help(cli = nil)
+ cli = self.class.all_aliases[cli] if self.class.all_aliases[cli]
+
case cli
when "gemfile" then command = "gemfile"
when nil then command = "bundle"
@@ -257,15 +260,15 @@ module Bundler
method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
method_option "group", aliases: "-g", type: :array, banner: "Update a specific group"
method_option "jobs", aliases: "-j", type: :numeric, banner: "Specify the number of jobs to run in parallel"
- method_option "local", type: :boolean, banner: "Do not attempt to fetch gems remotely and use the gem cache instead"
- method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
+ method_option "local", type: :boolean, banner: "Do not attempt to fetch gems remotely and use the gem cache instead"
+ method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
method_option "source", type: :array, banner: "Update a specific source (and all gems associated with it)"
method_option "redownload", type: :boolean, aliases: "--force", banner: "Force downloading every gem."
method_option "ruby", type: :boolean, banner: "Update ruby specified in Gemfile.lock"
method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
- method_option "patch", type: :boolean, banner: "Prefer updating only to next patch version"
- method_option "minor", type: :boolean, banner: "Prefer updating only to next minor version"
- method_option "major", type: :boolean, banner: "Prefer updating to next major version (default)"
+ method_option "patch", type: :boolean, banner: "Prefer updating only to next patch version"
+ method_option "minor", type: :boolean, banner: "Prefer updating only to next minor version"
+ method_option "major", type: :boolean, banner: "Prefer updating to next major version (default)"
method_option "pre", type: :boolean, banner: "Always choose the highest allowed version when updating gems, regardless of prerelease status"
method_option "strict", type: :boolean, banner: "Do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "conservative", type: :boolean, banner: "Use bundle install conservative update behavior and do not allow shared dependencies to be updated."
@@ -347,6 +350,7 @@ module Bundler
method_option "github", type: :string
method_option "branch", type: :string
method_option "ref", type: :string
+ method_option "glob", type: :string, banner: "The location of a dependency's .gemspec, expanded within Ruby (single quotes recommended)"
method_option "skip-install", type: :boolean, banner: "Adds gem to the Gemfile but does not install it"
method_option "optimistic", type: :boolean, banner: "Adds optimistic declaration of version to gem"
method_option "strict", type: :boolean, banner: "Adds strict declaration of version to gem"
@@ -393,11 +397,11 @@ module Bundler
end
desc "cache [OPTIONS]", "Locks and then caches all of the gems into vendor/cache"
- method_option "all", type: :boolean,
- default: Bundler.feature_flag.cache_all?,
- banner: "Include all sources (including path and git)."
+ method_option "all", type: :boolean,
+ default: Bundler.feature_flag.cache_all?,
+ banner: "Include all sources (including path and git)."
method_option "all-platforms", type: :boolean, banner: "Include gems for all platforms present in the lockfile, not only the current one"
- method_option "cache-path", type: :string, banner: "Specify a different cache path than the default (vendor/cache)."
+ method_option "cache-path", type: :string, banner: "Specify a different cache path than the default (vendor/cache)."
method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
method_option "no-install", type: :boolean, banner: "Don't install the gems, only update the cache."
method_option "no-prune", type: :boolean, banner: "Don't remove stale gems from the cache."
@@ -546,10 +550,13 @@ module Bundler
method_option :rubocop, type: :boolean, desc: "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`."
method_option :changelog, type: :boolean, desc: "Generate changelog file. Set a default with `bundle config set --global gem.changelog true`."
method_option :test, type: :string, lazy_default: Bundler.settings["gem.test"] || "", aliases: "-t", banner: "Use the specified test framework for your library",
+ enum: %w[rspec minitest test-unit],
desc: "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`."
method_option :ci, type: :string, lazy_default: Bundler.settings["gem.ci"] || "",
+ enum: %w[github gitlab circle],
desc: "Generate CI configuration, either GitHub Actions, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|gitlab|circle)`"
method_option :linter, type: :string, lazy_default: Bundler.settings["gem.linter"] || "",
+ enum: %w[rubocop standard],
desc: "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`"
method_option :github_username, type: :string, default: Bundler.settings["gem.github_username"], banner: "Set your username on GitHub", desc: "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username <your_username>`."
@@ -598,7 +605,7 @@ module Bundler
end
desc "inject GEM VERSION", "Add the named gem, with version requirements, to the resolved Gemfile", hide: true
- method_option "source", type: :string, banner: "Install gem from the given source"
+ method_option "source", type: :string, banner: "Install gem from the given source"
method_option "group", type: :string, banner: "Install gem into a bundler group"
def inject(name, version)
SharedHelpers.major_deprecation 2, "The `inject` command has been replaced by the `add` command"
@@ -608,19 +615,19 @@ module Bundler
desc "lock", "Creates a lockfile without installing"
method_option "update", type: :array, lazy_default: true, banner: "ignore the existing lockfile, update all gems by default, or update list of given gems"
- method_option "local", type: :boolean, default: false, banner: "do not attempt to fetch remote gemspecs and use the local gem cache only"
- method_option "print", type: :boolean, default: false, banner: "print the lockfile to STDOUT instead of writing to the file system"
+ method_option "local", type: :boolean, default: false, banner: "do not attempt to fetch remote gemspecs and use the local gem cache only"
+ method_option "print", type: :boolean, default: false, banner: "print the lockfile to STDOUT instead of writing to the file system"
method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
method_option "lockfile", type: :string, default: nil, banner: "the path the lockfile should be written to"
method_option "full-index", type: :boolean, default: false, banner: "Fall back to using the single-file index of all gems"
method_option "add-platform", type: :array, default: [], banner: "Add a new platform to the lockfile"
- method_option "remove-platform", type: :array, default: [], banner: "Remove a platform from the lockfile"
- method_option "patch", type: :boolean, banner: "If updating, prefer updating only to next patch version"
- method_option "minor", type: :boolean, banner: "If updating, prefer updating only to next minor version"
- method_option "major", type: :boolean, banner: "If updating, prefer updating to next major version (default)"
+ method_option "remove-platform", type: :array, default: [], banner: "Remove a platform from the lockfile"
+ method_option "patch", type: :boolean, banner: "If updating, prefer updating only to next patch version"
+ method_option "minor", type: :boolean, banner: "If updating, prefer updating only to next minor version"
+ method_option "major", type: :boolean, banner: "If updating, prefer updating to next major version (default)"
method_option "pre", type: :boolean, banner: "If updating, always choose the highest allowed version, regardless of prerelease status"
method_option "strict", type: :boolean, banner: "If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
- method_option "conservative", type: :boolean, banner: "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
+ method_option "conservative", type: :boolean, banner: "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
def lock
require_relative "cli/lock"
@@ -682,7 +689,6 @@ module Bundler
exec_used = args.index {|a| exec_commands.include? a }
command = args.find {|a| bundler_commands.include? a }
- command = all_aliases[command] if all_aliases[command]
if exec_used && help_used
if exec_used + help_used == 1
@@ -735,26 +741,6 @@ module Bundler
private
- # Automatically invoke `bundle install` and resume if
- # Bundler.settings[:auto_install] exists. This is set through config cmd
- # `bundle config set --global auto_install 1`.
- #
- # Note that this method `nil`s out the global Definition object, so it
- # should be called first, before you instantiate anything like an
- # `Installer` that'll keep a reference to the old one instead.
- def auto_install
- return unless Bundler.settings[:auto_install]
-
- begin
- Bundler.definition.specs
- rescue GemNotFound, GitError
- Bundler.ui.info "Automatically installing missing gems."
- Bundler.reset!
- invoke :install, []
- Bundler.reset!
- end
- end
-
def current_command
_, _, config = @_initializer
config[:current_command]
@@ -784,13 +770,10 @@ module Bundler
return unless SharedHelpers.md5_available?
- latest = Fetcher::CompactIndex.
- new(nil, Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org")), nil, nil).
- send(:compact_index_client).
- instance_variable_get(:@cache).
- dependencies("bundler").
- map {|d| Gem::Version.new(d.first) }.
- max
+ require_relative "vendored_uri"
+ remote = Source::Rubygems::Remote.new(Gem::URI("https://rubygems.org"))
+ cache_path = Bundler.user_cache.join("compact_index", remote.cache_slug)
+ latest = Bundler::CompactIndexClient.new(cache_path).latest_version("bundler")
return unless latest
current = Gem::Version.new(VERSION)
diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb
index 002d9e1d33..2b300e1783 100644
--- a/lib/bundler/cli/add.rb
+++ b/lib/bundler/cli/add.rb
@@ -34,7 +34,7 @@ module Bundler
end
def validate_options!
- raise InvalidOption, "You can not specify `--strict` and `--optimistic` at the same time." if options[:strict] && options[:optimistic]
+ raise InvalidOption, "You cannot specify `--strict` and `--optimistic` at the same time." if options[:strict] && options[:optimistic]
# raise error when no gems are specified
raise InvalidOption, "Please specify gems to add." if gems.empty?
diff --git a/lib/bundler/cli/binstubs.rb b/lib/bundler/cli/binstubs.rb
index ad41ebf4b4..8ce138df96 100644
--- a/lib/bundler/cli/binstubs.rb
+++ b/lib/bundler/cli/binstubs.rb
@@ -45,7 +45,7 @@ module Bundler
next
end
- Bundler.settings.temporary(path: (Bundler.settings[:path] || Bundler.root)) do
+ Bundler.settings.temporary(path: Bundler.settings[:path] || Bundler.root) do
installer.generate_standalone_bundler_executable_stubs(spec, installer_opts)
end
else
diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb
index 33d31cdd27..2adf59d5d5 100644
--- a/lib/bundler/cli/check.rb
+++ b/lib/bundler/cli/check.rb
@@ -17,7 +17,7 @@ module Bundler
begin
definition.resolve_only_locally!
not_installed = definition.missing_specs
- rescue GemNotFound, SolveFailure
+ rescue GemNotFound, GitError, SolveFailure
Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
Bundler.ui.warn "Install missing gems with `bundle install`."
exit 1
diff --git a/lib/bundler/cli/fund.rb b/lib/bundler/cli/fund.rb
index 52db5aef68..ad7f31f3d6 100644
--- a/lib/bundler/cli/fund.rb
+++ b/lib/bundler/cli/fund.rb
@@ -16,7 +16,7 @@ module Bundler
deps = if groups.any?
Bundler.definition.dependencies_for(groups)
else
- Bundler.definition.current_dependencies
+ Bundler.definition.requested_dependencies
end
fund_info = deps.each_with_object([]) do |dep, arr|
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
index 98192d952e..fb0a184e5d 100644
--- a/lib/bundler/cli/gem.rb
+++ b/lib/bundler/cli/gem.rb
@@ -32,7 +32,6 @@ module Bundler
validate_ext_name if @extension
validate_rust_builder_rubygems_version if @extension == "rust"
- travis_removal_info
end
def run
@@ -80,7 +79,7 @@ module Bundler
ensure_safe_gem_name(name, constant_array)
templates = {
- "#{Bundler.preferred_gemfile_name}.tt" => Bundler.preferred_gemfile_name,
+ "Gemfile.tt" => Bundler.preferred_gemfile_name,
"lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb",
"lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb",
"sig/newgem.rbs.tt" => "sig/#{namespaced_path}.rbs",
@@ -192,7 +191,10 @@ module Bundler
templates.merge!("standard.yml.tt" => ".standard.yml")
end
- templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe]
+ if config[:exe]
+ templates.merge!("exe/newgem.tt" => "exe/#{name}")
+ executables.push("exe/#{name}")
+ end
if extension == "c"
templates.merge!(
@@ -276,6 +278,7 @@ module Bundler
end
def ask_and_set_test_framework
+ return if skip?(:test)
test_framework = options[:test] || Bundler.settings["gem.test"]
if test_framework.to_s.empty?
@@ -301,6 +304,10 @@ module Bundler
test_framework
end
+ def skip?(option)
+ options.key?(option) && options[option].nil?
+ end
+
def hint_text(setting)
if Bundler.settings["gem.#{setting}"] == false
"Your choice will only be applied to this gem."
@@ -311,6 +318,7 @@ module Bundler
end
def ask_and_set_ci
+ return if skip?(:ci)
ci_template = options[:ci] || Bundler.settings["gem.ci"]
if ci_template.to_s.empty?
@@ -342,6 +350,7 @@ module Bundler
end
def ask_and_set_linter
+ return if skip?(:linter)
linter_template = options[:linter] || Bundler.settings["gem.linter"]
linter_template = deprecated_rubocop_option if linter_template.nil?
@@ -437,7 +446,7 @@ module Bundler
end
def required_ruby_version
- "2.6.0"
+ "3.0.0"
end
def rubocop_version
@@ -448,19 +457,6 @@ module Bundler
"1.3"
end
- # TODO: remove at next minor release
- def travis_removal_info
- if options[:ci] == "travis"
- Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator."
- exit 1
- end
-
- if Bundler.settings["gem.ci"] == "travis"
- Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator, but it is present in bundle config. Please configure another provider using `bundle config set gem.ci SERVICE` (where SERVICE is one of github/gitlab/circle) or unset configuration using `bundle config unset gem.ci`."
- exit 1
- end
- end
-
def validate_rust_builder_rubygems_version
if Gem::Version.new(rust_builder_required_rubygems_version) > Gem.rubygems_version
Bundler.ui.error "Your RubyGems version (#{Gem.rubygems_version}) is too old to build Rust extension. Please update your RubyGems using `gem update --system` or any other way and try again."
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index 6c102d537d..eb6c45e112 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -12,9 +12,13 @@ module Bundler
warn_if_root
- Bundler.self_manager.install_locked_bundler_and_restart_with_it_if_needed
+ if options[:local]
+ Bundler.self_manager.restart_with_locked_bundler_if_needed
+ else
+ Bundler.self_manager.install_locked_bundler_and_restart_with_it_if_needed
+ end
- 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]
@@ -25,9 +29,10 @@ module Bundler
if options[:deployment] || options[:frozen] || Bundler.frozen_bundle?
unless Bundler.default_lockfile.exist?
- flag = "--deployment flag" if options[:deployment]
- flag ||= "--frozen flag" if options[:frozen]
- flag ||= "deployment setting"
+ flag = "--deployment flag" if options[:deployment]
+ flag ||= "--frozen flag" if options[:frozen]
+ flag ||= "deployment setting" if Bundler.settings[:deployment]
+ flag ||= "frozen setting" if Bundler.settings[:frozen]
raise ProductionError, "The #{flag} requires a lockfile. Please make " \
"sure you have checked your #{SharedHelpers.relative_lockfile_path} into version control " \
"before deploying."
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
index 7247121df5..3f204bdc45 100644
--- a/lib/bundler/cli/lock.rb
+++ b/lib/bundler/cli/lock.rb
@@ -15,8 +15,8 @@ module Bundler
end
print = options[:print]
- previous_ui_level = Bundler.ui.level
- Bundler.ui.level = "silent" if print
+ previous_output_stream = Bundler.ui.output_stream
+ Bundler.ui.output_stream = :stderr if print
Bundler::Fetcher.disable_endpoint = options["full-index"]
@@ -33,8 +33,11 @@ module Bundler
update = { bundler: bundler }
end
+ file = options[:lockfile]
+ file = file ? Pathname.new(file).expand_path : Bundler.default_lockfile
+
Bundler.settings.temporary(frozen: false) do
- definition = Bundler.definition(update)
+ definition = Bundler.definition(update, file)
Bundler::CLI::Common.configure_gem_version_promoter(definition, options) if options[:update]
@@ -45,8 +48,8 @@ module Bundler
options["add-platform"].each do |platform_string|
platform = Gem::Platform.new(platform_string)
if platform.to_s == "unknown"
- Bundler.ui.warn "The platform `#{platform_string}` is unknown to RubyGems " \
- "and adding it will likely lead to resolution errors"
+ Bundler.ui.error "The platform `#{platform_string}` is unknown to RubyGems and can't be added to the lockfile."
+ exit 1
end
definition.add_platform(platform)
end
@@ -60,14 +63,12 @@ module Bundler
if print
puts definition.to_lock
else
- file = options[:lockfile]
- file = file ? File.expand_path(file) : Bundler.default_lockfile
puts "Writing lockfile to #{file}"
- definition.lock(file)
+ definition.lock
end
end
- Bundler.ui.level = previous_ui_level
+ Bundler.ui.output_stream = previous_output_stream
end
end
end
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
index ec42e631bb..75fcdca641 100644
--- a/lib/bundler/cli/outdated.rb
+++ b/lib/bundler/cli/outdated.rb
@@ -54,7 +54,7 @@ module Bundler
end
if options[:parseable]
- Bundler.ui.silence(&definition_resolution)
+ Bundler.ui.progress(&definition_resolution)
else
definition_resolution.call
end
@@ -97,28 +97,26 @@ module Bundler
}
end
- if outdated_gems.empty?
+ relevant_outdated_gems = if options_include_groups
+ outdated_gems.group_by {|g| g[:groups] }.sort.flat_map do |groups, gems|
+ contains_group = groups.split(", ").include?(options[:group])
+ next unless options[:groups] || contains_group
+
+ gems
+ end.compact
+ else
+ outdated_gems
+ end
+
+ if relevant_outdated_gems.empty?
unless options[:parseable]
Bundler.ui.info(nothing_outdated_message)
end
else
- if options_include_groups
- relevant_outdated_gems = outdated_gems.group_by {|g| g[:groups] }.sort.flat_map do |groups, gems|
- contains_group = groups.split(", ").include?(options[:group])
- next unless options[:groups] || contains_group
-
- gems
- end.compact
-
- if options[:parseable]
- print_gems(relevant_outdated_gems)
- else
- print_gems_table(relevant_outdated_gems)
- end
- elsif options[:parseable]
- print_gems(outdated_gems)
+ if options[:parseable]
+ print_gems(relevant_outdated_gems)
else
- print_gems_table(outdated_gems)
+ print_gems_table(relevant_outdated_gems)
end
exit 1
diff --git a/lib/bundler/cli/plugin.rb b/lib/bundler/cli/plugin.rb
index 1a33b5fc27..fd61ef0d95 100644
--- a/lib/bundler/cli/plugin.rb
+++ b/lib/bundler/cli/plugin.rb
@@ -5,14 +5,15 @@ module Bundler
class CLI::Plugin < Thor
desc "install PLUGINS", "Install the plugin from the source"
long_desc <<-D
- Install plugins either from the rubygems source provided (with --source option) or from a git source provided with --git (for remote repos) or --local_git (for local repos). If no sources are provided, it uses Gem.sources
+ Install plugins either from the rubygems source provided (with --source option), from a git source provided with --git, or a local path provided with --path. If no sources are provided, it uses Gem.sources
D
method_option "source", type: :string, default: nil, banner: "URL of the RubyGems source to fetch the plugin from"
method_option "version", type: :string, default: nil, banner: "The version of the plugin to fetch"
method_option "git", type: :string, default: nil, banner: "URL of the git repo to fetch from"
- method_option "local_git", type: :string, default: nil, banner: "Path of the local git repo to fetch from"
+ method_option "local_git", type: :string, default: nil, banner: "Path of the local git repo to fetch from (deprecated)"
method_option "branch", type: :string, default: nil, banner: "The git branch to checkout"
method_option "ref", type: :string, default: nil, banner: "The git revision to check out"
+ method_option "path", type: :string, default: nil, banner: "Path of a local gem to directly use"
def install(*plugins)
Bundler::Plugin.install(plugins, options)
end
diff --git a/lib/bundler/compact_index_client.rb b/lib/bundler/compact_index_client.rb
index 68e0d7e0d5..a4f5bb1638 100644
--- a/lib/bundler/compact_index_client.rb
+++ b/lib/bundler/compact_index_client.rb
@@ -4,6 +4,29 @@ require "pathname"
require "set"
module Bundler
+ # The CompactIndexClient is responsible for fetching and parsing the compact index.
+ #
+ # The compact index is a set of caching optimized files that are used to fetch gem information.
+ # The files are:
+ # - names: a list of all gem names
+ # - versions: a list of all gem versions
+ # - info/[gem]: a list of all versions of a gem
+ #
+ # The client is instantiated with:
+ # - `directory`: the root directory where the cache files are stored.
+ # - `fetcher`: (optional) an object that responds to #call(uri_path, headers) and returns an http response.
+ # If the `fetcher` is not provided, the client will only read cached files from disk.
+ #
+ # The client is organized into:
+ # - `Updater`: updates the cached files on disk using the fetcher.
+ # - `Cache`: calls the updater, caches files, read and return them from disk
+ # - `Parser`: parses the compact index file data
+ # - `CacheFile`: a concurrency safe file reader/writer that verifies checksums
+ #
+ # The client is intended to optimize memory usage and performance.
+ # It is called 100s or 1000s of times, parsing files with hundreds of thousands of lines.
+ # It may be called concurrently without global interpreter lock in some Rubies.
+ # As a result, some methods may look more complex than necessary to save memory or time.
class CompactIndexClient
# NOTE: MD5 is here not because we expect a server to respond with it, but
# because we use it to generate the etag on first request during the upgrade
@@ -12,6 +35,13 @@ module Bundler
SUPPORTED_DIGESTS = { "sha-256" => :SHA256, "md5" => :MD5 }.freeze
DEBUG_MUTEX = Thread::Mutex.new
+ # info returns an Array of INFO Arrays. Each INFO Array has the following indices:
+ INFO_NAME = 0
+ INFO_VERSION = 1
+ INFO_PLATFORM = 2
+ INFO_DEPS = 3
+ INFO_REQS = 4
+
def self.debug
return unless ENV["DEBUG_COMPACT_INDEX"]
DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
@@ -21,106 +51,47 @@ module Bundler
require_relative "compact_index_client/cache"
require_relative "compact_index_client/cache_file"
+ require_relative "compact_index_client/parser"
require_relative "compact_index_client/updater"
- attr_reader :directory
-
- def initialize(directory, fetcher)
- @directory = Pathname.new(directory)
- @updater = Updater.new(fetcher)
- @cache = Cache.new(@directory)
- @endpoints = Set.new
- @info_checksums_by_name = {}
- @parsed_checksums = false
- @mutex = Thread::Mutex.new
- end
-
- def execution_mode=(block)
- Bundler::CompactIndexClient.debug { "execution_mode=" }
- @endpoints = Set.new
-
- @execution_mode = block
- end
-
- # @return [Lambda] A lambda that takes an array of inputs and a block, and
- # maps the inputs with the block in parallel.
- #
- def execution_mode
- @execution_mode || sequentially
- end
-
- def sequential_execution_mode!
- self.execution_mode = sequentially
- end
-
- def sequentially
- @sequentially ||= lambda do |inputs, &blk|
- inputs.map(&blk)
- end
+ def initialize(directory, fetcher = nil)
+ @cache = Cache.new(directory, fetcher)
+ @parser = Parser.new(@cache)
end
def names
- Bundler::CompactIndexClient.debug { "/names" }
- update("names", @cache.names_path, @cache.names_etag_path)
- @cache.names
+ Bundler::CompactIndexClient.debug { "names" }
+ @parser.names
end
def versions
- Bundler::CompactIndexClient.debug { "/versions" }
- update("versions", @cache.versions_path, @cache.versions_etag_path)
- versions, @info_checksums_by_name = @cache.versions
- versions
+ Bundler::CompactIndexClient.debug { "versions" }
+ @parser.versions
end
def dependencies(names)
Bundler::CompactIndexClient.debug { "dependencies(#{names})" }
- execution_mode.call(names) do |name|
- update_info(name)
- @cache.dependencies(name).map {|d| d.unshift(name) }
- end.flatten(1)
+ names.map {|name| info(name) }
end
- def update_and_parse_checksums!
- Bundler::CompactIndexClient.debug { "update_and_parse_checksums!" }
- return @info_checksums_by_name if @parsed_checksums
- update("versions", @cache.versions_path, @cache.versions_etag_path)
- @info_checksums_by_name = @cache.checksums
- @parsed_checksums = true
- end
-
- private
-
- def update(remote_path, local_path, local_etag_path)
- Bundler::CompactIndexClient.debug { "update(#{local_path}, #{remote_path})" }
- unless synchronize { @endpoints.add?(remote_path) }
- Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
- return
- end
- @updater.update(url(remote_path), local_path, local_etag_path)
+ def info(name)
+ Bundler::CompactIndexClient.debug { "info(#{name})" }
+ @parser.info(name)
end
- def update_info(name)
- Bundler::CompactIndexClient.debug { "update_info(#{name})" }
- path = @cache.info_path(name)
- unless existing = @info_checksums_by_name[name]
- Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since it is missing from versions" }
- return
- end
- checksum = SharedHelpers.checksum_for_file(path, :MD5)
- if checksum == existing
- Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since the versions checksum matches the local checksum" }
- return
- end
- Bundler::CompactIndexClient.debug { "updating info for #{name} since the versions checksum #{existing} != the local checksum #{checksum}" }
- update("info/#{name}", path, @cache.info_etag_path(name))
+ def latest_version(name)
+ Bundler::CompactIndexClient.debug { "latest_version(#{name})" }
+ @parser.info(name).map {|d| Gem::Version.new(d[INFO_VERSION]) }.max
end
- def url(path)
- path
+ def available?
+ Bundler::CompactIndexClient.debug { "available?" }
+ @parser.available?
end
- def synchronize
- @mutex.synchronize { yield }
+ def reset!
+ Bundler::CompactIndexClient.debug { "reset!" }
+ @cache.reset!
end
end
end
diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb
index 55911fdecf..bedd7f8028 100644
--- a/lib/bundler/compact_index_client/cache.rb
+++ b/lib/bundler/compact_index_client/cache.rb
@@ -7,114 +7,89 @@ module Bundler
class Cache
attr_reader :directory
- def initialize(directory)
+ def initialize(directory, fetcher = nil)
@directory = Pathname.new(directory).expand_path
- info_roots.each {|dir| mkdir(dir) }
- mkdir(info_etag_root)
+ @updater = Updater.new(fetcher) if fetcher
+ @mutex = Thread::Mutex.new
+ @endpoints = Set.new
+
+ @info_root = mkdir("info")
+ @special_characters_info_root = mkdir("info-special-characters")
+ @info_etag_root = mkdir("info-etags")
end
def names
- lines(names_path)
+ fetch("names", names_path, names_etag_path)
end
- def names_path
- directory.join("names")
+ def versions
+ fetch("versions", versions_path, versions_etag_path)
end
- def names_etag_path
- directory.join("names.etag")
- end
+ def info(name, remote_checksum = nil)
+ path = info_path(name)
- def versions
- versions_by_name = Hash.new {|hash, key| hash[key] = [] }
- info_checksums_by_name = {}
-
- lines(versions_path).each do |line|
- name, versions_string, info_checksum = line.split(" ", 3)
- info_checksums_by_name[name] = info_checksum || ""
- versions_string.split(",") do |version|
- delete = version.delete_prefix!("-")
- version = version.split("-", 2).unshift(name)
- if delete
- versions_by_name[name].delete(version)
- else
- versions_by_name[name] << version
- end
- end
+ if remote_checksum && remote_checksum != SharedHelpers.checksum_for_file(path, :MD5)
+ fetch("info/#{name}", path, info_etag_path(name))
+ else
+ Bundler::CompactIndexClient.debug { "update skipped info/#{name} (#{remote_checksum ? "versions index checksum is nil" : "versions index checksum matches local"})" }
+ read(path)
end
-
- [versions_by_name, info_checksums_by_name]
- end
-
- def versions_path
- directory.join("versions")
end
- def versions_etag_path
- directory.join("versions.etag")
+ def reset!
+ @mutex.synchronize { @endpoints.clear }
end
- def checksums
- checksums = {}
-
- lines(versions_path).each do |line|
- name, _, checksum = line.split(" ", 3)
- checksums[name] = checksum
- end
-
- checksums
- end
+ private
- def dependencies(name)
- lines(info_path(name)).map do |line|
- parse_gem(line)
- end
- end
+ def names_path = directory.join("names")
+ def names_etag_path = directory.join("names.etag")
+ def versions_path = directory.join("versions")
+ def versions_etag_path = directory.join("versions.etag")
def info_path(name)
name = name.to_s
+ # TODO: converge this into the info_root by hashing all filenames like info_etag_path
if /[^a-z0-9_-]/.match?(name)
name += "-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}"
- info_roots.last.join(name)
+ @special_characters_info_root.join(name)
else
- info_roots.first.join(name)
+ @info_root.join(name)
end
end
def info_etag_path(name)
name = name.to_s
- info_etag_root.join("#{name}-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}")
+ @info_etag_root.join("#{name}-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}")
end
- private
-
- def mkdir(dir)
- SharedHelpers.filesystem_access(dir) do
- FileUtils.mkdir_p(dir)
+ def mkdir(name)
+ directory.join(name).tap do |dir|
+ SharedHelpers.filesystem_access(dir) do
+ FileUtils.mkdir_p(dir)
+ end
end
end
- def lines(path)
- return [] unless path.file?
- lines = SharedHelpers.filesystem_access(path, :read, &:read).split("\n")
- header = lines.index("---")
- header ? lines[header + 1..-1] : lines
- end
+ def fetch(remote_path, path, etag_path)
+ if already_fetched?(remote_path)
+ Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
+ else
+ Bundler::CompactIndexClient.debug { "fetching #{remote_path}" }
+ @updater&.update(remote_path, path, etag_path)
+ end
- def parse_gem(line)
- @dependency_parser ||= GemParser.new
- @dependency_parser.parse(line)
+ read(path)
end
- def info_roots
- [
- directory.join("info"),
- directory.join("info-special-characters"),
- ]
+ def already_fetched?(remote_path)
+ @mutex.synchronize { !@endpoints.add?(remote_path) }
end
- def info_etag_root
- directory.join("info-etags")
+ def read(path)
+ return unless path.file?
+ SharedHelpers.filesystem_access(path, :read, &:read)
end
end
end
diff --git a/lib/bundler/compact_index_client/parser.rb b/lib/bundler/compact_index_client/parser.rb
new file mode 100644
index 0000000000..3276abdd68
--- /dev/null
+++ b/lib/bundler/compact_index_client/parser.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CompactIndexClient
+ class Parser
+ # `compact_index` - an object responding to #names, #versions, #info(name, checksum),
+ # returning the file contents as a string
+ def initialize(compact_index)
+ @compact_index = compact_index
+ @info_checksums = nil
+ @versions_by_name = nil
+ @available = nil
+ @gem_parser = nil
+ end
+
+ def names
+ lines(@compact_index.names)
+ end
+
+ def versions
+ @versions_by_name ||= Hash.new {|hash, key| hash[key] = [] }
+ @info_checksums = {}
+
+ lines(@compact_index.versions).each do |line|
+ name, versions_string, checksum = line.split(" ", 3)
+ @info_checksums[name] = checksum || ""
+ versions_string.split(",") do |version|
+ delete = version.delete_prefix!("-")
+ version = version.split("-", 2).unshift(name)
+ if delete
+ @versions_by_name[name].delete(version)
+ else
+ @versions_by_name[name] << version
+ end
+ end
+ end
+
+ @versions_by_name
+ end
+
+ def info(name)
+ data = @compact_index.info(name, info_checksums[name])
+ lines(data).map {|line| gem_parser.parse(line).unshift(name) }
+ end
+
+ def available?
+ return @available unless @available.nil?
+ @available = !info_checksums.empty?
+ end
+
+ private
+
+ def info_checksums
+ @info_checksums ||= lines(@compact_index.versions).each_with_object({}) do |line, checksums|
+ parse_version_checksum(line, checksums)
+ end
+ end
+
+ def lines(data)
+ return [] if data.nil? || data.empty?
+ lines = data.split("\n")
+ header = lines.index("---")
+ header ? lines[header + 1..-1] : lines
+ end
+
+ def gem_parser
+ @gem_parser ||= GemParser.new
+ end
+
+ # This is mostly the same as `split(" ", 3)` but it avoids allocating extra objects.
+ # This method gets called at least once for every gem when parsing versions.
+ def parse_version_checksum(line, checksums)
+ return unless (name_end = line.index(" ")) # Artifactory bug causes blank lines in artifactor index files
+ return unless (checksum_start = line.index(" ", name_end + 1) + 1)
+ checksum_end = line.size - checksum_start
+
+ line.freeze # allows slicing into the string to not allocate a copy of the line
+ name = line[0, name_end]
+ checksum = line[checksum_start, checksum_end]
+ checksums[name.freeze] = checksum # freeze name since it is used as a hash key
+ end
+ end
+ end
+end
diff --git a/lib/bundler/constants.rb b/lib/bundler/constants.rb
index de9698b577..9564771e78 100644
--- a/lib/bundler/constants.rb
+++ b/lib/bundler/constants.rb
@@ -1,7 +1,14 @@
# frozen_string_literal: true
+require "rbconfig"
+
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
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 0b0e63f77e..e7e6c49e6c 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -69,7 +69,6 @@ module Bundler
@sources = sources
@unlock = unlock
@optional_groups = optional_groups
- @remote = false
@prefer_local = false
@specs = nil
@ruby_version = ruby_version
@@ -82,7 +81,7 @@ module Bundler
@resolved_bundler_version = nil
@locked_ruby_version = nil
- @new_platform = nil
+ @new_platforms = []
@removed_platform = nil
if lockfile_exists?
@@ -92,11 +91,12 @@ module Bundler
@platforms = @locked_platforms.dup
@locked_bundler_version = @locked_gems.bundler_version
@locked_ruby_version = @locked_gems.ruby_version
+ @originally_locked_deps = @locked_gems.dependencies
@originally_locked_specs = SpecSet.new(@locked_gems.specs)
@locked_checksums = @locked_gems.checksums
if unlock != true
- @locked_deps = @locked_gems.dependencies
+ @locked_deps = @originally_locked_deps
@locked_specs = @originally_locked_specs
@locked_sources = @locked_gems.sources
else
@@ -111,10 +111,11 @@ module Bundler
@locked_gems = nil
@locked_deps = {}
@locked_specs = SpecSet.new([])
+ @originally_locked_deps = {}
@originally_locked_specs = @locked_specs
@locked_sources = []
@locked_platforms = []
- @locked_checksums = nil
+ @locked_checksums = Bundler.feature_flag.bundler_3_mode?
end
locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
@@ -130,23 +131,25 @@ module Bundler
@sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
end
- @unlock[:sources] ||= []
+ @sources_to_unlock = @unlock.delete(:sources) || []
@unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
@ruby_version.diff(locked_ruby_version_object)
end
@unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
- add_current_platform unless Bundler.frozen_bundle?
+ @current_platform_missing = add_current_platform unless Bundler.frozen_bundle?
converge_path_sources_to_gemspec_sources
@path_changes = converge_paths
@source_changes = converge_sources
+ @explicit_unlocks = @unlock.delete(:gems) || []
+
if @unlock[:conservative]
- @unlock[:gems] ||= @dependencies.map(&:name)
+ @gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
else
- eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") }
- @unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq
+ eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") }
+ @gems_to_unlock = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq
end
@dependency_changes = converge_dependencies
@@ -160,37 +163,24 @@ module Bundler
end
def resolve_only_locally!
- @remote = false
sources.local_only!
resolve
end
def resolve_with_cache!
+ sources.local!
sources.cached!
resolve
end
def resolve_remotely!
- @remote = true
+ sources.cached!
sources.remote!
resolve
end
- def resolution_mode=(options)
- if options["local"]
- @remote = false
- else
- @remote = true
- @prefer_local = options["prefer-local"]
- end
- end
-
- def setup_sources_for_resolve
- if @remote == false
- sources.cached!
- else
- sources.remote!
- end
+ def prefer_local!
+ @prefer_local = true
end
# For given dependency list returns a SpecSet with Gemspec of all the required
@@ -224,8 +214,8 @@ module Bundler
@resolve = nil
@resolver = nil
@resolution_packages = nil
+ @source_requirements = nil
@specs = nil
- @gem_version_promoter = nil
Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})"
true
@@ -307,7 +297,12 @@ module Bundler
end
end
else
- Bundler.ui.debug "Found changes from the lockfile, re-resolving dependencies because #{change_reason}"
+ if lockfile_exists?
+ Bundler.ui.debug "Found changes from the lockfile, re-resolving dependencies because #{change_reason}"
+ else
+ Bundler.ui.debug "Resolving dependencies because there's no lockfile"
+ end
+
start_resolution
end
end
@@ -320,38 +315,26 @@ module Bundler
dependencies.map(&:groups).flatten.uniq
end
- def lock(file, preserve_unknown_sections = false)
- return if Definition.no_lock
-
- contents = to_lock
-
- # Convert to \r\n if the existing lock has them
- # i.e., Windows with `git config core.autocrlf=true`
- contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
-
- if @locked_bundler_version
- locked_major = @locked_bundler_version.segments.first
- current_major = bundler_version_to_lock.segments.first
-
- updating_major = locked_major < current_major
- end
+ def lock(file_or_preserve_unknown_sections = false, preserve_unknown_sections_or_unused = false)
+ if [true, false, nil].include?(file_or_preserve_unknown_sections)
+ target_lockfile = lockfile
+ preserve_unknown_sections = file_or_preserve_unknown_sections
+ else
+ target_lockfile = file_or_preserve_unknown_sections
+ preserve_unknown_sections = preserve_unknown_sections_or_unused
- preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
+ suggestion = if target_lockfile == lockfile
+ "To fix this warning, remove it from the `Definition#lock` call."
+ else
+ "Instead, instantiate a new definition passing `#{target_lockfile}`, and call `lock` without a file argument on that definition"
+ end
- if file && File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
- return if Bundler.frozen_bundle?
- SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
- return
- end
+ msg = "`Definition#lock` was passed a target file argument. #{suggestion}"
- if Bundler.frozen_bundle?
- Bundler.ui.error "Cannot write a changed lockfile while frozen."
- return
+ Bundler::SharedHelpers.major_deprecation 2, msg
end
- SharedHelpers.filesystem_access(file) do |p|
- File.open(p, "wb") {|f| f.puts(contents) }
- end
+ write_lock(target_lockfile, preserve_unknown_sections)
end
def locked_ruby_version
@@ -385,6 +368,10 @@ module Bundler
end
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
+ return unless Bundler.frozen_bundle?
+
+ raise ProductionError, "Frozen mode is set, but there's no lockfile" unless lockfile_exists?
+
added = []
deleted = []
changed = []
@@ -413,7 +400,7 @@ module Bundler
changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
end
- reason = change_reason
+ reason = nothing_changed? ? "some dependencies were deleted from your gemfile" : change_reason
msg = String.new
msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set"
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
@@ -471,8 +458,10 @@ module Bundler
end
def add_platform(platform)
- @new_platform ||= !@platforms.include?(platform)
- @platforms |= [platform]
+ return if @platforms.include?(platform)
+
+ @new_platforms << platform
+ @platforms << platform
end
def remove_platform(platform)
@@ -488,13 +477,13 @@ module Bundler
end
end
- attr_reader :sources
- private :sources
-
def nothing_changed?
+ return false unless lockfile_exists?
+
!@source_changes &&
!@dependency_changes &&
- !@new_platform &&
+ !@current_platform_missing &&
+ @new_platforms.empty? &&
!@path_changes &&
!@local_changes &&
!@missing_lockfile_dep &&
@@ -511,8 +500,12 @@ module Bundler
@unlocking
end
+ attr_writer :source_requirements
+
private
+ attr_reader :sources
+
def should_add_extra_platforms?
!lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform]
end
@@ -521,6 +514,40 @@ module Bundler
lockfile && File.exist?(lockfile)
end
+ def write_lock(file, preserve_unknown_sections)
+ return if Definition.no_lock || file.nil?
+
+ contents = to_lock
+
+ # Convert to \r\n if the existing lock has them
+ # i.e., Windows with `git config core.autocrlf=true`
+ contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
+
+ if @locked_bundler_version
+ locked_major = @locked_bundler_version.segments.first
+ current_major = bundler_version_to_lock.segments.first
+
+ updating_major = locked_major < current_major
+ end
+
+ preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
+
+ if File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
+ return if Bundler.frozen_bundle?
+ SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
+ return
+ end
+
+ if Bundler.frozen_bundle?
+ Bundler.ui.error "Cannot write a changed lockfile while frozen."
+ return
+ end
+
+ SharedHelpers.filesystem_access(file) do |p|
+ File.open(p, "wb") {|f| f.puts(contents) }
+ end
+ end
+
def resolver
@resolver ||= Resolver.new(resolution_packages, gem_version_promoter)
end
@@ -539,9 +566,11 @@ module Bundler
def resolution_packages
@resolution_packages ||= begin
last_resolve = converge_locked_specs
- remove_invalid_platforms!(current_dependencies)
- packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlock[:gems], prerelease: gem_version_promoter.pre?)
- additional_base_requirements_for_resolve(packages, last_resolve)
+ remove_invalid_platforms!
+ packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local)
+ packages = additional_base_requirements_to_prevent_downgrades(packages, last_resolve)
+ packages = additional_base_requirements_to_force_updates(packages)
+ packages
end
end
@@ -556,7 +585,7 @@ module Bundler
if missing_specs.any?
missing_specs.each do |s|
locked_gem = @locked_specs[s.name].last
- next if locked_gem.nil? || locked_gem.version != s.version || !@remote
+ next if locked_gem.nil? || locked_gem.version != s.version || sources.local_mode?
raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
"no longer be found in that source. That means the author of #{locked_gem} has removed it. " \
"You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
@@ -575,7 +604,7 @@ module Bundler
break if incomplete_specs.empty?
Bundler.ui.debug("The lockfile does not have all gems needed for the current platform though, Bundler will still re-resolve dependencies")
- setup_sources_for_resolve
+ sources.remote!
resolution_packages.delete(incomplete_specs)
@resolve = start_resolution
specs = resolve.materialize(dependencies)
@@ -597,12 +626,23 @@ module Bundler
end
def start_resolution
+ local_platform_needed_for_resolvability = @most_specific_non_local_locked_ruby_platform && !@platforms.include?(local_platform)
+ @platforms << local_platform if local_platform_needed_for_resolvability
+ add_platform(Gem::Platform::RUBY) if RUBY_ENGINE == "truffleruby"
+
result = SpecSet.new(resolver.start)
@resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
- @platforms = result.add_extra_platforms!(platforms) if should_add_extra_platforms?
- result.complete_platforms!(platforms)
+ if @most_specific_non_local_locked_ruby_platform
+ if spec_set_incomplete_for_platform?(result, @most_specific_non_local_locked_ruby_platform)
+ @platforms.delete(@most_specific_non_local_locked_ruby_platform)
+ elsif local_platform_needed_for_resolvability
+ @platforms.delete(local_platform)
+ end
+ end
+
+ @platforms = result.add_extra_platforms!(platforms) if should_add_extra_platforms?
SpecSet.new(result.for(dependencies, false, @platforms))
end
@@ -611,26 +651,6 @@ module Bundler
sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
end
- def pin_locally_available_names(source_requirements)
- source_requirements.each_with_object({}) do |(name, original_source), new_source_requirements|
- local_source = original_source.dup
- local_source.local_only!
-
- new_source_requirements[name] = if local_source.specs.search(name).any?
- local_source
- else
- original_source
- end
- end
- end
-
- def current_ruby_platform_locked?
- return false unless generic_local_platform_is_ruby?
- return false if Bundler.settings[:force_ruby_platform] && !@platforms.include?(Gem::Platform::RUBY)
-
- current_platform_locked?
- end
-
def current_platform_locked?
@platforms.any? do |bundle_platform|
MatchPlatform.platforms_match?(bundle_platform, local_platform)
@@ -638,27 +658,42 @@ module Bundler
end
def add_current_platform
- return if current_ruby_platform_locked?
+ return if @platforms.include?(local_platform)
+
+ @most_specific_non_local_locked_ruby_platform = find_most_specific_locked_ruby_platform
+ return if @most_specific_non_local_locked_ruby_platform
- add_platform(local_platform)
+ @platforms << local_platform
+ true
+ end
+
+ def find_most_specific_locked_ruby_platform
+ return unless generic_local_platform_is_ruby? && current_platform_locked?
+
+ most_specific_locked_platform
end
def change_reason
if unlocking?
- unlock_reason = @unlock.reject {|_k, v| Array(v).empty? }.map do |k, v|
- if v == true
- k.to_s
- else
- v = Array(v)
- "#{k}: (#{v.join(", ")})"
- end
- end.join(", ")
+ unlock_targets = if @gems_to_unlock.any?
+ ["gems", @gems_to_unlock]
+ elsif @sources_to_unlock.any?
+ ["sources", @sources_to_unlock]
+ end
+
+ unlock_reason = if unlock_targets
+ "#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})"
+ else
+ @unlock[:ruby] ? "ruby" : ""
+ end
+
return "bundler is unlocking #{unlock_reason}"
end
[
[@source_changes, "the list of sources changed"],
[@dependency_changes, "the dependencies in your gemfile changed"],
- [@new_platform, "you added a new platform to your gemfile"],
+ [@current_platform_missing, "your lockfile does not include the current platform"],
+ [@new_platforms.any?, "you added a new platform to your gemfile"],
[@path_changes, "the gemspecs for path gems changed"],
[@local_changes, "the gemspecs for git local gems changed"],
[@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""],
@@ -707,7 +742,7 @@ module Bundler
spec = @dependencies.find {|s| s.name == k }
source = spec&.source
if source&.respond_to?(:local_override!)
- source.unlock! if @unlock[:gems].include?(spec.name)
+ source.unlock! if @gems_to_unlock.include?(spec.name)
locals << [source, source.local_override!(v)]
end
end
@@ -715,7 +750,7 @@ module Bundler
sources_with_changes = locals.select do |source, changed|
changed || specs_changed?(source)
end.map(&:first)
- !sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
+ !sources_with_changes.each {|source| @sources_to_unlock << source.name }.empty?
end
def check_lockfile
@@ -792,7 +827,7 @@ module Bundler
# gem), unlock it. For git sources, this means to unlock the revision, which
# will cause the `ref` used to be the most recent for the branch (or master) if
# an explicit `ref` is not used.
- if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
+ if source.respond_to?(:unlock!) && @sources_to_unlock.include?(source.name)
source.unlock!
changes = true
end
@@ -809,9 +844,7 @@ module Bundler
dep.source = sources.get(dep.source)
end
- next if unlocking?
-
- unless locked_dep = @locked_deps[dep.name]
+ unless locked_dep = @originally_locked_deps[dep.name]
changes = true
next
end
@@ -838,7 +871,7 @@ module Bundler
def converge_locked_specs
converged = converge_specs(@locked_specs)
- resolve = SpecSet.new(converged.reject {|s| @unlock[:gems].include?(s.name) })
+ resolve = SpecSet.new(converged.reject {|s| @gems_to_unlock.include?(s.name) })
diff = nil
@@ -871,7 +904,7 @@ module Bundler
@specs_that_changed_sources << s if gemfile_source != lockfile_source
deps << dep if !dep.source || lockfile_source.include?(dep.source)
- @unlock[:gems] << name if lockfile_source.include?(dep.source) && lockfile_source != gemfile_source
+ @gems_to_unlock << name if lockfile_source.include?(dep.source) && lockfile_source != gemfile_source
# Replace the locked dependency's source with the equivalent source from the Gemfile
s.source = gemfile_source
@@ -880,7 +913,7 @@ module Bundler
s.source = default_source unless sources.get(lockfile_source)
end
- next if @unlock[:sources].include?(s.source.name)
+ next if @sources_to_unlock.include?(s.source.name)
# Path sources have special logic
if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec)
@@ -902,12 +935,12 @@ module Bundler
else
# If the spec is no longer in the path source, unlock it. This
# commonly happens if the version changed in the gemspec
- @unlock[:gems] << name
+ @gems_to_unlock << name
end
end
if dep.nil? && requested_dependencies.find {|d| name == d.name }
- @unlock[:gems] << s.name
+ @gems_to_unlock << s.name
else
converged << s
end
@@ -924,17 +957,20 @@ module Bundler
end
def source_requirements
+ @source_requirements ||= find_source_requirements
+ end
+
+ def find_source_requirements
# Record the specs available in each gem's source, so that those
# specs will be available later when the resolver knows where to
# look for that gemspec (or its dependencies)
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
all_requirements = source_map.all_requirements
- all_requirements = pin_locally_available_names(all_requirements) if @prefer_local
{ default: default_source }.merge(all_requirements)
else
{ default: Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
end
- source_requirements.merge!(source_map.locked_requirements) unless @remote
+ source_requirements.merge!(source_map.locked_requirements) if nothing_changed?
metadata_dependencies.each do |dep|
source_requirements[dep.name] = sources.metadata_source
end
@@ -984,7 +1020,7 @@ module Bundler
current == proposed
end
- def additional_base_requirements_for_resolve(resolution_packages, last_resolve)
+ def additional_base_requirements_to_prevent_downgrades(resolution_packages, last_resolve)
return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec|
next if locked_spec.source.is_a?(Source::Path)
@@ -993,21 +1029,46 @@ module Bundler
resolution_packages
end
- def remove_invalid_platforms!(dependencies)
+ def additional_base_requirements_to_force_updates(resolution_packages)
+ return resolution_packages if @explicit_unlocks.empty?
+ full_update = dup_for_full_unlock.resolve
+ @explicit_unlocks.each do |name|
+ version = full_update[name].first&.version
+ resolution_packages.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
+ end
+ resolution_packages
+ end
+
+ def dup_for_full_unlock
+ unlocked_definition = self.class.new(@lockfile, @dependencies, @sources, true, @ruby_version, @optional_groups, @gemfiles)
+ unlocked_definition.source_requirements = source_requirements
+ unlocked_definition.gem_version_promoter.tap do |gvp|
+ gvp.level = gem_version_promoter.level
+ gvp.strict = gem_version_promoter.strict
+ gvp.pre = gem_version_promoter.pre
+ end
+ unlocked_definition
+ end
+
+ def remove_invalid_platforms!
return if Bundler.frozen_bundle?
platforms.reverse_each do |platform|
next if local_platform == platform ||
- (@new_platform && platforms.last == platform) ||
+ @new_platforms.include?(platform) ||
@path_changes ||
@dependency_changes ||
- !@originally_locked_specs.incomplete_for_platform?(dependencies, platform)
+ @locked_spec_with_invalid_deps ||
+ !spec_set_incomplete_for_platform?(@originally_locked_specs, platform)
remove_platform(platform)
- add_current_platform if platform == Gem::Platform::RUBY
end
end
+ def spec_set_incomplete_for_platform?(spec_set, platform)
+ spec_set.incomplete_for_platform?(current_dependencies, platform)
+ end
+
def source_map
@source_map ||= SourceMap.new(sources, dependencies, @locked_specs)
end
diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb
index 77d7a00362..2a4f72fe55 100644
--- a/lib/bundler/dependency.rb
+++ b/lib/bundler/dependency.rb
@@ -7,7 +7,7 @@ require_relative "rubygems_ext"
module Bundler
class Dependency < Gem::Dependency
attr_reader :autorequire
- attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref
+ attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref, :glob
ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..34).to_a).freeze
PLATFORM_MAP = {
@@ -39,6 +39,7 @@ module Bundler
@github = options["github"]
@branch = options["branch"]
@ref = options["ref"]
+ @glob = options["glob"]
@platforms = Array(options["platforms"])
@env = options["env"]
@should_include = options.fetch("should_include", true)
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index 1460b9f52f..24a9a7683a 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -19,6 +19,7 @@ module Bundler
platform platforms type source install_if gemfile force_ruby_platform].freeze
GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}
+ GITLAB_MERGE_REQUEST_URL = %r{\Ahttps://gitlab\.com/([A-Za-z0-9_\-\./]+)/-/merge_requests/(\d+)\z}
attr_reader :gemspecs, :gemfile
attr_accessor :dependencies
@@ -41,20 +42,20 @@ module Bundler
end
def eval_gemfile(gemfile, contents = nil)
- expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent)
- original_gemfile = @gemfile
- @gemfile = expanded_gemfile_path
- @gemfiles << expanded_gemfile_path
- contents ||= Bundler.read_file(@gemfile.to_s)
- instance_eval(contents, gemfile.to_s, 1)
- rescue Exception => e # rubocop:disable Lint/RescueException
- message = "There was an error " \
- "#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \
- "`#{File.basename gemfile.to_s}`: #{e.message}"
-
- raise DSLError.new(message, gemfile, e.backtrace, contents)
- ensure
- @gemfile = original_gemfile
+ with_gemfile(gemfile) do |current_gemfile|
+ contents ||= Bundler.read_file(current_gemfile)
+ instance_eval(contents, current_gemfile, 1)
+ rescue GemfileEvalError => e
+ message = "There was an error evaluating `#{File.basename current_gemfile}`: #{e.message}"
+ raise DSLError.new(message, current_gemfile, e.backtrace, contents)
+ rescue GemfileError, InvalidArgumentError, InvalidOption, DeprecatedError, ScriptError => e
+ message = "There was an error parsing `#{File.basename current_gemfile}`: #{e.message}"
+ raise DSLError.new(message, current_gemfile, e.backtrace, contents)
+ rescue StandardError => e
+ raise unless e.backtrace_locations.first.path == current_gemfile
+ message = "There was an error parsing `#{File.basename current_gemfile}`: #{e.message}"
+ raise DSLError.new(message, current_gemfile, e.backtrace, contents)
+ end
end
def gemspec(opts = nil)
@@ -218,7 +219,7 @@ module Bundler
end
def github(repo, options = {})
- raise ArgumentError, "GitHub sources require a block" unless block_given?
+ raise InvalidArgumentError, "GitHub sources require a block" unless block_given?
github_uri = @git_sources["github"].call(repo)
git_options = normalize_hash(options).merge("uri" => github_uri)
git_source = @sources.add_git_source(git_options)
@@ -284,6 +285,16 @@ module Bundler
private
+ def with_gemfile(gemfile)
+ expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent)
+ original_gemfile = @gemfile
+ @gemfile = expanded_gemfile_path
+ @gemfiles << expanded_gemfile_path
+ yield @gemfile.to_s
+ ensure
+ @gemfile = original_gemfile
+ end
+
def add_git_sources
git_source(:github) do |repo_name|
if repo_name =~ GITHUB_PULL_REQUEST_URL
@@ -308,6 +319,20 @@ module Bundler
repo_name ||= user_name
"https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git"
end
+
+ git_source(:gitlab) do |repo_name|
+ if repo_name =~ GITLAB_MERGE_REQUEST_URL
+ {
+ "git" => "https://gitlab.com/#{$1}.git",
+ "branch" => nil,
+ "ref" => "refs/merge-requests/#{$2}/head",
+ "tag" => nil,
+ }
+ else
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
+ "https://gitlab.com/#{repo_name}.git"
+ end
+ end
end
def with_source(source)
@@ -562,23 +587,23 @@ module Bundler
return m unless backtrace && dsl_path && contents
- trace_line = backtrace.find {|l| l.include?(dsl_path.to_s) } || trace_line
+ trace_line = backtrace.find {|l| l.include?(dsl_path) } || trace_line
return m unless trace_line
- line_numer = trace_line.split(":")[1].to_i - 1
- return m unless line_numer
+ line_number = trace_line.split(":")[1].to_i - 1
+ return m unless line_number
lines = contents.lines.to_a
indent = " # "
indicator = indent.tr("#", ">")
- first_line = line_numer.zero?
- last_line = (line_numer == (lines.count - 1))
+ first_line = line_number.zero?
+ last_line = (line_number == (lines.count - 1))
m << "\n"
m << "#{indent}from #{trace_line.gsub(/:in.*$/, "")}\n"
m << "#{indent}-------------------------------------------\n"
- m << "#{indent}#{lines[line_numer - 1]}" unless first_line
- m << "#{indicator}#{lines[line_numer]}"
- m << "#{indent}#{lines[line_numer + 1]}" unless last_line
+ m << "#{indent}#{lines[line_number - 1]}" unless first_line
+ m << "#{indicator}#{lines[line_number]}"
+ m << "#{indent}#{lines[line_number + 1]}" unless last_line
m << "\n" unless m.end_with?("\n")
m << "#{indent}-------------------------------------------\n"
end
@@ -588,7 +613,7 @@ module Bundler
def parse_line_number_from_description
description = self.description
- if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path.to_s}):\d+)/
+ if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path}):\d+)/
trace_line = Regexp.last_match[1]
description = description.sub(/\n.*\n(\.\.\.)? *\^~+$/, "").sub(/#{Regexp.quote trace_line}:\s*/, "").sub("\n", " - ")
end
diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb
index 87cb352efa..201818cc33 100644
--- a/lib/bundler/endpoint_specification.rb
+++ b/lib/bundler/endpoint_specification.rb
@@ -92,6 +92,17 @@ module Bundler
end
end
+ # needed for `bundle fund`
+ def metadata
+ if @remote_specification
+ @remote_specification.metadata
+ elsif _local_specification
+ _local_specification.metadata
+ else
+ super
+ end
+ end
+
def _local_specification
return unless @loaded_from && File.exist?(local_specification_path)
eval(File.read(local_specification_path), nil, local_specification_path).tap do |spec|
diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb
index f6cb198e38..074bef6edc 100644
--- a/lib/bundler/env.rb
+++ b/lib/bundler/env.rb
@@ -120,7 +120,7 @@ module Bundler
specs = Bundler.rubygems.find_name(name)
out << [" #{name}", "(#{specs.map(&:version).join(",")})"] unless specs.empty?
end
- if (exe = caller.last.split(":").first)&.match? %r{(exe|bin)/bundler?\z}
+ if (exe = caller_locations.last.absolute_path)&.match? %r{(exe|bin)/bundler?\z}
shebang = File.read(exe).lines.first
shebang.sub!(/^#!\s*/, "")
unless shebang.start_with?(Gem.ruby, "/usr/bin/env ruby")
diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb
index 57013f5d50..444ab6fd37 100644
--- a/lib/bundler/environment_preserver.rb
+++ b/lib/bundler/environment_preserver.rb
@@ -19,14 +19,7 @@ module Bundler
BUNDLER_PREFIX = "BUNDLER_ORIG_"
def self.from_env
- new(env_to_hash(ENV), BUNDLER_KEYS)
- end
-
- def self.env_to_hash(env)
- to_hash = env.to_hash
- return to_hash unless Gem.win_platform?
-
- to_hash.each_with_object({}) {|(k,v), a| a[k.upcase] = v }
+ new(ENV.to_hash, BUNDLER_KEYS)
end
# @param env [Hash]
@@ -39,18 +32,7 @@ module Bundler
# Replaces `ENV` with the bundler environment variables backed up
def replace_with_backup
- unless Gem.win_platform?
- ENV.replace(backup)
- return
- end
-
- # Fallback logic for Windows below to workaround
- # https://bugs.ruby-lang.org/issues/16798. Can be dropped once all
- # supported rubies include the fix for that.
-
- ENV.clear
-
- backup.each {|k, v| ENV[k] = v }
+ ENV.replace(backup)
end
# @return [Hash]
@@ -58,9 +40,9 @@ module Bundler
env = @original.clone
@keys.each do |key|
value = env[key]
- if !value.nil? && !value.empty?
+ if !value.nil?
env[@prefix + key] ||= value
- elsif value.nil?
+ else
env[@prefix + key] ||= INTENTIONALLY_NIL
end
end
@@ -72,7 +54,7 @@ module Bundler
env = @original.clone
@keys.each do |key|
value_original = env[@prefix + key]
- next if value_original.nil? || value_original.empty?
+ next if value_original.nil?
if value_original == INTENTIONALLY_NIL
env.delete(key)
else
diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb
index b6a11cc721..a0ce739ad7 100644
--- a/lib/bundler/errors.rb
+++ b/lib/bundler/errors.rb
@@ -217,17 +217,33 @@ module Bundler
end
class InsecureInstallPathError < BundlerError
- def initialize(path)
+ def initialize(name, path)
+ @name = name
@path = path
end
def message
- "The installation path is insecure. Bundler cannot continue.\n" \
- "#{@path} is world-writable (without sticky bit).\n" \
- "Bundler cannot safely replace gems in world-writeable directories due to potential vulnerabilities.\n" \
- "Please change the permissions of this directory or choose a different install path."
+ "Bundler cannot reinstall #{@name} because there's a previous installation of it at #{@path} that is unsafe to remove.\n" \
+ "The parent of #{@path} is world-writable and does not have the sticky bit set, making it insecure to remove due to potential vulnerabilities.\n" \
+ "Please change the permissions of #{File.dirname(@path)} or choose a different install path."
end
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
+
+ class InvalidArgumentError < BundlerError; status_code(40); end
end
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index 42bd2c0984..14721623f9 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -3,7 +3,7 @@
require_relative "vendored_persistent"
require_relative "vendored_timeout"
require "cgi"
-require "securerandom"
+require_relative "vendored_securerandom"
require "zlib"
module Bundler
@@ -111,7 +111,7 @@ module Bundler
spec -= [nil, "ruby", ""]
spec_file_name = "#{spec.join "-"}.gemspec"
- uri = Bundler::URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
+ uri = Gem::URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
spec = if uri.scheme == "file"
path = Gem::Util.correct_for_windows_path(uri.path)
Bundler.safe_load_marshal Bundler.rubygems.inflate(Gem.read_binary(path))
@@ -182,7 +182,7 @@ module Bundler
agent << " ci/#{cis.join(",")}" if cis.any?
# add a random ID so we can consolidate runs server-side
- agent << " " << SecureRandom.hex(8)
+ agent << " " << Gem::SecureRandom.hex(8)
# add any user agent strings set in the config
extra_ua = Bundler.settings[:user_agent]
@@ -255,7 +255,7 @@ module Bundler
con = Gem::Net::HTTP::Persistent.new name: "bundler", proxy: :ENV
if gem_proxy = Gem.configuration[:http_proxy]
- con.proxy = Bundler::URI.parse(gem_proxy) if gem_proxy != :no_proxy
+ con.proxy = Gem::URI.parse(gem_proxy) if gem_proxy != :no_proxy
end
if remote_uri.scheme == "https"
diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb
index db914839b1..6e5656d26a 100644
--- a/lib/bundler/fetcher/compact_index.rb
+++ b/lib/bundler/fetcher/compact_index.rb
@@ -4,8 +4,6 @@ require_relative "base"
require_relative "../worker"
module Bundler
- autoload :CompactIndexClient, File.expand_path("../compact_index_client", __dir__)
-
class Fetcher
class CompactIndex < Base
def self.compact_index_request(method_name)
@@ -36,15 +34,8 @@ module Bundler
until remaining_gems.empty?
log_specs { "Looking up gems #{remaining_gems.inspect}" }
-
- deps = begin
- parallel_compact_index_client.dependencies(remaining_gems)
- rescue TooManyRequestsError
- @bundle_worker&.stop
- @bundle_worker = nil # reset it. Not sure if necessary
- serial_compact_index_client.dependencies(remaining_gems)
- end
- next_gems = deps.flat_map {|d| d[3].flat_map(&:first) }.uniq
+ deps = fetch_gem_infos(remaining_gems).flatten(1)
+ next_gems = deps.flat_map {|d| d[CompactIndexClient::INFO_DEPS].flat_map(&:first) }.uniq
deps.each {|dep| gem_info << dep }
complete_gems.concat(deps.map(&:first)).uniq!
remaining_gems = next_gems - complete_gems
@@ -61,7 +52,7 @@ module Bundler
return nil
end
# Read info file checksums out of /versions, so we can know if gems are up to date
- compact_index_client.update_and_parse_checksums!
+ compact_index_client.available?
rescue CompactIndexClient::Updater::MismatchedChecksumError => e
Bundler.ui.debug(e.message)
nil
@@ -81,20 +72,20 @@ module Bundler
end
end
- def parallel_compact_index_client
- compact_index_client.execution_mode = lambda do |inputs, &blk|
- func = lambda {|object, _index| blk.call(object) }
- worker = bundle_worker(func)
- inputs.each {|input| worker.enq(input) }
- inputs.map { worker.deq }
- end
-
- compact_index_client
+ def fetch_gem_infos(names)
+ in_parallel(names) {|name| compact_index_client.info(name) }
+ rescue TooManyRequestsError # rubygems.org is rate limiting us, slow down.
+ @bundle_worker&.stop
+ @bundle_worker = nil # reset it. Not sure if necessary
+ compact_index_client.reset!
+ names.map {|name| compact_index_client.info(name) }
end
- def serial_compact_index_client
- compact_index_client.sequential_execution_mode!
- compact_index_client
+ def in_parallel(inputs, &blk)
+ func = lambda {|object, _index| blk.call(object) }
+ worker = bundle_worker(func)
+ inputs.each {|input| worker.enq(input) }
+ inputs.map { worker.deq }
end
def bundle_worker(func = nil)
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb
index b5282a322e..868b39b959 100644
--- a/lib/bundler/fetcher/downloader.rb
+++ b/lib/bundler/fetcher/downloader.rb
@@ -23,7 +23,7 @@ module Bundler
when Gem::Net::HTTPSuccess, Gem::Net::HTTPNotModified
response
when Gem::Net::HTTPRedirection
- new_uri = Bundler::URI.parse(response["location"])
+ new_uri = Gem::URI.parse(response["location"])
if new_uri.host == uri.host
new_uri.user = uri.user
new_uri.password = uri.password
diff --git a/lib/bundler/force_platform.rb b/lib/bundler/force_platform.rb
index 249a24ecd1..7af33218cb 100644
--- a/lib/bundler/force_platform.rb
+++ b/lib/bundler/force_platform.rb
@@ -2,8 +2,6 @@
module Bundler
module ForcePlatform
- private
-
# The `:force_ruby_platform` value used by dependencies for resolution, and
# by locked specifications for materialization is `false` by default, except
# for TruffleRuby. TruffleRuby generally needs to force the RUBY platform
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
index d535d54f9b..5ce0ef6280 100644
--- a/lib/bundler/gem_helper.rb
+++ b/lib/bundler/gem_helper.rb
@@ -47,7 +47,7 @@ module Bundler
built_gem_path = build_gem
end
- desc "Generate SHA512 checksum if #{name}-#{version}.gem into the checksums directory."
+ desc "Generate SHA512 checksum of #{name}-#{version}.gem into the checksums directory."
task "build:checksum" => "build" do
build_checksum(built_gem_path)
end
diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb
index de007523ec..70ccceb491 100644
--- a/lib/bundler/gem_helpers.rb
+++ b/lib/bundler/gem_helpers.rb
@@ -46,19 +46,26 @@ module Bundler
end
module_function :platform_specificity_match
- def select_best_platform_match(specs, platform)
- matching = specs.select {|spec| spec.match_platform(platform) }
+ def select_best_platform_match(specs, platform, force_ruby: false, prefer_locked: false)
+ matching = if force_ruby
+ specs.select {|spec| spec.match_platform(Gem::Platform::RUBY) && spec.force_ruby_platform! }
+ else
+ specs.select {|spec| spec.match_platform(platform) }
+ end
+
+ if prefer_locked
+ locked_originally = matching.select {|spec| spec.is_a?(LazySpecification) }
+ return locked_originally if locked_originally.any?
+ end
sort_best_platform_match(matching, platform)
end
module_function :select_best_platform_match
- def force_ruby_platform(specs)
- matching = specs.select {|spec| spec.match_platform(Gem::Platform::RUBY) && spec.force_ruby_platform! }
-
- sort_best_platform_match(matching, Gem::Platform::RUBY)
+ def select_best_local_platform_match(specs, force_ruby: false)
+ select_best_platform_match(specs, local_platform, force_ruby: force_ruby).map(&:materialize_for_installation).compact
end
- module_function :force_ruby_platform
+ module_function :select_best_local_platform_match
def sort_best_platform_match(matching, platform)
exact = matching.select {|spec| spec.platform == platform }
diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb
index c7eacd1930..ecc65b4956 100644
--- a/lib/bundler/gem_version_promoter.rb
+++ b/lib/bundler/gem_version_promoter.rb
@@ -45,17 +45,37 @@ module Bundler
# Given a Resolver::Package and an Array of Specifications of available
# versions for a gem, this method will return the Array of Specifications
- # sorted (and possibly truncated if strict is true) in an order to give
- # preference to the current level (:major, :minor or :patch) when resolution
- # is deciding what versions best resolve all dependencies in the bundle.
+ # sorted in an order to give preference to the current level (:major, :minor
+ # or :patch) when resolution is deciding what versions best resolve all
+ # dependencies in the bundle.
# @param package [Resolver::Package] The package being resolved.
# @param specs [Specification] An array of Specifications for the package.
- # @return [Specification] A new instance of the Specification Array sorted and
- # possibly filtered.
+ # @return [Specification] A new instance of the Specification Array sorted.
def sort_versions(package, specs)
- specs = filter_dep_specs(specs, package) if strict
+ locked_version = package.locked_version
- sort_dep_specs(specs, package)
+ result = specs.sort do |a, b|
+ unless package.prerelease_specified? || pre?
+ a_pre = a.prerelease?
+ b_pre = b.prerelease?
+
+ next 1 if a_pre && !b_pre
+ next -1 if b_pre && !a_pre
+ end
+
+ if major? || locked_version.nil?
+ b <=> a
+ elsif either_version_older_than_locked?(a, b, locked_version)
+ b <=> a
+ elsif segments_do_not_match?(a, b, :major)
+ a <=> b
+ elsif !minor? && segments_do_not_match?(a, b, :minor)
+ a <=> b
+ else
+ b <=> a
+ end
+ end
+ post_sort(result, package.unlock?, locked_version)
end
# @return [bool] Convenience method for testing value of level variable.
@@ -73,9 +93,18 @@ module Bundler
pre == true
end
- private
+ # Given a Resolver::Package and an Array of Specifications of available
+ # versions for a gem, this method will truncate the Array if strict
+ # is true. That means filtering out downgrades from the version currently
+ # locked, and filtering out upgrades that go past the selected level (major,
+ # minor, or patch).
+ # @param package [Resolver::Package] The package being resolved.
+ # @param specs [Specification] An array of Specifications for the package.
+ # @return [Specification] A new instance of the Specification Array
+ # truncated.
+ def filter_versions(package, specs)
+ return specs unless strict
- def filter_dep_specs(specs, package)
locked_version = package.locked_version
return specs if locked_version.nil? || major?
@@ -89,32 +118,7 @@ module Bundler
end
end
- def sort_dep_specs(specs, package)
- locked_version = package.locked_version
-
- result = specs.sort do |a, b|
- unless package.prerelease_specified? || pre?
- a_pre = a.prerelease?
- b_pre = b.prerelease?
-
- next -1 if a_pre && !b_pre
- next 1 if b_pre && !a_pre
- end
-
- if major? || locked_version.nil?
- a <=> b
- elsif either_version_older_than_locked?(a, b, locked_version)
- a <=> b
- elsif segments_do_not_match?(a, b, :major)
- b <=> a
- elsif !minor? && segments_do_not_match?(a, b, :minor)
- b <=> a
- else
- a <=> b
- end
- end
- post_sort(result, package.unlock?, locked_version)
- end
+ private
def either_version_older_than_locked?(a, b, locked_version)
a.version < locked_version || b.version < locked_version
@@ -133,13 +137,13 @@ module Bundler
if unlock || locked_version.nil?
result
else
- move_version_to_end(result, locked_version)
+ move_version_to_beginning(result, locked_version)
end
end
- def move_version_to_end(result, version)
+ def move_version_to_beginning(result, version)
move, keep = result.partition {|s| s.version.to_s == version.to_s }
- keep.concat(move)
+ move.concat(keep)
end
end
end
diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb
index 2cf7754ecb..c7e93c9ee0 100644
--- a/lib/bundler/injector.rb
+++ b/lib/bundler/injector.rb
@@ -23,10 +23,7 @@ module Bundler
# @param [Pathname] lockfile_path The lockfile in which to inject the new dependency.
# @return [Array]
def inject(gemfile_path, lockfile_path)
- if Bundler.frozen_bundle?
- # ensure the lock and Gemfile are synced
- Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true)
- end
+ Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true)
# temporarily unfreeze
Bundler.settings.temporary(deployment: false, frozen: false) do
@@ -50,7 +47,7 @@ module Bundler
append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @deps.any?
# since we resolved successfully, write out the lockfile
- @definition.lock(Bundler.default_lockfile)
+ @definition.lock
# invalidate the cached Bundler.definition
Bundler.reset_paths!
@@ -120,9 +117,10 @@ module Bundler
github = ", :github => \"#{d.github}\"" unless d.github.nil?
branch = ", :branch => \"#{d.branch}\"" unless d.branch.nil?
ref = ", :ref => \"#{d.ref}\"" unless d.ref.nil?
+ glob = ", :glob => \"#{d.glob}\"" unless d.glob.nil?
require_path = ", :require => #{convert_autorequire(d.autorequire)}" unless d.autorequire.nil?
- %(gem #{name}#{requirement}#{group}#{source}#{path}#{git}#{github}#{branch}#{ref}#{require_path})
+ %(gem #{name}#{requirement}#{group}#{source}#{path}#{git}#{github}#{branch}#{ref}#{glob}#{require_path})
end.join("\n")
end
diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb
index ae4ccf2138..ca17d0233e 100644
--- a/lib/bundler/inline.rb
+++ b/lib/bundler/inline.rb
@@ -39,18 +39,20 @@ def gemfile(install = false, options = {}, &gemfile)
Bundler.ui = ui
raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty?
- Bundler.with_unbundled_env do
+ old_gemfile = ENV["BUNDLE_GEMFILE"]
+
+ Bundler.unbundle_env!
+
+ begin
Bundler.instance_variable_set(:@bundle_path, Pathname.new(Gem.dir))
Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile"
Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins?
builder = Bundler::Dsl.new
builder.instance_eval(&gemfile)
- builder.check_primary_source_safety
Bundler.settings.temporary(deployment: false, frozen: false) do
definition = builder.to_definition(nil, true)
- def definition.lock(*); end
definition.validate_runtime!
if install || definition.missing_specs?
@@ -62,12 +64,31 @@ def gemfile(install = false, options = {}, &gemfile)
end
end
- runtime = Bundler::Runtime.new(nil, definition)
- runtime.setup.require
- end
- end
+ begin
+ runtime = Bundler::Runtime.new(nil, definition).setup
+ rescue Gem::LoadError => e
+ name = e.name
+ version = e.requirement.requirements.first[1]
+ activated_version = Gem.loaded_specs[name].version
+
+ Bundler.ui.info \
+ "The #{name} gem was resolved to #{version}, but #{activated_version} was activated by Bundler while installing it, causing a conflict. " \
+ "Bundler will now retry resolving with #{activated_version} instead."
- if ENV["BUNDLE_GEMFILE"].nil?
- ENV["BUNDLE_GEMFILE"] = ""
+ builder.dependencies.delete_if {|d| d.name == name }
+ builder.instance_eval { gem name, activated_version }
+ definition = builder.to_definition(nil, true)
+
+ retry
+ end
+
+ runtime.require
+ end
+ ensure
+ if old_gemfile
+ ENV["BUNDLE_GEMFILE"] = old_gemfile
+ else
+ ENV["BUNDLE_GEMFILE"] = ""
+ end
end
end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index e837f732cf..0d7f26f9b7 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -69,9 +69,7 @@ module Bundler
Bundler.create_bundle_path
ProcessLock.lock do
- if Bundler.frozen_bundle?
- @definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
- end
+ @definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
if @definition.dependencies.empty?
Bundler.ui.warn "The Gemfile specifies no dependencies"
@@ -196,9 +194,14 @@ module Bundler
# that said, it's a rare situation (other than rake), and parallel
# installation is SO MUCH FASTER. so we let people opt in.
def install(options)
- force = options["force"]
+ standalone = options[:standalone]
+ force = options[:force]
+ local = options[:local]
jobs = installation_parallelization(options)
- install_in_parallel jobs, options[:standalone], force
+ spec_installations = ParallelInstaller.call(self, @definition.specs, jobs, standalone, force, local: local)
+ spec_installations.each do |installation|
+ post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message?
+ end
end
def installation_parallelization(options)
@@ -218,7 +221,7 @@ module Bundler
requested_path_gems = @definition.requested_specs.select {|s| s.source.is_a?(Source::Path) }
path_plugin_files = requested_path_gems.map do |spec|
- Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
+ spec.matches_for_glob("rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
rescue TypeError
error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
raise Gem::InvalidSpecificationException, error_message
@@ -240,28 +243,21 @@ module Bundler
end
end
- def install_in_parallel(size, standalone, force = false)
- spec_installations = ParallelInstaller.call(self, @definition.specs, size, standalone, force)
- spec_installations.each do |installation|
- post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message?
- end
- end
-
# returns whether or not a re-resolve was needed
def resolve_if_needed(options)
- @definition.resolution_mode = options
-
- if !@definition.unlocking? && !options["force"] && !Bundler.settings[:inline] && Bundler.default_lockfile.file?
- return false if @definition.nothing_changed? && !@definition.missing_specs?
+ @definition.prefer_local! if options[:"prefer-local"]
+
+ if options[:local] || (@definition.no_resolve_needed? && !@definition.missing_specs?)
+ @definition.resolve_with_cache!
+ false
+ else
+ @definition.resolve_remotely!
+ true
end
-
- @definition.setup_sources_for_resolve
-
- true
end
- def lock(opts = {})
- @definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
+ def lock
+ @definition.lock
end
end
end
diff --git a/lib/bundler/installer/gem_installer.rb b/lib/bundler/installer/gem_installer.rb
index d3bbcc90f5..1da91857bd 100644
--- a/lib/bundler/installer/gem_installer.rb
+++ b/lib/bundler/installer/gem_installer.rb
@@ -2,14 +2,15 @@
module Bundler
class GemInstaller
- attr_reader :spec, :standalone, :worker, :force, :installer
+ attr_reader :spec, :standalone, :worker, :force, :local, :installer
- def initialize(spec, installer, standalone = false, worker = 0, force = false)
+ def initialize(spec, installer, standalone = false, worker = 0, force = false, local = false)
@spec = spec
@installer = installer
@standalone = standalone
@worker = worker
@force = force
+ @local = local
end
def install_from_spec
@@ -54,7 +55,7 @@ module Bundler
spec.source.install(
spec,
force: force,
- ensure_builtin_gems_cached: standalone,
+ local: local,
build_args: Array(spec_settings),
previous_spec: previous_spec,
)
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
index e745088f81..d10e5ec924 100644
--- a/lib/bundler/installer/parallel_installer.rb
+++ b/lib/bundler/installer/parallel_installer.rb
@@ -68,11 +68,12 @@ module Bundler
attr_reader :size
- def initialize(installer, all_specs, size, standalone, force, skip: nil)
+ def initialize(installer, all_specs, size, standalone, force, local: false, skip: nil)
@installer = installer
@size = size
@standalone = standalone
@force = force
+ @local = local
@specs = all_specs.map {|s| SpecInstallation.new(s) }
@specs.each do |spec_install|
spec_install.state = :installed if skip.include?(spec_install.name)
@@ -127,7 +128,7 @@ module Bundler
def do_install(spec_install, worker_num)
Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL, spec_install)
gem_installer = Bundler::GemInstaller.new(
- spec_install.spec, @installer, @standalone, worker_num, @force
+ spec_install.spec, @installer, @standalone, worker_num, @force, @local
)
success, message = gem_installer.install_from_spec
if success
diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb
index 5331df2e95..cf5993448c 100644
--- a/lib/bundler/installer/standalone.rb
+++ b/lib/bundler/installer/standalone.rb
@@ -58,9 +58,6 @@ module Bundler
else
SharedHelpers.relative_path_to(full_path, from: Bundler.root.join(bundler_path))
end
- rescue TypeError
- error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
- raise Gem::InvalidSpecificationException.new(error_message)
end
def prevent_gem_activation
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 38aea2a4aa..8669e021c2 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -4,6 +4,7 @@ require_relative "force_platform"
module Bundler
class LazySpecification
+ include MatchMetadata
include MatchPlatform
include ForcePlatform
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index 1e11621e55..8a15e356c4 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -272,7 +272,7 @@ module Bundler
end
def parse_platform(line)
- @platforms << Gem::Platform.new($1) if line =~ /^ (.*)$/
+ @platforms << Gem::Platform.new($1.strip) if line =~ /^ (.*)$/
end
def parse_bundled_with(line)
diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1
index 210911dcf4..dae05bd945 100644
--- a/lib/bundler/man/bundle-add.1
+++ b/lib/bundler/man/bundle-add.1
@@ -1,24 +1,12 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-ADD" "1" "December 2023" ""
+.TH "BUNDLE\-ADD" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install
.SH "SYNOPSIS"
-\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-path=PATH] [\-\-git=GIT] [\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-skip\-install] [\-\-strict] [\-\-optimistic]
+\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-path=PATH] [\-\-git=GIT|\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-skip\-install] [\-\-strict|\-\-optimistic]
.SH "DESCRIPTION"
-Adds the named gem to the Gemfile and run \fBbundle install\fR\. \fBbundle install\fR can be avoided by using the flag \fB\-\-skip\-install\fR\.
-.P
-Example:
-.P
-bundle add rails
-.P
-bundle add rails \-\-version "< 3\.0, > 1\.1"
-.P
-bundle add rails \-\-version "~> 5\.0\.0" \-\-source "https://gems\.example\.com" \-\-group "development"
-.P
-bundle add rails \-\-skip\-install
-.P
-bundle add rails \-\-group "development, test"
+Adds the named gem to the [\fBGemfile(5)\fR][Gemfile(5)] and run \fBbundle install\fR\. \fBbundle install\fR can be avoided by using the flag \fB\-\-skip\-install\fR\.
.SH "OPTIONS"
.TP
\fB\-\-version\fR, \fB\-v\fR
@@ -56,4 +44,27 @@ Adds optimistic declaration of version\.
.TP
\fB\-\-strict\fR
Adds strict declaration of version\.
-
+.SH "EXAMPLES"
+.IP "1." 4
+You can add the \fBrails\fR gem to the Gemfile without any version restriction\. The source of the gem will be the global source\.
+.IP
+\fBbundle add rails\fR
+.IP "2." 4
+You can add the \fBrails\fR gem with version greater than 1\.1 (not including 1\.1) and less than 3\.0\.
+.IP
+\fBbundle add rails \-\-version "> 1\.1, < 3\.0"\fR
+.IP "3." 4
+You can use the \fBhttps://gems\.example\.com\fR custom source and assign the gem to a group\.
+.IP
+\fBbundle add rails \-\-version "~> 5\.0\.0" \-\-source "https://gems\.example\.com" \-\-group "development"\fR
+.IP "4." 4
+The following adds the \fBgem\fR entry to the Gemfile without installing the gem\. You can install gems later via \fBbundle install\fR\.
+.IP
+\fBbundle add rails \-\-skip\-install\fR
+.IP "5." 4
+You can assign the gem to more than one group\.
+.IP
+\fBbundle add rails \-\-group "development, test"\fR
+.IP "" 0
+.SH "SEE ALSO"
+Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR, bundle\-remove(1) \fIbundle\-remove\.1\.html\fR
diff --git a/lib/bundler/man/bundle-add.1.ronn b/lib/bundler/man/bundle-add.1.ronn
index 37c92e5fcd..8b38c7a248 100644
--- a/lib/bundler/man/bundle-add.1.ronn
+++ b/lib/bundler/man/bundle-add.1.ronn
@@ -1,26 +1,19 @@
bundle-add(1) -- Add gem to the Gemfile and run bundle install
-================================================================
+==============================================================
## SYNOPSIS
-`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--path=PATH] [--git=GIT] [--github=GITHUB] [--branch=BRANCH] [--ref=REF] [--skip-install] [--strict] [--optimistic]
+`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE]
+ [--path=PATH] [--git=GIT|--github=GITHUB] [--branch=BRANCH] [--ref=REF]
+ [--skip-install] [--strict|--optimistic]
## DESCRIPTION
-Adds the named gem to the Gemfile and run `bundle install`. `bundle install` can be avoided by using the flag `--skip-install`.
-Example:
-
-bundle add rails
-
-bundle add rails --version "< 3.0, > 1.1"
-
-bundle add rails --version "~> 5.0.0" --source "https://gems.example.com" --group "development"
-
-bundle add rails --skip-install
-
-bundle add rails --group "development, test"
+Adds the named gem to the [`Gemfile(5)`][Gemfile(5)] and run `bundle install`.
+`bundle install` can be avoided by using the flag `--skip-install`.
## OPTIONS
+
* `--version`, `-v`:
Specify version requirements(s) for the added gem.
@@ -56,3 +49,33 @@ bundle add rails --group "development, test"
* `--strict`:
Adds strict declaration of version.
+
+## EXAMPLES
+
+1. You can add the `rails` gem to the Gemfile without any version restriction.
+ The source of the gem will be the global source.
+
+ `bundle add rails`
+
+2. You can add the `rails` gem with version greater than 1.1 (not including 1.1) and less than 3.0.
+
+ `bundle add rails --version "> 1.1, < 3.0"`
+
+3. You can use the `https://gems.example.com` custom source and assign the gem
+ to a group.
+
+ `bundle add rails --version "~> 5.0.0" --source "https://gems.example.com" --group "development"`
+
+4. The following adds the `gem` entry to the Gemfile without installing the
+ gem. You can install gems later via `bundle install`.
+
+ `bundle add rails --skip-install`
+
+5. You can assign the gem to more than one group.
+
+ `bundle add rails --group "development, test"`
+
+## SEE ALSO
+
+[Gemfile(5)](https://bundler.io/man/gemfile.5.html),
+[bundle-remove(1)](bundle-remove.1.html)
diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1
index b71347d6e1..56c9966e75 100644
--- a/lib/bundler/man/bundle-binstubs.1
+++ b/lib/bundler/man/bundle-binstubs.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-BINSTUBS" "1" "December 2023" ""
+.TH "BUNDLE\-BINSTUBS" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1
index 5f03c38abe..d634eef203 100644
--- a/lib/bundler/man/bundle-cache.1
+++ b/lib/bundler/man/bundle-cache.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-CACHE" "1" "December 2023" ""
+.TH "BUNDLE\-CACHE" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1
index bc24dfe2b2..e15a41e4fd 100644
--- a/lib/bundler/man/bundle-check.1
+++ b/lib/bundler/man/bundle-check.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-CHECK" "1" "December 2023" ""
+.TH "BUNDLE\-CHECK" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
.SH "SYNOPSIS"
@@ -9,6 +9,8 @@
\fBcheck\fR searches the local machine for each of the gems requested in the Gemfile\. If all gems are found, Bundler prints a success message and exits with a status of 0\.
.P
If not, the first missing gem is listed and Bundler exits status 1\.
+.P
+If the lockfile needs to be updated then it will be resolved using the gems installed on the local machine, if they satisfy the requirements\.
.SH "OPTIONS"
.TP
\fB\-\-dry\-run\fR
diff --git a/lib/bundler/man/bundle-check.1.ronn b/lib/bundler/man/bundle-check.1.ronn
index f2846b8ff2..eb3ff1daf9 100644
--- a/lib/bundler/man/bundle-check.1.ronn
+++ b/lib/bundler/man/bundle-check.1.ronn
@@ -15,6 +15,9 @@ a status of 0.
If not, the first missing gem is listed and Bundler exits status 1.
+If the lockfile needs to be updated then it will be resolved using the gems
+installed on the local machine, if they satisfy the requirements.
+
## OPTIONS
* `--dry-run`:
diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1
index 00787da5f1..aa5ccf7594 100644
--- a/lib/bundler/man/bundle-clean.1
+++ b/lib/bundler/man/bundle-clean.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-CLEAN" "1" "December 2023" ""
+.TH "BUNDLE\-CLEAN" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1
index c5a976da46..47104fb5c6 100644
--- a/lib/bundler/man/bundle-config.1
+++ b/lib/bundler/man/bundle-config.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-CONFIG" "1" "December 2023" ""
+.TH "BUNDLE\-CONFIG" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-config\fR \- Set bundler configuration options
.SH "SYNOPSIS"
@@ -95,8 +95,6 @@ Any periods in the configuration keys must be replaced with two underscores when
.SH "LIST OF AVAILABLE KEYS"
The following is a list of all configuration keys and their purpose\. You can learn more about their operation in bundle install(1) \fIbundle\-install\.1\.html\fR\.
.IP "\(bu" 4
-\fBallow_deployment_source_credential_changes\fR (\fBBUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES\fR): When in deployment mode, allow changing the credentials to a gem's source\. Ex: \fBhttps://some\.host\.com/gems/path/\fR \-> \fBhttps://user_name:password@some\.host\.com/gems/path\fR
-.IP "\(bu" 4
\fBallow_offline_install\fR (\fBBUNDLE_ALLOW_OFFLINE_INSTALL\fR): Allow Bundler to use cached data when installing without network access\.
.IP "\(bu" 4
\fBauto_clean_without_path\fR (\fBBUNDLE_AUTO_CLEAN_WITHOUT_PATH\fR): Automatically run \fBbundle clean\fR after installing when an explicit \fBpath\fR has not been set and Bundler is not installing into the system gems\.
@@ -309,7 +307,7 @@ Any \fB\.\fR characters in a host name are mapped to a double underscore (\fB__\
.P
This means that if you have a gem server named \fBmy\.gem\-host\.com\fR, you'll need to use the \fBBUNDLE_MY__GEM___HOST__COM\fR variable to configure credentials for it through ENV\.
.SH "CONFIGURE BUNDLER DIRECTORIES"
-Bundler's home, config, cache and plugin directories are able to be configured through environment variables\. The default location for Bundler's home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
+Bundler's home, cache and plugin directories and config file can be configured through environment variables\. The default location for Bundler's home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
.IP "" 4
.nf
BUNDLE_USER_HOME : $HOME/\.bundle
diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn
index 587d31dbad..1a0ec2a5dc 100644
--- a/lib/bundler/man/bundle-config.1.ronn
+++ b/lib/bundler/man/bundle-config.1.ronn
@@ -137,9 +137,6 @@ the environment variable `BUNDLE_LOCAL__RACK`.
The following is a list of all configuration keys and their purpose. You can
learn more about their operation in [bundle install(1)](bundle-install.1.html).
-* `allow_deployment_source_credential_changes` (`BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES`):
- When in deployment mode, allow changing the credentials to a gem's source.
- Ex: `https://some.host.com/gems/path/` -> `https://user_name:password@some.host.com/gems/path`
* `allow_offline_install` (`BUNDLE_ALLOW_OFFLINE_INSTALL`):
Allow Bundler to use cached data when installing without network access.
* `auto_clean_without_path` (`BUNDLE_AUTO_CLEAN_WITHOUT_PATH`):
@@ -400,7 +397,7 @@ through ENV.
## CONFIGURE BUNDLER DIRECTORIES
-Bundler's home, config, cache and plugin directories are able to be configured
+Bundler's home, cache and plugin directories and config file can be configured
through environment variables. The default location for Bundler's home directory is
`~/.bundle`, which all directories inherit from by default. The following
outlines the available environment variables and their default values
diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1
index 14e5f55647..f2b2ddaed0 100644
--- a/lib/bundler/man/bundle-console.1
+++ b/lib/bundler/man/bundle-console.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-CONSOLE" "1" "December 2023" ""
+.TH "BUNDLE\-CONSOLE" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1
index aea8bacdad..f225d0cd79 100644
--- a/lib/bundler/man/bundle-doctor.1
+++ b/lib/bundler/man/bundle-doctor.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-DOCTOR" "1" "December 2023" ""
+.TH "BUNDLE\-DOCTOR" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-doctor\fR \- Checks the bundle for common problems
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1
index af622f4423..e16b7bc747 100644
--- a/lib/bundler/man/bundle-exec.1
+++ b/lib/bundler/man/bundle-exec.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-EXEC" "1" "December 2023" ""
+.TH "BUNDLE\-EXEC" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-exec\fR \- Execute a command in the context of the bundle
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1
index bc62a883ea..e6e58cd409 100644
--- a/lib/bundler/man/bundle-gem.1
+++ b/lib/bundler/man/bundle-gem.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-GEM" "1" "December 2023" ""
+.TH "BUNDLE\-GEM" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem
.SH "SYNOPSIS"
@@ -44,6 +44,8 @@ When Bundler is configured to not generate tests, an interactive prompt will be
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler's global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
+\fB\-\-no\-test\fR: Do not use a test framework (overrides \fB\-\-test\fR specified in the global config)\.
+.IP "\(bu" 4
\fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR: Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.IP
When Bundler is configured to generate CI files, this defaults to Bundler's global config setting \fBgem\.ci\fR\.
@@ -52,6 +54,8 @@ When Bundler is configured to not generate CI files, an interactive prompt will
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler's global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
+\fB\-\-no\-ci\fR: Do not use a continuous integration service (overrides \fB\-\-ci\fR specified in the global config)\.
+.IP "\(bu" 4
\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR: Specify the linter and code formatter that Bundler should add to the project's development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.IP
When Bundler is configured to add a linter, this defaults to Bundler's global config setting \fBgem\.linter\fR\.
@@ -60,6 +64,8 @@ When Bundler is configured not to add a linter, an interactive prompt will be di
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler's global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
+\fB\-\-no\-linter\fR: Do not add a linter (overrides \fB\-\-linter\fR specified in the global config)\.
+.IP "\(bu" 4
\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR: Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
.IP "" 0
.SH "SEE ALSO"
diff --git a/lib/bundler/man/bundle-gem.1.ronn b/lib/bundler/man/bundle-gem.1.ronn
index 46fa2f179f..2d71d8dabe 100644
--- a/lib/bundler/man/bundle-gem.1.ronn
+++ b/lib/bundler/man/bundle-gem.1.ronn
@@ -76,6 +76,10 @@ configuration file using the following names:
the answer will be saved in Bundler's global config for future `bundle gem`
use.
+* `--no-test`:
+ Do not use a test framework (overrides `--test` specified in the global
+ config).
+
* `--ci`, `--ci=github`, `--ci=gitlab`, `--ci=circle`:
Specify the continuous integration service that Bundler should use when
generating the project. Acceptable values are `github`, `gitlab`
@@ -92,6 +96,10 @@ configuration file using the following names:
the answer will be saved in Bundler's global config for future `bundle gem`
use.
+* `--no-ci`:
+ Do not use a continuous integration service (overrides `--ci` specified in
+ the global config).
+
* `--linter`, `--linter=rubocop`, `--linter=standard`:
Specify the linter and code formatter that Bundler should add to the
project's development dependencies. Acceptable values are `rubocop` and
@@ -108,6 +116,9 @@ configuration file using the following names:
the answer will be saved in Bundler's global config for future `bundle gem`
use.
+* `--no-linter`:
+ Do not add a linter (overrides `--linter` specified in the global config).
+
* `-e`, `--edit[=EDITOR]`:
Open the resulting GEM_NAME.gemspec in EDITOR, or the default editor if not
specified. The default is `$BUNDLER_EDITOR`, `$VISUAL`, or `$EDITOR`.
diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1
index 3e6d7a851e..d7a05f824e 100644
--- a/lib/bundler/man/bundle-help.1
+++ b/lib/bundler/man/bundle-help.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-HELP" "1" "December 2023" ""
+.TH "BUNDLE\-HELP" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-help\fR \- Displays detailed help for each subcommand
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1
index 43cc7d23b6..6b401a57f4 100644
--- a/lib/bundler/man/bundle-info.1
+++ b/lib/bundler/man/bundle-info.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-INFO" "1" "December 2023" ""
+.TH "BUNDLE\-INFO" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-info\fR \- Show information for the given gem in your bundle
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1
index 9c6f89a6b2..f2e444c7c2 100644
--- a/lib/bundler/man/bundle-init.1
+++ b/lib/bundler/man/bundle-init.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-INIT" "1" "December 2023" ""
+.TH "BUNDLE\-INIT" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-init\fR \- Generates a Gemfile into the current working directory
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1
index f8c4d4342e..8eb3633837 100644
--- a/lib/bundler/man/bundle-inject.1
+++ b/lib/bundler/man/bundle-inject.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-INJECT" "1" "December 2023" ""
+.TH "BUNDLE\-INJECT" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1
index a23763889b..7539d18f81 100644
--- a/lib/bundler/man/bundle-install.1
+++ b/lib/bundler/man/bundle-install.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-INSTALL" "1" "December 2023" ""
+.TH "BUNDLE\-INSTALL" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-install\fR \- Install the dependencies specified in your Gemfile
.SH "SYNOPSIS"
@@ -208,8 +208,8 @@ To explicitly update \fBactionpack\fR, including its dependencies which other ge
\fBSummary\fR: In general, after making a change to the Gemfile(5) , you should first try to run \fBbundle install\fR, which will guarantee that no other gem in the Gemfile(5) is impacted by the change\. If that does not work, run bundle update(1) \fIbundle\-update\.1\.html\fR\.
.SH "SEE ALSO"
.IP "\(bu" 4
-Gem install docs \fIhttp://guides\.rubygems\.org/rubygems\-basics/#installing\-gems\fR
+Gem install docs \fIhttps://guides\.rubygems\.org/rubygems\-basics/#installing\-gems\fR
.IP "\(bu" 4
-Rubygems signing docs \fIhttp://guides\.rubygems\.org/security/\fR
+Rubygems signing docs \fIhttps://guides\.rubygems\.org/security/\fR
.IP "" 0
diff --git a/lib/bundler/man/bundle-install.1.ronn b/lib/bundler/man/bundle-install.1.ronn
index ac0014e24e..ed8169de05 100644
--- a/lib/bundler/man/bundle-install.1.ronn
+++ b/lib/bundler/man/bundle-install.1.ronn
@@ -379,5 +379,5 @@ does not work, run [bundle update(1)](bundle-update.1.html).
## SEE ALSO
-* [Gem install docs](http://guides.rubygems.org/rubygems-basics/#installing-gems)
-* [Rubygems signing docs](http://guides.rubygems.org/security/)
+* [Gem install docs](https://guides.rubygems.org/rubygems-basics/#installing-gems)
+* [Rubygems signing docs](https://guides.rubygems.org/security/)
diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1
index 943f17ab83..5cbb1c3cfe 100644
--- a/lib/bundler/man/bundle-list.1
+++ b/lib/bundler/man/bundle-list.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-LIST" "1" "December 2023" ""
+.TH "BUNDLE\-LIST" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-list\fR \- List all the gems in the bundle
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1
index 041c2d739e..5f0d43a9aa 100644
--- a/lib/bundler/man/bundle-lock.1
+++ b/lib/bundler/man/bundle-lock.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-LOCK" "1" "December 2023" ""
+.TH "BUNDLE\-LOCK" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-lock\fR \- Creates / Updates a lockfile without installing
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1
index a349288987..fb5ff1fee7 100644
--- a/lib/bundler/man/bundle-open.1
+++ b/lib/bundler/man/bundle-open.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-OPEN" "1" "December 2023" ""
+.TH "BUNDLE\-OPEN" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-open\fR \- Opens the source directory for a gem in your bundle
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1
index a501fec83e..ea3005dd87 100644
--- a/lib/bundler/man/bundle-outdated.1
+++ b/lib/bundler/man/bundle-outdated.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-OUTDATED" "1" "December 2023" ""
+.TH "BUNDLE\-OUTDATED" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-outdated\fR \- List installed gems with newer versions available
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1
index 2ff1938585..c3058175fc 100644
--- a/lib/bundler/man/bundle-platform.1
+++ b/lib/bundler/man/bundle-platform.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-PLATFORM" "1" "December 2023" ""
+.TH "BUNDLE\-PLATFORM" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-platform\fR \- Displays platform compatibility information
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1
index 09e0d816ac..34437d9973 100644
--- a/lib/bundler/man/bundle-plugin.1
+++ b/lib/bundler/man/bundle-plugin.1
@@ -1,10 +1,10 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-PLUGIN" "1" "December 2023" ""
+.TH "BUNDLE\-PLUGIN" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-plugin\fR \- Manage Bundler plugins
.SH "SYNOPSIS"
-\fBbundle plugin\fR install PLUGINS [\-\-source=\fISOURCE\fR] [\-\-version=\fIversion\fR] [\-\-git|\-\-local_git=\fIgit\-url\fR] [\-\-branch=\fIbranch\fR|\-\-ref=\fIrev\fR]
+\fBbundle plugin\fR install PLUGINS [\-\-source=\fISOURCE\fR] [\-\-version=\fIversion\fR] [\-\-git=\fIgit\-url\fR] [\-\-branch=\fIbranch\fR|\-\-ref=\fIrev\fR] [\-\-path=\fIpath\fR]
.br
\fBbundle plugin\fR uninstall PLUGINS
.br
@@ -27,7 +27,7 @@ Install bundler\-graph gem from example\.com\. The global source, specified in s
You can specify the version of the gem via \fB\-\-version\fR\.
.TP
\fBbundle plugin install bundler\-graph \-\-git https://github\.com/rubygems/bundler\-graph\fR
-Install bundler\-graph gem from Git repository\. \fB\-\-git\fR can be replaced with \fB\-\-local\-git\fR\. You cannot use both \fB\-\-git\fR and \fB\-\-local\-git\fR\. You can use standard Git URLs like:
+Install bundler\-graph gem from Git repository\. You can use standard Git URLs like:
.IP
\fBssh://[user@]host\.xz[:port]/path/to/repo\.git\fR
.br
@@ -37,7 +37,10 @@ Install bundler\-graph gem from Git repository\. \fB\-\-git\fR can be replaced w
.br
\fBfile:///path/to/repo\fR
.IP
-When you specify \fB\-\-git\fR/\fB\-\-local\-git\fR, you can use \fB\-\-branch\fR or \fB\-\-ref\fR to specify any branch, tag, or commit hash (revision) to use\. When you specify both, only the latter is used\.
+When you specify \fB\-\-git\fR, you can use \fB\-\-branch\fR or \fB\-\-ref\fR to specify any branch, tag, or commit hash (revision) to use\.
+.TP
+\fBbundle plugin install bundler\-graph \-\-path \.\./bundler\-graph\fR
+Install bundler\-graph gem from a local path\.
.SS "uninstall"
Uninstall the plugin(s) specified in PLUGINS\.
.SS "list"
diff --git a/lib/bundler/man/bundle-plugin.1.ronn b/lib/bundler/man/bundle-plugin.1.ronn
index a11df4c162..b0a34660ea 100644
--- a/lib/bundler/man/bundle-plugin.1.ronn
+++ b/lib/bundler/man/bundle-plugin.1.ronn
@@ -4,7 +4,8 @@ bundle-plugin(1) -- Manage Bundler plugins
## SYNOPSIS
`bundle plugin` install PLUGINS [--source=<SOURCE>] [--version=<version>]
- [--git|--local_git=<git-url>] [--branch=<branch>|--ref=<rev>]<br>
+ [--git=<git-url>] [--branch=<branch>|--ref=<rev>]
+ [--path=<path>]<br>
`bundle plugin` uninstall PLUGINS<br>
`bundle plugin` list<br>
`bundle plugin` help [COMMAND]
@@ -29,14 +30,17 @@ Install the given plugin(s).
You can specify the version of the gem via `--version`.
* `bundle plugin install bundler-graph --git https://github.com/rubygems/bundler-graph`:
- Install bundler-graph gem from Git repository. `--git` can be replaced with `--local-git`. You cannot use both `--git` and `--local-git`. You can use standard Git URLs like:
+ Install bundler-graph gem from Git repository. You can use standard Git URLs like:
`ssh://[user@]host.xz[:port]/path/to/repo.git`<br>
`http[s]://host.xz[:port]/path/to/repo.git`<br>
`/path/to/repo`<br>
`file:///path/to/repo`
- When you specify `--git`/`--local-git`, you can use `--branch` or `--ref` to specify any branch, tag, or commit hash (revision) to use. When you specify both, only the latter is used.
+ When you specify `--git`, you can use `--branch` or `--ref` to specify any branch, tag, or commit hash (revision) to use.
+
+* `bundle plugin install bundler-graph --path ../bundler-graph`:
+ Install bundler-graph gem from a local path.
### uninstall
diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1
index 60609f4ee8..103c6f68ae 100644
--- a/lib/bundler/man/bundle-pristine.1
+++ b/lib/bundler/man/bundle-pristine.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-PRISTINE" "1" "December 2023" ""
+.TH "BUNDLE\-PRISTINE" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-pristine\fR \- Restores installed gems to their pristine condition
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1
index 31eae269c8..4a2ed4eb13 100644
--- a/lib/bundler/man/bundle-remove.1
+++ b/lib/bundler/man/bundle-remove.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-REMOVE" "1" "December 2023" ""
+.TH "BUNDLE\-REMOVE" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-remove\fR \- Removes gems from the Gemfile
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1
index 761629c625..dfbb439218 100644
--- a/lib/bundler/man/bundle-show.1
+++ b/lib/bundler/man/bundle-show.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-SHOW" "1" "December 2023" ""
+.TH "BUNDLE\-SHOW" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1
index 838261df0d..5eb9514f03 100644
--- a/lib/bundler/man/bundle-update.1
+++ b/lib/bundler/man/bundle-update.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-UPDATE" "1" "December 2023" ""
+.TH "BUNDLE\-UPDATE" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-update\fR \- Update your gems to the latest available versions
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1
index d9b0e7c3b1..a29858181a 100644
--- a/lib/bundler/man/bundle-version.1
+++ b/lib/bundler/man/bundle-version.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-VERSION" "1" "December 2023" ""
+.TH "BUNDLE\-VERSION" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-version\fR \- Prints Bundler version information
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1
index 0e7981ac9a..9609e098dd 100644
--- a/lib/bundler/man/bundle-viz.1
+++ b/lib/bundler/man/bundle-viz.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE\-VIZ" "1" "December 2023" ""
+.TH "BUNDLE\-VIZ" "1" "September 2024" ""
.SH "NAME"
\fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1
index 2417348be4..d84d788748 100644
--- a/lib/bundler/man/bundle.1
+++ b/lib/bundler/man/bundle.1
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "BUNDLE" "1" "December 2023" ""
+.TH "BUNDLE" "1" "September 2024" ""
.SH "NAME"
\fBbundle\fR \- Ruby Dependency Management
.SH "SYNOPSIS"
diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5
index 9f73687c8c..f24a1c540d 100644
--- a/lib/bundler/man/gemfile.5
+++ b/lib/bundler/man/gemfile.5
@@ -1,6 +1,6 @@
.\" generated with nRonn/v0.11.1
.\" https://github.com/n-ronn/nronn/tree/0.11.1
-.TH "GEMFILE" "5" "December 2023" ""
+.TH "GEMFILE" "5" "September 2024" ""
.SH "NAME"
\fBGemfile\fR \- A format for describing gem dependencies for Ruby programs
.SH "SYNOPSIS"
@@ -72,7 +72,7 @@ A Ruby engine is an implementation of the Ruby language\.
.IP "\(bu" 4
For background: the reference or original implementation of the Ruby programming language is called Matz's Ruby Interpreter \fIhttps://en\.wikipedia\.org/wiki/Ruby_MRI\fR, or MRI for short\. This is named after Ruby creator Yukihiro Matsumoto, also known as Matz\. MRI is also known as CRuby, because it is written in C\. MRI is the most widely used Ruby engine\.
.IP "\(bu" 4
-Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include JRuby \fIhttp://jruby\.org/\fR and TruffleRuby \fIhttps://www\.graalvm\.org/ruby/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\. TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM\.
+Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include JRuby \fIhttps://www\.jruby\.org/\fR and TruffleRuby \fIhttps://www\.graalvm\.org/ruby/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\. TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM\.
.IP "" 0
.SS "ENGINE VERSION"
Each application \fImay\fR specify a Ruby engine version\. If an engine version is specified, an engine \fImust\fR also be specified\. If the engine is "ruby" the engine version specified \fImust\fR match the Ruby version\.
@@ -216,6 +216,8 @@ The following platform values are deprecated and should be replaced with \fBwind
.IP "\(bu" 4
\fBmswin\fR, \fBmswin64\fR, \fBmingw32\fR, \fBx64_mingw\fR
.IP "" 0
+.P
+Note that, while unfortunately using the same terminology, the values of this option are different from the values that \fBbundle lock \-\-add\-platform\fR can take\. The values of this option are more closer to "Ruby Implementation" while the values that \fBbundle lock \-\-add\-platform\fR understands are more related to OS and architecture of the different systems where your lockfile will be used\.
.SS "FORCE_RUBY_PLATFORM"
If you always want the pure ruby variant of a gem to be chosen over platform specific variants, you can use the \fBforce_ruby_platform\fR option:
.IP "" 4
@@ -449,7 +451,7 @@ end
.fi
.IP "" 0
.SH "GEMSPEC"
-The \fB\.gemspec\fR \fIhttp://guides\.rubygems\.org/specification\-reference/\fR file is where you provide metadata about your gem to Rubygems\. Some required Gemspec attributes include the name, description, and homepage of your gem\. This is also where you specify the dependencies your gem needs to run\.
+The \fB\.gemspec\fR \fIhttps://guides\.rubygems\.org/specification\-reference/\fR file is where you provide metadata about your gem to Rubygems\. Some required Gemspec attributes include the name, description, and homepage of your gem\. This is also where you specify the dependencies your gem needs to run\.
.P
If you wish to use Bundler to help install dependencies for a gem while it is being developed, use the \fBgemspec\fR method to pull in the dependencies listed in the \fB\.gemspec\fR file\.
.P
diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn
index e8a1f8b79e..802549737e 100644
--- a/lib/bundler/man/gemfile.5.ronn
+++ b/lib/bundler/man/gemfile.5.ronn
@@ -96,7 +96,7 @@ What exactly is an Engine?
- [Other implementations](https://www.ruby-lang.org/en/about/) of Ruby exist.
Some of the more well-known implementations include
- [JRuby](http://jruby.org/) and [TruffleRuby](https://www.graalvm.org/ruby/).
+ [JRuby](https://www.jruby.org/) and [TruffleRuby](https://www.graalvm.org/ruby/).
Rubinius is an alternative implementation of Ruby written in Ruby.
JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine.
TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM.
@@ -242,6 +242,12 @@ The following platform values are deprecated and should be replaced with `window
* `mswin`, `mswin64`, `mingw32`, `x64_mingw`
+Note that, while unfortunately using the same terminology, the values of this
+option are different from the values that `bundle lock --add-platform` can take.
+The values of this option are more closer to "Ruby Implementation" while the
+values that `bundle lock --add-platform` understands are more related to OS and
+architecture of the different systems where your lockfile will be used.
+
### FORCE_RUBY_PLATFORM
If you always want the pure ruby variant of a gem to be chosen over platform
@@ -509,7 +515,7 @@ software is installed or some other conditions are met.
## GEMSPEC
-The [`.gemspec`](http://guides.rubygems.org/specification-reference/) file is where
+The [`.gemspec`](https://guides.rubygems.org/specification-reference/) file is where
you provide metadata about your gem to Rubygems. Some required Gemspec
attributes include the name, description, and homepage of your gem. This is
also where you specify the dependencies your gem needs to run.
diff --git a/lib/bundler/mirror.rb b/lib/bundler/mirror.rb
index 9d437a0951..494a6d6aef 100644
--- a/lib/bundler/mirror.rb
+++ b/lib/bundler/mirror.rb
@@ -47,7 +47,7 @@ module Bundler
def fetch_valid_mirror_for(uri)
downcased = uri.to_s.downcase
- mirror = @mirrors[downcased] || @mirrors[Bundler::URI(downcased).host] || Mirror.new(uri)
+ mirror = @mirrors[downcased] || @mirrors[Gem::URI(downcased).host] || Mirror.new(uri)
mirror.validate!(@prober)
mirror = Mirror.new(uri) unless mirror.valid?
mirror
@@ -74,7 +74,7 @@ module Bundler
@uri = if uri.nil?
nil
else
- Bundler::URI(uri.to_s)
+ Gem::URI(uri.to_s)
end
@valid = nil
end
@@ -126,7 +126,7 @@ module Bundler
if uri == "all"
@all = true
else
- @uri = Bundler::URI(uri).absolute? ? Settings.normalize_uri(uri) : uri
+ @uri = Gem::URI(uri).absolute? ? Settings.normalize_uri(uri) : uri
end
@value = value
end
diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb
index f051f938c2..690f379389 100644
--- a/lib/bundler/plugin/api/source.rb
+++ b/lib/bundler/plugin/api/source.rb
@@ -107,7 +107,7 @@ module Bundler
def install_path
@install_path ||=
begin
- base_name = File.basename(Bundler::URI.parse(uri).normalize.path)
+ base_name = File.basename(Gem::URI.parse(uri).normalize.path)
gem_install_dir.join("#{base_name}-#{uri_hash[0..11]}")
end
@@ -131,7 +131,7 @@ module Bundler
Bundler::Index.build do |index|
files.each do |file|
next unless spec = Bundler.load_gemspec(file)
- Bundler.rubygems.set_installed_by_version(spec)
+ spec.installed_by_version = Gem::VERSION
spec.source = self
Bundler.rubygems.validate(spec)
@@ -176,7 +176,7 @@ module Bundler
#
# This is used by `app_cache_path`
def app_cache_dirname
- base_name = File.basename(Bundler::URI.parse(uri).normalize.path)
+ base_name = File.basename(Gem::URI.parse(uri).normalize.path)
"#{base_name}-#{uri_hash}"
end
@@ -196,6 +196,7 @@ module Bundler
FileUtils.rm_rf(new_cache_path)
FileUtils.cp_r(install_path, new_cache_path)
+ FileUtils.rm_rf(app_cache_path.join(".git"))
FileUtils.touch(app_cache_path.join(".bundlecache"))
end
diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb
index 256dcf526c..4f60862bb4 100644
--- a/lib/bundler/plugin/installer.rb
+++ b/lib/bundler/plugin/installer.rb
@@ -10,6 +10,7 @@ module Bundler
class Installer
autoload :Rubygems, File.expand_path("installer/rubygems", __dir__)
autoload :Git, File.expand_path("installer/git", __dir__)
+ autoload :Path, File.expand_path("installer/path", __dir__)
def install(names, options)
check_sources_consistency!(options)
@@ -18,8 +19,8 @@ module Bundler
if options[:git]
install_git(names, version, options)
- elsif options[:local_git]
- install_local_git(names, version, options)
+ elsif options[:path]
+ install_path(names, version, options[:path])
else
sources = options[:source] || Gem.sources
install_rubygems(names, version, sources)
@@ -45,20 +46,40 @@ module Bundler
if options.key?(:git) && options.key?(:local_git)
raise InvalidOption, "Remote and local plugin git sources can't be both specified"
end
+
+ # back-compat; local_git is an alias for git
+ if options.key?(:local_git)
+ Bundler::SharedHelpers.major_deprecation(2, "--local_git is deprecated, use --git")
+ options[:git] = options.delete(:local_git)
+ end
+
+ if (options.keys & [:source, :git, :path]).length > 1
+ raise InvalidOption, "Only one of --source, --git, or --path may be specified"
+ end
+
+ if (options.key?(:branch) || options.key?(:ref)) && !options.key?(:git)
+ raise InvalidOption, "--#{options.key?(:branch) ? "branch" : "ref"} can only be used with git sources"
+ end
+
+ if options.key?(:branch) && options.key?(:ref)
+ raise InvalidOption, "--branch and --ref can't be both specified"
+ end
end
def install_git(names, version, options)
- uri = options.delete(:git)
- options["uri"] = uri
+ source_list = SourceList.new
+ source = source_list.add_git_source({ "uri" => options[:git],
+ "branch" => options[:branch],
+ "ref" => options[:ref] })
- install_all_sources(names, version, options, options[:source])
+ install_all_sources(names, version, source_list, source)
end
- def install_local_git(names, version, options)
- uri = options.delete(:local_git)
- options["uri"] = uri
+ def install_path(names, version, path)
+ source_list = SourceList.new
+ source = source_list.add_path_source({ "path" => path, "root_path" => SharedHelpers.pwd })
- install_all_sources(names, version, options, options[:source])
+ install_all_sources(names, version, source_list, source)
end
# Installs the plugin from rubygems source and returns the path where the
@@ -70,16 +91,15 @@ module Bundler
#
# @return [Hash] map of names to the specs of plugins installed
def install_rubygems(names, version, sources)
- install_all_sources(names, version, nil, sources)
- end
-
- def install_all_sources(names, version, git_source_options, rubygems_source)
source_list = SourceList.new
- source_list.add_git_source(git_source_options) if git_source_options
- Array(rubygems_source).each {|remote| source_list.add_global_rubygems_remote(remote) } if rubygems_source
+ Array(sources).each {|remote| source_list.add_global_rubygems_remote(remote) }
+
+ install_all_sources(names, version, source_list)
+ end
- deps = names.map {|name| Dependency.new name, version }
+ def install_all_sources(names, version, source_list, source = nil)
+ deps = names.map {|name| Dependency.new(name, version, { "source" => source }) }
Bundler.configure_gem_home_and_path(Plugin.root)
diff --git a/lib/bundler/plugin/installer/path.rb b/lib/bundler/plugin/installer/path.rb
new file mode 100644
index 0000000000..58a8fa7426
--- /dev/null
+++ b/lib/bundler/plugin/installer/path.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Bundler
+ module Plugin
+ class Installer
+ class Path < Bundler::Source::Path
+ def root
+ SharedHelpers.in_bundle? ? Bundler.root : Plugin.root
+ end
+
+ def generate_bin(spec, disable_extensions = false)
+ # Need to find a way without code duplication
+ # For now, we can ignore this
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/source_list.rb b/lib/bundler/plugin/source_list.rb
index 547661cf2f..746996de55 100644
--- a/lib/bundler/plugin/source_list.rb
+++ b/lib/bundler/plugin/source_list.rb
@@ -9,6 +9,10 @@ module Bundler
add_source_to_list Plugin::Installer::Git.new(options), git_sources
end
+ def add_path_source(options = {})
+ add_source_to_list Plugin::Installer::Path.new(options), path_sources
+ end
+
def add_rubygems_source(options = {})
add_source_to_list Plugin::Installer::Rubygems.new(options), @rubygems_sources
end
@@ -17,10 +21,6 @@ module Bundler
path_sources + git_sources + rubygems_sources + [metadata_source]
end
- def default_source
- git_sources.first || global_rubygems_source
- end
-
private
def rubygems_aggregate_class
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index f60069f421..a38b6974f8 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -50,26 +50,26 @@ module Bundler
specs[name] = matches.sort_by {|s| [s.version, s.platform.to_s] }
end
+ @all_versions = Hash.new do |candidates, package|
+ candidates[package] = all_versions_for(package)
+ end
+
@sorted_versions = Hash.new do |candidates, package|
- candidates[package] = if package.root?
- [root_version]
- else
- all_versions_for(package).sort
- end
+ candidates[package] = filtered_versions_for(package).sort
end
+ @sorted_versions[root] = [root_version]
+
root_dependencies = prepare_dependencies(@requirements, @packages)
@cached_dependencies = Hash.new do |dependencies, package|
- dependencies[package] = if package.root?
- { root_version => root_dependencies }
- else
- Hash.new do |versions, version|
- versions[version] = to_dependency_hash(version.dependencies.reject {|d| d.name == package.name }, @packages)
- end
+ dependencies[package] = Hash.new do |versions, version|
+ versions[version] = to_dependency_hash(version.dependencies.reject {|d| d.name == package.name }, @packages)
end
end
+ @cached_dependencies[root] = { root_version => root_dependencies }
+
logger = Bundler::UI::Shell.new
logger.level = debug? ? "debug" : "warn"
@@ -79,13 +79,14 @@ module Bundler
def solve_versions(root:, logger:)
solver = PubGrub::VersionSolver.new(source: self, root: root, logger: logger)
result = solver.solve
- result.map {|package, version| version.to_specs(package) }.flatten.uniq
+ resolved_specs = result.map {|package, version| version.to_specs(package) }.flatten
+ resolved_specs |= @base.specs_compatible_with(SpecSet.new(resolved_specs))
rescue PubGrub::SolveFailure => e
incompatibility = e.incompatibility
- names_to_unlock, names_to_allow_prereleases_for, extended_explanation = find_names_to_relax(incompatibility)
+ names_to_unlock, names_to_allow_prereleases_for, names_to_allow_remote_specs_for, extended_explanation = find_names_to_relax(incompatibility)
- names_to_relax = names_to_unlock + names_to_allow_prereleases_for
+ names_to_relax = names_to_unlock + names_to_allow_prereleases_for + names_to_allow_remote_specs_for
if names_to_relax.any?
if names_to_unlock.any?
@@ -95,11 +96,17 @@ module Bundler
end
if names_to_allow_prereleases_for.any?
- Bundler.ui.debug "Found conflicts with dependencies with prereleases. Will retrying considering prereleases for #{names_to_allow_prereleases_for.join(", ")}...", true
+ Bundler.ui.debug "Found conflicts with dependencies with prereleases. Will retry considering prereleases for #{names_to_allow_prereleases_for.join(", ")}...", true
@base.include_prereleases(names_to_allow_prereleases_for)
end
+ if names_to_allow_remote_specs_for.any?
+ Bundler.ui.debug "Found conflicts with local versions of #{names_to_allow_remote_specs_for.join(", ")}. Will retry considering remote versions...", true
+
+ @base.include_remote_specs(names_to_allow_remote_specs_for)
+ end
+
root, logger = setup_solver
Bundler.ui.debug "Retrying resolution...", true
@@ -119,6 +126,7 @@ module Bundler
def find_names_to_relax(incompatibility)
names_to_unlock = []
names_to_allow_prereleases_for = []
+ names_to_allow_remote_specs_for = []
extended_explanation = nil
while incompatibility.conflict?
@@ -133,6 +141,8 @@ module Bundler
names_to_unlock << name
elsif package.ignores_prereleases? && @all_specs[name].any? {|s| s.version.prerelease? }
names_to_allow_prereleases_for << name
+ elsif package.prefer_local? && @all_specs[name].any? {|s| !s.is_a?(StubSpecification) }
+ names_to_allow_remote_specs_for << name
end
no_versions_incompat = [cause.incompatibility, cause.satisfier].find {|incompat| incompat.cause.is_a?(PubGrub::Incompatibility::NoVersions) }
@@ -142,7 +152,7 @@ module Bundler
end
end
- [names_to_unlock.uniq, names_to_allow_prereleases_for.uniq, extended_explanation]
+ [names_to_unlock.uniq, names_to_allow_prereleases_for.uniq, names_to_allow_remote_specs_for.uniq, extended_explanation]
end
def parse_dependency(package, dependency)
@@ -156,9 +166,15 @@ module Bundler
end
def versions_for(package, range=VersionRange.any)
- versions = range.select_versions(@sorted_versions[package])
+ versions = select_sorted_versions(package, range)
- sort_versions(package, versions)
+ # Conditional avoids (among other things) calling
+ # sort_versions_by_preferred with the root package
+ if versions.size > 1
+ sort_versions_by_preferred(package, versions)
+ else
+ versions
+ end
end
def no_versions_incompatibility_for(package, unsatisfied_term)
@@ -237,7 +253,7 @@ module Bundler
def all_versions_for(package)
name = package.name
- results = (@base[name] + filter_prereleases(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] }
+ results = (@base[name] + filter_specs(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] }
if name == "bundler" && !bundler_pinned_to_current_version?
bundler_spec = Gem.loaded_specs["bundler"]
@@ -247,8 +263,8 @@ module Bundler
locked_requirement = base_requirements[name]
results = filter_matching_specs(results, locked_requirement) if locked_requirement
- versions = results.group_by(&:version).reduce([]) do |groups, (version, specs)|
- platform_specs = package.platforms.map {|platform| select_best_platform_match(specs, platform) }
+ results.group_by(&:version).reduce([]) do |groups, (version, specs)|
+ platform_specs = package.platform_specs(specs)
# If package is a top-level dependency,
# candidate is only valid if there are matching versions for all resolution platforms.
@@ -263,19 +279,25 @@ module Bundler
next groups if platform_specs.all?(&:empty?)
end
- platform_specs.flatten!
-
ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY)
- groups << Resolver::Candidate.new(version, specs: ruby_specs) if ruby_specs.any?
+ ruby_group = Resolver::SpecGroup.new(ruby_specs)
- next groups if platform_specs == ruby_specs || package.force_ruby_platform?
+ unless ruby_group.empty?
+ platform_specs.each do |specs|
+ ruby_group.merge(Resolver::SpecGroup.new(specs))
+ end
- groups << Resolver::Candidate.new(version, specs: platform_specs)
+ groups << Resolver::Candidate.new(version, group: ruby_group, priority: -1)
+ next groups if package.force_ruby_platform?
+ end
+
+ platform_group = Resolver::SpecGroup.new(platform_specs.flatten.uniq)
+ next groups if platform_group == ruby_group
+
+ groups << Resolver::Candidate.new(version, group: platform_group, priority: 1)
groups
end
-
- sort_versions(package, versions)
end
def source_for(name)
@@ -334,18 +356,43 @@ module Bundler
private
+ def filtered_versions_for(package)
+ @gem_version_promoter.filter_versions(package, @all_versions[package])
+ end
+
+ def raise_all_versions_filtered_out!(package)
+ level = @gem_version_promoter.level
+ name = package.name
+ locked_version = package.locked_version
+ requirement = package.dependency
+
+ raise GemNotFound,
+ "#{name} is locked to #{locked_version}, while Gemfile is requesting #{requirement}. " \
+ "--strict --#{level} was specified, but there are no #{level} level upgrades from #{locked_version} satisfying #{requirement}, so version solving has failed"
+ end
+
def filter_matching_specs(specs, requirements)
Array(requirements).flat_map do |requirement|
specs.select {| spec| requirement_satisfied_by?(requirement, spec) }
end
end
+ def filter_specs(specs, package)
+ filter_remote_specs(filter_prereleases(specs, package), package)
+ end
+
def filter_prereleases(specs, package)
return specs unless package.ignores_prereleases? && specs.size > 1
specs.reject {|s| s.version.prerelease? }
end
+ def filter_remote_specs(specs, package)
+ return specs unless package.prefer_local?
+
+ specs.select {|s| s.is_a?(StubSpecification) }
+ end
+
# Ignore versions that depend on themselves incorrectly
def filter_invalid_self_dependencies(specs, name)
specs.reject do |s|
@@ -357,12 +404,8 @@ module Bundler
requirement.satisfied_by?(spec.version) || spec.source.is_a?(Source::Gemspec)
end
- def sort_versions(package, versions)
- if versions.size > 1
- @gem_version_promoter.sort_versions(package, versions).reverse
- else
- versions
- end
+ def sort_versions_by_preferred(package, versions)
+ @gem_version_promoter.sort_versions(package, versions)
end
def repository_for(package)
@@ -379,12 +422,22 @@ module Bundler
next [dep_package, dep_constraint] if name == "bundler"
- versions = versions_for(dep_package, dep_constraint.range)
- if versions.empty? && dep_package.ignores_prereleases?
- @sorted_versions.delete(dep_package)
- dep_package.consider_prereleases!
- versions = versions_for(dep_package, dep_constraint.range)
+ dep_range = dep_constraint.range
+ versions = select_sorted_versions(dep_package, dep_range)
+ if versions.empty?
+ if dep_package.ignores_prereleases? || dep_package.prefer_local?
+ @all_versions.delete(dep_package)
+ @sorted_versions.delete(dep_package)
+ end
+ dep_package.consider_prereleases! if dep_package.ignores_prereleases?
+ dep_package.consider_remote_versions! if dep_package.prefer_local?
+ versions = select_sorted_versions(dep_package, dep_range)
+ end
+
+ if versions.empty? && select_all_versions(dep_package, dep_range).any?
+ raise_all_versions_filtered_out!(dep_package)
end
+
next [dep_package, dep_constraint] unless versions.empty?
next unless dep_package.current_platform?
@@ -393,6 +446,14 @@ module Bundler
end.compact.to_h
end
+ def select_sorted_versions(package, range)
+ range.select_versions(@sorted_versions[package])
+ end
+
+ def select_all_versions(package, range)
+ range.select_versions(@all_versions[package])
+ end
+
def other_specs_matching_message(specs, requirement)
message = String.new("The source contains the following gems matching '#{requirement}':\n")
message << specs.map {|s| " * #{s.full_name}" }.join("\n")
@@ -401,8 +462,8 @@ module Bundler
def requirement_to_range(requirement)
ranges = requirement.requirements.map do |(op, version)|
- ver = Resolver::Candidate.new(version).generic!
- platform_ver = Resolver::Candidate.new(version).platform_specific!
+ ver = Resolver::Candidate.new(version, priority: -1)
+ platform_ver = Resolver::Candidate.new(version, priority: 1)
case op
when "~>"
diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb
index ad19eeb3f4..3f2436672a 100644
--- a/lib/bundler/resolver/base.rb
+++ b/lib/bundler/resolver/base.rb
@@ -30,6 +30,10 @@ module Bundler
end.compact
end
+ def specs_compatible_with(result)
+ @base.specs_compatible_with(result)
+ end
+
def [](name)
@base[name]
end
@@ -68,6 +72,12 @@ module Bundler
end
end
+ def include_remote_specs(names)
+ names.each do |name|
+ get_package(name).consider_remote_versions!
+ end
+ end
+
private
def indirect_pins(names)
diff --git a/lib/bundler/resolver/candidate.rb b/lib/bundler/resolver/candidate.rb
index e695ef08ee..f593fc5d61 100644
--- a/lib/bundler/resolver/candidate.rb
+++ b/lib/bundler/resolver/candidate.rb
@@ -15,7 +15,7 @@ module Bundler
# considered separately.
#
# Some candidates may also keep some information explicitly about the
- # package the refer to. These candidates are referred to as "canonical" and
+ # package they refer to. These candidates are referred to as "canonical" and
# are used when materializing resolution results back into RubyGems
# specifications that can be installed, written to lock files, and so on.
#
@@ -24,10 +24,10 @@ module Bundler
attr_reader :version
- def initialize(version, specs: [])
- @spec_group = Resolver::SpecGroup.new(specs)
+ def initialize(version, group: nil, priority: -1)
+ @spec_group = group || SpecGroup.new([])
@version = Gem::Version.new(version)
- @ruby_only = specs.map(&:platform).uniq == [Gem::Platform::RUBY]
+ @priority = priority
end
def dependencies
@@ -40,18 +40,6 @@ module Bundler
@spec_group.to_specs(package.force_ruby_platform?)
end
- def generic!
- @ruby_only = true
-
- self
- end
-
- def platform_specific!
- @ruby_only = false
-
- self
- end
-
def prerelease?
@version.prerelease?
end
@@ -61,7 +49,7 @@ module Bundler
end
def sort_obj
- [@version, @ruby_only ? -1 : 1]
+ [@version, @priority]
end
def <=>(other)
diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb
index 0461328683..5aecc12d05 100644
--- a/lib/bundler/resolver/package.rb
+++ b/lib/bundler/resolver/package.rb
@@ -15,7 +15,7 @@ module Bundler
class Package
attr_reader :name, :platforms, :dependency, :locked_version
- def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, dependency: nil)
+ def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefer_local: false, dependency: nil)
@name = name
@platforms = platforms
@locked_version = locked_specs[name].first&.version
@@ -23,6 +23,11 @@ module Bundler
@dependency = dependency || Dependency.new(name, @locked_version)
@top_level = !dependency.nil?
@prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore
+ @prefer_local = prefer_local
+ end
+
+ def platform_specs(specs)
+ platforms.map {|platform| GemHelpers.select_best_platform_match(specs, platform, prefer_locked: !unlock?) }
end
def to_s
@@ -65,6 +70,14 @@ module Bundler
@prerelease = :consider_last
end
+ def prefer_local?
+ @prefer_local
+ end
+
+ def consider_remote_versions!
+ @prefer_local = false
+ end
+
def force_ruby_platform?
@dependency.force_ruby_platform
end
diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb
index 5cee444e5e..f950df6eda 100644
--- a/lib/bundler/resolver/spec_group.rb
+++ b/lib/bundler/resolver/spec_group.rb
@@ -3,6 +3,8 @@
module Bundler
class Resolver
class SpecGroup
+ attr_reader :specs
+
def initialize(specs)
@specs = specs
end
@@ -38,17 +40,33 @@ module Bundler
def dependencies
@dependencies ||= @specs.map do |spec|
__dependencies(spec) + metadata_dependencies(spec)
- end.flatten.uniq
+ end.flatten.uniq.sort
+ end
+
+ def ==(other)
+ sorted_spec_names == other.sorted_spec_names
+ end
+
+ def merge(other)
+ return false unless equivalent?(other)
+
+ @specs |= other.specs
+
+ true
end
protected
def sorted_spec_names
- @sorted_spec_names ||= @specs.map(&:full_name).sort
+ @specs.map(&:full_name).sort
end
private
+ def equivalent?(other)
+ name == other.name && version == other.version && source == other.source && dependencies == other.dependencies
+ end
+
def exemplary_spec
@specs.first
end
diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb
index b95c42c361..090cb7e2ca 100644
--- a/lib/bundler/retry.rb
+++ b/lib/bundler/retry.rb
@@ -50,7 +50,7 @@ module Bundler
end
return true unless name
Bundler.ui.info "" unless Bundler.ui.debug? # Add new line in case dots preceded this
- Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", Bundler.ui.debug?
+ Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", true
end
def keep_trying?
diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb
index 7e9e072b83..0ed5cbc6ca 100644
--- a/lib/bundler/ruby_version.rb
+++ b/lib/bundler/ruby_version.rb
@@ -23,7 +23,13 @@ module Bundler
# specified must match the version.
@versions = Array(versions).map do |v|
- op, v = Gem::Requirement.parse(normalize_version(v))
+ normalized_v = normalize_version(v)
+
+ unless Gem::Requirement::PATTERN.match?(normalized_v)
+ raise InvalidArgumentError, "#{v} is not a valid requirement on the Ruby version"
+ end
+
+ op, v = Gem::Requirement.parse(normalized_v)
op == "=" ? v.to_s : "#{op} #{v}"
end
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index e0582beba2..56d18f28fd 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -1,11 +1,7 @@
# frozen_string_literal: true
-require "pathname"
-
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 +13,6 @@ require "rubygems/specification"
# `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 +23,59 @@ unless Gem.ruby_version.to_s == RUBY_VERSION || RUBY_PATCHLEVEL == -1
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
+
+ # Can be removed once RubyGems 3.5.18 support is dropped
+ unless Gem.respond_to?(:open_file_with_lock)
+ class << self
+ remove_method :open_file_with_flock if Gem.respond_to?(:open_file_with_flock)
+
+ def open_file_with_flock(path, &block)
+ # read-write mode is used rather than read-only in order to support NFS
+ mode = IO::RDWR | IO::APPEND | IO::CREAT | IO::BINARY
+ mode |= IO::SHARE_DELETE if IO.const_defined?(:SHARE_DELETE)
+
+ File.open(path, mode) do |io|
+ begin
+ io.flock(File::LOCK_EX)
+ rescue Errno::ENOSYS, Errno::ENOTSUP
+ end
+ yield io
+ end
+ end
+
+ def open_file_with_lock(path, &block)
+ file_lock = "#{path}.lock"
+ open_file_with_flock(file_lock, &block)
+ ensure
+ FileUtils.rm_f file_lock
+ end
+ end
+ end
+
+ require "rubygems/specification"
+
+ # Can be removed once RubyGems 3.5.14 support is dropped
+ VALIDATES_FOR_RESOLUTION = Specification.new.respond_to?(:validate_for_resolution).freeze
+
+ # Can be removed once RubyGems 3.3.15 support is dropped
+ FLATTENS_REQUIRED_PATHS = Specification.new.respond_to?(:flatten_require_paths).freeze
+
class Specification
+ # Can be removed once RubyGems 3.5.15 support is dropped
+ correct_array_attributes = @@default_value.select {|_k,v| v.is_a?(Array) }.keys
+ unless @@array_attributes == correct_array_attributes
+ @@array_attributes = correct_array_attributes # rubocop:disable Style/ClassVars
+ end
+
+ require_relative "match_metadata"
+ require_relative "match_platform"
+
include ::Bundler::MatchMetadata
include ::Bundler::MatchPlatform
@@ -48,7 +92,7 @@ module Gem
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
@@ -127,6 +171,33 @@ module Gem
!default_gem? && !File.directory?(full_gem_path)
end
+ unless VALIDATES_FOR_RESOLUTION
+ def validate_for_resolution
+ SpecificationPolicy.new(self).validate_for_resolution
+ end
+ end
+
+ unless FLATTENS_REQUIRED_PATHS
+ def flatten_require_paths
+ return unless raw_require_paths.first.is_a?(Array)
+
+ warn "#{name} #{version} includes a gemspec with `require_paths` set to an array of arrays. Newer versions of this gem might've already fixed this"
+ raw_require_paths.flatten!
+ end
+
+ class << self
+ module RequirePathFlattener
+ def from_yaml(input)
+ spec = super(input)
+ spec.flatten_require_paths
+ spec
+ end
+ end
+
+ prepend RequirePathFlattener
+ end
+ end
+
private
def dependencies_to_gemfile(dependencies, group = nil)
@@ -146,29 +217,47 @@ module Gem
end
end
+ unless VALIDATES_FOR_RESOLUTION
+ class SpecificationPolicy
+ def validate_for_resolution
+ validate_required!
+ end
+ end
+ end
+
+ module BetterPermissionError
+ def data
+ super
+ rescue Errno::EACCES
+ raise Bundler::PermissionError.new(loaded_from, :read)
+ end
+ end
+
+ require "rubygems/stub_specification"
+
+ class StubSpecification
+ prepend BetterPermissionError
+ end
+
class Dependency
+ require_relative "force_platform"
+
include ::Bundler::ForcePlatform
+ attr_reader :force_ruby_platform
+
attr_accessor :source, :groups
alias_method :eql?, :==
- def force_ruby_platform
- return @force_ruby_platform if defined?(@force_ruby_platform) && !@force_ruby_platform.nil?
-
- @force_ruby_platform = default_force_ruby_platform
- end
-
- def encode_with(coder)
- to_yaml_properties.each do |ivar|
- coder[ivar.to_s.sub(/^@/, "")] = instance_variable_get(ivar)
+ unless method_defined?(:encode_with, false)
+ def encode_with(coder)
+ [:@name, :@requirement, :@type, :@prerelease, :@version_requirements].each do |ivar|
+ coder[ivar.to_s.sub(/^@/, "")] = instance_variable_get(ivar)
+ end
end
end
- def to_yaml_properties
- instance_variables.reject {|p| ["@source", "@groups"].include?(p.to_s) }
- end
-
def to_lock
out = String.new(" #{name}")
unless requirement.none?
@@ -177,6 +266,16 @@ module Gem
end
out
end
+
+ if Gem.rubygems_version < Gem::Version.new("3.5.22")
+ module FilterIgnoredSpecs
+ def matching_specs(platform_only = false)
+ super.reject(&:ignored?)
+ end
+ end
+
+ prepend FilterIgnoredSpecs
+ end
end
# Requirements using lambda operator differentiate trailing zeros since rubygems 3.2.6
@@ -221,7 +320,7 @@ module Gem
# cpu
([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
- (@cpu == "arm" && other.cpu.start_with?("arm"))) &&
+ (@cpu == "arm" && other.cpu.start_with?("armv"))) &&
# os
@os == other.os &&
@@ -299,6 +398,15 @@ module Gem
end
end
end
+
+ # Can be removed once RubyGems 3.5.22 support is dropped
+ unless new.respond_to?(:ignored?)
+ def ignored?
+ return @ignored unless @ignored.nil?
+
+ @ignored = missing_extensions?
+ end
+ end
end
require "rubygems/name_tuple"
@@ -325,4 +433,23 @@ module Gem
end
end
end
+
+ unless Gem.rubygems_version >= Gem::Version.new("3.5.19")
+ class Resolver::ActivationRequest
+ remove_method :installed?
+
+ def installed?
+ case @spec
+ when Gem::Resolver::VendorSpecification then
+ true
+ else
+ this_spec = full_spec
+
+ Gem::Specification.any? do |s|
+ s == this_spec && s.base_dir == this_spec.base_dir
+ end
+ end
+ end
+ end
+ end
end
diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb
index d563868cd2..62756680e7 100644
--- a/lib/bundler/rubygems_gem_installer.rb
+++ b/lib/bundler/rubygems_gem_installer.rb
@@ -29,7 +29,10 @@ module Bundler
write_build_info_file
run_post_build_hooks
- generate_bin
+ SharedHelpers.filesystem_access(bin_dir, :write) do
+ generate_bin
+ end
+
generate_plugins
write_spec
@@ -45,7 +48,17 @@ module Bundler
spec
end
- def pre_install_checks
+ if Bundler.rubygems.provides?("< 3.5")
+ def pre_install_checks
+ super
+ rescue Gem::FilePermissionError
+ # Ignore permission checks in RubyGems. Instead, go on, and try to write
+ # for real. We properly handle permission errors when they happen.
+ nil
+ end
+ end
+
+ def ensure_writable_dir(dir)
super
rescue Gem::FilePermissionError
# Ignore permission checks in RubyGems. Instead, go on, and try to write
@@ -68,6 +81,26 @@ module Bundler
end
end
+ if Bundler.rubygems.provides?("< 3.5.19")
+ def generate_bin_script(filename, bindir)
+ bin_script_path = File.join bindir, formatted_program_filename(filename)
+
+ Gem.open_file_with_lock(bin_script_path) do
+ require "fileutils"
+ FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
+
+ File.open(bin_script_path, "wb", 0o755) do |file|
+ file.write app_script_text(filename)
+ file.chmod(options[:prog_mode] || 0o755)
+ end
+ end
+
+ verbose bin_script_path
+
+ generate_windows_script filename, bindir
+ end
+ end
+
def build_extensions
extension_cache_path = options[:bundler_extension_cache_path]
extension_dir = spec.extension_dir
@@ -117,12 +150,13 @@ module Bundler
def strict_rm_rf(dir)
return unless File.exist?(dir)
+ return if Dir.empty?(dir)
parent = File.dirname(dir)
parent_st = File.stat(parent)
if parent_st.world_writable? && !parent_st.sticky?
- raise InsecureInstallPathError.new(parent)
+ raise InsecureInstallPathError.new(spec.full_name, dir)
end
begin
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index da555681f9..5944ab86e0 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -48,7 +48,7 @@ module Bundler
end
def validate(spec)
- Bundler.ui.silence { spec.validate(false) }
+ Bundler.ui.silence { spec.validate_for_resolution }
rescue Gem::InvalidSpecificationException => e
error_message = "The gemspec at #{spec.loaded_from} is not valid. Please fix this gemspec.\n" \
"The validation error was '#{e.message}'\n"
@@ -57,28 +57,6 @@ module Bundler
nil
end
- def set_installed_by_version(spec, installed_by_version = Gem::VERSION)
- return unless spec.respond_to?(:installed_by_version=)
- spec.installed_by_version = Gem::Version.create(installed_by_version)
- end
-
- def spec_missing_extensions?(spec, default = true)
- return spec.missing_extensions? if spec.respond_to?(:missing_extensions?)
-
- return false if spec.default_gem?
- return false if spec.extensions.empty?
-
- default
- end
-
- def spec_matches_for_glob(spec, glob)
- return spec.matches_for_glob(glob) if spec.respond_to?(:matches_for_glob)
-
- spec.load_paths.flat_map do |lp|
- Dir["#{lp}/#{glob}#{suffix_pattern}"]
- end
- end
-
def stub_set_spec(stub, spec)
stub.instance_variable_set(:@spec, spec)
end
@@ -204,7 +182,7 @@ module Bundler
[::Kernel.singleton_class, ::Kernel].each do |kernel_class|
redefine_method(kernel_class, :gem) do |dep, *reqs|
- if executables&.include?(File.basename(caller.first.split(":").first))
+ if executables&.include?(File.basename(caller_locations(1, 1).first.path))
break
end
@@ -469,11 +447,25 @@ module Bundler
end
def all_specs
+ SharedHelpers.major_deprecation 2, "Bundler.rubygems.all_specs has been removed in favor of Bundler.rubygems.installed_specs"
+
Gem::Specification.stubs.map do |stub|
StubSpecification.from_stub(stub)
end
end
+ def installed_specs
+ Gem::Specification.stubs.reject(&:default_gem?).map do |stub|
+ StubSpecification.from_stub(stub)
+ end
+ end
+
+ def default_specs
+ Gem::Specification.default_stubs.map do |stub|
+ StubSpecification.from_stub(stub)
+ end
+ end
+
def find_bundler(version)
find_name("bundler").find {|s| s.version.to_s == version }
end
diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb
index 993f1082c3..5f7dd7a848 100644
--- a/lib/bundler/runtime.rb
+++ b/lib/bundler/runtime.rb
@@ -10,7 +10,7 @@ module Bundler
end
def setup(*groups)
- @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle?
+ @definition.ensure_equivalent_gemfile_and_lockfile
# Has to happen first
clean_load_path
@@ -95,7 +95,7 @@ module Bundler
def lock(opts = {})
return if @definition.no_resolve_needed?
- @definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
+ @definition.lock(opts[:preserve_unknown_sections])
end
alias_method :gems, :specs
@@ -128,11 +128,6 @@ module Bundler
spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache)
end
- Dir[cache_path.join("*/.git")].each do |git_dir|
- FileUtils.rm_rf(git_dir)
- FileUtils.touch(File.expand_path("../.bundlecache", git_dir))
- end
-
prune_cache(cache_path) unless Bundler.settings[:no_prune]
end
diff --git a/lib/bundler/self_manager.rb b/lib/bundler/self_manager.rb
index 5accda4bcb..a6d93f20ff 100644
--- a/lib/bundler/self_manager.rb
+++ b/lib/bundler/self_manager.rb
@@ -70,8 +70,23 @@ module Bundler
configured_gem_home = ENV["GEM_HOME"]
configured_gem_path = ENV["GEM_PATH"]
- cmd = [$PROGRAM_NAME, *ARGV]
- cmd.unshift(Gem.ruby) unless File.executable?($PROGRAM_NAME)
+ # Bundler specs need some stuff to be required before Bundler starts
+ # running, for example, for faking the compact index API. However, these
+ # flags are lost when we reexec to a different version of Bundler. In the
+ # future, we may be able to properly reconstruct the original Ruby
+ # invocation (see https://bugs.ruby-lang.org/issues/6648), but for now
+ # there's no way to do it, so we need to be explicit about how to re-exec.
+ # This may be a feature end users request at some point, but maybe by that
+ # time, we have builtin tools to do. So for now, we use an undocumented
+ # ENV variable only for our specs.
+ bundler_spec_original_cmd = ENV["BUNDLER_SPEC_ORIGINAL_CMD"]
+ if bundler_spec_original_cmd
+ require "shellwords"
+ cmd = [*Shellwords.shellsplit(bundler_spec_original_cmd), *ARGV]
+ else
+ cmd = [$PROGRAM_NAME, *ARGV]
+ cmd.unshift(Gem.ruby) unless File.executable?($PROGRAM_NAME)
+ end
Bundler.with_original_env do
Kernel.exec(
@@ -83,15 +98,16 @@ module Bundler
def needs_switching?
autoswitching_applies? &&
- released?(lockfile_version) &&
- !running?(lockfile_version) &&
- !updating? &&
- Bundler.settings[:version] != "system"
+ Bundler.settings[:version] != "system" &&
+ released?(restart_version) &&
+ !running?(restart_version) &&
+ !updating?
end
def autoswitching_applies?
ENV["BUNDLER_VERSION"].nil? &&
Bundler.rubygems.supports_bundler_trampolining? &&
+ ruby_can_restart_with_same_arguments? &&
SharedHelpers.in_bundle? &&
lockfile_version
end
@@ -151,6 +167,10 @@ module Bundler
!version.to_s.end_with?(".dev")
end
+ def ruby_can_restart_with_same_arguments?
+ $PROGRAM_NAME != "-e"
+ end
+
def updating?
"update".start_with?(ARGV.first || " ") && ARGV[1..-1].any? {|a| a.start_with?("--bundler") }
end
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index 8f941c448d..878747a53b 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -7,7 +7,6 @@ module Bundler
autoload :Validator, File.expand_path("settings/validator", __dir__)
BOOL_KEYS = %w[
- allow_deployment_source_credential_changes
allow_offline_install
auto_clean_without_path
auto_install
@@ -104,6 +103,7 @@ module Bundler
def initialize(root = nil)
@root = root
@local_config = load_config(local_config_file)
+ @local_root = root || Pathname.new(".bundle").expand_path
@env_config = ENV.to_h
@env_config.select! {|key, _value| key.start_with?("BUNDLE_") }
@@ -143,7 +143,7 @@ module Bundler
end
def set_local(key, value)
- local_config_file || raise(GemfileNotFound, "Could not locate Gemfile")
+ local_config_file = @local_root.join("config")
set_key(key, value, @local_config, local_config_file)
end
@@ -189,7 +189,7 @@ module Bundler
def mirror_for(uri)
if uri.is_a?(String)
require_relative "vendored_uri"
- uri = Bundler::URI(uri)
+ uri = Gem::URI(uri)
end
gem_mirrors.for(uri.to_s).uri
@@ -492,16 +492,23 @@ module Bundler
valid_file = file.exist? && !file.size.zero?
return {} unless valid_file
serializer_class.load(file.read).inject({}) do |config, (k, v)|
- if k.include?("-")
- Bundler.ui.warn "Your #{file} config includes `#{k}`, which contains the dash character (`-`).\n" \
- "This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
- "Please edit #{file} and replace any dashes in configuration keys with a triple underscore (`___`)."
+ k = k.dup
+ k << "/" if /https?:/i.match?(k) && !k.end_with?("/", "__#{FALLBACK_TIMEOUT_URI_OPTION.upcase}")
+ k.gsub!(".", "__")
- # string hash keys are frozen
- k = k.gsub("-", "___")
+ unless k.start_with?("#")
+ if k.include?("-")
+ Bundler.ui.warn "Your #{file} config includes `#{k}`, which contains the dash character (`-`).\n" \
+ "This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
+ "Please edit #{file} and replace any dashes in configuration keys with a triple underscore (`___`)."
+
+ # string hash keys are frozen
+ k = k.gsub("-", "___")
+ end
+
+ config[k] = v
end
- config[k] = v
config
end
end
@@ -516,26 +523,25 @@ module Bundler
YAMLSerializer
end
- PER_URI_OPTIONS = %w[
- fallback_timeout
- ].freeze
+ FALLBACK_TIMEOUT_URI_OPTION = "fallback_timeout"
NORMALIZE_URI_OPTIONS_PATTERN =
/
\A
(\w+\.)? # optional prefix key
(https?.*?) # URI
- (\.#{Regexp.union(PER_URI_OPTIONS)})? # optional suffix key
+ (\.#{FALLBACK_TIMEOUT_URI_OPTION})? # optional suffix key
\z
/ix
def self.key_for(key)
- key = normalize_uri(key).to_s if key.is_a?(String) && key.start_with?("http", "mirror.http")
- key = key_to_s(key).gsub(".", "__")
+ key = key_to_s(key)
+ key = normalize_uri(key) if key.start_with?("http", "mirror.http")
+ key = key.gsub(".", "__")
key.gsub!("-", "___")
key.upcase!
- key.prepend("BUNDLE_")
+ key.gsub(/\A([ #]*)/, '\1BUNDLE_')
end
# TODO: duplicates Rubygems#normalize_uri
@@ -549,7 +555,7 @@ module Bundler
end
uri = URINormalizer.normalize_suffix(uri)
require_relative "vendored_uri"
- uri = Bundler::URI(uri)
+ uri = Gem::URI(uri)
unless uri.absolute?
raise ArgumentError, format("Gem sources must be absolute. You provided '%s'.", uri)
end
@@ -564,7 +570,7 @@ module Bundler
key
when Symbol
key.name
- when Bundler::URI::HTTP
+ when Gem::URI::HTTP
key.to_s
else
raise ArgumentError, "Invalid key: #{key.inspect}"
@@ -577,7 +583,7 @@ module Bundler
key
when Symbol
key.to_s
- when Bundler::URI::HTTP
+ when Gem::URI::HTTP
key.to_s
else
raise ArgumentError, "Invalid key: #{key.inspect}"
diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb
index 7131d15055..5a0fd8e0e3 100644
--- a/lib/bundler/setup.rb
+++ b/lib/bundler/setup.rb
@@ -5,6 +5,12 @@ require_relative "shared_helpers"
if Bundler::SharedHelpers.in_bundle?
require_relative "../bundler"
+ # autoswitch to locked Bundler version if available
+ Bundler.auto_switch
+
+ # try to auto_install first before we get to the `Bundler.ui.silence`, so user knows what is happening
+ Bundler.auto_install
+
if STDOUT.tty? || ENV["BUNDLER_FORCE_TTY"]
begin
Bundler.ui.silence { Bundler.setup }
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index 78760e6fa4..e55632b89f 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -1,14 +1,16 @@
# frozen_string_literal: true
-require "pathname"
-require "rbconfig"
-
require_relative "version"
-require_relative "constants"
require_relative "rubygems_integration"
require_relative "current_ruby"
+autoload :Pathname, "pathname"
+
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
def root
gemfile = find_gemfile
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index 831a13cba3..ef36efb3f4 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -32,6 +32,20 @@ module Bundler
@local = false
end
+ def remote!
+ return if @allow_remote
+
+ @local_specs = nil
+ @allow_remote = true
+ end
+
+ def cached!
+ return if @allow_cached
+
+ @local_specs = nil
+ @allow_cached = true
+ end
+
def self.from_lock(options)
new(options.merge("uri" => options.delete("remote")))
end
@@ -56,13 +70,13 @@ module Bundler
end
def hash
- [self.class, uri, ref, branch, name, version, glob, submodules].hash
+ [self.class, uri, ref, branch, name, glob, submodules].hash
end
def eql?(other)
other.is_a?(Git) && uri == other.uri && ref == other.ref &&
branch == other.branch && name == other.name &&
- version == other.version && glob == other.glob &&
+ glob == other.glob &&
submodules == other.submodules
end
@@ -150,7 +164,8 @@ module Bundler
"does not exist. Run `bundle config unset local.#{override_for(original_path)}` to remove the local override"
end
- set_local!(path)
+ @local = true
+ set_paths!(path)
# Create a new git proxy without the cached revision
# so the Gemfile.lock always picks up the new revision.
@@ -173,13 +188,13 @@ module Bundler
end
def specs(*)
- set_local!(app_cache_path) if has_app_cache? && !local?
+ set_up_app_cache!(app_cache_path) if use_app_cache?
if requires_checkout? && !@copied
+ FileUtils.rm_rf(app_cache_path) if use_app_cache? && git_proxy.not_a_repository?
+
fetch
- git_proxy.copy_to(install_path, submodules)
- serialize_gemspecs_in(install_path)
- @copied = true
+ checkout
end
local_specs
@@ -192,13 +207,10 @@ module Bundler
print_using_message "Using #{version_message(spec, options[:previous_spec])} from #{self}"
if (requires_checkout? && !@copied) || force
- Bundler.ui.debug " * Checking out revision: #{ref}"
- git_proxy.copy_to(install_path, submodules)
- serialize_gemspecs_in(install_path)
- @copied = true
+ checkout
end
- generate_bin_options = { disable_extensions: !Bundler.rubygems.spec_missing_extensions?(spec), build_args: options[:build_args] }
+ generate_bin_options = { disable_extensions: !spec.missing_extensions?, build_args: options[:build_args] }
generate_bin(spec, generate_bin_options)
requires_checkout? ? spec.post_install_message : nil
@@ -207,12 +219,14 @@ module Bundler
def cache(spec, custom_path = nil)
app_cache_path = app_cache_path(custom_path)
return unless Bundler.feature_flag.cache_all?
- return if path == app_cache_path
+ return if install_path == app_cache_path
+ return if cache_path == app_cache_path
cached!
FileUtils.rm_rf(app_cache_path)
git_proxy.checkout if requires_checkout?
- git_proxy.copy_to(app_cache_path, @submodules)
- serialize_gemspecs_in(app_cache_path)
+ FileUtils.cp_r("#{cache_path}/.", app_cache_path)
+ FileUtils.touch(app_cache_path.join(".bundlecache"))
+ FileUtils.rm_rf(Dir.glob(app_cache_path.join("hooks/*.sample")))
end
def load_spec_files
@@ -256,6 +270,13 @@ module Bundler
private
+ def checkout
+ Bundler.ui.debug " * Checking out revision: #{ref}"
+ git_proxy.copy_to(install_path, submodules)
+ serialize_gemspecs_in(install_path)
+ @copied = true
+ end
+
def humanized_ref
if local?
path
@@ -278,22 +299,40 @@ module Bundler
# The gemspecs we cache should already be evaluated.
spec = Bundler.load_gemspec(spec_path)
next unless spec
- Bundler.rubygems.set_installed_by_version(spec)
+ spec.installed_by_version = Gem::VERSION
Bundler.rubygems.validate(spec)
File.open(spec_path, "wb") {|file| file.write(spec.to_ruby) }
end
end
- def set_local!(path)
- @local = true
- @local_specs = @git_proxy = nil
- @cache_path = @install_path = path
+ def set_paths!(path)
+ set_cache_path!(path)
+ set_install_path!(path)
+ end
+
+ def set_cache_path!(path)
+ @git_proxy = nil
+ @cache_path = path
+ end
+
+ def set_install_path!(path)
+ @local_specs = nil
+ @install_path = path
+ end
+
+ def set_up_app_cache!(path)
+ FileUtils.mkdir_p(path.join("refs"))
+ set_cache_path!(path)
end
def has_app_cache?
cached_revision && super
end
+ def use_app_cache?
+ has_app_cache? && !local?
+ end
+
def requires_checkout?
allow_git_ops? && !local? && !cached_revision_checked_out?
end
@@ -326,7 +365,7 @@ module Bundler
if %r{^\w+://(\w+@)?}.match?(uri)
# Downcase the domain component of the URI
# and strip off a trailing slash, if one is present
- input = Bundler::URI.parse(uri).normalize.to_s.sub(%r{/$}, "")
+ input = Gem::URI.parse(uri).normalize.to_s.sub(%r{/$}, "")
else
# If there is no URI scheme, assume it is an ssh/git URI
input = uri
@@ -359,9 +398,12 @@ module Bundler
def validate_spec(_spec); end
def load_gemspec(file)
- stub = Gem::StubSpecification.gemspec_stub(file, install_path.parent, install_path.parent)
- stub.full_gem_path = Pathname.new(file).dirname.expand_path(root).to_s
- StubSpecification.from_stub(stub)
+ dirname = Pathname.new(file).dirname
+ SharedHelpers.chdir(dirname.to_s) do
+ stub = Gem::StubSpecification.gemspec_stub(file, install_path.parent, install_path.parent)
+ stub.full_gem_path = dirname.expand_path(root).to_s
+ StubSpecification.from_stub(stub)
+ end
end
def git_scope
diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb
index 8b6d420884..86c45780ae 100644
--- a/lib/bundler/source/git/git_proxy.rb
+++ b/lib/bundler/source/git/git_proxy.rb
@@ -84,6 +84,12 @@ module Bundler
end
end
+ def not_a_repository?
+ _, status = git_null("rev-parse", "--resolve-git-dir", path.to_s, dir: path)
+
+ !status.success?
+ end
+
def contains?(commit)
allowed_with_path do
result, status = git_null("branch", "--contains", commit, dir: path)
@@ -179,9 +185,18 @@ module Bundler
_, err, status = capture(command, nil)
return extra_ref if status.success?
- if err.include?("Could not find remote branch")
+ if err.include?("Could not find remote branch") || # git up to 2.49
+ err.include?("Remote branch #{branch_option} not found") # git 2.49 or higher
raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri)
else
+ idx = command.index("--depth")
+ if idx
+ command.delete_at(idx)
+ command.delete_at(idx)
+ command_with_no_credentials = check_allowed(command)
+
+ err += "Retrying without --depth argument."
+ end
raise GitCommandError.new(command_with_no_credentials, path, err)
end
end
@@ -248,7 +263,7 @@ module Bundler
end
def not_pinned?
- branch || tag || ref.nil?
+ branch_option || ref.nil?
end
def pinned_to_full_sha?
@@ -320,12 +335,10 @@ module Bundler
# Adds credentials to the URI
def configured_uri
if /https?:/.match?(uri)
- remote = Bundler::URI(uri)
+ remote = Gem::URI(uri)
config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
remote.userinfo ||= config_auth
remote.to_s
- elsif File.exist?(uri)
- "file://#{uri}"
else
uri.to_s
end
@@ -414,7 +427,7 @@ module Bundler
# anyways.
return args if @revision
- args += ["--branch", branch || tag] if branch || tag
+ args += ["--branch", branch_option] if branch_option
args
end
@@ -430,6 +443,10 @@ module Bundler
extra_args
end
+ def branch_option
+ branch || tag
+ end
+
def full_clone?
depth.nil?
end
diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb
index 4d27761365..6b05e17727 100644
--- a/lib/bundler/source/metadata.rb
+++ b/lib/bundler/source/metadata.rb
@@ -11,6 +11,8 @@ module Bundler
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/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index 978b0b2c9f..d4c530e922 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -18,9 +18,6 @@ module Bundler
@options = options.dup
@glob = options["glob"] || DEFAULT_GLOB
- @allow_cached = false
- @allow_remote = false
-
@root_path = options["root_path"] || root
if options["path"]
@@ -41,16 +38,6 @@ module Bundler
@original_path = @path
end
- def remote!
- @local_specs = nil
- @allow_remote = true
- end
-
- def cached!
- @local_specs = nil
- @allow_cached = true
- end
-
def self.from_lock(options)
new(options.merge("path" => options.delete("remote")))
end
@@ -66,6 +53,8 @@ module Bundler
"source at `#{@path}`"
end
+ alias_method :to_gemfile, :path
+
def hash
[self.class, expanded_path, version].hash
end
@@ -161,7 +150,7 @@ module Bundler
def load_gemspec(file)
return unless spec = Bundler.load_gemspec(file)
- Bundler.rubygems.set_installed_by_version(spec)
+ spec.installed_by_version = Gem::VERSION
spec
end
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index dfcedb5b16..36185561fa 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -10,7 +10,7 @@ module Bundler
# Ask for X gems per API request
API_REQUEST_SIZE = 50
- attr_reader :remotes
+ attr_accessor :remotes
def initialize(options = {})
@options = options
@@ -22,6 +22,8 @@ module Bundler
@checksum_store = Checksum::Store.new
Array(options["remotes"]).reverse_each {|r| add_remote(r) }
+
+ @lockfile_remotes = @remotes if options["from_lockfile"]
end
def caches
@@ -50,10 +52,11 @@ module Bundler
end
def cached!
+ return unless File.exist?(cache_path)
+
return if @allow_cached
@specs = nil
- @allow_local = true
@allow_cached = true
end
@@ -90,13 +93,13 @@ module Bundler
def self.from_lock(options)
options["remotes"] = Array(options.delete("remote")).reverse
- new(options)
+ new(options.merge("from_lockfile" => true))
end
def to_lock
out = String.new("GEM\n")
- remotes.reverse_each do |remote|
- out << " remote: #{suppress_configured_credentials remote}\n"
+ lockfile_remotes.reverse_each do |remote|
+ out << " remote: #{remote}\n"
end
out << " specs:\n"
end
@@ -133,22 +136,19 @@ module Bundler
# sources, and large_idx.merge! small_idx is way faster than
# small_idx.merge! large_idx.
index = @allow_remote ? remote_specs.dup : Index.new
- index.merge!(cached_specs) if @allow_cached || @allow_remote
+ index.merge!(cached_specs) if @allow_cached
index.merge!(installed_specs) if @allow_local
+
+ # complete with default specs, only if not already available in the
+ # index through remote, cached, or installed specs
+ index.use(default_specs) if @allow_local
+
index
end
end
def install(spec, options = {})
- force = options[:force]
- ensure_builtin_gems_cached = options[:ensure_builtin_gems_cached]
-
- if ensure_builtin_gems_cached && spec.default_gem? && !cached_path(spec)
- cached_built_in_gem(spec) unless spec.remote
- force = true
- end
-
- if installed?(spec) && !force
+ if (spec.default_gem? && !cached_built_in_gem(spec, local: options[:local])) || (installed?(spec) && !options[:force])
print_using_message "Using #{version_message(spec, options[:previous_spec])}"
return nil # no post-install message
end
@@ -206,6 +206,7 @@ module Bundler
spec.full_gem_path = installed_spec.full_gem_path
spec.loaded_from = installed_spec.loaded_from
+ spec.base_dir = installed_spec.base_dir
spec.post_install_message
end
@@ -221,12 +222,13 @@ module Bundler
raise InstallError, e.message
end
- def cached_built_in_gem(spec)
- cached_path = cached_path(spec)
- if cached_path.nil?
+ def cached_built_in_gem(spec, local: false)
+ cached_path = cached_gem(spec)
+ if cached_path.nil? && !local
remote_spec = remote_specs.search(spec).first
if remote_spec
cached_path = fetch_gem(remote_spec)
+ spec.remote = remote_spec.remote
else
Bundler.ui.warn "#{spec.full_name} is built in to Ruby, and can't be cached because your Gemfile doesn't have any sources that contain it."
end
@@ -312,11 +314,7 @@ module Bundler
end
def credless_remotes
- if Bundler.settings[:allow_deployment_source_credential_changes]
- remotes.map(&method(:remove_auth))
- else
- remotes.map(&method(:suppress_configured_credentials))
- end
+ remotes.map(&method(:remove_auth))
end
def remotes_for_spec(spec)
@@ -327,14 +325,6 @@ module Bundler
end
def cached_gem(spec)
- if spec.default_gem?
- cached_built_in_gem(spec)
- else
- cached_path(spec)
- end
- end
-
- def cached_path(spec)
global_cache_path = download_cache_path(spec)
caches << global_cache_path if global_cache_path
@@ -349,21 +339,12 @@ module Bundler
def normalize_uri(uri)
uri = URINormalizer.normalize_suffix(uri.to_s)
require_relative "../vendored_uri"
- uri = Bundler::URI(uri)
+ uri = Gem::URI(uri)
raise ArgumentError, "The source must be an absolute URI. For example:\n" \
- "source 'https://rubygems.org'" if !uri.absolute? || (uri.is_a?(Bundler::URI::HTTP) && uri.host.nil?)
+ "source 'https://rubygems.org'" if !uri.absolute? || (uri.is_a?(Gem::URI::HTTP) && uri.host.nil?)
uri
end
- def suppress_configured_credentials(remote)
- remote_nouser = remove_auth(remote)
- if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser]
- remote_nouser
- else
- remote
- end
- end
-
def remove_auth(remote)
if remote.user || remote.password
remote.dup.tap {|uri| uri.user = uri.password = nil }.to_s
@@ -374,12 +355,18 @@ module Bundler
def installed_specs
@installed_specs ||= Index.build do |idx|
- Bundler.rubygems.all_specs.reverse_each do |spec|
+ Bundler.rubygems.installed_specs.reverse_each do |spec|
+ spec.source = self
+ next if spec.ignored?
+ idx << spec
+ end
+ end
+ end
+
+ def default_specs
+ @default_specs ||= Index.build do |idx|
+ Bundler.rubygems.default_specs.each do |spec|
spec.source = self
- if Bundler.rubygems.spec_missing_extensions?(spec, false)
- Bundler.ui.debug "Source #{self} is ignoring #{spec} because it is missing extensions"
- next
- end
idx << spec
end
end
@@ -469,6 +456,10 @@ module Bundler
private
+ def lockfile_remotes
+ @lockfile_remotes || credless_remotes
+ end
+
# Checks if the requested spec exists in the global cache. If it does,
# we copy it to the download path, and if it does not, we download it.
#
diff --git a/lib/bundler/source/rubygems/remote.rb b/lib/bundler/source/rubygems/remote.rb
index 82c850ffbb..9c5c06de24 100644
--- a/lib/bundler/source/rubygems/remote.rb
+++ b/lib/bundler/source/rubygems/remote.rb
@@ -48,7 +48,7 @@ module Bundler
end
uri
- rescue Bundler::URI::InvalidComponentError
+ rescue Gem::URI::InvalidComponentError
error_message = "Please CGI escape your usernames and passwords before " \
"setting them for authentication."
raise HTTPError.new(error_message)
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
index 4419695b7f..5f9dd68f17 100644
--- a/lib/bundler/source_list.rb
+++ b/lib/bundler/source_list.rb
@@ -22,6 +22,7 @@ module Bundler
@metadata_source = Source::Metadata.new
@merged_gem_lockfile_sections = false
+ @local_mode = true
end
def merged_gem_lockfile_sections?
@@ -73,6 +74,10 @@ module Bundler
global_rubygems_source
end
+ def local_mode?
+ @local_mode
+ end
+
def default_source
global_path_source || global_rubygems_source
end
@@ -140,11 +145,17 @@ module Bundler
all_sources.each(&:local_only!)
end
+ def local!
+ all_sources.each(&:local!)
+ end
+
def cached!
all_sources.each(&:cached!)
end
def remote!
+ @local_mode = false
+
all_sources.each(&:remote!)
end
@@ -157,7 +168,11 @@ module Bundler
end
def map_sources(replacement_sources)
- rubygems, git, plugin = [@rubygems_sources, @git_sources, @plugin_sources].map do |sources|
+ rubygems = @rubygems_sources.map do |source|
+ replace_rubygems_source(replacement_sources, source) || source
+ end
+
+ git, plugin = [@git_sources, @plugin_sources].map do |sources|
sources.map do |source|
replacement_sources.find {|s| s == source } || source
end
@@ -171,13 +186,22 @@ module Bundler
end
def global_replacement_source(replacement_sources)
- replacement_source = replacement_sources.find {|s| s == global_rubygems_source }
+ replacement_source = replace_rubygems_source(replacement_sources, global_rubygems_source)
return global_rubygems_source unless replacement_source
replacement_source.local!
replacement_source
end
+ def replace_rubygems_source(replacement_sources, gemfile_source)
+ replacement_source = replacement_sources.find {|s| s == gemfile_source }
+ return unless replacement_source
+
+ # locked sources never include credentials so always prefer remotes from the gemfile
+ replacement_source.remotes = gemfile_source.remotes
+ replacement_source
+ end
+
def different_sources?(lock_sources, replacement_sources)
!equivalent_sources?(lock_sources, replacement_sources)
end
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index cc649abaf8..96c36c2dec 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -65,18 +65,12 @@ module Bundler
platforms.concat(new_platforms)
- less_specific_platform = new_platforms.find {|platform| platform != Gem::Platform::RUBY && platform === Bundler.local_platform }
+ less_specific_platform = new_platforms.find {|platform| platform != Gem::Platform::RUBY && Bundler.local_platform === platform && platform === Bundler.local_platform }
platforms.delete(Bundler.local_platform) if less_specific_platform
platforms
end
- def complete_platforms!(platforms)
- platforms.each do |platform|
- complete_platform(platform)
- end
- end
-
def validate_deps(s)
s.runtime_dependencies.each do |dep|
next if dep.name == "bundler"
@@ -100,7 +94,7 @@ module Bundler
end
def delete(specs)
- specs.each {|spec| @specs.delete(spec) }
+ Array(specs).each {|spec| @specs.delete(spec) }
reset!
end
@@ -158,6 +152,12 @@ module Bundler
@specs.detect {|spec| spec.name == name && spec.match_platform(platform) }
end
+ def specs_compatible_with(other)
+ select do |spec|
+ other.valid?(spec)
+ end
+ end
+
def delete_by_name(name)
@specs.reject! {|spec| spec.name == name }
@@ -195,6 +195,10 @@ module Bundler
lookup.keys
end
+ def valid?(s)
+ s.matches_current_metadata? && valid_dependencies?(s)
+ end
+
private
def reset!
@@ -209,7 +213,7 @@ module Bundler
spec = specs.first
matching_specs = spec.source.specs.search([spec.name, spec.version])
platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find do |s|
- s.matches_current_metadata? && valid_dependencies?(s)
+ valid?(s)
end
if platform_spec
@@ -273,13 +277,11 @@ module Bundler
specs_for_name = lookup[dep.name]
return [] unless specs_for_name
- matching_specs = if dep.force_ruby_platform
- GemHelpers.force_ruby_platform(specs_for_name)
+ if platform
+ GemHelpers.select_best_platform_match(specs_for_name, platform, force_ruby: dep.force_ruby_platform)
else
- GemHelpers.select_best_platform_match(specs_for_name, platform || Bundler.local_platform)
+ GemHelpers.select_best_local_platform_match(specs_for_name, force_ruby: dep.force_ruby_platform || dep.default_force_ruby_platform)
end
- matching_specs.map!(&:materialize_for_installation).compact! if platform.nil?
- matching_specs
end
def tsort_each_child(s)
diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb
index da830cf8d4..718920f091 100644
--- a/lib/bundler/stub_specification.rb
+++ b/lib/bundler/stub_specification.rb
@@ -28,6 +28,17 @@ module Bundler
# @!group Stub Delegates
+ def ignored?
+ return @ignored unless @ignored.nil?
+
+ @ignored = missing_extensions?
+ return false unless @ignored
+
+ warn "Source #{source} is ignoring #{self} because it is missing extensions"
+
+ true
+ end
+
def manually_installed?
# This is for manually installed gems which are gems that were fixed in place after a
# failed installation. Once the issue was resolved, the user then manually created
@@ -45,8 +56,8 @@ module Bundler
true
end
- def activated
- stub.activated
+ def activated?
+ stub.activated?
end
def activated=(activated)
@@ -77,6 +88,14 @@ module Bundler
stub.full_require_paths
end
+ def require_paths
+ stub.require_paths
+ end
+
+ def base_dir=(path)
+ stub.base_dir = path
+ end
+
def load_paths
full_require_paths
end
diff --git a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt
index 175b821a62..67fe8cee79 100644
--- a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt
+++ b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt
@@ -2,83 +2,131 @@
## Our Pledge
-We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
-We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
## Our Standards
-Examples of behavior that contributes to a positive environment for our community include:
+Examples of behavior that contributes to a positive environment for our
+community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the overall community
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the overall
+ community
Examples of unacceptable behavior include:
-* The use of sexualized language or imagery, and sexual attention or
- advances of any kind
+* The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
-* Publishing others' private information, such as a physical or email
- address, without their explicit permission
+* Publishing others' private information, such as a physical or email address,
+ without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
-Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
-Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
## Scope
-This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official email address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
## Enforcement
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at <%= config[:email] %>. All complaints will be reviewed and investigated promptly and fairly.
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+[INSERT CONTACT METHOD].
+All complaints will be reviewed and investigated promptly and fairly.
-All community leaders are obligated to respect the privacy and security of the reporter of any incident.
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
## Enforcement Guidelines
-Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
-**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
-**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
### 2. Warning
-**Community Impact**: A violation through a single incident or series of actions.
+**Community Impact**: A violation through a single incident or series of
+actions.
-**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
### 3. Temporary Ban
-**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
-**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
-**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
-**Consequence**: A permanent ban from any sort of public interaction within the community.
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
## Attribution
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
-available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
-Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
-
-[homepage]: https://www.contributor-covenant.org
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
-https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt
index 5bf36378e8..f9c97d5c7e 100644
--- a/lib/bundler/templates/newgem/README.md.tt
+++ b/lib/bundler/templates/newgem/README.md.tt
@@ -10,11 +10,15 @@ TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_O
Install the gem and add to the application's Gemfile by executing:
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
+```bash
+bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
+```
If bundler is not being used to manage dependencies, install the gem by executing:
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
+```bash
+gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
+```
## Usage
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
index 51f19a5be9..6e88f4dab1 100644
--- a/lib/bundler/templates/newgem/newgem.gemspec.tt
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -27,9 +27,10 @@ Gem::Specification.new do |spec|
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(__dir__) do
- `git ls-files -z`.split("\x0").reject do |f|
- (File.expand_path(f) == __FILE__) ||
+ gemspec = File.basename(__FILE__)
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
+ ls.readlines("\x0", chomp: true).reject do |f|
+ (f == gemspec) ||
f.start_with?(*%w[bin/ test/ spec/ features/ .git <%= config[:ci_config_path] %>appveyor Gemfile])
end
end
diff --git a/lib/bundler/templates/newgem/rubocop.yml.tt b/lib/bundler/templates/newgem/rubocop.yml.tt
index 9ecec78807..3d1c4ee7b2 100644
--- a/lib/bundler/templates/newgem/rubocop.yml.tt
+++ b/lib/bundler/templates/newgem/rubocop.yml.tt
@@ -2,12 +2,7 @@ AllCops:
TargetRubyVersion: <%= ::Gem::Version.new(config[:required_ruby_version]).segments[0..1].join(".") %>
Style/StringLiterals:
- Enabled: true
EnforcedStyle: double_quotes
Style/StringLiteralsInInterpolation:
- Enabled: true
EnforcedStyle: double_quotes
-
-Layout/LineLength:
- Max: 120
diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb
index 4555612dbb..6df1512a5b 100644
--- a/lib/bundler/ui/shell.rb
+++ b/lib/bundler/ui/shell.rb
@@ -6,14 +6,17 @@ module Bundler
module UI
class Shell
LEVELS = %w[silent error warn confirm info debug].freeze
+ OUTPUT_STREAMS = [:stdout, :stderr].freeze
attr_writer :shell
+ attr_reader :output_stream
def initialize(options = {})
Thor::Base.shell = options["no-color"] ? Thor::Shell::Basic : nil
@shell = Thor::Base.shell.new
@level = ENV["DEBUG"] ? "debug" : "info"
@warning_history = []
+ @output_stream = :stdout
end
def add_color(string, *color)
@@ -84,7 +87,7 @@ module Bundler
@shell.yes?(msg)
end
- def no?
+ def no?(msg)
@shell.no?(msg)
end
@@ -101,6 +104,11 @@ module Bundler
index <= LEVELS.index(@level)
end
+ def output_stream=(symbol)
+ raise ArgumentError unless OUTPUT_STREAMS.include?(symbol)
+ @output_stream = symbol
+ end
+
def trace(e, newline = nil, force = false)
return unless debug? || force
msg = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n ")}"
@@ -111,6 +119,10 @@ module Bundler
with_level("silent", &blk)
end
+ def progress(&blk)
+ with_output_stream(:stderr, &blk)
+ end
+
def unprinted_warnings
[]
end
@@ -119,6 +131,8 @@ module Bundler
# valimism
def tell_me(msg, color = nil, newline = nil)
+ return tell_err(msg, color, newline) if output_stream == :stderr
+
msg = word_wrap(msg) if newline.is_a?(Hash) && newline[:wrap]
if newline.nil?
@shell.say(msg, color)
@@ -130,7 +144,7 @@ module Bundler
def tell_err(message, color = nil, newline = nil)
return if @shell.send(:stderr).closed?
- newline ||= !message.to_s.match?(/( |\t)\Z/)
+ newline = !message.to_s.match?(/( |\t)\Z/) if newline.nil?
message = word_wrap(message) if newline.is_a?(Hash) && newline[:wrap]
color = nil if color && !$stderr.tty?
@@ -160,6 +174,14 @@ module Bundler
ensure
@level = original
end
+
+ def with_output_stream(symbol)
+ original = output_stream
+ self.output_stream = symbol
+ yield
+ ensure
+ @output_stream = original
+ end
end
end
end
diff --git a/lib/bundler/ui/silent.rb b/lib/bundler/ui/silent.rb
index fa3292bdc9..83d31d4b55 100644
--- a/lib/bundler/ui/silent.rb
+++ b/lib/bundler/ui/silent.rb
@@ -53,6 +53,13 @@ module Bundler
false
end
+ def output_stream=(_symbol)
+ end
+
+ def output_stream
+ nil
+ end
+
def ask(message)
end
@@ -60,7 +67,7 @@ module Bundler
raise "Cannot ask yes? with a silent shell"
end
- def no?
+ def no?(msg)
raise "Cannot ask no? with a silent shell"
end
@@ -77,6 +84,10 @@ module Bundler
yield
end
+ def progress
+ yield
+ end
+
def unprinted_warnings
@warnings
end
diff --git a/lib/bundler/uri_credentials_filter.rb b/lib/bundler/uri_credentials_filter.rb
index ccfaf0bc5d..a83f5304e2 100644
--- a/lib/bundler/uri_credentials_filter.rb
+++ b/lib/bundler/uri_credentials_filter.rb
@@ -11,7 +11,7 @@ module Bundler
return uri if File.exist?(uri)
require_relative "vendored_uri"
- uri = Bundler::URI(uri)
+ uri = Gem::URI(uri)
end
if uri.userinfo
@@ -25,7 +25,7 @@ module Bundler
end
return uri.to_s if uri_to_anonymize.is_a?(String)
uri
- rescue Bundler::URI::InvalidURIError # uri is not canonical uri scheme
+ rescue Gem::URI::InvalidURIError # uri is not canonical uri scheme
uri
end
diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
index c702bebc39..cfc0f48197 100644
--- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
@@ -1,5 +1,5 @@
require_relative '../../../../../vendored_net_http'
-require_relative '../../../../uri/lib/uri'
+require_relative '../../../../../vendored_uri'
require 'cgi' # for escaping
require_relative '../../../../connection_pool/lib/connection_pool'
@@ -22,7 +22,7 @@ autoload :OpenSSL, 'openssl'
#
# require 'bundler/vendor/net-http-persistent/lib/net/http/persistent'
#
-# uri = Bundler::URI 'http://example.com/awesome/web/service'
+# uri = Gem::URI 'http://example.com/awesome/web/service'
#
# http = Gem::Net::HTTP::Persistent.new
#
@@ -39,17 +39,17 @@ autoload :OpenSSL, 'openssl'
# post = Gem::Net::HTTP::Post.new post_uri.path
# post.set_form_data 'some' => 'cool data'
#
-# # perform the POST, the Bundler::URI is always required
+# # perform the POST, the Gem::URI is always required
# response http.request post_uri, post
#
# Note that for GET, HEAD and other requests that do not have a body you want
-# to use Bundler::URI#request_uri not Bundler::URI#path. The request_uri contains the query
+# to use Gem::URI#request_uri not Gem::URI#path. The request_uri contains the query
# params which are sent in the body for other requests.
#
# == TLS/SSL
#
# TLS connections are automatically created depending upon the scheme of the
-# Bundler::URI. TLS connections are automatically verified against the default
+# Gem::URI. TLS connections are automatically verified against the default
# certificate store for your computer. You can override this by changing
# verify_mode or by specifying an alternate cert_store.
#
@@ -68,11 +68,13 @@ autoload :OpenSSL, 'openssl'
# #verify_callback :: For server certificate verification
# #verify_depth :: Depth of certificate verification
# #verify_mode :: How connections should be verified
+# #verify_hostname :: Use hostname verification for server certificate
+# during the handshake
#
# == Proxies
#
# A proxy can be set through #proxy= or at initialization time by providing a
-# second argument to ::new. The proxy may be the Bundler::URI of the proxy server or
+# second argument to ::new. The proxy may be the Gem::URI of the proxy server or
# <code>:ENV</code> which will consult environment variables.
#
# See #proxy= and #proxy_from_env for details.
@@ -174,7 +176,7 @@ class Gem::Net::HTTP::Persistent
##
# The version of Gem::Net::HTTP::Persistent you are using
- VERSION = '4.0.2'
+ VERSION = '4.0.4'
##
# Error class for errors raised by Gem::Net::HTTP::Persistent. Various
@@ -197,7 +199,7 @@ class Gem::Net::HTTP::Persistent
# NOTE: This may not work on ruby > 1.9.
def self.detect_idle_timeout uri, max = 10
- uri = Bundler::URI uri unless Bundler::URI::Generic === uri
+ uri = Gem::URI uri unless Gem::URI::Generic === uri
uri += '/'
req = Gem::Net::HTTP::Head.new uri.request_uri
@@ -450,18 +452,33 @@ class Gem::Net::HTTP::Persistent
attr_reader :verify_mode
##
+ # HTTPS verify_hostname.
+ #
+ # If a client sets this to true and enables SNI with SSLSocket#hostname=,
+ # the hostname verification on the server certificate is performed
+ # automatically during the handshake using
+ # OpenSSL::SSL.verify_certificate_identity().
+ #
+ # You can set +verify_hostname+ as true to use hostname verification
+ # during the handshake.
+ #
+ # NOTE: This works with Ruby > 3.0.
+
+ attr_reader :verify_hostname
+
+ ##
# Creates a new Gem::Net::HTTP::Persistent.
#
# Set a +name+ for fun. Your library name should be good enough, but this
# otherwise has no purpose.
#
- # +proxy+ may be set to a Bundler::URI::HTTP or :ENV to pick up proxy options from
+ # +proxy+ may be set to a Gem::URI::HTTP or :ENV to pick up proxy options from
# the environment. See proxy_from_env for details.
#
- # In order to use a Bundler::URI for the proxy you may need to do some extra work
- # beyond Bundler::URI parsing if the proxy requires a password:
+ # In order to use a Gem::URI for the proxy you may need to do some extra work
+ # beyond Gem::URI parsing if the proxy requires a password:
#
- # proxy = Bundler::URI 'http://proxy.example'
+ # proxy = Gem::URI 'http://proxy.example'
# proxy.user = 'AzureDiamond'
# proxy.password = 'hunter2'
#
@@ -508,9 +525,10 @@ class Gem::Net::HTTP::Persistent
@verify_callback = nil
@verify_depth = nil
@verify_mode = nil
+ @verify_hostname = nil
@cert_store = nil
- @generation = 0 # incremented when proxy Bundler::URI changes
+ @generation = 0 # incremented when proxy Gem::URI changes
if HAVE_OPENSSL then
@verify_mode = OpenSSL::SSL::VERIFY_PEER
@@ -607,13 +625,23 @@ class Gem::Net::HTTP::Persistent
return yield connection
rescue Errno::ECONNREFUSED
- address = http.proxy_address || http.address
- port = http.proxy_port || http.port
+ if http.proxy?
+ address = http.proxy_address
+ port = http.proxy_port
+ else
+ address = http.address
+ port = http.port
+ end
raise Error, "connection refused: #{address}:#{port}"
rescue Errno::EHOSTDOWN
- address = http.proxy_address || http.address
- port = http.proxy_port || http.port
+ if http.proxy?
+ address = http.proxy_address
+ port = http.proxy_port
+ else
+ address = http.address
+ port = http.port
+ end
raise Error, "host down: #{address}:#{port}"
ensure
@@ -720,12 +748,12 @@ class Gem::Net::HTTP::Persistent
alias key= private_key=
##
- # Sets the proxy server. The +proxy+ may be the Bundler::URI of the proxy server,
+ # Sets the proxy server. The +proxy+ may be the Gem::URI of the proxy server,
# the symbol +:ENV+ which will read the proxy from the environment or nil to
# disable use of a proxy. See #proxy_from_env for details on setting the
# proxy from the environment.
#
- # If the proxy Bundler::URI is set after requests have been made, the next request
+ # If the proxy Gem::URI is set after requests have been made, the next request
# will shut-down and re-open all connections.
#
# The +no_proxy+ query parameter can be used to specify hosts which shouldn't
@@ -736,9 +764,9 @@ class Gem::Net::HTTP::Persistent
def proxy= proxy
@proxy_uri = case proxy
when :ENV then proxy_from_env
- when Bundler::URI::HTTP then proxy
+ when Gem::URI::HTTP then proxy
when nil then # ignore
- else raise ArgumentError, 'proxy must be :ENV or a Bundler::URI::HTTP'
+ else raise ArgumentError, 'proxy must be :ENV or a Gem::URI::HTTP'
end
@no_proxy.clear
@@ -763,13 +791,13 @@ class Gem::Net::HTTP::Persistent
end
##
- # Creates a Bundler::URI for an HTTP proxy server from ENV variables.
+ # Creates a Gem::URI for an HTTP proxy server from ENV variables.
#
# If +HTTP_PROXY+ is set a proxy will be returned.
#
- # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the Bundler::URI is given the
+ # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the Gem::URI is given the
# indicated user and password unless HTTP_PROXY contains either of these in
- # the Bundler::URI.
+ # the Gem::URI.
#
# The +NO_PROXY+ ENV variable can be used to specify hosts which shouldn't
# be reached via proxy; if set it should be a comma separated list of
@@ -785,7 +813,7 @@ class Gem::Net::HTTP::Persistent
return nil if env_proxy.nil? or env_proxy.empty?
- uri = Bundler::URI normalize_uri env_proxy
+ uri = Gem::URI normalize_uri env_proxy
env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
@@ -863,7 +891,7 @@ class Gem::Net::HTTP::Persistent
# +req+ must be a Gem::Net::HTTPGenericRequest subclass (see Gem::Net::HTTP for a list).
def request uri, req = nil, &block
- uri = Bundler::URI uri
+ uri = Gem::URI uri
req = request_setup req || uri
response = nil
@@ -896,7 +924,7 @@ class Gem::Net::HTTP::Persistent
end
##
- # Creates a GET request if +req_or_uri+ is a Bundler::URI and adds headers to the
+ # Creates a GET request if +req_or_uri+ is a Gem::URI and adds headers to the
# request.
#
# Returns the request.
@@ -948,8 +976,10 @@ class Gem::Net::HTTP::Persistent
connection.min_version = @min_version if @min_version
connection.max_version = @max_version if @max_version
- connection.verify_depth = @verify_depth
- connection.verify_mode = @verify_mode
+ connection.verify_depth = @verify_depth
+ connection.verify_mode = @verify_mode
+ connection.verify_hostname = @verify_hostname if
+ @verify_hostname != nil && connection.respond_to?(:verify_hostname=)
if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and
not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
@@ -1059,6 +1089,15 @@ application:
end
##
+ # Sets the HTTPS verify_hostname.
+
+ def verify_hostname= verify_hostname
+ @verify_hostname = verify_hostname
+
+ reconnect_ssl
+ end
+
+ ##
# SSL verification callback.
def verify_callback= callback
@@ -1070,4 +1109,3 @@ end
require_relative 'persistent/connection'
require_relative 'persistent/pool'
-
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
index 4bf61461b2..36ab06254d 100644
--- a/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
@@ -1,4 +1,5 @@
require_relative 'package'
+require_relative 'rubygems'
require_relative 'version_constraint'
require_relative 'incompatibility'
require_relative 'basic_package_source'
diff --git a/lib/rubygems/net-http/.document b/lib/bundler/vendor/securerandom/.document
index 0c43bbd6b3..0c43bbd6b3 100644
--- a/lib/rubygems/net-http/.document
+++ b/lib/bundler/vendor/securerandom/.document
diff --git a/lib/bundler/vendor/securerandom/lib/random/formatter.rb b/lib/bundler/vendor/securerandom/lib/random/formatter.rb
new file mode 100644
index 0000000000..e429709789
--- /dev/null
+++ b/lib/bundler/vendor/securerandom/lib/random/formatter.rb
@@ -0,0 +1,373 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: true
+
+# == \Random number formatter.
+#
+# Formats generated random numbers in many manners. When <tt>'random/formatter'</tt>
+# is required, several methods are added to empty core module <tt>Bundler::Random::Formatter</tt>,
+# making them available as Random's instance and module methods.
+#
+# Standard library Bundler::SecureRandom is also extended with the module, and the methods
+# described below are available as a module methods in it.
+#
+# === Examples
+#
+# Generate random hexadecimal strings:
+#
+# require 'bundler/vendor/securerandom/lib/random/formatter'
+#
+# prng = Random.new
+# prng.hex(10) #=> "52750b30ffbc7de3b362"
+# prng.hex(10) #=> "92b15d6c8dc4beb5f559"
+# prng.hex(13) #=> "39b290146bea6ce975c37cfc23"
+# # or just
+# Random.hex #=> "1aed0c631e41be7f77365415541052ee"
+#
+# Generate random base64 strings:
+#
+# prng.base64(10) #=> "EcmTPZwWRAozdA=="
+# prng.base64(10) #=> "KO1nIU+p9DKxGg=="
+# prng.base64(12) #=> "7kJSM/MzBJI+75j8"
+# Random.base64(4) #=> "bsQ3fQ=="
+#
+# Generate random binary strings:
+#
+# prng.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
+# prng.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
+# Random.random_bytes(6) #=> "\xA1\xE6Lr\xC43"
+#
+# Generate alphanumeric strings:
+#
+# prng.alphanumeric(10) #=> "S8baxMJnPl"
+# prng.alphanumeric(10) #=> "aOxAg8BAJe"
+# Random.alphanumeric #=> "TmP9OsJHJLtaZYhP"
+#
+# Generate UUIDs:
+#
+# prng.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
+# prng.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+# Random.uuid #=> "f14e0271-de96-45cc-8911-8910292a42cd"
+#
+# All methods are available in the standard library Bundler::SecureRandom, too:
+#
+# Bundler::SecureRandom.hex #=> "05b45376a30c67238eb93b16499e50cf"
+
+module Bundler::Random::Formatter
+
+ # Generate a random binary string.
+ #
+ # The argument _n_ specifies the length of the result string.
+ #
+ # If _n_ is not specified or is nil, 16 is assumed.
+ # It may be larger in future.
+ #
+ # The result may contain any byte: "\x00" - "\xff".
+ #
+ # require 'bundler/vendor/securerandom/lib/random/formatter'
+ #
+ # Random.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
+ # # or
+ # prng = Random.new
+ # prng.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
+ def random_bytes(n=nil)
+ n = n ? n.to_int : 16
+ gen_random(n)
+ end
+
+ # Generate a random hexadecimal string.
+ #
+ # The argument _n_ specifies the length, in bytes, of the random number to be generated.
+ # The length of the resulting hexadecimal string is twice of _n_.
+ #
+ # If _n_ is not specified or is nil, 16 is assumed.
+ # It may be larger in the future.
+ #
+ # The result may contain 0-9 and a-f.
+ #
+ # require 'bundler/vendor/securerandom/lib/random/formatter'
+ #
+ # Random.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
+ # # or
+ # prng = Random.new
+ # prng.hex #=> "91dc3bfb4de5b11d029d376634589b61"
+ def hex(n=nil)
+ random_bytes(n).unpack1("H*")
+ end
+
+ # Generate a random base64 string.
+ #
+ # The argument _n_ specifies the length, in bytes, of the random number
+ # to be generated. The length of the result string is about 4/3 of _n_.
+ #
+ # If _n_ is not specified or is nil, 16 is assumed.
+ # It may be larger in the future.
+ #
+ # The result may contain A-Z, a-z, 0-9, "+", "/" and "=".
+ #
+ # require 'bundler/vendor/securerandom/lib/random/formatter'
+ #
+ # Random.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
+ # # or
+ # prng = Random.new
+ # prng.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
+ #
+ # See RFC 3548 for the definition of base64.
+ def base64(n=nil)
+ [random_bytes(n)].pack("m0")
+ end
+
+ # Generate a random URL-safe base64 string.
+ #
+ # The argument _n_ specifies the length, in bytes, of the random number
+ # to be generated. The length of the result string is about 4/3 of _n_.
+ #
+ # If _n_ is not specified or is nil, 16 is assumed.
+ # It may be larger in the future.
+ #
+ # The boolean argument _padding_ specifies the padding.
+ # If it is false or nil, padding is not generated.
+ # Otherwise padding is generated.
+ # By default, padding is not generated because "=" may be used as a URL delimiter.
+ #
+ # The result may contain A-Z, a-z, 0-9, "-" and "_".
+ # "=" is also used if _padding_ is true.
+ #
+ # require 'bundler/vendor/securerandom/lib/random/formatter'
+ #
+ # Random.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
+ # # or
+ # prng = Random.new
+ # prng.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
+ #
+ # prng.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
+ # prng.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
+ #
+ # See RFC 3548 for the definition of URL-safe base64.
+ def urlsafe_base64(n=nil, padding=false)
+ s = [random_bytes(n)].pack("m0")
+ s.tr!("+/", "-_")
+ s.delete!("=") unless padding
+ s
+ end
+
+ # Generate a random v4 UUID (Universally Unique IDentifier).
+ #
+ # require 'bundler/vendor/securerandom/lib/random/formatter'
+ #
+ # Random.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
+ # Random.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+ # # or
+ # prng = Random.new
+ # prng.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
+ #
+ # The version 4 UUID is purely random (except the version).
+ # It doesn't contain meaningful information such as MAC addresses, timestamps, etc.
+ #
+ # The result contains 122 random bits (15.25 random bytes).
+ #
+ # See RFC4122[https://datatracker.ietf.org/doc/html/rfc4122] for details of UUID.
+ #
+ def uuid
+ ary = random_bytes(16).unpack("NnnnnN")
+ ary[2] = (ary[2] & 0x0fff) | 0x4000
+ ary[3] = (ary[3] & 0x3fff) | 0x8000
+ "%08x-%04x-%04x-%04x-%04x%08x" % ary
+ end
+
+ alias uuid_v4 uuid
+
+ # Generate a random v7 UUID (Universally Unique IDentifier).
+ #
+ # require 'bundler/vendor/securerandom/lib/random/formatter'
+ #
+ # Random.uuid_v7 # => "0188d4c3-1311-7f96-85c7-242a7aa58f1e"
+ # Random.uuid_v7 # => "0188d4c3-16fe-744f-86af-38fa04c62bb5"
+ # Random.uuid_v7 # => "0188d4c3-1af8-764f-b049-c204ce0afa23"
+ # Random.uuid_v7 # => "0188d4c3-1e74-7085-b14f-ef6415dc6f31"
+ # # |<--sorted-->| |<----- random ---->|
+ #
+ # # or
+ # prng = Random.new
+ # prng.uuid_v7 # => "0188ca51-5e72-7950-a11d-def7ff977c98"
+ #
+ # The version 7 UUID starts with the least significant 48 bits of a 64 bit
+ # Unix timestamp (milliseconds since the epoch) and fills the remaining bits
+ # with random data, excluding the version and variant bits.
+ #
+ # This allows version 7 UUIDs to be sorted by creation time. Time ordered
+ # UUIDs can be used for better database index locality of newly inserted
+ # records, which may have a significant performance benefit compared to random
+ # data inserts.
+ #
+ # The result contains 74 random bits (9.25 random bytes).
+ #
+ # Note that this method cannot be made reproducable because its output
+ # includes not only random bits but also timestamp.
+ #
+ # See draft-ietf-uuidrev-rfc4122bis[https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis/]
+ # for details of UUIDv7.
+ #
+ # ==== Monotonicity
+ #
+ # UUIDv7 has millisecond precision by default, so multiple UUIDs created
+ # within the same millisecond are not issued in monotonically increasing
+ # order. To create UUIDs that are time-ordered with sub-millisecond
+ # precision, up to 12 bits of additional timestamp may added with
+ # +extra_timestamp_bits+. The extra timestamp precision comes at the expense
+ # of random bits. Setting <tt>extra_timestamp_bits: 12</tt> provides ~244ns
+ # of precision, but only 62 random bits (7.75 random bytes).
+ #
+ # prng = Random.new
+ # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 12) }
+ # # =>
+ # ["0188d4c7-13da-74f9-8b53-22a786ffdd5a",
+ # "0188d4c7-13da-753b-83a5-7fb9b2afaeea",
+ # "0188d4c7-13da-754a-88ea-ac0baeedd8db",
+ # "0188d4c7-13da-7557-83e1-7cad9cda0d8d"]
+ # # |<--- sorted --->| |<-- random --->|
+ #
+ # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 8) }
+ # # =>
+ # ["0188d4c7-3333-7a95-850a-de6edb858f7e",
+ # "0188d4c7-3333-7ae8-842e-bc3a8b7d0cf9", # <- out of order
+ # "0188d4c7-3333-7ae2-995a-9f135dc44ead", # <- out of order
+ # "0188d4c7-3333-7af9-87c3-8f612edac82e"]
+ # # |<--- sorted -->||<---- random --->|
+ #
+ # Any rollbacks of the system clock will break monotonicity. UUIDv7 is based
+ # on UTC, which excludes leap seconds and can rollback the clock. To avoid
+ # this, the system clock can synchronize with an NTP server configured to use
+ # a "leap smear" approach. NTP or PTP will also be needed to synchronize
+ # across distributed nodes.
+ #
+ # Counters and other mechanisms for stronger guarantees of monotonicity are
+ # not implemented. Applications with stricter requirements should follow
+ # {Section 6.2}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-07.html#monotonicity_counters]
+ # of the specification.
+ #
+ def uuid_v7(extra_timestamp_bits: 0)
+ case (extra_timestamp_bits = Integer(extra_timestamp_bits))
+ when 0 # min timestamp precision
+ ms = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
+ rand = random_bytes(10)
+ rand.setbyte(0, rand.getbyte(0) & 0x0f | 0x70) # version
+ rand.setbyte(2, rand.getbyte(2) & 0x3f | 0x80) # variant
+ "%08x-%04x-%s" % [
+ (ms & 0x0000_ffff_ffff_0000) >> 16,
+ (ms & 0x0000_0000_0000_ffff),
+ rand.unpack("H4H4H12").join("-")
+ ]
+
+ when 12 # max timestamp precision
+ ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ .divmod(1_000_000)
+ extra_bits = ns * 4096 / 1_000_000
+ rand = random_bytes(8)
+ rand.setbyte(0, rand.getbyte(0) & 0x3f | 0x80) # variant
+ "%08x-%04x-7%03x-%s" % [
+ (ms & 0x0000_ffff_ffff_0000) >> 16,
+ (ms & 0x0000_0000_0000_ffff),
+ extra_bits,
+ rand.unpack("H4H12").join("-")
+ ]
+
+ when (0..12) # the generic version is slower than the special cases above
+ rand_a, rand_b1, rand_b2, rand_b3 = random_bytes(10).unpack("nnnN")
+ rand_mask_bits = 12 - extra_timestamp_bits
+ ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ .divmod(1_000_000)
+ "%08x-%04x-%04x-%04x-%04x%08x" % [
+ (ms & 0x0000_ffff_ffff_0000) >> 16,
+ (ms & 0x0000_0000_0000_ffff),
+ 0x7000 |
+ ((ns * (1 << extra_timestamp_bits) / 1_000_000) << rand_mask_bits) |
+ rand_a & ((1 << rand_mask_bits) - 1),
+ 0x8000 | (rand_b1 & 0x3fff),
+ rand_b2,
+ rand_b3
+ ]
+
+ else
+ raise ArgumentError, "extra_timestamp_bits must be in 0..12"
+ end
+ end
+
+ # Internal interface to Random; Generate random data _n_ bytes.
+ private def gen_random(n)
+ self.bytes(n)
+ end
+
+ # Generate a string that randomly draws from a
+ # source array of characters.
+ #
+ # The argument _source_ specifies the array of characters from which
+ # to generate the string.
+ # The argument _n_ specifies the length, in characters, of the string to be
+ # generated.
+ #
+ # The result may contain whatever characters are in the source array.
+ #
+ # require 'bundler/vendor/securerandom/lib/random/formatter'
+ #
+ # prng.choose([*'l'..'r'], 16) #=> "lmrqpoonmmlqlron"
+ # prng.choose([*'0'..'9'], 5) #=> "27309"
+ private def choose(source, n)
+ size = source.size
+ m = 1
+ limit = size
+ while limit * size <= 0x100000000
+ limit *= size
+ m += 1
+ end
+ result = ''.dup
+ while m <= n
+ rs = random_number(limit)
+ is = rs.digits(size)
+ (m-is.length).times { is << 0 }
+ result << source.values_at(*is).join('')
+ n -= m
+ end
+ if 0 < n
+ rs = random_number(limit)
+ is = rs.digits(size)
+ if is.length < n
+ (n-is.length).times { is << 0 }
+ else
+ is.pop while n < is.length
+ end
+ result.concat source.values_at(*is).join('')
+ end
+ result
+ end
+
+ # The default character list for #alphanumeric.
+ ALPHANUMERIC = [*'A'..'Z', *'a'..'z', *'0'..'9']
+
+ # Generate a random alphanumeric string.
+ #
+ # The argument _n_ specifies the length, in characters, of the alphanumeric
+ # string to be generated.
+ # The argument _chars_ specifies the character list which the result is
+ # consist of.
+ #
+ # If _n_ is not specified or is nil, 16 is assumed.
+ # It may be larger in the future.
+ #
+ # The result may contain A-Z, a-z and 0-9, unless _chars_ is specified.
+ #
+ # require 'bundler/vendor/securerandom/lib/random/formatter'
+ #
+ # Random.alphanumeric #=> "2BuBuLf3WfSKyQbR"
+ # # or
+ # prng = Random.new
+ # prng.alphanumeric(10) #=> "i6K93NdqiH"
+ #
+ # Random.alphanumeric(4, chars: [*"0".."9"]) #=> "2952"
+ # # or
+ # prng = Random.new
+ # prng.alphanumeric(10, chars: [*"!".."/"]) #=> ",.,++%/''."
+ def alphanumeric(n = nil, chars: ALPHANUMERIC)
+ n = 16 if n.nil?
+ choose(chars, n)
+ end
+end
diff --git a/lib/bundler/vendor/securerandom/lib/securerandom.rb b/lib/bundler/vendor/securerandom/lib/securerandom.rb
new file mode 100644
index 0000000000..e797054468
--- /dev/null
+++ b/lib/bundler/vendor/securerandom/lib/securerandom.rb
@@ -0,0 +1,96 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: true
+
+require_relative 'random/formatter'
+
+# == Secure random number generator interface.
+#
+# This library is an interface to secure random number generators which are
+# suitable for generating session keys in HTTP cookies, etc.
+#
+# You can use this library in your application by requiring it:
+#
+# require 'bundler/vendor/securerandom/lib/securerandom'
+#
+# It supports the following secure random number generators:
+#
+# * openssl
+# * /dev/urandom
+# * Win32
+#
+# Bundler::SecureRandom is extended by the Bundler::Random::Formatter module which
+# defines the following methods:
+#
+# * alphanumeric
+# * base64
+# * choose
+# * gen_random
+# * hex
+# * rand
+# * random_bytes
+# * random_number
+# * urlsafe_base64
+# * uuid
+#
+# These methods are usable as class methods of Bundler::SecureRandom such as
+# +Bundler::SecureRandom.hex+.
+#
+# If a secure random number generator is not available,
+# +NotImplementedError+ is raised.
+
+module Bundler::SecureRandom
+
+ # The version
+ VERSION = "0.3.1"
+
+ class << self
+ # Returns a random binary string containing +size+ bytes.
+ #
+ # See Random.bytes
+ def bytes(n)
+ return gen_random(n)
+ end
+
+ private
+
+ # :stopdoc:
+
+ # Implementation using OpenSSL
+ def gen_random_openssl(n)
+ return OpenSSL::Random.random_bytes(n)
+ end
+
+ # Implementation using system random device
+ def gen_random_urandom(n)
+ ret = Random.urandom(n)
+ unless ret
+ raise NotImplementedError, "No random device"
+ end
+ unless ret.length == n
+ raise NotImplementedError, "Unexpected partial read from random device: only #{ret.length} for #{n} bytes"
+ end
+ ret
+ end
+
+ begin
+ # Check if Random.urandom is available
+ Random.urandom(1)
+ alias gen_random gen_random_urandom
+ rescue RuntimeError
+ begin
+ require 'openssl'
+ rescue NoMethodError
+ raise NotImplementedError, "No random device"
+ else
+ alias gen_random gen_random_openssl
+ end
+ end
+
+ # :startdoc:
+
+ # Generate random data bytes for Bundler::Random::Formatter
+ public :gen_random
+ end
+end
+
+Bundler::SecureRandom.extend(Bundler::Random::Formatter)
diff --git a/lib/bundler/vendor/uri/lib/uri/common.rb b/lib/bundler/vendor/uri/lib/uri/common.rb
index 93f4f226ad..89044da036 100644
--- a/lib/bundler/vendor/uri/lib/uri/common.rb
+++ b/lib/bundler/vendor/uri/lib/uri/common.rb
@@ -19,6 +19,8 @@ module Bundler::URI
Parser = RFC2396_Parser
RFC3986_PARSER = RFC3986_Parser.new
Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor)
+ RFC2396_PARSER = RFC2396_Parser.new
+ Ractor.make_shareable(RFC2396_PARSER) if defined?(Ractor)
# Bundler::URI::Parser.new
DEFAULT_PARSER = Parser.new
diff --git a/lib/bundler/vendor/uri/lib/uri/version.rb b/lib/bundler/vendor/uri/lib/uri/version.rb
index 1fa1c7c09a..ac94e15221 100644
--- a/lib/bundler/vendor/uri/lib/uri/version.rb
+++ b/lib/bundler/vendor/uri/lib/uri/version.rb
@@ -1,6 +1,6 @@
module Bundler::URI
# :stopdoc:
- VERSION_CODE = '001300'.freeze
+ VERSION_CODE = '001301'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/lib/bundler/vendored_net_http.rb b/lib/bundler/vendored_net_http.rb
index 908ec4bcaf..8ff2ccd1fe 100644
--- a/lib/bundler/vendored_net_http.rb
+++ b/lib/bundler/vendored_net_http.rb
@@ -1,8 +1,23 @@
# frozen_string_literal: true
-begin
- require "rubygems/net/http"
-rescue LoadError
- require "net/http"
- Gem::Net = Net
+# This defined? guard can be removed once RubyGems 3.4 support is dropped.
+#
+# Bundler specs load this code from `spec/support/vendored_net_http.rb` to avoid
+# activating the Bundler gem too early. Without this guard, we get redefinition
+# warnings once Bundler is actually activated and
+# `lib/bundler/vendored_net_http.rb` is required. This is not an issue in
+# RubyGems versions including `rubygems/vendored_net_http` since `require` takes
+# care of avoiding the double load.
+#
+unless defined?(Gem::Net)
+ begin
+ require "rubygems/vendored_net_http"
+ rescue LoadError
+ begin
+ require "rubygems/net/http"
+ rescue LoadError
+ require "net/http"
+ Gem::Net = Net
+ end
+ end
end
diff --git a/lib/bundler/vendored_securerandom.rb b/lib/bundler/vendored_securerandom.rb
new file mode 100644
index 0000000000..6c15f4a2b2
--- /dev/null
+++ b/lib/bundler/vendored_securerandom.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+# Use RubyGems vendored copy when available. Otherwise fallback to Bundler
+# vendored copy. The vendored copy in Bundler can be removed once support for
+# RubyGems 3.5.18 is dropped.
+
+begin
+ require "rubygems/vendored_securerandom"
+rescue LoadError
+ module Bundler::Random; end
+ require_relative "vendor/securerandom/lib/securerandom"
+ Gem::SecureRandom = Bundler::SecureRandom
+ Gem::Random = Bundler::Random
+end
diff --git a/lib/bundler/vendored_timeout.rb b/lib/bundler/vendored_timeout.rb
index 34770f2116..9b2507c0cc 100644
--- a/lib/bundler/vendored_timeout.rb
+++ b/lib/bundler/vendored_timeout.rb
@@ -1,8 +1,12 @@
# frozen_string_literal: true
begin
- require "rubygems/timeout"
+ require "rubygems/vendored_timeout"
rescue LoadError
- require "timeout"
- Gem::Timeout = Timeout
+ begin
+ require "rubygems/timeout"
+ rescue LoadError
+ require "timeout"
+ Gem::Timeout = Timeout
+ end
end
diff --git a/lib/bundler/vendored_uri.rb b/lib/bundler/vendored_uri.rb
index 905e8158e8..2efddc65f9 100644
--- a/lib/bundler/vendored_uri.rb
+++ b/lib/bundler/vendored_uri.rb
@@ -1,4 +1,21 @@
# frozen_string_literal: true
module Bundler; end
-require_relative "vendor/uri/lib/uri"
+
+# Use RubyGems vendored copy when available. Otherwise fallback to Bundler
+# vendored copy. The vendored copy in Bundler can be removed once support for
+# RubyGems 3.5 is dropped.
+
+begin
+ require "rubygems/vendor/uri/lib/uri"
+rescue LoadError
+ require_relative "vendor/uri/lib/uri"
+ Gem::URI = Bundler::URI
+
+ module Gem
+ def URI(uri) # rubocop:disable Naming/MethodName
+ Bundler::URI(uri)
+ end
+ module_function :URI
+ end
+end
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
index 05b6c66ce4..b8bfd4fc18 100644
--- a/lib/bundler/version.rb
+++ b/lib/bundler/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module Bundler
- VERSION = "2.5.5".freeze
+ VERSION = "2.5.22".freeze
def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i
diff --git a/lib/bundler/yaml_serializer.rb b/lib/bundler/yaml_serializer.rb
index 37ccc46c26..ab1eb6dbcf 100644
--- a/lib/bundler/yaml_serializer.rb
+++ b/lib/bundler/yaml_serializer.rb
@@ -41,7 +41,7 @@ module Bundler
HASH_REGEX = /
^
([ ]*) # indentations
- (.+) # key
+ ([^#]+) # key excludes comment char '#'
(?::(?=(?:\s|$))) # : (without the lookahead the #key includes this when : is present in value)
[ ]?
(['"]?) # optional opening quote
@@ -58,7 +58,8 @@ module Bundler
str.split(/\r?\n/) do |line|
if match = HASH_REGEX.match(line)
indent, key, quote, val = match.captures
- convert_to_backward_compatible_key!(key)
+ val = strip_comment(val)
+
depth = indent.size / 2
if quote.empty? && val.empty?
new_hash = {}
@@ -72,6 +73,8 @@ module Bundler
end
elsif match = ARRAY_REGEX.match(line)
_, val = match.captures
+ val = strip_comment(val)
+
last_hash[last_empty_key] = [] unless last_hash[last_empty_key].is_a?(Array)
last_hash[last_empty_key].push(val)
@@ -80,14 +83,16 @@ module Bundler
res
end
- # for settings' keys
- def convert_to_backward_compatible_key!(key)
- key << "/" if /https?:/i.match?(key) && !%r{/\Z}.match?(key)
- key.gsub!(".", "__")
+ def strip_comment(val)
+ if val.include?("#") && !val.start_with?("#")
+ val.split("#", 2).first.strip
+ else
+ val
+ end
end
class << self
- private :dump_hash, :convert_to_backward_compatible_key!
+ private :dump_hash
end
end
end
diff --git a/lib/cgi.rb b/lib/cgi.rb
index 7af85e7fc8..69c3c4fd24 100644
--- a/lib/cgi.rb
+++ b/lib/cgi.rb
@@ -288,7 +288,7 @@
#
class CGI
- VERSION = "0.4.1"
+ VERSION = "0.4.2"
end
require 'cgi/core'
diff --git a/lib/cgi/cgi.gemspec b/lib/cgi/cgi.gemspec
index 381c55a5ca..5ef00d591d 100644
--- a/lib/cgi/cgi.gemspec
+++ b/lib/cgi/cgi.gemspec
@@ -25,7 +25,8 @@ Gem::Specification.new do |spec|
spec.executables = []
spec.files = [
- "LICENSE.txt",
+ "COPYING",
+ "BSDL",
"README.md",
*Dir["lib{.rb,/**/*.rb}", "bin/*"] ]
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index 9498e2f9fa..1c4ef6a600 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -190,9 +190,10 @@ class CGI
values ||= ""
values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) }
if cookies.has_key?(name)
- values = cookies[name].value + values
+ cookies[name].concat(values)
+ else
+ cookies[name] = Cookie.new(name, *values)
end
- cookies[name] = Cookie.new(name, *values)
end
cookies
diff --git a/lib/cgi/session/pstore.rb b/lib/cgi/session/pstore.rb
index 45d0d8ae2c..6e3d10f075 100644
--- a/lib/cgi/session/pstore.rb
+++ b/lib/cgi/session/pstore.rb
@@ -11,7 +11,10 @@
# cgi/session.rb for more details on session storage managers.
require_relative '../session'
-require 'pstore'
+begin
+ require 'pstore'
+rescue LoadError
+end
class CGI
class Session
@@ -82,7 +85,7 @@ class CGI
File::unlink path
end
- end
+ end if defined?(::PStore)
end
end
# :enddoc:
diff --git a/lib/cgi/util.rb b/lib/cgi/util.rb
index 4986e544e0..5f12eae130 100644
--- a/lib/cgi/util.rb
+++ b/lib/cgi/util.rb
@@ -184,7 +184,7 @@ module CGI::Util
def escapeElement(string, *elements)
elements = elements[0] if elements[0].kind_of?(Array)
unless elements.empty?
- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do
+ string.gsub(/<\/?(?:#{elements.join("|")})\b[^<>]*+>?/im) do
CGI.escapeHTML($&)
end
else
@@ -204,7 +204,7 @@ module CGI::Util
def unescapeElement(string, *elements)
elements = elements[0] if elements[0].kind_of?(Array)
unless elements.empty?
- string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/i) do
+ string.gsub(/&lt;\/?(?:#{elements.join("|")})\b(?>[^&]+|&(?![gl]t;)\w+;)*(?:&gt;)?/im) do
unescapeHTML($&)
end
else
diff --git a/lib/irb.rb b/lib/irb.rb
index 006b52bec5..b3435c257e 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -1,5 +1,6 @@
-# frozen_string_literal: false
-#
+# frozen_string_literal: true
+
+# :markup: markdown
# irb.rb - irb main module
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
@@ -9,7 +10,7 @@ require "reline"
require_relative "irb/init"
require_relative "irb/context"
-require_relative "irb/extend-command"
+require_relative "irb/default_commands"
require_relative "irb/ruby-lex"
require_relative "irb/statement"
@@ -22,546 +23,553 @@ require_relative "irb/easter-egg"
require_relative "irb/debug"
require_relative "irb/pager"
-# == \IRB
+# ## IRB
#
-# \Module \IRB ("Interactive Ruby") provides a shell-like interface
-# that supports user interaction with the Ruby interpreter.
+# Module IRB ("Interactive Ruby") provides a shell-like interface that supports
+# user interaction with the Ruby interpreter.
#
-# It operates as a <i>read-eval-print loop</i>
-# ({REPL}[https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop])
+# It operates as a *read-eval-print loop*
+# ([REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop))
# that:
#
-# - <b>_Reads_</b> each character as you type.
-# You can modify the \IRB context to change the way input works.
-# See {Input}[rdoc-ref:IRB@Input].
-# - <b>_Evaluates_</b> the code each time it has read a syntactically complete passage.
-# - <b>_Prints_</b> after evaluating.
-# You can modify the \IRB context to change the way output works.
-# See {Output}[rdoc-ref:IRB@Output].
+# * ***Reads*** each character as you type. You can modify the IRB context to
+# change the way input works. See [Input](rdoc-ref:IRB@Input).
+# * ***Evaluates*** the code each time it has read a syntactically complete
+# passage.
+# * ***Prints*** after evaluating. You can modify the IRB context to change
+# the way output works. See [Output](rdoc-ref:IRB@Output).
+#
#
# Example:
#
-# $ irb
-# irb(main):001> File.basename(Dir.pwd)
-# => "irb"
-# irb(main):002> Dir.entries('.').size
-# => 25
-# irb(main):003* Dir.entries('.').select do |entry|
-# irb(main):004* entry.start_with?('R')
-# irb(main):005> end
-# => ["README.md", "Rakefile"]
+# $ irb
+# irb(main):001> File.basename(Dir.pwd)
+# => "irb"
+# irb(main):002> Dir.entries('.').size
+# => 25
+# irb(main):003* Dir.entries('.').select do |entry|
+# irb(main):004* entry.start_with?('R')
+# irb(main):005> end
+# => ["README.md", "Rakefile"]
+#
+# The typed input may also include [\IRB-specific
+# commands](rdoc-ref:IRB@IRB-Specific+Commands).
#
-# The typed input may also include
-# {\IRB-specific commands}[rdoc-ref:IRB@IRB-Specific+Commands].
+# As seen above, you can start IRB by using the shell command `irb`.
#
-# As seen above, you can start \IRB by using the shell command +irb+.
+# You can stop an IRB session by typing command `exit`:
#
-# You can stop an \IRB session by typing command +exit+:
+# irb(main):006> exit
+# $
#
-# irb(main):006> exit
-# $
+# At that point, IRB calls any hooks found in array `IRB.conf[:AT_EXIT]`, then
+# exits.
#
-# At that point, \IRB calls any hooks found in array <tt>IRB.conf[:AT_EXIT]</tt>,
-# then exits.
+# ## Startup
#
-# == Startup
+# At startup, IRB:
#
-# At startup, \IRB:
+# 1. Interprets (as Ruby code) the content of the [configuration
+# file](rdoc-ref:IRB@Configuration+File) (if given).
+# 2. Constructs the initial session context from [hash
+# IRB.conf](rdoc-ref:IRB@Hash+IRB.conf) and from default values; the hash
+# content may have been affected by [command-line
+# options](rdoc-ref:IB@Command-Line+Options), and by direct assignments in
+# the configuration file.
+# 3. Assigns the context to variable `conf`.
+# 4. Assigns command-line arguments to variable `ARGV`.
+# 5. Prints the [prompt](rdoc-ref:IRB@Prompt+and+Return+Formats).
+# 6. Puts the content of the [initialization
+# script](rdoc-ref:IRB@Initialization+Script) onto the IRB shell, just as if
+# it were user-typed commands.
#
-# 1. Interprets (as Ruby code) the content of the
-# {configuration file}[rdoc-ref:IRB@Configuration+File] (if given).
-# 1. Constructs the initial session context
-# from {hash IRB.conf}[rdoc-ref:IRB@Hash+IRB.conf] and from default values;
-# the hash content may have been affected
-# by {command-line options}[rdoc-ref:IB@Command-Line+Options],
-# and by direct assignments in the configuration file.
-# 1. Assigns the context to variable +conf+.
-# 1. Assigns command-line arguments to variable <tt>ARGV</tt>.
-# 1. Prints the {prompt}[rdoc-ref:IRB@Prompt+and+Return+Formats].
-# 1. Puts the content of the
-# {initialization script}[rdoc-ref:IRB@Initialization+Script]
-# onto the \IRB shell, just as if it were user-typed commands.
#
-# === The Command Line
+# ### The Command Line
#
-# On the command line, all options precede all arguments;
-# the first item that is not recognized as an option is treated as an argument,
-# as are all items that follow.
+# On the command line, all options precede all arguments; the first item that is
+# not recognized as an option is treated as an argument, as are all items that
+# follow.
#
-# ==== Command-Line Options
+# #### Command-Line Options
#
-# Many command-line options affect entries in hash <tt>IRB.conf</tt>,
-# which in turn affect the initial configuration of the \IRB session.
+# Many command-line options affect entries in hash `IRB.conf`, which in turn
+# affect the initial configuration of the IRB session.
#
# Details of the options are described in the relevant subsections below.
#
-# A cursory list of the \IRB command-line options
-# may be seen in the {help message}[https://raw.githubusercontent.com/ruby/irb/master/lib/irb/lc/help-message],
-# which is also displayed if you use command-line option <tt>--help</tt>.
+# A cursory list of the IRB command-line options may be seen in the [help
+# message](https://raw.githubusercontent.com/ruby/irb/master/lib/irb/lc/help-message),
+# which is also displayed if you use command-line option `--help`.
#
# If you are interested in a specific option, consult the
-# {index}[rdoc-ref:doc/irb/indexes.md@Index+of+Command-Line+Options].
+# [index](rdoc-ref:doc/irb/indexes.md@Index+of+Command-Line+Options).
#
-# ==== Command-Line Arguments
+# #### Command-Line Arguments
#
-# Command-line arguments are passed to \IRB in array +ARGV+:
+# Command-line arguments are passed to IRB in array `ARGV`:
#
-# $ irb --noscript Foo Bar Baz
-# irb(main):001> ARGV
-# => ["Foo", "Bar", "Baz"]
-# irb(main):002> exit
-# $
+# $ irb --noscript Foo Bar Baz
+# irb(main):001> ARGV
+# => ["Foo", "Bar", "Baz"]
+# irb(main):002> exit
+# $
#
-# Command-line option <tt>--</tt> causes everything that follows
-# to be treated as arguments, even those that look like options:
+# Command-line option `--` causes everything that follows to be treated as
+# arguments, even those that look like options:
#
-# $ irb --noscript -- --noscript -- Foo Bar Baz
-# irb(main):001> ARGV
-# => ["--noscript", "--", "Foo", "Bar", "Baz"]
-# irb(main):002> exit
-# $
+# $ irb --noscript -- --noscript -- Foo Bar Baz
+# irb(main):001> ARGV
+# => ["--noscript", "--", "Foo", "Bar", "Baz"]
+# irb(main):002> exit
+# $
#
-# === Configuration File
+# ### Configuration File
#
-# You can initialize \IRB via a <i>configuration file</i>.
+# You can initialize IRB via a *configuration file*.
#
-# If command-line option <tt>-f</tt> is given,
-# no configuration file is looked for.
+# If command-line option `-f` is given, no configuration file is looked for.
#
-# Otherwise, \IRB reads and interprets a configuration file
-# if one is available.
+# Otherwise, IRB reads and interprets a configuration file if one is available.
#
# The configuration file can contain any Ruby code, and can usefully include
# user code that:
#
-# - Can then be debugged in \IRB.
-# - Configures \IRB itself.
-# - Requires or loads files.
+# * Can then be debugged in IRB.
+# * Configures IRB itself.
+# * Requires or loads files.
+#
#
# The path to the configuration file is the first found among:
#
-# - The value of variable <tt>$IRBRC</tt>, if defined.
-# - The value of variable <tt>$XDG_CONFIG_HOME/irb/irbrc</tt>, if defined.
-# - File <tt>$HOME/.irbrc</tt>, if it exists.
-# - File <tt>$HOME/.config/irb/irbrc</tt>, if it exists.
-# - File +.config/irb/irbrc+ in the current directory, if it exists.
-# - File +.irbrc+ in the current directory, if it exists.
-# - File +irb.rc+ in the current directory, if it exists.
-# - File +_irbrc+ in the current directory, if it exists.
-# - File <tt>$irbrc</tt> in the current directory, if it exists.
+# * The value of variable `$IRBRC`, if defined.
+# * The value of variable `$XDG_CONFIG_HOME/irb/irbrc`, if defined.
+# * File `$HOME/.irbrc`, if it exists.
+# * File `$HOME/.config/irb/irbrc`, if it exists.
+# * File `.irbrc` in the current directory, if it exists.
+# * File `irb.rc` in the current directory, if it exists.
+# * File `_irbrc` in the current directory, if it exists.
+# * File `$irbrc` in the current directory, if it exists.
+#
#
# If the search fails, there is no configuration file.
#
-# If the search succeeds, the configuration file is read as Ruby code,
-# and so can contain any Ruby programming you like.
+# If the search succeeds, the configuration file is read as Ruby code, and so
+# can contain any Ruby programming you like.
+#
+# Method `conf.rc?` returns `true` if a configuration file was read, `false`
+# otherwise. Hash entry `IRB.conf[:RC]` also contains that value.
#
-# \Method <tt>conf.rc?</tt> returns +true+ if a configuration file was read,
-# +false+ otherwise.
-# \Hash entry <tt>IRB.conf[:RC]</tt> also contains that value.
+# ### Hash `IRB.conf`
#
-# === \Hash <tt>IRB.conf</tt>
+# The initial entries in hash `IRB.conf` are determined by:
#
-# The initial entries in hash <tt>IRB.conf</tt> are determined by:
+# * Default values.
+# * Command-line options, which may override defaults.
+# * Direct assignments in the configuration file.
#
-# - Default values.
-# - Command-line options, which may override defaults.
-# - Direct assignments in the configuration file.
#
-# You can see the hash by typing <tt>IRB.conf</tt>.
+# You can see the hash by typing `IRB.conf`.
#
-# Details of the entries' meanings are described in the relevant subsections below.
+# Details of the entries' meanings are described in the relevant subsections
+# below.
#
# If you are interested in a specific entry, consult the
-# {index}[rdoc-ref:doc/irb/indexes.md@Index+of+IRB.conf+Entries].
+# [index](rdoc-ref:doc/irb/indexes.md@Index+of+IRB.conf+Entries).
+#
+# ### Notes on Initialization Precedence
#
-# === Notes on Initialization Precedence
+# * Any conflict between an entry in hash `IRB.conf` and a command-line option
+# is resolved in favor of the hash entry.
+# * Hash `IRB.conf` affects the context only once, when the configuration file
+# is interpreted; any subsequent changes to it do not affect the context and
+# are therefore essentially meaningless.
#
-# - Any conflict between an entry in hash <tt>IRB.conf</tt> and a command-line option
-# is resolved in favor of the hash entry.
-# - \Hash <tt>IRB.conf</tt> affects the context only once,
-# when the configuration file is interpreted;
-# any subsequent changes to it do not affect the context
-# and are therefore essentially meaningless.
#
-# === Initialization Script
+# ### Initialization Script
#
-# By default, the first command-line argument (after any options)
-# is the path to a Ruby initialization script.
+# By default, the first command-line argument (after any options) is the path to
+# a Ruby initialization script.
#
-# \IRB reads the initialization script and puts its content onto the \IRB shell,
+# IRB reads the initialization script and puts its content onto the IRB shell,
# just as if it were user-typed commands.
#
-# Command-line option <tt>--noscript</tt> causes the first command-line argument
-# to be treated as an ordinary argument (instead of an initialization script);
-# <tt>--script</tt> is the default.
+# Command-line option `--noscript` causes the first command-line argument to be
+# treated as an ordinary argument (instead of an initialization script);
+# `--script` is the default.
#
-# == Input
+# ## Input
#
-# This section describes the features that allow you to change
-# the way \IRB input works;
-# see also {Input and Output}[rdoc-ref:IRB@Input+and+Output].
+# This section describes the features that allow you to change the way IRB input
+# works; see also [Input and Output](rdoc-ref:IRB@Input+and+Output).
#
-# === Input Command History
+# ### Input Command History
#
-# By default, \IRB stores a history of up to 1000 input commands
-# in file <tt>~/.irb_history</tt>
-# (or, if a {configuration file}[rdoc-ref:IRB@Configuration+File]
-# is found, in file +.irb_history+
-# inin the same directory as that file).
+# By default, IRB stores a history of up to 1000 input commands in a file named
+# `.irb_history`. The history file will be in the same directory as the
+# [configuration file](rdoc-ref:IRB@Configuration+File) if one is found, or in
+# `~/` otherwise.
#
-# A new \IRB session creates the history file if it does not exist,
-# and appends to the file if it does exist.
+# A new IRB session creates the history file if it does not exist, and appends
+# to the file if it does exist.
#
# You can change the filepath by adding to your configuration file:
-# <tt>IRB.conf[:HISTORY_FILE] = _filepath_</tt>,
-# where _filepath_ is a string filepath.
+# `IRB.conf[:HISTORY_FILE] = *filepath*`, where *filepath* is a string filepath.
#
-# During the session, method <tt>conf.history_file</tt> returns the filepath,
-# and method <tt>conf.history_file = <i>new_filepath</i></tt>
-# copies the history to the file at <i>new_filepath</i>,
-# which becomes the history file for the session.
+# During the session, method `conf.history_file` returns the filepath, and
+# method `conf.history_file = *new_filepath*` copies the history to the file at
+# *new_filepath*, which becomes the history file for the session.
#
-# You can change the number of commands saved by adding to your configuration file:
-# <tt>IRB.conf[:SAVE_HISTORY] = _n_</tt>,
-# where _n_ is one of:
+# You can change the number of commands saved by adding to your configuration
+# file: `IRB.conf[:SAVE_HISTORY] = *n*`, wheHISTORY_FILEre *n* is one of:
#
-# - Positive integer: the number of commands to be saved,
-# - Zero: all commands are to be saved.
-# - +nil+: no commands are to be saved,.
+# * Positive integer: the number of commands to be saved,
+# * Zero: all commands are to be saved.
+# * `nil`: no commands are to be saved,.
#
-# During the session, you can use
-# methods <tt>conf.save_history</tt> or <tt>conf.save_history=</tt>
-# to retrieve or change the count.
#
-# === Command Aliases
+# During the session, you can use methods `conf.save_history` or
+# `conf.save_history=` to retrieve or change the count.
#
-# By default, \IRB defines several command aliases:
+# ### Command Aliases
#
-# irb(main):001> conf.command_aliases
-# => {:"$"=>:show_source, :"@"=>:whereami}
+# By default, IRB defines several command aliases:
+#
+# irb(main):001> conf.command_aliases
+# => {:"$"=>:show_source, :"@"=>:whereami}
#
# You can change the initial aliases in the configuration file with:
#
-# IRB.conf[:COMMAND_ALIASES] = {foo: :show_source, bar: :whereami}
+# IRB.conf[:COMMAND_ALIASES] = {foo: :show_source, bar: :whereami}
#
-# You can replace the current aliases at any time
-# with configuration method <tt>conf.command_aliases=</tt>;
-# Because <tt>conf.command_aliases</tt> is a hash,
-# you can modify it.
+# You can replace the current aliases at any time with configuration method
+# `conf.command_aliases=`; Because `conf.command_aliases` is a hash, you can
+# modify it.
#
-# === End-of-File
+# ### End-of-File
#
-# By default, <tt>IRB.conf[:IGNORE_EOF]</tt> is +false+,
-# which means that typing the end-of-file character <tt>Ctrl-D</tt>
-# causes the session to exit.
+# By default, `IRB.conf[:IGNORE_EOF]` is `false`, which means that typing the
+# end-of-file character `Ctrl-D` causes the session to exit.
#
-# You can reverse that behavior by adding <tt>IRB.conf[:IGNORE_EOF] = true</tt>
-# to the configuration file.
+# You can reverse that behavior by adding `IRB.conf[:IGNORE_EOF] = true` to the
+# configuration file.
#
-# During the session, method <tt>conf.ignore_eof?</tt> returns the setting,
-# and method <tt>conf.ignore_eof = _boolean_</tt> sets it.
+# During the session, method `conf.ignore_eof?` returns the setting, and method
+# `conf.ignore_eof = *boolean*` sets it.
#
-# === SIGINT
+# ### SIGINT
#
-# By default, <tt>IRB.conf[:IGNORE_SIGINT]</tt> is +true+,
-# which means that typing the interrupt character <tt>Ctrl-C</tt>
-# causes the session to exit.
+# By default, `IRB.conf[:IGNORE_SIGINT]` is `true`, which means that typing the
+# interrupt character `Ctrl-C` causes the session to exit.
#
-# You can reverse that behavior by adding <tt>IRB.conf[:IGNORE_SIGING] = false</tt>
-# to the configuration file.
+# You can reverse that behavior by adding `IRB.conf[:IGNORE_SIGING] = false` to
+# the configuration file.
#
-# During the session, method <tt>conf.ignore_siging?</tt> returns the setting,
-# and method <tt>conf.ignore_sigint = _boolean_</tt> sets it.
+# During the session, method `conf.ignore_siging?` returns the setting, and
+# method `conf.ignore_sigint = *boolean*` sets it.
#
-# === Automatic Completion
+# ### Automatic Completion
#
-# By default, \IRB enables
-# {automatic completion}[https://en.wikipedia.org/wiki/Autocomplete#In_command-line_interpreters]:
+# By default, IRB enables [automatic
+# completion](https://en.wikipedia.org/wiki/Autocomplete#In_command-line_interpr
+# eters):
#
# You can disable it by either of these:
#
-# - Adding <tt>IRB.conf[:USE_AUTOCOMPLETE] = false</tt> to the configuration file.
-# - Giving command-line option <tt>--noautocomplete</tt>
-# (<tt>--autocomplete</tt> is the default).
+# * Adding `IRB.conf[:USE_AUTOCOMPLETE] = false` to the configuration file.
+# * Giving command-line option `--noautocomplete` (`--autocomplete` is the
+# default).
+#
#
-# \Method <tt>conf.use_autocomplete?</tt> returns +true+
-# if automatic completion is enabled, +false+ otherwise.
+# Method `conf.use_autocomplete?` returns `true` if automatic completion is
+# enabled, `false` otherwise.
#
# The setting may not be changed during the session.
#
-# === Automatic Indentation
+# ### Automatic Indentation
#
-# By default, \IRB automatically indents lines of code to show structure
-# (e.g., it indent the contents of a block).
+# By default, IRB automatically indents lines of code to show structure (e.g.,
+# it indent the contents of a block).
#
-# The current setting is returned
-# by the configuration method <tt>conf.auto_indent_mode</tt>.
+# The current setting is returned by the configuration method
+# `conf.auto_indent_mode`.
#
-# The default initial setting is +true+:
+# The default initial setting is `true`:
#
-# irb(main):001> conf.auto_indent_mode
-# => true
-# irb(main):002* Dir.entries('.').select do |entry|
-# irb(main):003* entry.start_with?('R')
-# irb(main):004> end
-# => ["README.md", "Rakefile"]
+# irb(main):001> conf.auto_indent_mode
+# => true
+# irb(main):002* Dir.entries('.').select do |entry|
+# irb(main):003* entry.start_with?('R')
+# irb(main):004> end
+# => ["README.md", "Rakefile"]
#
-# You can change the initial setting in the
-# configuration file with:
+# You can change the initial setting in the configuration file with:
#
-# IRB.conf[:AUTO_INDENT] = false
+# IRB.conf[:AUTO_INDENT] = false
#
-# Note that the _current_ setting <i>may not</i> be changed in the \IRB session.
+# Note that the *current* setting *may not* be changed in the IRB session.
#
-# === Input \Method
+# ### Input Method
#
-# The \IRB input method determines how command input is to be read;
-# by default, the input method for a session is IRB::RelineInputMethod.
+# The IRB input method determines how command input is to be read; by default,
+# the input method for a session is IRB::RelineInputMethod. Unless the
+# value of the TERM environment variable is 'dumb', in which case the
+# most simplistic input method is used.
#
# You can set the input method by:
#
-# - Adding to the configuration file:
+# * Adding to the configuration file:
#
-# - <tt>IRB.conf[:USE_SINGLELINE] = true</tt>
-# or <tt>IRB.conf[:USE_MULTILINE]= false</tt>
-# sets the input method to IRB::ReadlineInputMethod.
-# - <tt>IRB.conf[:USE_SINGLELINE] = false</tt>
-# or <tt>IRB.conf[:USE_MULTILINE] = true</tt>
-# sets the input method to IRB::RelineInputMethod.
+# * `IRB.conf[:USE_SINGLELINE] = true` or `IRB.conf[:USE_MULTILINE]=
+# false` sets the input method to IRB::ReadlineInputMethod.
+# * `IRB.conf[:USE_SINGLELINE] = false` or `IRB.conf[:USE_MULTILINE] =
+# true` sets the input method to IRB::RelineInputMethod.
#
-# - Giving command-line options:
#
-# - <tt>--singleline</tt>
-# or <tt>--nomultiline</tt>
-# sets the input method to IRB::ReadlineInputMethod.
-# - <tt>--nosingleline</tt>
-# or <tt>--multiline/tt>
-# sets the input method to IRB::RelineInputMethod.
+# * Giving command-line options:
#
-# \Method <tt>conf.use_multiline?</tt>
-# and its synonym <tt>conf.use_reline</tt> return:
+# * `--singleline` or `--nomultiline` sets the input method to
+# IRB::ReadlineInputMethod.
+# * `--nosingleline` or `--multiline` sets the input method to
+# IRB::RelineInputMethod.
+# * `--nosingleline` together with `--nomultiline` sets the
+# input to IRB::StdioInputMethod.
#
-# - +true+ if option <tt>--multiline</tt> was given.
-# - +false+ if option <tt>--nomultiline</tt> was given.
-# - +nil+ if neither was given.
#
-# \Method <tt>conf.use_singleline?</tt>
-# and its synonym <tt>conf.use_readline</tt> return:
+# Method `conf.use_multiline?` and its synonym `conf.use_reline` return:
#
-# - +true+ if option <tt>--singleline</tt> was given.
-# - +false+ if option <tt>--nosingleline</tt> was given.
-# - +nil+ if neither was given.
+# * `true` if option `--multiline` was given.
+# * `false` if option `--nomultiline` was given.
+# * `nil` if neither was given.
#
-# == Output
#
-# This section describes the features that allow you to change
-# the way \IRB output works;
-# see also {Input and Output}[rdoc-ref:IRB@Input+and+Output].
+# Method `conf.use_singleline?` and its synonym `conf.use_readline` return:
#
-# === Return-Value Printing (Echoing)
+# * `true` if option `--singleline` was given.
+# * `false` if option `--nosingleline` was given.
+# * `nil` if neither was given.
#
-# By default, \IRB prints (echoes) the values returned by all input commands.
+#
+# ## Output
+#
+# This section describes the features that allow you to change the way IRB
+# output works; see also [Input and Output](rdoc-ref:IRB@Input+and+Output).
+#
+# ### Return-Value Printing (Echoing)
+#
+# By default, IRB prints (echoes) the values returned by all input commands.
#
# You can change the initial behavior and suppress all echoing by:
#
-# - Adding to the configuration file: <tt>IRB.conf[:ECHO] = false</tt>.
-# (The default value for this entry is +niL+, which means the same as +true+.)
-# - Giving command-line option <tt>--noecho</tt>.
-# (The default is <tt>--echo</tt>.)
+# * Adding to the configuration file: `IRB.conf[:ECHO] = false`. (The default
+# value for this entry is `nil`, which means the same as `true`.)
+# * Giving command-line option `--noecho`. (The default is `--echo`.)
+#
+#
+# During the session, you can change the current setting with configuration
+# method `conf.echo=` (set to `true` or `false`).
#
-# During the session, you can change the current setting
-# with configuration method <tt>conf.echo=</tt> (set to +true+ or +false+).
+# As stated above, by default IRB prints the values returned by all input
+# commands; but IRB offers special treatment for values returned by assignment
+# statements, which may be:
#
-# As stated above, by default \IRB prints the values returned by all input commands;
-# but \IRB offers special treatment for values returned by assignment statements,
-# which may be:
+# * Printed with truncation (to fit on a single line of output), which is the
+# default; an ellipsis (`...` is suffixed, to indicate the truncation):
#
-# - Printed with truncation (to fit on a single line of output),
-# which is the default;
-# an ellipsis (<tt>...</tt> is suffixed, to indicate the truncation):
+# irb(main):001> x = 'abc' * 100
#
-# irb(main):001> x = 'abc' * 100
-# => "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc...
#
-# - Printed in full (regardless of the length).
-# - Suppressed (not printed at all)
+# > "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc...
+#
+# * Printed in full (regardless of the length).
+# * Suppressed (not printed at all)
+#
#
# You can change the initial behavior by:
#
-# - Adding to the configuration file: <tt>IRB.conf[:ECHO_ON_ASSIGNMENT] = false</tt>.
-# (The default value for this entry is +niL+, which means the same as +:truncate+.)
-# - Giving command-line option <tt>--noecho-on-assignment</tt>
-# or <tt>--echo-on-assignment</tt>.
-# (The default is <tt>--truncate-echo-on-assigment</tt>.)
+# * Adding to the configuration file: `IRB.conf[:ECHO_ON_ASSIGNMENT] = false`.
+# (The default value for this entry is `niL`, which means the same as
+# `:truncate`.)
+# * Giving command-line option `--noecho-on-assignment` or
+# `--echo-on-assignment`. (The default is `--truncate-echo-on-assignment`.)
+#
#
-# During the session, you can change the current setting
-# with configuration method <tt>conf.echo_on_assignment=</tt>
-# (set to +true+, +false+, or +:truncate+).
+# During the session, you can change the current setting with configuration
+# method `conf.echo_on_assignment=` (set to `true`, `false`, or `:truncate`).
#
-# By default, \IRB formats returned values by calling method +inspect+.
+# By default, IRB formats returned values by calling method `inspect`.
#
# You can change the initial behavior by:
#
-# - Adding to the configuration file: <tt>IRB.conf[:INSPECT_MODE] = false</tt>.
-# (The default value for this entry is +true+.)
-# - Giving command-line option <tt>--noinspect</tt>.
-# (The default is <tt>--inspect</tt>.)
+# * Adding to the configuration file: `IRB.conf[:INSPECT_MODE] = false`. (The
+# default value for this entry is `true`.)
+# * Giving command-line option `--noinspect`. (The default is `--inspect`.)
#
-# During the session, you can change the setting using method <tt>conf.inspect_mode=</tt>.
#
-# === Multiline Output
+# During the session, you can change the setting using method
+# `conf.inspect_mode=`.
#
-# By default, \IRB prefixes a newline to a multiline response.
+# ### Multiline Output
#
-# You can change the initial default value by adding to the configuation file:
+# By default, IRB prefixes a newline to a multiline response.
#
-# IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] = false
+# You can change the initial default value by adding to the configuration file:
#
-# During a session, you can retrieve or set the value using
-# methods <tt>conf.newline_before_multiline_output?</tt>
-# and <tt>conf.newline_before_multiline_output=</tt>.
+# IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] = false
+#
+# During a session, you can retrieve or set the value using methods
+# `conf.newline_before_multiline_output?` and
+# `conf.newline_before_multiline_output=`.
#
# Examples:
#
-# irb(main):001> conf.inspect_mode = false
-# => false
-# irb(main):002> "foo\nbar"
-# =>
-# foo
-# bar
-# irb(main):003> conf.newline_before_multiline_output = false
-# => false
-# irb(main):004> "foo\nbar"
-# => foo
-# bar
+# irb(main):001> conf.inspect_mode = false
+# => false
+# irb(main):002> "foo\nbar"
+# =>
+# foo
+# bar
+# irb(main):003> conf.newline_before_multiline_output = false
+# => false
+# irb(main):004> "foo\nbar"
+# => foo
+# bar
+#
+# ### Evaluation History
#
-# === Evaluation History
+# By default, IRB saves no history of evaluations (returned values), and the
+# related methods `conf.eval_history`, `_`, and `__` are undefined.
#
-# By default, \IRB saves no history of evaluations (returned values),
-# and the related methods <tt>conf.eval_history</tt>, <tt>_</tt>,
-# and <tt>__</tt> are undefined.
+# You can turn on that history, and set the maximum number of evaluations to be
+# stored:
#
-# You can turn on that history, and set the maximum number of evaluations to be stored:
+# * In the configuration file: add `IRB.conf[:EVAL_HISTORY] = *n*`. (Examples
+# below assume that we've added `IRB.conf[:EVAL_HISTORY] = 5`.)
+# * In the session (at any time): `conf.eval_history = *n*`.
#
-# - In the configuration file: add <tt>IRB.conf[:EVAL_HISTORY] = _n_</tt>.
-# (Examples below assume that we've added <tt>IRB.conf[:EVAL_HISTORY] = 5</tt>.)
-# - In the session (at any time): <tt>conf.eval_history = _n_</tt>.
#
-# If +n+ is zero, all evaluation history is stored.
+# If `n` is zero, all evaluation history is stored.
#
# Doing either of the above:
#
-# - Sets the maximum size of the evaluation history;
-# defines method <tt>conf.eval_history</tt>,
-# which returns the maximum size +n+ of the evaluation history:
-#
-# irb(main):001> conf.eval_history = 5
-# => 5
-# irb(main):002> conf.eval_history
-# => 5
-#
-# - Defines variable <tt>_</tt>, which contains the most recent evaluation,
-# or +nil+ if none; same as method <tt>conf.last_value</tt>:
-#
-# irb(main):003> _
-# => 5
-# irb(main):004> :foo
-# => :foo
-# irb(main):005> :bar
-# => :bar
-# irb(main):006> _
-# => :bar
-# irb(main):007> _
-# => :bar
-#
-# - Defines variable <tt>__</tt>:
-#
-# - <tt>__</tt> unadorned: contains all evaluation history:
-#
-# irb(main):008> :foo
-# => :foo
-# irb(main):009> :bar
-# => :bar
-# irb(main):010> :baz
-# => :baz
-# irb(main):011> :bat
-# => :bat
-# irb(main):012> :bam
-# => :bam
-# irb(main):013> __
-# =>
-# 9 :bar
-# 10 :baz
-# 11 :bat
-# 12 :bam
-# irb(main):014> __
-# =>
-# 10 :baz
-# 11 :bat
-# 12 :bam
-# 13 ...self-history...
-#
-# Note that when the evaluation is multiline, it is displayed differently.
-#
-# - <tt>__[</tt>_m_<tt>]</tt>:
-#
-# - Positive _m_: contains the evaluation for the given line number,
-# or +nil+ if that line number is not in the evaluation history:
-#
-# irb(main):015> __[12]
-# => :bam
-# irb(main):016> __[1]
-# => nil
-#
-# - Negative _m_: contains the +mth+-from-end evaluation,
-# or +nil+ if that evaluation is not in the evaluation history:
-#
-# irb(main):017> __[-3]
-# => :bam
-# irb(main):018> __[-13]
-# => nil
-#
-# - Zero _m_: contains +nil+:
-#
-# irb(main):019> __[0]
-# => nil
-#
-# === Prompt and Return Formats
-#
-# By default, \IRB uses the prompt and return value formats
-# defined in its +:DEFAULT+ prompt mode.
-#
-# ==== The Default Prompt and Return Format
+# * Sets the maximum size of the evaluation history; defines method
+# `conf.eval_history`, which returns the maximum size `n` of the evaluation
+# history:
+#
+# irb(main):001> conf.eval_history = 5
+# => 5
+# irb(main):002> conf.eval_history
+# => 5
+#
+# * Defines variable `_`, which contains the most recent evaluation, or `nil`
+# if none; same as method `conf.last_value`:
+#
+# irb(main):003> _
+# => 5
+# irb(main):004> :foo
+# => :foo
+# irb(main):005> :bar
+# => :bar
+# irb(main):006> _
+# => :bar
+# irb(main):007> _
+# => :bar
+#
+# * Defines variable `__`:
+#
+# * `__` unadorned: contains all evaluation history:
+#
+# irb(main):008> :foo
+# => :foo
+# irb(main):009> :bar
+# => :bar
+# irb(main):010> :baz
+# => :baz
+# irb(main):011> :bat
+# => :bat
+# irb(main):012> :bam
+# => :bam
+# irb(main):013> __
+# =>
+# 9 :bar
+# 10 :baz
+# 11 :bat
+# 12 :bam
+# irb(main):014> __
+# =>
+# 10 :baz
+# 11 :bat
+# 12 :bam
+# 13 ...self-history...
+#
+# Note that when the evaluation is multiline, it is displayed
+# differently.
+#
+# * `__[`*m*`]`:
+#
+# * Positive *m*: contains the evaluation for the given line number,
+# or `nil` if that line number is not in the evaluation history:
+#
+# irb(main):015> __[12]
+# => :bam
+# irb(main):016> __[1]
+# => nil
+#
+# * Negative *m*: contains the `mth`-from-end evaluation, or `nil` if
+# that evaluation is not in the evaluation history:
+#
+# irb(main):017> __[-3]
+# => :bam
+# irb(main):018> __[-13]
+# => nil
+#
+# * Zero *m*: contains `nil`:
+#
+# irb(main):019> __[0]
+# => nil
+#
+#
+#
+#
+# ### Prompt and Return Formats
+#
+# By default, IRB uses the prompt and return value formats defined in its
+# `:DEFAULT` prompt mode.
+#
+# #### The Default Prompt and Return Format
#
# The default prompt and return values look like this:
#
-# irb(main):001> 1 + 1
-# => 2
-# irb(main):002> 2 + 2
-# => 4
+# irb(main):001> 1 + 1
+# => 2
+# irb(main):002> 2 + 2
+# => 4
#
# The prompt includes:
#
-# - The name of the running program (<tt>irb</tt>);
-# see {IRB Name}[rdoc-ref:IRB@IRB+Name].
-# - The name of the current session (<tt>main</tt>);
-# See {IRB Sessions}[rdoc-ref:IRB@IRB+Sessions].
-# - A 3-digit line number (1-based).
+# * The name of the running program (`irb`); see [IRB
+# Name](rdoc-ref:IRB@IRB+Name).
+# * The name of the current session (`main`); See [IRB
+# Sessions](rdoc-ref:IRB@IRB+Sessions).
+# * A 3-digit line number (1-based).
+#
#
# The default prompt actually defines three formats:
#
-# - One for most situations (as above):
+# * One for most situations (as above):
+#
+# irb(main):003> Dir
+# => Dir
#
-# irb(main):003> Dir
-# => Dir
+# * One for when the typed command is a statement continuation (adds trailing
+# asterisk):
#
-# - One for when the typed command is a statement continuation (adds trailing asterisk):
+# irb(main):004* Dir.
#
-# irb(main):004* Dir.
+# * One for when the typed command is a string continuation (adds trailing
+# single-quote):
#
-# - One for when the typed command is a string continuation (adds trailing single-quote):
+# irb(main):005' Dir.entries('.
#
-# irb(main):005' Dir.entries('.
#
# You can see the prompt change as you type the characters in the following:
#
@@ -570,276 +578,268 @@ require_relative "irb/pager"
# irb(main):003> end
# => ["README.md", "Rakefile"]
#
-# ==== Pre-Defined Prompts
+# #### Pre-Defined Prompts
#
-# \IRB has several pre-defined prompts, stored in hash <tt>IRB.conf[:PROMPT]</tt>:
+# IRB has several pre-defined prompts, stored in hash `IRB.conf[:PROMPT]`:
#
-# irb(main):001> IRB.conf[:PROMPT].keys
-# => [:NULL, :DEFAULT, :CLASSIC, :SIMPLE, :INF_RUBY, :XMP]
+# irb(main):001> IRB.conf[:PROMPT].keys
+# => [:NULL, :DEFAULT, :CLASSIC, :SIMPLE, :INF_RUBY, :XMP]
#
-# To see the full data for these, type <tt>IRB.conf[:PROMPT]</tt>.
+# To see the full data for these, type `IRB.conf[:PROMPT]`.
#
-# Most of these prompt definitions include specifiers that represent
-# values like the \IRB name, session name, and line number;
-# see {Prompt Specifiers}[rdoc-ref:IRB@Prompt+Specifiers].
+# Most of these prompt definitions include specifiers that represent values like
+# the IRB name, session name, and line number; see [Prompt
+# Specifiers](rdoc-ref:IRB@Prompt+Specifiers).
#
# You can change the initial prompt and return format by:
#
-# - Adding to the configuration file: <tt>IRB.conf[:PROMPT] = _mode_</tt>
-# where _mode_ is the symbol name of a prompt mode.
-# - Giving a command-line option:
+# * Adding to the configuration file: `IRB.conf[:PROMPT] = *mode*` where
+# *mode* is the symbol name of a prompt mode.
+# * Giving a command-line option:
+#
+# * `--prompt *mode*`: sets the prompt mode to *mode*. where *mode* is the
+# symbol name of a prompt mode.
+# * `--simple-prompt` or `--sample-book-mode`: sets the prompt mode to
+# `:SIMPLE`.
+# * `--inf-ruby-mode`: sets the prompt mode to `:INF_RUBY` and suppresses
+# both `--multiline` and `--singleline`.
+# * `--noprompt`: suppresses prompting; does not affect echoing.
+#
#
-# - <tt>--prompt _mode_</tt>: sets the prompt mode to _mode_.
-# where _mode_ is the symbol name of a prompt mode.
-# - <tt>--simple-prompt</tt> or <tt>--sample-book-mode</tt>:
-# sets the prompt mode to +:SIMPLE+.
-# - <tt>--inf-ruby-mode</tt>: sets the prompt mode to +:INF_RUBY+
-# and suppresses both <tt>--multiline</tt> and <tt>--singleline</tt>.
-# - <tt>--noprompt</tt>: suppresses prompting; does not affect echoing.
#
# You can retrieve or set the current prompt mode with methods
#
-# <tt>conf.prompt_mode</tt> and <tt>conf.prompt_mode=</tt>.
+# `conf.prompt_mode` and `conf.prompt_mode=`.
#
# If you're interested in prompts and return formats other than the defaults,
# you might experiment by trying some of the others.
#
-# ==== Custom Prompts
+# #### Custom Prompts
+#
+# You can also define custom prompts and return formats, which may be done
+# either in an IRB session or in the configuration file.
#
-# You can also define custom prompts and return formats,
-# which may be done either in an \IRB session or in the configuration file.
+# A prompt in IRB actually defines three prompts, as seen above. For simple
+# custom data, we'll make all three the same:
#
-# A prompt in \IRB actually defines three prompts, as seen above.
-# For simple custom data, we'll make all three the same:
+# irb(main):001* IRB.conf[:PROMPT][:MY_PROMPT] = {
+# irb(main):002* PROMPT_I: ': ',
+# irb(main):003* PROMPT_C: ': ',
+# irb(main):004* PROMPT_S: ': ',
+# irb(main):005* RETURN: '=> '
+# irb(main):006> }
+# => {:PROMPT_I=>": ", :PROMPT_C=>": ", :PROMPT_S=>": ", :RETURN=>"=> "}
#
-# irb(main):001* IRB.conf[:PROMPT][:MY_PROMPT] = {
-# irb(main):002* PROMPT_I: ': ',
-# irb(main):003* PROMPT_C: ': ',
-# irb(main):004* PROMPT_S: ': ',
-# irb(main):005* RETURN: '=> '
-# irb(main):006> }
-# => {:PROMPT_I=>": ", :PROMPT_C=>": ", :PROMPT_S=>": ", :RETURN=>"=> "}
+# If you define the custom prompt in the configuration file, you can also make
+# it the current prompt by adding:
#
-# If you define the custom prompt in the configuration file,
-# you can also make it the current prompt by adding:
+# IRB.conf[:PROMPT_MODE] = :MY_PROMPT
#
-# IRB.conf[:PROMPT_MODE] = :MY_PROMPT
+# Regardless of where it's defined, you can make it the current prompt in a
+# session:
#
-# Regardless of where it's defined, you can make it the current prompt in a session:
+# conf.prompt_mode = :MY_PROMPT
#
-# conf.prompt_mode = :MY_PROMPT
+# You can view or modify the current prompt data with various configuration
+# methods:
#
-# You can view or modify the current prompt data with various configuration methods:
+# * `conf.prompt_mode`, `conf.prompt_mode=`.
+# * `conf.prompt_c`, `conf.c=`.
+# * `conf.prompt_i`, `conf.i=`.
+# * `conf.prompt_s`, `conf.s=`.
+# * `conf.return_format`, `return_format=`.
#
-# - <tt>conf.prompt_mode</tt>, <tt>conf.prompt_mode=</tt>.
-# - <tt>conf.prompt_c</tt>, <tt>conf.c=</tt>.
-# - <tt>conf.prompt_i</tt>, <tt>conf.i=</tt>.
-# - <tt>conf.prompt_s</tt>, <tt>conf.s=</tt>.
-# - <tt>conf.return_format</tt>, <tt>return_format=</tt>.
#
-# ==== Prompt Specifiers
+# #### Prompt Specifiers
#
-# A prompt's definition can include specifiers for which certain values are substituted:
+# A prompt's definition can include specifiers for which certain values are
+# substituted:
#
-# - <tt>%N</tt>: the name of the running program.
-# - <tt>%m</tt>: the value of <tt>self.to_s</tt>.
-# - <tt>%M</tt>: the value of <tt>self.inspect</tt>.
-# - <tt>%l</tt>: an indication of the type of string;
-# one of <tt>"</tt>, <tt>'</tt>, <tt>/</tt>, <tt>]</tt>.
-# - <tt><i>NN</i>i</tt>: Indentation level.
-# - <tt><i>NN</i>n</tt>: Line number.
-# - <tt>%%</tt>: Literal <tt>%</tt>.
+# * `%N`: the name of the running program.
+# * `%m`: the value of `self.to_s`.
+# * `%M`: the value of `self.inspect`.
+# * `%l`: an indication of the type of string; one of `"`, `'`, `/`, `]`.
+# * `%NNi`: Indentation level. NN is a 2-digit number that specifies the number
+# of digits of the indentation level (03 will result in 001).
+# * `%NNn`: Line number. NN is a 2-digit number that specifies the number
+# of digits of the line number (03 will result in 001).
+# * `%%`: Literal `%`.
#
-# === Verbosity
#
-# By default, \IRB verbosity is disabled, which means that output is smaller
+# ### Verbosity
+#
+# By default, IRB verbosity is disabled, which means that output is smaller
# rather than larger.
#
# You can enable verbosity by:
#
-# - Adding to the configuration file: <tt>IRB.conf[:VERBOSE] = true</tt>
-# (the default is +nil+).
-# - Giving command-line options <tt>--verbose</tt>
-# (the default is <tt>--noverbose</tt>).
+# * Adding to the configuration file: `IRB.conf[:VERBOSE] = true` (the default
+# is `nil`).
+# * Giving command-line options `--verbose` (the default is `--noverbose`).
+#
#
# During a session, you can retrieve or set verbosity with methods
-# <tt>conf.verbose</tt> and <tt>conf.verbose=</tt>.
+# `conf.verbose` and `conf.verbose=`.
+#
+# ### Help
#
-# === Help
+# Command-line option `--version` causes IRB to print its help text and exit.
#
-# Command-line option <tt>--version</tt> causes \IRB to print its help text
-# and exit.
+# ### Version
#
-# === Version
+# Command-line option `--version` causes IRB to print its version text and exit.
#
-# Command-line option <tt>--version</tt> causes \IRB to print its version text
-# and exit.
+# ## Input and Output
#
-# == Input and Output
+# ### Color Highlighting
#
-# === \Color Highlighting
+# By default, IRB color highlighting is enabled, and is used for both:
#
-# By default, \IRB color highlighting is enabled, and is used for both:
+# * Input: As you type, IRB reads the typed characters and highlights elements
+# that it recognizes; it also highlights errors such as mismatched
+# parentheses.
+# * Output: IRB highlights syntactical elements.
#
-# - Input: As you type, \IRB reads the typed characters and highlights
-# elements that it recognizes;
-# it also highlights errors such as mismatched parentheses.
-# - Output: \IRB highlights syntactical elements.
#
# You can disable color highlighting by:
#
-# - Adding to the configuration file: <tt>IRB.conf[:USE_COLORIZE] = false</tt>
-# (the default value is +true+).
-# - Giving command-line option <tt>--nocolorize</tt>
+# * Adding to the configuration file: `IRB.conf[:USE_COLORIZE] = false` (the
+# default value is `true`).
+# * Giving command-line option `--nocolorize`
#
-# == Debugging
#
-# Command-line option <tt>-d</tt> sets variables <tt>$VERBOSE</tt>
-# and <tt>$DEBUG</tt> to +true+;
-# these have no effect on \IRB output.
+# ## Debugging
#
-# === Warnings
+# Command-line option `-d` sets variables `$VERBOSE` and `$DEBUG` to `true`;
+# these have no effect on IRB output.
#
-# Command-line option <tt>-w</tt> suppresses warnings.
+# ### Warnings
#
-# Command-line option <tt>-W[_level_]<tt>
-# sets warning level; 0=silence, 1=medium, 2=verbose.
+# Command-line option `-w` suppresses warnings.
#
-# :stopdoc:
-# === Performance Measurement
+# Command-line option `-W[*level*]` sets warning level;
#
-# IRB.conf[:MEASURE] IRB.conf[:MEASURE_CALLBACKS] IRB.conf[:MEASURE_PROC]
-# :startdoc:
+# * 0=silence
+# * 1=medium
+# * 2=verbose
#
-# == Other Features
+# ## Other Features
#
-# === Load Modules
+# ### Load Modules
#
# You can specify the names of modules that are to be required at startup.
#
-# \Array <tt>conf.load_modules</tt> determines the modules (if any)
-# that are to be required during session startup.
-# The array is used only during session startup,
-# so the initial value is the only one that counts.
+# Array `conf.load_modules` determines the modules (if any) that are to be
+# required during session startup. The array is used only during session
+# startup, so the initial value is the only one that counts.
#
-# The default initial value is <tt>[]</tt> (load no modules):
+# The default initial value is `[]` (load no modules):
#
-# irb(main):001> conf.load_modules
-# => []
+# irb(main):001> conf.load_modules
+# => []
#
# You can set the default initial value via:
#
-# - Command-line option <tt>-r</tt>
+# * Command-line option `-r`
+#
+# $ irb -r csv -r json
+# irb(main):001> conf.load_modules
+# => ["csv", "json"]
#
-# $ irb -r csv -r json
-# irb(main):001> conf.load_modules
-# => ["csv", "json"]
+# * Hash entry `IRB.conf[:LOAD_MODULES] = *array*`:
#
-# - \Hash entry <tt>IRB.conf[:LOAD_MODULES] = _array_</tt>:
+# IRB.conf[:LOAD_MODULES] = %w[csv, json]
#
-# IRB.conf[:LOAD_MODULES] = %w[csv, json]
#
# Note that the configuration file entry overrides the command-line options.
#
-# === RI Documentation Directories
+# ### RI Documentation Directories
#
-# You can specify the paths to RI documentation directories
-# that are to be loaded (in addition to the default directories) at startup;
-# see details about RI by typing <tt>ri --help</tt>.
+# You can specify the paths to RI documentation directories that are to be
+# loaded (in addition to the default directories) at startup; see details about
+# RI by typing `ri --help`.
#
-# \Array <tt>conf.extra_doc_dirs</tt> determines the directories (if any)
-# that are to be loaded during session startup.
-# The array is used only during session startup,
+# Array `conf.extra_doc_dirs` determines the directories (if any) that are to be
+# loaded during session startup. The array is used only during session startup,
# so the initial value is the only one that counts.
#
-# The default initial value is <tt>[]</tt> (load no extra documentation):
-#
-# irb(main):001> conf.extra_doc_dirs
-# => []
+# The default initial value is `[]` (load no extra documentation):
#
-# You can set the default initial value via:
-#
-# - Command-line option <tt>--extra_doc_dir</tt>
-#
-# $ irb --extra-doc-dir your_doc_dir --extra-doc-dir my_doc_dir
# irb(main):001> conf.extra_doc_dirs
-# => ["your_doc_dir", "my_doc_dir"]
+# => []
#
-# - \Hash entry <tt>IRB.conf[:EXTRA_DOC_DIRS] = _array_</tt>:
+# You can set the default initial value via:
#
-# IRB.conf[:EXTRA_DOC_DIRS] = %w[your_doc_dir my_doc_dir]
+# * Command-line option `--extra_doc_dir`
#
-# Note that the configuration file entry overrides the command-line options.
+# $ irb --extra-doc-dir your_doc_dir --extra-doc-dir my_doc_dir
+# irb(main):001> conf.extra_doc_dirs
+# => ["your_doc_dir", "my_doc_dir"]
#
-# :stopdoc:
-# === \Context Mode
+# * Hash entry `IRB.conf[:EXTRA_DOC_DIRS] = *array*`:
#
-# IRB.conf[:CONTEXT_MODE]
-# :startdoc:
+# IRB.conf[:EXTRA_DOC_DIRS] = %w[your_doc_dir my_doc_dir]
#
-# === \IRB Name
#
-# You can specify a name for \IRB.
+# Note that the configuration file entry overrides the command-line options.
#
-# The default initial value is <tt>'irb'</tt>:
+# ### IRB Name
#
-# irb(main):001> conf.irb_name
-# => "irb"
+# You can specify a name for IRB.
#
-# You can set the default initial value
-# via hash entry <tt>IRB.conf[:IRB_NAME] = _string_</tt>:
+# The default initial value is `'irb'`:
#
-# IRB.conf[:IRB_NAME] = 'foo'
+# irb(main):001> conf.irb_name
+# => "irb"
#
-# === Application Name
+# You can set the default initial value via hash entry `IRB.conf[:IRB_NAME] =
+# *string*`:
#
-# You can specify an application name for the \IRB session.
+# IRB.conf[:IRB_NAME] = 'foo'
#
-# The default initial value is <tt>'irb'</tt>:
+# ### Application Name
#
-# irb(main):001> conf.ap_name
-# => "irb"
+# You can specify an application name for the IRB session.
#
-# You can set the default initial value
-# via hash entry <tt>IRB.conf[:AP_NAME] = _string_</tt>:
+# The default initial value is `'irb'`:
#
-# IRB.conf[:AP_NAME] = 'my_ap_name'
+# irb(main):001> conf.ap_name
+# => "irb"
#
-# === Configuration Monitor
+# You can set the default initial value via hash entry `IRB.conf[:AP_NAME] =
+# *string*`:
#
-# You can monitor changes to the configuration by assigning a proc
-# to <tt>IRB.conf[:IRB_RC]</tt> in the configuration file:
+# IRB.conf[:AP_NAME] = 'my_ap_name'
#
-# IRB.conf[:IRB_RC] = proc {|conf| puts conf.class }
+# ### Configuration Monitor
#
-# Each time the configuration is changed,
-# that proc is called with argument +conf+:
+# You can monitor changes to the configuration by assigning a proc to
+# `IRB.conf[:IRB_RC]` in the configuration file:
#
-# :stopdoc:
-# === \Locale
+# IRB.conf[:IRB_RC] = proc {|conf| puts conf.class }
#
-# IRB.conf[:LC_MESSAGES]
-# :startdoc:
+# Each time the configuration is changed, that proc is called with argument
+# `conf`:
#
-# === Encodings
+# ### Encodings
#
-# Command-line option <tt>-E _ex_[:_in_]</tt>
-# sets initial external (ex) and internal (in) encodings.
+# Command-line option `-E *ex*[:*in*]` sets initial external (ex) and internal
+# (in) encodings.
#
-# Command-line option <tt>-U</tt> sets both to UTF-8.
+# Command-line option `-U` sets both to UTF-8.
#
-# === Commands
+# ### Commands
#
-# Please use the `show_cmds` command to see the list of available commands.
+# Please use the `help` command to see the list of available commands.
#
-# === IRB Sessions
+# ### IRB Sessions
#
# IRB has a special feature, that allows you to manage many sessions at once.
#
# You can create new sessions with Irb.irb, and get a list of current sessions
-# with the +jobs+ command in the prompt.
+# with the `jobs` command in the prompt.
#
-# ==== Configuration
+# #### Configuration
#
# The command line options, or IRB.conf, specify the default behavior of
# Irb.irb.
@@ -847,32 +847,33 @@ require_relative "irb/pager"
# On the other hand, each conf in IRB@Command-Line+Options is used to
# individually configure IRB.irb.
#
-# If a proc is set for <code>IRB.conf[:IRB_RC]</code>, its will be invoked after execution
+# If a proc is set for `IRB.conf[:IRB_RC]`, its will be invoked after execution
# of that proc with the context of the current session as its argument. Each
# session can be configured using this mechanism.
#
-# ==== Session variables
+# #### Session variables
#
# There are a few variables in every Irb session that can come in handy:
#
-# <code>_</code>::
-# The value command executed, as a local variable
-# <code>__</code>::
-# The history of evaluated commands. Available only if
-# <code>IRB.conf[:EVAL_HISTORY]</code> is not +nil+ (which is the default).
-# See also IRB::Context#eval_history= and IRB::History.
-# <code>__[line_no]</code>::
-# Returns the evaluation value at the given line number, +line_no+.
-# If +line_no+ is a negative, the return value +line_no+ many lines before
-# the most recent return value.
+# `_`
+# : The value command executed, as a local variable
+# `__`
+# : The history of evaluated commands. Available only if
+# `IRB.conf[:EVAL_HISTORY]` is not `nil` (which is the default). See also
+# IRB::Context#eval_history= and IRB::History.
+# `__[line_no]`
+# : Returns the evaluation value at the given line number, `line_no`. If
+# `line_no` is a negative, the return value `line_no` many lines before the
+# most recent return value.
#
-# == Restrictions
#
-# Ruby code typed into \IRB behaves the same as Ruby code in a file, except that:
+# ## Restrictions
#
-# - Because \IRB evaluates input immediately after it is syntactically complete,
-# some results may be slightly different.
-# - Forking may not be well behaved.
+# Ruby code typed into IRB behaves the same as Ruby code in a file, except that:
+#
+# * Because IRB evaluates input immediately after it is syntactically
+# complete, some results may be slightly different.
+# * Forking may not be well behaved.
#
module IRB
@@ -881,14 +882,14 @@ module IRB
# The current IRB::Context of the session, see IRB.conf
#
- # irb
- # irb(main):001:0> IRB.CurrentContext.irb_name = "foo"
- # foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo"
- def IRB.CurrentContext
+ # irb
+ # irb(main):001:0> IRB.CurrentContext.irb_name = "foo"
+ # foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo"
+ def IRB.CurrentContext # :nodoc:
IRB.conf[:MAIN_CONTEXT]
end
- # Initializes IRB and creates a new Irb.irb object at the +TOPLEVEL_BINDING+
+ # Initializes IRB and creates a new Irb.irb object at the `TOPLEVEL_BINDING`
def IRB.start(ap_path = nil)
STDOUT.sync = true
$0 = File::basename(ap_path, ".rb") if ap_path
@@ -904,42 +905,46 @@ module IRB
end
# Quits irb
- def IRB.irb_exit(irb, ret)
- throw :IRB_EXIT, ret
+ def IRB.irb_exit(*) # :nodoc:
+ throw :IRB_EXIT, false
end
# Aborts then interrupts irb.
#
- # Will raise an Abort exception, or the given +exception+.
- def IRB.irb_abort(irb, exception = Abort)
+ # Will raise an Abort exception, or the given `exception`.
+ def IRB.irb_abort(irb, exception = Abort) # :nodoc:
irb.context.thread.raise exception, "abort then interrupt!"
end
class Irb
# Note: instance and index assignment expressions could also be written like:
- # "foo.bar=(1)" and "foo.[]=(1, bar)", when expressed that way, the former
- # be parsed as :assign and echo will be suppressed, but the latter is
- # parsed as a :method_add_arg and the output won't be suppressed
+ # "foo.bar=(1)" and "foo.[]=(1, bar)", when expressed that way, the former be
+ # parsed as :assign and echo will be suppressed, but the latter is parsed as a
+ # :method_add_arg and the output won't be suppressed
PROMPT_MAIN_TRUNCATE_LENGTH = 32
- PROMPT_MAIN_TRUNCATE_OMISSION = '...'.freeze
- CONTROL_CHARACTERS_PATTERN = "\x00-\x1F".freeze
+ PROMPT_MAIN_TRUNCATE_OMISSION = '...'
+ CONTROL_CHARACTERS_PATTERN = "\x00-\x1F"
# Returns the current context of this irb session
attr_reader :context
# The lexer used by this irb session
attr_accessor :scanner
+ attr_reader :from_binding
+
# Creates a new irb session
- def initialize(workspace = nil, input_method = nil)
+ def initialize(workspace = nil, input_method = nil, from_binding: false)
+ @from_binding = from_binding
@context = Context.new(self, workspace, input_method)
- @context.workspace.load_commands_to_main
+ @context.workspace.load_helper_methods_to_main
@signal_status = :IN_IRB
@scanner = RubyLex.new
@line_no = 1
end
- # A hook point for `debug` command's breakpoint after :IRB_EXIT as well as its clean-up
+ # A hook point for `debug` command's breakpoint after :IRB_EXIT as well as its
+ # clean-up
def debug_break
# it means the debug integration has been activated
if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:capture_frames_without_irb)
@@ -952,29 +957,37 @@ module IRB
def debug_readline(binding)
workspace = IRB::WorkSpace.new(binding)
- context.workspace = workspace
- context.workspace.load_commands_to_main
+ context.replace_workspace(workspace)
+ context.workspace.load_helper_methods_to_main
@line_no += 1
# When users run:
- # 1. Debugging commands, like `step 2`
- # 2. Any input that's not irb-command, like `foo = 123`
+ # 1. Debugging commands, like `step 2`
+ # 2. Any input that's not irb-command, like `foo = 123`
+ #
#
- # Irb#eval_input will simply return the input, and we need to pass it to the debugger.
- input = if IRB.conf[:SAVE_HISTORY] && context.io.support_history_saving?
- # Previous IRB session's history has been saved when `Irb#run` is exited
- # We need to make sure the saved history is not saved again by reseting the counter
- context.io.reset_history_counter
+ # Irb#eval_input will simply return the input, and we need to pass it to the
+ # debugger.
+ input = nil
+ forced_exit = catch(:IRB_EXIT) do
+ if IRB.conf[:SAVE_HISTORY] && context.io.support_history_saving?
+ # Previous IRB session's history has been saved when `Irb#run` is exited We need
+ # to make sure the saved history is not saved again by resetting the counter
+ context.io.reset_history_counter
- begin
- eval_input
- ensure
- context.io.save_history
+ begin
+ input = eval_input
+ ensure
+ context.io.save_history
+ end
+ else
+ input = eval_input
end
- else
- eval_input
+ false
end
+ Kernel.exit if forced_exit
+
if input&.include?("\n")
@line_no += input.count("\n") - 1
end
@@ -985,6 +998,7 @@ module IRB
def run(conf = IRB.conf)
in_nested_session = !!conf[:MAIN_CONTEXT]
conf[:IRB_RC].call(context) if conf[:IRB_RC]
+ prev_context = conf[:MAIN_CONTEXT]
conf[:MAIN_CONTEXT] = context
save_history = !in_nested_session && conf[:SAVE_HISTORY] && context.io.support_history_saving?
@@ -998,13 +1012,24 @@ module IRB
end
begin
- catch(:IRB_EXIT) do
+ if defined?(RubyVM.keep_script_lines)
+ keep_script_lines_backup = RubyVM.keep_script_lines
+ RubyVM.keep_script_lines = true
+ end
+
+ forced_exit = catch(:IRB_EXIT) do
eval_input
end
ensure
+ # Do not restore to nil. It will cause IRB crash when used with threads.
+ IRB.conf[:MAIN_CONTEXT] = prev_context if prev_context
+
+ RubyVM.keep_script_lines = keep_script_lines_backup if defined?(RubyVM.keep_script_lines)
trap("SIGINT", prev_trap)
conf[:AT_EXIT].each{|hook| hook.call}
+
context.io.save_history if save_history
+ Kernel.exit if forced_exit
end
end
@@ -1015,12 +1040,13 @@ module IRB
each_top_level_statement do |statement, line_no|
signal_status(:IN_EVAL) do
begin
- # If the integration with debugger is activated, we return certain input if it should be dealt with by debugger
+ # If the integration with debugger is activated, we return certain input if it
+ # should be dealt with by debugger
if @context.with_debugger && statement.should_be_handled_by_debugger?
return statement.code
end
- @context.evaluate(statement.evaluable_code, line_no)
+ @context.evaluate(statement, line_no)
if @context.echo? && !statement.suppresses_echo?
if statement.is_assignment?
@@ -1067,7 +1093,7 @@ module IRB
return read_input(prompt) if @context.io.respond_to?(:check_termination)
# nomultiline
- code = ''
+ code = +''
line_offset = 0
loop do
line = read_input(prompt)
@@ -1076,9 +1102,7 @@ module IRB
end
code << line
-
- # Accept any single-line input for symbol aliases or commands that transform args
- return code if single_line_command?(code)
+ return code if command?(code)
tokens, opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
return code if terminated
@@ -1093,34 +1117,48 @@ module IRB
loop do
code = readmultiline
break unless code
-
- if code != "\n"
- yield build_statement(code), @line_no
- end
+ yield build_statement(code), @line_no
@line_no += code.count("\n")
rescue RubyLex::TerminateLineInput
end
end
def build_statement(code)
+ if code.match?(/\A\n*\z/)
+ return Statement::EmptyInput.new
+ end
+
code.force_encoding(@context.io.encoding)
- command_or_alias, arg = code.split(/\s/, 2)
- # Transform a non-identifier alias (@, $) or keywords (next, break)
- command_name = @context.command_aliases[command_or_alias.to_sym]
- command = command_name || command_or_alias
- command_class = ExtendCommandBundle.load_command(command)
-
- if command_class
- Statement::Command.new(code, command, arg, command_class)
+ if (command, arg = parse_command(code))
+ command_class = Command.load_command(command)
+ Statement::Command.new(code, command_class, arg)
else
is_assignment_expression = @scanner.assignment_expression?(code, local_variables: @context.local_variables)
Statement::Expression.new(code, is_assignment_expression)
end
end
- def single_line_command?(code)
- command = code.split(/\s/, 2).first
- @context.symbol_alias?(command) || @context.transform_args?(command)
+ def parse_command(code)
+ command_name, arg = code.strip.split(/\s+/, 2)
+ return unless code.lines.size == 1 && command_name
+
+ arg ||= ''
+ command = command_name.to_sym
+ # Command aliases are always command. example: $, @
+ if (alias_name = @context.command_aliases[command])
+ return [alias_name, arg]
+ end
+
+ # Check visibility
+ public_method = !!Kernel.instance_method(:public_method).bind_call(@context.main, command) rescue false
+ private_method = !public_method && !!Kernel.instance_method(:method).bind_call(@context.main, command) rescue false
+ if Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
+ [command, arg]
+ end
+ end
+
+ def command?(code)
+ !!parse_command(code)
end
def configure_io
@@ -1138,8 +1176,7 @@ module IRB
false
end
else
- # Accept any single-line input for symbol aliases or commands that transform args
- next true if single_line_command?(code)
+ next true if command?(code)
_tokens, _opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
terminated
@@ -1148,13 +1185,13 @@ module IRB
end
if @context.io.respond_to?(:dynamic_prompt)
@context.io.dynamic_prompt do |lines|
- lines << '' if lines.empty?
tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, local_variables: @context.local_variables)
line_results = IRB::NestingParser.parse_by_line(tokens)
tokens_until_line = []
line_results.map.with_index do |(line_tokens, _prev_opens, next_opens, _min_depth), line_num_offset|
line_tokens.each do |token, _s|
- # Avoid appending duplicated token. Tokens that include "\n" like multiline tstring_content can exist in multiple lines.
+ # Avoid appending duplicated token. Tokens that include "n" like multiline
+ # tstring_content can exist in multiple lines.
tokens_until_line << token if token != tokens_until_line.last
end
continue = @scanner.should_continue?(tokens_until_line)
@@ -1211,20 +1248,33 @@ module IRB
irb_bug = true
else
irb_bug = false
+ # To support backtrace filtering while utilizing Exception#full_message, we need to clone
+ # the exception to avoid modifying the original exception's backtrace.
+ exc = exc.clone
+ filtered_backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact
+ backtrace_filter = IRB.conf[:BACKTRACE_FILTER]
+
+ if backtrace_filter
+ if backtrace_filter.respond_to?(:call)
+ filtered_backtrace = backtrace_filter.call(filtered_backtrace)
+ else
+ warn "IRB.conf[:BACKTRACE_FILTER] #{backtrace_filter} should respond to `call` method"
+ end
+ end
+
+ exc.set_backtrace(filtered_backtrace)
end
- if RUBY_VERSION < '3.0.0'
- if STDOUT.tty?
- message = exc.full_message(order: :bottom)
- order = :bottom
- else
- message = exc.full_message(order: :top)
- order = :top
+ highlight = Color.colorable?
+
+ order =
+ if RUBY_VERSION < '3.0.0'
+ STDOUT.tty? ? :bottom : :top
+ else # '3.0.0' <= RUBY_VERSION
+ :top
end
- else # '3.0.0' <= RUBY_VERSION
- message = exc.full_message(order: :top)
- order = :top
- end
+
+ message = exc.full_message(order: order, highlight: highlight)
message = convert_invalid_byte_sequence(message, exc.message.encoding)
message = encode_with_invalid_byte_sequence(message, IRB.conf[:LC_MESSAGES].encoding) unless message.encoding.to_s.casecmp?(IRB.conf[:LC_MESSAGES].encoding.to_s)
message = message.gsub(/((?:^\t.+$\n)+)/) { |m|
@@ -1235,7 +1285,6 @@ module IRB
lines = m.split("\n").reverse
end
unless irb_bug
- lines = lines.map { |l| @context.workspace.filter_backtrace(l) }.compact
if lines.size > @context.back_trace_limit
omit = lines.size - @context.back_trace_limit
lines = lines[0..(@context.back_trace_limit - 1)]
@@ -1246,7 +1295,7 @@ module IRB
lines.map{ |l| l + "\n" }.join
}
# The "<top (required)>" in "(irb)" may be the top level of IRB so imitate the main object.
- message = message.gsub(/\(irb\):(?<num>\d+):in `<(?<frame>top \(required\))>'/) { "(irb):#{$~[:num]}:in `<main>'" }
+ message = message.gsub(/\(irb\):(?<num>\d+):in (?<open_quote>[`'])<(?<frame>top \(required\))>'/) { "(irb):#{$~[:num]}:in #{$~[:open_quote]}<main>'" }
puts message
puts 'Maybe IRB bug!' if irb_bug
rescue Exception => handler_exc
@@ -1258,11 +1307,10 @@ module IRB
end
end
- # Evaluates the given block using the given +path+ as the Context#irb_path
- # and +name+ as the Context#irb_name.
+ # Evaluates the given block using the given `path` as the Context#irb_path and
+ # `name` as the Context#irb_name.
#
- # Used by the irb command +source+, see IRB@IRB+Sessions for more
- # information.
+ # Used by the irb command `source`, see IRB@IRB+Sessions for more information.
def suspend_name(path = nil, name = nil)
@context.irb_path, back_path = path, @context.irb_path if path
@context.irb_name, back_name = name, @context.irb_name if name
@@ -1274,25 +1322,22 @@ module IRB
end
end
- # Evaluates the given block using the given +workspace+ as the
+ # Evaluates the given block using the given `workspace` as the
# Context#workspace.
#
- # Used by the irb command +irb_load+, see IRB@IRB+Sessions for more
- # information.
+ # Used by the irb command `irb_load`, see IRB@IRB+Sessions for more information.
def suspend_workspace(workspace)
- @context.workspace, back_workspace = workspace, @context.workspace
- begin
- yield back_workspace
- ensure
- @context.workspace = back_workspace
- end
+ current_workspace = @context.workspace
+ @context.replace_workspace(workspace)
+ yield
+ ensure
+ @context.replace_workspace current_workspace
end
- # Evaluates the given block using the given +input_method+ as the
- # Context#io.
+ # Evaluates the given block using the given `input_method` as the Context#io.
#
- # Used by the irb commands +source+ and +irb_load+, see IRB@IRB+Sessions
- # for more information.
+ # Used by the irb commands `source` and `irb_load`, see IRB@IRB+Sessions for
+ # more information.
def suspend_input_method(input_method)
back_io = @context.io
@context.instance_eval{@io = input_method}
@@ -1325,7 +1370,7 @@ module IRB
end
end
- # Evaluates the given block using the given +status+.
+ # Evaluates the given block using the given `status`.
def signal_status(status)
return yield if @signal_status == :IN_LOAD
@@ -1375,8 +1420,8 @@ module IRB
Pager.page_content(format(@context.return_format, str), retain_content: true)
end
- # Outputs the local variables to this current session, including
- # #signal_status and #context, using IRB::Locale.
+ # Outputs the local variables to this current session, including #signal_status
+ # and #context, using IRB::Locale.
def inspect
ary = []
for iv in instance_variables
@@ -1434,7 +1479,7 @@ module IRB
end
def format_prompt(format, ltype, indent, line_no) # :nodoc:
- format.gsub(/%([0-9]+)?([a-zA-Z])/) do
+ format.gsub(/%([0-9]+)?([a-zA-Z%])/) do
case $2
when "N"
@context.irb_name
@@ -1467,7 +1512,7 @@ module IRB
line_no.to_s
end
when "%"
- "%"
+ "%" unless $1
end
end
end
@@ -1475,12 +1520,11 @@ module IRB
end
class Binding
- # Opens an IRB session where +binding.irb+ is called which allows for
- # interactive debugging. You can call any methods or variables available in
- # the current scope, and mutate state if you need to.
- #
+ # Opens an IRB session where `binding.irb` is called which allows for
+ # interactive debugging. You can call any methods or variables available in the
+ # current scope, and mutate state if you need to.
#
- # Given a Ruby file called +potato.rb+ containing the following code:
+ # Given a Ruby file called `potato.rb` containing the following code:
#
# class Potato
# def initialize
@@ -1492,8 +1536,8 @@ class Binding
#
# Potato.new
#
- # Running <code>ruby potato.rb</code> will open an IRB session where
- # +binding.irb+ is called, and you will see the following:
+ # Running `ruby potato.rb` will open an IRB session where `binding.irb` is
+ # called, and you will see the following:
#
# $ ruby potato.rb
#
@@ -1523,8 +1567,8 @@ class Binding
# irb(#<Potato:0x00007feea1916670>):004:0> @cooked = true
# => true
#
- # You can exit the IRB session with the +exit+ command. Note that exiting will
- # resume execution where +binding.irb+ had paused it, as you can see from the
+ # You can exit the IRB session with the `exit` command. Note that exiting will
+ # resume execution where `binding.irb` had paused it, as you can see from the
# output printed to standard output in this example:
#
# irb(#<Potato:0x00007feea1916670>):005:0> exit
@@ -1533,7 +1577,7 @@ class Binding
# See IRB for more information.
def irb(show_code: true)
# Setup IRB with the current file's path and no command line arguments
- IRB.setup(source_location[0], argv: [])
+ IRB.setup(source_location[0], argv: []) unless IRB.initialized?
# Create a new workspace using the current binding
workspace = IRB::WorkSpace.new(self)
# Print the code around the binding if show_code is true
@@ -1545,16 +1589,17 @@ class Binding
if debugger_irb
# If we're already in a debugger session, set the workspace and irb_path for the original IRB instance
- debugger_irb.context.workspace = workspace
+ debugger_irb.context.replace_workspace(workspace)
debugger_irb.context.irb_path = irb_path
- # If we've started a debugger session and hit another binding.irb, we don't want to start an IRB session
- # instead, we want to resume the irb:rdbg session.
+ # If we've started a debugger session and hit another binding.irb, we don't want
+ # to start an IRB session instead, we want to resume the irb:rdbg session.
IRB::Debug.setup(debugger_irb)
IRB::Debug.insert_debug_break
debugger_irb.debug_break
else
- # If we're not in a debugger session, create a new IRB instance with the current workspace
- binding_irb = IRB::Irb.new(workspace)
+ # If we're not in a debugger session, create a new IRB instance with the current
+ # workspace
+ binding_irb = IRB::Irb.new(workspace, from_binding: true)
binding_irb.context.irb_path = irb_path
binding_irb.run(IRB.conf)
binding_irb.debug_break
diff --git a/lib/irb/cmd/backtrace.rb b/lib/irb/cmd/backtrace.rb
deleted file mode 100644
index f632894618..0000000000
--- a/lib/irb/cmd/backtrace.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Backtrace < DebugCommand
- def self.transform_args(args)
- args&.dump
- end
-
- def execute(*args)
- super(pre_cmds: ["backtrace", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/break.rb b/lib/irb/cmd/break.rb
deleted file mode 100644
index df259a90ca..0000000000
--- a/lib/irb/cmd/break.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Break < DebugCommand
- def self.transform_args(args)
- args&.dump
- end
-
- def execute(args = nil)
- super(pre_cmds: "break #{args}")
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/catch.rb b/lib/irb/cmd/catch.rb
deleted file mode 100644
index 40b62c7533..0000000000
--- a/lib/irb/cmd/catch.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Catch < DebugCommand
- def self.transform_args(args)
- args&.dump
- end
-
- def execute(*args)
- super(pre_cmds: ["catch", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/chws.rb b/lib/irb/cmd/chws.rb
deleted file mode 100644
index 31045f9bbb..0000000000
--- a/lib/irb/cmd/chws.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: false
-#
-# change-ws.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-require_relative "nop"
-require_relative "../ext/change-ws"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
-
- class CurrentWorkingWorkspace < Nop
- category "Workspace"
- description "Show the current workspace."
-
- def execute(*obj)
- irb_context.main
- end
- end
-
- class ChangeWorkspace < Nop
- category "Workspace"
- description "Change the current workspace to an object."
-
- def execute(*obj)
- irb_context.change_workspace(*obj)
- irb_context.main
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/edit.rb b/lib/irb/cmd/edit.rb
deleted file mode 100644
index 69606beea0..0000000000
--- a/lib/irb/cmd/edit.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-require 'shellwords'
-require_relative "nop"
-require_relative "../source_finder"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Edit < Nop
- category "Misc"
- description 'Open a file with the editor command defined with `ENV["VISUAL"]` or `ENV["EDITOR"]`.'
-
- class << self
- def transform_args(args)
- # Return a string literal as is for backward compatibility
- if args.nil? || args.empty? || string_literal?(args)
- args
- else # Otherwise, consider the input as a String for convenience
- args.strip.dump
- end
- end
- end
-
- def execute(*args)
- path = args.first
-
- if path.nil? && (irb_path = @irb_context.irb_path)
- path = irb_path
- end
-
- if !File.exist?(path)
- source =
- begin
- SourceFinder.new(@irb_context).find_source(path)
- rescue NameError
- # if user enters a path that doesn't exist, it'll cause NameError when passed here because find_source would try to evaluate it as well
- # in this case, we should just ignore the error
- end
-
- if source
- path = source.file
- else
- puts "Can not find file: #{path}"
- return
- end
- end
-
- if editor = (ENV['VISUAL'] || ENV['EDITOR'])
- puts "command: '#{editor}'"
- puts " path: #{path}"
- system(*Shellwords.split(editor), path)
- else
- puts "Can not find editor setting: ENV['VISUAL'] or ENV['EDITOR']"
- end
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/help.rb b/lib/irb/cmd/help.rb
deleted file mode 100644
index 64b885c383..0000000000
--- a/lib/irb/cmd/help.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "show_doc"
-
-module IRB
- module ExtendCommand
- class Help < ShowDoc
- category "Context"
- description "[DEPRECATED] Enter the mode to look up RI documents."
-
- DEPRECATION_MESSAGE = <<~MSG
- [Deprecation] The `help` command will be repurposed to display command help in the future.
- For RI document lookup, please use the `show_doc` command instead.
- For command help, please use `show_cmds` for now.
- MSG
-
- def execute(*names)
- warn DEPRECATION_MESSAGE
- super
- end
- end
- end
-end
diff --git a/lib/irb/cmd/info.rb b/lib/irb/cmd/info.rb
deleted file mode 100644
index 2c0a32b34f..0000000000
--- a/lib/irb/cmd/info.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Info < DebugCommand
- def self.transform_args(args)
- args&.dump
- end
-
- def execute(*args)
- super(pre_cmds: ["info", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb
index 7fb197c51f..9d2e3c4d47 100644
--- a/lib/irb/cmd/nop.rb
+++ b/lib/irb/cmd/nop.rb
@@ -1,53 +1,4 @@
-# frozen_string_literal: false
-#
-# nop.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
+# frozen_string_literal: true
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class CommandArgumentError < StandardError; end
-
- class Nop
- class << self
- def category(category = nil)
- @category = category if category
- @category
- end
-
- def description(description = nil)
- @description = description if description
- @description
- end
-
- private
-
- def string_literal?(args)
- sexp = Ripper.sexp(args)
- sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
- end
- end
-
- def self.execute(irb_context, *opts, **kwargs, &block)
- command = new(irb_context)
- command.execute(*opts, **kwargs, &block)
- rescue CommandArgumentError => e
- puts e.message
- end
-
- def initialize(irb_context)
- @irb_context = irb_context
- end
-
- attr_reader :irb_context
-
- def execute(*opts)
- #nop
- end
- end
- end
-
- # :startdoc:
-end
+# This file is just a placeholder for backward-compatibility.
+# Please require 'irb' and inherit your command from `IRB::Command::Base` instead.
diff --git a/lib/irb/cmd/pushws.rb b/lib/irb/cmd/pushws.rb
deleted file mode 100644
index 59996ceb0c..0000000000
--- a/lib/irb/cmd/pushws.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: false
-#
-# change-ws.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-require_relative "nop"
-require_relative "../ext/workspaces"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Workspaces < Nop
- category "Workspace"
- description "Show workspaces."
-
- def execute(*obj)
- irb_context.workspaces.collect{|ws| ws.main}
- end
- end
-
- class PushWorkspace < Workspaces
- category "Workspace"
- description "Push an object to the workspace stack."
-
- def execute(*obj)
- irb_context.push_workspace(*obj)
- super
- end
- end
-
- class PopWorkspace < Workspaces
- category "Workspace"
- description "Pop a workspace from the workspace stack."
-
- def execute(*obj)
- irb_context.pop_workspace(*obj)
- super
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/show_cmds.rb b/lib/irb/cmd/show_cmds.rb
deleted file mode 100644
index a8d899e4ac..0000000000
--- a/lib/irb/cmd/show_cmds.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-require "stringio"
-require_relative "nop"
-require_relative "../pager"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class ShowCmds < Nop
- category "IRB"
- description "List all available commands and their description."
-
- def execute(*args)
- commands_info = IRB::ExtendCommandBundle.all_commands_info
- commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
-
- user_aliases = irb_context.instance_variable_get(:@user_aliases)
-
- commands_grouped_by_categories["Aliases"] = user_aliases.map do |alias_name, target|
- { display_name: alias_name, description: "Alias for `#{target}`" }
- end
-
- if irb_context.with_debugger
- # Remove the original "Debugging" category
- commands_grouped_by_categories.delete("Debugging")
- # Remove the `help` command as it's delegated to the debugger
- commands_grouped_by_categories["Context"].delete_if { |cmd| cmd[:display_name] == :help }
- # Add an empty "Debugging (from debug.gem)" category at the end
- commands_grouped_by_categories["Debugging (from debug.gem)"] = []
- end
-
- longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max
-
- output = StringIO.new
-
- commands_grouped_by_categories.each do |category, cmds|
- output.puts Color.colorize(category, [:BOLD])
-
- cmds.each do |cmd|
- output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
- end
-
- output.puts
- end
-
- # Append the debugger help at the end
- if irb_context.with_debugger
- output.puts DEBUGGER__.help
- end
-
- Pager.page_content(output.string)
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/show_doc.rb b/lib/irb/cmd/show_doc.rb
deleted file mode 100644
index 99dd9ab95a..0000000000
--- a/lib/irb/cmd/show_doc.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "nop"
-
-module IRB
- module ExtendCommand
- class ShowDoc < Nop
- class << self
- def transform_args(args)
- # Return a string literal as is for backward compatibility
- if args.empty? || string_literal?(args)
- args
- else # Otherwise, consider the input as a String for convenience
- args.strip.dump
- end
- end
- end
-
- category "Context"
- description "Enter the mode to look up RI documents."
-
- def execute(*names)
- require 'rdoc/ri/driver'
-
- unless ShowDoc.const_defined?(:Ri)
- opts = RDoc::RI::Driver.process_args([])
- ShowDoc.const_set(:Ri, RDoc::RI::Driver.new(opts))
- end
-
- if names.empty?
- Ri.interactive
- else
- names.each do |name|
- begin
- Ri.display_name(name.to_s)
- rescue RDoc::RI::Error
- puts $!.message
- end
- end
- end
-
- nil
- rescue LoadError, SystemExit
- warn "Can't display document because `rdoc` is not installed."
- end
- end
- end
-end
diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb
deleted file mode 100644
index 826cb11ed2..0000000000
--- a/lib/irb/cmd/show_source.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "nop"
-require_relative "../source_finder"
-require_relative "../pager"
-require_relative "../color"
-
-module IRB
- module ExtendCommand
- class ShowSource < Nop
- category "Context"
- description "Show the source code of a given method or constant."
-
- class << self
- def transform_args(args)
- # Return a string literal as is for backward compatibility
- if args.empty? || string_literal?(args)
- args
- else # Otherwise, consider the input as a String for convenience
- args.strip.dump
- end
- end
- end
-
- def execute(str = nil)
- unless str.is_a?(String)
- puts "Error: Expected a string but got #{str.inspect}"
- return
- end
-
- str, esses = str.split(" -")
- super_level = esses ? esses.count("s") : 0
- source = SourceFinder.new(@irb_context).find_source(str, super_level)
-
- if source
- show_source(source)
- elsif super_level > 0
- puts "Error: Couldn't locate a super definition for #{str}"
- else
- puts "Error: Couldn't locate a definition for #{str}"
- end
- nil
- end
-
- private
-
- def show_source(source)
- file_content = IRB::Color.colorize_code(File.read(source.file))
- code = file_content.lines[(source.first_line - 1)...source.last_line].join
- content = <<~CONTENT
-
- #{bold("From")}: #{source.file}:#{source.first_line}
-
- #{code}
- CONTENT
-
- Pager.page_content(content)
- end
-
- def bold(str)
- Color.colorize(str, [:BOLD])
- end
- end
- end
-end
diff --git a/lib/irb/color.rb b/lib/irb/color.rb
index ad8670160c..fca942b28b 100644
--- a/lib/irb/color.rb
+++ b/lib/irb/color.rb
@@ -79,12 +79,12 @@ module IRB # :nodoc:
class << self
def colorable?
- supported = $stdout.tty? && (/mswin|mingw/ =~ RUBY_PLATFORM || (ENV.key?('TERM') && ENV['TERM'] != 'dumb'))
+ supported = $stdout.tty? && (/mswin|mingw/.match?(RUBY_PLATFORM) || (ENV.key?('TERM') && ENV['TERM'] != 'dumb'))
# because ruby/debug also uses irb's color module selectively,
# irb won't be activated in that case.
if IRB.respond_to?(:conf)
- supported && IRB.conf.fetch(:USE_COLORIZE, true)
+ supported && !!IRB.conf.fetch(:USE_COLORIZE, true)
else
supported
end
diff --git a/lib/irb/command.rb b/lib/irb/command.rb
new file mode 100644
index 0000000000..68a4b52727
--- /dev/null
+++ b/lib/irb/command.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+#
+# irb/command.rb - irb command
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+require_relative "command/base"
+
+module IRB # :nodoc:
+ module Command
+ @commands = {}
+
+ class << self
+ attr_reader :commands
+
+ # Registers a command with the given name.
+ # Aliasing is intentionally not supported at the moment.
+ def register(name, command_class)
+ @commands[name.to_sym] = [command_class, []]
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/backtrace.rb b/lib/irb/command/backtrace.rb
new file mode 100644
index 0000000000..687bb075ac
--- /dev/null
+++ b/lib/irb/command/backtrace.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Backtrace < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "backtrace #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/base.rb b/lib/irb/command/base.rb
new file mode 100644
index 0000000000..1d406630a2
--- /dev/null
+++ b/lib/irb/command/base.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+#
+# nop.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class CommandArgumentError < StandardError; end
+
+ def self.extract_ruby_args(*args, **kwargs)
+ throw :EXTRACT_RUBY_ARGS, [args, kwargs]
+ end
+
+ class Base
+ class << self
+ def category(category = nil)
+ @category = category if category
+ @category || "No category"
+ end
+
+ def description(description = nil)
+ @description = description if description
+ @description || "No description provided."
+ end
+
+ def help_message(help_message = nil)
+ @help_message = help_message if help_message
+ @help_message
+ end
+
+ private
+
+ def highlight(text)
+ Color.colorize(text, [:BOLD, :BLUE])
+ end
+ end
+
+ def self.execute(irb_context, arg)
+ new(irb_context).execute(arg)
+ rescue CommandArgumentError => e
+ puts e.message
+ end
+
+ def initialize(irb_context)
+ @irb_context = irb_context
+ end
+
+ attr_reader :irb_context
+
+ def execute(arg)
+ #nop
+ end
+ end
+
+ Nop = Base
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/break.rb b/lib/irb/command/break.rb
new file mode 100644
index 0000000000..a8f81fe665
--- /dev/null
+++ b/lib/irb/command/break.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Break < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "break #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/catch.rb b/lib/irb/command/catch.rb
new file mode 100644
index 0000000000..529dcbca5a
--- /dev/null
+++ b/lib/irb/command/catch.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Catch < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "catch #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/chws.rb b/lib/irb/command/chws.rb
new file mode 100644
index 0000000000..ef456d0961
--- /dev/null
+++ b/lib/irb/command/chws.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+#
+# change-ws.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+require_relative "../ext/change-ws"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+
+ class CurrentWorkingWorkspace < Base
+ category "Workspace"
+ description "Show the current workspace."
+
+ def execute(_arg)
+ puts "Current workspace: #{irb_context.main}"
+ end
+ end
+
+ class ChangeWorkspace < Base
+ category "Workspace"
+ description "Change the current workspace to an object."
+
+ def execute(arg)
+ if arg.empty?
+ irb_context.change_workspace
+ else
+ obj = eval(arg, irb_context.workspace.binding)
+ irb_context.change_workspace(obj)
+ end
+
+ puts "Current workspace: #{irb_context.main}"
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/context.rb b/lib/irb/command/context.rb
new file mode 100644
index 0000000000..b4fc807343
--- /dev/null
+++ b/lib/irb/command/context.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module IRB
+ module Command
+ class Context < Base
+ category "IRB"
+ description "Displays current configuration."
+
+ def execute(_arg)
+ # This command just displays the configuration.
+ # Modifying the configuration is achieved by sending a message to IRB.conf.
+ Pager.page_content(IRB.CurrentContext.inspect)
+ end
+ end
+ end
+end
diff --git a/lib/irb/cmd/continue.rb b/lib/irb/command/continue.rb
index 9136177eef..0daa029b15 100644
--- a/lib/irb/cmd/continue.rb
+++ b/lib/irb/command/continue.rb
@@ -5,10 +5,10 @@ require_relative "debug"
module IRB
# :stopdoc:
- module ExtendCommand
+ module Command
class Continue < DebugCommand
- def execute(*args)
- super(do_cmds: ["continue", *args].join(" "))
+ def execute(arg)
+ execute_debug_command(do_cmds: "continue #{arg}")
end
end
end
diff --git a/lib/irb/cmd/debug.rb b/lib/irb/command/debug.rb
index e236084ca4..8a091a49ed 100644
--- a/lib/irb/cmd/debug.rb
+++ b/lib/irb/command/debug.rb
@@ -1,20 +1,21 @@
-require_relative "nop"
require_relative "../debug"
module IRB
# :stopdoc:
- module ExtendCommand
- class Debug < Nop
+ module Command
+ class Debug < Base
category "Debugging"
description "Start the debugger of debug.gem."
- BINDING_IRB_FRAME_REGEXPS = [
- '<internal:prelude>',
- binding.method(:irb).source_location.first,
- ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ }
+ def execute(_arg)
+ execute_debug_command
+ end
+
+ def execute_debug_command(pre_cmds: nil, do_cmds: nil)
+ pre_cmds = pre_cmds&.rstrip
+ do_cmds = do_cmds&.rstrip
- def execute(pre_cmds: nil, do_cmds: nil)
if irb_context.with_debugger
# If IRB is already running with a debug session, throw the command and IRB.debug_readline will pass it to the debugger.
if cmd = pre_cmds || do_cmds
@@ -30,7 +31,7 @@ module IRB
# 3. Insert a debug breakpoint at `Irb#debug_break` with the intended command.
# 4. Exit the current Irb#run call via `throw :IRB_EXIT`.
# 5. `Irb#debug_break` will be called and trigger the breakpoint, which will run the intended command.
- unless binding_irb?
+ unless irb_context.from_binding?
puts "Debugging commands are only available when IRB is started with binding.irb"
return
end
@@ -54,16 +55,6 @@ module IRB
throw :IRB_EXIT
end
end
-
- private
-
- def binding_irb?
- caller.any? do |frame|
- BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
- frame.match?(regexp)
- end
- end
- end
end
class DebugCommand < Debug
diff --git a/lib/irb/cmd/delete.rb b/lib/irb/command/delete.rb
index aeb26d2572..2a57a4a3de 100644
--- a/lib/irb/cmd/delete.rb
+++ b/lib/irb/command/delete.rb
@@ -5,10 +5,10 @@ require_relative "debug"
module IRB
# :stopdoc:
- module ExtendCommand
+ module Command
class Delete < DebugCommand
- def execute(*args)
- super(pre_cmds: ["delete", *args].join(" "))
+ def execute(arg)
+ execute_debug_command(pre_cmds: "delete #{arg}")
end
end
end
diff --git a/lib/irb/command/disable_irb.rb b/lib/irb/command/disable_irb.rb
new file mode 100644
index 0000000000..0b00d0302b
--- /dev/null
+++ b/lib/irb/command/disable_irb.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class DisableIrb < Base
+ category "IRB"
+ description "Disable binding.irb."
+
+ def execute(*)
+ ::Binding.define_method(:irb) {}
+ IRB.irb_exit
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/edit.rb b/lib/irb/command/edit.rb
new file mode 100644
index 0000000000..cb7e0c4873
--- /dev/null
+++ b/lib/irb/command/edit.rb
@@ -0,0 +1,63 @@
+require 'shellwords'
+
+require_relative "../color"
+require_relative "../source_finder"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Edit < Base
+ include RubyArgsExtractor
+
+ category "Misc"
+ description 'Open a file or source location.'
+ help_message <<~HELP_MESSAGE
+ Usage: edit [FILE or constant or method signature]
+
+ Open a file in the editor specified in #{highlight('ENV["VISUAL"]')} or #{highlight('ENV["EDITOR"]')}
+
+ - If no arguments are provided, IRB will attempt to open the file the current context was defined in.
+ - If FILE is provided, IRB will open the file.
+ - If a constant or method signature is provided, IRB will attempt to locate the source file and open it.
+
+ Examples:
+
+ edit
+ edit foo.rb
+ edit Foo
+ edit Foo#bar
+ HELP_MESSAGE
+
+ def execute(arg)
+ # Accept string literal for backward compatibility
+ path = unwrap_string_literal(arg)
+
+ if path.nil?
+ path = @irb_context.irb_path
+ elsif !File.exist?(path)
+ source = SourceFinder.new(@irb_context).find_source(path)
+
+ if source&.file_exist? && !source.binary_file?
+ path = source.file
+ end
+ end
+
+ unless File.exist?(path)
+ puts "Can not find file: #{path}"
+ return
+ end
+
+ if editor = (ENV['VISUAL'] || ENV['EDITOR'])
+ puts "command: '#{editor}'"
+ puts " path: #{path}"
+ system(*Shellwords.split(editor), path)
+ else
+ puts "Can not find editor setting: ENV['VISUAL'] or ENV['EDITOR']"
+ end
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/exit.rb b/lib/irb/command/exit.rb
new file mode 100644
index 0000000000..b4436f0343
--- /dev/null
+++ b/lib/irb/command/exit.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Exit < Base
+ category "IRB"
+ description "Exit the current irb session."
+
+ def execute(_arg)
+ IRB.irb_exit
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/cmd/finish.rb b/lib/irb/command/finish.rb
index 29f100feb5..3311a0e6e9 100644
--- a/lib/irb/cmd/finish.rb
+++ b/lib/irb/command/finish.rb
@@ -5,10 +5,10 @@ require_relative "debug"
module IRB
# :stopdoc:
- module ExtendCommand
+ module Command
class Finish < DebugCommand
- def execute(*args)
- super(do_cmds: ["finish", *args].join(" "))
+ def execute(arg)
+ execute_debug_command(do_cmds: "finish #{arg}")
end
end
end
diff --git a/lib/irb/command/force_exit.rb b/lib/irb/command/force_exit.rb
new file mode 100644
index 0000000000..14086aa849
--- /dev/null
+++ b/lib/irb/command/force_exit.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class ForceExit < Base
+ category "IRB"
+ description "Exit the current process."
+
+ def execute(_arg)
+ throw :IRB_EXIT, true
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/help.rb b/lib/irb/command/help.rb
new file mode 100644
index 0000000000..c2018f9b30
--- /dev/null
+++ b/lib/irb/command/help.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+module IRB
+ module Command
+ class Help < Base
+ category "Help"
+ description "List all available commands. Use `help <command>` to get information about a specific command."
+
+ def execute(command_name)
+ content =
+ if command_name.empty?
+ help_message
+ else
+ if command_class = Command.load_command(command_name)
+ command_class.help_message || command_class.description
+ else
+ "Can't find command `#{command_name}`. Please check the command name and try again.\n\n"
+ end
+ end
+ Pager.page_content(content)
+ end
+
+ private
+
+ def help_message
+ commands_info = IRB::Command.all_commands_info
+ helper_methods_info = IRB::HelperMethod.all_helper_methods_info
+ commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
+ commands_grouped_by_categories["Helper methods"] = helper_methods_info
+
+ if irb_context.with_debugger
+ # Remove the original "Debugging" category
+ commands_grouped_by_categories.delete("Debugging")
+ end
+
+ longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max
+
+ output = StringIO.new
+
+ help_cmds = commands_grouped_by_categories.delete("Help")
+ no_category_cmds = commands_grouped_by_categories.delete("No category")
+ aliases = irb_context.instance_variable_get(:@user_aliases).map do |alias_name, target|
+ { display_name: alias_name, description: "Alias for `#{target}`" }
+ end
+
+ # Display help commands first
+ add_category_to_output("Help", help_cmds, output, longest_cmd_name_length)
+
+ # Display the rest of the commands grouped by categories
+ commands_grouped_by_categories.each do |category, cmds|
+ add_category_to_output(category, cmds, output, longest_cmd_name_length)
+ end
+
+ # Display commands without a category
+ if no_category_cmds
+ add_category_to_output("No category", no_category_cmds, output, longest_cmd_name_length)
+ end
+
+ # Display aliases
+ add_category_to_output("Aliases", aliases, output, longest_cmd_name_length)
+
+ # Append the debugger help at the end
+ if irb_context.with_debugger
+ # Add "Debugging (from debug.gem)" category as title
+ add_category_to_output("Debugging (from debug.gem)", [], output, longest_cmd_name_length)
+ output.puts DEBUGGER__.help
+ end
+
+ output.string
+ end
+
+ def add_category_to_output(category, cmds, output, longest_cmd_name_length)
+ output.puts Color.colorize(category, [:BOLD])
+
+ cmds.each do |cmd|
+ output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
+ end
+
+ output.puts
+ end
+ end
+ end
+end
diff --git a/lib/irb/cmd/history.rb b/lib/irb/command/history.rb
index 5b712fa44d..90f87f9102 100644
--- a/lib/irb/cmd/history.rb
+++ b/lib/irb/command/history.rb
@@ -1,25 +1,23 @@
# frozen_string_literal: true
require "stringio"
-require_relative "nop"
+
require_relative "../pager"
module IRB
# :stopdoc:
- module ExtendCommand
- class History < Nop
+ module Command
+ class History < Base
category "IRB"
description "Shows the input history. `-g [query]` or `-G [query]` allows you to filter the output."
- def self.transform_args(args)
- match = args&.match(/(-g|-G)\s+(?<grep>.+)\s*\n\z/)
- return unless match
+ def execute(arg)
- "grep: #{Regexp.new(match[:grep]).inspect}"
- end
+ if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\n\z/))
+ grep = Regexp.new(match[:grep])
+ end
- def execute(grep: nil)
formatted_inputs = irb_context.io.class::HISTORY.each_with_index.reverse_each.filter_map do |input, index|
next if grep && !input.match?(grep)
diff --git a/lib/irb/command/info.rb b/lib/irb/command/info.rb
new file mode 100644
index 0000000000..d08ce00a32
--- /dev/null
+++ b/lib/irb/command/info.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Info < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "info #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/internal_helpers.rb b/lib/irb/command/internal_helpers.rb
new file mode 100644
index 0000000000..249b5cdede
--- /dev/null
+++ b/lib/irb/command/internal_helpers.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module IRB
+ module Command
+ # Internal use only, for default command's backward compatibility.
+ module RubyArgsExtractor # :nodoc:
+ def unwrap_string_literal(str)
+ return if str.empty?
+
+ sexp = Ripper.sexp(str)
+ if sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
+ @irb_context.workspace.binding.eval(str).to_s
+ else
+ str
+ end
+ end
+
+ def ruby_args(arg)
+ # Use throw and catch to handle arg that includes `;`
+ # For example: "1, kw: (2; 3); 4" will be parsed to [[1], { kw: 3 }]
+ catch(:EXTRACT_RUBY_ARGS) do
+ @irb_context.workspace.binding.eval "IRB::Command.extract_ruby_args #{arg}"
+ end || [[], {}]
+ end
+ end
+ end
+end
diff --git a/lib/irb/cmd/irb_info.rb b/lib/irb/command/irb_info.rb
index 5b905a09bd..6d868de94c 100644
--- a/lib/irb/cmd/irb_info.rb
+++ b/lib/irb/command/irb_info.rb
@@ -1,21 +1,20 @@
-# frozen_string_literal: false
-
-require_relative "nop"
+# frozen_string_literal: true
module IRB
# :stopdoc:
- module ExtendCommand
- class IrbInfo < Nop
+ module Command
+ class IrbInfo < Base
category "IRB"
description "Show information about IRB."
- def execute
+ def execute(_arg)
str = "Ruby version: #{RUBY_VERSION}\n"
str += "IRB version: #{IRB.version}\n"
str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
str += "Completion: #{IRB.CurrentContext.io.respond_to?(:completion_info) ? IRB.CurrentContext.io.completion_info : 'off'}\n"
- str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
+ rc_files = IRB.irbrc_files
+ str += ".irbrc paths: #{rc_files.join(", ")}\n" if rc_files.any?
str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
diff --git a/lib/irb/cmd/load.rb b/lib/irb/command/load.rb
index a3e797a7e0..1cd3f279d1 100644
--- a/lib/irb/cmd/load.rb
+++ b/lib/irb/command/load.rb
@@ -1,17 +1,16 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# load.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
-
-require_relative "nop"
require_relative "../ext/loader"
module IRB
# :stopdoc:
- module ExtendCommand
- class LoaderCommand < Nop
+ module Command
+ class LoaderCommand < Base
+ include RubyArgsExtractor
include IrbLoader
def raise_cmd_argument_error
@@ -23,7 +22,12 @@ module IRB
category "IRB"
description "Load a Ruby file."
- def execute(file_name = nil, priv = nil)
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(file_name = nil, priv = nil)
raise_cmd_argument_error unless file_name
irb_load(file_name, priv)
end
@@ -32,7 +36,13 @@ module IRB
class Require < LoaderCommand
category "IRB"
description "Require a Ruby file."
- def execute(file_name = nil)
+
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(file_name = nil)
raise_cmd_argument_error unless file_name
rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
@@ -65,7 +75,12 @@ module IRB
category "IRB"
description "Loads a given file in the current session."
- def execute(file_name = nil)
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(file_name = nil)
raise_cmd_argument_error unless file_name
source_file(file_name)
diff --git a/lib/irb/cmd/ls.rb b/lib/irb/command/ls.rb
index 791b1c1b21..cbd9998bc4 100644
--- a/lib/irb/cmd/ls.rb
+++ b/lib/irb/command/ls.rb
@@ -2,39 +2,55 @@
require "reline"
require "stringio"
-require_relative "nop"
+
require_relative "../pager"
require_relative "../color"
module IRB
# :stopdoc:
- module ExtendCommand
- class Ls < Nop
+ module Command
+ class Ls < Base
+ include RubyArgsExtractor
+
category "Context"
- description "Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output."
+ description "Show methods, constants, and variables."
+
+ help_message <<~HELP_MESSAGE
+ Usage: ls [obj] [-g [query]]
+
+ -g [query] Filter the output with a query.
+ HELP_MESSAGE
- def self.transform_args(args)
- if match = args&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
- args = match[:args]
- "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/"
+ def execute(arg)
+ if match = arg.match(/\A(?<target>.+\s|)(-g|-G)\s+(?<grep>.+)$/)
+ if match[:target].empty?
+ use_main = true
+ else
+ obj = @irb_context.workspace.binding.eval(match[:target])
+ end
+ grep = Regexp.new(match[:grep])
else
- args
+ args, kwargs = ruby_args(arg)
+ use_main = args.empty?
+ obj = args.first
+ grep = kwargs[:grep]
+ end
+
+ if use_main
+ obj = irb_context.workspace.main
+ locals = irb_context.workspace.binding.local_variables
end
- end
- def execute(*arg, grep: nil)
o = Output.new(grep: grep)
- obj = arg.empty? ? irb_context.workspace.main : arg.first
- locals = arg.empty? ? irb_context.workspace.binding.local_variables : []
klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
o.dump("constants", obj.constants) if obj.respond_to?(:constants)
dump_methods(o, klass, obj)
o.dump("instance variables", obj.instance_variables)
o.dump("class variables", klass.class_variables)
- o.dump("locals", locals)
+ o.dump("locals", locals) if locals
o.print_result
end
diff --git a/lib/irb/cmd/measure.rb b/lib/irb/command/measure.rb
index 4e1125a0a6..f96be20de8 100644
--- a/lib/irb/cmd/measure.rb
+++ b/lib/irb/command/measure.rb
@@ -1,10 +1,10 @@
-require_relative "nop"
-
module IRB
# :stopdoc:
- module ExtendCommand
- class Measure < Nop
+ module Command
+ class Measure < Base
+ include RubyArgsExtractor
+
category "Misc"
description "`measure` enables the mode to measure processing time. `measure :off` disables it."
@@ -12,15 +12,19 @@ module IRB
super(*args)
end
- def execute(type = nil, arg = nil)
- # Please check IRB.init_config in lib/irb/init.rb that sets
- # IRB.conf[:MEASURE_PROC] to register default "measure" methods,
- # "measure :time" (abbreviated as "measure") and "measure :stackprof".
-
- if block_given?
+ def execute(arg)
+ if arg&.match?(/^do$|^do[^\w]|^\{/)
warn 'Configure IRB.conf[:MEASURE_PROC] to add custom measure methods.'
return
end
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(type = nil, arg = nil)
+ # Please check IRB.init_config in lib/irb/init.rb that sets
+ # IRB.conf[:MEASURE_PROC] to register default "measure" methods,
+ # "measure :time" (abbreviated as "measure") and "measure :stackprof".
case type
when :off
diff --git a/lib/irb/cmd/next.rb b/lib/irb/command/next.rb
index d29c82e7fc..3fc6b68d21 100644
--- a/lib/irb/cmd/next.rb
+++ b/lib/irb/command/next.rb
@@ -5,10 +5,10 @@ require_relative "debug"
module IRB
# :stopdoc:
- module ExtendCommand
+ module Command
class Next < DebugCommand
- def execute(*args)
- super(do_cmds: ["next", *args].join(" "))
+ def execute(arg)
+ execute_debug_command(do_cmds: "next #{arg}")
end
end
end
diff --git a/lib/irb/command/pushws.rb b/lib/irb/command/pushws.rb
new file mode 100644
index 0000000000..b51928c650
--- /dev/null
+++ b/lib/irb/command/pushws.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+#
+# change-ws.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+require_relative "../ext/workspaces"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Workspaces < Base
+ category "Workspace"
+ description "Show workspaces."
+
+ def execute(_arg)
+ inspection_resuls = irb_context.instance_variable_get(:@workspace_stack).map do |ws|
+ truncated_inspect(ws.main)
+ end
+
+ puts "[" + inspection_resuls.join(", ") + "]"
+ end
+
+ private
+
+ def truncated_inspect(obj)
+ obj_inspection = obj.inspect
+
+ if obj_inspection.size > 20
+ obj_inspection = obj_inspection[0, 19] + "...>"
+ end
+
+ obj_inspection
+ end
+ end
+
+ class PushWorkspace < Workspaces
+ category "Workspace"
+ description "Push an object to the workspace stack."
+
+ def execute(arg)
+ if arg.empty?
+ irb_context.push_workspace
+ else
+ obj = eval(arg, irb_context.workspace.binding)
+ irb_context.push_workspace(obj)
+ end
+ super
+ end
+ end
+
+ class PopWorkspace < Workspaces
+ category "Workspace"
+ description "Pop a workspace from the workspace stack."
+
+ def execute(_arg)
+ irb_context.pop_workspace
+ super
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/show_doc.rb b/lib/irb/command/show_doc.rb
new file mode 100644
index 0000000000..8a2188e4eb
--- /dev/null
+++ b/lib/irb/command/show_doc.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module IRB
+ module Command
+ class ShowDoc < Base
+ include RubyArgsExtractor
+
+ category "Context"
+ description "Look up documentation with RI."
+
+ help_message <<~HELP_MESSAGE
+ Usage: show_doc [name]
+
+ When name is provided, IRB will look up the documentation for the given name.
+ When no name is provided, a RI session will be started.
+
+ Examples:
+
+ show_doc
+ show_doc Array
+ show_doc Array#each
+
+ HELP_MESSAGE
+
+ def execute(arg)
+ # Accept string literal for backward compatibility
+ name = unwrap_string_literal(arg)
+ require 'rdoc/ri/driver'
+
+ unless ShowDoc.const_defined?(:Ri)
+ opts = RDoc::RI::Driver.process_args([])
+ ShowDoc.const_set(:Ri, RDoc::RI::Driver.new(opts))
+ end
+
+ if name.nil?
+ Ri.interactive
+ else
+ begin
+ Ri.display_name(name)
+ rescue RDoc::RI::Error
+ puts $!.message
+ end
+ end
+
+ nil
+ rescue LoadError, SystemExit
+ warn "Can't display document because `rdoc` is not installed."
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/show_source.rb b/lib/irb/command/show_source.rb
new file mode 100644
index 0000000000..f4c6f104a2
--- /dev/null
+++ b/lib/irb/command/show_source.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require_relative "../source_finder"
+require_relative "../pager"
+require_relative "../color"
+
+module IRB
+ module Command
+ class ShowSource < Base
+ include RubyArgsExtractor
+
+ category "Context"
+ description "Show the source code of a given method, class/module, or constant."
+
+ help_message <<~HELP_MESSAGE
+ Usage: show_source [target] [-s]
+
+ -s Show the super method. You can stack it like `-ss` to show the super of the super, etc.
+
+ Examples:
+
+ show_source Foo
+ show_source Foo#bar
+ show_source Foo#bar -s
+ show_source Foo.baz
+ show_source Foo::BAR
+ HELP_MESSAGE
+
+ def execute(arg)
+ # Accept string literal for backward compatibility
+ str = unwrap_string_literal(arg)
+ unless str.is_a?(String)
+ puts "Error: Expected a string but got #{str.inspect}"
+ return
+ end
+
+ str, esses = str.split(" -")
+ super_level = esses ? esses.count("s") : 0
+ source = SourceFinder.new(@irb_context).find_source(str, super_level)
+
+ if source
+ show_source(source)
+ elsif super_level > 0
+ puts "Error: Couldn't locate a super definition for #{str}"
+ else
+ puts "Error: Couldn't locate a definition for #{str}"
+ end
+ nil
+ end
+
+ private
+
+ def show_source(source)
+ if source.binary_file?
+ content = "\n#{bold('Defined in binary file')}: #{source.file}\n\n"
+ else
+ code = source.colorized_content || 'Source not available'
+ content = <<~CONTENT
+
+ #{bold("From")}: #{source.file}:#{source.line}
+
+ #{code.chomp}
+
+ CONTENT
+ end
+ Pager.page_content(content)
+ end
+
+ def bold(str)
+ Color.colorize(str, [:BOLD])
+ end
+ end
+ end
+end
diff --git a/lib/irb/cmd/step.rb b/lib/irb/command/step.rb
index 2bc74a9d79..29e5e35ac0 100644
--- a/lib/irb/cmd/step.rb
+++ b/lib/irb/command/step.rb
@@ -5,10 +5,10 @@ require_relative "debug"
module IRB
# :stopdoc:
- module ExtendCommand
+ module Command
class Step < DebugCommand
- def execute(*args)
- super(do_cmds: ["step", *args].join(" "))
+ def execute(arg)
+ execute_debug_command(do_cmds: "step #{arg}")
end
end
end
diff --git a/lib/irb/cmd/subirb.rb b/lib/irb/command/subirb.rb
index 5ffd646416..85af28c1a5 100644
--- a/lib/irb/cmd/subirb.rb
+++ b/lib/irb/command/subirb.rb
@@ -1,19 +1,15 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# multi.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
-require_relative "nop"
-
module IRB
# :stopdoc:
- module ExtendCommand
- class MultiIRBCommand < Nop
- def execute(*args)
- extend_irb_context
- end
+ module Command
+ class MultiIRBCommand < Base
+ include RubyArgsExtractor
private
@@ -38,7 +34,12 @@ module IRB
category "Multi-irb (DEPRECATED)"
description "Start a child IRB."
- def execute(*obj)
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(*obj)
print_deprecated_warning
if irb_context.with_debugger
@@ -46,8 +47,9 @@ module IRB
return
end
- super
+ extend_irb_context
IRB.irb(nil, *obj)
+ puts IRB.JobManager.inspect
end
end
@@ -55,7 +57,7 @@ module IRB
category "Multi-irb (DEPRECATED)"
description "List of current sessions."
- def execute
+ def execute(_arg)
print_deprecated_warning
if irb_context.with_debugger
@@ -63,8 +65,8 @@ module IRB
return
end
- super
- IRB.JobManager
+ extend_irb_context
+ puts IRB.JobManager.inspect
end
end
@@ -72,7 +74,12 @@ module IRB
category "Multi-irb (DEPRECATED)"
description "Switches to the session of the given number."
- def execute(key = nil)
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(key = nil)
print_deprecated_warning
if irb_context.with_debugger
@@ -80,10 +87,11 @@ module IRB
return
end
- super
+ extend_irb_context
raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key
IRB.JobManager.switch(key)
+ puts IRB.JobManager.inspect
end
end
@@ -91,7 +99,12 @@ module IRB
category "Multi-irb (DEPRECATED)"
description "Kills the session with the given number."
- def execute(*keys)
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(*keys)
print_deprecated_warning
if irb_context.with_debugger
@@ -99,8 +112,9 @@ module IRB
return
end
- super
+ extend_irb_context
IRB.JobManager.kill(*keys)
+ puts IRB.JobManager.inspect
end
end
end
diff --git a/lib/irb/cmd/whereami.rb b/lib/irb/command/whereami.rb
index 8f56ba073d..c8439f1212 100644
--- a/lib/irb/cmd/whereami.rb
+++ b/lib/irb/command/whereami.rb
@@ -1,16 +1,14 @@
# frozen_string_literal: true
-require_relative "nop"
-
module IRB
# :stopdoc:
- module ExtendCommand
- class Whereami < Nop
+ module Command
+ class Whereami < Base
category "Context"
description "Show the source code around binding.irb again."
- def execute(*)
+ def execute(_arg)
code = irb_context.workspace.code_around_binding
if code
puts code
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index af3b69eb27..a3d89373c3 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/completion.rb -
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
@@ -86,6 +86,14 @@ module IRB
)
end
+ def command_completions(preposing, target)
+ if preposing.empty? && !target.empty?
+ IRB::Command.command_names.select { _1.start_with?(target) }
+ else
+ []
+ end
+ end
+
def retrieve_files_to_require_relative_from_current_dir
@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
@@ -103,9 +111,11 @@ module IRB
end
def completion_candidates(preposing, target, _postposing, bind:)
+ commands = command_completions(preposing, target)
result = ReplTypeCompletor.analyze(preposing + target, binding: bind, filename: @context.irb_path)
- return [] unless result
- result.completion_candidates.map { target + _1 }
+ return commands unless result
+
+ commands | result.completion_candidates.map { target + _1 }
end
def doc_namespace(preposing, matched, _postposing, bind:)
@@ -181,7 +191,8 @@ module IRB
result = complete_require_path(target, preposing, postposing)
return result if result
end
- retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
+ commands = command_completions(preposing || '', target)
+ commands | retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
end
def doc_namespace(_preposing, matched, _postposing, bind:)
@@ -388,7 +399,7 @@ module IRB
if doc_namespace
rec_class = rec.is_a?(Module) ? rec : rec.class
- "#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}"
+ "#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}" rescue nil
else
select_message(receiver, message, candidates, sep)
end
@@ -406,13 +417,19 @@ module IRB
else
select_message(receiver, message, candidates.sort)
end
-
+ when /^\s*$/
+ # empty input
+ if doc_namespace
+ nil
+ else
+ []
+ end
else
if doc_namespace
vars = (bind.local_variables | bind.eval_instance_variables).collect{|m| m.to_s}
perfect_match_var = vars.find{|m| m.to_s == input}
if perfect_match_var
- eval("#{perfect_match_var}.class.name", bind)
+ eval("#{perfect_match_var}.class.name", bind) rescue nil
else
candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
candidates |= ReservedWords
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index c3690fcac7..5d2ff97328 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/context.rb - irb context
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -22,10 +22,11 @@ module IRB
# +other+:: uses this as InputMethod
def initialize(irb, workspace = nil, input_method = nil)
@irb = irb
+ @workspace_stack = []
if workspace
- @workspace = workspace
+ @workspace_stack << workspace
else
- @workspace = WorkSpace.new
+ @workspace_stack << WorkSpace.new
end
@thread = Thread.current
@@ -61,7 +62,7 @@ module IRB
@io = nil
self.inspect_mode = IRB.conf[:INSPECT_MODE]
- self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER]
+ self.use_tracer = IRB.conf[:USE_TRACER]
self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER]
self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY]
@@ -77,14 +78,14 @@ module IRB
else
@irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s
end
- @irb_path = "(" + @irb_name + ")"
+ self.irb_path = "(" + @irb_name + ")"
case input_method
when nil
@io = nil
case use_multiline?
when nil
- if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
+ if term_interactive? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
# Both of multiline mode and singleline mode aren't specified.
@io = RelineInputMethod.new(build_completor)
else
@@ -98,7 +99,7 @@ module IRB
unless @io
case use_singleline?
when nil
- if (defined?(ReadlineInputMethod) && STDIN.tty? &&
+ if (defined?(ReadlineInputMethod) && term_interactive? &&
IRB.conf[:PROMPT_MODE] != :INF_RUBY)
@io = ReadlineInputMethod.new
else
@@ -121,11 +122,11 @@ module IRB
when '-'
@io = FileInputMethod.new($stdin)
@irb_name = '-'
- @irb_path = '-'
+ self.irb_path = '-'
when String
@io = FileInputMethod.new(input_method)
@irb_name = File.basename(input_method)
- @irb_path = input_method
+ self.irb_path = input_method
else
@io = input_method
end
@@ -150,6 +151,11 @@ module IRB
@command_aliases = @user_aliases.merge(KEYWORD_ALIASES)
end
+ private def term_interactive?
+ return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
+ STDIN.tty? && ENV['TERM'] != 'dumb'
+ end
+
# because all input will eventually be evaluated as Ruby code,
# command names that conflict with Ruby keywords need special workaround
# we can remove them once we implemented a better command system for IRB
@@ -161,6 +167,23 @@ module IRB
private_constant :KEYWORD_ALIASES
+ def use_tracer=(val)
+ require_relative "ext/tracer" if val
+ IRB.conf[:USE_TRACER] = val
+ end
+
+ def eval_history=(val)
+ self.class.remove_method(__method__)
+ require_relative "ext/eval_history"
+ __send__(__method__, val)
+ end
+
+ def use_loader=(val)
+ self.class.remove_method(__method__)
+ require_relative "ext/use-loader"
+ __send__(__method__, val)
+ end
+
private def build_completor
completor_type = IRB.conf[:COMPLETOR]
case completor_type
@@ -178,7 +201,7 @@ module IRB
private def build_type_completor
if RUBY_ENGINE == 'truffleruby'
- # Avoid SynatxError. truffleruby does not support endless method definition yet.
+ # Avoid SyntaxError. truffleruby does not support endless method definition yet.
warn 'TypeCompletor is not supported on TruffleRuby yet'
return
end
@@ -212,15 +235,24 @@ module IRB
IRB.conf[:HISTORY_FILE] = hist
end
+ # Workspace in the current context.
+ def workspace
+ @workspace_stack.last
+ end
+
+ # Replace the current workspace with the given +workspace+.
+ def replace_workspace(workspace)
+ @workspace_stack.pop
+ @workspace_stack.push(workspace)
+ end
+
# The top-level workspace, see WorkSpace#main
def main
- @workspace.main
+ workspace.main
end
# The toplevel workspace, see #home_workspace
attr_reader :workspace_home
- # WorkSpace in the current context.
- attr_accessor :workspace
# The current thread in this context.
attr_reader :thread
# The current input method.
@@ -241,9 +273,27 @@ module IRB
# Can be either name from <code>IRB.conf[:IRB_NAME]</code>, or the number of
# the current job set by JobManager, such as <code>irb#2</code>
attr_accessor :irb_name
- # Can be either the #irb_name surrounded by parenthesis, or the
- # +input_method+ passed to Context.new
- attr_accessor :irb_path
+
+ # Can be one of the following:
+ # - the #irb_name surrounded by parenthesis
+ # - the +input_method+ passed to Context.new
+ # - the file path of the current IRB context in a binding.irb session
+ attr_reader :irb_path
+
+ # Sets @irb_path to the given +path+ as well as @eval_path
+ # @eval_path is used for evaluating code in the context of IRB session
+ # It's the same as irb_path, but with the IRB name postfix
+ # This makes sure users can distinguish the methods defined in the IRB session
+ # from the methods defined in the current file's context, especially with binding.irb
+ def irb_path=(path)
+ @irb_path = path
+
+ if File.exist?(path)
+ @eval_path = "#{path}(#{IRB.conf[:IRB_NAME]})"
+ else
+ @eval_path = path
+ end
+ end
# Whether multiline editor mode is enabled or not.
#
@@ -444,9 +494,7 @@ module IRB
# StdioInputMethod or RelineInputMethod or ReadlineInputMethod, see #io
# for more information.
def prompting?
- verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
- @io.kind_of?(RelineInputMethod) ||
- (defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
+ verbose? || @io.prompting?
end
# The return value of the last statement evaluated.
@@ -456,7 +504,7 @@ module IRB
# to #last_value.
def set_last_value(value)
@last_value = value
- @workspace.local_variable_set :_, value
+ workspace.local_variable_set :_, value
end
# Sets the +mode+ of the prompt in this context.
@@ -542,45 +590,55 @@ module IRB
@inspect_mode
end
- def evaluate(line, line_no) # :nodoc:
+ def evaluate(statement, line_no) # :nodoc:
@line_no = line_no
- result = nil
+ case statement
+ when Statement::EmptyInput
+ return
+ when Statement::Expression
+ result = evaluate_expression(statement.code, line_no)
+ set_last_value(result)
+ when Statement::Command
+ statement.command_class.execute(self, statement.arg)
+ set_last_value(nil)
+ end
+
+ nil
+ end
+
+ def from_binding?
+ @irb.from_binding
+ end
+
+ def evaluate_expression(code, line_no) # :nodoc:
+ result = nil
if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
IRB.set_measure_callback
end
if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
last_proc = proc do
- result = @workspace.evaluate(line, irb_path, line_no)
+ result = workspace.evaluate(code, @eval_path, line_no)
end
IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item|
_name, callback, arg = item
proc do
- callback.(self, line, line_no, arg) do
+ callback.(self, code, line_no, arg) do
chain.call
end
end
end.call
else
- result = @workspace.evaluate(line, irb_path, line_no)
+ result = workspace.evaluate(code, @eval_path, line_no)
end
-
- set_last_value(result)
+ result
end
def inspect_last_value # :nodoc:
@inspect_method.inspect_value(@last_value)
end
- alias __exit__ exit
- # Exits the current session, see IRB.irb_exit
- def exit(ret = 0)
- IRB.irb_exit(@irb, ret)
- rescue UncaughtThrowError
- super
- end
-
NOPRINTING_IVARS = ["@last_value"] # :nodoc:
NO_INSPECTING_IVARS = ["@irb", "@io"] # :nodoc:
IDNAME_IVARS = ["@prompt_mode"] # :nodoc:
@@ -611,17 +669,5 @@ module IRB
def local_variables # :nodoc:
workspace.binding.local_variables
end
-
- # Return true if it's aliased from the argument and it's not an identifier.
- def symbol_alias?(command)
- return nil if command.match?(/\A\w+\z/)
- command_aliases.key?(command.to_sym)
- end
-
- # Return true if the command supports transforming args
- def transform_args?(command)
- command = command_aliases.fetch(command.to_sym, command)
- ExtendCommandBundle.load_command(command)&.respond_to?(:transform_args)
- end
end
end
diff --git a/lib/irb/default_commands.rb b/lib/irb/default_commands.rb
new file mode 100644
index 0000000000..1bbc68efa7
--- /dev/null
+++ b/lib/irb/default_commands.rb
@@ -0,0 +1,260 @@
+# frozen_string_literal: true
+
+require_relative "command"
+require_relative "command/internal_helpers"
+require_relative "command/context"
+require_relative "command/exit"
+require_relative "command/force_exit"
+require_relative "command/chws"
+require_relative "command/pushws"
+require_relative "command/subirb"
+require_relative "command/load"
+require_relative "command/debug"
+require_relative "command/edit"
+require_relative "command/break"
+require_relative "command/catch"
+require_relative "command/next"
+require_relative "command/delete"
+require_relative "command/step"
+require_relative "command/continue"
+require_relative "command/finish"
+require_relative "command/backtrace"
+require_relative "command/info"
+require_relative "command/help"
+require_relative "command/show_doc"
+require_relative "command/irb_info"
+require_relative "command/ls"
+require_relative "command/measure"
+require_relative "command/show_source"
+require_relative "command/whereami"
+require_relative "command/history"
+
+module IRB
+ module Command
+ NO_OVERRIDE = 0
+ OVERRIDE_PRIVATE_ONLY = 0x01
+ OVERRIDE_ALL = 0x02
+
+ class << self
+ # This API is for IRB's internal use only and may change at any time.
+ # Please do NOT use it.
+ def _register_with_aliases(name, command_class, *aliases)
+ @commands[name.to_sym] = [command_class, aliases]
+ end
+
+ def all_commands_info
+ user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result|
+ result[target] ||= []
+ result[target] << alias_name
+ end
+
+ commands.map do |command_name, (command_class, aliases)|
+ aliases = aliases.map { |a| a.first }
+
+ if additional_aliases = user_aliases[command_name]
+ aliases += additional_aliases
+ end
+
+ display_name = aliases.shift || command_name
+ {
+ display_name: display_name,
+ description: command_class.description,
+ category: command_class.category
+ }
+ end
+ end
+
+ def command_override_policies
+ @@command_override_policies ||= commands.flat_map do |cmd_name, (cmd_class, aliases)|
+ [[cmd_name, OVERRIDE_ALL]] + aliases
+ end.to_h
+ end
+
+ def execute_as_command?(name, public_method:, private_method:)
+ case command_override_policies[name]
+ when OVERRIDE_ALL
+ true
+ when OVERRIDE_PRIVATE_ONLY
+ !public_method
+ when NO_OVERRIDE
+ !public_method && !private_method
+ end
+ end
+
+ def command_names
+ command_override_policies.keys.map(&:to_s)
+ end
+
+ # Convert a command name to its implementation class if such command exists
+ def load_command(command)
+ command = command.to_sym
+ commands.each do |command_name, (command_class, aliases)|
+ if command_name == command || aliases.any? { |alias_name, _| alias_name == command }
+ return command_class
+ end
+ end
+ nil
+ end
+ end
+
+ _register_with_aliases(:irb_context, Command::Context,
+ [:context, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_exit, Command::Exit,
+ [:exit, OVERRIDE_PRIVATE_ONLY],
+ [:quit, OVERRIDE_PRIVATE_ONLY],
+ [:irb_quit, OVERRIDE_PRIVATE_ONLY]
+ )
+
+ _register_with_aliases(:irb_exit!, Command::ForceExit,
+ [:exit!, OVERRIDE_PRIVATE_ONLY]
+ )
+
+ _register_with_aliases(:irb_current_working_workspace, Command::CurrentWorkingWorkspace,
+ [:cwws, NO_OVERRIDE],
+ [:pwws, NO_OVERRIDE],
+ [:irb_print_working_workspace, OVERRIDE_ALL],
+ [:irb_cwws, OVERRIDE_ALL],
+ [:irb_pwws, OVERRIDE_ALL],
+ [:irb_current_working_binding, OVERRIDE_ALL],
+ [:irb_print_working_binding, OVERRIDE_ALL],
+ [:irb_cwb, OVERRIDE_ALL],
+ [:irb_pwb, OVERRIDE_ALL],
+ )
+
+ _register_with_aliases(:irb_change_workspace, Command::ChangeWorkspace,
+ [:chws, NO_OVERRIDE],
+ [:cws, NO_OVERRIDE],
+ [:irb_chws, OVERRIDE_ALL],
+ [:irb_cws, OVERRIDE_ALL],
+ [:irb_change_binding, OVERRIDE_ALL],
+ [:irb_cb, OVERRIDE_ALL],
+ [:cb, NO_OVERRIDE],
+ )
+
+ _register_with_aliases(:irb_workspaces, Command::Workspaces,
+ [:workspaces, NO_OVERRIDE],
+ [:irb_bindings, OVERRIDE_ALL],
+ [:bindings, NO_OVERRIDE],
+ )
+
+ _register_with_aliases(:irb_push_workspace, Command::PushWorkspace,
+ [:pushws, NO_OVERRIDE],
+ [:irb_pushws, OVERRIDE_ALL],
+ [:irb_push_binding, OVERRIDE_ALL],
+ [:irb_pushb, OVERRIDE_ALL],
+ [:pushb, NO_OVERRIDE],
+ )
+
+ _register_with_aliases(:irb_pop_workspace, Command::PopWorkspace,
+ [:popws, NO_OVERRIDE],
+ [:irb_popws, OVERRIDE_ALL],
+ [:irb_pop_binding, OVERRIDE_ALL],
+ [:irb_popb, OVERRIDE_ALL],
+ [:popb, NO_OVERRIDE],
+ )
+
+ _register_with_aliases(:irb_load, Command::Load)
+ _register_with_aliases(:irb_require, Command::Require)
+ _register_with_aliases(:irb_source, Command::Source,
+ [:source, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb, Command::IrbCommand)
+ _register_with_aliases(:irb_jobs, Command::Jobs,
+ [:jobs, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_fg, Command::Foreground,
+ [:fg, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_kill, Command::Kill,
+ [:kill, OVERRIDE_PRIVATE_ONLY]
+ )
+
+ _register_with_aliases(:irb_debug, Command::Debug,
+ [:debug, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_edit, Command::Edit,
+ [:edit, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_break, Command::Break)
+ _register_with_aliases(:irb_catch, Command::Catch)
+ _register_with_aliases(:irb_next, Command::Next)
+ _register_with_aliases(:irb_delete, Command::Delete,
+ [:delete, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_step, Command::Step,
+ [:step, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_continue, Command::Continue,
+ [:continue, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_finish, Command::Finish,
+ [:finish, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_backtrace, Command::Backtrace,
+ [:backtrace, NO_OVERRIDE],
+ [:bt, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_debug_info, Command::Info,
+ [:info, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_help, Command::Help,
+ [:help, NO_OVERRIDE],
+ [:show_cmds, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_show_doc, Command::ShowDoc,
+ [:show_doc, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_info, Command::IrbInfo)
+
+ _register_with_aliases(:irb_ls, Command::Ls,
+ [:ls, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_measure, Command::Measure,
+ [:measure, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_show_source, Command::ShowSource,
+ [:show_source, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_whereami, Command::Whereami,
+ [:whereami, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_history, Command::History,
+ [:history, NO_OVERRIDE],
+ [:hist, NO_OVERRIDE]
+ )
+ end
+
+ ExtendCommand = Command
+
+ # For backward compatibility, we need to keep this module:
+ # - As a container of helper methods
+ # - As a place to register commands with the deprecated def_extend_command method
+ module ExtendCommandBundle
+ # For backward compatibility
+ NO_OVERRIDE = Command::NO_OVERRIDE
+ OVERRIDE_PRIVATE_ONLY = Command::OVERRIDE_PRIVATE_ONLY
+ OVERRIDE_ALL = Command::OVERRIDE_ALL
+
+ # Deprecated. Doesn't have any effect.
+ @EXTEND_COMMANDS = []
+
+ # Drepcated. Use Command.regiser instead.
+ def self.def_extend_command(cmd_name, cmd_class, _, *aliases)
+ Command._register_with_aliases(cmd_name, cmd_class, *aliases)
+ Command.class_variable_set(:@@command_override_policies, nil)
+ end
+ end
+end
diff --git a/lib/irb/ext/change-ws.rb b/lib/irb/ext/change-ws.rb
index c0f810a4c8..60e8afe31f 100644
--- a/lib/irb/ext/change-ws.rb
+++ b/lib/irb/ext/change-ws.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/ext/cb.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -12,7 +12,7 @@ module IRB # :nodoc:
if defined? @home_workspace
@home_workspace
else
- @home_workspace = @workspace
+ @home_workspace = workspace
end
end
@@ -25,15 +25,13 @@ module IRB # :nodoc:
# See IRB::WorkSpace.new for more information.
def change_workspace(*_main)
if _main.empty?
- @workspace = home_workspace
+ replace_workspace(home_workspace)
return main
end
- @workspace = WorkSpace.new(_main[0])
-
- if !(class<<main;ancestors;end).include?(ExtendCommandBundle)
- main.extend ExtendCommandBundle
- end
+ workspace = WorkSpace.new(_main[0])
+ replace_workspace(workspace)
+ workspace.load_helper_methods_to_main
end
end
end
diff --git a/lib/irb/ext/eval_history.rb b/lib/irb/ext/eval_history.rb
index 1a04178b40..6c21ff00ee 100644
--- a/lib/irb/ext/eval_history.rb
+++ b/lib/irb/ext/eval_history.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# history.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -18,7 +18,7 @@ module IRB # :nodoc:
if defined?(@eval_history) && @eval_history
@eval_history_values.push @line_no, @last_value
- @workspace.evaluate "__ = IRB.CurrentContext.instance_eval{@eval_history_values}"
+ workspace.evaluate "__ = IRB.CurrentContext.instance_eval{@eval_history_values}"
end
@last_value
@@ -49,7 +49,7 @@ module IRB # :nodoc:
else
@eval_history_values = EvalHistory.new(no)
IRB.conf[:__TMP__EHV__] = @eval_history_values
- @workspace.evaluate("__ = IRB.conf[:__TMP__EHV__]")
+ workspace.evaluate("__ = IRB.conf[:__TMP__EHV__]")
IRB.conf.delete(:__TMP_EHV__)
end
else
diff --git a/lib/irb/ext/loader.rb b/lib/irb/ext/loader.rb
index d65695df3b..df5aaa8e5a 100644
--- a/lib/irb/ext/loader.rb
+++ b/lib/irb/ext/loader.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# loader.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -98,13 +98,13 @@ module IRB # :nodoc:
def old # :nodoc:
back_io = @io
- back_path = @irb_path
+ back_path = irb_path
back_name = @irb_name
back_scanner = @irb.scanner
begin
@io = FileInputMethod.new(path)
@irb_name = File.basename(path)
- @irb_path = path
+ self.irb_path = path
@irb.signal_status(:IN_LOAD) do
if back_io.kind_of?(FileInputMethod)
@irb.eval_input
@@ -119,7 +119,7 @@ module IRB # :nodoc:
ensure
@io = back_io
@irb_name = back_name
- @irb_path = back_path
+ self.irb_path = back_path
@irb.scanner = back_scanner
end
end
diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb
index 1c20489137..9f234f0cdc 100644
--- a/lib/irb/ext/multi-irb.rb
+++ b/lib/irb/ext/multi-irb.rb
@@ -1,11 +1,11 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/multi-irb.rb - multiple irb module
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
module IRB
- class JobManager
+ class JobManager # :nodoc:
# Creates a new JobManager object
def initialize
@@ -166,12 +166,12 @@ module IRB
@JobManager = JobManager.new
# The current JobManager in the session
- def IRB.JobManager
+ def IRB.JobManager # :nodoc:
@JobManager
end
# The current Context in this session
- def IRB.CurrentContext
+ def IRB.CurrentContext # :nodoc:
IRB.JobManager.irb(Thread.current).context
end
@@ -179,7 +179,7 @@ module IRB
#
# The optional +file+ argument is given to Context.new, along with the
# workspace created with the remaining arguments, see WorkSpace.new
- def IRB.irb(file = nil, *main)
+ def IRB.irb(file = nil, *main) # :nodoc:
workspace = WorkSpace.new(*main)
parent_thread = Thread.current
Thread.start do
diff --git a/lib/irb/ext/tracer.rb b/lib/irb/ext/tracer.rb
index 3eaeb70ef2..fd6daa88ae 100644
--- a/lib/irb/ext/tracer.rb
+++ b/lib/irb/ext/tracer.rb
@@ -1,78 +1,39 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/lib/tracer.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
-
+# Loading the gem "tracer" will cause it to extend IRB commands with:
+# https://github.com/ruby/tracer/blob/v0.2.2/lib/tracer/irb.rb
begin
require "tracer"
rescue LoadError
$stderr.puts "Tracer extension of IRB is enabled but tracer gem wasn't found."
- module IRB
- class Context
- def use_tracer=(opt)
- # do nothing
- end
- end
- end
return # This is about to disable loading below
end
module IRB
+ class CallTracer < ::CallTracer
+ IRB_DIR = File.expand_path('../..', __dir__)
- # initialize tracing function
- def IRB.initialize_tracer
- Tracer.verbose = false
- Tracer.add_filter {
- |event, file, line, id, binding, *rests|
- /^#{Regexp.quote(@CONF[:IRB_LIB_PATH])}/ !~ file and
- File::basename(file) != "irb.rb"
- }
- end
-
- class Context
- # Whether Tracer is used when evaluating statements in this context.
- #
- # See +lib/tracer.rb+ for more information.
- attr_reader :use_tracer
- alias use_tracer? use_tracer
-
- # Sets whether or not to use the Tracer library when evaluating statements
- # in this context.
- #
- # See +lib/tracer.rb+ for more information.
- def use_tracer=(opt)
- if opt
- Tracer.set_get_line_procs(@irb_path) {
- |line_no, *rests|
- @io.line(line_no)
- }
- elsif !opt && @use_tracer
- Tracer.off
- end
- @use_tracer=opt
+ def skip?(tp)
+ super || tp.path.match?(IRB_DIR) || tp.path.match?('<internal:prelude>')
end
end
-
class WorkSpace
alias __evaluate__ evaluate
# Evaluate the context of this workspace and use the Tracer library to
# output the exact lines of code are being executed in chronological order.
#
- # See +lib/tracer.rb+ for more information.
- def evaluate(context, statements, file = nil, line = nil)
- if context.use_tracer? && file != nil && line != nil
- Tracer.on
- begin
+ # See https://github.com/ruby/tracer for more information.
+ def evaluate(statements, file = __FILE__, line = __LINE__)
+ if IRB.conf[:USE_TRACER] == true
+ CallTracer.new(colorize: Color.colorable?).start do
__evaluate__(statements, file, line)
- ensure
- Tracer.off
end
else
- __evaluate__(statements, file || __FILE__, line || __LINE__)
+ __evaluate__(statements, file, line)
end
end
end
-
- IRB.initialize_tracer
end
diff --git a/lib/irb/ext/use-loader.rb b/lib/irb/ext/use-loader.rb
index d0b8c2d4f4..c8a3ea1fe8 100644
--- a/lib/irb/ext/use-loader.rb
+++ b/lib/irb/ext/use-loader.rb
@@ -1,10 +1,10 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# use-loader.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
-require_relative "../cmd/load"
+require_relative "../command/load"
require_relative "loader"
class Object
@@ -17,12 +17,12 @@ module IRB
remove_method :irb_load if method_defined?(:irb_load)
# Loads the given file similarly to Kernel#load, see IrbLoader#irb_load
def irb_load(*opts, &b)
- ExtendCommand::Load.execute(irb_context, *opts, &b)
+ Command::Load.execute(irb_context, *opts, &b)
end
remove_method :irb_require if method_defined?(:irb_require)
# Loads the given file similarly to Kernel#require
def irb_require(*opts, &b)
- ExtendCommand::Require.execute(irb_context, *opts, &b)
+ Command::Require.execute(irb_context, *opts, &b)
end
end
@@ -49,14 +49,12 @@ module IRB
if IRB.conf[:USE_LOADER] != opt
IRB.conf[:USE_LOADER] = opt
if opt
- if !$".include?("irb/cmd/load")
- end
- (class<<@workspace.main;self;end).instance_eval {
+ (class<<workspace.main;self;end).instance_eval {
alias_method :load, :irb_load
alias_method :require, :irb_require
}
else
- (class<<@workspace.main;self;end).instance_eval {
+ (class<<workspace.main;self;end).instance_eval {
alias_method :load, :__original__load__IRB_use_loader__
alias_method :require, :__original__require__IRB_use_loader__
}
diff --git a/lib/irb/ext/workspaces.rb b/lib/irb/ext/workspaces.rb
index 9defc3e17b..da09faa83e 100644
--- a/lib/irb/ext/workspaces.rb
+++ b/lib/irb/ext/workspaces.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# push-ws.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -6,21 +6,6 @@
module IRB # :nodoc:
class Context
-
- # Size of the current WorkSpace stack
- def irb_level
- workspace_stack.size
- end
-
- # WorkSpaces in the current stack
- def workspaces
- if defined? @workspaces
- @workspaces
- else
- @workspaces = []
- end
- end
-
# Creates a new workspace with the given object or binding, and appends it
# onto the current #workspaces stack.
#
@@ -28,20 +13,15 @@ module IRB # :nodoc:
# information.
def push_workspace(*_main)
if _main.empty?
- if workspaces.empty?
- print "No other workspace\n"
- return nil
+ if @workspace_stack.size > 1
+ # swap the top two workspaces
+ previous_workspace, current_workspace = @workspace_stack.pop(2)
+ @workspace_stack.push current_workspace, previous_workspace
end
- ws = workspaces.pop
- workspaces.push @workspace
- @workspace = ws
- return workspaces
- end
-
- workspaces.push @workspace
- @workspace = WorkSpace.new(@workspace.binding, _main[0])
- if !(class<<main;ancestors;end).include?(ExtendCommandBundle)
- main.extend ExtendCommandBundle
+ else
+ new_workspace = WorkSpace.new(workspace.binding, _main[0])
+ @workspace_stack.push new_workspace
+ new_workspace.load_helper_methods_to_main
end
end
@@ -50,11 +30,7 @@ module IRB # :nodoc:
#
# Also, see #push_workspace.
def pop_workspace
- if workspaces.empty?
- print "workspace stack empty\n"
- return
- end
- @workspace = workspaces.pop
+ @workspace_stack.pop if @workspace_stack.size > 1
end
end
end
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
deleted file mode 100644
index 072069d4c4..0000000000
--- a/lib/irb/extend-command.rb
+++ /dev/null
@@ -1,360 +0,0 @@
-# frozen_string_literal: false
-#
-# irb/extend-command.rb - irb extend command
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-module IRB # :nodoc:
- # Installs the default irb extensions command bundle.
- module ExtendCommandBundle
- EXCB = ExtendCommandBundle # :nodoc:
-
- # See #install_alias_method.
- NO_OVERRIDE = 0
- # See #install_alias_method.
- OVERRIDE_PRIVATE_ONLY = 0x01
- # See #install_alias_method.
- OVERRIDE_ALL = 0x02
-
- # Quits the current irb context
- #
- # +ret+ is the optional signal or message to send to Context#exit
- #
- # Same as <code>IRB.CurrentContext.exit</code>.
- def irb_exit(ret = 0)
- irb_context.exit(ret)
- end
-
- # Displays current configuration.
- #
- # Modifying the configuration is achieved by sending a message to IRB.conf.
- def irb_context
- IRB.CurrentContext
- end
-
- @ALIASES = [
- [:context, :irb_context, NO_OVERRIDE],
- [:conf, :irb_context, NO_OVERRIDE],
- [:irb_quit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
- [:exit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
- [:quit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
- ]
-
-
- @EXTEND_COMMANDS = [
- [
- :irb_current_working_workspace, :CurrentWorkingWorkspace, "cmd/chws",
- [:cwws, NO_OVERRIDE],
- [:pwws, NO_OVERRIDE],
- [:irb_print_working_workspace, OVERRIDE_ALL],
- [:irb_cwws, OVERRIDE_ALL],
- [:irb_pwws, OVERRIDE_ALL],
- [:irb_current_working_binding, OVERRIDE_ALL],
- [:irb_print_working_binding, OVERRIDE_ALL],
- [:irb_cwb, OVERRIDE_ALL],
- [:irb_pwb, OVERRIDE_ALL],
- ],
- [
- :irb_change_workspace, :ChangeWorkspace, "cmd/chws",
- [:chws, NO_OVERRIDE],
- [:cws, NO_OVERRIDE],
- [:irb_chws, OVERRIDE_ALL],
- [:irb_cws, OVERRIDE_ALL],
- [:irb_change_binding, OVERRIDE_ALL],
- [:irb_cb, OVERRIDE_ALL],
- [:cb, NO_OVERRIDE],
- ],
-
- [
- :irb_workspaces, :Workspaces, "cmd/pushws",
- [:workspaces, NO_OVERRIDE],
- [:irb_bindings, OVERRIDE_ALL],
- [:bindings, NO_OVERRIDE],
- ],
- [
- :irb_push_workspace, :PushWorkspace, "cmd/pushws",
- [:pushws, NO_OVERRIDE],
- [:irb_pushws, OVERRIDE_ALL],
- [:irb_push_binding, OVERRIDE_ALL],
- [:irb_pushb, OVERRIDE_ALL],
- [:pushb, NO_OVERRIDE],
- ],
- [
- :irb_pop_workspace, :PopWorkspace, "cmd/pushws",
- [:popws, NO_OVERRIDE],
- [:irb_popws, OVERRIDE_ALL],
- [:irb_pop_binding, OVERRIDE_ALL],
- [:irb_popb, OVERRIDE_ALL],
- [:popb, NO_OVERRIDE],
- ],
-
- [
- :irb_load, :Load, "cmd/load"],
- [
- :irb_require, :Require, "cmd/load"],
- [
- :irb_source, :Source, "cmd/load",
- [:source, NO_OVERRIDE],
- ],
-
- [
- :irb, :IrbCommand, "cmd/subirb"],
- [
- :irb_jobs, :Jobs, "cmd/subirb",
- [:jobs, NO_OVERRIDE],
- ],
- [
- :irb_fg, :Foreground, "cmd/subirb",
- [:fg, NO_OVERRIDE],
- ],
- [
- :irb_kill, :Kill, "cmd/subirb",
- [:kill, OVERRIDE_PRIVATE_ONLY],
- ],
-
- [
- :irb_debug, :Debug, "cmd/debug",
- [:debug, NO_OVERRIDE],
- ],
- [
- :irb_edit, :Edit, "cmd/edit",
- [:edit, NO_OVERRIDE],
- ],
- [
- :irb_break, :Break, "cmd/break",
- ],
- [
- :irb_catch, :Catch, "cmd/catch",
- ],
- [
- :irb_next, :Next, "cmd/next"
- ],
- [
- :irb_delete, :Delete, "cmd/delete",
- [:delete, NO_OVERRIDE],
- ],
- [
- :irb_step, :Step, "cmd/step",
- [:step, NO_OVERRIDE],
- ],
- [
- :irb_continue, :Continue, "cmd/continue",
- [:continue, NO_OVERRIDE],
- ],
- [
- :irb_finish, :Finish, "cmd/finish",
- [:finish, NO_OVERRIDE],
- ],
- [
- :irb_backtrace, :Backtrace, "cmd/backtrace",
- [:backtrace, NO_OVERRIDE],
- [:bt, NO_OVERRIDE],
- ],
- [
- :irb_debug_info, :Info, "cmd/info",
- [:info, NO_OVERRIDE],
- ],
-
- [
- :irb_help, :Help, "cmd/help",
- [:help, NO_OVERRIDE],
- ],
-
- [
- :irb_show_doc, :ShowDoc, "cmd/show_doc",
- [:show_doc, NO_OVERRIDE],
- ],
-
- [
- :irb_info, :IrbInfo, "cmd/irb_info"
- ],
-
- [
- :irb_ls, :Ls, "cmd/ls",
- [:ls, NO_OVERRIDE],
- ],
-
- [
- :irb_measure, :Measure, "cmd/measure",
- [:measure, NO_OVERRIDE],
- ],
-
- [
- :irb_show_source, :ShowSource, "cmd/show_source",
- [:show_source, NO_OVERRIDE],
- ],
-
- [
- :irb_whereami, :Whereami, "cmd/whereami",
- [:whereami, NO_OVERRIDE],
- ],
- [
- :irb_show_cmds, :ShowCmds, "cmd/show_cmds",
- [:show_cmds, NO_OVERRIDE],
- ],
-
- [
- :irb_history, :History, "cmd/history",
- [:history, NO_OVERRIDE],
- [:hist, NO_OVERRIDE],
- ]
- ]
-
-
- @@commands = []
-
- def self.all_commands_info
- return @@commands unless @@commands.empty?
- user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result|
- result[target] ||= []
- result[target] << alias_name
- end
-
- @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
- if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
- require_relative load_file
- end
-
- klass = ExtendCommand.const_get(cmd_class, false)
- aliases = aliases.map { |a| a.first }
-
- if additional_aliases = user_aliases[cmd_name]
- aliases += additional_aliases
- end
-
- display_name = aliases.shift || cmd_name
- @@commands << { display_name: display_name, description: klass.description, category: klass.category }
- end
-
- @@commands
- end
-
- # Convert a command name to its implementation class if such command exists
- def self.load_command(command)
- command = command.to_sym
- @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
- next if cmd_name != command && aliases.all? { |alias_name, _| alias_name != command }
-
- if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
- require_relative load_file
- end
- return ExtendCommand.const_get(cmd_class, false)
- end
- nil
- end
-
- # Installs the default irb commands.
- def self.install_extend_commands
- for args in @EXTEND_COMMANDS
- def_extend_command(*args)
- end
- end
-
- # Evaluate the given +cmd_name+ on the given +cmd_class+ Class.
- #
- # Will also define any given +aliases+ for the method.
- #
- # The optional +load_file+ parameter will be required within the method
- # definition.
- def self.def_extend_command(cmd_name, cmd_class, load_file, *aliases)
- case cmd_class
- when Symbol
- cmd_class = cmd_class.id2name
- when String
- when Class
- cmd_class = cmd_class.name
- end
-
- line = __LINE__; eval %[
- def #{cmd_name}(*opts, **kwargs, &b)
- Kernel.require_relative "#{load_file}"
- ::IRB::ExtendCommand::#{cmd_class}.execute(irb_context, *opts, **kwargs, &b)
- end
- ], nil, __FILE__, line
-
- for ali, flag in aliases
- @ALIASES.push [ali, cmd_name, flag]
- end
- end
-
- # Installs alias methods for the default irb commands, see
- # ::install_extend_commands.
- def install_alias_method(to, from, override = NO_OVERRIDE)
- to = to.id2name unless to.kind_of?(String)
- from = from.id2name unless from.kind_of?(String)
-
- if override == OVERRIDE_ALL or
- (override == OVERRIDE_PRIVATE_ONLY) && !respond_to?(to) or
- (override == NO_OVERRIDE) && !respond_to?(to, true)
- target = self
- (class << self; self; end).instance_eval{
- if target.respond_to?(to, true) &&
- !target.respond_to?(EXCB.irb_original_method_name(to), true)
- alias_method(EXCB.irb_original_method_name(to), to)
- end
- alias_method to, from
- }
- else
- Kernel.warn "irb: warn: can't alias #{to} from #{from}.\n"
- end
- end
-
- def self.irb_original_method_name(method_name) # :nodoc:
- "irb_" + method_name + "_org"
- end
-
- # Installs alias methods for the default irb commands on the given object
- # using #install_alias_method.
- def self.extend_object(obj)
- unless (class << obj; ancestors; end).include?(EXCB)
- super
- for ali, com, flg in @ALIASES
- obj.install_alias_method(ali, com, flg)
- end
- end
- end
-
- install_extend_commands
- end
-
- # Extends methods for the Context module
- module ContextExtender
- CE = ContextExtender # :nodoc:
-
- @EXTEND_COMMANDS = [
- [:eval_history=, "ext/eval_history.rb"],
- [:use_tracer=, "ext/tracer.rb"],
- [:use_loader=, "ext/use-loader.rb"],
- ]
-
- # Installs the default context extensions as irb commands:
- #
- # Context#eval_history=:: +irb/ext/history.rb+
- # Context#use_tracer=:: +irb/ext/tracer.rb+
- # Context#use_loader=:: +irb/ext/use-loader.rb+
- def self.install_extend_commands
- for args in @EXTEND_COMMANDS
- def_extend_command(*args)
- end
- end
-
- # Evaluate the given +command+ from the given +load_file+ on the Context
- # module.
- #
- # Will also define any given +aliases+ for the method.
- def self.def_extend_command(cmd_name, load_file, *aliases)
- line = __LINE__; Context.module_eval %[
- def #{cmd_name}(*opts, &b)
- Context.module_eval {remove_method(:#{cmd_name})}
- require_relative "#{load_file}"
- __send__ :#{cmd_name}, *opts, &b
- end
- for ali in aliases
- alias_method ali, cmd_name
- end
- ], __FILE__, line
- end
-
- CE.install_extend_commands
- end
-end
diff --git a/lib/irb/frame.rb b/lib/irb/frame.rb
index 14768bd8f6..4b697c8719 100644
--- a/lib/irb/frame.rb
+++ b/lib/irb/frame.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# frame.rb -
# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
diff --git a/lib/irb/help.rb b/lib/irb/help.rb
index 6861d7efc8..a24bc10a15 100644
--- a/lib/irb/help.rb
+++ b/lib/irb/help.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/help.rb - print usage module
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
@@ -6,7 +6,7 @@
module IRB
# Outputs the irb help message, see IRB@Command-Line+Options.
- def IRB.print_usage
+ def IRB.print_usage # :nodoc:
lc = IRB.conf[:LC_MESSAGES]
path = lc.find("irb/help-message")
space_line = false
diff --git a/lib/irb/helper_method.rb b/lib/irb/helper_method.rb
new file mode 100644
index 0000000000..f1f6fff915
--- /dev/null
+++ b/lib/irb/helper_method.rb
@@ -0,0 +1,29 @@
+require_relative "helper_method/base"
+
+module IRB
+ module HelperMethod
+ @helper_methods = {}
+
+ class << self
+ attr_reader :helper_methods
+
+ def register(name, helper_class)
+ @helper_methods[name] = helper_class
+
+ if defined?(HelpersContainer)
+ HelpersContainer.install_helper_methods
+ end
+ end
+
+ def all_helper_methods_info
+ @helper_methods.map do |name, helper_class|
+ { display_name: name, description: helper_class.description }
+ end
+ end
+ end
+
+ # Default helper_methods
+ require_relative "helper_method/conf"
+ register(:conf, HelperMethod::Conf)
+ end
+end
diff --git a/lib/irb/helper_method/base.rb b/lib/irb/helper_method/base.rb
new file mode 100644
index 0000000000..a68001ed28
--- /dev/null
+++ b/lib/irb/helper_method/base.rb
@@ -0,0 +1,16 @@
+require "singleton"
+
+module IRB
+ module HelperMethod
+ class Base
+ include Singleton
+
+ class << self
+ def description(description = nil)
+ @description = description if description
+ @description
+ end
+ end
+ end
+ end
+end
diff --git a/lib/irb/helper_method/conf.rb b/lib/irb/helper_method/conf.rb
new file mode 100644
index 0000000000..718ed279c0
--- /dev/null
+++ b/lib/irb/helper_method/conf.rb
@@ -0,0 +1,11 @@
+module IRB
+ module HelperMethod
+ class Conf < Base
+ description "Returns the current IRB context."
+
+ def execute
+ IRB.CurrentContext
+ end
+ end
+ end
+end
diff --git a/lib/irb/history.rb b/lib/irb/history.rb
index 06088adb0d..685354b2d8 100644
--- a/lib/irb/history.rb
+++ b/lib/irb/history.rb
@@ -1,3 +1,5 @@
+require "pathname"
+
module IRB
module HistorySavingAbility # :nodoc:
def support_history_saving?
@@ -5,7 +7,7 @@ module IRB
end
def reset_history_counter
- @loaded_history_lines = self.class::HISTORY.size if defined? @loaded_history_lines
+ @loaded_history_lines = self.class::HISTORY.size
end
def load_history
@@ -15,7 +17,7 @@ module IRB
history_file = File.expand_path(history_file)
end
history_file = IRB.rc_file("_history") unless history_file
- if File.exist?(history_file)
+ if history_file && File.exist?(history_file)
File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
f.each { |l|
l = l.chomp
@@ -41,6 +43,9 @@ module IRB
end
history_file = IRB.rc_file("_history") unless history_file
+ # When HOME and XDG_CONFIG_HOME are not available, history_file might be nil
+ return unless history_file
+
# Change the permission of a file that already exists[BUG #7694]
begin
if File.stat(history_file).mode & 066 != 0
@@ -59,13 +64,19 @@ module IRB
append_history = true
end
+ pathname = Pathname.new(history_file)
+ unless Dir.exist?(pathname.dirname)
+ warn "Warning: The directory to save IRB's history file does not exist. Please double check `IRB.conf[:HISTORY_FILE]`'s value."
+ return
+ end
+
File.open(history_file, (append_history ? 'a' : 'w'), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
hist = history.map{ |l| l.scrub.split("\n").join("\\\n") }
unless append_history
begin
hist = hist.last(num) if hist.size > num and num > 0
rescue RangeError # bignum too big to convert into `long'
- # Do nothing because the bignum should be treated as inifinity
+ # Do nothing because the bignum should be treated as infinity
end
end
f.puts(hist)
diff --git a/lib/irb/init.rb b/lib/irb/init.rb
index 66e7b61468..355047519c 100644
--- a/lib/irb/init.rb
+++ b/lib/irb/init.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/init.rb - irb initialize module
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -6,6 +6,7 @@
module IRB # :nodoc:
@CONF = {}
+ @INITIALIZED = false
# Displays current configuration.
#
# Modifying the configuration is achieved by sending a message to IRB.conf.
@@ -41,6 +42,10 @@ module IRB # :nodoc:
format("irb %s (%s)", @RELEASE_VERSION, @LAST_UPDATE_DATE)
end
+ def IRB.initialized?
+ !!@INITIALIZED
+ end
+
# initialize config
def IRB.setup(ap_path, argv: ::ARGV)
IRB.init_config(ap_path)
@@ -52,13 +57,11 @@ module IRB # :nodoc:
unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]]
fail UndefinedPromptMode, @CONF[:PROMPT_MODE]
end
+ @INITIALIZED = true
end
# @CONF default setting
def IRB.init_config(ap_path)
- # class instance variables
- @TRACER_INITIALIZED = false
-
# default configurations
unless ap_path and @CONF[:AP_NAME]
ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb")
@@ -392,56 +395,36 @@ module IRB # :nodoc:
# Run the config file
def IRB.run_config
if @CONF[:RC]
- begin
- file = rc_file
- # Because rc_file always returns `HOME/.irbrc` even if no rc file is present, we can't warn users about missing rc files.
- # Otherwise, it'd be very noisy.
- load file if File.exist?(file)
+ irbrc_files.each do |rc|
+ load rc
rescue StandardError, ScriptError => e
- warn "Error loading RC file '#{file}':\n#{e.full_message(highlight: false)}"
+ warn "Error loading RC file '#{rc}':\n#{e.full_message(highlight: false)}"
end
end
end
IRBRC_EXT = "rc"
- def IRB.rc_file(ext = IRBRC_EXT)
- if !@CONF[:RC_NAME_GENERATOR]
- rc_file_generators do |rcgen|
- @CONF[:RC_NAME_GENERATOR] ||= rcgen
- if File.exist?(rcgen.call(IRBRC_EXT))
- @CONF[:RC_NAME_GENERATOR] = rcgen
- break
- end
- end
+
+ def IRB.rc_file(ext)
+ prepare_irbrc_name_generators
+
+ # When irbrc exist in default location
+ if (rcgen = @existing_rc_name_generators.first)
+ return rcgen.call(ext)
end
- case rc_file = @CONF[:RC_NAME_GENERATOR].call(ext)
- when String
- rc_file
- else
- fail IllegalRCNameGenerator
+
+ # When irbrc does not exist in default location
+ rc_file_generators do |rcgen|
+ return rcgen.call(ext)
end
+
+ # When HOME and XDG_CONFIG_HOME are not available
+ nil
end
- # enumerate possible rc-file base name generators
- def IRB.rc_file_generators
- if irbrc = ENV["IRBRC"]
- yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc}
- end
- if xdg_config_home = ENV["XDG_CONFIG_HOME"]
- irb_home = File.join(xdg_config_home, "irb")
- if File.directory?(irb_home)
- yield proc{|rc| irb_home + "/irb#{rc}"}
- end
- end
- if home = ENV["HOME"]
- yield proc{|rc| home+"/.irb#{rc}"}
- yield proc{|rc| home+"/.config/irb/irb#{rc}"}
- end
- current_dir = Dir.pwd
- yield proc{|rc| current_dir+"/.irb#{rc}"}
- yield proc{|rc| current_dir+"/irb#{rc.sub(/\A_?/, '.')}"}
- yield proc{|rc| current_dir+"/_irb#{rc}"}
- yield proc{|rc| current_dir+"/$irb#{rc}"}
+ def IRB.irbrc_files
+ prepare_irbrc_name_generators
+ @irbrc_files
end
# loading modules
@@ -457,6 +440,50 @@ module IRB # :nodoc:
class << IRB
private
+
+ def prepare_irbrc_name_generators
+ return if @existing_rc_name_generators
+
+ @existing_rc_name_generators = []
+ @irbrc_files = []
+ rc_file_generators do |rcgen|
+ irbrc = rcgen.call(IRBRC_EXT)
+ if File.exist?(irbrc)
+ @irbrc_files << irbrc
+ @existing_rc_name_generators << rcgen
+ end
+ end
+ generate_current_dir_irbrc_files.each do |irbrc|
+ @irbrc_files << irbrc if File.exist?(irbrc)
+ end
+ @irbrc_files.uniq!
+ end
+
+ # enumerate possible rc-file base name generators
+ def rc_file_generators
+ if irbrc = ENV["IRBRC"]
+ yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc}
+ end
+ if xdg_config_home = ENV["XDG_CONFIG_HOME"]
+ irb_home = File.join(xdg_config_home, "irb")
+ if File.directory?(irb_home)
+ yield proc{|rc| irb_home + "/irb#{rc}"}
+ end
+ end
+ if home = ENV["HOME"]
+ yield proc{|rc| home+"/.irb#{rc}"}
+ if xdg_config_home.nil? || xdg_config_home.empty?
+ yield proc{|rc| home+"/.config/irb/irb#{rc}"}
+ end
+ end
+ end
+
+ # possible irbrc files in current directory
+ def generate_current_dir_irbrc_files
+ current_dir = Dir.pwd
+ %w[.irbrc irbrc _irbrc $irbrc].map { |file| "#{current_dir}/#{file}" }
+ end
+
def set_encoding(extern, intern = nil, override: true)
verbose, $VERBOSE = $VERBOSE, nil
Encoding.default_external = extern unless extern.nil? || extern.empty?
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index b74974b925..684527edc4 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/input-method.rb - input methods used irb
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -20,7 +20,7 @@ module IRB
#
# See IO#gets for more information.
def gets
- fail NotImplementedError, "gets"
+ fail NotImplementedError
end
public :gets
@@ -44,6 +44,10 @@ module IRB
false
end
+ def prompting?
+ false
+ end
+
# For debug message
def inspect
'Abstract InputMethod'
@@ -63,6 +67,7 @@ module IRB
#
# See IO#gets for more information.
def gets
+ puts if @stdout.tty? # workaround for debug compatibility test
print @prompt
line = @stdin.gets
@line[@line_no += 1] = line
@@ -91,6 +96,10 @@ module IRB
true
end
+ def prompting?
+ STDIN.tty?
+ end
+
# Returns the current line number for #io.
#
# #line counts the number of times #gets is called.
@@ -220,6 +229,10 @@ module IRB
@eof
end
+ def prompting?
+ true
+ end
+
# For debug message
def inspect
readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline'
@@ -291,11 +304,27 @@ module IRB
@auto_indent_proc = block
end
+ def retrieve_doc_namespace(matched)
+ preposing, _target, postposing, bind = @completion_params
+ @completor.doc_namespace(preposing, matched, postposing, bind: bind)
+ end
+
+ def rdoc_ri_driver
+ return @rdoc_ri_driver if defined?(@rdoc_ri_driver)
+
+ begin
+ require 'rdoc'
+ rescue LoadError
+ @rdoc_ri_driver = nil
+ else
+ options = {}
+ options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
+ @rdoc_ri_driver = RDoc::RI::Driver.new(options)
+ end
+ end
+
def show_doc_dialog_proc
- doc_namespace = ->(matched) {
- preposing, _target, postposing, bind = @completion_params
- @completor.doc_namespace(preposing, matched, postposing, bind: bind)
- }
+ input_method = self # self is changed in the lambda below.
->() {
dialog.trap_key = nil
alt_d = [
@@ -311,15 +340,13 @@ module IRB
cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
return nil if result.nil? or pointer.nil? or pointer < 0
- name = doc_namespace.call(result[pointer])
+ name = input_method.retrieve_doc_namespace(result[pointer])
# Use first one because document dialog does not support multiple namespaces.
name = name.first if name.is_a?(Array)
show_easter_egg = name&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
- options = {}
- options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
- driver = RDoc::RI::Driver.new(options)
+ driver = input_method.rdoc_ri_driver
if key.match?(dialog.name)
if show_easter_egg
@@ -407,23 +434,18 @@ module IRB
}
end
- def display_document(matched, driver: nil)
- begin
- require 'rdoc'
- rescue LoadError
- return
- end
+ def display_document(matched)
+ driver = rdoc_ri_driver
+ return unless driver
if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
IRB.__send__(:easter_egg)
return
end
- _target, preposing, postposing, bind = @completion_params
- namespace = @completor.doc_namespace(preposing, matched, postposing, bind: bind)
+ namespace = retrieve_doc_namespace(matched)
return unless namespace
- driver ||= RDoc::RI::Driver.new
if namespace.is_a?(Array)
out = RDoc::Markup::Document.new
namespace.each do |m|
@@ -466,6 +488,10 @@ module IRB
@eof
end
+ def prompting?
+ true
+ end
+
# For debug message
def inspect
config = Reline::Config.new
diff --git a/lib/irb/inspector.rb b/lib/irb/inspector.rb
index ee3b19efdc..667087ccba 100644
--- a/lib/irb/inspector.rb
+++ b/lib/irb/inspector.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/inspector.rb - inspect methods
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -46,7 +46,7 @@ module IRB # :nodoc:
# Determines the inspector to use where +inspector+ is one of the keys passed
# during inspector definition.
def self.keys_with_inspector(inspector)
- INSPECTORS.select{|k,v| v == inspector}.collect{|k, v| k}
+ INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k}
end
# Example
@@ -113,7 +113,7 @@ module IRB # :nodoc:
Color.colorize_code(v.inspect, colorable: Color.colorable? && Color.inspect_colorable?(v))
}
Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v|
- IRB::ColorPrinter.pp(v, '').chomp
+ IRB::ColorPrinter.pp(v, +'').chomp
}
Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v|
begin
diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec
index a008a39f9d..b29002f593 100644
--- a/lib/irb/irb.gemspec
+++ b/lib/irb/irb.gemspec
@@ -41,6 +41,6 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
- spec.add_dependency "reline", ">= 0.3.8"
- spec.add_dependency "rdoc"
+ spec.add_dependency "reline", ">= 0.4.2"
+ spec.add_dependency "rdoc", ">= 4.0.0"
end
diff --git a/lib/irb/lc/error.rb b/lib/irb/lc/error.rb
index a5ec150865..ee0f047822 100644
--- a/lib/irb/lc/error.rb
+++ b/lib/irb/lc/error.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/lc/error.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -12,11 +12,6 @@ module IRB
super("Unrecognized switch: #{val}")
end
end
- class NotImplementedError < StandardError
- def initialize(val)
- super("Need to define `#{val}'")
- end
- end
class CantReturnToNormalMode < StandardError
def initialize
super("Can't return to normal mode.")
@@ -52,11 +47,6 @@ module IRB
super("Undefined prompt mode(#{val}).")
end
end
- class IllegalRCGenerator < StandardError
- def initialize
- super("Define illegal RC_NAME_GENERATOR.")
- end
- end
# :startdoc:
end
diff --git a/lib/irb/lc/ja/error.rb b/lib/irb/lc/ja/error.rb
index 50d72c4a10..9e2e5b8870 100644
--- a/lib/irb/lc/ja/error.rb
+++ b/lib/irb/lc/ja/error.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/lc/ja/error.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -12,11 +12,6 @@ module IRB
super("スイッチ(#{val})が分りません")
end
end
- class NotImplementedError < StandardError
- def initialize(val)
- super("`#{val}'の定義が必要です")
- end
- end
class CantReturnToNormalMode < StandardError
def initialize
super("Normalモードに戻れません.")
@@ -52,11 +47,6 @@ module IRB
super("プロンプトモード(#{val})は定義されていません.")
end
end
- class IllegalRCGenerator < StandardError
- def initialize
- super("RC_NAME_GENERATORが正しく定義されていません.")
- end
- end
# :startdoc:
end
diff --git a/lib/irb/lc/ja/help-message b/lib/irb/lc/ja/help-message
index cec339cf2f..99f4449b3b 100644
--- a/lib/irb/lc/ja/help-message
+++ b/lib/irb/lc/ja/help-message
@@ -9,10 +9,18 @@ Usage: irb.rb [options] [programfile] [arguments]
-W[level=2] ruby -W と同じ.
--context-mode n 新しいワークスペースを作成した時に関連する Binding
オブジェクトの作成方法を 0 から 3 のいずれかに設定する.
+ --extra-doc-dir 指定したディレクトリのドキュメントを追加で読み込む.
--echo 実行結果を表示する(デフォルト).
--noecho 実行結果を表示しない.
+ --echo-on-assignment
+ 代入結果を表示する.
+ --noecho-on-assignment
+ 代入結果を表示しない.
+ --truncate-echo-on-assignment
+ truncateされた代入結果を表示する(デフォルト).
--inspect 結果出力にinspectを用いる.
--noinspect 結果出力にinspectを用いない.
+ --no-pager ページャを使用しない.
--multiline マルチラインエディタを利用する.
--nomultiline マルチラインエディタを利用しない.
--singleline シングルラインエディタを利用する.
@@ -34,6 +42,8 @@ Usage: irb.rb [options] [programfile] [arguments]
--sample-book-mode/--simple-prompt
非常にシンプルなプロンプトを用いるモードです.
--noprompt プロンプト表示を行なわない.
+ --script スクリプトモード(最初の引数をスクリプトファイルとして扱う、デフォルト)
+ --noscript 引数をargvとして扱う.
--single-irb irb 中で self を実行して得られるオブジェクトをサ
ブ irb と共有する.
--tracer コマンド実行時にトレースを行なう.
diff --git a/lib/irb/locale.rb b/lib/irb/locale.rb
index f94aa0f40b..2abcc7354b 100644
--- a/lib/irb/locale.rb
+++ b/lib/irb/locale.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/locale.rb - internationalization module
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -94,7 +94,7 @@ module IRB # :nodoc:
end
end
- def find(file , paths = $:)
+ def find(file, paths = $:)
dir = File.dirname(file)
dir = "" if dir == "."
base = File.basename(file)
diff --git a/lib/irb/nesting_parser.rb b/lib/irb/nesting_parser.rb
index 3d4db82444..5aa940cc28 100644
--- a/lib/irb/nesting_parser.rb
+++ b/lib/irb/nesting_parser.rb
@@ -12,6 +12,8 @@ module IRB
skip = false
last_tok, state, args = opens.last
case state
+ when :in_alias_undef
+ skip = t.event == :on_kw
when :in_unquoted_symbol
unless IGNORE_TOKENS.include?(t.event)
opens.pop
@@ -61,17 +63,17 @@ module IRB
if args.include?(:arg)
case t.event
when :on_nl, :on_semicolon
- # def recever.f;
+ # def receiver.f;
body = :normal
when :on_lparen
- # def recever.f()
+ # def receiver.f()
next_args << :eq
else
if t.event == :on_op && t.tok == '='
# def receiver.f =
body = :oneliner
else
- # def recever.f arg
+ # def receiver.f arg
next_args << :arg_without_paren
end
end
@@ -130,6 +132,10 @@ module IRB
opens.pop
opens << [t, nil]
end
+ when 'alias'
+ opens << [t, :in_alias_undef, 2]
+ when 'undef'
+ opens << [t, :in_alias_undef, 1]
when 'elsif', 'else', 'when'
opens.pop
opens << [t, nil]
@@ -174,6 +180,10 @@ module IRB
pending_heredocs.reverse_each { |t| opens << [t, nil] }
pending_heredocs = []
end
+ if opens.last && opens.last[1] == :in_alias_undef && !IGNORE_TOKENS.include?(t.event) && t.event != :on_heredoc_end
+ tok, state, arg = opens.pop
+ opens << [tok, state, arg - 1] if arg >= 1
+ end
yield t, opens if block_given?
end
opens.map(&:first) + pending_heredocs.reverse
diff --git a/lib/irb/notifier.rb b/lib/irb/notifier.rb
index 612de3df16..dc1b9ef14b 100644
--- a/lib/irb/notifier.rb
+++ b/lib/irb/notifier.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# notifier.rb - output methods used by irb
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
diff --git a/lib/irb/output-method.rb b/lib/irb/output-method.rb
index f5ea57111d..69942f47a2 100644
--- a/lib/irb/output-method.rb
+++ b/lib/irb/output-method.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# output-method.rb - output methods used by irb
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -9,16 +9,10 @@ module IRB
# IRB::Notifier. You can define your own output method to use with Irb.new,
# or Context.new
class OutputMethod
- class NotImplementedError < StandardError
- def initialize(val)
- super("Need to define `#{val}'")
- end
- end
-
# Open this method to implement your own output method, raises a
# NotImplementedError if you don't define #print in your own class.
def print(*opts)
- raise NotImplementedError, "print"
+ raise NotImplementedError
end
# Prints the given +opts+, with a newline delimiter.
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 4bce2aa6b2..cfe36be83f 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/ruby-lex.rb - ruby lexcal analyzer
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -290,7 +290,7 @@ module IRB
when :on_embdoc_beg
indent_level = 0
else
- indent_level += 1
+ indent_level += 1 unless t.tok == 'alias' || t.tok == 'undef'
end
end
indent_level
diff --git a/lib/irb/source_finder.rb b/lib/irb/source_finder.rb
index 659d4200fd..5d7d729d19 100644
--- a/lib/irb/source_finder.rb
+++ b/lib/irb/source_finder.rb
@@ -4,12 +4,63 @@ require_relative "ruby-lex"
module IRB
class SourceFinder
- Source = Struct.new(
- :file, # @param [String] - file name
- :first_line, # @param [String] - first line
- :last_line, # @param [String] - last line
- keyword_init: true,
- )
+ class EvaluationError < StandardError; end
+
+ class Source
+ attr_reader :file, :line
+ def initialize(file, line, ast_source = nil)
+ @file = file
+ @line = line
+ @ast_source = ast_source
+ end
+
+ def file_exist?
+ File.exist?(@file)
+ end
+
+ def binary_file?
+ # If the line is zero, it means that the target's source is probably in a binary file.
+ @line.zero?
+ end
+
+ def file_content
+ @file_content ||= File.read(@file)
+ end
+
+ def colorized_content
+ if !binary_file? && file_exist?
+ end_line = find_end
+ # To correctly colorize, we need to colorize full content and extract the relevant lines.
+ colored = IRB::Color.colorize_code(file_content)
+ colored.lines[@line - 1...end_line].join
+ elsif @ast_source
+ IRB::Color.colorize_code(@ast_source)
+ end
+ end
+
+ private
+
+ def find_end
+ lex = RubyLex.new
+ code = file_content
+ lines = code.lines[(@line - 1)..-1]
+ tokens = RubyLex.ripper_lex_without_warning(lines.join)
+ prev_tokens = []
+
+ # chunk with line number
+ tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
+ code = lines[0..lnum].join
+ prev_tokens.concat chunk
+ continue = lex.should_continue?(prev_tokens)
+ syntax = lex.check_code_syntax(code, local_variables: [])
+ if !continue && syntax == :valid
+ return @line + lnum
+ end
+ end
+ @line
+ end
+ end
+
private_constant :Source
def initialize(irb_context)
@@ -17,49 +68,47 @@ module IRB
end
def find_source(signature, super_level = 0)
- context_binding = @irb_context.workspace.binding
case signature
- when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
- eval(signature, context_binding) # trigger autoload
- base = context_binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
- file, line = base.const_source_location(signature)
+ when /\A(::)?[A-Z]\w*(::[A-Z]\w*)*\z/ # ConstName, ::ConstName, ConstPath::ConstName
+ eval_receiver_or_owner(signature) # trigger autoload
+ *parts, name = signature.split('::', -1)
+ base =
+ if parts.empty? # ConstName
+ find_const_owner(name)
+ elsif parts == [''] # ::ConstName
+ Object
+ else # ConstPath::ConstName
+ eval_receiver_or_owner(parts.join('::'))
+ end
+ file, line = base.const_source_location(name)
when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
- owner = eval(Regexp.last_match[:owner], context_binding)
+ owner = eval_receiver_or_owner(Regexp.last_match[:owner])
method = Regexp.last_match[:method]
return unless owner.respond_to?(:instance_method)
- file, line = method_target(owner, super_level, method, "owner")
+ method = method_target(owner, super_level, method, "owner")
+ file, line = method&.source_location
when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
- receiver = eval(Regexp.last_match[:receiver] || 'self', context_binding)
+ receiver = eval_receiver_or_owner(Regexp.last_match[:receiver] || 'self')
method = Regexp.last_match[:method]
return unless receiver.respond_to?(method, true)
- file, line = method_target(receiver, super_level, method, "receiver")
+ method = method_target(receiver, super_level, method, "receiver")
+ file, line = method&.source_location
end
- if file && line && File.exist?(file)
- Source.new(file: file, first_line: line, last_line: find_end(file, line))
+ return unless file && line
+
+ if File.exist?(file)
+ Source.new(file, line)
+ elsif method
+ # Method defined with eval, probably in IRB session
+ source = RubyVM::AbstractSyntaxTree.of(method)&.source rescue nil
+ Source.new(file, line, source)
end
+ rescue EvaluationError
+ nil
end
private
- def find_end(file, first_line)
- lex = RubyLex.new
- lines = File.read(file).lines[(first_line - 1)..-1]
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
- prev_tokens = []
-
- # chunk with line number
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
- code = lines[0..lnum].join
- prev_tokens.concat chunk
- continue = lex.should_continue?(prev_tokens)
- syntax = lex.check_code_syntax(code, local_variables: [])
- if !continue && syntax == :valid
- return first_line + lnum
- end
- end
- first_line
- end
-
def method_target(owner_receiver, super_level, method, type)
case type
when "owner"
@@ -70,9 +119,21 @@ module IRB
super_level.times do |s|
target_method = target_method.super_method if target_method
end
- target_method.nil? ? nil : target_method.source_location
+ target_method
rescue NameError
nil
end
+
+ def eval_receiver_or_owner(code)
+ context_binding = @irb_context.workspace.binding
+ eval(code, context_binding)
+ rescue NameError
+ raise EvaluationError
+ end
+
+ def find_const_owner(name)
+ module_nesting = @irb_context.workspace.binding.eval('::Module.nesting')
+ module_nesting.find { |mod| mod.const_defined?(name, false) } || module_nesting.find { |mod| mod.const_defined?(name) } || Object
+ end
end
end
diff --git a/lib/irb/statement.rb b/lib/irb/statement.rb
index b12110600c..a3391c12a3 100644
--- a/lib/irb/statement.rb
+++ b/lib/irb/statement.rb
@@ -16,8 +16,23 @@ module IRB
raise NotImplementedError
end
- def evaluable_code
- raise NotImplementedError
+ class EmptyInput < Statement
+ def is_assignment?
+ false
+ end
+
+ def suppresses_echo?
+ true
+ end
+
+ # Debugger takes empty input to repeat the last command
+ def should_be_handled_by_debugger?
+ true
+ end
+
+ def code
+ ""
+ end
end
class Expression < Statement
@@ -37,18 +52,15 @@ module IRB
def is_assignment?
@is_assignment
end
-
- def evaluable_code
- @code
- end
end
class Command < Statement
- def initialize(code, command, arg, command_class)
- @code = code
- @command = command
- @arg = arg
+ attr_reader :command_class, :arg
+
+ def initialize(original_code, command_class, arg)
+ @code = original_code
@command_class = command_class
+ @arg = arg
end
def is_assignment?
@@ -60,20 +72,8 @@ module IRB
end
def should_be_handled_by_debugger?
- require_relative 'cmd/help'
- require_relative 'cmd/debug'
- IRB::ExtendCommand::DebugCommand > @command_class || IRB::ExtendCommand::Help == @command_class
- end
-
- def evaluable_code
- # Hook command-specific transformation to return valid Ruby code
- if @command_class.respond_to?(:transform_args)
- arg = @command_class.transform_args(@arg)
- else
- arg = @arg
- end
-
- [@command, arg].compact.join(' ')
+ require_relative 'command/debug'
+ IRB::Command::DebugCommand > @command_class
end
end
end
diff --git a/lib/irb/version.rb b/lib/irb/version.rb
index c0be6d91e5..c41917329c 100644
--- a/lib/irb/version.rb
+++ b/lib/irb/version.rb
@@ -1,11 +1,11 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/version.rb - irb version definition file
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
module IRB # :nodoc:
- VERSION = "1.11.0"
+ VERSION = "1.13.1"
@RELEASE_VERSION = VERSION
- @LAST_UPDATE_DATE = "2023-12-19"
+ @LAST_UPDATE_DATE = "2024-05-05"
end
diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb
index 2bf3d5e0f1..d24d1cc38d 100644
--- a/lib/irb/workspace.rb
+++ b/lib/irb/workspace.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/workspace-binding.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -6,6 +6,8 @@
require "delegate"
+require_relative "helper_method"
+
IRB::TOPLEVEL_BINDING = binding
module IRB # :nodoc:
class WorkSpace
@@ -90,11 +92,11 @@ EOF
IRB.conf[:__MAIN__] = @main
@main.singleton_class.class_eval do
private
- define_method(:exit) do |*a, &b|
- # Do nothing, will be overridden
- end
define_method(:binding, Kernel.instance_method(:binding))
define_method(:local_variables, Kernel.instance_method(:local_variables))
+ # Define empty method to avoid delegator warning, will be overridden.
+ define_method(:exit) {|*a, &b| }
+ define_method(:exit!) {|*a, &b| }
end
@binding = eval("IRB.conf[:__MAIN__].instance_eval('binding', __FILE__, __LINE__)", @binding, *@binding.source_location)
end
@@ -108,8 +110,10 @@ EOF
# <code>IRB.conf[:__MAIN__]</code>
attr_reader :main
- def load_commands_to_main
- main.extend ExtendCommandBundle
+ def load_helper_methods_to_main
+ ancestors = class<<main;ancestors;end
+ main.extend ExtendCommandBundle if !ancestors.include?(ExtendCommandBundle)
+ main.extend HelpersContainer if !ancestors.include?(HelpersContainer)
end
# Evaluate the given +statements+ within the context of this workspace.
@@ -170,4 +174,16 @@ EOF
"\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}#{Color.clear}\n"
end
end
+
+ module HelpersContainer
+ def self.install_helper_methods
+ HelperMethod.helper_methods.each do |name, helper_method_class|
+ define_method name do |*args, **opts, &block|
+ helper_method_class.instance.execute(*args, **opts, &block)
+ end unless method_defined?(name)
+ end
+ end
+
+ install_helper_methods
+ end
end
diff --git a/lib/irb/ws-for-case-2.rb b/lib/irb/ws-for-case-2.rb
index a0f617e4ed..03f42d73d9 100644
--- a/lib/irb/ws-for-case-2.rb
+++ b/lib/irb/ws-for-case-2.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/ws-for-case-2.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
diff --git a/lib/irb/xmp.rb b/lib/irb/xmp.rb
index de29089429..b1bc53283e 100644
--- a/lib/irb/xmp.rb
+++ b/lib/irb/xmp.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# xmp.rb - irb version of gotoken xmp
# by Keiju ISHITSUKA(Nippon Rational Inc.)
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index 6da7dde5f1..51585b135c 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -2465,7 +2465,7 @@ static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{$extout ? " install-rb" :
dest = "#{dir}/#{File.basename(f)}"
mfile.print("do-install-rb#{sfx}: #{dest}\n")
mfile.print("#{dest}: #{f} #{timestamp_file(dir, target_prefix)}\n")
- mfile.print("\t$(Q) $(#{$extout ? 'COPY' : 'INSTALL_DATA'}) #{f} $(@D)\n")
+ mfile.print("\t$(Q) $(#{$extout ? 'COPY' : 'INSTALL_DATA'}) #{f} $@\n")
if defined?($installed_list) and !$extout
mfile.print("\t@echo #{dest}>>$(INSTALLED_LIST)\n")
end
diff --git a/lib/rdoc/store.rb b/lib/rdoc/store.rb
index fe749edc1c..cd27d47dd1 100644
--- a/lib/rdoc/store.rb
+++ b/lib/rdoc/store.rb
@@ -559,9 +559,7 @@ class RDoc::Store
def load_cache
#orig_enc = @encoding
- File.open cache_path, 'rb' do |io|
- @cache = Marshal.load io
- end
+ @cache = marshal_load(cache_path)
load_enc = @cache[:encoding]
@@ -618,9 +616,7 @@ class RDoc::Store
def load_class_data klass_name
file = class_file klass_name
- File.open file, 'rb' do |io|
- Marshal.load io
- end
+ marshal_load(file)
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name)
error.set_backtrace e.backtrace
@@ -633,14 +629,10 @@ class RDoc::Store
def load_method klass_name, method_name
file = method_file klass_name, method_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io
- obj.store = self
- obj.parent =
- find_class_or_module(klass_name) || load_class(klass_name) unless
- obj.parent
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj.parent ||= find_class_or_module(klass_name) || load_class(klass_name)
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name + method_name)
error.set_backtrace e.backtrace
@@ -653,11 +645,9 @@ class RDoc::Store
def load_page page_name
file = page_file page_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io
- obj.store = self
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, page_name)
error.set_backtrace e.backtrace
@@ -979,4 +969,21 @@ class RDoc::Store
@unique_modules
end
+ private
+ def marshal_load(file)
+ File.open(file, 'rb') {|io| Marshal.load(io, MarshalFilter)}
+ end
+
+ MarshalFilter = proc do |obj|
+ case obj
+ when true, false, nil, Array, Class, Encoding, Hash, Integer, String, Symbol, RDoc::Text
+ else
+ unless obj.class.name.start_with?("RDoc::")
+ raise TypeError, "not permitted class: #{obj.class.name}"
+ end
+ end
+ obj
+ end
+ private_constant :MarshalFilter
+
end
diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb
index 04dfaebf74..87842d9847 100644
--- a/lib/rdoc/version.rb
+++ b/lib/rdoc/version.rb
@@ -5,6 +5,6 @@ module RDoc
##
# RDoc version you are using
- VERSION = '6.6.2'
+ VERSION = '6.6.3.1'
end
diff --git a/lib/reline.rb b/lib/reline.rb
index fb19982081..ddb0224180 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -7,6 +7,7 @@ require 'reline/key_stroke'
require 'reline/line_editor'
require 'reline/history'
require 'reline/terminfo'
+require 'reline/io'
require 'reline/face'
require 'rbconfig'
@@ -18,20 +19,10 @@ module Reline
class ConfigEncodingConversionError < StandardError; end
Key = Struct.new(:char, :combined_char, :with_meta) do
- def match?(other)
- case other
- when Reline::Key
- (other.char.nil? or char.nil? or char == other.char) and
- (other.combined_char.nil? or combined_char.nil? or combined_char == other.combined_char) and
- (other.with_meta.nil? or with_meta.nil? or with_meta == other.with_meta)
- when Integer, Symbol
- (combined_char and combined_char == other) or
- (combined_char.nil? and char and char == other)
- else
- false
- end
+ # For dialog_proc `key.match?(dialog.name)`
+ def match?(sym)
+ combined_char.is_a?(Symbol) && combined_char == sym
end
- alias_method :==, :match?
end
CursorPos = Struct.new(:x, :y)
DialogRenderInfo = Struct.new(
@@ -75,10 +66,10 @@ module Reline
def initialize
self.output = STDOUT
+ @mutex = Mutex.new
@dialog_proc_list = {}
yield self
@completion_quote_character = nil
- @bracketed_paste_finished = false
end
def io_gate
@@ -220,32 +211,25 @@ module Reline
Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
# autocomplete
- return nil unless config.autocompletion
- if just_cursor_moving and completion_journey_data.nil?
- # Auto complete starts only when edited
- return nil
- end
- pre, target, post = retrieve_completion_block(true)
- if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
- return nil
- end
- if completion_journey_data and completion_journey_data.list
- result = completion_journey_data.list.dup
- result.shift
- pointer = completion_journey_data.pointer - 1
- else
- result = call_completion_proc_with_checking_args(pre, target, post)
- pointer = nil
- end
- if result and result.size == 1 and result[0] == target and pointer != 0
- result = nil
- end
+ return unless config.autocompletion
+
+ journey_data = completion_journey_data
+ return unless journey_data
+
+ target = journey_data.list.first
+ completed = journey_data.list[journey_data.pointer]
+ result = journey_data.list.drop(1)
+ pointer = journey_data.pointer - 1
+ return if completed.empty? || (result == [completed] && pointer < 0)
+
target_width = Reline::Unicode.calculate_width(target)
- x = cursor_pos.x - target_width
- if x < 0
- x = screen_width + x
+ completed_width = Reline::Unicode.calculate_width(completed)
+ if cursor_pos.x <= completed_width - target_width
+ # When target is rendered on the line above cursor position
+ x = screen_width - completed_width
y = -1
else
+ x = [cursor_pos.x - completed_width, 0].max
y = 0
end
cursor_pos_to_render = Reline::CursorPos.new(x, y)
@@ -265,12 +249,14 @@ module Reline
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
- Reline.update_iogate
- io_gate.with_raw_input do
+ @mutex.synchronize do
unless confirm_multiline_termination
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
end
- inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
+
+ io_gate.with_raw_input do
+ inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
+ end
whole_buffer = line_editor.whole_buffer.dup
whole_buffer.taint if RUBY_VERSION < '2.7'
@@ -278,23 +264,31 @@ module Reline
Reline::HISTORY << whole_buffer
end
- line_editor.reset_line if line_editor.whole_buffer.nil?
- whole_buffer
+ if line_editor.eof?
+ line_editor.reset_line
+ # Return nil if the input is aborted by C-d.
+ nil
+ else
+ whole_buffer
+ end
end
end
def readline(prompt = '', add_hist = false)
- Reline.update_iogate
- inner_readline(prompt, add_hist, false)
+ @mutex.synchronize do
+ io_gate.with_raw_input do
+ inner_readline(prompt, add_hist, false)
+ end
- line = line_editor.line.dup
- line.taint if RUBY_VERSION < '2.7'
- if add_hist and line and line.chomp("\n").size > 0
- Reline::HISTORY << line.chomp("\n")
- end
+ line = line_editor.line.dup
+ line.taint if RUBY_VERSION < '2.7'
+ if add_hist and line and line.chomp("\n").size > 0
+ Reline::HISTORY << line.chomp("\n")
+ end
- line_editor.reset_line if line_editor.line.nil?
- line
+ line_editor.reset_line if line_editor.line.nil?
+ line
+ end
end
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
@@ -307,6 +301,10 @@ module Reline
$stderr.sync = true
$stderr.puts "Reline is used by #{Process.pid}"
end
+ unless config.test_mode or config.loaded?
+ config.read
+ io_gate.set_default_key_bindings(config)
+ end
otio = io_gate.prep
may_req_ambiguous_char_width
@@ -326,147 +324,85 @@ module Reline
line_editor.prompt_proc = prompt_proc
line_editor.auto_indent_proc = auto_indent_proc
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
- line_editor.pre_input_hook = pre_input_hook
- @dialog_proc_list.each_pair do |name_sym, d|
- line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
- end
- unless config.test_mode
- config.read
- config.reset_default_key_bindings
- io_gate.set_default_key_bindings(config)
+ # Readline calls pre_input_hook just after printing the first prompt.
+ line_editor.print_nomultiline_prompt
+ pre_input_hook&.call
+
+ unless Reline::IOGate.dumb?
+ @dialog_proc_list.each_pair do |name_sym, d|
+ line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
+ end
end
+ line_editor.update_dialogs
line_editor.rerender
begin
line_editor.set_signal_handlers
- prev_pasting_state = false
loop do
- prev_pasting_state = io_gate.in_pasting?
read_io(config.keyseq_timeout) { |inputs|
line_editor.set_pasting_state(io_gate.in_pasting?)
- inputs.each { |c|
- line_editor.input_key(c)
- line_editor.rerender
- }
- if @bracketed_paste_finished
- line_editor.rerender_all
- @bracketed_paste_finished = false
+ inputs.each do |key|
+ if key.char == :bracketed_paste_start
+ text = io_gate.read_bracketed_paste
+ line_editor.insert_multiline_text(text)
+ line_editor.scroll_into_view
+ else
+ line_editor.update(key)
+ end
end
}
- if prev_pasting_state == true and not io_gate.in_pasting? and not line_editor.finished?
- line_editor.set_pasting_state(false)
- prev_pasting_state = false
- line_editor.rerender_all
+ if line_editor.finished?
+ line_editor.render_finished
+ break
+ else
+ line_editor.set_pasting_state(io_gate.in_pasting?)
+ line_editor.rerender
end
- break if line_editor.finished?
end
io_gate.move_cursor_column(0)
rescue Errno::EIO
# Maybe the I/O has been closed.
- rescue StandardError => e
+ ensure
line_editor.finalize
io_gate.deprep(otio)
- raise e
- rescue Exception
- # Including Interrupt
- line_editor.finalize
- io_gate.deprep(otio)
- raise
end
-
- line_editor.finalize
- io_gate.deprep(otio)
end
- # GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
- # is followed by a character, and times out and treats it as a standalone
- # ESC if the second character does not arrive. If the second character
- # comes before timed out, it is treated as a modifier key with the
- # meta-property of meta-key, so that it can be distinguished from
- # multibyte characters with the 8th bit turned on.
- #
- # GNU Readline will wait for the 2nd character with "keyseq-timeout"
- # milli-seconds but wait forever after 3rd characters.
+ # GNU Readline watis for "keyseq-timeout" milliseconds when the input is
+ # ambiguous whether it is matching or matched.
+ # If the next character does not arrive within the specified timeout, input
+ # is considered as matched.
+ # `ESC` is ambiguous because it can be a standalone ESC (matched) or part of
+ # `ESC char` or part of CSI sequence (matching).
private def read_io(keyseq_timeout, &block)
buffer = []
+ status = KeyStroke::MATCHING
loop do
- c = io_gate.getc(Float::INFINITY)
- if c == -1
- result = :unmatched
- @bracketed_paste_finished = true
- else
- buffer << c
- result = key_stroke.match_status(buffer)
- end
- case result
- when :matched
- expanded = key_stroke.expand(buffer).map{ |expanded_c|
- Reline::Key.new(expanded_c, expanded_c, false)
- }
- block.(expanded)
- break
- when :matching
- if buffer.size == 1
- case read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
- when :break then break
- when :next then next
- end
- end
- when :unmatched
- if buffer.size == 1 and c == "\e".ord
- read_escaped_key(keyseq_timeout, c, block)
+ timeout = status == KeyStroke::MATCHING_MATCHED ? keyseq_timeout.fdiv(1000) : Float::INFINITY
+ c = io_gate.getc(timeout)
+ if c.nil? || c == -1
+ if status == KeyStroke::MATCHING_MATCHED
+ status = KeyStroke::MATCHED
+ elsif buffer.empty?
+ # io_gate is closed and reached EOF
+ block.call([Key.new(nil, nil, false)])
+ return
else
- expanded = buffer.map{ |expanded_c|
- Reline::Key.new(expanded_c, expanded_c, false)
- }
- block.(expanded)
+ status = KeyStroke::UNMATCHED
end
- break
+ else
+ buffer << c
+ status = key_stroke.match_status(buffer)
end
- end
- end
- private def read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
- succ_c = io_gate.getc(keyseq_timeout.fdiv(1000))
- if succ_c
- case key_stroke.match_status(buffer.dup.push(succ_c))
- when :unmatched
- if c == "\e".ord
- block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
- else
- block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
- end
- return :break
- when :matching
- io_gate.ungetc(succ_c)
- return :next
- when :matched
- buffer << succ_c
- expanded = key_stroke.expand(buffer).map{ |expanded_c|
- Reline::Key.new(expanded_c, expanded_c, false)
- }
- block.(expanded)
- return :break
+ if status == KeyStroke::MATCHED || status == KeyStroke::UNMATCHED
+ expanded, rest_bytes = key_stroke.expand(buffer)
+ rest_bytes.reverse_each { |c| io_gate.ungetc(c) }
+ block.call(expanded)
+ return
end
- else
- block.([Reline::Key.new(c, c, false)])
- return :break
- end
- end
-
- private def read_escaped_key(keyseq_timeout, c, block)
- escaped_c = io_gate.getc(keyseq_timeout.fdiv(1000))
-
- if escaped_c.nil?
- block.([Reline::Key.new(c, c, false)])
- elsif escaped_c >= 128 # maybe, first byte of multi byte
- block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
- elsif escaped_c == "\e".ord # escape twice
- block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
- else
- block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
end
end
@@ -476,7 +412,7 @@ module Reline
end
private def may_req_ambiguous_char_width
- @ambiguous_width = 2 if io_gate == Reline::GeneralIO or !STDOUT.tty?
+ @ambiguous_width = 2 if io_gate.dumb? || !STDIN.tty? || !STDOUT.tty?
return if defined? @ambiguous_width
io_gate.move_cursor_column(0)
begin
@@ -524,8 +460,8 @@ module Reline
def_single_delegator :line_editor, :byte_pointer, :point
def_single_delegator :line_editor, :byte_pointer=, :point=
- def self.insert_text(*args, &block)
- line_editor.insert_text(*args, &block)
+ def self.insert_text(text)
+ line_editor.insert_multiline_text(text)
self
end
@@ -570,37 +506,13 @@ module Reline
def self.line_editor
core.line_editor
end
+end
- def self.update_iogate
- return if core.config.test_mode
- # Need to change IOGate when `$stdout.tty?` change from false to true by `$stdout.reopen`
- # Example: rails/spring boot the application in non-tty, then run console in tty.
- if ENV['TERM'] != 'dumb' && core.io_gate == Reline::GeneralIO && $stdout.tty?
- require 'reline/ansi'
- remove_const(:IOGate)
- const_set(:IOGate, Reline::ANSI)
- end
- end
-end
+Reline::IOGate = Reline::IO.decide_io_gate
-require 'reline/general_io'
-io = Reline::GeneralIO
-unless ENV['TERM'] == 'dumb'
- case RbConfig::CONFIG['host_os']
- when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
- require 'reline/windows'
- tty = (io = Reline::Windows).msys_tty?
- else
- tty = $stdout.tty?
- end
-end
-Reline::IOGate = if tty
- require 'reline/ansi'
- Reline::ANSI
-else
- io
-end
+# Deprecated
+Reline::GeneralIO = Reline::Dumb.new
Reline::Face.load_initial_configs
diff --git a/lib/reline/config.rb b/lib/reline/config.rb
index 4c07a73701..6aa6ba8d94 100644
--- a/lib/reline/config.rb
+++ b/lib/reline/config.rb
@@ -8,31 +8,12 @@ class Reline::Config
end
VARIABLE_NAMES = %w{
- bind-tty-special-chars
- blink-matching-paren
- byte-oriented
completion-ignore-case
convert-meta
disable-completion
- enable-keypad
- expand-tilde
- history-preserve-point
history-size
- horizontal-scroll-mode
- input-meta
keyseq-timeout
- mark-directories
- mark-modified-lines
- mark-symlinked-directories
- match-hidden-files
- meta-flag
- output-meta
- page-completions
- prefer-visible-bell
- print-completions-horizontally
show-all-if-ambiguous
- show-all-if-unmodified
- visible-stats
show-mode-in-prompt
vi-cmd-mode-string
vi-ins-mode-string
@@ -48,20 +29,31 @@ class Reline::Config
attr_accessor :autocompletion
def initialize
- @additional_key_bindings = {} # from inputrc
- @additional_key_bindings[:emacs] = {}
- @additional_key_bindings[:vi_insert] = {}
- @additional_key_bindings[:vi_command] = {}
- @oneshot_key_bindings = {}
- @skip_section = nil
- @if_stack = nil
+ reset_variables
+ end
+
+ def reset
+ if editing_mode_is?(:vi_command)
+ @editing_mode_label = :vi_insert
+ end
+ @oneshot_key_bindings.clear
+ end
+
+ def reset_variables
+ @additional_key_bindings = { # from inputrc
+ emacs: Reline::KeyActor::Base.new,
+ vi_insert: Reline::KeyActor::Base.new,
+ vi_command: Reline::KeyActor::Base.new
+ }
+ @oneshot_key_bindings = Reline::KeyActor::Base.new
@editing_mode_label = :emacs
@keymap_label = :emacs
@keymap_prefix = []
- @key_actors = {}
- @key_actors[:emacs] = Reline::KeyActor::Emacs.new
- @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
- @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
+ @default_key_bindings = {
+ emacs: Reline::KeyActor::Base.new(Reline::KeyActor::EMACS_MAPPING),
+ vi_insert: Reline::KeyActor::Base.new(Reline::KeyActor::VI_INSERT_MAPPING),
+ vi_command: Reline::KeyActor::Base.new(Reline::KeyActor::VI_COMMAND_MAPPING)
+ }
@vi_cmd_mode_string = '(cmd)'
@vi_ins_mode_string = '(ins)'
@emacs_mode_string = '@'
@@ -70,22 +62,15 @@ class Reline::Config
@keyseq_timeout = 500
@test_mode = false
@autocompletion = false
- @convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
- end
-
- def reset
- if editing_mode_is?(:vi_command)
- @editing_mode_label = :vi_insert
- end
- @additional_key_bindings.keys.each do |key|
- @additional_key_bindings[key].clear
- end
- @oneshot_key_bindings.clear
- reset_default_key_bindings
+ @convert_meta = seven_bit_encoding?(Reline::IOGate.encoding)
+ @loaded = false
+ @enable_bracketed_paste = true
+ @show_mode_in_prompt = false
+ @default_inputrc_path = nil
end
def editing_mode
- @key_actors[@editing_mode_label]
+ @default_key_bindings[@editing_mode_label]
end
def editing_mode=(val)
@@ -97,7 +82,11 @@ class Reline::Config
end
def keymap
- @key_actors[@keymap_label]
+ @default_key_bindings[@keymap_label]
+ end
+
+ def loaded?
+ @loaded
end
def inputrc_path
@@ -131,6 +120,7 @@ class Reline::Config
end
def read(file = nil)
+ @loaded = true
file ||= default_inputrc_path
begin
if file.respond_to?(:readlines)
@@ -151,14 +141,14 @@ class Reline::Config
def key_bindings
# The key bindings for each editing mode will be overwritten by the user-defined ones.
- kb = @key_actors[@editing_mode_label].default_key_bindings.dup
- kb.merge!(@additional_key_bindings[@editing_mode_label])
- kb.merge!(@oneshot_key_bindings)
- kb
+ Reline::KeyActor::Composite.new([@oneshot_key_bindings, @additional_key_bindings[@editing_mode_label], @default_key_bindings[@editing_mode_label]])
end
def add_oneshot_key_binding(keystroke, target)
- @oneshot_key_bindings[keystroke] = target
+ # IRB sets invalid keystroke [Reline::Key]. We should ignore it.
+ return unless keystroke.all? { |c| c.is_a?(Integer) }
+
+ @oneshot_key_bindings.add(keystroke, target)
end
def reset_oneshot_key_bindings
@@ -166,17 +156,11 @@ class Reline::Config
end
def add_default_key_binding_by_keymap(keymap, keystroke, target)
- @key_actors[keymap].default_key_bindings[keystroke] = target
+ @default_key_bindings[keymap].add(keystroke, target)
end
def add_default_key_binding(keystroke, target)
- @key_actors[@keymap_label].default_key_bindings[keystroke] = target
- end
-
- def reset_default_key_bindings
- @key_actors.values.each do |ka|
- ka.reset_default_key_bindings
- end
+ add_default_key_binding_by_keymap(@keymap_label, keystroke, target)
end
def read_lines(lines, file = nil)
@@ -190,9 +174,7 @@ class Reline::Config
end
end
end
- conditions = [@skip_section, @if_stack]
- @skip_section = nil
- @if_stack = []
+ if_stack = []
lines.each_with_index do |line, no|
next if line.match(/\A\s*#/)
@@ -201,62 +183,67 @@ class Reline::Config
line = line.chomp.lstrip
if line.start_with?('$')
- handle_directive(line[1..-1], file, no)
+ handle_directive(line[1..-1], file, no, if_stack)
next
end
- next if @skip_section
+ next if if_stack.any? { |_no, skip| skip }
case line
- when /^set +([^ ]+) +([^ ]+)/i
- var, value = $1.downcase, $2
- bind_variable(var, value)
+ when /^set +([^ ]+) +(.+)/i
+ # value ignores everything after a space, raw_value does not.
+ var, value, raw_value = $1.downcase, $2.partition(' ').first, $2
+ bind_variable(var, value, raw_value)
next
when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
key, func_name = $1, $2
+ func_name = func_name.split.first
keystroke, func = bind_key(key, func_name)
next unless keystroke
- @additional_key_bindings[@keymap_label][@keymap_prefix + keystroke] = func
+ @additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func)
end
end
- unless @if_stack.empty?
- raise InvalidInputrc, "#{file}:#{@if_stack.last[1]}: unclosed if"
+ unless if_stack.empty?
+ raise InvalidInputrc, "#{file}:#{if_stack.last[0]}: unclosed if"
end
- ensure
- @skip_section, @if_stack = conditions
end
- def handle_directive(directive, file, no)
+ def handle_directive(directive, file, no, if_stack)
directive, args = directive.split(' ')
case directive
when 'if'
condition = false
case args
- when 'mode'
+ when /^mode=(vi|emacs)$/i
+ mode = $1.downcase
+ # NOTE: mode=vi means vi-insert mode
+ mode = 'vi_insert' if mode == 'vi'
+ if @editing_mode_label == mode.to_sym
+ condition = true
+ end
when 'term'
when 'version'
else # application name
condition = true if args == 'Ruby'
condition = true if args == 'Reline'
end
- @if_stack << [file, no, @skip_section]
- @skip_section = !condition
+ if_stack << [no, !condition]
when 'else'
- if @if_stack.empty?
+ if if_stack.empty?
raise InvalidInputrc, "#{file}:#{no}: unmatched else"
end
- @skip_section = !@skip_section
+ if_stack.last[1] = !if_stack.last[1]
when 'endif'
- if @if_stack.empty?
+ if if_stack.empty?
raise InvalidInputrc, "#{file}:#{no}: unmatched endif"
end
- @skip_section = @if_stack.pop
+ if_stack.pop
when 'include'
read(File.expand_path(args))
end
end
- def bind_variable(name, value)
+ def bind_variable(name, value, raw_value)
case name
when 'history-size'
begin
@@ -264,24 +251,8 @@ class Reline::Config
rescue ArgumentError
@history_size = 500
end
- when 'bell-style'
- @bell_style =
- case value
- when 'none', 'off'
- :none
- when 'audible', 'on'
- :audible
- when 'visible'
- :visible
- else
- :audible
- end
- when 'comment-begin'
- @comment_begin = value.dup
- when 'completion-query-items'
- @completion_query_items = value.to_i
when 'isearch-terminators'
- @isearch_terminators = retrieve_string(value)
+ @isearch_terminators = retrieve_string(raw_value)
when 'editing-mode'
case value
when 'emacs'
@@ -323,11 +294,11 @@ class Reline::Config
@show_mode_in_prompt = false
end
when 'vi-cmd-mode-string'
- @vi_cmd_mode_string = retrieve_string(value)
+ @vi_cmd_mode_string = retrieve_string(raw_value)
when 'vi-ins-mode-string'
- @vi_ins_mode_string = retrieve_string(value)
+ @vi_ins_mode_string = retrieve_string(raw_value)
when 'emacs-mode-string'
- @emacs_mode_string = retrieve_string(value)
+ @emacs_mode_string = retrieve_string(raw_value)
when *VARIABLE_NAMES then
variable_name = :"@#{name.tr(?-, ?_)}"
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
@@ -395,6 +366,11 @@ class Reline::Config
ret
end
+ def reload
+ reset_variables
+ read
+ end
+
private def seven_bit_encoding?(encoding)
encoding == Encoding::US_ASCII
end
diff --git a/lib/reline/face.rb b/lib/reline/face.rb
index e18ec957e8..5b4464a623 100644
--- a/lib/reline/face.rb
+++ b/lib/reline/face.rb
@@ -107,7 +107,7 @@ class Reline::Face
def sgr_rgb_256color(key, value)
# 256 colors are
- # 0..15: standard colors, hight intensity colors
+ # 0..15: standard colors, high intensity colors
# 16..232: 216 colors (R, G, B each 6 steps)
# 233..255: grayscale colors (24 steps)
# This methods converts rgb_expression to 216 colors
@@ -186,9 +186,9 @@ class Reline::Face
conf.define :scrollbar, style: :reset
end
config(:completion_dialog) do |conf|
- conf.define :default, foreground: :white, background: :cyan
- conf.define :enhanced, foreground: :white, background: :magenta
- conf.define :scrollbar, foreground: :white, background: :cyan
+ conf.define :default, foreground: :bright_white, background: :gray
+ conf.define :enhanced, foreground: :black, background: :white
+ conf.define :scrollbar, foreground: :white, background: :gray
end
end
diff --git a/lib/reline/general_io.rb b/lib/reline/general_io.rb
deleted file mode 100644
index eaae63f925..0000000000
--- a/lib/reline/general_io.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-require 'io/wait'
-
-class Reline::GeneralIO
- def self.reset(encoding: nil)
- @@pasting = false
- if encoding
- @@encoding = encoding
- elsif defined?(@@encoding)
- remove_class_variable(:@@encoding)
- end
- end
-
- def self.encoding
- if defined?(@@encoding)
- @@encoding
- elsif RUBY_PLATFORM =~ /mswin|mingw/
- Encoding::UTF_8
- else
- Encoding::default_external
- end
- end
-
- def self.win?
- false
- end
-
- def self.set_default_key_bindings(_)
- end
-
- @@buf = []
- @@input = STDIN
-
- def self.input=(val)
- @@input = val
- end
-
- def self.with_raw_input
- yield
- end
-
- def self.getc(_timeout_second)
- unless @@buf.empty?
- return @@buf.shift
- end
- c = nil
- loop do
- result = @@input.wait_readable(0.1)
- next if result.nil?
- c = @@input.read(1)
- break
- end
- c&.ord
- end
-
- def self.ungetc(c)
- @@buf.unshift(c)
- end
-
- def self.get_screen_size
- [1, 1]
- end
-
- def self.cursor_pos
- Reline::CursorPos.new(1, 1)
- end
-
- def self.hide_cursor
- end
-
- def self.show_cursor
- end
-
- def self.move_cursor_column(val)
- end
-
- def self.move_cursor_up(val)
- end
-
- def self.move_cursor_down(val)
- end
-
- def self.erase_after_cursor
- end
-
- def self.scroll_down(val)
- end
-
- def self.clear_screen
- end
-
- def self.set_screen_size(rows, columns)
- end
-
- def self.set_winch_handler(&handler)
- end
-
- @@pasting = false
-
- def self.in_pasting?
- @@pasting
- end
-
- def self.start_pasting
- @@pasting = true
- end
-
- def self.finish_pasting
- @@pasting = false
- end
-
- def self.prep
- end
-
- def self.deprep(otio)
- end
-end
diff --git a/lib/reline/history.rb b/lib/reline/history.rb