diff --git a/.cirrus.yml b/.cirrus.yml index 1e7832ed000716..854a3df98201fb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -90,6 +90,7 @@ yjit_task: matrix: - CC: clang-12 configure: --enable-yjit=dev + rustup_init: --default-toolchain=1.58.1 - CC: gcc-11 configure: --enable-yjit id_script: id @@ -107,7 +108,7 @@ yjit_task: install_rust_script: - sudo apt-get update -y - sudo apt-get install -y curl - - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y" + - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y $rustup_init" autogen_script: ./autogen.sh configure_script: >- source $HOME/.cargo/env && ./configure -C diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index b7cd624b0dda95..00000000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,11 +0,0 @@ -# Lines starting with '#' are comments. -# Each line is a file pattern followed by one or more owners. -# Code owners will be automatically tagged as reviewers when a pull request is opened - -# YJIT sources and tests -yjit* @maximecb @xrxr @tenderlove @k0kubun -yjit/* @maximecb @xrxr @tenderlove @k0kubun -doc/yjit/* @maximecb @xrxr @tenderlove @k0kubun -bootstraptest/test_yjit* @maximecb @xrxr @tenderlove @k0kubun -test/ruby/test_yjit* @maximecb @xrxr @tenderlove @k0kubun -.github/workflows/yjit* @maximecb @xrxr @tenderlove @k0kubun diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml new file mode 100644 index 00000000000000..d058b6ca00c9bc --- /dev/null +++ b/.github/auto_request_review.yml @@ -0,0 +1,9 @@ +files: + 'yjit*': [team:yjit] + 'yjit/**/*': [team:yjit] + 'doc/yjit/*': [team:yjit] + 'bootstraptest/test_yjit*': [team:yjit] + 'test/ruby/test_yjit*': [team:yjit] + '.github/workflows/yjit*': [team:yjit] +options: + ignore_draft: true diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 91f82b842bf802..6036043d8cbda2 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -1,3 +1,7 @@ name: "CodeQL config for the Ruby language" languages: cpp + +# syntax_suggest consumes an hour. +paths-ignore: + - lib/syntax_suggest diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml new file mode 100644 index 00000000000000..7e163de6978893 --- /dev/null +++ b/.github/workflows/auto_request_review.yml @@ -0,0 +1,14 @@ +name: Auto Request Review +on: + pull_request_target: + types: [opened, ready_for_review, reopened] +jobs: + auto-request-review: + name: Auto Request Review + runs-on: ubuntu-latest + steps: + - name: Request review based on files changes and/or groups the author belongs to + uses: necojackarc/auto-request-review@e08cdffa277d50854744de3f76230260e61c67f4 # v0.7.0, checking sha + with: + # scope: public_repo + token: ${{ secrets.MATZBOT_GITHUB_TOKEN }} diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 340dd20f575ff2..70d0e3ae6ca08b 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -22,7 +22,7 @@ concurrency: # environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that # restriction. env: - default_cc: clang-14 + default_cc: clang-15 append_cc: '' # -O1 is faster than -O3 in our tests... Majority of time are consumed trying @@ -91,6 +91,7 @@ jobs: # https://sourceware.org/annobin/annobin.html/Test-pie.html TEST_ANNOCHECK_OPTS: "--skip-pie" check: true + - { name: clang-16, env: { default_cc: clang-16 } } - { name: clang-15, env: { default_cc: clang-15 } } - { name: clang-14, env: { default_cc: clang-14 } } - { name: clang-13, env: { default_cc: clang-13 } } @@ -214,7 +215,7 @@ jobs: name: ${{ matrix.entry.name }} runs-on: ubuntu-latest container: - image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-14' }} + image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-15' }} options: --user root if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} env: ${{ matrix.entry.env || matrix.env }} diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 6f93aa5392e3c2..020295baa12189 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -70,7 +70,6 @@ jobs: - name: set env run: | echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV - echo "TEST_JOBS=$((15 * NUMBER_OF_PROCESSORS / 10))" >> $GITHUB_ENV - name: where check run: | @@ -116,7 +115,7 @@ jobs: make update-gems - name: make all - timeout-minutes: 20 + timeout-minutes: 30 run: | make @@ -140,7 +139,7 @@ jobs: make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }} env: RUBY_TESTOPTS: >- - -j${{env.TEST_JOBS}} --retry --job-status=normal --show-skip --timeout-scale=1.5 + --retry --job-status=normal --show-skip --timeout-scale=1.5 ${{ matrix.test-all-opts }} BUNDLER_VERSION: if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-all' || StartsWith(matrix.test_task, 'test/')}} diff --git a/.github/workflows/mjit-bindgen.yml b/.github/workflows/mjit-bindgen.yml index 33a1650f922ac4..04c9ac4a9f2ade 100644 --- a/.github/workflows/mjit-bindgen.yml +++ b/.github/workflows/mjit-bindgen.yml @@ -23,9 +23,6 @@ jobs: matrix: include: - task: mjit-bindgen - configure: '--enable-yjit=dev_nodebug' - - task: mjit-bindgen - arch: i686 fail-fast: false env: SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }} @@ -74,7 +71,7 @@ jobs: env: arch: ${{ matrix.arch }} run: >- - $SETARCH ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }} + $SETARCH ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug ${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE} - run: $SETARCH make incs - run: $SETARCH make diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 6ea8c06b930534..a1818182a34beb 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -21,32 +21,33 @@ jobs: make: strategy: matrix: - test_task: ["check", "test-syntax-suggest", "test-bundler-parallel", "test-bundled-gems"] - os: - - ubuntu-20.04 - configure: ["", "cppflags=-DRUBY_DEBUG"] + # main variables included in the job name + test_task: [check] + configure: [cppflags=-DRUBY_DEBUG] # default to use more assertions + arch: [''] + # specify all jobs with `include` to avoid testing duplicated things include: - - test_task: "check" - configure: "" + - test_task: check + - test_task: check arch: i686 - - test_task: "check" + configure: '' # test without -DRUBY_DEBUG as well + - test_task: check configure: "--enable-shared --enable-load-relative" - skipped_tests: "TestGem#test_.*_from_binstubs.*" - continue-on-skipped_tests: true - - test_task: "test-all TESTS=--repeat-count=2" + - test_task: test-all TESTS=--repeat-count=2 + - test_task: test-syntax-suggest + - test_task: test-bundler-parallel + - test_task: test-bundled-gems fail-fast: false env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} RUBY_DEBUG: ci SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }} - runs-on: ${{ matrix.os || 'ubuntu-20.04' }} + runs-on: ubuntu-20.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build working-directory: - name: Set ENV - env: - configure: ${{matrix.configure}} run: | echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - name: Install libraries @@ -120,7 +121,7 @@ jobs: payload: | { "ci": "GitHub Actions", - "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}", + "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }}", "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "commit": "${{ github.sha }}", "branch": "${{ github.ref }}".split('/').reverse()[0] diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4418c78d813cf2..ab9e35d5a303ba 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -128,14 +128,12 @@ jobs: YACC: win_bison - run: nmake test timeout-minutes: 5 + - run: nmake test-spec + timeout-minutes: 10 - run: nmake test-all env: RUBY_TESTOPTS: -j${{env.TEST_JOBS}} --job-status=normal timeout-minutes: 60 - continue-on-error: ${{ matrix.continue-on-error || false }} - - run: nmake test-spec - timeout-minutes: 10 - continue-on-error: ${{ matrix.continue-on-error || false }} - uses: k0kubun/action-slack@v2.0.0 with: payload: | diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index bf90b80efb1ee0..ae108d72a5bbbc 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -59,10 +59,16 @@ jobs: - test_task: "test-bundled-gems" configure: "--enable-yjit=dev" + + - test_task: "yjit-bench" + configure: "--enable-yjit=dev" + yjit_bench_opts: "--yjit-stats" env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} RUN_OPTS: ${{ matrix.yjit_opts }} + YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }} RUBY_DEBUG: ci + BUNDLE_JOBS: 8 # for yjit-bench runs-on: ubuntu-20.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: @@ -98,7 +104,7 @@ jobs: - run: ./autogen.sh working-directory: src - name: Run configure - run: ../src/configure -C --disable-install-doc ${{ matrix.configure }} + run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }} - run: make incs - run: make prepare-gems if: ${{ matrix.test_task == 'test-bundled-gems' }} @@ -111,18 +117,26 @@ jobs: if: ${{ matrix.test_task == 'check' }} - name: Enable YJIT through ENV run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV - - run: make -s ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" + - name: make ${{ matrix.test_task }} + run: make -s -j ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS" timeout-minutes: 60 env: RUBY_TESTOPTS: "-q --tty=no" TEST_BUNDLED_GEMS_ALLOW_FAILURES: "" PRECHECK_BUNDLED_GEMS: "no" + continue-on-error: ${{ matrix.test_task == 'yjit-bench' }} + - name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison + run: echo "https://github.com/${BASE_REPO}/commit/${BASE_SHA}" + env: + BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + if: ${{ matrix.test_task == 'yjit-bench' && startsWith(github.event_name, 'pull') }} - uses: k0kubun/action-slack@v2.0.0 with: payload: | { "ci": "GitHub Actions", - "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}", + "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}", "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "commit": "${{ github.sha }}", "branch": "${{ github.ref }}".split('/').reverse()[0] diff --git a/.gitignore b/.gitignore index 52089ac21cf3a7..4bbf24f0941934 100644 --- a/.gitignore +++ b/.gitignore @@ -233,10 +233,13 @@ lcov*.info /win32/*.ico # MJIT -/rb_mjit_header.h -/mjit_config.h /include/ruby-*/*/rb_mjit_min_header-*.h -/mjit_instruction.rb +/lib/mjit/instruction.rb +/mjit_config.h +/rb_mjit_header.h + +# YJIT +/yjit-bench # /wasm/ /wasm/tests/*.wasm diff --git a/.travis.yml b/.travis.yml index 6875c766a9f00f..665feba914a28b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -113,6 +113,7 @@ env: libncursesw5-dev:armhf libreadline-dev:armhf libssl-dev:armhf + libyaml-dev:armhf linux-libc-dev:armhf zlib1g-dev:armhf @@ -170,9 +171,9 @@ before_script: else mkdir build fi - - mkdir config_1st config_2nd + - mkdir config_1st config_2nd gems/src - chmod -R a-w . - - chmod -R u+w build config_1st config_2nd + - chmod -R u+w build config_1st config_2nd gems/src - cd build - |- case "$CC" in diff --git a/NEWS.md b/NEWS.md index 4dd8cbbe2b2eca..82558f8e9dc85e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -90,12 +90,22 @@ Note that each entry is kept to a minimum, see links for details. foo(k: 1) ``` +* `eval` and related methods are able to generate code coverage. + [[Feature #19008]] + ## Command line options ## Core classes updates Note: We're only listing outstanding class updates. +* Encoding + * Encoding#replicate has been deprecated and will be removed in 3.3. [[Feature #18949]] + * The dummy `Encoding::UTF_16` and `Encoding::UTF_32` encodings no longer + try to dynamically guess the endian based on a byte order mark. + Use `Encoding::UTF_16BE/UTF_16LE` and `Encoding::UTF_32BE/UTF_32LE` instead. + This change speeds up getting the encoding of a String. [[Feature #18949]] + * Enumerator * Enumerator.product has been added. Enumerator::Product is the implementation. [[Feature #18685]] @@ -124,10 +134,13 @@ Note: We're only listing outstanding class updates. * Proc#dup returns an instance of subclass. [[Bug #17545]] * Proc#parameters now accepts lambda keyword. [[Feature #15357]] +* Process + * Added `RLIMIT_NPTS` constant to FreeBSD platform + * Regexp * Regexp.new now supports passing the regexp flags not only as an Integer, - but also as a String Unknown flags raise errors. Otherwise, anything - other than `true`, `false`, `nil` or Integer will be warned. + but also as a String. Unknown flags raise ArgumentError. + Otherwise, anything other than `true`, `false`, `nil` or Integer will be warned. [[Feature #18788]] * Refinement @@ -137,6 +150,16 @@ Note: We're only listing outstanding class updates. * Set is now available as a built-in class without the need for `require "set"`. [[Feature #16989]] It is currently autoloaded via the `Set` constant or a call to `Enumerable#to_set`. +* Socket + * Added the following constants for supported platforms. + * `SO_INCOMING_CPU` + * `SO_INCOMING_NAPI_ID` + * `SO_RTABLE` + * `SO_SETFIB` + * `SO_USER_COOKIE` + * `TCP_KEEPALIVE` + * `TCP_CONNECTION_INFO` + * String * String#byteindex and String#byterindex have been added. [[Feature #13110]] * Update Unicode to Version 14.0.0 and Emoji Version 14.0. [[Feature #18037]] @@ -165,7 +188,7 @@ Note: We're only listing outstanding class updates. * RubyGems 3.4.0.dev * bigdecimal 3.1.2 * bundler 2.4.0.dev - * cgi 0.3.2 + * cgi 0.3.3 * date 3.2.3 * error_highlight 0.4.0 * etc 1.4.0 @@ -292,3 +315,5 @@ The following deprecated APIs are removed. [Feature #18788]: https://bugs.ruby-lang.org/issues/18788 [Feature #18809]: https://bugs.ruby-lang.org/issues/18809 [Feature #18481]: https://bugs.ruby-lang.org/issues/18481 +[Feature #18949]: https://bugs.ruby-lang.org/issues/18949 +[Feature #19008]: https://bugs.ruby-lang.org/issues/19008 \ No newline at end of file diff --git a/README.md b/README.md index da2bbbdd22dfde..35ef2d9e8989bc 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ to see the list of branches: You may also want to use https://git.ruby-lang.org/ruby.git (actual master of Ruby source) if you are a committer. +## How to build + +see [Building Ruby](doc/contributing/building_ruby.md) + ## Ruby home page https://www.ruby-lang.org/ diff --git a/benchmark/buffer_each.yml b/benchmark/buffer_each.yml new file mode 100644 index 00000000000000..417941104ecef2 --- /dev/null +++ b/benchmark/buffer_each.yml @@ -0,0 +1,27 @@ +prelude: | + # frozen_string_literal: true + Warning[:experimental] = false + string = "The quick brown fox jumped over the lazy dog." + array = string.bytes + buffer = IO::Buffer.for(string) +benchmark: + string.each_byte: | + upcased = String.new + string.each_byte do |byte| + upcased << (byte ^ 32) + end + array.each: | + upcased = String.new + array.each do |byte| + upcased << (byte ^ 32) + end + buffer.each: | + upcased = String.new + buffer.each(:U8) do |offset, byte| + upcased << (byte ^ 32) + end + buffer.each_byte: | + upcased = String.new + buffer.each_byte do |byte| + upcased << (byte ^ 32) + end diff --git a/benchmark/buffer_get.yml b/benchmark/buffer_get.yml index bb9ca7e94aca7f..9e1f99d64e8643 100644 --- a/benchmark/buffer_get.yml +++ b/benchmark/buffer_get.yml @@ -1,10 +1,25 @@ prelude: | # frozen_string_literal: true Warning[:experimental] = false - buffer = IO::Buffer.new(32, IO::Buffer::MAPPED) - string = "\0" * 32 + string = "The quick brown fox jumped over the lazy dog." + buffer = IO::Buffer.for(string) + format = [:U32, :U32, :U32, :U32] benchmark: - buffer.get_value: | - buffer.get_value(:U32, 0) string.unpack1: | - string.unpack1("N") + [ + string.unpack1("N"), + string.unpack1("N", offset: 4), + string.unpack1("N", offset: 8), + string.unpack1("N", offset: 12), + ] + buffer.get_value: | + [ + buffer.get_value(:U32, 0), + buffer.get_value(:U32, 4), + buffer.get_value(:U32, 8), + buffer.get_value(:U32, 12), + ] + buffer.get_values: | + buffer.get_values(format, 0) + string.unpack: | + string.unpack("NNNN") diff --git a/benchmark/string_concat.yml b/benchmark/string_concat.yml index b8a69ed909f2a4..e65c00cca97650 100644 --- a/benchmark/string_concat.yml +++ b/benchmark/string_concat.yml @@ -33,3 +33,13 @@ benchmark: buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK + interpolation: | + buffer = "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \ + "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" diff --git a/benchmark/vm_freezeobj.yml b/benchmark/vm_freezeobj.yml new file mode 100644 index 00000000000000..69a795a35403c0 --- /dev/null +++ b/benchmark/vm_freezeobj.yml @@ -0,0 +1,6 @@ +prelude: | + objs = 100000.times.map { Object.new } +benchmark: + vm_freeze_obj: | + objs.map(&:freeze) +loop_count: 600 diff --git a/benchmark/vm_ivar_get.yml b/benchmark/vm_ivar_get.yml new file mode 100644 index 00000000000000..9174af6965490c --- /dev/null +++ b/benchmark/vm_ivar_get.yml @@ -0,0 +1,37 @@ +prelude: | + class Example + def initialize + @v0 = 1 + @v1 = 2 + @v3 = 3 + @levar = 1 + end + + def get_value_loop + sum = 0 + + i = 0 + while i < 1000000 + # 10 times to de-emphasize loop overhead + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + i += 1 + end + + return sum + end + end + + obj = Example.new +benchmark: + vm_ivar_get: | + obj.get_value_loop +loop_count: 100 diff --git a/benchmark/vm_ivar_get_unintialized.yml b/benchmark/vm_ivar_get_unintialized.yml new file mode 100644 index 00000000000000..a1ccfb06ce9cae --- /dev/null +++ b/benchmark/vm_ivar_get_unintialized.yml @@ -0,0 +1,12 @@ +prelude: | + class Example + def read + @uninitialized + end + end + + obj = Example.new +benchmark: + vm_ivar_get_uninitialized: | + obj.read +loop_count: 30000000 diff --git a/benchmark/vm_ivar_lazy_set.yml b/benchmark/vm_ivar_lazy_set.yml new file mode 100644 index 00000000000000..7372ffcfbcad56 --- /dev/null +++ b/benchmark/vm_ivar_lazy_set.yml @@ -0,0 +1,12 @@ +prelude: | + class Example + def lazy_set + @uninitialized ||= 123 + end + end + + objs = 10000000.times.map { Example.new } +benchmark: + vm_ivar_lazy_set: | + objs.each(&:lazy_set) +loop_count: 1 diff --git a/benchmark/vm_lvar_cond_set.yml b/benchmark/vm_lvar_cond_set.yml new file mode 100644 index 00000000000000..1845f9d12ede19 --- /dev/null +++ b/benchmark/vm_lvar_cond_set.yml @@ -0,0 +1,8 @@ +benchmark: + vm_lvar_cond_set: | + a ||= 1 + b ||= 1 + c ||= 1 + d ||= 1 + nil +loop_count: 30000000 diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb index 794757a8b1ed58..df1b7a4aaa7502 100755 --- a/bootstraptest/runner.rb +++ b/bootstraptest/runner.rb @@ -154,8 +154,7 @@ def main end } if tests and not ARGV.empty? - $stderr.puts "--tests and arguments are exclusive" - exit false + abort "--sets and arguments are exclusive" end tests ||= ARGV tests = Dir.glob("#{File.dirname($0)}/test_*.rb").sort if tests.empty? diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 4d7780d1d45a2a..99a26f85d54fb9 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -3257,3 +3257,57 @@ def foo foo foo } + +# respond_to? with changing symbol +assert_equal 'false', %q{ + def foo(name) + :sym.respond_to?(name) + end + foo(:to_s) + foo(:to_s) + foo(:not_exist) +} + +# respond_to? with method being defined +assert_equal 'true', %q{ + def foo + :sym.respond_to?(:not_yet_defined) + end + foo + foo + module Kernel + def not_yet_defined = true + end + foo +} + +# respond_to? with undef method +assert_equal 'false', %q{ + module Kernel + def to_be_removed = true + end + def foo + :sym.respond_to?(:to_be_removed) + end + foo + foo + class Object + undef_method :to_be_removed + end + foo +} + +# respond_to? with respond_to_missing? +assert_equal 'true', %q{ + class Foo + end + def foo(x) + x.respond_to?(:bar) + end + foo(Foo.new) + foo(Foo.new) + class Foo + def respond_to_missing?(*) = true + end + foo(Foo.new) +} diff --git a/builtin.h b/builtin.h index b827b289281674..7ad52f44f47b7b 100644 --- a/builtin.h +++ b/builtin.h @@ -17,7 +17,7 @@ struct rb_builtin_function { }; #define RB_BUILTIN_FUNCTION(_i, _name, _fname, _arity, _compiler) {\ - .name = #_name, \ + .name = _i < 0 ? NULL : #_name, \ .func_ptr = (void *)_fname, \ .argc = _arity, \ .index = _i, \ diff --git a/class.c b/class.c index 5e57068f03c5ec..3a83a7f052e0f2 100644 --- a/class.c +++ b/class.c @@ -423,8 +423,6 @@ copy_tables(VALUE clone, VALUE orig) st_delete(RCLASS_IV_TBL(clone), &id, 0); CONST_ID(id, "__classpath__"); st_delete(RCLASS_IV_TBL(clone), &id, 0); - CONST_ID(id, "__classid__"); - st_delete(RCLASS_IV_TBL(clone), &id, 0); } if (RCLASS_CONST_TBL(orig)) { struct clone_const_arg arg; diff --git a/common.mk b/common.mk index fe4fbce0381edf..0ee605933aed0c 100644 --- a/common.mk +++ b/common.mk @@ -19,7 +19,9 @@ gnumake_recursive = enable_shared = $(ENABLE_SHARED:no=) UNICODE_VERSION = 14.0.0 -UNICODE_EMOJI_VERSION = 14.0 +UNICODE_EMOJI_VERSION_0 = $(UNICODE_VERSION)/// +UNICODE_EMOJI_VERSION_1 = $(UNICODE_EMOJI_VERSION_0:.0///=) +UNICODE_EMOJI_VERSION = $(UNICODE_EMOJI_VERSION_1:///=) UNICODE_BETA = NO ### set the following environment variable or uncomment the line if @@ -217,12 +219,26 @@ MAKE_LINK = $(MINIRUBY) -rfileutils -e "include FileUtils::Verbose" \ -e "noraise {ln(src, dest)} or" \ -e "cp(src, dest)" +# For release builds +YJIT_RUSTC_ARGS = --crate-name=yjit \ + --crate-type=staticlib \ + --edition=2021 \ + -C opt-level=3 \ + -C overflow-checks=on \ + '--out-dir=$(CARGO_TARGET_DIR)/release/' \ + $(top_srcdir)/yjit/src/lib.rs all: $(SHOWFLAGS) main docs main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs @$(NULLCMD) +main: $(srcdir)/lib/mjit/instruction.rb +srcs: $(srcdir)/lib/mjit/instruction.rb +$(srcdir)/lib/mjit/instruction.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/views/lib/mjit/instruction.rb.erb $(srcdir)/insns.def + $(ECHO) generating $@ + $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ + mjit-headers: $(MJIT_SUPPORT)-mjit-headers no-mjit-headers: PHONY yes-mjit-headers: mjit_config.h PHONY @@ -783,6 +799,9 @@ $(arch:noarch=ignore)-fake.rb: $(srcdir)/template/fake.rb.in $(tooldir)/generic_ $(BOOTSTRAPRUBY) "$(tooldir)/generic_erb.rb" -o $@ "$(srcdir)/template/fake.rb.in" \ i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)" +noarch-fake.rb: # prerequisite of yes-fake + touch $@ + btest: $(TEST_RUNNABLE)-btest no-btest: PHONY yes-btest: yes-fake miniruby$(EXEEXT) PHONY @@ -933,7 +952,7 @@ PHONY: {$(srcdir)}.y.c: $(ECHO) generating $@ - $(Q)$(BASERUBY) $(tooldir)/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ --vpath=$(VPATH) id.h $(SRC_FILE) > parse.tmp.y + $(Q)$(BASERUBY) $(tooldir)/id2token.rb $(SRC_FILE) > parse.tmp.y $(Q)$(BASERUBY) $(tooldir)/pure_parser.rb parse.tmp.y $(YACC) $(Q)$(RM) parse.tmp.y.bak $(Q)$(YACC) -d $(YFLAGS) -o y.tab.c parse.tmp.y @@ -1075,8 +1094,8 @@ BUILTIN_RB_SRCS = \ $(srcdir)/io.rb \ $(srcdir)/marshal.rb \ $(srcdir)/mjit.rb \ + $(srcdir)/mjit_c.rb \ $(srcdir)/mjit_compiler.rb \ - $(srcdir)/mjit_instruction.rb \ $(srcdir)/pack.rb \ $(srcdir)/trace_point.rb \ $(srcdir)/warning.rb \ @@ -1168,7 +1187,7 @@ vm_call_iseq_optimized.inc: $(srcdir)/template/call_iseq_optimized.inc.tmpl $(ECHO) generating $@ $(Q) $(BASERUBY) $(tooldir)/generic_erb.rb -c -o $@ $(srcdir)/template/call_iseq_optimized.inc.tmpl -$(MINIPRELUDE_C): $(COMPILE_PRELUDE) $(BUILTIN_RB_SRCS) $(srcdir)/mjit_instruction.rb +$(MINIPRELUDE_C): $(COMPILE_PRELUDE) $(BUILTIN_RB_SRCS) $(ECHO) generating $@ $(Q) $(BASERUBY) $(tooldir)/generic_erb.rb -I$(srcdir) -o $@ \ $(srcdir)/template/prelude.c.tmpl $(BUILTIN_RB_SRCS) @@ -1206,12 +1225,9 @@ builtin_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/template/builtin_binary $(BUILTIN_RB_INCS): $(top_srcdir)/tool/mk_builtin_loader.rb -$(srcdir)/revision.h: -$(srcdir)/revision.h$(gnumake:yes=-nongnumake): - $(Q)$(RM) $(@F) - $(Q)$(NULLCMD) > $@ || $(NULLCMD) > $(@F) +$(srcdir)/revision.h: $(REVISION_H) -revision.tmp:: +revision.$(HAVE_BASERUBY:no=tmp):: $(Q) $(NULLCMD) > $@ revision.$(HAVE_BASERUBY:yes=tmp):: $(srcdir)/version.h $(tooldir)/file2lastrev.rb $(REVISION_FORCE) $(Q) $(BASERUBY) $(tooldir)/file2lastrev.rb -q --revision.h --srcdir="$(srcdir)" > $@ @@ -1219,10 +1235,10 @@ revision.$(HAVE_BASERUBY:yes=tmp):: $(srcdir)/version.h $(tooldir)/file2lastrev. $(REVISION_H): revision.tmp $(Q)$(IFCHANGE) "--timestamp=$@" "$(srcdir)/revision.h" revision.tmp -$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y id.h $(srcdir)/ext/ripper/depend +$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y $(srcdir)/defs/id.def $(srcdir)/ext/ripper/depend $(ECHO) generating $@ $(Q) $(CHDIR) $(@D) && \ - sed -e 's/{\$$([^(){}]*)[^{}]*}//g' -e /AUTOGENERATED/q depend | \ + sed -e "s/{\$$([^(){}]*)[^{}]*}//g" -e /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) RM="$(RM1)" BISON=$(YACC) top_srcdir=../.. srcdir=. VPATH=../.. \ RUBY="$(BASERUBY)" PATH_SEPARATOR="$(PATH_SEPARATOR)" LANG=C @@ -1241,7 +1257,7 @@ $(srcdir)/ext/rbconfig/sizeof/sizes.c: $(srcdir)/ext/rbconfig/sizeof/depend \ $(tooldir)/generic_erb.rb $(srcdir)/template/sizes.c.tmpl $(srcdir)/configure.ac $(ECHO) generating $@ $(Q) $(CHDIR) $(@D) && \ - sed '/AUTOGENERATED/q' depend | \ + sed /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F) @@ -1249,19 +1265,19 @@ $(srcdir)/ext/rbconfig/sizeof/limits.c: $(srcdir)/ext/rbconfig/sizeof/depend \ $(tooldir)/generic_erb.rb $(srcdir)/template/limits.c.tmpl $(ECHO) generating $@ $(Q) $(CHDIR) $(@D) && \ - sed '/AUTOGENERATED/q' depend | \ + sed /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F) $(srcdir)/ext/socket/constdefs.c: $(srcdir)/ext/socket/depend $(Q) $(CHDIR) $(@D) && \ - sed '/AUTOGENERATED/q' depend | \ + sed /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)" $(srcdir)/ext/etc/constdefs.h: $(srcdir)/ext/etc/depend $(Q) $(CHDIR) $(@D) && \ - sed '/AUTOGENERATED/q' depend | \ + sed /AUTOGENERATED/q depend | \ $(exec) $(MAKE) -f - $(mflags) \ Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)" @@ -1458,7 +1474,7 @@ no-test-syntax-suggest: test-bundler-precheck: $(TEST_RUNNABLE)-test-bundler-precheck no-test-bundler-precheck: -yes-test-bundler-precheck: main +yes-test-bundler-precheck: main $(arch)-fake.rb no-test-bundler-prepare: no-test-bundler-precheck yes-test-bundler-prepare: yes-test-bundler-precheck @@ -1475,14 +1491,18 @@ RSPECOPTS = BUNDLER_SPECS = test-bundler: $(TEST_RUNNABLE)-test-bundler yes-test-bundler: yes-test-bundler-prepare - $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \ + $(gnumake_recursive)$(XRUBY) \ + -r./$(arch)-fake \ + -e "exec(*ARGV)" -- \ + $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \ --require spec_helper $(RSPECOPTS) spec/bundler/$(BUNDLER_SPECS) no-test-bundler: PARALLELRSPECOPTS = --runtime-log $(srcdir)/tmp/parallel_runtime_rspec.log test-bundler-parallel: $(TEST_RUNNABLE)-test-bundler-parallel yes-test-bundler-parallel: yes-test-bundler-prepare - $(XRUBY) \ + $(gnumake_recursive)$(XRUBY) \ + -r./$(arch)-fake \ -e "ARGV[-1] = File.expand_path(ARGV[-1])" \ -e "exec(*ARGV)" -- \ $(XRUBY) -I$(srcdir)/spec/bundler \ @@ -1541,34 +1561,28 @@ update-unicode: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES) \ $(UNICODE_AUXILIARY_FILES) $(UNICODE_UCD_EMOJI_FILES) $(UNICODE_EMOJI_FILES) CACHE_DIR = $(srcdir)/.downloaded-cache -UNICODE_DOWNLOAD = \ +UNICODE_DOWNLOADER_ALWAYS_UPDATE = $(ALWAYS_UPDATE_UNICODE:yes=--always) +UNICODE_DOWNLOADER = \ $(BASERUBY) $(tooldir)/downloader.rb \ --cache-dir=$(CACHE_DIR) \ - --unicode-beta $(UNICODE_BETA) \ + --exist $(UNICODE_DOWNLOADER_ALWAYS_UPDATE:no=) \ + unicode --unicode-beta=$(UNICODE_BETA) +UNICODE_DOWNLOAD = \ + $(UNICODE_DOWNLOADER) \ -d $(UNICODE_SRC_DATA_DIR) \ - -p $(UNICODE_VERSION)/ucd \ - -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode + -p $(UNICODE_VERSION)/ucd UNICODE_AUXILIARY_DOWNLOAD = \ - $(BASERUBY) $(tooldir)/downloader.rb \ - --cache-dir=$(CACHE_DIR) \ - --unicode-beta $(UNICODE_BETA) \ + $(UNICODE_DOWNLOADER) \ -d $(UNICODE_SRC_DATA_DIR)/auxiliary \ - -p $(UNICODE_VERSION)/ucd/auxiliary \ - -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode + -p $(UNICODE_VERSION)/ucd/auxiliary UNICODE_UCD_EMOJI_DOWNLOAD = \ - $(BASERUBY) $(tooldir)/downloader.rb \ - --cache-dir=$(CACHE_DIR) \ - --unicode-beta $(UNICODE_BETA) \ + $(UNICODE_DOWNLOADER) \ -d $(UNICODE_SRC_DATA_DIR)/emoji \ - -p $(UNICODE_VERSION)/ucd/emoji \ - -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode + -p $(UNICODE_VERSION)/ucd/emoji UNICODE_EMOJI_DOWNLOAD = \ - $(BASERUBY) $(tooldir)/downloader.rb \ - --cache-dir=$(CACHE_DIR) \ - --unicode-beta $(UNICODE_BETA) \ + $(UNICODE_DOWNLOADER) \ -d $(UNICODE_SRC_EMOJI_DATA_DIR) \ - -p emoji/$(UNICODE_EMOJI_VERSION) \ - -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode + -p emoji/$(UNICODE_EMOJI_VERSION) $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES): update-unicode-files update-unicode-files: @@ -1635,19 +1649,19 @@ $(UNICODE_HDR_DIR)/name2ctype.h: $(MV) $@.new $@ # the next non-comment line was: -# $(UNICODE_HDR_DIR)/casefold.h: $(srcdir)/enc/unicode/case-folding.rb \ +# $(UNICODE_HDR_DIR)/casefold.h: $(tooldir)/enc-case-folding.rb \ # but was changed to make sure CI works on systems that don't have gperf unicode-up: $(UNICODE_DATA_HEADERS) $(UNICODE_HDR_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=casefold.h): \ - $(srcdir)/enc/unicode/case-folding.rb \ + $(tooldir)/enc-case-folding.rb \ $(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \ $(UNICODE_SRC_DATA_DIR)/SpecialCasing.txt \ $(UNICODE_SRC_DATA_DIR)/CaseFolding.txt $(UNICODE_HDR_DIR)/casefold.h: $(MAKEDIRS) $(@D) - $(Q) $(BASERUBY) $(srcdir)/enc/unicode/case-folding.rb \ + $(Q) $(BASERUBY) $(tooldir)/enc-case-folding.rb \ --output-file=$@ \ --mapping-data-directory=$(UNICODE_SRC_DATA_DIR) @@ -3362,6 +3376,7 @@ cont.$(OBJEXT): $(top_srcdir)/internal/cont.h cont.$(OBJEXT): $(top_srcdir)/internal/gc.h cont.$(OBJEXT): $(top_srcdir)/internal/imemo.h cont.$(OBJEXT): $(top_srcdir)/internal/proc.h +cont.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h cont.$(OBJEXT): $(top_srcdir)/internal/serial.h cont.$(OBJEXT): $(top_srcdir)/internal/static_assert.h cont.$(OBJEXT): $(top_srcdir)/internal/vm.h @@ -5418,6 +5433,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/class.h encoding.$(OBJEXT): $(top_srcdir)/internal/compilers.h encoding.$(OBJEXT): $(top_srcdir)/internal/enc.h encoding.$(OBJEXT): $(top_srcdir)/internal/encoding.h +encoding.$(OBJEXT): $(top_srcdir)/internal/error.h encoding.$(OBJEXT): $(top_srcdir)/internal/gc.h encoding.$(OBJEXT): $(top_srcdir)/internal/inits.h encoding.$(OBJEXT): $(top_srcdir)/internal/load.h @@ -7653,11 +7669,13 @@ io.$(OBJEXT): {$(VPATH)}util.h io.$(OBJEXT): {$(VPATH)}vm_core.h io.$(OBJEXT): {$(VPATH)}vm_opts.h io_buffer.$(OBJEXT): $(hdrdir)/ruby/ruby.h +io_buffer.$(OBJEXT): $(top_srcdir)/internal/array.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/bits.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/compilers.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/error.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/static_assert.h io_buffer.$(OBJEXT): $(top_srcdir)/internal/string.h +io_buffer.$(OBJEXT): $(top_srcdir)/internal/thread.h io_buffer.$(OBJEXT): {$(VPATH)}assert.h io_buffer.$(OBJEXT): {$(VPATH)}backward/2/assume.h io_buffer.$(OBJEXT): {$(VPATH)}backward/2/attributes.h @@ -8265,6 +8283,7 @@ load.$(OBJEXT): {$(VPATH)}vm_core.h load.$(OBJEXT): {$(VPATH)}vm_opts.h loadpath.$(OBJEXT): $(hdrdir)/ruby/ruby.h loadpath.$(OBJEXT): $(hdrdir)/ruby/version.h +loadpath.$(OBJEXT): $(top_srcdir)/revision.h loadpath.$(OBJEXT): $(top_srcdir)/version.h loadpath.$(OBJEXT): {$(VPATH)}assert.h loadpath.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -9294,7 +9313,7 @@ miniinit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h miniinit.$(OBJEXT): $(CCAN_DIR)/list/list.h miniinit.$(OBJEXT): $(CCAN_DIR)/str/str.h miniinit.$(OBJEXT): $(hdrdir)/ruby/ruby.h -miniinit.$(OBJEXT): $(srcdir)/mjit_instruction.rb +miniinit.$(OBJEXT): $(srcdir)/mjit_c.rb miniinit.$(OBJEXT): $(top_srcdir)/internal/array.h miniinit.$(OBJEXT): $(top_srcdir)/internal/compilers.h miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h @@ -9485,8 +9504,8 @@ miniinit.$(OBJEXT): {$(VPATH)}miniinit.c miniinit.$(OBJEXT): {$(VPATH)}miniprelude.c miniinit.$(OBJEXT): {$(VPATH)}missing.h miniinit.$(OBJEXT): {$(VPATH)}mjit.rb +miniinit.$(OBJEXT): {$(VPATH)}mjit_c.rb miniinit.$(OBJEXT): {$(VPATH)}mjit_compiler.rb -miniinit.$(OBJEXT): {$(VPATH)}mjit_instruction.rb miniinit.$(OBJEXT): {$(VPATH)}nilclass.rb miniinit.$(OBJEXT): {$(VPATH)}node.h miniinit.$(OBJEXT): {$(VPATH)}numeric.rb @@ -9740,7 +9759,7 @@ mjit_compiler.$(OBJEXT): $(CCAN_DIR)/list/list.h mjit_compiler.$(OBJEXT): $(CCAN_DIR)/str/str.h mjit_compiler.$(OBJEXT): $(hdrdir)/ruby.h mjit_compiler.$(OBJEXT): $(hdrdir)/ruby/ruby.h -mjit_compiler.$(OBJEXT): $(srcdir)/mjit_instruction.rb +mjit_compiler.$(OBJEXT): $(srcdir)/mjit_c.rb mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/array.h mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/class.h mjit_compiler.$(OBJEXT): $(top_srcdir)/internal/compile.h @@ -9921,12 +9940,13 @@ mjit_compiler.$(OBJEXT): {$(VPATH)}iseq.h mjit_compiler.$(OBJEXT): {$(VPATH)}method.h mjit_compiler.$(OBJEXT): {$(VPATH)}missing.h mjit_compiler.$(OBJEXT): {$(VPATH)}mjit.h +mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_c.rb +mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_c.rbinc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compile_attr.inc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.c mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.h mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rb mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rbinc -mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_instruction.rbinc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_unit.h mjit_compiler.$(OBJEXT): {$(VPATH)}node.h mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_assert.h diff --git a/compile.c b/compile.c index b495d8ced80527..a5da919c0ae4e9 100644 --- a/compile.c +++ b/compile.c @@ -766,7 +766,7 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) end->rescued = LABEL_RESCUE_END; ADD_TRACE(ret, RUBY_EVENT_B_CALL); - NODE dummy_line_node = generate_dummy_line_node(FIX2INT(ISEQ_BODY(iseq)->location.first_lineno), -1); + NODE dummy_line_node = generate_dummy_line_node(ISEQ_BODY(iseq)->location.first_lineno, -1); ADD_INSN (ret, &dummy_line_node, nop); ADD_LABEL(ret, start); CHECK(COMPILE(ret, "block body", node->nd_body)); @@ -1333,7 +1333,7 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node, int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth; ret_iseq = rb_iseq_new_with_opt(&ast, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(line_no), parent, + line_no, parent, isolated_depth ? isolated_depth + 1 : 0, type, ISEQ_COMPILE_DATA(iseq)->option); debugs("[new_child_iseq]< ---------------------------------------\n"); @@ -1349,7 +1349,7 @@ new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_call debugs("[new_child_iseq_with_callback]> ---------------------------------------\n"); ret_iseq = rb_iseq_new_with_callback(ifunc, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option); + line_no, parent, type, ISEQ_COMPILE_DATA(iseq)->option); debugs("[new_child_iseq_with_callback]< ---------------------------------------\n"); return ret_iseq; } @@ -2308,9 +2308,9 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) if (ISEQ_COVERAGE(iseq)) { if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) && !(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) { - int line = iobj->insn_info.line_no; - if (line >= 1) { - RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, INT2FIX(0)); + int line = iobj->insn_info.line_no - 1; + if (line >= 0 && line < RARRAY_LEN(ISEQ_LINE_COVERAGE(iseq))) { + RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line, INT2FIX(0)); } } if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) { @@ -3571,6 +3571,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (IS_INSN_ID(iobj, dup)) { if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) { LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL; + + /* + * dup + * setlocal x, y + * setlocal x, y + * => + * dup + * setlocal x, y + */ if (IS_NEXT_INSN_ID(set1, setlocal)) { set2 = set1->next; if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) && @@ -3579,6 +3588,16 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal ELEM_REMOVE(&iobj->link); } } + + /* + * dup + * setlocal x, y + * dup + * setlocal x, y + * => + * dup + * setlocal x, y + */ else if (IS_NEXT_INSN_ID(set1, dup) && IS_NEXT_INSN_ID(set1->next, setlocal)) { set2 = set1->next->next; @@ -3591,6 +3610,13 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * getlocal x, y + * dup + * setlocal x, y + * => + * dup + */ if (IS_INSN_ID(iobj, getlocal)) { LINK_ELEMENT *niobj = &iobj->link; if (IS_NEXT_INSN_ID(niobj, dup)) { @@ -3606,6 +3632,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * opt_invokebuiltin_delegate + * trace + * leave + * => + * opt_invokebuiltin_delegate_leave + * trace + * leave + */ if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) { if (IS_TRACE(iobj->link.next)) { if (IS_NEXT_INSN_ID(iobj->link.next, leave)) { @@ -3614,6 +3649,13 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * getblockparam + * branchif / branchunless + * => + * getblockparamproxy + * branchif / branchunless + */ if (IS_INSN_ID(iobj, getblockparam)) { if (IS_NEXT_INSN_ID(&iobj->link, branchif) || IS_NEXT_INSN_ID(&iobj->link, branchunless)) { iobj->insn_id = BIN(getblockparamproxy); @@ -8189,7 +8231,7 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N ISEQ_BODY(iseq)->mandatory_only_iseq = rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq), rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(nd_line(line_node)), NULL, 0, + nd_line(line_node), NULL, 0, ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option); GET_VM()->builtin_inline_index = prev_inline_index; @@ -8621,6 +8663,17 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node lfin: # o ? pop # o + # or (popped) + if lcfin # r + eval v # r v + send a= # ? + jump lfin # ? + + lcfin: # r + + lfin: # ? + pop # + # and dup # r o o unless lcfin @@ -8649,32 +8702,32 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag)); if (atype == idOROP || atype == idANDOP) { - ADD_INSN(ret, node, dup); + if (!popped) { + ADD_INSN(ret, node, dup); + } if (atype == idOROP) { ADD_INSNL(ret, node, branchif, lcfin); } else { /* idANDOP */ ADD_INSNL(ret, node, branchunless, lcfin); } - ADD_INSN(ret, node, pop); + if (!popped) { + ADD_INSN(ret, node, pop); + } CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value)); - ADD_INSN(ret, node, swap); - ADD_INSN1(ret, node, topn, INT2FIX(1)); + if (!popped) { + ADD_INSN(ret, node, swap); + ADD_INSN1(ret, node, topn, INT2FIX(1)); + } ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag)); ADD_INSNL(ret, node, jump, lfin); ADD_LABEL(ret, lcfin); - ADD_INSN(ret, node, swap); + if (!popped) { + ADD_INSN(ret, node, swap); + } ADD_LABEL(ret, lfin); - ADD_INSN(ret, node, pop); - if (lskip) { - ADD_LABEL(ret, lskip); - } - if (popped) { - /* we can apply more optimize */ - ADD_INSN(ret, node, pop); - } } else { CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value)); @@ -8684,13 +8737,13 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_INSN1(ret, node, topn, INT2FIX(1)); } ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag)); - if (lskip && popped) { - ADD_LABEL(ret, lskip); - } - ADD_INSN(ret, node, pop); - if (lskip && !popped) { - ADD_LABEL(ret, lskip); - } + } + if (lskip && popped) { + ADD_LABEL(ret, lskip); + } + ADD_INSN(ret, node, pop); + if (lskip && !popped) { + ADD_LABEL(ret, lskip); } return COMPILE_OK; } @@ -8789,7 +8842,10 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, } CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head)); - ADD_INSN(ret, node, dup); + + if (!popped) { + ADD_INSN(ret, node, dup); + } if (type == NODE_OP_ASGN_AND) { ADD_INSNL(ret, node, branchunless, lfin); @@ -8798,15 +8854,13 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, ADD_INSNL(ret, node, branchif, lfin); } - ADD_INSN(ret, node, pop); - ADD_LABEL(ret, lassign); - CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value)); - ADD_LABEL(ret, lfin); - - if (popped) { - /* we can apply more optimize */ + if (!popped) { ADD_INSN(ret, node, pop); } + + ADD_LABEL(ret, lassign); + CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value, popped)); + ADD_LABEL(ret, lfin); return COMPILE_OK; } @@ -12077,7 +12131,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos); const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos); const VALUE location_label_index = ibf_load_small_value(load, &reading_pos); - const VALUE location_first_lineno = ibf_load_small_value(load, &reading_pos); + const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos); const int location_node_id = (int)ibf_load_small_value(load, &reading_pos); const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos); const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos); diff --git a/configure.ac b/configure.ac index 7519e8379d3ae6..493a2ce2550bb1 100644 --- a/configure.ac +++ b/configure.ac @@ -498,6 +498,7 @@ AS_CASE(["$target_os"], AC_DEFINE_UNQUOTED(RUBY_MSVCRT_VERSION, $RT_VER) sysconfdir= ]) + rb_cv_binary_elf=no : ${enable_shared=yes} ], [hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi @@ -607,22 +608,39 @@ RUBY_WERROR_FLAG([ cd .. && rm -fr tmp.$$.try_link ]) -: ${RPATHFLAG=''} -rpathflag='' -AS_IF([test x"${RPATHFLAG}" = x], [ - AS_CASE(["$target_os"], +: "rpath" && { + AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf, + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[ + AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"], + [.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])], + [rb_cv_binary_elf=no])]) + + rpathflag='' + AS_IF([test x"${RPATHFLAG=}" = x], [ + AS_CASE(["$target_os"], [aix*], [rpathflag='-blibpath:'], - [for rpathflag in -R "-rpath "; do + [for rpathflag in "-rpath " -R; do AS_CASE("$rpathflag", [*" "], [AS_CASE(["${linker_flag}"], [*,], [rpathflag=`echo "$rpathflag" | tr ' ' ,`])]) rpathflag="${linker_flag}${rpathflag}" RUBY_TRY_LDFLAGS([${rpathflag}.], [], [rpathflag=]) - AS_IF([test "x${rpathflag}" != x], []) + AS_IF([test "x${rpathflag}" != x], [break]) done]) -], [ - rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'` -]) + ], [ + rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'` + ]) + + AC_ARG_ENABLE(rpath, + AS_HELP_STRING([--enable-rpath], [embed run path into extension libraries. + enabled by default on ELF platforms]), + [enable_rpath=$enableval], [enable_rpath="$rb_cv_binary_elf"]) + + AS_IF([test "$enable_rpath:${RPATHFLAG}" = yes:], [ + RPATHFLAG="${rpathflag:+ ${rpathflag}%1\$-s}" + ]) + AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}]) +} RUBY_TRY_LDFLAGS(-fdeclspec, [fdeclspec=yes], [fdeclspec=no]) AS_IF([test "$fdeclspec" = yes], [ @@ -850,33 +868,6 @@ AS_IF([test "$GCC" = yes], [ # need lgamma_r() ]) - # ANSI (no XCFLAGS because this is C only) - AS_CASE(["$target_os"], - [solaris*], [ - # Because "-std=gnu99" affects existence of functions on Solaris, - # "-std=gnu99" will be appended to CPPFLAGS. - for ansi_options in -std=gnu99; do - RUBY_TRY_CFLAGS(${ansi_options}, [ - RUBY_APPEND_OPTIONS(CPPFLAGS, ${ansi_options}) - ], [ansi_options=]) - test "x${ansi_options}" = x || break - done - ], - [ - # ANSI (no XCFLAGS because this is C only) - rb_tmp_std_check=`echo $CC $CFLAGS $optflags $warnflags $debugflags | fgrep std= | tr -d '\015'` - AS_IF([test "x$rb_tmp_std_check" = "x"], - [ - for ansi_options in -std=gnu99; do - RUBY_TRY_CFLAGS(${ansi_options}, [ - RUBY_APPEND_OPTIONS(warnflags, ${ansi_options}) - RUBY_APPEND_OPTIONS(strict_warnflags, ${ansi_options}) - ], [ansi_options=]) - test "x${ansi_options}" = x || break - done - ]) - ]) - # suppress annoying -Wstrict-overflow warnings RUBY_TRY_CFLAGS(-fno-strict-overflow, [RUBY_APPEND_OPTION(XCFLAGS, -fno-strict-overflow)]) @@ -939,17 +930,34 @@ AS_CASE(["$target_cpu"], [[i[3-6]86*]], [ AS_IF([test "$rb_cv_gcc_compiler_cas" = i486], [ARCH_FLAG="-march=i486"]) ]) +OPT_DIR= +AC_ARG_WITH([gmp-dir], + AS_HELP_STRING([--with-gmp-dir=DIR], + [specify the prefix directory where gmp is installed]), + [OPT_DIR="${OPT_DIR:+$OPT_DIR$PATH_SEPARATOR}$withval"], []) +AC_ARG_WITH([gmp], + [AS_HELP_STRING([--without-gmp], + [disable GNU GMP to accelerate Bignum operations])], + [], [with_gmp=yes]) + AC_ARG_WITH(opt-dir, AS_HELP_STRING([--with-opt-dir=DIR-LIST], [add optional headers and libraries directories separated by $PATH_SEPARATOR]), - [ - val=`echo "$PATH_SEPARATOR$withval" | sed "s|$PATH_SEPARATOR\([[^$PATH_SEPARATOR]*]\)| -I\1/include|g;s/^ //"` - CPPFLAGS="$CPPFLAGS $val" - val=`echo "$PATH_SEPARATOR$withval" | sed "s|$PATH_SEPARATOR\([[^$PATH_SEPARATOR]*]\)| -L\1/lib${rpathflag:+ $rpathflag\\\\1/lib}|g;s/^ //"` - LDFLAGS="$LDFLAGS $val" - LDFLAGS_OPTDIR="$val" - OPT_DIR="$withval" - ], [OPT_DIR=]) + [OPT_DIR="${OPT_DIR:+$OPT_DIR$PATH_SEPARATOR}$withval"], []) + +AS_IF([test "x$OPT_DIR" != x], [ + val=`IFS="$PATH_SEPARATOR" + for dir in $OPT_DIR; do + test -z "$dir" && continue + echo x ${LIBPATHFLAG} ${RPATHFLAG} | + sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${dir}/lib${IFS}g;s${IFS}%s${IFS}${dir}/lib${IFS}g" + done | tr '\012' ' ' | sed 's/ *$//'` + LDFLAGS="${LDFLAGS:+$LDFLAGS }$val" + DLDFLAGS="${DLDFLAGS:+$DLDFLAGS }$val" + LDFLAGS_OPTDIR="$val" + CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }"`echo "$OPT_DIR" | tr "${PATH_SEPARATOR}" '\012' | + sed '/^$/d;s|^|-I|;s|$|/include|' | tr '\012' ' ' | sed 's/ *$//'` +]) test -z "${ac_env_CFLAGS_set}" -a -n "${cflags+set}" && eval CFLAGS="\"$cflags $ARCH_FLAG\"" test -z "${ac_env_CXXFLAGS_set}" -a -n "${cxxflags+set}" && eval CXXFLAGS="\"$cxxflags $ARCH_FLAG\"" @@ -1168,7 +1176,6 @@ main() ac_cv_func_getpgrp_void=no ac_cv_func_memcmp_working=yes ac_cv_lib_dl_dlopen=no - rb_cv_binary_elf=no rb_cv_negative_time_t=yes ac_cv_func_fcntl=yes ac_cv_func_flock=yes @@ -1322,11 +1329,6 @@ AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [ ]) RUBY_UNIVERSAL_CHECK_HEADER([x86_64, i386], x86intrin.h) -AC_ARG_WITH([gmp], - [AS_HELP_STRING([--without-gmp], - [disable GNU GMP to accelerate Bignum operations])], - [], - [with_gmp=yes]) AS_IF([test "x$with_gmp" != xno], [AC_CHECK_HEADERS(gmp.h) AS_IF([test "x$ac_cv_header_gmp_h" != xno], @@ -2878,12 +2880,6 @@ AC_ARG_WITH(dln-a-out, ]) ]) -AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf, -[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[ -AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"], -[.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])], -rb_cv_binary_elf=no)]) - AS_IF([test "$rb_cv_binary_elf" = yes], [ AC_DEFINE(USE_ELF) AC_CHECK_HEADERS([elf.h elf_abi.h]) @@ -2961,12 +2957,7 @@ STATIC= } : "rpath" && { - AC_ARG_ENABLE(rpath, - AS_HELP_STRING([--enable-rpath], [embed run path into extension libraries. - enabled by default on ELF platforms]), - [enable_rpath=$enableval], [enable_rpath="$rb_cv_binary_elf"]) - - AS_CASE(["$target_os"], + AS_CASE(["$target_os"], [solaris*], [ AS_IF([test "$GCC" = yes], [ : ${LDSHARED='$(CC) -shared'} AS_IF([test "$rb_cv_prog_gnu_ld" = yes], [ @@ -3006,7 +2997,6 @@ STATIC= rb_cv_dlopen=yes], [interix*], [ : ${LDSHARED='$(CC) -shared'} XLDFLAGS="$XLDFLAGS -Wl,-E" - LIBPATHFLAG=" -L%1\$-s" rb_cv_dlopen=yes], [freebsd*|dragonfly*], [ : ${LDSHARED='$(CC) -shared'} @@ -3066,30 +3056,35 @@ STATIC= [atheos*], [ : ${LDSHARED='$(CC) -shared'} rb_cv_dlopen=yes], [ : ${LDSHARED='$(LD)'}]) - AC_MSG_RESULT($rb_cv_dlopen) + AC_MSG_RESULT($rb_cv_dlopen) +} - AS_IF([test "$rb_cv_dlopen" = yes], [ +AS_IF([test "$rb_cv_dlopen" = yes], [ AS_CASE(["$target_os"], - [darwin*], [ + [darwin*], [ + AC_SUBST(ADDITIONAL_DLDFLAGS, "") for flag in \ - "-undefined dynamic_lookup" \ "-multiply_defined suppress" \ + "-undefined dynamic_lookup" \ ; do - test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`" - RUBY_TRY_LDFLAGS([$flag], [], [flag=]) - AS_IF([test "x$flag" != x], [ - RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag]) - ]) + test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`" + RUBY_TRY_LDFLAGS([$flag], [], [$flag=]) + AS_IF([test x"$flag" = x], [continue]) + + AC_MSG_CHECKING([whether $flag is accepted for bundle]) + : > conftest.c + AS_IF([${LDSHARED/'$(CC)'/$CC} -o conftest.bundle $flag conftest.c >/dev/null 2>conftest.err && + test ! -s conftest.err], [ + AC_MSG_RESULT([yes]) + RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag]) + ], [ + AC_MSG_RESULT([no]) + RUBY_APPEND_OPTIONS(ADDITIONAL_DLDFLAGS, [$flag]) + ]) + rm -fr conftest.* done - ]) - ]) - - AS_IF([test "$enable_rpath:${RPATHFLAG}" = yes:], [ - AS_IF([test "x$rpathflag" != x], [ - RPATHFLAG=" ${rpathflag}%1\$-s" - ]) - ]) -} + ]) +]) AS_IF([test "${LDSHAREDXX}" = ""], [ AS_CASE(["${LDSHARED}"], @@ -3105,7 +3100,6 @@ AS_IF([test "${LDSHAREDXX}" = ""], [ [ld" "*], [ ]) ]) -AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}]) AC_SUBST(LINK_SO) AC_SUBST(LIBPATHFLAG) @@ -3114,23 +3108,6 @@ AC_SUBST(LIBPATHENV, "${LIBPATHENV-LD_LIBRARY_PATH}") AC_SUBST(PRELOADENV, "${PRELOADENV-LD_PRELOAD}") AC_SUBST(TRY_LINK) -AS_IF([test "x$OPT_DIR" != x], [ - pat=`echo "${LDFLAGS_OPTDIR}" | sed ['s/[][\\.*|]/\\\\&/']` - LDFLAGS=`echo "${LDFLAGS}" | sed "s| ${pat}||"` - val=`IFS="$PATH_SEPARATOR" - for dir in $OPT_DIR; do - echo x ${LIBPATHFLAG} ${RPATHFLAG} | - sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${dir}/lib${IFS}g;s${IFS}%s${IFS}${dir}/lib${IFS}g" - done | tr '\012' ' ' | sed 's/ *$//'` - AS_IF([test x"$val" != x], [ - test x"${LDFLAGS}" = x || LDFLAGS="$LDFLAGS " - LDFLAGS="$LDFLAGS$val" - test x"${DLDFLAGS}" = x || DLDFLAGS="$DLDFLAGS " - DLDFLAGS="$DLDFLAGS$val" - ]) - LDFLAGS_OPTDIR="$val" -]) - AS_CASE(["$target_os"], [freebsd*], [ AC_CHECK_LIB([procstat], [procstat_open_sysctl]) @@ -3793,6 +3770,10 @@ AS_CASE(["${YJIT_SUPPORT}"], ])) YJIT_LIBS="yjit/target/${rb_rust_target_subdir}/libyjit.a" + AS_CASE(["$target_os"],[openbsd*],[ + # Link libc++abi (which requires libpthread) for _Unwind_* functions needed by yjit + LDFLAGS="$LDFLAGS -lpthread -lc++abi" + ]) YJIT_OBJ='yjit.$(OBJEXT)' AC_DEFINE(USE_YJIT, 1) ], [AC_DEFINE(USE_YJIT, 0)]) @@ -4370,6 +4351,14 @@ AC_SUBST(XCC_WRAPPER) AS_CASE([" $CPP "], [*" $CC "*], [CPP=`echo " $CPP " | sed "s| $CC |"' $(CC) |;s/^ *//;s/ *$//'`]) +AS_IF([test ! -f "$srcdir/revision.h"], [ + AS_IF([test "x$HAVE_BASERUBY" = xyes], [ + ${BASERUBY} -C "$srcdir" tool/file2lastrev.rb -q --revision.h > "$srcdir/revision.h" + ], [ + touch "$srcdir/revision.h" + ]) +]) + AS_IF([test x"$firstmf" != x], [ AC_CONFIG_FILES($firstmf:$firsttmpl, [], [firstmf="$firstmf" firsttmpl="$firsttmpl"]) ]) @@ -4386,17 +4375,24 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [ AS_CASE("$VCS", ['$(GIT)'|git], [VCSUP='$(VCS) pull --rebase $(GITPULLOPTIONS)'], [VCSUP='$(VCS)']) - sed -n \ - -e '[/^@%:@define \(RUBY_RELEASE_[A-Z]*\) \([0-9][0-9]*\)/]{' \ - -e 's//\1 = \2/' \ - -e '[s/ \([0-9]\)$/ 0\1/]' \ - -e p \ - -e '}' "$srcdir/version.h" + for f in "$srcdir/version.h" "$srcdir/revision.h"; do + test -f "$f" || continue + sed -n \ + -e '[/^@%:@define \(RUBY_RELEASE_[A-Z]*\) \([0-9][0-9]*\)/]{' \ + -e 's//\1 = \2/' \ + -e '[s/ \([0-9]\)$/ 0\1/]' \ + -e p \ + -e '}' "$f" + done sed '/^MISSING/s/\$U\././g;/^VCS *=/s#@VCS@#'"$VCS"'#;/^VCSUP *=/s#@VCSUP@#'"$VCSUP"'#' Makefile echo; test x"$EXEEXT" = x || echo 'miniruby: miniruby$(EXEEXT)' AS_IF([test "$gnumake" != yes], [ echo ['$(MKFILES): $(srcdir)/common.mk'] sed ['s/{\$([^(){}]*)[^{}]*}//g'] ${srcdir}/common.mk + AS_IF([test "$YJIT_SUPPORT" = yes], [ + cat ${srcdir}/yjit/not_gmake.mk + echo ['$(MKFILES): ${srcdir}/yjit/not_gmake.mk'] + ]) ], [ echo 'distclean-local::; @$(RM) GNUmakefile uncommon.mk' ]) @@ -4414,7 +4410,7 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [ echo 'ruby: $(PROGRAM);' >> $tmpmk test "$tmpmk" = "$tmpgmk" || rm -f "$tmpgmk" ]) && mv -f $tmpmk Makefile], -[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT']) +[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT' YJIT_SUPPORT='$YJIT_SUPPORT']) AC_ARG_WITH([ruby-pc], AS_HELP_STRING([--with-ruby-pc=FILENAME], [pc file basename]), diff --git a/cont.c b/cont.c index 8a9aded7133318..19e719ccd95e5a 100644 --- a/cont.c +++ b/cont.c @@ -30,6 +30,7 @@ extern int madvise(caddr_t, size_t, int); #include "internal.h" #include "internal/cont.h" #include "internal/proc.h" +#include "internal/sanitizers.h" #include "internal/warnings.h" #include "ruby/fiber/scheduler.h" #include "mjit.h" @@ -1161,6 +1162,7 @@ cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont) } FLUSH_REGISTER_WINDOWS; + asan_unpoison_memory_region(cont->machine.stack_src, size, false); MEMCPY(cont->machine.stack, cont->machine.stack_src, VALUE, size); } diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in index 3b11d3f69c2ca8..43e92a27f06a3f 100644 --- a/cygwin/GNUmakefile.in +++ b/cygwin/GNUmakefile.in @@ -52,7 +52,7 @@ $(RUBY_EXP) $(LIBRUBY_SO): $(DLL_BASE_NAME).res.$(OBJEXT) $(ECHO) compiling $@ $(Q) $(WINDRES) --include-dir . --include-dir $(/dev/null) -REVISION_LATEST := $(shell $(CHDIR) $(srcdir) && $(GIT) log -1 --format=%H 2>/dev/null) -endif -ifneq ($(REVISION_IN_HEADER),$(REVISION_LATEST)) -# GNU make treat the target as unmodified when its dependents get -# updated but it is not updated, while others may not. -$(srcdir)/revision.h: $(REVISION_H) -endif - include $(top_srcdir)/yjit/yjit.mk # Query on the generated rdoc diff --git a/defs/id.def b/defs/id.def index 097e34e405614d..94af02b12fbca4 100644 --- a/defs/id.def +++ b/defs/id.def @@ -194,13 +194,14 @@ predefined.split(/^/).each_with_index do |line, num| end << token predefined_ids[token] = name end +index = 127 token_ops.split(/^/).each do |line| next if /^#/ =~ line line.sub!(/\s+#.*/, '') id, op, token = line.split next unless id and op token ||= (id unless /\A\W\z/ =~ op) - token_op_ids << [id, op, token] + token_op_ids << [id, op, token, (index += 1 if token)] end { "LOCAL" => local_ids, @@ -212,4 +213,5 @@ end :preserved => preserved_ids, :predefined => predefined_ids, :token_op => token_op_ids, + :last_token => index, } diff --git a/defs/keywords b/defs/keywords index fc30ec2d15923f..a1b1f4f60fff88 100644 --- a/defs/keywords +++ b/defs/keywords @@ -2,7 +2,7 @@ struct kwtable {short name, id[2], state;}; const struct kwtable *rb_reserved_word(const char *, unsigned int); #ifndef RIPPER -static const struct kwtable *reserved_word(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/); +static const struct kwtable *reserved_word(register const char *str, register size_t len); #define rb_reserved_word(str, len) reserved_word(str, len) %} diff --git a/defs/lex.c.src b/defs/lex.c.src index fc30ec2d15923f..a1b1f4f60fff88 100644 --- a/defs/lex.c.src +++ b/defs/lex.c.src @@ -2,7 +2,7 @@ struct kwtable {short name, id[2], state;}; const struct kwtable *rb_reserved_word(const char *, unsigned int); #ifndef RIPPER -static const struct kwtable *reserved_word(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/); +static const struct kwtable *reserved_word(register const char *str, register size_t len); #define rb_reserved_word(str, len) reserved_word(str, len) %} diff --git a/doc/NEWS/NEWS-3.0.0.md b/doc/NEWS/NEWS-3.0.0.md index 00c26fe5854874..bdbd47327bde3d 100644 --- a/doc/NEWS/NEWS-3.0.0.md +++ b/doc/NEWS/NEWS-3.0.0.md @@ -512,6 +512,18 @@ Outstanding ones only. * This version is Ractor compatible. +* URI + + * URI.escape and URI.unescape have been removed. + Instead, use the following methods depending on your specific use case. + + * CGI.escape + * URI.encode_www_form + * URI.encode_www_form_component + * CGI.unescape + * URI.decode_www_form + * URI.decode_www_form_component + ## Compatibility issues Excluding feature bug fixes. diff --git a/doc/examples/files.rdoc b/doc/examples/files.rdoc new file mode 100644 index 00000000000000..f7361327709812 --- /dev/null +++ b/doc/examples/files.rdoc @@ -0,0 +1,26 @@ +# English text with newlines. +text = <<~EOT + First line + Second line + + Fourth line + Fifth line +EOT + +# Russian text. +russian = "\u{442 435 441 442}" # => "тест" + +# Binary data. +data = "\u9990\u9991\u9992\u9993\u9994" + +# Text file. +File.write('t.txt', text) + +# File with Russian text. +File.write('t.rus', russian) + +# File with binary data. +f = File.new('t.dat', 'wb:UTF-16') +f.write(data) +f.close + diff --git a/doc/io_streams.rdoc b/doc/io_streams.rdoc new file mode 100644 index 00000000000000..96285e4f360bba --- /dev/null +++ b/doc/io_streams.rdoc @@ -0,0 +1,509 @@ +== \IO Streams + +This page describes: + +- {Stream classes}[rdoc-ref:io_streams.rdoc@Stream+Classes]. +- {Pre-existing streams}[rdoc-ref:io_streams.rdoc@Pre-Existing+Streams]. +- {User-created streams}[rdoc-ref:io_streams.rdoc@User-Created+Streams]. +- {Basic \IO}[rdoc-ref:io_streams.rdoc@Basic+IO], including: + + - {Position}[rdoc-ref:io_streams.rdoc@Position]. + - {Open and closed streams}[rdoc-ref:io_streams.rdoc@Open+and+Closed+Streams]. + - {End-of-stream}[rdoc-ref:io_streams.rdoc@End-of-Stream]. + +- {Line \IO}[rdoc-ref:io_streams.rdoc@Line+IO], including: + + - {Line separator}[rdoc-ref:io_streams.rdoc@Line+Separator]. + - {Line limit}[rdoc-ref:io_streams.rdoc@Line+Limit]. + - {Line number}[rdoc-ref:io_streams.rdoc@Line+Number]. + - {Line options}[rdoc-ref:io_streams.rdoc@Line+Options]. + +- {Character \IO}[rdoc-ref:io_streams.rdoc@Character+IO]. +- {Byte \IO}[rdoc-ref:io_streams.rdoc@Byte+IO]. +- {Codepoint \IO}[rdoc-ref:io_streams.rdoc@Codepoint+IO]. + +=== Stream Classes + +Ruby supports processing data as \IO streams; +that is, as data that may be read, re-read, written, re-written, +and traversed via iteration. + +Core classes with such support include: + +- IO, and its derived class File. +- {StringIO}[rdoc-ref:StringIO]: for processing a string. +- {ARGF}[rdoc-ref:ARGF]: for processing files cited on the command line. + +Except as noted, the instance methods described on this page +are available in classes \ARGF, \File, \IO, and \StringIO. +A few, also noted, are available in class \Kernel. + +=== Pre-Existing Streams + +Pre-existing streams that are referenced by constants include: + +- $stdin: read-only instance of \IO. +- $stdout: write-only instance of \IO. +- $stderr: read-only instance of \IO. +- \ARGF: read-only instance of \ARGF. + +=== User-Created Streams + +You can create streams: + +- \File: + + - File.new: returns a new \File object; + the file should be closed when no longer needed. + - File.open: passes a new \File object to given the block; + the file is automatically closed on block exit. + +- \IO: + + - IO.new: returns a new \IO object for the given integer file descriptor; + the \IO object should be closed when no longer needed. + - IO.open: passes a new \IO object to the given block; + the \IO object is automatically closed on block exit. + - IO.popen: returns a new \IO object that is connected to the $stdin + and $stdout of a newly-launched subprocess. + - Kernel#open: returns a new \IO object connected to a given source: + stream, file, or subprocess; + the \IO object should be closed when no longer needed. + +- \StringIO: + + - StringIO.new: returns a new \StringIO object; + the \StringIO object should be closed when no longer needed. + - StringIO.open: passes a new \StringIO object to the given block; + the \StringIO object is automatically closed on block exit. + +(You cannot create an \ARGF object, but one already exists.) + +=== About the Examples + +Many examples here use these variables: + + :include: doc/examples/files.rdoc + +=== Basic \IO + +You can perform basic stream \IO with these methods: + +- IO#read: Returns all remaining or the next _n_ bytes read from the stream, + for a given _n_: + + f = File.new('t.txt') + f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n" + f.rewind + f.read(30) # => "First line\r\nSecond line\r\n\r\nFou" + f.read(30) # => "rth line\r\nFifth line\r\n" + f.read(30) # => nil + f.close + +- IO#write: Writes one or more given strings to the stream: + + $stdout.write('Hello', ', ', 'World!', "\n") # => 14 + $stdout.write('foo', :bar, 2, "\n") + + Output: + + Hello, World! + foobar2 + +==== Position + +An \IO stream has a nonnegative integer _position_, +which is the byte offset at which the next read or write is to occur; +the relevant methods: + +- IO#tell (aliased as +#pos+): + Returns the current position (in bytes) in the stream: + + f = File.new('t.txt') + f.tell # => 0 + f.gets # => "First line\n" + f.tell # => 12 + f.close + +- IO#pos=: Sets the position of the stream (in bytes): + + f = File.new('t.txt') + f.tell # => 0 + f.pos = 20 # => 20 + f.tell # => 20 + f.close + +- IO#seek: Sets the position of the stream to a given integer +offset+ + (in bytes), with respect to a given constant +whence+, which is one of: + + - +:CUR+ or IO::SEEK_CUR: + Repositions the stream to its current position plus the given +offset+: + + f = File.new('t.txt') + f.tell # => 0 + f.seek(20, :CUR) # => 0 + f.tell # => 20 + f.seek(-10, :CUR) # => 0 + f.tell # => 10 + f.close + + - +:END+ or IO::SEEK_END: + Repositions the stream to its end plus the given +offset+: + + f = File.new('t.txt') + f.tell # => 0 + f.seek(0, :END) # => 0 # Repositions to stream end. + f.tell # => 52 + f.seek(-20, :END) # => 0 + f.tell # => 32 + f.seek(-40, :END) # => 0 + f.tell # => 12 + f.close + + - +:SET+ or IO:SEEK_SET: + Repositions the stream to the given +offset+: + + f = File.new('t.txt') + f.tell # => 0 + f.seek(20, :SET) # => 0 + f.tell # => 20 + f.seek(40, :SET) # => 0 + f.tell # => 40 + f.close + +- IO#rewind: Positions the stream to the beginning: + + f = File.new('t.txt') + f.tell # => 0 + f.gets # => "First line\n" + f.tell # => 12 + f.rewind # => 0 + f.tell # => 0 + f.close + +==== Open and Closed Streams + +A new \IO stream may be open for reading, open for writing, or both. + +You can close a stream using these methods: + +- IO#close: Closes the stream for both reading and writing. +- IO#close_read (not in \ARGF): Closes the stream for reading. +- IO#close_write (not in \ARGF): Closes the stream for writing. + +You can query whether a stream is closed using this method: + +- IO#closed?: Returns whether the stream is closed. + +==== End-of-Stream + +You can query whether a stream is positioned at its end using +method IO#eof? (also aliased as +#eof+). + +You can reposition to end-of-stream by reading all stream content: + + f = File.new('t.txt') + f.eof? # => false + f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n" + f.eof? # => true + +Or by using method IO#seek: + + f = File.new('t.txt') + f.eof? # => false + f.seek(0, :END) + f.eof? # => true + +=== Line \IO + +You can read an \IO stream line-by-line using these methods: + +- IO#each_line: Passes each line to the block: + + f = File.new('t.txt') + f.each_line {|line| p line } + + Output: + + "First line\n" + "Second line\n" + "\n" + "Fourth line\n" + "Fifth line\n" + + The reading may begin mid-line: + + f = File.new('t.txt') + f.pos = 27 + f.each_line {|line| p line } + + Output: + + "rth line\n" + "Fifth line\n" + +- IO#gets (also in Kernel): Returns the next line (which may begin mid-line): + + f = File.new('t.txt') + f.gets # => "First line\n" + f.gets # => "Second line\n" + f.pos = 27 + f.gets # => "rth line\n" + f.readlines # => ["Fifth line\n"] + f.gets # => nil + +- IO#readline (also in Kernel; not in StringIO): + Like #gets, but raises an exception at end-of-stream. + +- IO#readlines (also in Kernel): Returns all remaining lines in an array; + may begin mid-line: + + f = File.new('t.txt') + f.pos = 19 + f.readlines # => ["ine\n", "\n", "Fourth line\n", "Fifth line\n"] + f.readlines # => [] + +Each of these reader methods may be called with: + +- An optional line separator, +sep+. +- An optional line-size limit, +limit+. +- Both +sep+ and +limit+. + +You can write to an \IO stream line-by-line using this method: + +- IO#puts (also in Kernel; not in \StringIO): Writes objects to the stream: + + f = File.new('t.tmp', 'w') + f.puts('foo', :bar, 1, 2.0, Complex(3, 0)) + f.flush + File.read('t.tmp') # => "foo\nbar\n1\n2.0\n3+0i\n" + +==== Line Separator + +The default line separator is the given by the global variable $/, +whose value is by default "\n". +The line to be read next is all data from the current position +to the next line separator: + + f = File.new('t.txt') + f.gets # => "First line\n" + f.gets # => "Second line\n" + f.gets # => "\n" + f.gets # => "Fourth line\n" + f.gets # => "Fifth line\n" + f.close + +You can specify a different line separator: + + f = File.new('t.txt') + f.gets('l') # => "First l" + f.gets('li') # => "ine\nSecond li" + f.gets('lin') # => "ne\n\nFourth lin" + f.gets # => "e\n" + f.close + +There are two special line separators: + +- +nil+: The entire stream is read into a single string: + + f = File.new('t.txt') + f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n" + f.close + +- '' (the empty string): The next "paragraph" is read + (paragraphs being separated by two consecutive line separators): + + f = File.new('t.txt') + f.gets('') # => "First line\nSecond line\n\n" + f.gets('') # => "Fourth line\nFifth line\n" + f.close + +==== Line Limit + +The line to be read may be further defined by an optional integer argument +limit+, +which specifies that the number of bytes returned may not be (much) longer +than the given +limit+; +a multi-byte character will not be split, and so a line may be slightly longer +than the given limit. + +If +limit+ is not given, the line is determined only by +sep+. + + # Text with 1-byte characters. + File.new('t.txt') {|f| f.gets(1) } # => "F" + File.new('t.txt') {|f| f.gets(2) } # => "Fi" + File.new('t.txt') {|f| f.gets(3) } # => "Fir" + File.new('t.txt') {|f| f.gets(4) } # => "Firs" + # No more than one line. + File.new('t.txt') {|f| f.gets(10) } # => "First line" + File.new('t.txt') {|f| f.gets(11) } # => "First line\n" + File.new('t.txt') {|f| f.gets(12) } # => "First line\n" + + # Text with 2-byte characters, which will not be split. + File.new('r.rus') {|f| f.gets(1).size } # => 1 + File.new('r.rus') {|f| f.gets(2).size } # => 1 + File.new('r.rus') {|f| f.gets(3).size } # => 2 + File.new('r.rus') {|f| f.gets(4).size } # => 2 + +==== Line Separator and Line Limit + +With arguments +sep+ and +limit+ given, +combines the two behaviors: + +- Returns the next line as determined by line separator +sep+. +- But returns no more bytes than are allowed by the limit. + +Example: + + File.new('t.txt') {|f| f.gets('li', 20) } # => "First li" + File.new('t.txt') {|f| f.gets('li', 2) } # => "Fi" + +==== Line Number + +A readable \IO stream has a _line_ _number_, +which is the non-negative integer line number +in the stream where the next read will occur. + +A new stream is initially has line number +0+. + +\Method IO#lineno returns the line number. + +Reading lines from a stream usually changes its line number: + + f = File.new('t.txt', 'r') + f.lineno # => 0 + f.readline # => "This is line one.\n" + f.lineno # => 1 + f.readline # => "This is the second line.\n" + f.lineno # => 2 + f.readline # => "Here's the third line.\n" + f.lineno # => 3 + f.eof? # => true + f.close + +Iterating over lines in a stream usually changes its line number: + + File.open('t.txt') do |f| + f.each_line do |line| + p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}" + end + end + +Output: + + "position=11 eof?=false lineno=1" + "position=23 eof?=false lineno=2" + "position=24 eof?=false lineno=3" + "position=36 eof?=false lineno=4" + "position=47 eof?=true lineno=5" + +==== Line Options + +A number of \IO methods accept optional keyword arguments +that determine how lines in a stream are to be treated: + +- +:chomp+: If +true+, line separators are omitted; default is +false+. + +=== Character \IO + +You can process an \IO stream character-by-character using these methods: + +- IO#getc: Reads and returns the next character from the stream: + + f = File.new('t.rus') + f.getc # => "т" + f.getc # => "е" + f.getc # => "с" + f.getc # => "т" + f.getc # => nil + +- IO#readchar (not in \StringIO): + Like #getc, but raises an exception at end-of-stream: + + f.readchar # Raises EOFError. + +- IO#ungetc (not in \ARGF): + Pushes back ("unshifts") a character or integer onto the stream: + + path = 't.tmp' + File.write(path, 'foo') + File.open(path) do |f| + f.ungetc('т') + f.read # => "тfoo" + end + +- IO#putc (also in Kernel): Writes a character to the stream: + + File.open('t.tmp', 'w') do |f| + f.putc('т') + f.putc('е') + f.putc('с') + f.putc('т') + end + File.read('t.tmp') # => "тест" + +- IO#each_char: Reads each remaining character in the stream, + passing the character to the given block: + + File.open('t.rus') do |f| + f.pos = 4 + f.each_char {|c| p c } + end + + Output: + + "с" + "т" + +=== Byte \IO + +You can process an \IO stream byte-by-byte using these methods: + +- IO#getbyte: Returns the next 8-bit byte as an integer in range 0..255: + + File.read('t.dat') + # => "\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94" + File.read('t.dat') + # => "\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94" + f = File.new('t.dat') + f.getbyte # => 254 + f.getbyte # => 255 + f.seek(-2, :END) + f.getbyte # => 153 + f.getbyte # => 148 + f.getbyte # => nil + +- IO#readbyte (not in \StringIO): + Like #getbyte, but raises an exception if at end-of-stream: + + f.readbyte # Raises EOFError. + +- IO#ungetbyte (not in \ARGF): + Pushes back ("unshifts") a byte back onto the stream: + + f.ungetbyte(0) + f.ungetbyte(01) + f.read # => "\u0001\u0000" + +- IO#each_byte: Reads each remaining byte in the stream, + passing the byte to the given block: + + f.seek(-4, :END) + f.each_byte {|b| p b } + + Output: + + 153 + 147 + 153 + 148 + +=== Codepoint \IO + +You can process an \IO stream codepoint-by-codepoint using method ++#each_codepoint+: + + a = [] + File.open('t.rus') do |f| + f.each_codepoint {|c| a << c } + end + a # => [1090, 1077, 1089, 1090] diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md index 1eeb75824a950e..c2521eba426bf9 100644 --- a/doc/yjit/yjit.md +++ b/doc/yjit/yjit.md @@ -79,7 +79,7 @@ The YJIT `ruby` binary can be built with either GCC or Clang. It can be built ei ``` # Configure in release mode for maximum performance, build and install ./autogen.sh -./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc +./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc make -j install ``` @@ -88,7 +88,7 @@ or ``` # Configure in dev (debug) mode for development, build and install ./autogen.sh -./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc +./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc make -j install ``` @@ -100,7 +100,7 @@ brew install openssl readline libyaml # Configure in dev (debug) mode for development, build and install ./autogen.sh -./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --disable--install-rdoc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)" +./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)" make -j install ``` diff --git a/enc/encdb.c b/enc/encdb.c index a1936df804fbbd..8247e9ff6a08ae 100644 --- a/enc/encdb.c +++ b/enc/encdb.c @@ -17,7 +17,7 @@ #define ENC_DEFINE(name) rb_encdb_declare(name) #define ENC_SET_BASE(name, orig) rb_enc_set_base((name), (orig)) #define ENC_SET_DUMMY(name, orig) rb_enc_set_dummy(name) -#define ENC_DUMMY_UNICODE(name) rb_encdb_set_unicode(rb_enc_set_dummy(ENC_REPLICATE((name), name "BE"))) +#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name) void Init_encdb(void) diff --git a/enc/jis/props.h.blt b/enc/jis/props.h.blt index 54aa94f8bcbabe..508a0844498441 100644 --- a/enc/jis/props.h.blt +++ b/enc/jis/props.h.blt @@ -69,7 +69,7 @@ struct enc_property { unsigned char ctype; }; -static const struct enc_property *onig_jis_property(/*const char *str, unsigned int len*/); +static const struct enc_property *onig_jis_property(register const char *str, register size_t len); #line 43 "enc/jis/props.kwd" struct enc_property; @@ -82,7 +82,7 @@ struct enc_property; #ifndef GPERF_DOWNCASE #define GPERF_DOWNCASE 1 -static unsigned char gperf_downcase[256] = +static const unsigned char gperf_downcase[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, diff --git a/enc/jis/props.kwd b/enc/jis/props.kwd index 659cf0aff40a57..9606828459e4aa 100644 --- a/enc/jis/props.kwd +++ b/enc/jis/props.kwd @@ -37,7 +37,7 @@ struct enc_property { unsigned char ctype; }; -static const struct enc_property *onig_jis_property(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/); +static const struct enc_property *onig_jis_property(register const char *str, register size_t len); %} struct enc_property; diff --git a/enc/jis/props.src b/enc/jis/props.src index 659cf0aff40a57..9606828459e4aa 100644 --- a/enc/jis/props.src +++ b/enc/jis/props.src @@ -37,7 +37,7 @@ struct enc_property { unsigned char ctype; }; -static const struct enc_property *onig_jis_property(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/); +static const struct enc_property *onig_jis_property(register const char *str, register size_t len); %} struct enc_property; diff --git a/enc/unicode/14.0.0/name2ctype.h b/enc/unicode/14.0.0/name2ctype.h index 99a3eeca1902d6..61c16bafc283fc 100644 --- a/enc/unicode/14.0.0/name2ctype.h +++ b/enc/unicode/14.0.0/name2ctype.h @@ -40642,11 +40642,7 @@ struct uniname2ctype_struct { }; #define uniname2ctype_offset(str) offsetof(struct uniname2ctype_pool_t, uniname2ctype_pool_##str) -static const struct uniname2ctype_struct *uniname2ctype_p( -#if !(1+0) /* if ANSI, old style not to conflict with generated prototype */ - const char *, unsigned int -#endif -); +static const struct uniname2ctype_struct *uniname2ctype_p(register const char *str, register size_t len); #ifndef USE_UNICODE_PROPERTIES #define TOTAL_KEYWORDS 15 diff --git a/enc/utf_16_32.h b/enc/utf_16_32.h index 9f9216d8ffaaea..4d669019bfb949 100644 --- a/enc/utf_16_32.h +++ b/enc/utf_16_32.h @@ -1,5 +1,5 @@ #include "regenc.h" /* dummy for unsupported, stateful encoding */ -#define ENC_DUMMY_UNICODE(name) ENC_REPLICATE(name, name "BE") +#define ENC_DUMMY_UNICODE(name) ENC_DUMMY(name) ENC_DUMMY_UNICODE("UTF-16"); ENC_DUMMY_UNICODE("UTF-32"); diff --git a/encoding.c b/encoding.c index b8fedfb7976724..b8e7f790b8a187 100644 --- a/encoding.c +++ b/encoding.c @@ -17,6 +17,7 @@ #include "internal.h" #include "internal/enc.h" #include "internal/encoding.h" +#include "internal/error.h" #include "internal/inits.h" #include "internal/load.h" #include "internal/object.h" @@ -49,7 +50,6 @@ void rb_encdb_declare(const char *name); int rb_encdb_replicate(const char *name, const char *orig); int rb_encdb_dummy(const char *name); int rb_encdb_alias(const char *alias, const char *orig); -void rb_encdb_set_unicode(int index); #pragma GCC visibility pop #endif @@ -569,7 +569,10 @@ rb_enc_replicate(const char *name, rb_encoding *encoding) static VALUE enc_replicate_m(VALUE encoding, VALUE name) { - int idx = rb_enc_replicate(name_for_encoding(&name), rb_to_encoding(encoding)); + int idx; + rb_warn_deprecated_to_remove("3.3", "Encoding#replicate", "the original encoding"); + + idx = rb_enc_replicate(name_for_encoding(&name), rb_to_encoding(encoding)); RB_GC_GUARD(name); return rb_enc_from_encoding_index(idx); } @@ -756,14 +759,6 @@ rb_encdb_alias(const char *alias, const char *orig) return r; } -void -rb_encdb_set_unicode(int index) -{ - rb_raw_encoding *enc = (rb_raw_encoding *)rb_enc_from_index(index); - ASSUME(enc); - enc->flags |= ONIGENC_FLAG_UNICODE; -} - static void rb_enc_init(struct enc_table *enc_table) { diff --git a/error.c b/error.c index 07fa04627e8d5d..cfa086a1ebad26 100644 --- a/error.c +++ b/error.c @@ -34,6 +34,7 @@ #include "internal/io.h" #include "internal/load.h" #include "internal/object.h" +#include "internal/string.h" #include "internal/symbol.h" #include "internal/thread.h" #include "internal/variable.h" @@ -1439,8 +1440,15 @@ exc_inspect(VALUE exc) str = rb_str_buf_new2("#<"); klass = rb_class_name(klass); rb_str_buf_append(str, klass); - rb_str_buf_cat(str, ": ", 2); - rb_str_buf_append(str, exc); + + if (RTEST(rb_str_include(exc, rb_str_new2("\n")))) { + rb_str_catf(str, ":%+"PRIsVALUE, exc); + } + else { + rb_str_buf_cat(str, ": ", 2); + rb_str_buf_append(str, exc); + } + rb_str_buf_cat(str, ">", 1); return str; @@ -2437,9 +2445,6 @@ get_syserr(int n) static VALUE syserr_initialize(int argc, VALUE *argv, VALUE self) { -#if !defined(_WIN32) - char *strerror(); -#endif const char *err; VALUE mesg, error, func, errmsg; VALUE klass = rb_obj_class(self); diff --git a/eval.c b/eval.c index cf32a82214557e..9567f801501d86 100644 --- a/eval.c +++ b/eval.c @@ -1792,10 +1792,12 @@ top_include(int argc, VALUE *argv, VALUE self) static VALUE top_using(VALUE self, VALUE module) { - const rb_cref_t *cref = rb_vm_cref(); + const rb_cref_t *cref = CREF_NEXT(rb_vm_cref());; rb_control_frame_t *prev_cfp = previous_frame(GET_EC()); + rb_thread_t *th = GET_THREAD(); - if (CREF_NEXT(cref) || (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) { + if ((th->top_wrapper ? CREF_NEXT(cref) : cref) || + (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) { rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel"); } if (rb_block_given_p()) { diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index b3ef70a2c96ec0..1483f327a6de8c 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3500,6 +3500,9 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) * in the value, the result is rounded to that number of digits, * according to the current rounding mode; see BigDecimal.mode. * + * When +ndigits+ is 0, the number of digits to correctly represent a float number + * is determined automatically. + * * Returns +value+ converted to a \BigDecimal, depending on the type of +value+: * * - Integer, Float, Rational, Complex, or BigDecimal: converted directly: @@ -3606,8 +3609,10 @@ BigDecimal_limit(int argc, VALUE *argv, VALUE self) /* Returns the sign of the value. * - * Returns a positive value if > 0, a negative value if < 0, and a - * zero if == 0. + * Returns a positive value if > 0, a negative value if < 0. + * It behaves the same with zeros - + * it returns a positive value for a positive zero (BigDecimal('0')) and + * a negative value for a negative zero (BigDecimal('-0')). * * The specific value returned indicates the type and sign of the BigDecimal, * as follows: diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb index 4920374b1a9bc8..17e7905dd67e3b 100644 --- a/ext/bigdecimal/extconf.rb +++ b/ext/bigdecimal/extconf.rb @@ -71,8 +71,6 @@ def have_builtin_func(name, check_expr, opt = "", &b) have_func("rb_rational_den", "ruby.h") have_func("rb_complex_real", "ruby.h") have_func("rb_complex_imag", "ruby.h") -have_func("rb_array_const_ptr", "ruby.h") -have_func("rb_sym2str", "ruby.h") have_func("rb_opts_exception_p", "ruby.h") have_func("rb_category_warn", "ruby.h") have_const("RB_WARN_CATEGORY_DEPRECATED", "ruby.h") diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb index cb645d2a7128e2..ad92f7cfe64eef 100644 --- a/ext/bigdecimal/lib/bigdecimal/util.rb +++ b/ext/bigdecimal/lib/bigdecimal/util.rb @@ -33,12 +33,16 @@ class Float < Numeric # # Returns the value of +float+ as a BigDecimal. # The +precision+ parameter is used to determine the number of - # significant digits for the result (the default is Float::DIG). + # significant digits for the result. When +precision+ is set to +0+, + # the number of digits to represent the float being converted is determined + # automatically. + # The default +precision+ is +0+. # # require 'bigdecimal' # require 'bigdecimal/util' # # 0.5.to_d # => 0.5e0 + # 1.234.to_d # => 0.1234e1 # 1.234.to_d(2) # => 0.12e1 # # See also BigDecimal::new. diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h index 49b7c7667f9f98..307147c0fdc6bf 100644 --- a/ext/bigdecimal/missing.h +++ b/ext/bigdecimal/missing.h @@ -172,45 +172,6 @@ rb_complex_imag(VALUE cmp) } #endif -/* array */ - -#ifndef FIX_CONST_VALUE_PTR -# if defined(__fcc__) || defined(__fcc_version) || \ - defined(__FCC__) || defined(__FCC_VERSION) -/* workaround for old version of Fujitsu C Compiler (fcc) */ -# define FIX_CONST_VALUE_PTR(x) ((const VALUE *)(x)) -# else -# define FIX_CONST_VALUE_PTR(x) (x) -# endif -#endif - -#ifndef HAVE_RB_ARRAY_CONST_PTR -static inline const VALUE * -rb_array_const_ptr(VALUE a) -{ - return FIX_CONST_VALUE_PTR((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ? - RARRAY(a)->as.ary : RARRAY(a)->as.heap.ptr); -} -#endif - -#ifndef RARRAY_CONST_PTR -# define RARRAY_CONST_PTR(a) rb_array_const_ptr(a) -#endif - -#ifndef RARRAY_AREF -# define RARRAY_AREF(a, i) (RARRAY_CONST_PTR(a)[i]) -#endif - -/* symbol */ - -#ifndef HAVE_RB_SYM2STR -static inline VALUE -rb_sym2str(VALUE sym) -{ - return rb_id2str(SYM2ID(sym)); -} -#endif - /* st */ #ifndef ST2FIX diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c index 95274d5baa8e0c..4890401894e12b 100644 --- a/ext/date/date_parse.c +++ b/ext/date/date_parse.c @@ -473,27 +473,53 @@ date_zone_to_diff(VALUE str) s++; l--; +#define out_of_range(v, min, max) ((v) < (min) || (max) < (v)) hour = STRTOUL(s, &p, 10); if (*p == ':') { + if (out_of_range(sec, 0, 59)) return Qnil; s = ++p; min = STRTOUL(s, &p, 10); + if (out_of_range(min, 0, 59)) return Qnil; if (*p == ':') { s = ++p; sec = STRTOUL(s, &p, 10); + if (out_of_range(hour, 0, 23)) return Qnil; } - goto num; } - if (*p == ',' || *p == '.') { - char *e = 0; - p++; - min = STRTOUL(p, &e, 10) * 3600; + else if (*p == ',' || *p == '.') { + /* fractional hour */ + size_t n; + int ov; + /* no over precision for offset; 10**-7 hour = 0.36 + * milliseconds should be enough. */ + const size_t max_digits = 7; /* 36 * 10**7 < 32-bit FIXNUM_MAX */ + + if (out_of_range(hour, 0, 23)) return Qnil; + + n = (s + l) - ++p; + if (n > max_digits) n = max_digits; + sec = ruby_scan_digits(p, n, 10, &n, &ov); + if ((p += n) < s + l && *p >= ('5' + !(sec & 1)) && *p <= '9') { + /* round half to even */ + sec++; + } + sec *= 36; if (sign) { hour = -hour; - min = -min; + sec = -sec; + } + if (n <= 2) { + /* HH.nn or HH.n */ + if (n == 1) sec *= 10; + offset = INT2FIX(sec + hour * 3600); + } + else { + VALUE denom = rb_int_positive_pow(10, (int)(n - 2)); + offset = f_add(rb_rational_new(INT2FIX(sec), denom), INT2FIX(hour * 3600)); + if (rb_rational_den(offset) == INT2FIX(1)) { + offset = rb_rational_num(offset); + } } - offset = rb_rational_new(INT2FIX(min), - rb_int_positive_pow(10, (int)(e - p))); - offset = f_add(INT2FIX(hour * 3600), offset); goto ok; } else if (l > 2) { @@ -506,12 +532,11 @@ date_zone_to_diff(VALUE str) min = ruby_scan_digits(&s[2 - l % 2], 2, 10, &n, &ov); if (l >= 5) sec = ruby_scan_digits(&s[4 - l % 2], 2, 10, &n, &ov); - goto num; } - num: sec += min * 60 + hour * 3600; if (sign) sec = -sec; offset = INT2FIX(sec); +#undef out_of_range } } } diff --git a/ext/date/zonetab.h b/ext/date/zonetab.h index 39a435db16536b..7ced9e03087408 100644 --- a/ext/date/zonetab.h +++ b/ext/date/zonetab.h @@ -36,7 +36,7 @@ struct zone { int name; int offset; }; -static const struct zone *zonetab(); +static const struct zone *zonetab(register const char *str, register size_t len); #line 9 "zonetab.list" struct zone; diff --git a/ext/date/zonetab.list b/ext/date/zonetab.list index d2f902d2d5c53b..748aec1d8a93f0 100644 --- a/ext/date/zonetab.list +++ b/ext/date/zonetab.list @@ -3,7 +3,7 @@ struct zone { int name; int offset; }; -static const struct zone *zonetab(); +static const struct zone *zonetab(register const char *str, register size_t len); %} struct zone; diff --git a/ext/etc/etc.c b/ext/etc/etc.c index 4cd941f5862a5b..c355fe117a2d65 100644 --- a/ext/etc/etc.c +++ b/ext/etc/etc.c @@ -47,8 +47,12 @@ static VALUE sGroup; #define HAVE_UNAME 1 #endif -#ifndef _WIN32 -char *getenv(); +#ifdef STDC_HEADERS +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif #endif char *getlogin(); diff --git a/ext/extmk.rb b/ext/extmk.rb index 40fc10ea1c4258..939eb735659bee 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -607,7 +607,7 @@ def create_makefile(*args, &block) gemlib = $(TARGET_TOPDIR)/gems/$(gem)/lib gemlib:#{%{ $(gemlib)\n$(gemlib): $(gem_srcdir)/lib} if $nmake} - $(Q) #{@inplace ? '$(NULLCMD) ' : ''}$(RUBY) $(top_srcdir)/tool/ln_sr.rb -f -T $(gem_srcdir)/lib $(gemlib) + $(Q) #{@inplace ? '$(NULLCMD) ' : ''}$(RUBY) $(top_srcdir)/tool/ln_sr.rb -q -f -T $(gem_srcdir)/lib $(gemlib) clean-gemlib: $(Q) $(#{@inplace ? 'NULLCMD' : 'RM_RF'}) $(gemlib) diff --git a/ext/fiddle/extlibs b/ext/fiddle/extlibs deleted file mode 100644 index 68dac46a9504b4..00000000000000 --- a/ext/fiddle/extlibs +++ /dev/null @@ -1,13 +0,0 @@ -ver = 3.2.1 -pkg = libffi-$(ver) - -https://ftp.osuosl.org/pub/blfs/conglomeration/libffi/$(pkg).tar.gz \ - md5:83b89587607e3eb65c70d361f13bab43 \ - sha512:980ca30a8d76f963fca722432b1fe5af77d7a4e4d2eac5144fbc5374d4c596609a293440573f4294207e1bdd9fda80ad1e1cafb2ffb543df5a275bc3bd546483 \ - # - win32/$(pkg)-mswin.patch -p0 - -$(pkg)/config.guess -> /tool/config.guess -$(pkg)/config.sub -> /tool/config.sub - -! chdir: $(pkg)| autoconf || exit 0 diff --git a/ext/fiddle/lib/fiddle/pack.rb b/ext/fiddle/lib/fiddle/pack.rb index 22eccedb76920a..eb99fe090de5b5 100644 --- a/ext/fiddle/lib/fiddle/pack.rb +++ b/ext/fiddle/lib/fiddle/pack.rb @@ -18,17 +18,17 @@ module PackInfo # :nodoc: all } PACK_MAP = { - TYPE_VOIDP => "l!", + TYPE_VOIDP => "L!", TYPE_CHAR => "c", TYPE_SHORT => "s!", TYPE_INT => "i!", TYPE_LONG => "l!", TYPE_FLOAT => "f", TYPE_DOUBLE => "d", - -TYPE_CHAR => "c", - -TYPE_SHORT => "s!", - -TYPE_INT => "i!", - -TYPE_LONG => "l!", + -TYPE_CHAR => "C", + -TYPE_SHORT => "S!", + -TYPE_INT => "I!", + -TYPE_LONG => "L!", } SIZE_MAP = { @@ -46,9 +46,10 @@ module PackInfo # :nodoc: all } if defined?(TYPE_LONG_LONG) ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG - PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q" + PACK_MAP[TYPE_LONG_LONG] = "q" + PACK_MAP[-TYPE_LONG_LONG] = "Q" SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG - PACK_MAP[TYPE_VOIDP] = "q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP + PACK_MAP[TYPE_VOIDP] = "Q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP end def align(addr, align) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 0d3fa9ad15f11c..71c452c88af26f 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -509,7 +509,8 @@ ossl_asn1_get_asn1type(VALUE obj) ASN1_TYPE *ret; VALUE value, rflag; void *ptr; - void (*free_func)(); + typedef void free_func_type(void *); + free_func_type *free_func; int tag; tag = ossl_asn1_default_tag(obj); @@ -522,16 +523,16 @@ ossl_asn1_get_asn1type(VALUE obj) case V_ASN1_INTEGER: /* FALLTHROUGH */ case V_ASN1_ENUMERATED: ptr = obj_to_asn1int(value); - free_func = ASN1_INTEGER_free; + free_func = (free_func_type *)ASN1_INTEGER_free; break; case V_ASN1_BIT_STRING: rflag = rb_attr_get(obj, sivUNUSED_BITS); ptr = obj_to_asn1bstr(value, NUM2INT(rflag)); - free_func = ASN1_BIT_STRING_free; + free_func = (free_func_type *)ASN1_BIT_STRING_free; break; case V_ASN1_NULL: ptr = obj_to_asn1null(value); - free_func = ASN1_NULL_free; + free_func = (free_func_type *)ASN1_NULL_free; break; case V_ASN1_OCTET_STRING: /* FALLTHROUGH */ case V_ASN1_UTF8STRING: /* FALLTHROUGH */ @@ -546,24 +547,24 @@ ossl_asn1_get_asn1type(VALUE obj) case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */ case V_ASN1_BMPSTRING: ptr = obj_to_asn1str(value); - free_func = ASN1_STRING_free; + free_func = (free_func_type *)ASN1_STRING_free; break; case V_ASN1_OBJECT: ptr = obj_to_asn1obj(value); - free_func = ASN1_OBJECT_free; + free_func = (free_func_type *)ASN1_OBJECT_free; break; case V_ASN1_UTCTIME: ptr = obj_to_asn1utime(value); - free_func = ASN1_TIME_free; + free_func = (free_func_type *)ASN1_TIME_free; break; case V_ASN1_GENERALIZEDTIME: ptr = obj_to_asn1gtime(value); - free_func = ASN1_TIME_free; + free_func = (free_func_type *)ASN1_TIME_free; break; case V_ASN1_SET: /* FALLTHROUGH */ case V_ASN1_SEQUENCE: ptr = obj_to_asn1derstr(obj); - free_func = ASN1_STRING_free; + free_func = (free_func_type *)ASN1_STRING_free; break; default: ossl_raise(eASN1Error, "unsupported ASN.1 type"); diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index af262d9f561552..6e1a50fd6da809 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1662,7 +1662,7 @@ io_wait_readable(rb_io_t *fptr) } static VALUE -ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) +ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) { SSL *ssl; rb_io_t *fptr; diff --git a/ext/psych/extlibs b/ext/psych/extlibs deleted file mode 100644 index 108aad42af9ae8..00000000000000 --- a/ext/psych/extlibs +++ /dev/null @@ -1,11 +0,0 @@ -ver = 0.2.5 -pkg = yaml-$(ver) - -https://github.com/yaml/libyaml/releases/download/$(ver)/$(pkg).tar.gz \ - rmd160:cc175ed640046722fb7790de828002633407b6b9 \ - sha256:c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4 \ - sha512:dadd7d8e0d88b5ebab005e5d521d56d541580198aa497370966b98c904586e642a1cd4f3881094eb57624f218d50db77417bbfd0ffdce50340f011e35e8c4c02 \ - # - -$(pkg)/config/config.guess -> /tool/config.guess -$(pkg)/config/config.sub -> /tool/config.sub diff --git a/ext/psych/lib/psych/parser.rb b/ext/psych/lib/psych/parser.rb index 39bc8289be7fb1..2181c730e59ce9 100644 --- a/ext/psych/lib/psych/parser.rb +++ b/ext/psych/lib/psych/parser.rb @@ -48,5 +48,18 @@ def initialize handler = Handler.new @handler = handler @external_encoding = ANY end + + ### + # call-seq: + # parser.parse(yaml) + # + # Parse the YAML document contained in +yaml+. Events will be called on + # the handler set on the parser instance. + # + # See Psych::Parser and Psych::Parser#handler + + def parse yaml, path = yaml.respond_to?(:path) ? yaml.path : "" + _native_parse @handler, yaml, path + end end end diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb index f39d30ce5a1daa..a9585c887f65a6 100644 --- a/ext/psych/lib/psych/versions.rb +++ b/ext/psych/lib/psych/versions.rb @@ -5,6 +5,6 @@ module Psych VERSION = '5.0.0.dev' if RUBY_ENGINE == 'jruby' - DEFAULT_SNAKEYAML_VERSION = '1.31'.freeze + DEFAULT_SNAKEYAML_VERSION = '1.33'.freeze end end diff --git a/ext/psych/psych_parser.c b/ext/psych/psych_parser.c index f91475b835debf..9c5179cc44398d 100644 --- a/ext/psych/psych_parser.c +++ b/ext/psych/psych_parser.c @@ -245,18 +245,8 @@ static VALUE protected_event_location(VALUE pointer) return rb_funcall3(args[0], id_event_location, 4, args + 1); } -/* - * call-seq: - * parser.parse(yaml) - * - * Parse the YAML document contained in +yaml+. Events will be called on - * the handler set on the parser instance. - * - * See Psych::Parser and Psych::Parser#handler - */ -static VALUE parse(int argc, VALUE *argv, VALUE self) +static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) { - VALUE yaml, path; yaml_parser_t * parser; yaml_event_t event; int done = 0; @@ -264,14 +254,6 @@ static VALUE parse(int argc, VALUE *argv, VALUE self) int parser_encoding = YAML_ANY_ENCODING; int encoding = rb_utf8_encindex(); rb_encoding * internal_enc = rb_default_internal_encoding(); - VALUE handler = rb_iv_get(self, "@handler"); - - if (rb_scan_args(argc, argv, "11", &yaml, &path) == 1) { - if(rb_respond_to(yaml, id_path)) - path = rb_funcall(yaml, id_path, 0); - else - path = rb_str_new2(""); - } TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser); @@ -562,7 +544,7 @@ void Init_psych_parser(void) rb_require("psych/syntax_error"); - rb_define_method(cPsychParser, "parse", parse, -1); + rb_define_private_method(cPsychParser, "_native_parse", parse, 3); rb_define_method(cPsychParser, "mark", mark, 0); id_read = rb_intern("read"); diff --git a/ext/pty/pty.c b/ext/pty/pty.c index 155d2159421bc2..acec33f9bf5342 100644 --- a/ext/pty/pty.c +++ b/ext/pty/pty.c @@ -170,7 +170,7 @@ establishShell(int argc, VALUE *argv, struct pty_info *info, { int master, slave, status = 0; rb_pid_t pid; - char *p, *getenv(); + char *p; VALUE v; struct child_info carg; char errbuf[32]; diff --git a/ext/ripper/depend b/ext/ripper/depend index 80a6d62346c7d7..15c557a8efa052 100644 --- a/ext/ripper/depend +++ b/ext/ripper/depend @@ -17,10 +17,9 @@ ripper.o: ripper.c all: check static: check -ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y {$(VPATH)}id.h +ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y $(top_srcdir)/defs/id.def $(ECHO) extracting $@ from $(top_srcdir)/parse.y - $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ \ - --vpath=$(VPATH)$(PATH_SEPARATOR)$(top_srcdir) id.h $(top_srcdir)/parse.y > ripper.tmp.y + $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb $(top_srcdir)/parse.y > ripper.tmp.y $(Q) $(RUBY) $(top_srcdir)/tool/pure_parser.rb ripper.tmp.y $(BISON) $(Q) $(RM) ripper.tmp.y.bak $(Q) $(RUBY) $(srcdir)/tools/preproc.rb ripper.tmp.y --output=$@ diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index 577958a358d5c2..5e6c0668f63da9 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -626,6 +626,7 @@ def def_intern(func_name, pat, prefix_optional=nil) SO_ACCEPTCONN nil Socket has had listen() called on it SO_USELOOPBACK nil Bypass hardware when possible SO_ACCEPTFILTER nil There is an accept filter +SO_USER_COOKIE nil Setting an identifier for ipfw purpose mainly SO_DONTTRUNC nil Retain unread data SO_WANTMORE nil Give a hint when more data is ready SO_WANTOOBFLAG nil OOB data is wanted in MSG_FLAG on receive @@ -661,6 +662,10 @@ def def_intern(func_name, pat, prefix_optional=nil) SO_BUSY_POLL nil Set the threshold in microseconds for low latency polling (Linux 3.11) SO_MAX_PACING_RATE nil Cap the rate computed by transport layer. [bytes per second] (Linux 3.13) SO_BPF_EXTENSIONS nil Query supported BPF extensions (Linux 3.14) +SO_SETFIB nil Set the associated routing table for the socket (FreeBSD) +SO_RTABLE nil Set the routing table for this socket (OpenBSD) +SO_INCOMING_CPU nil Receive the cpu attached to the socket (Linux 3.19) +SO_INCOMING_NAPI_ID nil Receive the napi ID attached to a RX queue (Linux 4.12) SOPRI_INTERACTIVE nil Interactive socket priority SOPRI_NORMAL nil Normal socket priority @@ -670,9 +675,11 @@ def def_intern(func_name, pat, prefix_optional=nil) TCP_NODELAY nil Don't delay sending to coalesce packets TCP_MAXSEG nil Set maximum segment size +TCP_CONNECTION_INFO nil Retrieve information about this socket (macOS) TCP_CORK nil Don't send partial frames (Linux 2.2, glibc 2.2) TCP_DEFER_ACCEPT nil Don't notify a listening socket until data is ready (Linux 2.4, glibc 2.2) TCP_INFO nil Retrieve information about this socket (Linux 2.4, glibc 2.2) +TCP_KEEPALIVE nil Idle time before keepalive probes are sent (macOS) TCP_KEEPCNT nil Maximum number of keepalive probes allowed before dropping a connection (Linux 2.4, glibc 2.2) TCP_KEEPIDLE nil Idle time before keepalive probes are sent (Linux 2.4, glibc 2.2) TCP_KEEPINTVL nil Time between keepalive probes (Linux 2.4, glibc 2.2) diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index b914aa6520141c..a4e1ed37a352d6 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -2192,7 +2192,7 @@ addrinfo_ipv6_multicast_p(VALUE self) } /* - * Returns true for IPv6 link local address (ff80::/10). + * Returns true for IPv6 link local address (fe80::/10). * It returns false otherwise. */ static VALUE @@ -2204,7 +2204,7 @@ addrinfo_ipv6_linklocal_p(VALUE self) } /* - * Returns true for IPv6 site local address (ffc0::/10). + * Returns true for IPv6 site local address (fec0::/10). * It returns false otherwise. */ static VALUE diff --git a/file.c b/file.c index ced2c1e4a20a05..709c33cca7c863 100644 --- a/file.c +++ b/file.c @@ -268,6 +268,46 @@ rb_str_encode_ospath(VALUE path) #ifdef __APPLE__ # define NORMALIZE_UTF8PATH 1 + +# ifdef HAVE_WORKING_FORK +static void +rb_CFString_class_initialize_before_fork(void) +{ + /* + * Since macOS 13, CFString family API used in + * rb_str_append_normalized_ospath may internally use Objective-C classes + * (NSTaggedPointerString and NSPlaceholderMutableString) for small strings. + * + * On the other hand, Objective-C classes should not be used for the first + * time in a fork()'ed but not exec()'ed process. Violations for this rule + * can result deadlock during class initialization, so Objective-C runtime + * conservatively crashes on such cases by default. + * + * Therefore, we need to use CFString API to initialize Objective-C classes + * used internally *before* fork(). + * + * For future changes, please note that this initialization process cannot + * be done in ctor because NSTaggedPointerString in CoreFoundation is enabled + * after CFStringInitializeTaggedStrings(), which is called during loading + * Objective-C runtime after ctor. + * For more details, see https://bugs.ruby-lang.org/issues/18912 + */ + + /* Enough small but non-empty ASCII string to fit in NSTaggedPointerString. */ + const char small_str[] = "/"; + long len = sizeof(small_str) - 1; + + const CFAllocatorRef alloc = kCFAllocatorDefault; + CFStringRef s = CFStringCreateWithBytesNoCopy(alloc, + (const UInt8 *)small_str, + len, kCFStringEncodingUTF8, + FALSE, kCFAllocatorNull); + CFMutableStringRef m = CFStringCreateMutableCopy(alloc, len, s); + CFRelease(m); + CFRelease(s); +} +# endif + static VALUE rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len) { @@ -2517,7 +2557,7 @@ rb_file_birthtime(VALUE obj) * */ -off_t +rb_off_t rb_file_size(VALUE file) { if (RB_TYPE_P(file, T_FILE)) { @@ -4500,9 +4540,9 @@ rb_check_realpath_internal(VALUE basedir, VALUE path, rb_encoding *origenc, enum rb_enc_associate(resolved, origenc); } - if (rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) { + if (is_broken_string(resolved)) { rb_enc_associate(resolved, rb_filesystem_encoding()); - if (rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) { + if (is_broken_string(resolved)) { rb_enc_associate(resolved, rb_ascii8bit_encoding()); } } @@ -5086,41 +5126,17 @@ rb_file_s_join(VALUE klass, VALUE args) return rb_file_join(args); } -#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE) +#if defined(HAVE_TRUNCATE) struct truncate_arg { const char *path; -#if defined(HAVE_TRUNCATE) -#define NUM2POS(n) NUM2OFFT(n) - off_t pos; -#else -#define NUM2POS(n) NUM2LONG(n) - long pos; -#endif + rb_off_t pos; }; static void * nogvl_truncate(void *ptr) { struct truncate_arg *ta = ptr; -#ifdef HAVE_TRUNCATE return (void *)(VALUE)truncate(ta->path, ta->pos); -#else /* defined(HAVE_CHSIZE) */ - { - int tmpfd = rb_cloexec_open(ta->path, 0, 0); - - if (tmpfd < 0) - return (void *)-1; - rb_update_max_fd(tmpfd); - if (chsize(tmpfd, ta->pos) < 0) { - int e = errno; - close(tmpfd); - errno = e; - return (void *)-1; - } - close(tmpfd); - return 0; - } -#endif } /* @@ -5144,7 +5160,7 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len) struct truncate_arg ta; int r; - ta.pos = NUM2POS(len); + ta.pos = NUM2OFFT(len); FilePathValue(path); path = rb_str_encode_ospath(path); ta.path = StringValueCStr(path); @@ -5154,22 +5170,15 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len) if (r < 0) rb_sys_fail_path(path); return INT2FIX(0); -#undef NUM2POS } #else #define rb_file_s_truncate rb_f_notimplement #endif -#if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE) +#if defined(HAVE_FTRUNCATE) struct ftruncate_arg { int fd; -#if defined(HAVE_FTRUNCATE) -#define NUM2POS(n) NUM2OFFT(n) - off_t pos; -#else -#define NUM2POS(n) NUM2LONG(n) - long pos; -#endif + rb_off_t pos; }; static VALUE @@ -5177,11 +5186,7 @@ nogvl_ftruncate(void *ptr) { struct ftruncate_arg *fa = ptr; -#ifdef HAVE_FTRUNCATE return (VALUE)ftruncate(fa->fd, fa->pos); -#else /* defined(HAVE_CHSIZE) */ - return (VALUE)chsize(fa->fd, fa->pos); -#endif } /* @@ -5204,7 +5209,7 @@ rb_file_truncate(VALUE obj, VALUE len) rb_io_t *fptr; struct ftruncate_arg fa; - fa.pos = NUM2POS(len); + fa.pos = NUM2OFFT(len); GetOpenFile(obj, fptr); if (!(fptr->mode & FMODE_WRITABLE)) { rb_raise(rb_eIOError, "not opened for writing"); @@ -5215,7 +5220,6 @@ rb_file_truncate(VALUE obj, VALUE len) rb_sys_fail_path(fptr->pathv); } return INT2FIX(0); -#undef NUM2POS } #else #define rb_file_truncate rb_f_notimplement @@ -6129,7 +6133,7 @@ rb_stat_z(VALUE obj) static VALUE rb_stat_s(VALUE obj) { - off_t size = get_stat(obj)->st_size; + rb_off_t size = get_stat(obj)->st_size; if (size == 0) return Qnil; return OFFT2NUM(size); @@ -6540,12 +6544,10 @@ rb_find_file(VALUE path) return copy_path_class(tmp, path); } -static void -define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc) -{ - rb_define_module_function(rb_mFileTest, name, func, argc); - rb_define_singleton_method(rb_cFile, name, func, argc); -} +#define define_filetest_function(name, func, argc) do { \ + rb_define_module_function(rb_mFileTest, name, func, argc); \ + rb_define_singleton_method(rb_cFile, name, func, argc); \ +} while(false) const char ruby_null_device[] = #if defined DOSISH @@ -6565,6 +6567,602 @@ const char ruby_null_device[] = * \Class \File extends module FileTest, supporting such singleton methods * as File.exist?. * + * === About the Examples + * + * Many examples here use these variables: + * + * :include: doc/examples/files.rdoc + * + * == \File Access Modes + * + * \Methods File.new and File.open each create a \File object for a given file path. + * + * === \String Access Modes + * + * \Methods File.new and File.open each may take string argument +mode+, which: + * + * - Begins with a 1- or 2-character + * {read/write mode}[rdoc-ref:File@Read-2FWrite+Mode]. + * - May also contain a 1-character {data mode}[rdoc-ref:File@Data+Mode]. + * - May also contain a 1-character + * {file-create mode}[rdoc-ref:File@File-Create+Mode]. + * + * ==== Read/Write Mode + * + * The read/write +mode+ determines: + * + * - Whether the file is to be initially truncated. + * + * - Whether reading is allowed, and if so: + * + * - The initial read position in the file. + * - Where in the file reading can occur. + * + * - Whether writing is allowed, and if so: + * + * - The initial write position in the file. + * - Where in the file writing can occur. + * + * These tables summarize: + * + * Read/Write Modes for Existing File + * + * |------|-----------|----------|----------|----------|-----------| + * | R/W | Initial | | Initial | | Initial | + * | Mode | Truncate? | Read | Read Pos | Write | Write Pos | + * |------|-----------|----------|----------|----------|-----------| + * | 'r' | No | Anywhere | 0 | Error | - | + * | 'w' | Yes | Error | - | Anywhere | 0 | + * | 'a' | No | Error | - | End only | End | + * | 'r+' | No | Anywhere | 0 | Anywhere | 0 | + * | 'w+' | Yes | Anywhere | 0 | Anywhere | 0 | + * | 'a+' | No | Anywhere | End | End only | End | + * |------|-----------|----------|----------|----------|-----------| + * + * Read/Write Modes for \File To Be Created + * + * |------|----------|----------|----------|-----------| + * | R/W | | Initial | | Initial | + * | Mode | Read | Read Pos | Write | Write Pos | + * |------|----------|----------|----------|-----------| + * | 'w' | Error | - | Anywhere | 0 | + * | 'a' | Error | - | End only | 0 | + * | 'w+' | Anywhere | 0 | Anywhere | 0 | + * | 'a+' | Anywhere | 0 | End only | End | + * |------|----------|----------|----------|-----------| + * + * Note that modes 'r' and 'r+' are not allowed + * for a non-existent file (exception raised). + * + * In the tables: + * + * - +Anywhere+ means that methods IO#rewind, IO#pos=, and IO#seek + * may be used to change the file's position, + * so that allowed reading or writing may occur anywhere in the file. + * - End only means that writing can occur only at end-of-file, + * and that methods IO#rewind, IO#pos=, and IO#seek do not affect writing. + * - +Error+ means that an exception is raised if disallowed reading or writing + * is attempted. + * + * ===== Read/Write Modes for Existing \File + * + * - 'r': + * + * - File is not initially truncated: + * + * f = File.new('t.txt') # => # + * f.size == 0 # => false + * + * - File's initial read position is 0: + * + * f.pos # => 0 + * + * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek: + * + * f.readline # => "First line\n" + * f.readline # => "Second line\n" + * + * f.rewind + * f.readline # => "First line\n" + * + * f.pos = 1 + * f.readline # => "irst line\n" + * + * f.seek(1, :CUR) + * f.readline # => "econd line\n" + * + * - Writing is not allowed: + * + * f.write('foo') # Raises IOError. + * + * - 'w': + * + * - File is initially truncated: + * + * path = 't.tmp' + * File.write(path, text) + * f = File.new(path, 'w') + * f.size == 0 # => true + * + * - File's initial write position is 0: + * + * f.pos # => 0 + * + * - File may be written anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.write('foo') + * f.flush + * File.read(path) # => "foo" + * f.pos # => 3 + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.pos # => 6 + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "bazbar" + * f.pos # => 3 + * + * f.pos = 3 + * f.write('foo') + * f.flush + * File.read(path) # => "bazfoo" + * f.pos # => 6 + * + * f.seek(-3, :END) + * f.write('bam') + * f.flush + * File.read(path) # => "bazbam" + * f.pos # => 6 + * + * f.pos = 8 + * f.write('bah') # Zero padding as needed. + * f.flush + * File.read(path) # => "bazbam\u0000\u0000bah" + * f.pos # => 11 + * + * - Reading is not allowed: + * + * f.read # Raises IOError. + * + * - 'a': + * + * - File is not initially truncated: + * + * path = 't.tmp' + * File.write(path, 'foo') + * f = File.new(path, 'a') + * f.size == 0 # => false + * + * - File's initial position is 0 (but is ignored): + * + * f.pos # => 0 + * + * - File may be written only at end-of-file; + * IO#rewind, IO#pos=, IO#seek do not affect writing: + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.write('baz') + * f.flush + * File.read(path) # => "foobarbaz" + * + * f.rewind + * f.write('bat') + * f.flush + * File.read(path) # => "foobarbazbat" + * + * - Reading is not allowed: + * + * f.read # Raises IOError. + * + * - 'r+': + * + * - File is not initially truncated: + * + * path = 't.tmp' + * File.write(path, text) + * f = File.new(path, 'r+') + * f.size == 0 # => false + * + * - File's initial read position is 0: + * + * f.pos # => 0 + * + * - File may be read or written anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.readline # => "First line\n" + * f.readline # => "Second line\n" + * + * f.rewind + * f.readline # => "First line\n" + * + * f.pos = 1 + * f.readline # => "irst line\n" + * + * f.seek(1, :CUR) + * f.readline # => "econd line\n" + * + * f.rewind + * f.write('WWW') + * f.flush + * File.read(path) + * # => "WWWst line\nSecond line\nFourth line\nFifth line\n" + * + * f.pos = 10 + * f.write('XXX') + * f.flush + * File.read(path) + * # => "WWWst lineXXXecond line\nFourth line\nFifth line\n" + * + * f.seek(-6, :END) + * # => 0 + * f.write('YYY') + * # => 3 + * f.flush + * # => # + * File.read(path) + * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n" + * + * f.seek(2, :END) + * f.write('ZZZ') # Zero padding as needed. + * f.flush + * File.read(path) + * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n\u0000\u0000ZZZ" + * + * + * - 'a+': + * + * - File is not initially truncated: + * + * path = 't.tmp' + * File.write(path, 'foo') + * f = File.new(path, 'a+') + * f.size == 0 # => false + * + * - File's initial read position is 0: + * + * f.pos # => 0 + * + * - File may be written only at end-of-file; + * IO#rewind, IO#pos=, IO#seek do not affect writing: + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.write('baz') + * f.flush + * File.read(path) # => "foobarbaz" + * + * f.rewind + * f.write('bat') + * f.flush + * File.read(path) # => "foobarbazbat" + * + * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek: + * + * f.rewind + * f.read # => "foobarbazbat" + * + * f.pos = 3 + * f.read # => "barbazbat" + * + * f.seek(-3, :END) + * f.read # => "bat" + * + * ===== Read/Write Modes for \File To Be Created + * + * Note that modes 'r' and 'r+' are not allowed + * for a non-existent file (exception raised). + * + * - 'w': + * + * - File's initial write position is 0: + * + * path = 't.tmp' + * FileUtils.rm_f(path) + * f = File.new(path, 'w') + * f.pos # => 0 + * + * - File may be written anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.write('foo') + * f.flush + * File.read(path) # => "foo" + * f.pos # => 3 + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.pos # => 6 + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "bazbar" + * f.pos # => 3 + * + * f.pos = 3 + * f.write('foo') + * f.flush + * File.read(path) # => "bazfoo" + * f.pos # => 6 + * + * f.seek(-3, :END) + * f.write('bam') + * f.flush + * File.read(path) # => "bazbam" + * f.pos # => 6 + * + * f.pos = 8 + * f.write('bah') # Zero padding as needed. + * f.flush + * File.read(path) # => "bazbam\u0000\u0000bah" + * f.pos # => 11 + * + * - Reading is not allowed: + * + * f.read # Raises IOError. + * + * - 'a': + * + * - File's initial write position is 0: + * + * path = 't.tmp' + * FileUtils.rm_f(path) + * f = File.new(path, 'a') + * f.pos # => 0 + * + * - Writing occurs only at end-of-file: + * + * f.write('foo') + * f.pos # => 3 + * f.write('bar') + * f.pos # => 6 + * f.flush + * File.read(path) # => "foobar" + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "foobarbaz" + * + * - Reading is not allowed: + * + * f.read # Raises IOError. + * + * - 'w+': + * + * - File's initial position is 0: + * + * path = 't.tmp' + * FileUtils.rm_f(path) + * f = File.new(path, 'w+') + * f.pos # => 0 + * + * - File may be written anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.write('foo') + * f.flush + * File.read(path) # => "foo" + * f.pos # => 3 + * + * f.write('bar') + * f.flush + * File.read(path) # => "foobar" + * f.pos # => 6 + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "bazbar" + * f.pos # => 3 + * + * f.pos = 3 + * f.write('foo') + * f.flush + * File.read(path) # => "bazfoo" + * f.pos # => 6 + * + * f.seek(-3, :END) + * f.write('bam') + * f.flush + * File.read(path) # => "bazbam" + * f.pos # => 6 + * + * f.pos = 8 + * f.write('bah') # Zero padding as needed. + * f.flush + * File.read(path) # => "bazbam\u0000\u0000bah" + * f.pos # => 11 + * + * - File may be read anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.rewind + * # => 0 + * f.read + * # => "bazbam\u0000\u0000bah" + * + * f.pos = 3 + * # => 3 + * f.read + * # => "bam\u0000\u0000bah" + * + * f.seek(-3, :END) + * # => 0 + * f.read + * # => "bah" + * + * - 'a+': + * + * - File's initial write position is 0: + * + * path = 't.tmp' + * FileUtils.rm_f(path) + * f = File.new(path, 'a+') + * f.pos # => 0 + * + * - Writing occurs only at end-of-file: + * + * f.write('foo') + * f.pos # => 3 + * f.write('bar') + * f.pos # => 6 + * f.flush + * File.read(path) # => "foobar" + * + * f.rewind + * f.write('baz') + * f.flush + * File.read(path) # => "foobarbaz" + * + * - File may be read anywhere (even past end-of-file); + * see IO#rewind, IO#pos=, IO#seek: + * + * f.rewind + * f.read # => "foobarbaz" + * + * f.pos = 3 + * f.read # => "barbaz" + * + * f.seek(-3, :END) + * f.read # => "baz" + * + * f.pos = 800 + * f.read # => "" + * + * ==== Data Mode + * + * To specify whether data is to be treated as text or as binary data, + * either of the following may be suffixed to any of the string read/write modes + * above: + * + * - 't': Text data; sets the default external encoding + * to Encoding::UTF_8; + * on Windows, enables conversion between EOL and CRLF + * and enables interpreting 0x1A as an end-of-file marker. + * - 'b': Binary data; sets the default external encoding + * to Encoding::ASCII_8BIT; + * on Windows, suppresses conversion between EOL and CRLF + * and disables interpreting 0x1A as an end-of-file marker. + * + * If neither is given, the stream defaults to text data. + * + * Examples: + * + * File.new('t.txt', 'rt') + * File.new('t.dat', 'rb') + * + * When the data mode is specified, the read/write mode may not be omitted, + * and the data mode must precede the file-create mode, if given: + * + * File.new('t.dat', 'b') # Raises an exception. + * File.new('t.dat', 'rxb') # Raises an exception. + * + * ==== \File-Create Mode + * + * The following may be suffixed to any writable string mode above: + * + * - 'x': Creates the file if it does not exist; + * raises an exception if the file exists. + * + * Example: + * + * File.new('t.tmp', 'wx') + * + * When the file-create mode is specified, the read/write mode may not be omitted, + * and the file-create mode must follow the data mode: + * + * File.new('t.dat', 'x') # Raises an exception. + * File.new('t.dat', 'rxb') # Raises an exception. + * + * === \Integer Access Modes + * + * When mode is an integer it must be one or more of the following constants, + * which may be combined by the bitwise OR operator |: + * + * - +File::RDONLY+: Open for reading only. + * - +File::WRONLY+: Open for writing only. + * - +File::RDWR+: Open for reading and writing. + * - +File::APPEND+: Open for appending only. + * + * Examples: + * + * File.new('t.txt', File::RDONLY) + * File.new('t.tmp', File::RDWR | File::CREAT | File::EXCL) + * + * Note: Method IO#set_encoding does not allow the mode to be specified as an integer. + * + * === File-Create Mode Specified as an \Integer + * + * These constants may also be ORed into the integer mode: + * + * - +File::CREAT+: Create file if it does not exist. + * - +File::EXCL+: Raise an exception if +File::CREAT+ is given and the file exists. + * + * === Data Mode Specified as an \Integer + * + * Data mode cannot be specified as an integer. + * When the stream access mode is given as an integer, + * the data mode is always text, never binary. + * + * Note that although there is a constant +File::BINARY+, + * setting its value in an integer stream mode has no effect; + * this is because, as documented in File::Constants, + * the +File::BINARY+ value disables line code conversion, + * but does not change the external encoding. + * + * === Encodings + * + * Any of the string modes above may specify encodings - + * either external encoding only or both external and internal encodings - + * by appending one or both encoding names, separated by colons: + * + * f = File.new('t.dat', 'rb') + * f.external_encoding # => # + * f.internal_encoding # => nil + * f = File.new('t.dat', 'rb:UTF-16') + * f.external_encoding # => # + * f.internal_encoding # => nil + * f = File.new('t.dat', 'rb:UTF-16:UTF-16') + * f.external_encoding # => # + * f.internal_encoding # => # + * f.close + * + * The numerous encoding names are available in array Encoding.name_list: + * + * Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"] + * + * When the external encoding is set, strings read are tagged by that encoding + * when reading, and strings written are converted to that encoding when + * writing. + * + * When both external and internal encodings are set, + * strings read are converted from external to internal encoding, + * and strings written are converted from internal to external encoding. + * For further details about transcoding input and output, + * see {Encodings}[rdoc-ref:io_streams.rdoc@Encodings]. + * + * If the external encoding is 'BOM|UTF-8', 'BOM|UTF-16LE' + * or 'BOM|UTF16-BE', + * Ruby checks for a Unicode BOM in the input document + * to help determine the encoding. + * For UTF-16 encodings the file open mode must be binary. + * If the BOM is found, + * it is stripped and the external encoding from the BOM is used. + * + * Note that the BOM-style encoding option is case insensitive, + * so 'bom|utf-8' is also valid. + * * == \File Permissions * * A \File object has _permissions_, an octal integer representing @@ -6622,34 +7220,6 @@ const char ruby_null_device[] = * may be found in module File::Constants; * an array of their names is returned by File::Constants.constants. * - * == Example Files - * - * Many examples here use these filenames and their corresponding files: - * - * - t.txt: A text-only file that is assumed to exist via: - * - * text = <<~EOT - * First line - * Second line - * - * Fourth line - * Fifth line - * EOT - * File.write('t.txt', text) - * - * - t.dat: A data file that is assumed to exist via: - * - * data = "\u9990\u9991\u9992\u9993\u9994" - * f = File.open('t.dat', 'wb:UTF-16') - * f.write(data) - * f.close - * - * - t.rus: A Russian-language text file that is assumed to exist via: - * - * File.write('t.rus', "\u{442 435 441 442}") - * - * - t.tmp: A file that is assumed _not_ to exist. - * * == What's Here * * First, what's elsewhere. \Class \File: @@ -6787,6 +7357,10 @@ const char ruby_null_device[] = void Init_File(void) { +#if defined(__APPLE__) && defined(HAVE_WORKING_FORK) + rb_CFString_class_initialize_before_fork(); +#endif + VALUE separator; rb_mFileTest = rb_define_module("FileTest"); diff --git a/gc.c b/gc.c index 037b0fd4da2680..f1aee6c369aca9 100644 --- a/gc.c +++ b/gc.c @@ -14315,11 +14315,10 @@ rb_raw_iseq_info(char *const buff, const size_t buff_size, const rb_iseq_t *iseq { if (buff_size > 0 && ISEQ_BODY(iseq) && ISEQ_BODY(iseq)->location.label && !RB_TYPE_P(ISEQ_BODY(iseq)->location.pathobj, T_MOVED)) { VALUE path = rb_iseq_path(iseq); - VALUE n = ISEQ_BODY(iseq)->location.first_lineno; + int n = ISEQ_BODY(iseq)->location.first_lineno; snprintf(buff, buff_size, " %s@%s:%d", RSTRING_PTR(ISEQ_BODY(iseq)->location.label), - RSTRING_PTR(path), - n ? FIX2INT(n) : 0 ); + RSTRING_PTR(path), n); } } diff --git a/gems/bundled_gems b/gems/bundled_gems index be637125fb5b47..e8cb03d38cbdc6 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -13,4 +13,4 @@ matrix 0.4.2 https://github.com/ruby/matrix prime 0.1.2 https://github.com/ruby/prime rbs 2.6.0 https://github.com/ruby/rbs 5ec9d53efe4bf0a97f33c3016aed430be135583a typeprof 0.21.3 https://github.com/ruby/typeprof -debug 1.6.2 https://github.com/ruby/debug e7c37486ff9579251e5d25645b8d38ec96708f12 +debug 1.6.2 https://github.com/ruby/debug 19b4dde3308f532943e4234d1588d4fa26c52345 diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h index 9f67bd5bf6d194..d38651da5c9109 100644 --- a/include/ruby/fiber/scheduler.h +++ b/include/ruby/fiber/scheduler.h @@ -276,7 +276,7 @@ VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_ * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`. * @return otherwise What `scheduler.io_read` returns. */ -VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset); +VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset); /** * Nonblocking write to the passed IO at the specified offset. @@ -289,7 +289,7 @@ VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_ * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`. * @return otherwise What `scheduler.io_write` returns. */ -VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset); +VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset); /** * Nonblocking read from the passed IO using a native buffer. diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h index 9d8d16fdab6bd8..e3e1b6166db3fa 100644 --- a/include/ruby/internal/anyargs.h +++ b/include/ruby/internal/anyargs.h @@ -239,15 +239,16 @@ # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_method_12(n)) # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n)) # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n)) # define RBIMPL_ANYARGS_ATTRSET(sym) RBIMPL_ATTR_MAYBE_UNUSED() RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_WEAKREF(sym) # define RBIMPL_ANYARGS_DECL(sym, ...) \ +RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _notimpl(__VA_ARGS__, VALUE(*)(int, const VALUE *, VALUE, VALUE), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m3(__VA_ARGS__, VALUE(*)(ANYARGS), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m2(__VA_ARGS__, VALUE(*)(VALUE, VALUE), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m1(__VA_ARGS__, VALUE(*)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE), int); \ diff --git a/include/ruby/internal/intern/file.h b/include/ruby/internal/intern/file.h index 2dc60c7ba78e36..79820fdc61657c 100644 --- a/include/ruby/internal/intern/file.h +++ b/include/ruby/internal/intern/file.h @@ -206,7 +206,7 @@ int rb_is_absolute_path(const char *path); * unpredictable. POSIX's `` states that "the use of * this field is unspecified" then. */ -off_t rb_file_size(VALUE file); +rb_off_t rb_file_size(VALUE file); RBIMPL_SYMBOL_EXPORT_END() diff --git a/include/ruby/internal/scan_args.h b/include/ruby/internal/scan_args.h index cf5b18f77d89b5..1ed2bf636874f3 100644 --- a/include/ruby/internal/scan_args.h +++ b/include/ruby/internal/scan_args.h @@ -100,7 +100,7 @@ RBIMPL_ATTR_NONNULL((2, 3)) * param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec / * pre-opt-post-arg-spec * pre-arg-spec := num-of-leading-mandatory-args - [num-of-optional-args] + * [num-of-optional-args] * post-arg-spec := sym-for-variable-length-args * [num-of-trailing-mandatory-args] * pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h index 907fec20bb47b9..16b23ec6290871 100644 --- a/include/ruby/io/buffer.h +++ b/include/ruby/io/buffer.h @@ -1,5 +1,5 @@ -#ifndef RUBY_IO_BUFFER_T -#define RUBY_IO_BUFFER_T 1 +#ifndef RUBY_IO_BUFFER_H +#define RUBY_IO_BUFFER_H /** * @file * @author Samuel Williams @@ -65,7 +65,7 @@ enum rb_io_buffer_endian { }; VALUE rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags); -VALUE rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags); +VALUE rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags); VALUE rb_io_buffer_lock(VALUE self); VALUE rb_io_buffer_unlock(VALUE self); @@ -82,10 +82,10 @@ void rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length) // The length is the minimum required length. VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length); -VALUE rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset); +VALUE rb_io_buffer_pread(VALUE self, VALUE io, size_t length, rb_off_t offset); VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length); -VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset); +VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset); RBIMPL_SYMBOL_EXPORT_END() -#endif /* RUBY_IO_BUFFER_T */ +#endif /* RUBY_IO_BUFFER_H */ diff --git a/include/ruby/win32.h b/include/ruby/win32.h index ea460a5e46cd40..93e6183ed990a2 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -195,7 +195,6 @@ struct stati128 { long st_ctimensec; }; -#define off_t __int64 #define stat stati128 #undef SIZEOF_STRUCT_STAT_ST_INO #define SIZEOF_STRUCT_STAT_ST_INO sizeof(unsigned __int64) @@ -401,9 +400,9 @@ scalb(double a, long b) #define SUFFIX -extern int rb_w32_ftruncate(int fd, off_t length); -extern int rb_w32_truncate(const char *path, off_t length); -extern int rb_w32_utruncate(const char *path, off_t length); +extern int rb_w32_ftruncate(int fd, rb_off_t length); +extern int rb_w32_truncate(const char *path, rb_off_t length); +extern int rb_w32_utruncate(const char *path, rb_off_t length); #undef HAVE_FTRUNCATE #define HAVE_FTRUNCATE 1 @@ -722,7 +721,7 @@ int rb_w32_fclose(FILE*); int rb_w32_pipe(int[2]); ssize_t rb_w32_read(int, void *, size_t); ssize_t rb_w32_write(int, const void *, size_t); -off_t rb_w32_lseek(int, off_t, int); +rb_off_t rb_w32_lseek(int, rb_off_t, int); int rb_w32_uutime(const char *, const struct utimbuf *); int rb_w32_uutimes(const char *, const struct timeval *); int rb_w32_uutimensat(int /* must be AT_FDCWD */, const char *, const struct timespec *, int /* must be 0 */); @@ -815,7 +814,7 @@ double rb_w32_pow(double x, double y); #define MAP_ANON 0x1000 #define MAP_ANONYMOUS MAP_ANON -extern void *rb_w32_mmap(void *, size_t, int, int, int, off_t); +extern void *rb_w32_mmap(void *, size_t, int, int, int, rb_off_t); extern int rb_w32_munmap(void *, size_t); extern int rb_w32_mprotect(void *, size_t, int); diff --git a/inits.c b/inits.c index f43ece0e47a024..9decba3c11006a 100644 --- a/inits.c +++ b/inits.c @@ -104,8 +104,8 @@ rb_call_builtin_inits(void) BUILTIN(marshal); #if USE_MJIT BUILTIN(mjit); + BUILTIN(mjit_c); BUILTIN(mjit_compiler); - BUILTIN(mjit_instruction); #endif Init_builtin_prelude(); } diff --git a/internal/encoding.h b/internal/encoding.h index c48cb24b0459ee..853426a58d1daa 100644 --- a/internal/encoding.h +++ b/internal/encoding.h @@ -24,7 +24,6 @@ int rb_encdb_dummy(const char *name); void rb_encdb_declare(const char *name); void rb_enc_set_base(const char *name, const char *orig); int rb_enc_set_dummy(int index); -void rb_encdb_set_unicode(int index); PUREFUNC(int rb_data_is_encoding(VALUE obj)); #endif /* INTERNAL_ENCODING_H */ diff --git a/internal/string.h b/internal/string.h index 46862d77f5cbc2..43b716f9b25f4b 100644 --- a/internal/string.h +++ b/internal/string.h @@ -44,6 +44,7 @@ const char *ruby_escaped_char(int c); void rb_str_make_independent(VALUE str); int rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc); int rb_ascii8bit_appendable_encoding_index(rb_encoding *enc, unsigned int code); +VALUE rb_str_include(VALUE str, VALUE arg); static inline bool STR_EMBED_P(VALUE str); static inline bool STR_SHARED_P(VALUE str); diff --git a/io.c b/io.c index 1eb48dd19b22d0..494e40bcd931c5 100644 --- a/io.c +++ b/io.c @@ -75,10 +75,6 @@ #include #endif -#if !HAVE_OFF_T && !defined(off_t) -# define off_t long -#endif - #ifdef HAVE_SYS_TIME_H # include #endif @@ -149,10 +145,6 @@ #define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) #endif -#if SIZEOF_OFF_T > SIZEOF_LONG && !defined(HAVE_LONG_LONG) -# error off_t is bigger than long, but you have no long long... -#endif - #ifndef PIPE_BUF # ifdef _POSIX_PIPE_BUF # define PIPE_BUF _POSIX_PIPE_BUF @@ -620,7 +612,7 @@ raise_on_write(rb_io_t *fptr, int e, VALUE errinfo) static void io_unread(rb_io_t *fptr) { - off_t r, pos; + rb_off_t r, pos; ssize_t read_size; long i; long newlines = 0; @@ -859,7 +851,7 @@ rb_io_s_try_convert(VALUE dummy, VALUE io) static void io_unread(rb_io_t *fptr) { - off_t r; + rb_off_t r; rb_io_check_closed(fptr); if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX) return; @@ -2224,7 +2216,7 @@ static VALUE rb_io_tell(VALUE io) { rb_io_t *fptr; - off_t pos; + rb_off_t pos; GetOpenFile(io, fptr); pos = io_tell(fptr); @@ -2237,7 +2229,7 @@ static VALUE rb_io_seek(VALUE io, VALUE offset, int whence) { rb_io_t *fptr; - off_t pos; + rb_off_t pos; pos = NUM2OFFT(offset); GetOpenFile(io, fptr); @@ -2348,7 +2340,7 @@ static VALUE rb_io_set_pos(VALUE io, VALUE offset) { rb_io_t *fptr; - off_t pos; + rb_off_t pos; pos = NUM2OFFT(offset); GetOpenFile(io, fptr); @@ -2884,8 +2876,8 @@ static long remain_size(rb_io_t *fptr) { struct stat st; - off_t siz = READ_DATA_PENDING_COUNT(fptr); - off_t pos; + rb_off_t siz = READ_DATA_PENDING_COUNT(fptr); + rb_off_t pos; if (fstat(fptr->fd, &st) == 0 && S_ISREG(st.st_mode) #if defined(__HAIKU__) @@ -3061,7 +3053,8 @@ static int io_setstrbuf(VALUE *str, long len) { #ifdef _WIN32 - len = (len + 1) & ~1L; /* round up for wide char */ + if (len > 0) + len = (len + 1) & ~1L; /* round up for wide char */ #endif if (NIL_P(*str)) { *str = rb_str_new(0, len); @@ -4063,7 +4056,7 @@ rb_io_gets_internal(VALUE io) * * With only integer argument +limit+ given, * limits the number of bytes in the line; - * see {Line Limit}}[rdoc-ref:IO@Line+Limit]: + * see {Line Limit}[rdoc-ref:IO@Line+Limit]: * * # No more than one line. * File.open('t.txt') {|f| f.gets(10) } # => "First line" @@ -4320,7 +4313,7 @@ io_readlines(const struct getline_arg *arg, VALUE io) * * With only integer argument +limit+ given, * limits the number of bytes in each line; - * see {Line Limit}}[rdoc-ref:IO@Line+Limit]: + * see {Line Limit}[rdoc-ref:IO@Line+Limit]: * * f = File.new('t.txt') * f.each_line(8) {|line| p line } @@ -5662,7 +5655,7 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io) VALUE offset, ptrname; int whence = SEEK_SET; rb_io_t *fptr; - off_t pos; + rb_off_t pos; if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) { whence = interpret_seek_whence(ptrname); @@ -5790,7 +5783,7 @@ struct prdwr_internal_arg { int fd; void *buf; size_t count; - off_t offset; + rb_off_t offset; }; #endif /* HAVE_PREAD || HAVE_PWRITE */ @@ -7984,7 +7977,7 @@ io_reopen(VALUE io, VALUE nfile) { rb_io_t *fptr, *orig; int fd, fd2; - off_t pos = 0; + rb_off_t pos = 0; nfile = rb_io_get_io(nfile); GetOpenFile(io, fptr); @@ -8204,7 +8197,7 @@ rb_io_init_copy(VALUE dest, VALUE io) rb_io_t *fptr, *orig; int fd; VALUE write_io; - off_t pos; + rb_off_t pos; io = rb_io_get_io(io); if (!OBJ_INIT_COPY(dest, io)) return dest; @@ -10345,8 +10338,8 @@ static VALUE sym_normal, sym_sequential, sym_random, struct io_advise_struct { int fd; int advice; - off_t offset; - off_t len; + rb_off_t offset; + rb_off_t len; }; static VALUE @@ -10393,7 +10386,7 @@ io_advise_sym_to_const(VALUE sym) } static VALUE -do_io_advise(rb_io_t *fptr, VALUE advice, off_t offset, off_t len) +do_io_advise(rb_io_t *fptr, VALUE advice, rb_off_t offset, rb_off_t len) { int rv; struct io_advise_struct ias; @@ -10483,7 +10476,7 @@ static VALUE rb_io_advise(int argc, VALUE *argv, VALUE io) { VALUE advice, offset, len; - off_t off, l; + rb_off_t off, l; rb_io_t *fptr; rb_scan_args(argc, argv, "12", &advice, &offset, &len); @@ -12029,15 +12022,15 @@ rb_io_s_binwrite(int argc, VALUE *argv, VALUE io) struct copy_stream_struct { VALUE src; VALUE dst; - off_t copy_length; /* (off_t)-1 if not specified */ - off_t src_offset; /* (off_t)-1 if not specified */ + rb_off_t copy_length; /* (rb_off_t)-1 if not specified */ + rb_off_t src_offset; /* (rb_off_t)-1 if not specified */ rb_io_t *src_fptr; rb_io_t *dst_fptr; unsigned close_src : 1; unsigned close_dst : 1; int error_no; - off_t total; + rb_off_t total; const char *syserr; const char *notimp; VALUE th; @@ -12202,7 +12195,7 @@ nogvl_copy_stream_wait_write(struct copy_stream_struct *stp) #ifdef USE_COPY_FILE_RANGE static ssize_t -simple_copy_file_range(int in_fd, off_t *in_offset, int out_fd, off_t *out_offset, size_t count, unsigned int flags) +simple_copy_file_range(int in_fd, rb_off_t *in_offset, int out_fd, rb_off_t *out_offset, size_t count, unsigned int flags) { #ifdef HAVE_COPY_FILE_RANGE return copy_file_range(in_fd, in_offset, out_fd, out_offset, count, flags); @@ -12215,15 +12208,15 @@ static int nogvl_copy_file_range(struct copy_stream_struct *stp) { ssize_t ss; - off_t src_size; - off_t copy_length, src_offset, *src_offset_ptr; + rb_off_t src_size; + rb_off_t copy_length, src_offset, *src_offset_ptr; if (!S_ISREG(stp->src_stat.st_mode)) return 0; src_size = stp->src_stat.st_size; src_offset = stp->src_offset; - if (src_offset >= (off_t)0) { + if (src_offset >= (rb_off_t)0) { src_offset_ptr = &src_offset; } else { @@ -12231,12 +12224,12 @@ nogvl_copy_file_range(struct copy_stream_struct *stp) } copy_length = stp->copy_length; - if (copy_length < (off_t)0) { - if (src_offset < (off_t)0) { - off_t current_offset; + if (copy_length < (rb_off_t)0) { + if (src_offset < (rb_off_t)0) { + rb_off_t current_offset; errno = 0; current_offset = lseek(stp->src_fptr->fd, 0, SEEK_CUR); - if (current_offset < (off_t)0 && errno) { + if (current_offset < (rb_off_t)0 && errno) { stp->syserr = "lseek"; stp->error_no = errno; return (int)current_offset; @@ -12251,7 +12244,7 @@ nogvl_copy_file_range(struct copy_stream_struct *stp) retry_copy_file_range: # if SIZEOF_OFF_T > SIZEOF_SIZE_T /* we are limited by the 32-bit ssize_t return value on 32-bit */ - ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length; + ss = (copy_length > (rb_off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length; # else ss = (ssize_t)copy_length; # endif @@ -12310,11 +12303,11 @@ nogvl_copy_file_range(struct copy_stream_struct *stp) static int nogvl_fcopyfile(struct copy_stream_struct *stp) { - off_t cur, ss = 0; - const off_t src_offset = stp->src_offset; + rb_off_t cur, ss = 0; + const rb_off_t src_offset = stp->src_offset; int ret; - if (stp->copy_length >= (off_t)0) { + if (stp->copy_length >= (rb_off_t)0) { /* copy_length can't be specified in fcopyfile(3) */ return 0; } @@ -12324,30 +12317,30 @@ nogvl_fcopyfile(struct copy_stream_struct *stp) if (!S_ISREG(stp->dst_stat.st_mode)) return 0; - if (lseek(stp->dst_fptr->fd, 0, SEEK_CUR) > (off_t)0) /* if dst IO was already written */ + if (lseek(stp->dst_fptr->fd, 0, SEEK_CUR) > (rb_off_t)0) /* if dst IO was already written */ return 0; if (fcntl(stp->dst_fptr->fd, F_GETFL) & O_APPEND) { /* fcopyfile(3) appends src IO to dst IO and then truncates * dst IO to src IO's original size. */ - off_t end = lseek(stp->dst_fptr->fd, 0, SEEK_END); + rb_off_t end = lseek(stp->dst_fptr->fd, 0, SEEK_END); lseek(stp->dst_fptr->fd, 0, SEEK_SET); - if (end > (off_t)0) return 0; + if (end > (rb_off_t)0) return 0; } - if (src_offset > (off_t)0) { - off_t r; + if (src_offset > (rb_off_t)0) { + rb_off_t r; /* get current offset */ errno = 0; cur = lseek(stp->src_fptr->fd, 0, SEEK_CUR); - if (cur < (off_t)0 && errno) { + if (cur < (rb_off_t)0 && errno) { stp->error_no = errno; return 1; } errno = 0; r = lseek(stp->src_fptr->fd, src_offset, SEEK_SET); - if (r < (off_t)0 && errno) { + if (r < (rb_off_t)0 && errno) { stp->error_no = errno; return 1; } @@ -12359,12 +12352,12 @@ nogvl_fcopyfile(struct copy_stream_struct *stp) if (ret == 0) { /* success */ stp->total = ss; - if (src_offset > (off_t)0) { - off_t r; + if (src_offset > (rb_off_t)0) { + rb_off_t r; errno = 0; /* reset offset */ r = lseek(stp->src_fptr->fd, cur, SEEK_SET); - if (r < (off_t)0 && errno) { + if (r < (rb_off_t)0 && errno) { stp->error_no = errno; return 1; } @@ -12395,7 +12388,7 @@ nogvl_fcopyfile(struct copy_stream_struct *stp) # endif static ssize_t -simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count) +simple_sendfile(int out_fd, int in_fd, rb_off_t *offset, rb_off_t count) { return sendfile(out_fd, in_fd, offset, (size_t)count); } @@ -12407,11 +12400,11 @@ simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count) # define USE_SENDFILE static ssize_t -simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count) +simple_sendfile(int out_fd, int in_fd, rb_off_t *offset, rb_off_t count) { int r; - off_t pos = offset ? *offset : lseek(in_fd, 0, SEEK_CUR); - off_t sbytes; + rb_off_t pos = offset ? *offset : lseek(in_fd, 0, SEEK_CUR); + rb_off_t sbytes; # ifdef __APPLE__ r = sendfile(in_fd, out_fd, pos, &count, NULL, 0); sbytes = count; @@ -12437,9 +12430,9 @@ static int nogvl_copy_stream_sendfile(struct copy_stream_struct *stp) { ssize_t ss; - off_t src_size; - off_t copy_length; - off_t src_offset; + rb_off_t src_size; + rb_off_t copy_length; + rb_off_t src_offset; int use_pread; if (!S_ISREG(stp->src_stat.st_mode)) @@ -12452,17 +12445,17 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp) #endif src_offset = stp->src_offset; - use_pread = src_offset >= (off_t)0; + use_pread = src_offset >= (rb_off_t)0; copy_length = stp->copy_length; - if (copy_length < (off_t)0) { + if (copy_length < (rb_off_t)0) { if (use_pread) copy_length = src_size - src_offset; else { - off_t cur; + rb_off_t cur; errno = 0; cur = lseek(stp->src_fptr->fd, 0, SEEK_CUR); - if (cur < (off_t)0 && errno) { + if (cur < (rb_off_t)0 && errno) { stp->syserr = "lseek"; stp->error_no = errno; return (int)cur; @@ -12474,7 +12467,7 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp) retry_sendfile: # if SIZEOF_OFF_T > SIZEOF_SIZE_T /* we are limited by the 32-bit ssize_t return value on 32-bit */ - ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length; + ss = (copy_length > (rb_off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length; # else ss = (ssize_t)copy_length; # endif @@ -12545,11 +12538,11 @@ maygvl_read(int has_gvl, rb_io_t *fptr, void *buf, size_t count) } static ssize_t -maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, size_t len, off_t offset) +maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, size_t len, rb_off_t offset) { ssize_t ss; retry_read: - if (offset < (off_t)0) { + if (offset < (rb_off_t)0) { ss = maygvl_read(has_gvl, stp->src_fptr, buf, len); } else { @@ -12582,7 +12575,7 @@ maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf, return ss; #endif } - stp->syserr = offset < (off_t)0 ? "read" : "pread"; + stp->syserr = offset < (rb_off_t)0 ? "read" : "pread"; stp->error_no = errno; } return ss; @@ -12621,31 +12614,31 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp) size_t len; ssize_t ss; int ret; - off_t copy_length; + rb_off_t copy_length; + rb_off_t src_offset; int use_eof; - off_t src_offset; int use_pread; copy_length = stp->copy_length; - use_eof = copy_length < (off_t)0; + use_eof = copy_length < (rb_off_t)0; src_offset = stp->src_offset; - use_pread = src_offset >= (off_t)0; + use_pread = src_offset >= (rb_off_t)0; if (use_pread && stp->close_src) { - off_t r; + rb_off_t r; errno = 0; r = lseek(stp->src_fptr->fd, src_offset, SEEK_SET); - if (r < (off_t)0 && errno) { + if (r < (rb_off_t)0 && errno) { stp->syserr = "lseek"; stp->error_no = errno; return; } - src_offset = (off_t)-1; + src_offset = (rb_off_t)-1; use_pread = 0; } while (use_eof || 0 < copy_length) { - if (!use_eof && copy_length < (off_t)sizeof(buf)) { + if (!use_eof && copy_length < (rb_off_t)sizeof(buf)) { len = (size_t)copy_length; } else { @@ -12657,7 +12650,7 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp) src_offset += ss; } else { - ss = maygvl_copy_stream_read(0, stp, buf, len, (off_t)-1); + ss = maygvl_copy_stream_read(0, stp, buf, len, (rb_off_t)-1); } if (ss <= 0) /* EOF or error */ return; @@ -12712,8 +12705,8 @@ copy_stream_fallback_body(VALUE arg) const int buflen = 16*1024; VALUE n; VALUE buf = rb_str_buf_new(buflen); - off_t rest = stp->copy_length; - off_t off = stp->src_offset; + rb_off_t rest = stp->copy_length; + rb_off_t off = stp->src_offset; ID read_method = id_readpartial; if (!stp->src_fptr) { @@ -12725,7 +12718,7 @@ copy_stream_fallback_body(VALUE arg) while (1) { long numwrote; long l; - if (stp->copy_length < (off_t)0) { + if (stp->copy_length < (rb_off_t)0) { l = buflen; } else { @@ -12750,7 +12743,7 @@ copy_stream_fallback_body(VALUE arg) return Qnil; if (ss == 0) rb_eof_error(); - if (off >= (off_t)0) + if (off >= (rb_off_t)0) off += ss; } n = rb_io_write(stp->dst, buf); @@ -12768,7 +12761,7 @@ copy_stream_fallback_body(VALUE arg) static VALUE copy_stream_fallback(struct copy_stream_struct *stp) { - if (!stp->src_fptr && stp->src_offset >= (off_t)0) { + if (!stp->src_fptr && stp->src_offset >= (rb_off_t)0) { rb_raise(rb_eArgError, "cannot specify src_offset for non-IO"); } rb_rescue2(copy_stream_fallback_body, (VALUE)stp, @@ -12868,10 +12861,10 @@ copy_stream_body(VALUE arg) if (stp->dst_fptr) io_ascii8bit_binmode(stp->dst_fptr); - if (stp->src_offset < (off_t)0 && stp->src_fptr && stp->src_fptr->rbuf.len) { + if (stp->src_offset < (rb_off_t)0 && stp->src_fptr && stp->src_fptr->rbuf.len) { size_t len = stp->src_fptr->rbuf.len; VALUE str; - if (stp->copy_length >= (off_t)0 && stp->copy_length < (off_t)len) { + if (stp->copy_length >= (rb_off_t)0 && stp->copy_length < (rb_off_t)len) { len = (size_t)stp->copy_length; } str = rb_str_buf_new(len); @@ -12885,7 +12878,7 @@ copy_stream_body(VALUE arg) rb_io_write(dst_io, str); rb_str_resize(str, 0); stp->total += len; - if (stp->copy_length >= (off_t)0) + if (stp->copy_length >= (rb_off_t)0) stp->copy_length -= len; } @@ -12998,12 +12991,12 @@ rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io) st.dst_fptr = NULL; if (NIL_P(length)) - st.copy_length = (off_t)-1; + st.copy_length = (rb_off_t)-1; else st.copy_length = NUM2OFFT(length); if (NIL_P(src_offset)) - st.src_offset = (off_t)-1; + st.src_offset = (rb_off_t)-1; else st.src_offset = NUM2OFFT(src_offset); diff --git a/io_buffer.c b/io_buffer.c index 6ce0dd13dfba76..4326d21defcb7e 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -11,9 +11,11 @@ #include "ruby/fiber/scheduler.h" #include "internal.h" -#include "internal/string.h" +#include "internal/array.h" #include "internal/bits.h" #include "internal/error.h" +#include "internal/string.h" +#include "internal/thread.h" VALUE rb_cIOBuffer; VALUE rb_eIOBufferLockedError; @@ -64,7 +66,7 @@ io_buffer_map_memory(size_t size) } static void -io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t offset, enum rb_io_buffer_flags flags) +io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags) { #if defined(_WIN32) HANDLE file = (HANDLE)_get_osfhandle(descriptor); @@ -409,7 +411,7 @@ rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags) } VALUE -rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags) +rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags) { io_buffer_experimental(); @@ -461,7 +463,6 @@ rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags fl * * Note that some operating systems may not have cache coherency between mapped * buffers and file reads. - * */ static VALUE io_buffer_map(int argc, VALUE *argv, VALUE klass) @@ -478,7 +479,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass) size = RB_NUM2SIZE(argv[1]); } else { - off_t file_size = rb_file_size(io); + rb_off_t file_size = rb_file_size(io); // Compiler can confirm that we handled file_size < 0 case: if (file_size < 0) { @@ -494,7 +495,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass) } } - off_t offset = 0; + rb_off_t offset = 0; if (argc >= 3) { offset = NUM2OFFT(argv[2]); } @@ -543,7 +544,6 @@ io_flags_for_size(size_t size) * # => * # # * # 0x00000000 74 65 73 74 test - * */ VALUE rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self) @@ -628,7 +628,6 @@ io_buffer_validate(struct rb_io_buffer *data) * * puts IO::Buffer.new(4) # uses to_s internally * # # - * */ VALUE rb_io_buffer_to_s(VALUE self) @@ -756,7 +755,6 @@ rb_io_buffer_inspect(VALUE self) * * Returns the size of the buffer that was explicitly set (on creation with ::new * or on #resize), or deduced on buffer's creation from string or file. - * */ VALUE rb_io_buffer_size(VALUE self) @@ -774,7 +772,6 @@ rb_io_buffer_size(VALUE self) * * A buffer becomes invalid if it is a slice of another buffer which has been * freed. - * */ static VALUE rb_io_buffer_valid_p(VALUE self) @@ -790,7 +787,6 @@ rb_io_buffer_valid_p(VALUE self) * * If the buffer was freed with #free or was never allocated in the first * place. - * */ static VALUE rb_io_buffer_null_p(VALUE self) @@ -807,7 +803,6 @@ rb_io_buffer_null_p(VALUE self) * If the buffer has 0 size: it is created by ::new with size 0, or with ::for * from an empty string. (Note that empty files can't be mapped, so the buffer * created with ::map will never be empty.) - * */ static VALUE rb_io_buffer_empty_p(VALUE self) @@ -828,7 +823,6 @@ rb_io_buffer_empty_p(VALUE self) * memory. * * External buffer can't be resized. - * */ static VALUE rb_io_buffer_external_p(VALUE self) @@ -854,7 +848,6 @@ rb_io_buffer_external_p(VALUE self) * * Internal buffers can be resized, and such an operation will typically * invalidate all slices, but not always. - * */ static VALUE rb_io_buffer_internal_p(VALUE self) @@ -877,7 +870,6 @@ rb_io_buffer_internal_p(VALUE self) * * Mapped buffers can usually be resized, and such an operation will typically * invalidate all slices, but not always. - * */ static VALUE rb_io_buffer_mapped_p(VALUE self) @@ -901,7 +893,6 @@ rb_io_buffer_mapped_p(VALUE self) * buffer.locked do * buffer.write(io) # theoretical system call interface * end - * */ static VALUE rb_io_buffer_locked_p(VALUE self) @@ -928,7 +919,6 @@ rb_io_buffer_readonly_p(VALUE self) * #set_value, #set_string or #copy and similar. * * Frozen strings and read-only files create read-only buffers. - * */ static VALUE io_buffer_readonly_p(VALUE self) @@ -1053,7 +1043,6 @@ rb_io_buffer_locked(VALUE self) * # => true * * You can resize a freed buffer to re-allocate it. - * */ VALUE rb_io_buffer_free(VALUE self) @@ -1115,7 +1104,6 @@ io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length * # ...and original string * string * # => tost - * */ VALUE rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length) @@ -1166,7 +1154,7 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size) return 0; } -static void +inline static void io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size) { if (data->flags & RB_IO_BUFFER_READONLY) { @@ -1238,7 +1226,6 @@ rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size) * # # * buffer.null? * # => true - * */ VALUE rb_io_buffer_transfer(VALUE self) @@ -1358,7 +1345,6 @@ rb_io_buffer_resize(VALUE self, size_t size) * * External buffer (created with ::for), and locked buffer * can not be resized. - * */ static VALUE io_buffer_resize(VALUE self, VALUE size) @@ -1373,7 +1359,6 @@ io_buffer_resize(VALUE self, VALUE size) * * Buffers are compared by size and exact contents of the memory they are * referencing using +memcmp+. - * */ static VALUE rb_io_buffer_compare(VALUE self, VALUE other) @@ -1399,7 +1384,7 @@ static void io_buffer_validate_type(size_t size, size_t offset) { if (offset > size) { - rb_raise(rb_eArgError, "Type extends beyond end of buffer!"); + rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%ld > size=%ld)", offset, size); } } @@ -1449,8 +1434,8 @@ ruby_swapf64(double value) return swap.value; } -#define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \ -static ID RB_IO_BUFFER_TYPE_##name; \ +#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \ +static ID RB_IO_BUFFER_DATA_TYPE_##name; \ \ static VALUE \ io_buffer_read_##name(const void* base, size_t size, size_t *offset) \ @@ -1471,67 +1456,123 @@ io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _val if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \ memcpy((char*)base + *offset, &value, sizeof(type)); \ *offset += sizeof(type); \ -} - -DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8) -DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8) - -DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) -DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) -DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) -DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) +} \ +\ +enum { \ + RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \ +}; -DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) -DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) -DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) -DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8) +IO_BUFFER_DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8) + +IO_BUFFER_DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) + +IO_BUFFER_DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) + +IO_BUFFER_DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) +IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) +IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) +IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) + +IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) +IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) +IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) +IO_BUFFER_DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) +#undef IO_BUFFER_DECLARE_TYPE + +static inline size_t +io_buffer_data_type_size(ID data_type) { +#define IO_BUFFER_DATA_TYPE_SIZE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE; + IO_BUFFER_DATA_TYPE_SIZE(U8) + IO_BUFFER_DATA_TYPE_SIZE(S8) + IO_BUFFER_DATA_TYPE_SIZE(u16) + IO_BUFFER_DATA_TYPE_SIZE(U16) + IO_BUFFER_DATA_TYPE_SIZE(s16) + IO_BUFFER_DATA_TYPE_SIZE(S16) + IO_BUFFER_DATA_TYPE_SIZE(u32) + IO_BUFFER_DATA_TYPE_SIZE(U32) + IO_BUFFER_DATA_TYPE_SIZE(s32) + IO_BUFFER_DATA_TYPE_SIZE(S32) + IO_BUFFER_DATA_TYPE_SIZE(u64) + IO_BUFFER_DATA_TYPE_SIZE(U64) + IO_BUFFER_DATA_TYPE_SIZE(s64) + IO_BUFFER_DATA_TYPE_SIZE(S64) + IO_BUFFER_DATA_TYPE_SIZE(f32) + IO_BUFFER_DATA_TYPE_SIZE(F32) + IO_BUFFER_DATA_TYPE_SIZE(f64) + IO_BUFFER_DATA_TYPE_SIZE(F64) +#undef IO_BUFFER_DATA_TYPE_SIZE -DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) -DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) -DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) -DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) + rb_raise(rb_eArgError, "Invalid type name!"); +} -DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) -DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) -DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) -DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) -#undef DECLARE_TYPE +/* + * call-seq: + * size_of(data_type) -> byte size + * size_of(array of data_type) -> byte size + * + * Returns the size of the given data type(s) in bytes. + * + * Example: + * + * IO::Buffer.size_of(:u32) # => 4 + * IO::Buffer.size_of([:u32, :u32]) # => 8 + */ +static VALUE +io_buffer_size_of(VALUE klass, VALUE data_type) +{ + if (RB_TYPE_P(data_type, T_ARRAY)) { + size_t total = 0; + for (long i = 0; i < RARRAY_LEN(data_type); i++) { + total += io_buffer_data_type_size(RB_SYM2ID(RARRAY_AREF(data_type, i))); + } + return SIZET2NUM(total); + } else { + return SIZET2NUM(io_buffer_data_type_size(RB_SYM2ID(data_type))); + } +} -VALUE -rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset) +static inline VALUE +rb_io_buffer_get_value(const void* base, size_t size, ID data_type, size_t *offset) { -#define READ_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) return io_buffer_read_##name(base, size, &offset); - READ_TYPE(U8); - READ_TYPE(S8); +#define IO_BUFFER_GET_VALUE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset); + IO_BUFFER_GET_VALUE(U8) + IO_BUFFER_GET_VALUE(S8) - READ_TYPE(u16); - READ_TYPE(U16); - READ_TYPE(s16); - READ_TYPE(S16); + IO_BUFFER_GET_VALUE(u16) + IO_BUFFER_GET_VALUE(U16) + IO_BUFFER_GET_VALUE(s16) + IO_BUFFER_GET_VALUE(S16) - READ_TYPE(u32); - READ_TYPE(U32); - READ_TYPE(s32); - READ_TYPE(S32); + IO_BUFFER_GET_VALUE(u32) + IO_BUFFER_GET_VALUE(U32) + IO_BUFFER_GET_VALUE(s32) + IO_BUFFER_GET_VALUE(S32) - READ_TYPE(u64); - READ_TYPE(U64); - READ_TYPE(s64); - READ_TYPE(S64); + IO_BUFFER_GET_VALUE(u64) + IO_BUFFER_GET_VALUE(U64) + IO_BUFFER_GET_VALUE(s64) + IO_BUFFER_GET_VALUE(S64) - READ_TYPE(f32); - READ_TYPE(F32); - READ_TYPE(f64); - READ_TYPE(F64); -#undef READ_TYPE + IO_BUFFER_GET_VALUE(f32) + IO_BUFFER_GET_VALUE(F32) + IO_BUFFER_GET_VALUE(f64) + IO_BUFFER_GET_VALUE(F64) +#undef IO_BUFFER_GET_VALUE rb_raise(rb_eArgError, "Invalid type name!"); } /* - * call-seq: get_value(type, offset) -> numeric + * call-seq: get_value(data_type, offset) -> numeric * - * Read from buffer a value of +type+ at +offset+. +type+ should be one + * Read from buffer a value of +type+ at +offset+. +data_type+ should be one * of symbols: * * * +:U8+: unsigned integer, 1 byte @@ -1553,13 +1594,16 @@ rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset) * * +:f64+: double, 8 bytes, little-endian * * +:F64+: double, 8 bytes, big-endian * + * A data type refers specifically to the type of binary data that is stored + * in the buffer. For example, a +:u32+ data type is a 32-bit unsigned + * integer in little-endian format. + * * Example: * * string = [1.5].pack('f') * # => "\x00\x00\xC0?" * IO::Buffer.for(string).get_value(:f32, 0) * # => 1.5 - * */ static VALUE io_buffer_get_value(VALUE self, VALUE type, VALUE _offset) @@ -1570,36 +1614,230 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset) rb_io_buffer_get_bytes_for_reading(self, &base, &size); - return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset); + return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset); } -void -rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VALUE value) +/* + * call-seq: get_values(data_types, offset) -> array + * + * Similar to #get_value, except that it can handle multiple data types and + * returns an array of values. + * + * Example: + * + * string = [1.5, 2.5].pack('ff') + * IO::Buffer.for(string).get_values([:f32, :f32], 0) + * # => [1.5, 2.5] + */ +static VALUE +io_buffer_get_values(VALUE self, VALUE data_types, VALUE _offset) +{ + size_t offset = NUM2SIZET(_offset); + + const void *base; + size_t size; + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + if (!RB_TYPE_P(data_types, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument data_types should be an array!"); + } + + VALUE array = rb_ary_new_capa(RARRAY_LEN(data_types)); + + for (long i = 0; i < RARRAY_LEN(data_types); i++) { + VALUE type = rb_ary_entry(data_types, i); + VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset); + rb_ary_push(array, value); + } + + return array; +} + +/* + * call-seq: + * each(data_type, [offset, [count]]) {|offset, value| ...} -> self + * each(data_type, [offset, [count]]) -> enumerator + * + * Iterates over the buffer, yielding each +value+ of +data_type+ starting + * from +offset+. + * + * If +count+ is given, only +count+ values will be yielded. + * + * Example: + * + * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value| + * puts "#{offset}: #{value}" + * end + * # 2: 108 + * # 3: 108 + */ +static VALUE +io_buffer_each(int argc, VALUE *argv, VALUE self) +{ + RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS); + + const void *base; + size_t size; + + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + ID data_type; + if (argc >= 1) { + data_type = RB_SYM2ID(argv[0]); + } else { + data_type = RB_IO_BUFFER_DATA_TYPE_U8; + } + + size_t offset; + if (argc >= 2) { + offset = NUM2SIZET(argv[1]); + } else { + offset = 0; + } + + size_t count; + if (argc >= 3) { + count = NUM2SIZET(argv[2]); + } else { + count = (size - offset) / io_buffer_data_type_size(data_type); + } + + for (size_t i = 0; i < count; i++) { + size_t current_offset = offset; + VALUE value = rb_io_buffer_get_value(base, size, data_type, &offset); + rb_yield_values(2, SIZET2NUM(current_offset), value); + } + + return self; +} + +/* + * call-seq: values(data_type, [offset, [count]]) -> array + * + * Returns an array of values of +data_type+ starting from +offset+. + * + * If +count+ is given, only +count+ values will be returned. + * + * Example: + * + * IO::Buffer.for("Hello World").values(:U8, 2, 2) + * # => [108, 108] + */ +static VALUE +io_buffer_values(int argc, VALUE *argv, VALUE self) +{ + const void *base; + size_t size; + + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + ID data_type; + if (argc >= 1) { + data_type = RB_SYM2ID(argv[0]); + } else { + data_type = RB_IO_BUFFER_DATA_TYPE_U8; + } + + size_t offset; + if (argc >= 2) { + offset = NUM2SIZET(argv[1]); + } else { + offset = 0; + } + + size_t count; + if (argc >= 3) { + count = NUM2SIZET(argv[2]); + } else { + count = (size - offset) / io_buffer_data_type_size(data_type); + } + + VALUE array = rb_ary_new_capa(count); + + for (size_t i = 0; i < count; i++) { + VALUE value = rb_io_buffer_get_value(base, size, data_type, &offset); + rb_ary_push(array, value); + } + + return array; +} + +/* + * call-seq: + * each_byte([offset, [count]]) {|offset, byte| ...} -> self + * each_byte([offset, [count]]) -> enumerator + * + * Iterates over the buffer, yielding each byte starting from +offset+. + * + * If +count+ is given, only +count+ bytes will be yielded. + * + * Example: + * + * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte| + * puts "#{offset}: #{byte}" + * end + * # 2: 108 + * # 3: 108 + */ +static VALUE +io_buffer_each_byte(int argc, VALUE *argv, VALUE self) { -#define WRITE_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) {io_buffer_write_##name(base, size, &offset, value); return;} - WRITE_TYPE(U8); - WRITE_TYPE(S8); + RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS); + + const void *base; + size_t size; + + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + size_t offset; + if (argc >= 2) { + offset = NUM2SIZET(argv[1]); + } else { + offset = 0; + } - WRITE_TYPE(u16); - WRITE_TYPE(U16); - WRITE_TYPE(s16); - WRITE_TYPE(S16); + size_t count; + if (argc >= 3) { + count = NUM2SIZET(argv[2]); + } else { + count = (size - offset); + } - WRITE_TYPE(u32); - WRITE_TYPE(U32); - WRITE_TYPE(s32); - WRITE_TYPE(S32); + for (size_t i = 0; i < count; i++) { + unsigned char *value = (unsigned char *)base + i + offset; + rb_yield(RB_INT2FIX(*value)); + } - WRITE_TYPE(u64); - WRITE_TYPE(U64); - WRITE_TYPE(s64); - WRITE_TYPE(S64); + return self; +} - WRITE_TYPE(f32); - WRITE_TYPE(F32); - WRITE_TYPE(f64); - WRITE_TYPE(F64); -#undef WRITE_TYPE +inline static void +rb_io_buffer_set_value(const void* base, size_t size, ID data_type, size_t *offset, VALUE value) +{ +#define IO_BUFFER_SET_VALUE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;} + IO_BUFFER_SET_VALUE(U8); + IO_BUFFER_SET_VALUE(S8); + + IO_BUFFER_SET_VALUE(u16); + IO_BUFFER_SET_VALUE(U16); + IO_BUFFER_SET_VALUE(s16); + IO_BUFFER_SET_VALUE(S16); + + IO_BUFFER_SET_VALUE(u32); + IO_BUFFER_SET_VALUE(U32); + IO_BUFFER_SET_VALUE(s32); + IO_BUFFER_SET_VALUE(S32); + + IO_BUFFER_SET_VALUE(u64); + IO_BUFFER_SET_VALUE(U64); + IO_BUFFER_SET_VALUE(s64); + IO_BUFFER_SET_VALUE(S64); + + IO_BUFFER_SET_VALUE(f32); + IO_BUFFER_SET_VALUE(F32); + IO_BUFFER_SET_VALUE(f64); + IO_BUFFER_SET_VALUE(F64); +#undef IO_BUFFER_SET_VALUE rb_raise(rb_eArgError, "Invalid type name!"); } @@ -1640,7 +1878,53 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value) rb_io_buffer_get_bytes_for_writing(self, &base, &size); - rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value); + rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value); + + return SIZET2NUM(offset); +} + +/* + * call-seq: set_values(data_types, offset, values) -> offset + * + * Write +values+ of +data_types+ at +offset+ to the buffer. +data_types+ + * should be an array of symbols as described in #get_value. +values+ should + * be an array of values to write. + * + * Example: + * + * buffer = IO::Buffer.new(8) + * buffer.set_values([:U8, :U16], 0, [1, 2]) + * buffer + * # => + * # # + * # 0x00000000 01 00 02 00 00 00 00 00 ........ + */ +static VALUE +io_buffer_set_values(VALUE self, VALUE data_types, VALUE _offset, VALUE values) +{ + if (!RB_TYPE_P(data_types, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument data_types should be an array!"); + } + + if (!RB_TYPE_P(values, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument values should be an array!"); + } + + if (RARRAY_LEN(data_types) != RARRAY_LEN(values)) { + rb_raise(rb_eArgError, "Argument data_types and values should have the same length!"); + } + + size_t offset = NUM2SIZET(_offset); + + void *base; + size_t size; + rb_io_buffer_get_bytes_for_writing(self, &base, &size); + + for (long i = 0; i < RARRAY_LEN(data_types); i++) { + VALUE type = rb_ary_entry(data_types, i); + VALUE value = rb_ary_entry(values, i); + rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value); + } return SIZET2NUM(offset); } @@ -1719,7 +2003,6 @@ io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t s * # => * # # * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World - * */ static VALUE rb_io_buffer_initialize_copy(VALUE self, VALUE source) @@ -1790,7 +2073,6 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source) * buffer = IO::Buffer.new(2) * buffer.copy(IO::Buffer.for('test'), 0) * # in `copy': Specified offset+length exceeds source size! (ArgumentError) - * */ static VALUE io_buffer_copy(int argc, VALUE *argv, VALUE self) @@ -1822,7 +2104,6 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self) * # => "st" * buffer.get_string(2, 1) * # => "s" - * */ static VALUE io_buffer_get_string(int argc, VALUE *argv, VALUE self) @@ -1944,7 +2225,6 @@ rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length) * # => * # * # 0x00000000 01 02 02 02 .... - * */ static VALUE io_buffer_clear(int argc, VALUE *argv, VALUE self) @@ -2002,6 +2282,20 @@ io_buffer_default_size(size_t page_size) return platform_agnostic_default_size; } +struct io_buffer_read_internal_argument { + int descriptor; + void *base; + size_t size; +}; + +static VALUE +io_buffer_read_internal(void *_argument) +{ + struct io_buffer_read_internal_argument *argument = _argument; + ssize_t result = read(argument->descriptor, argument->base, argument->size); + return rb_fiber_scheduler_io_result(result, errno); +} + VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length) { @@ -2025,9 +2319,13 @@ rb_io_buffer_read(VALUE self, VALUE io, size_t length) size_t size; io_buffer_get_bytes_for_writing(data, &base, &size); - ssize_t result = read(descriptor, base, size); + struct io_buffer_read_internal_argument argument = { + .descriptor = descriptor, + .base = base, + .size = length, + }; - return rb_fiber_scheduler_io_result(result, errno); + return rb_thread_io_blocking_region(io_buffer_read_internal, &argument, descriptor); } static VALUE @@ -2036,8 +2334,40 @@ io_buffer_read(VALUE self, VALUE io, VALUE length) return rb_io_buffer_read(self, io, RB_NUM2SIZE(length)); } +struct io_buffer_pread_internal_argument { + int descriptor; + void *base; + size_t size; + off_t offset; +}; + +static VALUE +io_buffer_pread_internal(void *_argument) +{ + struct io_buffer_pread_internal_argument *argument = _argument; + +#if defined(HAVE_PREAD) + ssize_t result = pread(argument->descriptor, argument->base, argument->size, argument->offset); +#else + // This emulation is not thread safe. + rb_off_t offset = lseek(argument->descriptor, 0, SEEK_CUR); + if (offset == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); + + if (lseek(argument->descriptor, argument->offset, SEEK_SET) == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); + + ssize_t result = read(argument->descriptor, argument->base, argument->size); + + if (lseek(argument->descriptor, offset, SEEK_SET) == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); +#endif + + return rb_fiber_scheduler_io_result(result, errno); +} + VALUE -rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset) +rb_io_buffer_pread(VALUE self, VALUE io, size_t length, rb_off_t offset) { VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { @@ -2059,24 +2389,14 @@ rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset) size_t size; io_buffer_get_bytes_for_writing(data, &base, &size); -#if defined(HAVE_PREAD) - ssize_t result = pread(descriptor, base, size, offset); -#else - // This emulation is not thread safe, but the GVL means it's unlikely to be a problem. - off_t current_offset = lseek(descriptor, 0, SEEK_CUR); - if (current_offset == (off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); + struct io_buffer_pread_internal_argument argument = { + .descriptor = descriptor, + .base = base, + .size = length, + .offset = offset, + }; - if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); - - ssize_t result = read(descriptor, base, size); - - if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); -#endif - - return rb_fiber_scheduler_io_result(result, errno); + return rb_thread_io_blocking_region(io_buffer_pread_internal, &argument, descriptor); } static VALUE @@ -2085,6 +2405,20 @@ io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset) return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset)); } +struct io_buffer_write_internal_argument { + int descriptor; + const void *base; + size_t size; +}; + +static VALUE +io_buffer_write_internal(void *_argument) +{ + struct io_buffer_write_internal_argument *argument = _argument; + ssize_t result = write(argument->descriptor, argument->base, argument->size); + return rb_fiber_scheduler_io_result(result, errno); +} + VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length) { @@ -2108,9 +2442,13 @@ rb_io_buffer_write(VALUE self, VALUE io, size_t length) size_t size; io_buffer_get_bytes_for_reading(data, &base, &size); - ssize_t result = write(descriptor, base, length); + struct io_buffer_write_internal_argument argument = { + .descriptor = descriptor, + .base = base, + .size = length, + }; - return rb_fiber_scheduler_io_result(result, errno); + return rb_thread_io_blocking_region(io_buffer_write_internal, &argument, descriptor); } static VALUE @@ -2119,8 +2457,40 @@ io_buffer_write(VALUE self, VALUE io, VALUE length) return rb_io_buffer_write(self, io, RB_NUM2SIZE(length)); } +struct io_buffer_pwrite_internal_argument { + int descriptor; + const void *base; + size_t size; + off_t offset; +}; + +static VALUE +io_buffer_pwrite_internal(void *_argument) +{ + struct io_buffer_pwrite_internal_argument *argument = _argument; + +#if defined(HAVE_PWRITE) + ssize_t result = pwrite(argument->descriptor, argument->base, argument->size, argument->offset); +#else + // This emulation is not thread safe. + rb_off_t offset = lseek(argument->descriptor, 0, SEEK_CUR); + if (offset == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); + + if (lseek(argument->descriptor, argument->offset, SEEK_SET) == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); + + ssize_t result = write(argument->descriptor, argument->base, argument->size); + + if (lseek(argument->descriptor, offset, SEEK_SET) == (rb_off_t)-1) + return rb_fiber_scheduler_io_result(-1, errno); +#endif + + return rb_fiber_scheduler_io_result(result, errno); +} + VALUE -rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset) +rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, rb_off_t offset) { VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { @@ -2142,24 +2512,14 @@ rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset) size_t size; io_buffer_get_bytes_for_reading(data, &base, &size); -#if defined(HAVE_PWRITE) - ssize_t result = pwrite(descriptor, base, length, offset); -#else - // This emulation is not thread safe, but the GVL means it's unlikely to be a problem. - off_t current_offset = lseek(descriptor, 0, SEEK_CUR); - if (current_offset == (off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); + struct io_buffer_pwrite_internal_argument argument = { + .descriptor = descriptor, + .base = base, + .size = length, + .offset = offset, + }; - if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); - - ssize_t result = write(descriptor, base, length); - - if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1) - return rb_fiber_scheduler_io_result(-1, errno); -#endif - - return rb_fiber_scheduler_io_result(result, errno); + return rb_thread_io_blocking_region(io_buffer_pwrite_internal, &argument, descriptor); } static VALUE @@ -2685,17 +3045,41 @@ Init_IO_Buffer(void) rb_include_module(rb_cIOBuffer, rb_mComparable); -#define DEFINE_TYPE(name) RB_IO_BUFFER_TYPE_##name = rb_intern_const(#name) - DEFINE_TYPE(U8); DEFINE_TYPE(S8); - DEFINE_TYPE(u16); DEFINE_TYPE(U16); DEFINE_TYPE(s16); DEFINE_TYPE(S16); - DEFINE_TYPE(u32); DEFINE_TYPE(U32); DEFINE_TYPE(s32); DEFINE_TYPE(S32); - DEFINE_TYPE(u64); DEFINE_TYPE(U64); DEFINE_TYPE(s64); DEFINE_TYPE(S64); - DEFINE_TYPE(f32); DEFINE_TYPE(F32); DEFINE_TYPE(f64); DEFINE_TYPE(F64); -#undef DEFINE_TYPE +#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name) + IO_BUFFER_DEFINE_DATA_TYPE(U8); + IO_BUFFER_DEFINE_DATA_TYPE(S8); + + IO_BUFFER_DEFINE_DATA_TYPE(u16); + IO_BUFFER_DEFINE_DATA_TYPE(U16); + IO_BUFFER_DEFINE_DATA_TYPE(s16); + IO_BUFFER_DEFINE_DATA_TYPE(S16); + + IO_BUFFER_DEFINE_DATA_TYPE(u32); + IO_BUFFER_DEFINE_DATA_TYPE(U32); + IO_BUFFER_DEFINE_DATA_TYPE(s32); + IO_BUFFER_DEFINE_DATA_TYPE(S32); + + IO_BUFFER_DEFINE_DATA_TYPE(u64); + IO_BUFFER_DEFINE_DATA_TYPE(U64); + IO_BUFFER_DEFINE_DATA_TYPE(s64); + IO_BUFFER_DEFINE_DATA_TYPE(S64); + + IO_BUFFER_DEFINE_DATA_TYPE(f32); + IO_BUFFER_DEFINE_DATA_TYPE(F32); + IO_BUFFER_DEFINE_DATA_TYPE(f64); + IO_BUFFER_DEFINE_DATA_TYPE(F64); +#undef IO_BUFFER_DEFINE_DATA_TYPE + + rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1); // Data access: rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2); + rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2); + rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1); + rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1); + rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1); rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3); + rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3); rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1); diff --git a/iseq.c b/iseq.c index 4892d93df1608c..a4792d81fdb5b6 100644 --- a/iseq.c +++ b/iseq.c @@ -591,7 +591,7 @@ rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath) } static rb_iseq_location_t * -iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id) +iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id) { rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location; @@ -656,7 +656,7 @@ new_arena(void) static VALUE prepare_iseq_build(rb_iseq_t *iseq, - VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id, + VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type, VALUE script_lines, const rb_compile_option_t *option) { @@ -697,7 +697,6 @@ prepare_iseq_build(rb_iseq_t *iseq, ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL; ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table; - if (option->coverage_enabled) { VALUE coverages = rb_get_coverages(); if (RTEST(coverages)) { @@ -883,7 +882,7 @@ rb_iseq_t * rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type type) { - return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, + return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, 0, type, &COMPILE_OPTION_DEFAULT); } @@ -900,20 +899,32 @@ ast_line_count(const rb_ast_body_t *ast) return FIX2INT(ast->script_lines); } +static VALUE +iseq_setup_coverage(VALUE coverages, VALUE path, const rb_ast_body_t *ast, int line_offset) +{ + int line_count = line_offset + ast_line_count(ast); + + if (line_count >= 0) { + int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count; + + VALUE coverage = rb_default_coverage(len); + rb_hash_aset(coverages, path, coverage); + + return coverage; + } + + return Qnil; +} + rb_iseq_t * rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent) { VALUE coverages = rb_get_coverages(); if (RTEST(coverages)) { - int line_count = ast_line_count(ast); - if (line_count >= 0) { - int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count; - VALUE coverage = rb_default_coverage(len); - rb_hash_aset(coverages, path, coverage); - } + iseq_setup_coverage(coverages, path, ast, 0); } - return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, 0, + return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, 0, ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT); } @@ -921,13 +932,18 @@ rb_iseq_t * rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt) { return rb_iseq_new_with_opt(ast, rb_fstring_lit("
"), - path, realpath, INT2FIX(0), + path, realpath, 0, parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE); } rb_iseq_t * -rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth) +rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth) { + VALUE coverages = rb_get_coverages(); + if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) { + iseq_setup_coverage(coverages, path, ast, first_lineno - 1); + } + return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno, parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT); } @@ -948,7 +964,7 @@ iseq_translate(rb_iseq_t *iseq) rb_iseq_t * rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, - VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth, + int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type, const rb_compile_option_t *option) { const NODE *node = ast ? ast->root : 0; @@ -986,7 +1002,7 @@ rb_iseq_t * rb_iseq_new_with_callback( const struct rb_iseq_new_with_callback_callback_func * ifunc, VALUE name, VALUE path, VALUE realpath, - VALUE first_lineno, const rb_iseq_t *parent, + int first_lineno, const rb_iseq_t *parent, enum rb_iseq_type type, const rb_compile_option_t *option) { /* TODO: argument check */ @@ -1052,7 +1068,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt) rb_iseq_t *iseq = iseq_alloc(); VALUE magic, version1, version2, format_type, misc; - VALUE name, path, realpath, first_lineno, code_location, node_id; + VALUE name, path, realpath, code_location, node_id; VALUE type, body, locals, params, exception; st_data_t iseq_type; @@ -1078,7 +1094,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt) path = CHECK_STRING(rb_ary_entry(data, i++)); realpath = rb_ary_entry(data, i++); realpath = NIL_P(realpath) ? Qnil : CHECK_STRING(realpath); - first_lineno = CHECK_INTEGER(rb_ary_entry(data, i++)); + int first_lineno = RB_NUM2INT(rb_ary_entry(data, i++)); type = CHECK_SYMBOL(rb_ary_entry(data, i++)); locals = CHECK_ARRAY(rb_ary_entry(data, i++)); @@ -1172,7 +1188,7 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V rb_exc_raise(GET_EC()->errinfo); } else { - iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, line, + iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option); rb_ast_dispose(ast); } @@ -1219,7 +1235,7 @@ rb_iseq_base_label(const rb_iseq_t *iseq) VALUE rb_iseq_first_lineno(const rb_iseq_t *iseq) { - return ISEQ_BODY(iseq)->location.first_lineno; + return RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno); } VALUE @@ -1411,7 +1427,7 @@ iseqw_s_compile(int argc, VALUE *argv, VALUE self) static VALUE iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) { - VALUE file, line = INT2FIX(1), opt = Qnil; + VALUE file, opt = Qnil; VALUE parser, f, exc = Qnil, ret; rb_ast_t *ast; rb_compile_option_t option; @@ -1443,7 +1459,7 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("
"), file, rb_realpath_internal(Qnil, file, 1), - line, NULL, 0, ISEQ_TYPE_TOP, &option)); + 1, NULL, 0, ISEQ_TYPE_TOP, &option)); rb_ast_dispose(ast); return ret; } @@ -3148,7 +3164,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq) rb_ary_push(val, iseq_body->location.label); rb_ary_push(val, rb_iseq_path(iseq)); rb_ary_push(val, rb_iseq_realpath(iseq)); - rb_ary_push(val, iseq_body->location.first_lineno); + rb_ary_push(val, RB_INT2NUM(iseq_body->location.first_lineno)); rb_ary_push(val, ID2SYM(type)); rb_ary_push(val, locals); rb_ary_push(val, params); diff --git a/lex.c.blt b/lex.c.blt index 92a4793b00970b..85727ed00f520d 100644 --- a/lex.c.blt +++ b/lex.c.blt @@ -34,7 +34,7 @@ struct kwtable {short name, id[2], state;}; const struct kwtable *rb_reserved_word(const char *, unsigned int); #ifndef RIPPER -static const struct kwtable *reserved_word(/*const char *, unsigned int*/); +static const struct kwtable *reserved_word(register const char *str, register size_t len); #define rb_reserved_word(str, len) reserved_word(str, len) #line 9 "defs/keywords" struct kwtable; diff --git a/lib/bundler/bundler.gemspec b/lib/bundler/bundler.gemspec index 38c533b0c1bc2b..a9c9fac462675c 100644 --- a/lib/bundler/bundler.gemspec +++ b/lib/bundler/bundler.gemspec @@ -22,14 +22,12 @@ Gem::Specification.new do |s| s.summary = "The best way to manage your application's dependencies" s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably" - if s.respond_to?(:metadata=) - s.metadata = { - "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler", - "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md", - "homepage_uri" => "https://bundler.io/", - "source_code_uri" => "https://github.com/rubygems/rubygems/", - } - end + s.metadata = { + "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler", + "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md", + "homepage_uri" => "https://bundler.io/", + "source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler", + } s.required_ruby_version = ">= 2.3.0" s.required_rubygems_version = ">= 2.5.2" diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 4f969b17bfecb4..3ba4d0f8c42ef1 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -517,7 +517,7 @@ def console(group = nil) end end - desc "version", "Prints the bundler's version information" + desc "version", "Prints Bundler version information" def version cli_help = current_command.name == "cli_help" if cli_help || ARGV.include?("version") diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb index 97b8dc06636b1d..1eb8ea82545126 100644 --- a/lib/bundler/cli/console.rb +++ b/lib/bundler/cli/console.rb @@ -30,9 +30,9 @@ def get_console(name) def get_constant(name) const_name = { - "pry" => :Pry, + "pry" => :Pry, "ripl" => :Ripl, - "irb" => :IRB, + "irb" => :IRB, }[name] Object.const_get(const_name) rescue NameError diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb index c4c76d1b69de8d..8c8ebe8ff30ba4 100644 --- a/lib/bundler/cli/gem.rb +++ b/lib/bundler/cli/gem.rb @@ -55,20 +55,20 @@ def run end config = { - :name => name, + :name => name, :underscored_name => underscored_name, - :namespaced_path => namespaced_path, - :makefile_path => "#{underscored_name}/#{underscored_name}", - :constant_name => constant_name, - :constant_array => constant_array, - :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name, - :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email, - :test => options[:test], - :ext => options[:ext], - :exe => options[:exe], - :bundler_version => bundler_dependency_version, - :git => use_git, - :github_username => github_username.empty? ? "[USERNAME]" : github_username, + :namespaced_path => namespaced_path, + :makefile_path => "#{underscored_name}/#{underscored_name}", + :constant_name => constant_name, + :constant_array => constant_array, + :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name, + :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email, + :test => options[:test], + :ext => options[:ext], + :exe => options[:exe], + :bundler_version => bundler_dependency_version, + :git => use_git, + :github_username => github_username.empty? ? "[USERNAME]" : github_username, :required_ruby_version => required_ruby_version, :minitest_constant_name => minitest_constant_name, } diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 79369ec374cfdf..a46d7387de76c8 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -106,6 +106,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti @locked_gems = nil @locked_deps = {} @locked_specs = SpecSet.new([]) + @originally_locked_specs = @locked_specs @locked_sources = [] @locked_platforms = [] end @@ -149,18 +150,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti end def gem_version_promoter - @gem_version_promoter ||= begin - locked_specs = - if unlocking? && @locked_specs.empty? && !@lockfile_contents.empty? - # Definition uses an empty set of locked_specs to indicate all gems - # are unlocked, but GemVersionPromoter needs the locked_specs - # for conservative comparison. - Bundler::SpecSet.new(@locked_gems.specs) - else - @locked_specs - end - GemVersionPromoter.new(locked_specs, @unlock[:gems]) - end + @gem_version_promoter ||= GemVersionPromoter.new(@originally_locked_specs, @unlock[:gems]) end def resolve_only_locally! diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index 7607b4695c03c7..605000ba5321ee 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -9,91 +9,22 @@ class Dependency < Gem::Dependency attr_reader :autorequire attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref, :force_ruby_platform - # rubocop:disable Naming/VariableNumber + ALL_RUBY_VERSIONS = ((18..27).to_a + (30..31).to_a).freeze PLATFORM_MAP = { - :ruby => Gem::Platform::RUBY, - :ruby_18 => Gem::Platform::RUBY, - :ruby_19 => Gem::Platform::RUBY, - :ruby_20 => Gem::Platform::RUBY, - :ruby_21 => Gem::Platform::RUBY, - :ruby_22 => Gem::Platform::RUBY, - :ruby_23 => Gem::Platform::RUBY, - :ruby_24 => Gem::Platform::RUBY, - :ruby_25 => Gem::Platform::RUBY, - :ruby_26 => Gem::Platform::RUBY, - :ruby_27 => Gem::Platform::RUBY, - :ruby_30 => Gem::Platform::RUBY, - :ruby_31 => Gem::Platform::RUBY, - :mri => Gem::Platform::RUBY, - :mri_18 => Gem::Platform::RUBY, - :mri_19 => Gem::Platform::RUBY, - :mri_20 => Gem::Platform::RUBY, - :mri_21 => Gem::Platform::RUBY, - :mri_22 => Gem::Platform::RUBY, - :mri_23 => Gem::Platform::RUBY, - :mri_24 => Gem::Platform::RUBY, - :mri_25 => Gem::Platform::RUBY, - :mri_26 => Gem::Platform::RUBY, - :mri_27 => Gem::Platform::RUBY, - :mri_30 => Gem::Platform::RUBY, - :mri_31 => Gem::Platform::RUBY, - :rbx => Gem::Platform::RUBY, - :truffleruby => Gem::Platform::RUBY, - :jruby => Gem::Platform::JAVA, - :jruby_18 => Gem::Platform::JAVA, - :jruby_19 => Gem::Platform::JAVA, - :windows => Gem::Platform::WINDOWS, - :mswin => Gem::Platform::MSWIN, - :mswin_18 => Gem::Platform::MSWIN, - :mswin_19 => Gem::Platform::MSWIN, - :mswin_20 => Gem::Platform::MSWIN, - :mswin_21 => Gem::Platform::MSWIN, - :mswin_22 => Gem::Platform::MSWIN, - :mswin_23 => Gem::Platform::MSWIN, - :mswin_24 => Gem::Platform::MSWIN, - :mswin_25 => Gem::Platform::MSWIN, - :mswin_26 => Gem::Platform::MSWIN, - :mswin_27 => Gem::Platform::MSWIN, - :mswin_30 => Gem::Platform::MSWIN, - :mswin_31 => Gem::Platform::MSWIN, - :mswin64 => Gem::Platform::MSWIN64, - :mswin64_19 => Gem::Platform::MSWIN64, - :mswin64_20 => Gem::Platform::MSWIN64, - :mswin64_21 => Gem::Platform::MSWIN64, - :mswin64_22 => Gem::Platform::MSWIN64, - :mswin64_23 => Gem::Platform::MSWIN64, - :mswin64_24 => Gem::Platform::MSWIN64, - :mswin64_25 => Gem::Platform::MSWIN64, - :mswin64_26 => Gem::Platform::MSWIN64, - :mswin64_27 => Gem::Platform::MSWIN64, - :mswin64_30 => Gem::Platform::MSWIN64, - :mswin64_31 => Gem::Platform::MSWIN64, - :mingw => Gem::Platform::MINGW, - :mingw_18 => Gem::Platform::MINGW, - :mingw_19 => Gem::Platform::MINGW, - :mingw_20 => Gem::Platform::MINGW, - :mingw_21 => Gem::Platform::MINGW, - :mingw_22 => Gem::Platform::MINGW, - :mingw_23 => Gem::Platform::MINGW, - :mingw_24 => Gem::Platform::MINGW, - :mingw_25 => Gem::Platform::MINGW, - :mingw_26 => Gem::Platform::MINGW, - :mingw_27 => Gem::Platform::MINGW, - :mingw_30 => Gem::Platform::MINGW, - :mingw_31 => Gem::Platform::MINGW, - :x64_mingw => Gem::Platform::X64_MINGW, - :x64_mingw_20 => Gem::Platform::X64_MINGW, - :x64_mingw_21 => Gem::Platform::X64_MINGW, - :x64_mingw_22 => Gem::Platform::X64_MINGW, - :x64_mingw_23 => Gem::Platform::X64_MINGW, - :x64_mingw_24 => Gem::Platform::X64_MINGW, - :x64_mingw_25 => Gem::Platform::X64_MINGW, - :x64_mingw_26 => Gem::Platform::X64_MINGW, - :x64_mingw_27 => Gem::Platform::X64_MINGW, - :x64_mingw_30 => Gem::Platform::X64_MINGW, - :x64_mingw_31 => Gem::Platform::X64_MINGW, - }.freeze - # rubocop:enable Naming/VariableNumber + :ruby => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS], + :mri => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS], + :rbx => [Gem::Platform::RUBY], + :truffleruby => [Gem::Platform::RUBY], + :jruby => [Gem::Platform::JAVA, [18, 19]], + :windows => [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS], + :mswin => [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS], + :mswin64 => [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]], + :mingw => [Gem::Platform::MINGW, ALL_RUBY_VERSIONS], + :x64_mingw => [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]], + }.each_with_object({}) do |(platform, spec), hash| + hash[platform] = spec[0] + spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] } + end.freeze def initialize(name, version, options = {}, &blk) type = options["type"] || :runtime diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 385fdd43838b36..547db16190bc4f 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -67,7 +67,6 @@ def gemspec(opts = nil) gemspecs = Gem::Util.glob_files_in_dir("{,*}.gemspec", expanded_path).map {|g| Bundler.load_gemspec(g) }.compact gemspecs.reject! {|s| s.name != name } if name - Index.sort_specs(gemspecs) specs_by_name_and_version = gemspecs.group_by {|s| [s.name, s.version] } case specs_by_name_and_version.size diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index f0e7ba2595a3f0..863544b1f926db 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -12,7 +12,7 @@ def initialize(name, version, platform, spec_fetcher, dependencies, metadata = n super() @name = name @version = Gem::Version.create version - @platform = platform.nil? ? nil : Gem::Platform.new(platform) + @platform = Gem::Platform.new(platform) @spec_fetcher = spec_fetcher @dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) } diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb index 1ae41e2928f306..ee2c38a6ecdefa 100644 --- a/lib/bundler/gem_version_promoter.rb +++ b/lib/bundler/gem_version_promoter.rb @@ -116,15 +116,14 @@ def filter_dep_specs(spec_groups, locked_spec) end def sort_dep_specs(spec_groups, locked_spec) - return spec_groups unless locked_spec - @gem_name = locked_spec.name - @locked_version = locked_spec.version + @locked_version = locked_spec&.version + @gem_name = locked_spec&.name result = spec_groups.sort do |a, b| @a_ver = a.version @b_ver = b.version - unless @prerelease_specified[@gem_name] + unless @gem_name && @prerelease_specified[@gem_name] a_pre = @a_ver.prerelease? b_pre = @b_ver.prerelease? @@ -148,7 +147,7 @@ def sort_dep_specs(spec_groups, locked_spec) end def either_version_older_than_locked - @a_ver < @locked_version || @b_ver < @locked_version + @locked_version && (@a_ver < @locked_version || @b_ver < @locked_version) end def segments_do_not_match(level) @@ -157,7 +156,7 @@ def segments_do_not_match(level) end def unlocking_gem? - unlock_gems.empty? || unlock_gems.include?(@gem_name) + unlock_gems.empty? || (@gem_name && unlock_gems.include?(@gem_name)) end # Specific version moves can't always reliably be done during sorting @@ -165,7 +164,7 @@ def unlocking_gem? def post_sort(result) # default :major behavior in Bundler does not do this return result if major? - if unlocking_gem? + if unlocking_gem? || @locked_version.nil? result else move_version_to_end(result, @locked_version) diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb index 8f52e2f0f03950..3c008e63e367d7 100644 --- a/lib/bundler/graph.rb +++ b/lib/bundler/graph.rb @@ -114,10 +114,10 @@ def run @groups.each do |group| g.add_nodes( group, { - :style => "filled", + :style => "filled", :fillcolor => "#B9B9D5", - :shape => "box3d", - :fontsize => 16, + :shape => "box3d", + :fontsize => 16, }.merge(@node_options[group]) ) end diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index 00c7a9e00d7c17..d3743adb688553 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -57,36 +57,13 @@ def search_all(name) # Search this index's specs, and any source indexes that this index knows # about, returning all of the results. def search(query) - sort_specs(unsorted_search(query)) - end - - def unsorted_search(query) results = local_search(query) - - seen = results.map(&:full_name).uniq unless @sources.empty? + return results unless @sources.any? @sources.each do |source| - source.unsorted_search(query).each do |spec| - next if seen.include?(spec.full_name) - - seen << spec.full_name - results << spec - end + results.concat(source.search(query)) end - - results - end - protected :unsorted_search - - def self.sort_specs(specs) - specs.sort_by do |s| - platform_string = s.platform.to_s - [s.version, platform_string == RUBY ? NULL : platform_string] - end - end - - def sort_specs(specs) - self.class.sort_specs(specs) + results.uniq(&:full_name) end def local_search(query) diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 64fff4713db03b..871f53663cef9b 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -100,9 +100,9 @@ def may_include_redundant_platform_specific_gems? private TYPES = { - GIT => Bundler::Source::Git, - GEM => Bundler::Source::Rubygems, - PATH => Bundler::Source::Path, + GIT => Bundler::Source::Git, + GEM => Bundler::Source::Rubygems, + PATH => Bundler::Source::Path, PLUGIN => Bundler::Plugin, }.freeze diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1 index e5f1e228b574e8..fd49dd084f5711 100644 --- a/lib/bundler/man/bundle-add.1 +++ b/lib/bundler/man/bundle-add.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-ADD" "1" "August 2022" "" "" +.TH "BUNDLE\-ADD" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1 index d1204104b1a6df..59bb6a44470336 100644 --- a/lib/bundler/man/bundle-binstubs.1 +++ b/lib/bundler/man/bundle-binstubs.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-BINSTUBS" "1" "August 2022" "" "" +.TH "BUNDLE\-BINSTUBS" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-binstubs\fR \- Install the binstubs of the listed gems diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1 index 1393caec65c241..4346aa00f82cbd 100644 --- a/lib/bundler/man/bundle-cache.1 +++ b/lib/bundler/man/bundle-cache.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CACHE" "1" "August 2022" "" "" +.TH "BUNDLE\-CACHE" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1 index 71ddb5cee3ab3e..bb91ed90d5167b 100644 --- a/lib/bundler/man/bundle-check.1 +++ b/lib/bundler/man/bundle-check.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CHECK" "1" "August 2022" "" "" +.TH "BUNDLE\-CHECK" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1 index e5dc4ccf92c76d..eaa8ea35fb4465 100644 --- a/lib/bundler/man/bundle-clean.1 +++ b/lib/bundler/man/bundle-clean.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CLEAN" "1" "August 2022" "" "" +.TH "BUNDLE\-CLEAN" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1 index ffc91ed4922dd0..1582e3c464d93d 100644 --- a/lib/bundler/man/bundle-config.1 +++ b/lib/bundler/man/bundle-config.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CONFIG" "1" "August 2022" "" "" +.TH "BUNDLE\-CONFIG" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-config\fR \- Set bundler configuration options diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1 new file mode 100644 index 00000000000000..6359f442313c7e --- /dev/null +++ b/lib/bundler/man/bundle-console.1 @@ -0,0 +1,53 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "BUNDLE\-CONSOLE" "1" "September 2022" "" "" +. +.SH "NAME" +\fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded +. +.SH "SYNOPSIS" +\fBbundle console\fR [GROUP] +. +.SH "DESCRIPTION" +Starts an interactive Ruby console session in the context of the current bundle\. +. +.P +If no \fBGROUP\fR is specified, all gems in the \fBdefault\fR group in the Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR are preliminarily loaded\. +. +.P +If \fBGROUP\fR is specified, all gems in the given group in the Gemfile in addition to the gems in \fBdefault\fR group are loaded\. Even if the given group does not exist in the Gemfile, IRB console starts without any warning or error\. +. +.P +The environment variable \fBBUNDLE_CONSOLE\fR or \fBbundle config set console\fR can be used to change the shell from the following: +. +.IP "\(bu" 4 +\fBirb\fR (default) +. +.IP "\(bu" 4 +\fBpry\fR (https://github\.com/pry/pry) +. +.IP "\(bu" 4 +\fBripl\fR (https://github\.com/cldwalker/ripl) +. +.IP "" 0 +. +.P +\fBbundle console\fR uses irb by default\. An alternative Pry or Ripl can be used with \fBbundle console\fR by adjusting the \fBconsole\fR Bundler setting\. Also make sure that \fBpry\fR or \fBripl\fR is in your Gemfile\. +. +.SH "EXAMPLE" +. +.nf + +$ bundle config set console pry +$ bundle console +Resolving dependencies\.\.\. +[1] pry(main)> +. +.fi +. +.SH "NOTES" +This command was deprecated in Bundler 2\.1 and will be removed in 3\.0\. Use \fBbin/console\fR script, which can be generated by \fBbundle gem \fR\. +. +.SH "SEE ALSO" +Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR diff --git a/lib/bundler/man/bundle-console.1.ronn b/lib/bundler/man/bundle-console.1.ronn new file mode 100644 index 00000000000000..f9096d386aefc2 --- /dev/null +++ b/lib/bundler/man/bundle-console.1.ronn @@ -0,0 +1,44 @@ +bundle-console(1) -- Deprecated way to open an IRB session with the bundle pre-loaded +===================================================================================== + +## SYNOPSIS + +`bundle console` [GROUP] + +## DESCRIPTION + +Starts an interactive Ruby console session in the context of the current bundle. + +If no `GROUP` is specified, all gems in the `default` group in the [Gemfile(5)](https://bundler.io/man/gemfile.5.html) are +preliminarily loaded. + +If `GROUP` is specified, all gems in the given group in the Gemfile in addition +to the gems in `default` group are loaded. Even if the given group does not +exist in the Gemfile, IRB console starts without any warning or error. + +The environment variable `BUNDLE_CONSOLE` or `bundle config set console` can be used to change +the shell from the following: + +* `irb` (default) +* `pry` (https://github.com/pry/pry) +* `ripl` (https://github.com/cldwalker/ripl) + +`bundle console` uses irb by default. An alternative Pry or Ripl can be used with +`bundle console` by adjusting the `console` Bundler setting. Also make sure that +`pry` or `ripl` is in your Gemfile. + +## EXAMPLE + + $ bundle config set console pry + $ bundle console + Resolving dependencies... + [1] pry(main)> + +## NOTES + +This command was deprecated in Bundler 2.1 and will be removed in 3.0. +Use `bin/console` script, which can be generated by `bundle gem `. + +## SEE ALSO + +[Gemfile(5)](https://bundler.io/man/gemfile.5.html) diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1 index 3f18aaf569d406..92f5c80df932c7 100644 --- a/lib/bundler/man/bundle-doctor.1 +++ b/lib/bundler/man/bundle-doctor.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-DOCTOR" "1" "August 2022" "" "" +.TH "BUNDLE\-DOCTOR" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-doctor\fR \- Checks the bundle for common problems diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1 index 7f6a892f6c25a0..158a9e0bf644cc 100644 --- a/lib/bundler/man/bundle-exec.1 +++ b/lib/bundler/man/bundle-exec.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-EXEC" "1" "August 2022" "" "" +.TH "BUNDLE\-EXEC" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-exec\fR \- Execute a command in the context of the bundle diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1 index 63d611bdd25715..2c36627559899a 100644 --- a/lib/bundler/man/bundle-gem.1 +++ b/lib/bundler/man/bundle-gem.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-GEM" "1" "August 2022" "" "" +.TH "BUNDLE\-GEM" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1 index c0893425826b7e..ed72024e06b6a8 100644 --- a/lib/bundler/man/bundle-help.1 +++ b/lib/bundler/man/bundle-help.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-HELP" "1" "August 2022" "" "" +.TH "BUNDLE\-HELP" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-help\fR \- Displays detailed help for each subcommand diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1 index ae9bc48539a51b..f1ef32b758c108 100644 --- a/lib/bundler/man/bundle-info.1 +++ b/lib/bundler/man/bundle-info.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INFO" "1" "August 2022" "" "" +.TH "BUNDLE\-INFO" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-info\fR \- Show information for the given gem in your bundle diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1 index 0b06ce89973262..83f8d75324f54d 100644 --- a/lib/bundler/man/bundle-init.1 +++ b/lib/bundler/man/bundle-init.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INIT" "1" "August 2022" "" "" +.TH "BUNDLE\-INIT" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-init\fR \- Generates a Gemfile into the current working directory diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1 index 4ad37ec3eb8d1e..d675dba79b889a 100644 --- a/lib/bundler/man/bundle-inject.1 +++ b/lib/bundler/man/bundle-inject.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INJECT" "1" "August 2022" "" "" +.TH "BUNDLE\-INJECT" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1 index 077a58f4152f6f..858f56e673692e 100644 --- a/lib/bundler/man/bundle-install.1 +++ b/lib/bundler/man/bundle-install.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INSTALL" "1" "August 2022" "" "" +.TH "BUNDLE\-INSTALL" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-install\fR \- Install the dependencies specified in your Gemfile diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1 index 6e89f810a116c5..bf15769eaf8c25 100644 --- a/lib/bundler/man/bundle-list.1 +++ b/lib/bundler/man/bundle-list.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-LIST" "1" "August 2022" "" "" +.TH "BUNDLE\-LIST" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-list\fR \- List all the gems in the bundle diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1 index f5f0ce2bdbeae3..af805f34d32b17 100644 --- a/lib/bundler/man/bundle-lock.1 +++ b/lib/bundler/man/bundle-lock.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-LOCK" "1" "August 2022" "" "" +.TH "BUNDLE\-LOCK" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-lock\fR \- Creates / Updates a lockfile without installing diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1 index 7bda0afd33efab..f2b10b8808b754 100644 --- a/lib/bundler/man/bundle-open.1 +++ b/lib/bundler/man/bundle-open.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-OPEN" "1" "August 2022" "" "" +.TH "BUNDLE\-OPEN" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-open\fR \- Opens the source directory for a gem in your bundle diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1 index a2b8e2bf92193f..699416583857d8 100644 --- a/lib/bundler/man/bundle-outdated.1 +++ b/lib/bundler/man/bundle-outdated.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-OUTDATED" "1" "August 2022" "" "" +.TH "BUNDLE\-OUTDATED" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-outdated\fR \- List installed gems with newer versions available diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1 index 7fda4ef0466a59..848c3024cd2564 100644 --- a/lib/bundler/man/bundle-platform.1 +++ b/lib/bundler/man/bundle-platform.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PLATFORM" "1" "August 2022" "" "" +.TH "BUNDLE\-PLATFORM" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-platform\fR \- Displays platform compatibility information diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1 index 72108063279f4c..1508b85b38036a 100644 --- a/lib/bundler/man/bundle-plugin.1 +++ b/lib/bundler/man/bundle-plugin.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PLUGIN" "1" "August 2022" "" "" +.TH "BUNDLE\-PLUGIN" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-plugin\fR \- Manage Bundler plugins diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1 index d635d24e2d67bb..9a3a26bbfae144 100644 --- a/lib/bundler/man/bundle-pristine.1 +++ b/lib/bundler/man/bundle-pristine.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PRISTINE" "1" "August 2022" "" "" +.TH "BUNDLE\-PRISTINE" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-pristine\fR \- Restores installed gems to their pristine condition diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1 index 4952e8094e5cb1..f9d7d574d30d8a 100644 --- a/lib/bundler/man/bundle-remove.1 +++ b/lib/bundler/man/bundle-remove.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-REMOVE" "1" "August 2022" "" "" +.TH "BUNDLE\-REMOVE" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-remove\fR \- Removes gems from the Gemfile diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1 index 72ef3283ccd0fd..ff860c64cc9d8a 100644 --- a/lib/bundler/man/bundle-show.1 +++ b/lib/bundler/man/bundle-show.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-SHOW" "1" "August 2022" "" "" +.TH "BUNDLE\-SHOW" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1 index 06ceef85db2be7..608ad744367875 100644 --- a/lib/bundler/man/bundle-update.1 +++ b/lib/bundler/man/bundle-update.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-UPDATE" "1" "August 2022" "" "" +.TH "BUNDLE\-UPDATE" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-update\fR \- Update your gems to the latest available versions diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1 new file mode 100644 index 00000000000000..68fc24c4483bbc --- /dev/null +++ b/lib/bundler/man/bundle-version.1 @@ -0,0 +1,35 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "BUNDLE\-VERSION" "1" "September 2022" "" "" +. +.SH "NAME" +\fBbundle\-version\fR \- Prints Bundler version information +. +.SH "SYNOPSIS" +\fBbundle version\fR +. +.SH "DESCRIPTION" +Prints Bundler version information\. +. +.SH "OPTIONS" +No options\. +. +.SH "EXAMPLE" +Print the version of Bundler with build date and commit hash of the in the Git source\. +. +.IP "" 4 +. +.nf + +bundle version +. +.fi +. +.IP "" 0 +. +.P +shows \fBBundler version 2\.3\.21 (2022\-08\-24 commit d54be5fdd8)\fR for example\. +. +.P +cf\. \fBbundle \-\-version\fR shows \fBBundler version 2\.3\.21\fR\. diff --git a/lib/bundler/man/bundle-version.1.ronn b/lib/bundler/man/bundle-version.1.ronn new file mode 100644 index 00000000000000..46c6f0b30a290a --- /dev/null +++ b/lib/bundler/man/bundle-version.1.ronn @@ -0,0 +1,24 @@ +bundle-version(1) -- Prints Bundler version information +======================================================= + +## SYNOPSIS + +`bundle version` + +## DESCRIPTION + +Prints Bundler version information. + +## OPTIONS + +No options. + +## EXAMPLE + +Print the version of Bundler with build date and commit hash of the in the Git source. + + bundle version + +shows `Bundler version 2.3.21 (2022-08-24 commit d54be5fdd8)` for example. + +cf. `bundle --version` shows `Bundler version 2.3.21`. diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1 index cf63dd8f9e3b26..4d108a2aea98d3 100644 --- a/lib/bundler/man/bundle-viz.1 +++ b/lib/bundler/man/bundle-viz.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-VIZ" "1" "August 2022" "" "" +.TH "BUNDLE\-VIZ" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1 index d178b2f5dcb1e4..1898b15647703f 100644 --- a/lib/bundler/man/bundle.1 +++ b/lib/bundler/man/bundle.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE" "1" "August 2022" "" "" +.TH "BUNDLE" "1" "September 2022" "" "" . .SH "NAME" \fBbundle\fR \- Ruby Dependency Management @@ -124,6 +124,10 @@ Removes gems from the Gemfile \fBbundle plugin(1)\fR \fIbundle\-plugin\.1\.html\fR Manage Bundler plugins . +.TP +\fBbundle version(1)\fR \fIbundle\-version\.1\.html\fR +Prints Bundler version information +. .SH "PLUGINS" When running a command that isn\'t listed in PRIMARY COMMANDS or UTILITIES, Bundler will try to find an executable on your path named \fBbundler\-\fR and execute it, passing down any extra arguments to it\. . diff --git a/lib/bundler/man/bundle.1.ronn b/lib/bundler/man/bundle.1.ronn index 35c0a7ef873aec..8245effabd0bb7 100644 --- a/lib/bundler/man/bundle.1.ronn +++ b/lib/bundler/man/bundle.1.ronn @@ -100,6 +100,9 @@ We divide `bundle` subcommands into primary commands and utilities: * [`bundle plugin(1)`](bundle-plugin.1.html): Manage Bundler plugins +* [`bundle version(1)`](bundle-version.1.html): + Prints Bundler version information + ## PLUGINS When running a command that isn't listed in PRIMARY COMMANDS or UTILITIES, diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index 3d3994153b1bf9..e793500517a93c 100644 --- a/lib/bundler/man/gemfile.5 +++ b/lib/bundler/man/gemfile.5 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GEMFILE" "5" "August 2022" "" "" +.TH "GEMFILE" "5" "September 2022" "" "" . .SH "NAME" \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs @@ -254,19 +254,15 @@ There are a number of \fBGemfile\fR platforms: . .TP \fBruby\fR -C Ruby (MRI), Rubinius or TruffleRuby, but \fBNOT\fR Windows +C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows . .TP \fBmri\fR -Same as \fIruby\fR, but only C Ruby (MRI) +C Ruby (MRI) only, but not Windows . .TP -\fBmingw\fR -Windows 32 bit \'mingw32\' platform (aka RubyInstaller) -. -.TP -\fBx64_mingw\fR -Windows 64 bit \'mingw32\' platform (aka RubyInstaller x64) +\fBwindows\fR +Windows C Ruby (MRI), including RubyInstaller 32\-bit and 64\-bit versions . .TP \fBrbx\fR @@ -280,15 +276,8 @@ JRuby \fBtruffleruby\fR TruffleRuby . -.TP -\fBmswin\fR -Windows -. .P -You can restrict further by platform and version for all platforms \fIexcept\fR for \fBrbx\fR, \fBjruby\fR, \fBtruffleruby\fR and \fBmswin\fR\. -. -.P -To specify a version in addition to a platform, append the version number without the delimiter to the platform\. For example, to specify that a gem should only be used on platforms with Ruby 2\.3, use: +On platforms \fBruby\fR, \fBmri\fR, and \fBwindows\fR, you may additionally specify a version by appending the major and minor version numbers without a delimiter\. For example, to specify that a gem should only be used on platform \fBruby\fR version 2\.3, use: . .IP "" 4 . @@ -301,26 +290,7 @@ ruby_23 .IP "" 0 . .P -The full list of platforms and supported versions includes: -. -.TP -\fBruby\fR -1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 -. -.TP -\fBmri\fR -1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 -. -.TP -\fBmingw\fR -1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 -. -.TP -\fBx64_mingw\fR -2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 -. -.P -As with groups, you can specify one or more platforms: +As with groups (above), you may specify one or more platforms: . .IP "" 4 . @@ -328,7 +298,7 @@ As with groups, you can specify one or more platforms: gem "weakling", platforms: :jruby gem "ruby\-debug", platforms: :mri_18 -gem "nokogiri", platforms: [:mri_18, :jruby] +gem "nokogiri", platforms: [:windows_26, :jruby] . .fi . diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn index c2f9141c6553a1..89ebcc7214d342 100644 --- a/lib/bundler/man/gemfile.5.ronn +++ b/lib/bundler/man/gemfile.5.ronn @@ -190,47 +190,29 @@ platforms. There are a number of `Gemfile` platforms: * `ruby`: - C Ruby (MRI), Rubinius or TruffleRuby, but `NOT` Windows + C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows * `mri`: - Same as _ruby_, but only C Ruby (MRI) - * `mingw`: - Windows 32 bit 'mingw32' platform (aka RubyInstaller) - * `x64_mingw`: - Windows 64 bit 'mingw32' platform (aka RubyInstaller x64) + C Ruby (MRI) only, but not Windows + * `windows`: + Windows C Ruby (MRI), including RubyInstaller 32-bit and 64-bit versions * `rbx`: Rubinius * `jruby`: JRuby * `truffleruby`: TruffleRuby - * `mswin`: - Windows -You can restrict further by platform and version for all platforms *except* for -`rbx`, `jruby`, `truffleruby` and `mswin`. - -To specify a version in addition to a platform, append the version number without -the delimiter to the platform. For example, to specify that a gem should only be -used on platforms with Ruby 2.3, use: +On platforms `ruby`, `mri`, and `windows`, you may additionally specify a version +by appending the major and minor version numbers without a delimiter. For example, +to specify that a gem should only be used on platform `ruby` version 2.3, use: ruby_23 -The full list of platforms and supported versions includes: - - * `ruby`: - 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - * `mri`: - 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - * `mingw`: - 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - * `x64_mingw`: - 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - -As with groups, you can specify one or more platforms: +As with groups (above), you may specify one or more platforms: gem "weakling", platforms: :jruby gem "ruby-debug", platforms: :mri_18 - gem "nokogiri", platforms: [:mri_18, :jruby] + gem "nokogiri", platforms: [:windows_26, :jruby] All operations involving groups ([`bundle install`](bundle-install.1.html), `Bundler.setup`, `Bundler.require`) behave exactly the same as if any groups not diff --git a/lib/bundler/man/index.txt b/lib/bundler/man/index.txt index 6de81ea0271862..24f7633e66912c 100644 --- a/lib/bundler/man/index.txt +++ b/lib/bundler/man/index.txt @@ -6,6 +6,7 @@ bundle-cache(1) bundle-cache.1 bundle-check(1) bundle-check.1 bundle-clean(1) bundle-clean.1 bundle-config(1) bundle-config.1 +bundle-console(1) bundle-console.1 bundle-doctor(1) bundle-doctor.1 bundle-exec(1) bundle-exec.1 bundle-gem(1) bundle-gem.1 @@ -24,4 +25,5 @@ bundle-pristine(1) bundle-pristine.1 bundle-remove(1) bundle-remove.1 bundle-show(1) bundle-show.1 bundle-update(1) bundle-update.1 +bundle-version(1) bundle-version.1 bundle-viz(1) bundle-viz.1 diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb index 29d33be7181687..6fbd036f3811e6 100644 --- a/lib/bundler/plugin/index.rb +++ b/lib/bundler/plugin/index.rb @@ -167,11 +167,11 @@ def load_index(index_file, global = false) # to be only String key value pairs) def save_index index = { - "commands" => @commands, - "hooks" => @hooks, - "load_paths" => @load_paths, + "commands" => @commands, + "hooks" => @hooks, + "load_paths" => @load_paths, "plugin_paths" => @plugin_paths, - "sources" => @sources, + "sources" => @sources, } require_relative "../yaml_serializer" diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index fcb3812c5a797c..161a3c0518c639 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -42,8 +42,7 @@ def start(requirements, exclude_specs: []) remove_from_candidates(spec) end - @gem_version_promoter.prerelease_specified = @prerelease_specified = {} - requirements.each {|dep| @prerelease_specified[dep.name] ||= dep.prerelease? } + requirements.each {|dep| prerelease_specified[dep.name] ||= dep.prerelease? } verify_gemfile_dependencies_are_found!(requirements) result = @resolver.resolve(requirements). @@ -127,13 +126,6 @@ def search_for(dependency_proxy) results = results_for(dependency) + locked_results results = results.select {|spec| requirement_satisfied_by?(locked_requirement, nil, spec) } if locked_requirement - if !@prerelease_specified[name] && locked_results.empty? - # Move prereleases to the beginning of the list, so they're considered - # last during resolution. - pre, results = results.partition {|spec| spec.version.prerelease? } - results = pre + results - end - if results.any? results = @gem_version_promoter.sort_versions(dependency, results) @@ -221,6 +213,10 @@ def base_requirements @base.base_requirements end + def prerelease_specified + @gem_version_promoter.prerelease_specified + end + def remove_from_candidates(spec) @base.delete(spec) @@ -255,7 +251,7 @@ def amount_constrained(dependency) all - 1_000_000 else search = search_for(dependency) - search = @prerelease_specified[dependency.name] ? search.count : search.count {|s| !s.version.prerelease? } + search = prerelease_specified[dependency.name] ? search.count : search.count {|s| !s.version.prerelease? } search - all end end @@ -284,7 +280,7 @@ def verify_gemfile_dependencies_are_found!(requirements) end def gem_not_found_message(name, requirement, source, extra_message = "") - specs = source.specs.search(name) + specs = source.specs.search(name).sort_by {|s| [s.version, s.platform.to_s] } matching_part = name requirement_label = SharedHelpers.pretty_dependency(requirement) cache_message = begin diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 396465f0cb7a7b..3b640b55a96a5c 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -169,13 +169,13 @@ def install(spec, options = {}) installer = Bundler::RubyGemsGemInstaller.at( path, - :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]], - :install_dir => install_path.to_s, - :bin_dir => bin_path.to_s, + :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]], + :install_dir => install_path.to_s, + :bin_dir => bin_path.to_s, :ignore_dependencies => true, - :wrappers => true, - :env_shebang => true, - :build_args => options[:build_args], + :wrappers => true, + :env_shebang => true, + :build_args => options[:build_args], :bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum, :bundler_extension_cache_path => extension_cache_path(spec) ) diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index 1d0b7a460d37bd..21d57fdab4370e 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -176,7 +176,7 @@ def extract_circular_gems(error) def lookup @lookup ||= begin lookup = Hash.new {|h, k| h[k] = [] } - Index.sort_specs(@specs).reverse_each do |s| + @specs.each do |s| lookup[s.name] << s end lookup diff --git a/lib/bundler/templates/newgem/gitlab-ci.yml.tt b/lib/bundler/templates/newgem/gitlab-ci.yml.tt index 0e71ff26a4a91b..42e00392de9380 100644 --- a/lib/bundler/templates/newgem/gitlab-ci.yml.tt +++ b/lib/bundler/templates/newgem/gitlab-ci.yml.tt @@ -1,8 +1,9 @@ -image: ruby:<%= RUBY_VERSION %> +default: + image: ruby:<%= RUBY_VERSION %> -before_script: - - gem install bundler -v <%= Bundler::VERSION %> - - bundle install + before_script: + - gem install bundler -v <%= Bundler::VERSION %> + - bundle install example_job: script: diff --git a/lib/bundler/vendored_persistent.rb b/lib/bundler/vendored_persistent.rb index dc9573e025b7a8..e29f27cdfd54d9 100644 --- a/lib/bundler/vendored_persistent.rb +++ b/lib/bundler/vendored_persistent.rb @@ -11,37 +11,5 @@ module HTTP require_relative "vendor/net-http-persistent/lib/net/http/persistent" module Bundler - class PersistentHTTP < Persistent::Net::HTTP::Persistent - def connection_for(uri) - super(uri) do |connection| - result = yield connection - warn_old_tls_version_rubygems_connection(uri, connection) - result - end - end - - def warn_old_tls_version_rubygems_connection(uri, connection) - return unless connection.http.use_ssl? - return unless (uri.host || "").end_with?("rubygems.org") - - socket = connection.instance_variable_get(:@socket) - return unless socket - socket_io = socket.io - return unless socket_io.respond_to?(:ssl_version) - ssl_version = socket_io.ssl_version - - case ssl_version - when /TLSv([\d\.]+)/ - version = Gem::Version.new($1) - if version < Gem::Version.new("1.2") - Bundler.ui.warn \ - "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \ - "Starting in January 2018, RubyGems.org will refuse connection requests from these " \ - "very old versions of OpenSSL. If you will need to continue installing gems after " \ - "January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.", - :wrap => true - end - end - end - end + PersistentHTTP = Persistent::Net::HTTP::Persistent end diff --git a/lib/cgi.rb b/lib/cgi.rb index affe8fd3fcefa1..8d51cb24fe17f9 100644 --- a/lib/cgi.rb +++ b/lib/cgi.rb @@ -288,7 +288,7 @@ # class CGI - VERSION = "0.3.2" + VERSION = "0.3.3" end require 'cgi/core' diff --git a/lib/forwardable.rb b/lib/forwardable.rb index c9c4128f9f59ef..9318639f016a4b 100644 --- a/lib/forwardable.rb +++ b/lib/forwardable.rb @@ -113,7 +113,9 @@ module Forwardable # Version of +forwardable.rb+ VERSION = "1.3.2" + VERSION.freeze FORWARDABLE_VERSION = VERSION + FORWARDABLE_VERSION.freeze @debug = nil class << self diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index 459b8478a0d072..7a99069fafe48c 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -47,7 +47,7 @@ class IPAddr # 128 bit mask for IPv6 IN6MASK = 0xffffffffffffffffffffffffffffffff # Format string for IPv6 - IN6FORMAT = (["%.4x"] * 8).join(':') + IN6FORMAT = (["%.4x"] * 8).join(':').freeze # Regexp _internally_ used for parsing IPv4 address. RE_IPV4ADDRLIKE = %r{ @@ -736,7 +736,7 @@ def _to_string(addr) unless Socket.const_defined? :AF_INET6 class Socket < BasicSocket # IPv6 protocol family - AF_INET6 = Object.new + AF_INET6 = Object.new.freeze end class << IPSocket diff --git a/lib/irb/color.rb b/lib/irb/color.rb index 8307af25a94758..7071696cb29c7c 100644 --- a/lib/irb/color.rb +++ b/lib/irb/color.rb @@ -128,10 +128,14 @@ def colorize_code(code, complete: true, ignore_error: false, colorable: colorabl symbol_state = SymbolState.new colored = +'' - length = 0 - end_seen = false scan(code, allow_last_error: !complete) do |token, str, expr| + # handle uncolorable code + if token.nil? + colored << Reline::Unicode.escape_for_print(str) + next + end + # IRB::ColorPrinter skips colorizing fragments with any invalid token if ignore_error && ERROR_TOKENS.include?(token) return Reline::Unicode.escape_for_print(code) @@ -147,15 +151,7 @@ def colorize_code(code, complete: true, ignore_error: false, colorable: colorabl colored << line end end - length += str.bytesize - end_seen = true if token == :on___end__ end - - # give up colorizing incomplete Ripper tokens - unless end_seen or length == code.bytesize - return Reline::Unicode.escape_for_print(code) - end - colored end @@ -170,33 +166,42 @@ def without_circular_ref(obj, seen:, &block) end def scan(code, allow_last_error:) - pos = [1, 0] - verbose, $VERBOSE = $VERBOSE, nil RubyLex.compile_with_errors_suppressed(code) do |inner_code, line_no| lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no) - if lexer.respond_to?(:scan) # Ruby 2.7+ - lexer.scan.each do |elem| - str = elem.tok - next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message - next if ([elem.pos[0], elem.pos[1] + str.bytesize] <=> pos) <= 0 + byte_pos = 0 + line_positions = [0] + inner_code.lines.each do |line| + line_positions << line_positions.last + line.bytesize + end + + on_scan = proc do |elem| + start_pos = line_positions[elem.pos[0] - 1] + elem.pos[1] - str.each_line do |line| - if line.end_with?("\n") - pos[0] += 1 - pos[1] = 0 - else - pos[1] += line.bytesize - end - end + # yield uncolorable code + if byte_pos < start_pos + yield(nil, inner_code.byteslice(byte_pos...start_pos), nil) + end + if byte_pos <= start_pos + str = elem.tok yield(elem.event, str, elem.state) + byte_pos = start_pos + str.bytesize + end + end + + if lexer.respond_to?(:scan) # Ruby 2.7+ + lexer.scan.each do |elem| + next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message + on_scan.call(elem) end else - lexer.parse.each do |elem| - yield(elem.event, elem.tok, elem.state) + lexer.parse.sort_by(&:pos).each do |elem| + on_scan.call(elem) end end + # yield uncolorable DATA section + yield(nil, inner_code.byteslice(byte_pos...inner_code.bytesize), nil) if byte_pos < inner_code.bytesize end ensure $VERBOSE = verbose diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index 9121174a508070..bfe6c7e7a4aac8 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -60,7 +60,14 @@ def self.retrieve_gem_and_system_load_path end } }.flatten if defined?(Gem::Specification) - (gem_paths.to_a | $LOAD_PATH).sort + candidates = (gem_paths.to_a | $LOAD_PATH) + candidates.map do |p| + if p.respond_to?(:to_path) + p.to_path + else + String(p) rescue nil + end + end.compact.sort end def self.retrieve_files_to_require_from_load_path diff --git a/lib/irb/context.rb b/lib/irb/context.rb index e6c993d4230e6a..b74cae12231651 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -115,6 +115,10 @@ def initialize(irb, workspace = nil, input_method = nil) end @io = StdioInputMethod.new unless @io + when '-' + @io = FileInputMethod.new($stdin) + @irb_name = '-' + @irb_path = '-' when String @io = FileInputMethod.new(input_method) @irb_name = File.basename(input_method) diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb index 7acaebe36a137e..2135184dba37a9 100644 --- a/lib/irb/ext/save-history.rb +++ b/lib/irb/ext/save-history.rb @@ -107,13 +107,13 @@ def save_history raise end - if File.exist?(history_file) && @loaded_history_mtime && + if File.exist?(history_file) && File.mtime(history_file) != @loaded_history_mtime - history = history[@loaded_history_lines..-1] + history = history[@loaded_history_lines..-1] if @loaded_history_lines append_history = true end - open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f| + File.open(history_file, (append_history ? 'a' : 'w'), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f| hist = history.map{ |l| l.split("\n").join("\\\n") } unless append_history begin diff --git a/lib/irb/init.rb b/lib/irb/init.rb index d2baee2017e241..d9c4353f39ced4 100644 --- a/lib/irb/init.rb +++ b/lib/irb/init.rb @@ -289,6 +289,10 @@ def IRB.parse_opts(argv: ::ARGV) @CONF[:PROMPT_MODE] = prompt_mode when "--noprompt" @CONF[:PROMPT_MODE] = :NULL + when "--script" + noscript = false + when "--noscript" + noscript = true when "--inf-ruby-mode" @CONF[:PROMPT_MODE] = :INF_RUBY when "--sample-book-mode", "--simple-prompt" @@ -309,16 +313,20 @@ def IRB.parse_opts(argv: ::ARGV) IRB.print_usage exit 0 when "--" - if opt = argv.shift + if !noscript && (opt = argv.shift) @CONF[:SCRIPT] = opt $0 = opt end break - when /^-/ + when /^-./ fail UnrecognizedSwitch, opt else - @CONF[:SCRIPT] = opt - $0 = opt + if noscript + argv.unshift(opt) + else + @CONF[:SCRIPT] = opt + $0 = opt + end break end end diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index b77fd3207def87..eec2daa549c22a 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -137,7 +137,7 @@ def open(file, &block) # Creates a new input method object def initialize(file) super - @io = IRB::MagicFile.open(file) + @io = file.is_a?(IO) ? file : IRB::MagicFile.open(file) @external_encoding = @io.external_encoding end # The file name of this input method, usually given during initialization. diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message index 3405fa775f5acc..5b23f4c41ed2c2 100644 --- a/lib/irb/lc/help-message +++ b/lib/irb/lc/help-message @@ -38,6 +38,8 @@ Usage: irb.rb [options] [programfile] [arguments] --sample-book-mode, --simple-prompt Set prompt mode to 'simple'. --noprompt Don't output prompt. + --script Script mode (default, treat first argument as script) + --noscript No script mode (leave arguments in argv) --single-irb Share self with sub-irb. --tracer Show stack trace for each command. --back-trace-limit n[=16] diff --git a/lib/mjit/c_32.rb b/lib/mjit/c_32.rb deleted file mode 100644 index c628e60967ca32..00000000000000 --- a/lib/mjit/c_32.rb +++ /dev/null @@ -1,438 +0,0 @@ -require_relative 'c_type' - -module RubyVM::MJIT - C = Object.new - - def C.NOT_COMPILED_STACK_SIZE = - 1 - - def C.USE_LAZY_LOAD = false - - def C.USE_RVARGC = true - - def C.VM_CALL_KW_SPLAT = ( 0x01 << self.VM_CALL_KW_SPLAT_bit ) - - def C.VM_CALL_TAILCALL = ( 0x01 << self.VM_CALL_TAILCALL_bit ) - - def C.VM_METHOD_TYPE_CFUNC = 1 - - def C.VM_METHOD_TYPE_ISEQ = 0 - - def C.VM_CALL_KW_SPLAT_bit = 7 - - def C.VM_CALL_TAILCALL_bit = 8 - - def C.CALL_DATA - @CALL_DATA ||= self.rb_call_data - end - - def C.IC - @IC ||= self.iseq_inline_constant_cache - end - - def C.IVC - @IVC ||= self.iseq_inline_iv_cache_entry - end - - def C.RB_BUILTIN - @RB_BUILTIN ||= self.rb_builtin_function - end - - def C.VALUE - @VALUE ||= CType::Immediate.new(-5) - end - - def C.compile_branch - @compile_branch ||= CType::Struct.new( - "compile_branch", 8, - stack_size: [0, CType::Immediate.new(-4)], - finish_p: [32, self._Bool], - ) - end - - def C.compile_status - @compile_status ||= CType::Struct.new( - "compile_status", 68, - success: [0, self._Bool], - stack_size_for_pos: [32, CType::Pointer.new { CType::Immediate.new(4) }], - local_stack_p: [64, self._Bool], - is_entries: [96, CType::Pointer.new { self.iseq_inline_storage_entry }], - cc_entries_index: [128, CType::Immediate.new(4)], - compiled_iseq: [160, CType::Pointer.new { self.rb_iseq_constant_body }], - compiled_id: [192, CType::Immediate.new(4)], - compile_info: [224, CType::Pointer.new { self.rb_mjit_compile_info }], - merge_ivar_guards_p: [256, self._Bool], - ivar_serial: [288, self.rb_serial_t], - max_ivar_index: [352, CType::Immediate.new(-4)], - inlined_iseqs: [384, CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], - inline_context: [416, self.inlined_call_context], - ) - end - - def C.inlined_call_context - @inlined_call_context ||= CType::Struct.new( - "inlined_call_context", 16, - orig_argc: [0, CType::Immediate.new(4)], - me: [32, self.VALUE], - param_size: [64, CType::Immediate.new(4)], - local_size: [96, CType::Immediate.new(4)], - ) - end - - def C.iseq_inline_constant_cache - @iseq_inline_constant_cache ||= CType::Struct.new( - "iseq_inline_constant_cache", 8, - entry: [0, CType::Pointer.new { self.iseq_inline_constant_cache_entry }], - segments: [32, CType::Pointer.new { self.ID }], - ) - end - - def C.iseq_inline_constant_cache_entry - @iseq_inline_constant_cache_entry ||= CType::Struct.new( - "iseq_inline_constant_cache_entry", 20, - flags: [0, self.VALUE], - value: [32, self.VALUE], - _unused1: [64, self.VALUE], - _unused2: [96, self.VALUE], - ic_cref: [128, CType::Pointer.new { self.rb_cref_t }], - ) - end - - def C.iseq_inline_iv_cache_entry - @iseq_inline_iv_cache_entry ||= CType::Struct.new( - "iseq_inline_iv_cache_entry", 4, - entry: [0, CType::Pointer.new { self.rb_iv_index_tbl_entry }], - ) - end - - def C.iseq_inline_storage_entry - @iseq_inline_storage_entry ||= CType::Union.new( - "iseq_inline_storage_entry", 8, - once: CType::Struct.new( - "", 8, - running_thread: [0, CType::Pointer.new { self.rb_thread_struct }], - value: [32, self.VALUE], - ), - ic_cache: self.iseq_inline_constant_cache, - iv_cache: self.iseq_inline_iv_cache_entry, - ) - end - - def C.mjit_options - @mjit_options ||= CType::Struct.new( - "mjit_options", 28, - on: [0, self._Bool], - save_temps: [8, self._Bool], - warnings: [16, self._Bool], - debug: [24, self._Bool], - debug_flags: [32, CType::Pointer.new { CType::Immediate.new(2) }], - wait: [64, self._Bool], - min_calls: [96, CType::Immediate.new(-4)], - verbose: [128, CType::Immediate.new(4)], - max_cache_size: [160, CType::Immediate.new(4)], - pause: [192, self._Bool], - custom: [200, self._Bool], - ) - end - - def C.rb_builtin_function - @rb_builtin_function ||= CType::Struct.new( - "rb_builtin_function", 20, - func_ptr: [0, CType::Pointer.new { CType::Immediate.new(0) }], - argc: [32, CType::Immediate.new(4)], - index: [64, CType::Immediate.new(4)], - name: [96, CType::Pointer.new { CType::Immediate.new(2) }], - compiler: [128, CType::Immediate.new(1)], - ) - end - - def C.rb_call_data - @rb_call_data ||= CType::Struct.new( - "rb_call_data", 8, - ci: [0, CType::Pointer.new { self.rb_callinfo }], - cc: [32, CType::Pointer.new { self.rb_callcache }], - ) - end - - def C.rb_callable_method_entry_struct - @rb_callable_method_entry_struct ||= CType::Struct.new( - "rb_callable_method_entry_struct", 20, - flags: [0, self.VALUE], - defined_class: [32, self.VALUE], - def: [64, CType::Pointer.new { self.rb_method_definition_struct }], - called_id: [96, self.ID], - owner: [128, self.VALUE], - ) - end - - def C.rb_callcache - @rb_callcache ||= CType::Struct.new( - "rb_callcache", 20, - flags: [0, self.VALUE], - klass: [32, self.VALUE], - cme_: [64, CType::Pointer.new { self.rb_callable_method_entry_struct }], - call_: [96, self.vm_call_handler], - aux_: [128, CType::Union.new( - "", 4, - attr_index: CType::Immediate.new(-4), - method_missing_reason: self.method_missing_reason, - v: self.VALUE, - )], - ) - end - - def C.rb_callinfo - @rb_callinfo ||= CType::Struct.new( - "rb_callinfo", 20, - flags: [0, self.VALUE], - kwarg: [32, CType::Pointer.new { self.rb_callinfo_kwarg }], - mid: [64, self.VALUE], - flag: [96, self.VALUE], - argc: [128, self.VALUE], - ) - end - - def C.rb_cref_t - @rb_cref_t ||= CType::Struct.new( - "rb_cref_struct", 20, - flags: [0, self.VALUE], - refinements: [32, self.VALUE], - klass_or_self: [64, self.VALUE], - next: [96, CType::Pointer.new { self.rb_cref_struct }], - scope_visi: [128, self.rb_scope_visibility_t], - ) - end - - def C.rb_iseq_constant_body - @rb_iseq_constant_body ||= CType::Struct.new( - "rb_iseq_constant_body", 204, - type: [0, self.rb_iseq_type], - iseq_size: [32, CType::Immediate.new(-4)], - iseq_encoded: [64, CType::Pointer.new { self.VALUE }], - param: [96, CType::Struct.new( - "", 40, - flags: [0, CType::Struct.new( - "", 4, - has_lead: [0, CType::BitField.new(1, 0)], - has_opt: [1, CType::BitField.new(1, 1)], - has_rest: [2, CType::BitField.new(1, 2)], - has_post: [3, CType::BitField.new(1, 3)], - has_kw: [4, CType::BitField.new(1, 4)], - has_kwrest: [5, CType::BitField.new(1, 5)], - has_block: [6, CType::BitField.new(1, 6)], - ambiguous_param0: [7, CType::BitField.new(1, 7)], - accepts_no_kwarg: [8, CType::BitField.new(1, 0)], - ruby2_keywords: [9, CType::BitField.new(1, 1)], - )], - size: [32, CType::Immediate.new(-4)], - lead_num: [64, CType::Immediate.new(4)], - opt_num: [96, CType::Immediate.new(4)], - rest_start: [128, CType::Immediate.new(4)], - post_start: [160, CType::Immediate.new(4)], - post_num: [192, CType::Immediate.new(4)], - block_start: [224, CType::Immediate.new(4)], - opt_table: [256, CType::Pointer.new { self.VALUE }], - keyword: [288, CType::Pointer.new { self.rb_iseq_param_keyword }], - )], - location: [416, self.rb_iseq_location_t], - insns_info: [704, self.iseq_insn_info], - local_table: [832, CType::Pointer.new { self.ID }], - catch_table: [864, CType::Pointer.new { self.iseq_catch_table }], - parent_iseq: [896, CType::Pointer.new { self.rb_iseq_struct }], - local_iseq: [928, CType::Pointer.new { self.rb_iseq_struct }], - is_entries: [960, CType::Pointer.new { self.iseq_inline_storage_entry }], - call_data: [992, CType::Pointer.new { self.rb_call_data }], - variable: [1024, CType::Struct.new( - "", 20, - flip_count: [0, self.rb_snum_t], - script_lines: [32, self.VALUE], - coverage: [64, self.VALUE], - pc2branchindex: [96, self.VALUE], - original_iseq: [128, CType::Pointer.new { self.VALUE }], - )], - local_table_size: [1184, CType::Immediate.new(-4)], - ic_size: [1216, CType::Immediate.new(-4)], - ise_size: [1248, CType::Immediate.new(-4)], - ivc_size: [1280, CType::Immediate.new(-4)], - icvarc_size: [1312, CType::Immediate.new(-4)], - ci_size: [1344, CType::Immediate.new(-4)], - stack_max: [1376, CType::Immediate.new(-4)], - mark_bits: [1408, CType::Union.new( - "", 4, - list: CType::Pointer.new { self.iseq_bits_t }, - single: self.iseq_bits_t, - )], - catch_except_p: [1440, self._Bool], - builtin_inline_p: [1448, self._Bool], - outer_variables: [1472, CType::Pointer.new { self.rb_id_table }], - mandatory_only_iseq: [1504, CType::Pointer.new { self.rb_iseq_t }], - jit_func: [1536, CType::Immediate.new(1)], - total_calls: [1568, CType::Immediate.new(-5)], - jit_unit: [1600, CType::Pointer.new { self.rb_mjit_unit }], - ) - end - - def C.rb_iseq_location_t - @rb_iseq_location_t ||= CType::Struct.new( - "rb_iseq_location_struct", 36, - pathobj: [0, self.VALUE, true], - base_label: [32, self.VALUE, true], - label: [64, self.VALUE, true], - first_lineno: [96, self.VALUE, true], - node_id: [128, CType::Immediate.new(4)], - code_location: [160, self.rb_code_location_t], - ) - end - - def C.rb_iseq_struct - @rb_iseq_struct ||= CType::Struct.new( - "rb_iseq_struct", 20, - flags: [0, self.VALUE], - wrapper: [32, self.VALUE], - body: [64, CType::Pointer.new { self.rb_iseq_constant_body }], - aux: [96, CType::Union.new( - "", 8, - compile_data: CType::Pointer.new { self.iseq_compile_data }, - loader: CType::Struct.new( - "", 8, - obj: [0, self.VALUE], - index: [32, CType::Immediate.new(4)], - ), - exec: CType::Struct.new( - "", 8, - local_hooks: [0, CType::Pointer.new { self.rb_hook_list_struct }], - global_trace_events: [32, self.rb_event_flag_t], - ), - )], - ) - end - - def C.rb_iseq_t - @rb_iseq_t ||= self.rb_iseq_struct - end - - def C.rb_iv_index_tbl_entry - @rb_iv_index_tbl_entry ||= CType::Struct.new( - "rb_iv_index_tbl_entry", 16, - index: [0, CType::Immediate.new(-4)], - class_serial: [32, self.rb_serial_t], - class_value: [96, self.VALUE], - ) - end - - def C.rb_method_definition_struct - @rb_method_definition_struct ||= CType::Struct.new( - "rb_method_definition_struct", 28, - type: [0, self.rb_method_type_t], - iseq_overload: [4, CType::BitField.new(1, 4)], - alias_count: [5, CType::BitField.new(27, 5)], - complemented_count: [32, CType::BitField.new(28, 0)], - no_redef_warning: [60, CType::BitField.new(1, 4)], - body: [64, CType::Union.new( - "", 12, - iseq: self.rb_method_iseq_t, - cfunc: self.rb_method_cfunc_t, - attr: self.rb_method_attr_t, - alias: self.rb_method_alias_t, - refined: self.rb_method_refined_t, - bmethod: self.rb_method_bmethod_t, - optimized: self.rb_method_optimized_t, - )], - original_id: [160, self.ID], - method_serial: [192, CType::Immediate.new(-4)], - ) - end - - def C.rb_method_iseq_t - @rb_method_iseq_t ||= CType::Struct.new( - "rb_method_iseq_struct", 8, - iseqptr: [0, CType::Pointer.new { self.rb_iseq_t }], - cref: [32, CType::Pointer.new { self.rb_cref_t }], - ) - end - - def C.rb_method_type_t - @rb_method_type_t ||= CType::Immediate.new(4) - end - - def C.rb_mjit_compile_info - @rb_mjit_compile_info ||= CType::Struct.new( - "rb_mjit_compile_info", 5, - disable_ivar_cache: [0, self._Bool], - disable_exivar_cache: [8, self._Bool], - disable_send_cache: [16, self._Bool], - disable_inlining: [24, self._Bool], - disable_const_cache: [32, self._Bool], - ) - end - - def C.rb_mjit_unit - @rb_mjit_unit ||= CType::Struct.new( - "rb_mjit_unit", 36, - unode: [0, self.ccan_list_node], - id: [64, CType::Immediate.new(4)], - handle: [96, CType::Pointer.new { CType::Immediate.new(0) }], - iseq: [128, CType::Pointer.new { self.rb_iseq_t }], - used_code_p: [160, self._Bool], - compact_p: [168, self._Bool], - compile_info: [176, self.rb_mjit_compile_info], - cc_entries: [224, CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], - cc_entries_size: [256, CType::Immediate.new(-4)], - ) - end - - def C.rb_serial_t - @rb_serial_t ||= CType::Immediate.new(-6) - end - - def C._Bool = CType::Bool.new - - def C.ID = CType::Stub.new(:ID) - - def C.rb_thread_struct = CType::Stub.new(:rb_thread_struct) - - def C.vm_call_handler = CType::Stub.new(:vm_call_handler) - - def C.method_missing_reason = CType::Stub.new(:method_missing_reason) - - def C.rb_callinfo_kwarg = CType::Stub.new(:rb_callinfo_kwarg) - - def C.rb_cref_struct = CType::Stub.new(:rb_cref_struct) - - def C.rb_scope_visibility_t = CType::Stub.new(:rb_scope_visibility_t) - - def C.rb_iseq_type = CType::Stub.new(:rb_iseq_type) - - def C.rb_iseq_param_keyword = CType::Stub.new(:rb_iseq_param_keyword) - - def C.iseq_insn_info = CType::Stub.new(:iseq_insn_info) - - def C.iseq_catch_table = CType::Stub.new(:iseq_catch_table) - - def C.rb_snum_t = CType::Stub.new(:rb_snum_t) - - def C.iseq_bits_t = CType::Stub.new(:iseq_bits_t) - - def C.rb_id_table = CType::Stub.new(:rb_id_table) - - def C.rb_code_location_t = CType::Stub.new(:rb_code_location_t) - - def C.iseq_compile_data = CType::Stub.new(:iseq_compile_data) - - def C.rb_hook_list_struct = CType::Stub.new(:rb_hook_list_struct) - - def C.rb_event_flag_t = CType::Stub.new(:rb_event_flag_t) - - def C.rb_method_cfunc_t = CType::Stub.new(:rb_method_cfunc_t) - - def C.rb_method_attr_t = CType::Stub.new(:rb_method_attr_t) - - def C.rb_method_alias_t = CType::Stub.new(:rb_method_alias_t) - - def C.rb_method_refined_t = CType::Stub.new(:rb_method_refined_t) - - def C.rb_method_bmethod_t = CType::Stub.new(:rb_method_bmethod_t) - - def C.rb_method_optimized_t = CType::Stub.new(:rb_method_optimized_t) - - def C.ccan_list_node = CType::Stub.new(:ccan_list_node) -end diff --git a/lib/mjit/c_64.rb b/lib/mjit/c_64.rb deleted file mode 100644 index 864cb32b64e600..00000000000000 --- a/lib/mjit/c_64.rb +++ /dev/null @@ -1,439 +0,0 @@ -require_relative 'c_type' - -module RubyVM::MJIT - C = Object.new - - def C.NOT_COMPILED_STACK_SIZE = - 1 - - def C.USE_LAZY_LOAD = false - - def C.USE_RVARGC = true - - def C.VM_CALL_KW_SPLAT = ( 0x01 << self.VM_CALL_KW_SPLAT_bit ) - - def C.VM_CALL_TAILCALL = ( 0x01 << self.VM_CALL_TAILCALL_bit ) - - def C.VM_METHOD_TYPE_CFUNC = 1 - - def C.VM_METHOD_TYPE_ISEQ = 0 - - def C.VM_CALL_KW_SPLAT_bit = 7 - - def C.VM_CALL_TAILCALL_bit = 8 - - def C.CALL_DATA - @CALL_DATA ||= self.rb_call_data - end - - def C.IC - @IC ||= self.iseq_inline_constant_cache - end - - def C.IVC - @IVC ||= self.iseq_inline_iv_cache_entry - end - - def C.RB_BUILTIN - @RB_BUILTIN ||= self.rb_builtin_function - end - - def C.VALUE - @VALUE ||= CType::Immediate.new(-5) - end - - def C.compile_branch - @compile_branch ||= CType::Struct.new( - "compile_branch", 8, - stack_size: [0, CType::Immediate.new(-4)], - finish_p: [32, self._Bool], - ) - end - - def C.compile_status - @compile_status ||= CType::Struct.new( - "compile_status", 120, - success: [0, self._Bool], - stack_size_for_pos: [64, CType::Pointer.new { CType::Immediate.new(4) }], - local_stack_p: [128, self._Bool], - is_entries: [192, CType::Pointer.new { self.iseq_inline_storage_entry }], - cc_entries_index: [256, CType::Immediate.new(4)], - compiled_iseq: [320, CType::Pointer.new { self.rb_iseq_constant_body }], - compiled_id: [384, CType::Immediate.new(4)], - compile_info: [448, CType::Pointer.new { self.rb_mjit_compile_info }], - merge_ivar_guards_p: [512, self._Bool], - ivar_serial: [576, self.rb_serial_t], - max_ivar_index: [640, CType::Immediate.new(-5)], - inlined_iseqs: [704, CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }], - inline_context: [768, self.inlined_call_context], - ) - end - - def C.inlined_call_context - @inlined_call_context ||= CType::Struct.new( - "inlined_call_context", 24, - orig_argc: [0, CType::Immediate.new(4)], - me: [64, self.VALUE], - param_size: [128, CType::Immediate.new(4)], - local_size: [160, CType::Immediate.new(4)], - ) - end - - def C.iseq_inline_constant_cache - @iseq_inline_constant_cache ||= CType::Struct.new( - "iseq_inline_constant_cache", 16, - entry: [0, CType::Pointer.new { self.iseq_inline_constant_cache_entry }], - segments: [64, CType::Pointer.new { self.ID }], - ) - end - - def C.iseq_inline_constant_cache_entry - @iseq_inline_constant_cache_entry ||= CType::Struct.new( - "iseq_inline_constant_cache_entry", 40, - flags: [0, self.VALUE], - value: [64, self.VALUE], - _unused1: [128, self.VALUE], - _unused2: [192, self.VALUE], - ic_cref: [256, CType::Pointer.new { self.rb_cref_t }], - ) - end - - def C.iseq_inline_iv_cache_entry - @iseq_inline_iv_cache_entry ||= CType::Struct.new( - "iseq_inline_iv_cache_entry", 8, - entry: [0, CType::Pointer.new { self.rb_iv_index_tbl_entry }], - ) - end - - def C.iseq_inline_storage_entry - @iseq_inline_storage_entry ||= CType::Union.new( - "iseq_inline_storage_entry", 16, - once: CType::Struct.new( - "", 16, - running_thread: [0, CType::Pointer.new { self.rb_thread_struct }], - value: [64, self.VALUE], - ), - ic_cache: self.iseq_inline_constant_cache, - iv_cache: self.iseq_inline_iv_cache_entry, - ) - end - - def C.mjit_options - @mjit_options ||= CType::Struct.new( - "mjit_options", 40, - on: [0, self._Bool], - save_temps: [8, self._Bool], - warnings: [16, self._Bool], - debug: [24, self._Bool], - debug_flags: [64, CType::Pointer.new { CType::Immediate.new(2) }], - wait: [128, self._Bool], - min_calls: [160, CType::Immediate.new(-4)], - verbose: [192, CType::Immediate.new(4)], - max_cache_size: [224, CType::Immediate.new(4)], - pause: [256, self._Bool], - custom: [264, self._Bool], - ) - end - - def C.rb_builtin_function - @rb_builtin_function ||= CType::Struct.new( - "rb_builtin_function", 32, - func_ptr: [0, CType::Pointer.new { CType::Immediate.new(0) }], - argc: [64, CType::Immediate.new(4)], - index: [96, CType::Immediate.new(4)], - name: [128, CType::Pointer.new { CType::Immediate.new(2) }], - compiler: [192, CType::Immediate.new(1)], - ) - end - - def C.rb_call_data - @rb_call_data ||= CType::Struct.new( - "rb_call_data", 16, - ci: [0, CType::Pointer.new { self.rb_callinfo }], - cc: [64, CType::Pointer.new { self.rb_callcache }], - ) - end - - def C.rb_callable_method_entry_struct - @rb_callable_method_entry_struct ||= CType::Struct.new( - "rb_callable_method_entry_struct", 40, - flags: [0, self.VALUE], - defined_class: [64, self.VALUE], - def: [128, CType::Pointer.new { self.rb_method_definition_struct }], - called_id: [192, self.ID], - owner: [256, self.VALUE], - ) - end - - def C.rb_callcache - @rb_callcache ||= CType::Struct.new( - "rb_callcache", 40, - flags: [0, self.VALUE], - klass: [64, self.VALUE], - cme_: [128, CType::Pointer.new { self.rb_callable_method_entry_struct }], - call_: [192, self.vm_call_handler], - aux_: [256, CType::Union.new( - "", 8, - attr_index: CType::Immediate.new(-4), - method_missing_reason: self.method_missing_reason, - v: self.VALUE, - )], - ) - end - - def C.rb_callinfo - @rb_callinfo ||= CType::Struct.new( - "rb_callinfo", 40, - flags: [0, self.VALUE], - kwarg: [64, CType::Pointer.new { self.rb_callinfo_kwarg }], - mid: [128, self.VALUE], - flag: [192, self.VALUE], - argc: [256, self.VALUE], - ) - end - - def C.rb_cref_t - @rb_cref_t ||= CType::Struct.new( - "rb_cref_struct", 40, - flags: [0, self.VALUE], - refinements: [64, self.VALUE], - klass_or_self: [128, self.VALUE], - next: [192, CType::Pointer.new { self.rb_cref_struct }], - scope_visi: [256, self.rb_scope_visibility_t], - ) - end - - def C.rb_iseq_constant_body - @rb_iseq_constant_body ||= CType::Struct.new( - "rb_iseq_constant_body", 336, - type: [0, self.rb_iseq_type], - iseq_size: [32, CType::Immediate.new(-4)], - iseq_encoded: [64, CType::Pointer.new { self.VALUE }], - param: [128, CType::Struct.new( - "", 48, - flags: [0, CType::Struct.new( - "", 4, - has_lead: [0, CType::BitField.new(1, 0)], - has_opt: [1, CType::BitField.new(1, 1)], - has_rest: [2, CType::BitField.new(1, 2)], - has_post: [3, CType::BitField.new(1, 3)], - has_kw: [4, CType::BitField.new(1, 4)], - has_kwrest: [5, CType::BitField.new(1, 5)], - has_block: [6, CType::BitField.new(1, 6)], - ambiguous_param0: [7, CType::BitField.new(1, 7)], - accepts_no_kwarg: [8, CType::BitField.new(1, 0)], - ruby2_keywords: [9, CType::BitField.new(1, 1)], - )], - size: [32, CType::Immediate.new(-4)], - lead_num: [64, CType::Immediate.new(4)], - opt_num: [96, CType::Immediate.new(4)], - rest_start: [128, CType::Immediate.new(4)], - post_start: [160, CType::Immediate.new(4)], - post_num: [192, CType::Immediate.new(4)], - block_start: [224, CType::Immediate.new(4)], - opt_table: [256, CType::Pointer.new { self.VALUE }], - keyword: [320, CType::Pointer.new { self.rb_iseq_param_keyword }], - )], - location: [512, self.rb_iseq_location_t], - insns_info: [960, self.iseq_insn_info], - local_table: [1216, CType::Pointer.new { self.ID }], - catch_table: [1280, CType::Pointer.new { self.iseq_catch_table }], - parent_iseq: [1344, CType::Pointer.new { self.rb_iseq_struct }], - local_iseq: [1408, CType::Pointer.new { self.rb_iseq_struct }], - is_entries: [1472, CType::Pointer.new { self.iseq_inline_storage_entry }], - call_data: [1536, CType::Pointer.new { self.rb_call_data }], - variable: [1600, CType::Struct.new( - "", 40, - flip_count: [0, self.rb_snum_t], - script_lines: [64, self.VALUE], - coverage: [128, self.VALUE], - pc2branchindex: [192, self.VALUE], - original_iseq: [256, CType::Pointer.new { self.VALUE }], - )], - local_table_size: [1920, CType::Immediate.new(-4)], - ic_size: [1952, CType::Immediate.new(-4)], - ise_size: [1984, CType::Immediate.new(-4)], - ivc_size: [2016, CType::Immediate.new(-4)], - icvarc_size: [2048, CType::Immediate.new(-4)], - ci_size: [2080, CType::Immediate.new(-4)], - stack_max: [2112, CType::Immediate.new(-4)], - mark_bits: [2176, CType::Union.new( - "", 8, - list: CType::Pointer.new { self.iseq_bits_t }, - single: self.iseq_bits_t, - )], - catch_except_p: [2240, self._Bool], - builtin_inline_p: [2248, self._Bool], - outer_variables: [2304, CType::Pointer.new { self.rb_id_table }], - mandatory_only_iseq: [2368, CType::Pointer.new { self.rb_iseq_t }], - jit_func: [2432, CType::Immediate.new(1)], - total_calls: [2496, CType::Immediate.new(-5)], - jit_unit: [2560, CType::Pointer.new { self.rb_mjit_unit }], - yjit_payload: [2624, CType::Pointer.new { CType::Immediate.new(0) }], - ) - end - - def C.rb_iseq_location_t - @rb_iseq_location_t ||= CType::Struct.new( - "rb_iseq_location_struct", 56, - pathobj: [0, self.VALUE, true], - base_label: [64, self.VALUE, true], - label: [128, self.VALUE, true], - first_lineno: [192, self.VALUE, true], - node_id: [256, CType::Immediate.new(4)], - code_location: [288, self.rb_code_location_t], - ) - end - - def C.rb_iseq_struct - @rb_iseq_struct ||= CType::Struct.new( - "rb_iseq_struct", 40, - flags: [0, self.VALUE], - wrapper: [64, self.VALUE], - body: [128, CType::Pointer.new { self.rb_iseq_constant_body }], - aux: [192, CType::Union.new( - "", 16, - compile_data: CType::Pointer.new { self.iseq_compile_data }, - loader: CType::Struct.new( - "", 16, - obj: [0, self.VALUE], - index: [64, CType::Immediate.new(4)], - ), - exec: CType::Struct.new( - "", 16, - local_hooks: [0, CType::Pointer.new { self.rb_hook_list_struct }], - global_trace_events: [64, self.rb_event_flag_t], - ), - )], - ) - end - - def C.rb_iseq_t - @rb_iseq_t ||= self.rb_iseq_struct - end - - def C.rb_iv_index_tbl_entry - @rb_iv_index_tbl_entry ||= CType::Struct.new( - "rb_iv_index_tbl_entry", 24, - index: [0, CType::Immediate.new(-4)], - class_serial: [64, self.rb_serial_t], - class_value: [128, self.VALUE], - ) - end - - def C.rb_method_definition_struct - @rb_method_definition_struct ||= CType::Struct.new( - "rb_method_definition_struct", 48, - type: [0, self.rb_method_type_t], - iseq_overload: [4, CType::BitField.new(1, 4)], - alias_count: [5, CType::BitField.new(27, 5)], - complemented_count: [32, CType::BitField.new(28, 0)], - no_redef_warning: [60, CType::BitField.new(1, 4)], - body: [64, CType::Union.new( - "", 24, - iseq: self.rb_method_iseq_t, - cfunc: self.rb_method_cfunc_t, - attr: self.rb_method_attr_t, - alias: self.rb_method_alias_t, - refined: self.rb_method_refined_t, - bmethod: self.rb_method_bmethod_t, - optimized: self.rb_method_optimized_t, - )], - original_id: [256, self.ID], - method_serial: [320, CType::Immediate.new(-5)], - ) - end - - def C.rb_method_iseq_t - @rb_method_iseq_t ||= CType::Struct.new( - "rb_method_iseq_struct", 16, - iseqptr: [0, CType::Pointer.new { self.rb_iseq_t }], - cref: [64, CType::Pointer.new { self.rb_cref_t }], - ) - end - - def C.rb_method_type_t - @rb_method_type_t ||= CType::Immediate.new(4) - end - - def C.rb_mjit_compile_info - @rb_mjit_compile_info ||= CType::Struct.new( - "rb_mjit_compile_info", 5, - disable_ivar_cache: [0, self._Bool], - disable_exivar_cache: [8, self._Bool], - disable_send_cache: [16, self._Bool], - disable_inlining: [24, self._Bool], - disable_const_cache: [32, self._Bool], - ) - end - - def C.rb_mjit_unit - @rb_mjit_unit ||= CType::Struct.new( - "rb_mjit_unit", 64, - unode: [0, self.ccan_list_node], - id: [128, CType::Immediate.new(4)], - handle: [192, CType::Pointer.new { CType::Immediate.new(0) }], - iseq: [256, CType::Pointer.new { self.rb_iseq_t }], - used_code_p: [320, self._Bool], - compact_p: [328, self._Bool], - compile_info: [336, self.rb_mjit_compile_info], - cc_entries: [384, CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }], - cc_entries_size: [448, CType::Immediate.new(-4)], - ) - end - - def C.rb_serial_t - @rb_serial_t ||= CType::Immediate.new(-6) - end - - def C._Bool = CType::Bool.new - - def C.ID = CType::Stub.new(:ID) - - def C.rb_thread_struct = CType::Stub.new(:rb_thread_struct) - - def C.vm_call_handler = CType::Stub.new(:vm_call_handler) - - def C.method_missing_reason = CType::Stub.new(:method_missing_reason) - - def C.rb_callinfo_kwarg = CType::Stub.new(:rb_callinfo_kwarg) - - def C.rb_cref_struct = CType::Stub.new(:rb_cref_struct) - - def C.rb_scope_visibility_t = CType::Stub.new(:rb_scope_visibility_t) - - def C.rb_iseq_type = CType::Stub.new(:rb_iseq_type) - - def C.rb_iseq_param_keyword = CType::Stub.new(:rb_iseq_param_keyword) - - def C.iseq_insn_info = CType::Stub.new(:iseq_insn_info) - - def C.iseq_catch_table = CType::Stub.new(:iseq_catch_table) - - def C.rb_snum_t = CType::Stub.new(:rb_snum_t) - - def C.iseq_bits_t = CType::Stub.new(:iseq_bits_t) - - def C.rb_id_table = CType::Stub.new(:rb_id_table) - - def C.rb_code_location_t = CType::Stub.new(:rb_code_location_t) - - def C.iseq_compile_data = CType::Stub.new(:iseq_compile_data) - - def C.rb_hook_list_struct = CType::Stub.new(:rb_hook_list_struct) - - def C.rb_event_flag_t = CType::Stub.new(:rb_event_flag_t) - - def C.rb_method_cfunc_t = CType::Stub.new(:rb_method_cfunc_t) - - def C.rb_method_attr_t = CType::Stub.new(:rb_method_attr_t) - - def C.rb_method_alias_t = CType::Stub.new(:rb_method_alias_t) - - def C.rb_method_refined_t = CType::Stub.new(:rb_method_refined_t) - - def C.rb_method_bmethod_t = CType::Stub.new(:rb_method_bmethod_t) - - def C.rb_method_optimized_t = CType::Stub.new(:rb_method_optimized_t) - - def C.ccan_list_node = CType::Stub.new(:ccan_list_node) -end diff --git a/lib/mjit/c_pointer.rb b/lib/mjit/c_pointer.rb index 75ed8babd59a69..7f64a8ac8f4906 100644 --- a/lib/mjit/c_pointer.rb +++ b/lib/mjit/c_pointer.rb @@ -2,20 +2,12 @@ module RubyVM::MJIT # Every class under this namespace is a pointer. Even if the type is # immediate, it shouldn't be dereferenced until `*` is called. module CPointer - # Patched PACK_MAP to support unsigned operations - PACK_MAP = Fiddle::PackInfo::PACK_MAP.dup - PACK_MAP.keys.each do |type| - if type.negative? || type == Fiddle::TYPE_VOIDP - PACK_MAP[type] = PACK_MAP[type].upcase - end - end - # Note: We'd like to avoid alphabetic method names to avoid a conflict # with member methods. to_i and to_s are considered an exception. class Struct # @param name [String] # @param sizeof [Integer] - # @param members [Hash{ Symbol => [Integer, RubyVM::MJIT::CType::*] }] + # @param members [Hash{ Symbol => [RubyVM::MJIT::CType::*, Integer, TrueClass] }] def initialize(addr, sizeof, members) @addr = addr @sizeof = sizeof @@ -42,7 +34,7 @@ def -(struct) # TODO: remove this? # @param member [Symbol] def [](member) - offset, type = @members.fetch(member) + type, offset = @members.fetch(member) type.new(@addr + offset / 8) end @@ -51,7 +43,7 @@ def [](member) # @param member [Symbol] # @param value [Object] def []=(member, value) - offset, type = @members.fetch(member) + type, offset = @members.fetch(member) type[@addr + offset / 8] = value end @@ -69,7 +61,7 @@ def self.define(sizeof, members) super(addr, sizeof, members) end - members.each do |member, (offset, type, to_ruby)| + members.each do |member, (type, offset, to_ruby)| # Intelligent API that does automatic dereference define_method(member) do value = self[member] @@ -188,7 +180,7 @@ def to_s # @param fiddle_type [Integer] Fiddle::TYPE_* def self.define(fiddle_type) size = Fiddle::PackInfo::SIZE_MAP.fetch(fiddle_type) - pack = PACK_MAP.fetch(fiddle_type) + pack = Fiddle::PackInfo::PACK_MAP.fetch(fiddle_type) Class.new(self) do define_method(:initialize) do |addr| @@ -252,13 +244,13 @@ def [](index) # @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself or an object that return an address with to_i def []=(index, value) Fiddle::Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP)[0, Fiddle::SIZEOF_VOIDP] = - [value.to_i].pack(PACK_MAP[Fiddle::TYPE_VOIDP]) + [value.to_i].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP]) end private def dest_addr - Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_VOIDP].unpack1(PACK_MAP[Fiddle::TYPE_VOIDP]) + Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_VOIDP].unpack1(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP]) end def self.define(block) @@ -272,7 +264,7 @@ def self.define(block) # @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself, or an object that return an address with to_i define_singleton_method(:[]=) do |addr, value| value = value.to_i - Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP] = [value].pack(PACK_MAP[Fiddle::TYPE_VOIDP]) + Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP] = [value].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP]) end end end diff --git a/lib/mjit/c_type.rb b/lib/mjit/c_type.rb index be93b36e902f41..9e45d8d41c5379 100644 --- a/lib/mjit/c_type.rb +++ b/lib/mjit/c_type.rb @@ -27,7 +27,7 @@ def self.new(name, sizeof, **members) end module Immediate - # @param fiddle_type [Integer] Fiddle::TYPE_* + # @param fiddle_type [Integer] def self.new(fiddle_type) name = Fiddle.constants.find do |const| const.start_with?('TYPE_') && Fiddle.const_get(const) == fiddle_type.abs @@ -40,6 +40,20 @@ def self.new(fiddle_type) CPointer::Immediate.define(fiddle_type) end end + + # @param type [String] + def self.parse(ctype) + new(Fiddle::Importer.parse_ctype(ctype)) + end + + def self.find(size, signed) + fiddle_type = TYPE_MAP.fetch(size) + fiddle_type = -fiddle_type unless signed + new(fiddle_type) + end + + TYPE_MAP = Fiddle::PackInfo::SIZE_MAP.map { |type, size| [size, type.abs] }.to_h + private_constant :TYPE_MAP end module Bool diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 254ecc885fa6fd..49f28ab6908612 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -785,8 +785,8 @@ def precompile_inlinable_iseqs(f, iseq, status) if C.mjit_opts.verbose >= 1 # print beforehand because ISeq may be GCed during copy job. child_location = child_iseq.body.location - $stderr.puts "JIT inline: #{child_location.label}@#{C.rb_iseq_path(child_iseq)}:#{child_location.first_lineno} " \ - "=> #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{iseq.body.location.first_lineno}" + $stderr.puts "JIT inline: #{child_location.label}@#{C.rb_iseq_path(child_iseq)}:#{C.rb_iseq_first_lineno(child_iseq)} " \ + "=> #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{C.rb_iseq_first_lineno(iseq)}" end if !precompile_inlinable_child_iseq(f, child_iseq, status, ci, cc, pos) return false diff --git a/lib/mkmf.rb b/lib/mkmf.rb index efe3419fd700fb..61ca9e53acdd11 100644 --- a/lib/mkmf.rb +++ b/lib/mkmf.rb @@ -2599,6 +2599,7 @@ def init_mkmf(config = CONFIG, rbconfig = RbConfig::CONFIG) $INCFLAGS << " -I$(hdrdir)/ruby/backward" unless $extmk $INCFLAGS << " -I$(hdrdir) -I$(srcdir)" $DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup + config_string("ADDITIONAL_DLDFLAGS") {|flags| $DLDFLAGS << " " << flags} unless $extmk $LIBEXT = config['LIBEXT'].dup $OBJEXT = config["OBJEXT"].dup $EXEEXT = config["EXEEXT"].dup diff --git a/lib/net/http.rb b/lib/net/http.rb index a583441253c092..7e89409c1bb496 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1221,16 +1221,9 @@ def proxy_port end end - # [Bug #12921] - if /linux|freebsd|darwin/ =~ RUBY_PLATFORM - ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = true - else - ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = false - end - # The username of the proxy server, if one is configured. def proxy_user - if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env + if @proxy_from_env user = proxy_uri&.user unescape(user) if user else @@ -1240,7 +1233,7 @@ def proxy_user # The password of the proxy server, if one is configured. def proxy_pass - if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env + if @proxy_from_env pass = proxy_uri&.password unescape(pass) if pass else diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 3e0aac759a24b3..8153aaba05574e 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -1430,7 +1430,7 @@ def editing_mode if @waiting_operator_proc if VI_MOTIONS.include?(method_symbol) old_cursor, old_byte_pointer = @cursor, @byte_pointer - @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1 + @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1 block.(true) unless @waiting_proc cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb index 9d49138ae71af1..8d31d85b443c6d 100644 --- a/lib/rubygems/command_manager.rb +++ b/lib/rubygems/command_manager.rb @@ -73,8 +73,8 @@ class Gem::CommandManager ].freeze ALIAS_COMMANDS = { - "i" => "install", - "login" => "signin", + "i" => "install", + "login" => "signin", "logout" => "signout", }.freeze diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb index 3a8c435d0e4711..5eb45d259c0c0b 100644 --- a/lib/rubygems/commands/fetch_command.rb +++ b/lib/rubygems/commands/fetch_command.rb @@ -10,7 +10,7 @@ class Gem::Commands::FetchCommand < Gem::Command def initialize defaults = { :suggest_alternate => true, - :version => Gem::Requirement.default, + :version => Gem::Requirement.default, } super "fetch", "Download a gem and place it in the current directory", defaults diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb index 724b4fe51dac1c..071687c63fe46a 100644 --- a/lib/rubygems/commands/install_command.rb +++ b/lib/rubygems/commands/install_command.rb @@ -21,10 +21,10 @@ class Gem::Commands::InstallCommand < Gem::Command def initialize defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ :format_executable => false, - :lock => true, + :lock => true, :suggest_alternate => true, - :version => Gem::Requirement.default, - :without_groups => [], + :version => Gem::Requirement.default, + :without_groups => [], }) defaults.merge!(install_update_options) diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index c782c3618cf480..37c9b4ada1ae94 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -576,7 +576,7 @@ def uninstall_old_gemcutter require_relative "../uninstaller" ui = Gem::Uninstaller.new("gemcutter", :all => true, :ignore => true, - :version => "< 0.4") + :version => "< 0.4") ui.uninstall rescue Gem::InstallError end diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb index a365e8541659f8..b1f939b0bc7b85 100644 --- a/lib/rubygems/commands/unpack_command.rb +++ b/lib/rubygems/commands/unpack_command.rb @@ -21,7 +21,7 @@ def initialize super "unpack", "Unpack an installed gem to the current directory", :version => Gem::Requirement.default, - :target => Dir.pwd + :target => Dir.pwd add_option("--target=DIR", "target directory for unpacking") do |value, options| diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb index aa7b66d36bbfa3..1009376b903040 100644 --- a/lib/rubygems/dependency_installer.rb +++ b/lib/rubygems/dependency_installer.rb @@ -16,16 +16,16 @@ class Gem::DependencyInstaller extend Gem::Deprecate DEFAULT_OPTIONS = { # :nodoc: - :env_shebang => false, - :document => %w[ri], - :domain => :both, # HACK dup - :force => false, - :format_executable => false, # HACK dup + :env_shebang => false, + :document => %w[ri], + :domain => :both, # HACK dup + :force => false, + :format_executable => false, # HACK dup :ignore_dependencies => false, - :prerelease => false, - :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low? - :wrappers => true, - :build_args => nil, + :prerelease => false, + :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low? + :wrappers => true, + :build_args => nil, :build_docs_in_background => false, :install_as_default => false, }.freeze @@ -230,22 +230,22 @@ def install(dep_or_name, version = Gem::Requirement.default) @installed_gems = [] options = { - :bin_dir => @bin_dir, - :build_args => @build_args, - :document => @document, - :env_shebang => @env_shebang, - :force => @force, - :format_executable => @format_executable, + :bin_dir => @bin_dir, + :build_args => @build_args, + :document => @document, + :env_shebang => @env_shebang, + :force => @force, + :format_executable => @format_executable, :ignore_dependencies => @ignore_dependencies, - :prerelease => @prerelease, - :security_policy => @security_policy, - :user_install => @user_install, - :wrappers => @wrappers, - :build_root => @build_root, - :install_as_default => @install_as_default, - :dir_mode => @dir_mode, - :data_mode => @data_mode, - :prog_mode => @prog_mode, + :prerelease => @prerelease, + :security_policy => @security_policy, + :user_install => @user_install, + :wrappers => @wrappers, + :build_root => @build_root, + :install_as_default => @install_as_default, + :dir_mode => @dir_mode, + :data_mode => @data_mode, + :prog_mode => @prog_mode, } options[:install_dir] = @install_dir if @only_install_dir diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb index 39653462e90686..d0061ff82ee23c 100644 --- a/lib/rubygems/indexer.rb +++ b/lib/rubygems/indexer.rb @@ -397,7 +397,7 @@ def update_index dst_name = File.join @dest_directory, file # REFACTOR: duped above FileUtils.mv src_name, dst_name, :verbose => verbose, - :force => true + :force => true File.utime newest_mtime, newest_mtime, dst_name end diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 531ca8716eeda1..9fbb2824c72019 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -649,9 +649,9 @@ def ensure_dependencies_met # :nodoc: def process_options # :nodoc: @options = { - :bin_dir => nil, - :env_shebang => false, - :force => false, + :bin_dir => nil, + :env_shebang => false, + :force => false, :only_install_dir => false, :post_install_message => true, }.merge options diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb index 084dc5d2d9523a..4672866985f866 100644 --- a/lib/rubygems/package.rb +++ b/lib/rubygems/package.rb @@ -444,10 +444,10 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc: directories << mkdir end - File.open destination, "wb" do |out| - out.write entry.read + if entry.file? + File.open(destination, "wb") {|out| out.write entry.read } FileUtils.chmod file_mode(entry.header.mode), destination - end if entry.file? + end verbose destination end @@ -467,7 +467,12 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc: end def file_mode(mode) # :nodoc: - ((mode & 0111).zero? ? data_mode : prog_mode) || mode + ((mode & 0111).zero? ? data_mode : prog_mode) || + # If we're not using one of the default modes, then we're going to fall + # back to the mode from the tarball. In this case we need to mask it down + # to fit into 2^16 bits (the maximum value for a mode in CRuby since it + # gets put into an unsigned short). + (mode & ((1 << 16) - 1)) end ## diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb index ee515a9e050f5a..590a2f03155f8d 100644 --- a/lib/rubygems/package/tar_header.rb +++ b/lib/rubygems/package/tar_header.rb @@ -103,22 +103,22 @@ def self.from(stream) fields = header.unpack UNPACK_FORMAT - new :name => fields.shift, - :mode => strict_oct(fields.shift), - :uid => oct_or_256based(fields.shift), - :gid => oct_or_256based(fields.shift), - :size => strict_oct(fields.shift), - :mtime => strict_oct(fields.shift), + new :name => fields.shift, + :mode => strict_oct(fields.shift), + :uid => oct_or_256based(fields.shift), + :gid => oct_or_256based(fields.shift), + :size => strict_oct(fields.shift), + :mtime => strict_oct(fields.shift), :checksum => strict_oct(fields.shift), :typeflag => fields.shift, :linkname => fields.shift, - :magic => fields.shift, - :version => strict_oct(fields.shift), - :uname => fields.shift, - :gname => fields.shift, + :magic => fields.shift, + :version => strict_oct(fields.shift), + :uname => fields.shift, + :gname => fields.shift, :devmajor => strict_oct(fields.shift), :devminor => strict_oct(fields.shift), - :prefix => fields.shift, + :prefix => fields.shift, :empty => empty end diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index 06de5ded8da6c9..1dacc596c47be4 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -22,6 +22,7 @@ def self.match(platform) end def self.match_platforms?(platform, platforms) + platform = Gem::Platform.new(platform) unless platform.is_a?(Gem::Platform) platforms.any? do |local_platform| platform.nil? || local_platform == platform || diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb index fe75ac5208de5c..693cd2793a986a 100644 --- a/lib/rubygems/request_set/gem_dependency_api.rb +++ b/lib/rubygems/request_set/gem_dependency_api.rb @@ -32,22 +32,22 @@ class Gem::RequestSet::GemDependencyAPI ENGINE_MAP = { # :nodoc: - :jruby => %w[jruby], - :jruby_18 => %w[jruby], - :jruby_19 => %w[jruby], - :maglev => %w[maglev], - :mri => %w[ruby], - :mri_18 => %w[ruby], - :mri_19 => %w[ruby], - :mri_20 => %w[ruby], - :mri_21 => %w[ruby], - :rbx => %w[rbx], - :truffleruby => %w[truffleruby], - :ruby => %w[ruby rbx maglev truffleruby], - :ruby_18 => %w[ruby rbx maglev truffleruby], - :ruby_19 => %w[ruby rbx maglev truffleruby], - :ruby_20 => %w[ruby rbx maglev truffleruby], - :ruby_21 => %w[ruby rbx maglev truffleruby], + :jruby => %w[jruby], + :jruby_18 => %w[jruby], + :jruby_19 => %w[jruby], + :maglev => %w[maglev], + :mri => %w[ruby], + :mri_18 => %w[ruby], + :mri_19 => %w[ruby], + :mri_20 => %w[ruby], + :mri_21 => %w[ruby], + :rbx => %w[rbx], + :truffleruby => %w[truffleruby], + :ruby => %w[ruby rbx maglev truffleruby], + :ruby_18 => %w[ruby rbx maglev truffleruby], + :ruby_19 => %w[ruby rbx maglev truffleruby], + :ruby_20 => %w[ruby rbx maglev truffleruby], + :ruby_21 => %w[ruby rbx maglev truffleruby], }.freeze mswin = Gem::Platform.new "x86-mswin32" @@ -56,37 +56,37 @@ class Gem::RequestSet::GemDependencyAPI x64_mingw = Gem::Platform.new "x64-mingw32" PLATFORM_MAP = { # :nodoc: - :jruby => Gem::Platform::RUBY, - :jruby_18 => Gem::Platform::RUBY, - :jruby_19 => Gem::Platform::RUBY, - :maglev => Gem::Platform::RUBY, - :mingw => x86_mingw, - :mingw_18 => x86_mingw, - :mingw_19 => x86_mingw, - :mingw_20 => x86_mingw, - :mingw_21 => x86_mingw, - :mri => Gem::Platform::RUBY, - :mri_18 => Gem::Platform::RUBY, - :mri_19 => Gem::Platform::RUBY, - :mri_20 => Gem::Platform::RUBY, - :mri_21 => Gem::Platform::RUBY, - :mswin => mswin, - :mswin_18 => mswin, - :mswin_19 => mswin, - :mswin_20 => mswin, - :mswin_21 => mswin, - :mswin64 => mswin64, - :mswin64_19 => mswin64, - :mswin64_20 => mswin64, - :mswin64_21 => mswin64, - :rbx => Gem::Platform::RUBY, - :ruby => Gem::Platform::RUBY, - :ruby_18 => Gem::Platform::RUBY, - :ruby_19 => Gem::Platform::RUBY, - :ruby_20 => Gem::Platform::RUBY, - :ruby_21 => Gem::Platform::RUBY, - :truffleruby => Gem::Platform::RUBY, - :x64_mingw => x64_mingw, + :jruby => Gem::Platform::RUBY, + :jruby_18 => Gem::Platform::RUBY, + :jruby_19 => Gem::Platform::RUBY, + :maglev => Gem::Platform::RUBY, + :mingw => x86_mingw, + :mingw_18 => x86_mingw, + :mingw_19 => x86_mingw, + :mingw_20 => x86_mingw, + :mingw_21 => x86_mingw, + :mri => Gem::Platform::RUBY, + :mri_18 => Gem::Platform::RUBY, + :mri_19 => Gem::Platform::RUBY, + :mri_20 => Gem::Platform::RUBY, + :mri_21 => Gem::Platform::RUBY, + :mswin => mswin, + :mswin_18 => mswin, + :mswin_19 => mswin, + :mswin_20 => mswin, + :mswin_21 => mswin, + :mswin64 => mswin64, + :mswin64_19 => mswin64, + :mswin64_20 => mswin64, + :mswin64_21 => mswin64, + :rbx => Gem::Platform::RUBY, + :ruby => Gem::Platform::RUBY, + :ruby_18 => Gem::Platform::RUBY, + :ruby_19 => Gem::Platform::RUBY, + :ruby_20 => Gem::Platform::RUBY, + :ruby_21 => Gem::Platform::RUBY, + :truffleruby => Gem::Platform::RUBY, + :x64_mingw => x64_mingw, :x64_mingw_20 => x64_mingw, :x64_mingw_21 => x64_mingw, }.freeze @@ -98,68 +98,68 @@ class Gem::RequestSet::GemDependencyAPI tilde_gt_2_1_0 = Gem::Requirement.new "~> 2.1.0" VERSION_MAP = { # :nodoc: - :jruby => gt_eq_0, - :jruby_18 => tilde_gt_1_8_0, - :jruby_19 => tilde_gt_1_9_0, - :maglev => gt_eq_0, - :mingw => gt_eq_0, - :mingw_18 => tilde_gt_1_8_0, - :mingw_19 => tilde_gt_1_9_0, - :mingw_20 => tilde_gt_2_0_0, - :mingw_21 => tilde_gt_2_1_0, - :mri => gt_eq_0, - :mri_18 => tilde_gt_1_8_0, - :mri_19 => tilde_gt_1_9_0, - :mri_20 => tilde_gt_2_0_0, - :mri_21 => tilde_gt_2_1_0, - :mswin => gt_eq_0, - :mswin_18 => tilde_gt_1_8_0, - :mswin_19 => tilde_gt_1_9_0, - :mswin_20 => tilde_gt_2_0_0, - :mswin_21 => tilde_gt_2_1_0, - :mswin64 => gt_eq_0, - :mswin64_19 => tilde_gt_1_9_0, - :mswin64_20 => tilde_gt_2_0_0, - :mswin64_21 => tilde_gt_2_1_0, - :rbx => gt_eq_0, - :ruby => gt_eq_0, - :ruby_18 => tilde_gt_1_8_0, - :ruby_19 => tilde_gt_1_9_0, - :ruby_20 => tilde_gt_2_0_0, - :ruby_21 => tilde_gt_2_1_0, - :truffleruby => gt_eq_0, - :x64_mingw => gt_eq_0, + :jruby => gt_eq_0, + :jruby_18 => tilde_gt_1_8_0, + :jruby_19 => tilde_gt_1_9_0, + :maglev => gt_eq_0, + :mingw => gt_eq_0, + :mingw_18 => tilde_gt_1_8_0, + :mingw_19 => tilde_gt_1_9_0, + :mingw_20 => tilde_gt_2_0_0, + :mingw_21 => tilde_gt_2_1_0, + :mri => gt_eq_0, + :mri_18 => tilde_gt_1_8_0, + :mri_19 => tilde_gt_1_9_0, + :mri_20 => tilde_gt_2_0_0, + :mri_21 => tilde_gt_2_1_0, + :mswin => gt_eq_0, + :mswin_18 => tilde_gt_1_8_0, + :mswin_19 => tilde_gt_1_9_0, + :mswin_20 => tilde_gt_2_0_0, + :mswin_21 => tilde_gt_2_1_0, + :mswin64 => gt_eq_0, + :mswin64_19 => tilde_gt_1_9_0, + :mswin64_20 => tilde_gt_2_0_0, + :mswin64_21 => tilde_gt_2_1_0, + :rbx => gt_eq_0, + :ruby => gt_eq_0, + :ruby_18 => tilde_gt_1_8_0, + :ruby_19 => tilde_gt_1_9_0, + :ruby_20 => tilde_gt_2_0_0, + :ruby_21 => tilde_gt_2_1_0, + :truffleruby => gt_eq_0, + :x64_mingw => gt_eq_0, :x64_mingw_20 => tilde_gt_2_0_0, :x64_mingw_21 => tilde_gt_2_1_0, }.freeze WINDOWS = { # :nodoc: - :mingw => :only, - :mingw_18 => :only, - :mingw_19 => :only, - :mingw_20 => :only, - :mingw_21 => :only, - :mri => :never, - :mri_18 => :never, - :mri_19 => :never, - :mri_20 => :never, - :mri_21 => :never, - :mswin => :only, - :mswin_18 => :only, - :mswin_19 => :only, - :mswin_20 => :only, - :mswin_21 => :only, - :mswin64 => :only, - :mswin64_19 => :only, - :mswin64_20 => :only, - :mswin64_21 => :only, - :rbx => :never, - :ruby => :never, - :ruby_18 => :never, - :ruby_19 => :never, - :ruby_20 => :never, - :ruby_21 => :never, - :x64_mingw => :only, + :mingw => :only, + :mingw_18 => :only, + :mingw_19 => :only, + :mingw_20 => :only, + :mingw_21 => :only, + :mri => :never, + :mri_18 => :never, + :mri_19 => :never, + :mri_20 => :never, + :mri_21 => :never, + :mswin => :only, + :mswin_18 => :only, + :mswin_19 => :only, + :mswin_20 => :only, + :mswin_21 => :only, + :mswin64 => :only, + :mswin64_19 => :only, + :mswin64_20 => :only, + :mswin64_21 => :only, + :rbx => :never, + :ruby => :never, + :ruby_18 => :never, + :ruby_19 => :never, + :ruby_20 => :never, + :ruby_21 => :never, + :x64_mingw => :only, :x64_mingw_20 => :only, :x64_mingw_21 => :only, }.freeze diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 4f19b8c5b0362d..64f9ac3465a8a9 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -10,13 +10,13 @@ class Gem::Requirement OPS = { #:nodoc: - "=" => lambda {|v, r| v == r }, - "!=" => lambda {|v, r| v != r }, - ">" => lambda {|v, r| v > r }, - "<" => lambda {|v, r| v < r }, - ">=" => lambda {|v, r| v >= r }, - "<=" => lambda {|v, r| v <= r }, - "~>" => lambda {|v, r| v >= r && v.release < r.bump }, + "=" => lambda {|v, r| v == r }, + "!=" => lambda {|v, r| v != r }, + ">" => lambda {|v, r| v > r }, + "<" => lambda {|v, r| v < r }, + ">=" => lambda {|v, r| v >= r }, + "<=" => lambda {|v, r| v <= r }, + "~>" => lambda {|v, r| v >= r && v.release < r.bump }, }.freeze SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc: diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb index bf7d6d943b8fb7..76d1e9d0cc7426 100644 --- a/lib/rubygems/resolver.rb +++ b/lib/rubygems/resolver.rb @@ -246,7 +246,7 @@ def search_for(dependency) sources.each do |source| groups[source]. - sort_by {|spec| [spec.version, Gem::Platform.local =~ spec.platform ? 1 : 0] }. + sort_by {|spec| [spec.version, spec.platform =~ Gem::Platform.local ? 1 : 0] }. map {|spec| ActivationRequest.new spec, dependency }. each {|activation_request| activation_requests << activation_request } end diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb index dd16283a982296..2ba9562fd48fbb 100644 --- a/lib/rubygems/security.rb +++ b/lib/rubygems/security.rb @@ -376,8 +376,8 @@ class Exception < Gem::Exception; end # * The certificate contains a subject key identifier EXTENSIONS = { - "basicConstraints" => "CA:FALSE", - "keyUsage" => + "basicConstraints" => "CA:FALSE", + "keyUsage" => "keyEncipherment,dataEncipherment,digitalSignature", "subjectKeyIdentifier" => "hash", }.freeze diff --git a/lib/rubygems/security/policies.rb b/lib/rubygems/security/policies.rb index b3f9070394d44a..d28005223e9b8d 100644 --- a/lib/rubygems/security/policies.rb +++ b/lib/rubygems/security/policies.rb @@ -6,12 +6,12 @@ module Gem::Security NoSecurity = Policy.new( "No Security", - :verify_data => false, - :verify_signer => false, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + :verify_data => false, + :verify_signer => false, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false ) ## @@ -24,12 +24,12 @@ module Gem::Security AlmostNoSecurity = Policy.new( "Almost No Security", - :verify_data => true, - :verify_signer => false, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + :verify_data => true, + :verify_signer => false, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false ) ## @@ -41,12 +41,12 @@ module Gem::Security LowSecurity = Policy.new( "Low Security", - :verify_data => true, - :verify_signer => true, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + :verify_data => true, + :verify_signer => true, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false ) ## @@ -60,12 +60,12 @@ module Gem::Security MediumSecurity = Policy.new( "Medium Security", - :verify_data => true, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => true, - :only_signed => false + :verify_data => true, + :verify_signer => true, + :verify_chain => true, + :verify_root => true, + :only_trusted => true, + :only_signed => false ) ## @@ -79,12 +79,12 @@ module Gem::Security HighSecurity = Policy.new( "High Security", - :verify_data => true, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => true, - :only_signed => true + :verify_data => true, + :verify_signer => true, + :verify_chain => true, + :verify_root => true, + :only_trusted => true, + :only_signed => true ) ## @@ -92,23 +92,23 @@ module Gem::Security SigningPolicy = Policy.new( "Signing Policy", - :verify_data => false, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => false, - :only_signed => false + :verify_data => false, + :verify_signer => true, + :verify_chain => true, + :verify_root => true, + :only_trusted => false, + :only_signed => false ) ## # Hash of configured security policies Policies = { - "NoSecurity" => NoSecurity, + "NoSecurity" => NoSecurity, "AlmostNoSecurity" => AlmostNoSecurity, - "LowSecurity" => LowSecurity, - "MediumSecurity" => MediumSecurity, - "HighSecurity" => HighSecurity, + "LowSecurity" => LowSecurity, + "MediumSecurity" => MediumSecurity, + "HighSecurity" => HighSecurity, # SigningPolicy is not intended for use by `gem -P` so do not list it }.freeze diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb index a6882c66e7f185..df59680d845100 100644 --- a/lib/rubygems/security/trust_dir.rb +++ b/lib/rubygems/security/trust_dir.rb @@ -8,7 +8,7 @@ class Gem::Security::TrustDir # Default permissions for the trust directory and its contents DEFAULT_PERMISSIONS = { - :trust_dir => 0700, + :trust_dir => 0700, :trusted_cert => 0600, }.freeze diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb index fc72a1038aa68a..aa0cbc1641e59e 100644 --- a/lib/rubygems/source.rb +++ b/lib/rubygems/source.rb @@ -12,8 +12,8 @@ class Gem::Source include Gem::Text FILES = { # :nodoc: - :released => "specs", - :latest => "latest_specs", + :released => "specs", + :latest => "latest_specs", :prerelease => "prerelease_specs", }.freeze diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index af07cd36e25e81..5175db2d84403a 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -75,7 +75,7 @@ class Gem::Specification < Gem::BasicSpecification SPECIFICATION_VERSION_HISTORY = { # :nodoc: -1 => ["(RubyGems versions up to and including 0.7 did not have versioned specifications)"], - 1 => [ + 1 => [ 'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"', '"test_file=x" is a shortcut for "test_files=[x]"', ], @@ -93,10 +93,10 @@ class Gem::Specification < Gem::BasicSpecification MARSHAL_FIELDS = { # :nodoc: -1 => 16, - 1 => 16, - 2 => 16, - 3 => 17, - 4 => 18, + 1 => 16, + 2 => 16, + 3 => 17, + 4 => 18, }.freeze today = Time.now.utc @@ -124,35 +124,35 @@ class Gem::Specification < Gem::BasicSpecification # Map of attribute names to default values. @@default_value = { - :authors => [], - :autorequire => nil, - :bindir => "bin", - :cert_chain => [], - :date => nil, - :dependencies => [], - :description => nil, - :email => nil, - :executables => [], - :extensions => [], - :extra_rdoc_files => [], - :files => [], - :homepage => nil, - :licenses => [], - :metadata => {}, - :name => nil, - :platform => Gem::Platform::RUBY, - :post_install_message => nil, - :rdoc_options => [], - :require_paths => ["lib"], - :required_ruby_version => Gem::Requirement.default, + :authors => [], + :autorequire => nil, + :bindir => "bin", + :cert_chain => [], + :date => nil, + :dependencies => [], + :description => nil, + :email => nil, + :executables => [], + :extensions => [], + :extra_rdoc_files => [], + :files => [], + :homepage => nil, + :licenses => [], + :metadata => {}, + :name => nil, + :platform => Gem::Platform::RUBY, + :post_install_message => nil, + :rdoc_options => [], + :require_paths => ["lib"], + :required_ruby_version => Gem::Requirement.default, :required_rubygems_version => Gem::Requirement.default, - :requirements => [], - :rubygems_version => Gem::VERSION, - :signing_key => nil, - :specification_version => CURRENT_SPECIFICATION_VERSION, - :summary => nil, - :test_files => [], - :version => nil, + :requirements => [], + :rubygems_version => Gem::VERSION, + :signing_key => nil, + :specification_version => CURRENT_SPECIFICATION_VERSION, + :summary => nil, + :test_files => [], + :version => nil, }.freeze # rubocop:disable Style/MutableConstant diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb index 33b4f45b0a0717..512ca9143d92d2 100644 --- a/lib/rubygems/stub_specification.rb +++ b/lib/rubygems/stub_specification.rb @@ -19,9 +19,9 @@ class StubLine # :nodoc: all # These are common require paths. REQUIRE_PATHS = { # :nodoc: - "lib" => "lib".freeze, + "lib" => "lib".freeze, "test" => "test".freeze, - "ext" => "ext".freeze, + "ext" => "ext".freeze, }.freeze # These are common require path lists. This hash is used to optimize diff --git a/lib/syntax_suggest/capture_code_context.rb b/lib/syntax_suggest/capture_code_context.rb index c74a366a258d3f..6439af4ddeb8e8 100644 --- a/lib/syntax_suggest/capture_code_context.rb +++ b/lib/syntax_suggest/capture_code_context.rb @@ -137,7 +137,7 @@ def capture_before_after_kws(block) # puts "woof" # 3 # end # 4 # - # However due to https://github.com/zombocom/syntax_suggest/issues/32 + # However due to https://github.com/ruby/syntax_suggest/issues/32 # the problem line will be identified as: # # ❯ class Dog # 1 diff --git a/lib/syntax_suggest/pathname_from_message.rb b/lib/syntax_suggest/pathname_from_message.rb index ea1a90856e90ed..b6fe1617be0afa 100644 --- a/lib/syntax_suggest/pathname_from_message.rb +++ b/lib/syntax_suggest/pathname_from_message.rb @@ -4,7 +4,7 @@ module SyntaxSuggest # Converts a SyntaxError message to a path # # Handles the case where the filename has a colon in it - # such as on a windows file system: https://github.com/zombocom/syntax_suggest/issues/111 + # such as on a windows file system: https://github.com/ruby/syntax_suggest/issues/111 # # Example: # diff --git a/lib/syntax_suggest/syntax_suggest.gemspec b/lib/syntax_suggest/syntax_suggest.gemspec index acf9be7710150e..73b25c6a5faa39 100644 --- a/lib/syntax_suggest/syntax_suggest.gemspec +++ b/lib/syntax_suggest/syntax_suggest.gemspec @@ -14,12 +14,12 @@ Gem::Specification.new do |spec| spec.summary = "Find syntax errors in your source in a snap" spec.description = 'When you get an "unexpected end" in your syntax this gem helps you find it' - spec.homepage = "https://github.com/zombocom/syntax_suggest.git" + spec.homepage = "https://github.com/ruby/syntax_suggest.git" spec.license = "MIT" spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0") spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = "https://github.com/zombocom/syntax_suggest.git" + spec.metadata["source_code_uri"] = "https://github.com/ruby/syntax_suggest.git" # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. diff --git a/lib/timeout.rb b/lib/timeout.rb index 2aad1d746543be..badba9a39797b6 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -120,6 +120,7 @@ def self.create_timeout_thread requests.reject!(&:done?) end end + ThreadGroup::Default.add(watcher) watcher.name = "Timeout stdlib thread" watcher.thread_variable_set(:"\0__detached_thread__", true) watcher diff --git a/marshal.c b/marshal.c index 9e2f1c4381d786..e4b40c0607b608 100644 --- a/marshal.c +++ b/marshal.c @@ -1554,7 +1554,7 @@ r_symreal(struct load_arg *arg, int ivar) } if (idx > 0) { rb_enc_associate_index(s, idx); - if (rb_enc_str_coderange(s) == ENC_CODERANGE_BROKEN) { + if (is_broken_string(s)) { rb_raise(rb_eArgError, "invalid byte sequence in %s: %+"PRIsVALUE, rb_enc_name(rb_enc_from_index(idx)), s); } diff --git a/method.h b/method.h index 16d212a1c802f4..d33ab5053cbcaa 100644 --- a/method.h +++ b/method.h @@ -226,6 +226,7 @@ const rb_method_entry_t *rb_resolve_me_location(const rb_method_entry_t *, VALUE RUBY_SYMBOL_EXPORT_END const rb_callable_method_entry_t *rb_callable_method_entry(VALUE klass, ID id); +const rb_callable_method_entry_t *rb_callable_method_entry_or_negative(VALUE klass, ID id); const rb_callable_method_entry_t *rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class); const rb_callable_method_entry_t *rb_callable_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class); diff --git a/mini_builtin.c b/mini_builtin.c index 8c8cf66263e59b..c263d1ee71fc3b 100644 --- a/mini_builtin.c +++ b/mini_builtin.c @@ -36,7 +36,7 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta FALSE, /* unsigned int coverage_enabled; */ 0, /* int debug_level; */ }; - const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, INT2FIX(0), NULL, 0, ISEQ_TYPE_TOP, &optimization); + const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization); GET_VM()->builtin_function_table = NULL; rb_ast_dispose(ast); diff --git a/mjit.c b/mjit.c index 1997eaa939ab85..80743a150bdba3 100644 --- a/mjit.c +++ b/mjit.c @@ -707,15 +707,12 @@ mjit_compact(char* c_file) char funcname[MAXPATHLEN]; sprint_funcname(funcname, child_unit); - long iseq_lineno = 0; - if (FIXNUM_P(ISEQ_BODY(child_unit->iseq)->location.first_lineno)) - // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG. - iseq_lineno = FIX2LONG(ISEQ_BODY(child_unit->iseq)->location.first_lineno); + int iseq_lineno = ISEQ_BODY(child_unit->iseq)->location.first_lineno; const char *sep = "@"; const char *iseq_label = RSTRING_PTR(ISEQ_BODY(child_unit->iseq)->location.label); const char *iseq_path = RSTRING_PTR(rb_iseq_path(child_unit->iseq)); if (!iseq_label) iseq_label = sep = ""; - fprintf(f, "\n/* %s%s%s:%ld */\n", iseq_label, sep, iseq_path, iseq_lineno); + fprintf(f, "\n/* %s%s%s:%d */\n", iseq_label, sep, iseq_path, iseq_lineno); success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id); } @@ -875,24 +872,21 @@ mjit_compile_unit(struct rb_mjit_unit *unit) compile_prelude(f); // To make MJIT worker thread-safe against GC.compact, copy ISeq values while `in_jit` is true. - long iseq_lineno = 0; - if (FIXNUM_P(ISEQ_BODY(unit->iseq)->location.first_lineno)) - // FIX2INT may fallback to rb_num2long(), which is a method call and dangerous in MJIT worker. So using only FIX2LONG. - iseq_lineno = FIX2LONG(ISEQ_BODY(unit->iseq)->location.first_lineno); + int iseq_lineno = ISEQ_BODY(unit->iseq)->location.first_lineno; char *iseq_label = alloca(RSTRING_LEN(ISEQ_BODY(unit->iseq)->location.label) + 1); char *iseq_path = alloca(RSTRING_LEN(rb_iseq_path(unit->iseq)) + 1); strcpy(iseq_label, RSTRING_PTR(ISEQ_BODY(unit->iseq)->location.label)); strcpy(iseq_path, RSTRING_PTR(rb_iseq_path(unit->iseq))); - verbose(2, "start compilation: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file); - fprintf(f, "/* %s@%s:%ld */\n\n", iseq_label, iseq_path, iseq_lineno); + verbose(2, "start compilation: %s@%s:%d -> %s", iseq_label, iseq_path, iseq_lineno, c_file); + fprintf(f, "/* %s@%s:%d */\n\n", iseq_label, iseq_path, iseq_lineno); bool success = mjit_compile(f, unit->iseq, funcname, unit->id); fclose(f); if (!success) { if (!mjit_opts.save_temps) remove_file(c_file); - verbose(1, "JIT failure: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file); + verbose(1, "JIT failure: %s@%s:%d -> %s", iseq_label, iseq_path, iseq_lineno, c_file); return 1; } @@ -1372,9 +1366,9 @@ mjit_notify_waitpid(int exit_code) rb_iseq_t *iseq = current_cc_unit->iseq; if ((uintptr_t)func > (uintptr_t)LAST_JIT_ISEQ_FUNC) { double end_time = real_ms_time(); - verbose(1, "JIT success (%.1fms): %s@%s:%ld -> %s", + verbose(1, "JIT success (%.1fms): %s@%s:%d -> %s", end_time - current_cc_ms, RSTRING_PTR(ISEQ_BODY(iseq)->location.label), - RSTRING_PTR(rb_iseq_path(iseq)), FIX2LONG(ISEQ_BODY(iseq)->location.first_lineno), c_file); + RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno, c_file); add_to_list(current_cc_unit, &active_units); } @@ -1531,7 +1525,7 @@ mjit_recompile(const rb_iseq_t *iseq) return; verbose(1, "JIT recompile: %s@%s:%d", RSTRING_PTR(ISEQ_BODY(iseq)->location.label), - RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno)); + RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno); VM_ASSERT(ISEQ_BODY(iseq)->jit_unit != NULL); mjit_add_iseq_to_process(iseq, &ISEQ_BODY(iseq)->jit_unit->compile_info, true); @@ -2011,7 +2005,7 @@ mjit_dump_total_calls(void) ccan_list_for_each(&active_units.head, unit, unode) { const rb_iseq_t *iseq = unit->iseq; fprintf(stderr, "%8ld: %s@%s:%d\n", ISEQ_BODY(iseq)->total_calls, RSTRING_PTR(ISEQ_BODY(iseq)->location.label), - RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(ISEQ_BODY(iseq)->location.first_lineno)); + RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno); } } #endif diff --git a/mjit_c.rb b/mjit_c.rb new file mode 100644 index 00000000000000..d8e5628bda57d4 --- /dev/null +++ b/mjit_c.rb @@ -0,0 +1,705 @@ +# frozen_string_literal: true +# Part of this file is generated by tool/mjit/bindgen.rb. +# Run `make mjit-bindgen` to update code between "MJIT bindgen begin" and "MJIT bindgen end". +module RubyVM::MJIT + C = Object.new + + class << C + def ROBJECT_EMBED_LEN_MAX + Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' + end + + def cdhash_to_hash(cdhash_addr) + Primitive.cdhash_to_hash(cdhash_addr) + end + + def builtin_compiler(f, bf, index, stack_size, builtin_inline_p) + Primitive.builtin_compile(f, bf.to_i, index, stack_size, builtin_inline_p) + end + + def has_cache_for_send(cc, insn) + Primitive.has_cache_for_send(cc.to_i, insn) + end + + def rb_iseq_check(iseq) + _iseq_addr = iseq.to_i + iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseq_check((rb_iseq_t *)NUM2PTR(_iseq_addr)))' + rb_iseq_t.new(iseq_addr) + end + + def rb_iseq_path(iseq) + _iseq_addr = iseq.to_i + Primitive.cexpr! 'rb_iseq_path((rb_iseq_t *)NUM2PTR(_iseq_addr))' + end + + def rb_iseq_first_lineno(iseq) + _iseq_addr = iseq.to_i + Primitive.cexpr! 'rb_iseq_first_lineno((rb_iseq_t *)NUM2PTR(_iseq_addr))' + end + + def vm_ci_argc(ci) + _ci_addr = ci.to_i + Primitive.cexpr! 'UINT2NUM(vm_ci_argc((CALL_INFO)NUM2PTR(_ci_addr)))' + end + + def vm_ci_flag(ci) + _ci_addr = ci.to_i + Primitive.cexpr! 'UINT2NUM(vm_ci_flag((CALL_INFO)NUM2PTR(_ci_addr)))' + end + + def rb_splat_or_kwargs_p(ci) + _ci_addr = ci.to_i + Primitive.cexpr! 'RBOOL(rb_splat_or_kwargs_p((CALL_INFO)NUM2PTR(_ci_addr)))' + end + + def fastpath_applied_iseq_p(ci, cc, iseq) + _ci_addr = ci.to_i + _cc_addr = cc.to_i + _iseq_addr = iseq.to_i + Primitive.cexpr! 'RBOOL(fastpath_applied_iseq_p((CALL_INFO)NUM2PTR(_ci_addr), (CALL_CACHE)NUM2PTR(_cc_addr), (rb_iseq_t *)NUM2PTR(_iseq_addr)))' + end + + def mjit_opts + addr = Primitive.cexpr! 'PTR2NUM((VALUE)&mjit_opts)' + mjit_options.new(addr) + end + + def mjit_call_attribute_sp_inc(insn, operands) + _operands_addr = operands.to_i + Primitive.cexpr! 'LONG2NUM(mjit_call_attribute_sp_inc(NUM2INT(insn), (VALUE *)NUM2PTR(_operands_addr)))' + end + + def mjit_capture_cc_entries(compiled_body, captured_body) + _compiled_body_addr = compiled_body.to_i + _captured_body_addr = captured_body.to_i + Primitive.cexpr! 'INT2NUM(mjit_capture_cc_entries((struct rb_iseq_constant_body *)NUM2PTR(_compiled_body_addr), (struct rb_iseq_constant_body *)NUM2PTR(_captured_body_addr)))' + end + + #const struct rb_iseq_constant_body *body, union iseq_inline_storage_entry *is_entries + def mjit_capture_is_entries(body, is_entries) + _body_addr = body.to_i + _is_entries_addr = is_entries.to_i + Primitive.cstmt! %{ + mjit_capture_is_entries((struct rb_iseq_constant_body *)NUM2PTR(_body_addr), (union iseq_inline_storage_entry *)NUM2PTR(_is_entries_addr)); + return Qnil; + } + end + + # Convert encoded VM pointers to insn BINs. + def rb_vm_insn_decode(encoded) + Primitive.cexpr! 'INT2NUM(rb_vm_insn_decode(NUM2PTR(encoded)))' + end + + # Convert insn BINs to encoded VM pointers. This one is not used by the compiler, but useful for debugging. + def rb_vm_insn_encode(bin) + Primitive.cexpr! 'PTR2NUM((VALUE)rb_vm_get_insns_address_table()[NUM2INT(bin)])' + end + + def insn_may_depend_on_sp_or_pc(insn, opes) + _opes_addr = opes.to_i + Primitive.cexpr! 'RBOOL(insn_may_depend_on_sp_or_pc(NUM2INT(insn), (VALUE *)NUM2PTR(_opes_addr)))' + end + + # Convert Integer VALUE to an actual Ruby object + def to_ruby(value) + Primitive.cexpr! '(VALUE)NUM2PTR(value)' + end + + # Convert RubyVM::InstructionSequence to C.rb_iseq_t. Not used by the compiler, but useful for debugging. + def rb_iseqw_to_iseq(iseqw) + iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseqw_to_iseq(iseqw))' + rb_iseq_t.new(iseq_addr) + end + + # TODO: remove this after migration + def fprintf(f, str) + Primitive.cstmt! %{ + fprintf((FILE *)NUM2PTR(f), "%s", RSTRING_PTR(str)); + return Qnil; + } + end + + def rb_cFalseClass; Primitive.cexpr! 'PTR2NUM(rb_cFalseClass)' end + def rb_cNilClass; Primitive.cexpr! 'PTR2NUM(rb_cNilClass)' end + def rb_cTrueClass; Primitive.cexpr! 'PTR2NUM(rb_cTrueClass)' end + def rb_cInteger; Primitive.cexpr! 'PTR2NUM(rb_cInteger)' end + def rb_cSymbol; Primitive.cexpr! 'PTR2NUM(rb_cSymbol)' end + def rb_cFloat; Primitive.cexpr! 'PTR2NUM(rb_cFloat)' end + end + + ### MJIT bindgen begin ### + + def C.USE_LAZY_LOAD + Primitive.cexpr! %q{ RBOOL(USE_LAZY_LOAD != 0) } + end + + def C.USE_RVARGC + Primitive.cexpr! %q{ RBOOL(USE_RVARGC != 0) } + end + + def C.NOT_COMPILED_STACK_SIZE + Primitive.cexpr! %q{ INT2NUM(NOT_COMPILED_STACK_SIZE) } + end + + def C.VM_CALL_KW_SPLAT + Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT) } + end + + def C.VM_CALL_KW_SPLAT_bit + Primitive.cexpr! %q{ INT2NUM(VM_CALL_KW_SPLAT_bit) } + end + + def C.VM_CALL_TAILCALL + Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL) } + end + + def C.VM_CALL_TAILCALL_bit + Primitive.cexpr! %q{ INT2NUM(VM_CALL_TAILCALL_bit) } + end + + def C.VM_METHOD_TYPE_CFUNC + Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_CFUNC) } + end + + def C.VM_METHOD_TYPE_ISEQ + Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) } + end + + def C.CALL_DATA + @CALL_DATA ||= self.rb_call_data + end + + def C.IC + @IC ||= self.iseq_inline_constant_cache + end + + def C.IVC + @IVC ||= self.iseq_inline_iv_cache_entry + end + + def C.RB_BUILTIN + @RB_BUILTIN ||= self.rb_builtin_function + end + + def C.compile_branch + @compile_branch ||= CType::Struct.new( + "compile_branch", Primitive.cexpr!("SIZEOF(struct compile_branch)"), + stack_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), stack_size)")], + finish_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_branch *)NULL)), finish_p)")], + ) + end + + def C.compile_status + @compile_status ||= CType::Struct.new( + "compile_status", Primitive.cexpr!("SIZEOF(struct compile_status)"), + success: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), success)")], + stack_size_for_pos: [CType::Pointer.new { CType::Immediate.parse("int") }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), stack_size_for_pos)")], + local_stack_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), local_stack_p)")], + is_entries: [CType::Pointer.new { self.iseq_inline_storage_entry }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), is_entries)")], + cc_entries_index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), cc_entries_index)")], + compiled_iseq: [CType::Pointer.new { self.rb_iseq_constant_body }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_iseq)")], + compiled_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_id)")], + compile_info: [CType::Pointer.new { self.rb_mjit_compile_info }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compile_info)")], + merge_ivar_guards_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), merge_ivar_guards_p)")], + ivar_serial: [self.rb_serial_t, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), ivar_serial)")], + max_ivar_index: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), max_ivar_index)")], + inlined_iseqs: [CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inlined_iseqs)")], + inline_context: [self.inlined_call_context, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inline_context)")], + ) + end + + def C.inlined_call_context + @inlined_call_context ||= CType::Struct.new( + "inlined_call_context", Primitive.cexpr!("SIZEOF(struct inlined_call_context)"), + orig_argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), orig_argc)")], + me: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), me)")], + param_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), param_size)")], + local_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct inlined_call_context *)NULL)), local_size)")], + ) + end + + def C.iseq_inline_constant_cache + @iseq_inline_constant_cache ||= CType::Struct.new( + "iseq_inline_constant_cache", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache)"), + entry: [CType::Pointer.new { self.iseq_inline_constant_cache_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), entry)")], + segments: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache *)NULL)), segments)")], + ) + end + + def C.iseq_inline_constant_cache_entry + @iseq_inline_constant_cache_entry ||= CType::Struct.new( + "iseq_inline_constant_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_constant_cache_entry)"), + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), flags)")], + value: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), value)")], + _unused1: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused1)")], + _unused2: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), _unused2)")], + ic_cref: [CType::Pointer.new { self.rb_cref_t }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_constant_cache_entry *)NULL)), ic_cref)")], + ) + end + + def C.iseq_inline_iv_cache_entry + @iseq_inline_iv_cache_entry ||= CType::Struct.new( + "iseq_inline_iv_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_iv_cache_entry)"), + entry: [CType::Pointer.new { self.rb_iv_index_tbl_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), entry)")], + ) + end + + def C.iseq_inline_storage_entry + @iseq_inline_storage_entry ||= CType::Union.new( + "iseq_inline_storage_entry", Primitive.cexpr!("SIZEOF(union iseq_inline_storage_entry)"), + once: CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((union iseq_inline_storage_entry *)NULL)->once)"), + running_thread: [CType::Pointer.new { self.rb_thread_struct }, Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, running_thread)")], + value: [self.VALUE, Primitive.cexpr!("OFFSETOF(((union iseq_inline_storage_entry *)NULL)->once, value)")], + ), + ic_cache: self.iseq_inline_constant_cache, + iv_cache: self.iseq_inline_iv_cache_entry, + ) + end + + def C.mjit_options + @mjit_options ||= CType::Struct.new( + "mjit_options", Primitive.cexpr!("SIZEOF(struct mjit_options)"), + on: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), on)")], + save_temps: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), save_temps)")], + warnings: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), warnings)")], + debug: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug)")], + debug_flags: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug_flags)")], + wait: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), wait)")], + min_calls: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), min_calls)")], + verbose: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), verbose)")], + max_cache_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), max_cache_size)")], + pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), pause)")], + custom: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), custom)")], + ) + end + + def C.rb_builtin_function + @rb_builtin_function ||= CType::Struct.new( + "rb_builtin_function", Primitive.cexpr!("SIZEOF(struct rb_builtin_function)"), + func_ptr: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), func_ptr)")], + argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), argc)")], + index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), index)")], + name: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), name)")], + compiler: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_builtin_function *)NULL)), compiler)")], + ) + end + + def C.rb_call_data + @rb_call_data ||= CType::Struct.new( + "rb_call_data", Primitive.cexpr!("SIZEOF(struct rb_call_data)"), + ci: [CType::Pointer.new { self.rb_callinfo }, Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), ci)")], + cc: [CType::Pointer.new { self.rb_callcache }, Primitive.cexpr!("OFFSETOF((*((struct rb_call_data *)NULL)), cc)")], + ) + end + + def C.rb_callable_method_entry_struct + @rb_callable_method_entry_struct ||= CType::Struct.new( + "rb_callable_method_entry_struct", Primitive.cexpr!("SIZEOF(struct rb_callable_method_entry_struct)"), + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), flags)")], + defined_class: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), defined_class)")], + def: [CType::Pointer.new { self.rb_method_definition_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), def)")], + called_id: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), called_id)")], + owner: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callable_method_entry_struct *)NULL)), owner)")], + ) + end + + def C.rb_callcache + @rb_callcache ||= CType::Struct.new( + "rb_callcache", Primitive.cexpr!("SIZEOF(struct rb_callcache)"), + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), flags)")], + klass: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), klass)")], + cme_: [CType::Pointer.new { self.rb_callable_method_entry_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), cme_)")], + call_: [self.vm_call_handler, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), call_)")], + aux_: [CType::Union.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_)"), + attr_index: CType::Immediate.parse("unsigned int"), + method_missing_reason: self.method_missing_reason, + v: self.VALUE, + ), Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)")], + ) + end + + def C.rb_callinfo + @rb_callinfo ||= CType::Struct.new( + "rb_callinfo", Primitive.cexpr!("SIZEOF(struct rb_callinfo)"), + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flags)")], + kwarg: [CType::Pointer.new { self.rb_callinfo_kwarg }, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), kwarg)")], + mid: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), mid)")], + flag: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), flag)")], + argc: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo *)NULL)), argc)")], + ) + end + + def C.rb_control_frame_t + @rb_control_frame_t ||= CType::Struct.new( + "rb_control_frame_struct", Primitive.cexpr!("SIZEOF(struct rb_control_frame_struct)"), + pc: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), pc)")], + sp: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), sp)")], + iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), iseq)")], + self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), self)")], + ep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), ep)")], + block_code: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), block_code)")], + __bp__: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), __bp__)")], + jit_return: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), jit_return)")], + ) + end + + def C.rb_cref_t + @rb_cref_t ||= CType::Struct.new( + "rb_cref_struct", Primitive.cexpr!("SIZEOF(struct rb_cref_struct)"), + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), flags)")], + refinements: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), refinements)")], + klass_or_self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), klass_or_self)")], + next: [CType::Pointer.new { self.rb_cref_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), next)")], + scope_visi: [self.rb_scope_visibility_t, Primitive.cexpr!("OFFSETOF((*((struct rb_cref_struct *)NULL)), scope_visi)")], + ) + end + + def C.rb_execution_context_struct + @rb_execution_context_struct ||= CType::Struct.new( + "rb_execution_context_struct", Primitive.cexpr!("SIZEOF(struct rb_execution_context_struct)"), + vm_stack: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack)")], + vm_stack_size: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), vm_stack_size)")], + cfp: [CType::Pointer.new { self.rb_control_frame_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), cfp)")], + tag: [CType::Pointer.new { self.rb_vm_tag }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), tag)")], + interrupt_flag: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_flag)")], + interrupt_mask: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), interrupt_mask)")], + fiber_ptr: [CType::Pointer.new { self.rb_fiber_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), fiber_ptr)")], + thread_ptr: [CType::Pointer.new { self.rb_thread_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), thread_ptr)")], + local_storage: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage)")], + local_storage_recursive_hash: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash)")], + local_storage_recursive_hash_for_trace: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), local_storage_recursive_hash_for_trace)")], + root_lep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_lep)")], + root_svar: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), root_svar)")], + ensure_list: [CType::Pointer.new { self.rb_ensure_list_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), ensure_list)")], + trace_arg: [CType::Pointer.new { self.rb_trace_arg_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), trace_arg)")], + errinfo: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), errinfo)")], + passed_block_handler: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), passed_block_handler)")], + raised_flag: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), raised_flag)")], + private_const_reference: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), private_const_reference)")], + machine: [CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_execution_context_struct *)NULL)->machine)"), + stack_start: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_start)")], + stack_end: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_end)")], + stack_maxsize: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF(((struct rb_execution_context_struct *)NULL)->machine, stack_maxsize)")], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_execution_context_struct *)NULL)), machine)")], + ) + end + + def C.rb_execution_context_t + @rb_execution_context_t ||= self.rb_execution_context_struct + end + + def C.rb_iseq_constant_body + @rb_iseq_constant_body ||= CType::Struct.new( + "rb_iseq_constant_body", Primitive.cexpr!("SIZEOF(struct rb_iseq_constant_body)"), + type: [self.rb_iseq_type, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), type)")], + iseq_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_size)")], + iseq_encoded: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), iseq_encoded)")], + param: [CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param)"), + flags: [CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->param.flags)"), + has_lead: [CType::BitField.new(1, 0), 0], + has_opt: [CType::BitField.new(1, 1), 1], + has_rest: [CType::BitField.new(1, 2), 2], + has_post: [CType::BitField.new(1, 3), 3], + has_kw: [CType::BitField.new(1, 4), 4], + has_kwrest: [CType::BitField.new(1, 5), 5], + has_block: [CType::BitField.new(1, 6), 6], + ambiguous_param0: [CType::BitField.new(1, 7), 7], + accepts_no_kwarg: [CType::BitField.new(1, 0), 8], + ruby2_keywords: [CType::BitField.new(1, 1), 9], + ), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, flags)")], + size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, size)")], + lead_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, lead_num)")], + opt_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_num)")], + rest_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, rest_start)")], + post_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_start)")], + post_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, post_num)")], + block_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, block_start)")], + opt_table: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, opt_table)")], + keyword: [CType::Pointer.new { self.rb_iseq_param_keyword }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, keyword)")], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), param)")], + location: [self.rb_iseq_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), location)")], + insns_info: [self.iseq_insn_info, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), insns_info)")], + local_table: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table)")], + catch_table: [CType::Pointer.new { self.iseq_catch_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_table)")], + parent_iseq: [CType::Pointer.new { self.rb_iseq_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), parent_iseq)")], + local_iseq: [CType::Pointer.new { self.rb_iseq_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_iseq)")], + is_entries: [CType::Pointer.new { self.iseq_inline_storage_entry }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), is_entries)")], + call_data: [CType::Pointer.new { self.rb_call_data }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), call_data)")], + variable: [CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->variable)"), + flip_count: [self.rb_snum_t, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, flip_count)")], + script_lines: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, script_lines)")], + coverage: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, coverage)")], + pc2branchindex: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, pc2branchindex)")], + original_iseq: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->variable, original_iseq)")], + ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), variable)")], + local_table_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), local_table_size)")], + ic_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ic_size)")], + ise_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ise_size)")], + ivc_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ivc_size)")], + icvarc_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), icvarc_size)")], + ci_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ci_size)")], + stack_max: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), stack_max)")], + mark_bits: [CType::Union.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->mark_bits)"), + list: CType::Pointer.new { self.iseq_bits_t }, + single: self.iseq_bits_t, + ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mark_bits)")], + catch_except_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), catch_except_p)")], + builtin_inline_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), builtin_inline_p)")], + outer_variables: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), outer_variables)")], + mandatory_only_iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mandatory_only_iseq)")], + jit_func: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)")], + total_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)")], + jit_unit: [CType::Pointer.new { self.rb_mjit_unit }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_unit)")], + ) + end + + def C.rb_iseq_location_t + @rb_iseq_location_t ||= CType::Struct.new( + "rb_iseq_location_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_location_struct)"), + pathobj: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), pathobj)"), true], + base_label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), base_label)"), true], + label: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), label)"), true], + first_lineno: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), first_lineno)"), true], + node_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), node_id)")], + code_location: [self.rb_code_location_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_location_struct *)NULL)), code_location)")], + ) + end + + def C.rb_iseq_struct + @rb_iseq_struct ||= CType::Struct.new( + "rb_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_struct)"), + flags: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), flags)")], + wrapper: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), wrapper)")], + body: [CType::Pointer.new { self.rb_iseq_constant_body }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), body)")], + aux: [CType::Union.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux)"), + compile_data: CType::Pointer.new { self.iseq_compile_data }, + loader: CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.loader)"), + obj: [self.VALUE, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, obj)")], + index: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.loader, index)")], + ), + exec: CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_iseq_struct *)NULL)->aux.exec)"), + local_hooks: [CType::Pointer.new { self.rb_hook_list_struct }, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, local_hooks)")], + global_trace_events: [self.rb_event_flag_t, Primitive.cexpr!("OFFSETOF(((struct rb_iseq_struct *)NULL)->aux.exec, global_trace_events)")], + ), + ), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_struct *)NULL)), aux)")], + ) + end + + def C.rb_iseq_t + @rb_iseq_t ||= self.rb_iseq_struct + end + + def C.rb_iv_index_tbl_entry + @rb_iv_index_tbl_entry ||= CType::Struct.new( + "rb_iv_index_tbl_entry", Primitive.cexpr!("SIZEOF(struct rb_iv_index_tbl_entry)"), + index: [CType::Immediate.parse("uint32_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), index)")], + class_serial: [self.rb_serial_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_serial)")], + class_value: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_value)")], + ) + end + + def C.rb_method_definition_struct + @rb_method_definition_struct ||= CType::Struct.new( + "rb_method_definition_struct", Primitive.cexpr!("SIZEOF(struct rb_method_definition_struct)"), + type: [self.rb_method_type_t, 0], + iseq_overload: [CType::BitField.new(1, 4), 4], + alias_count: [CType::BitField.new(27, 5), 5], + complemented_count: [CType::BitField.new(28, 0), 32], + no_redef_warning: [CType::BitField.new(1, 4), 60], + body: [CType::Union.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_method_definition_struct *)NULL)->body)"), + iseq: self.rb_method_iseq_t, + cfunc: self.rb_method_cfunc_t, + attr: self.rb_method_attr_t, + alias: self.rb_method_alias_t, + refined: self.rb_method_refined_t, + bmethod: self.rb_method_bmethod_t, + optimized: self.rb_method_optimized_t, + ), Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), body)")], + original_id: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), original_id)")], + method_serial: [CType::Immediate.parse("uintptr_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_method_definition_struct *)NULL)), method_serial)")], + ) + end + + def C.rb_method_iseq_t + @rb_method_iseq_t ||= CType::Struct.new( + "rb_method_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_method_iseq_struct)"), + iseqptr: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), iseqptr)")], + cref: [CType::Pointer.new { self.rb_cref_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_iseq_struct *)NULL)), cref)")], + ) + end + + def C.rb_method_type_t + @rb_method_type_t ||= CType::Immediate.parse("int") + end + + def C.rb_mjit_compile_info + @rb_mjit_compile_info ||= CType::Struct.new( + "rb_mjit_compile_info", Primitive.cexpr!("SIZEOF(struct rb_mjit_compile_info)"), + disable_ivar_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_ivar_cache)")], + disable_exivar_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_exivar_cache)")], + disable_send_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_send_cache)")], + disable_inlining: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_inlining)")], + disable_const_cache: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_compile_info *)NULL)), disable_const_cache)")], + ) + end + + def C.rb_mjit_unit + @rb_mjit_unit ||= CType::Struct.new( + "rb_mjit_unit", Primitive.cexpr!("SIZEOF(struct rb_mjit_unit)"), + unode: [self.ccan_list_node, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), unode)")], + id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), id)")], + handle: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), handle)")], + iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), iseq)")], + used_code_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), used_code_p)")], + compact_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compact_p)")], + compile_info: [self.rb_mjit_compile_info, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), compile_info)")], + cc_entries: [CType::Pointer.new { CType::Pointer.new { self.rb_callcache } }, Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries)")], + cc_entries_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_unit *)NULL)), cc_entries_size)")], + ) + end + + def C.rb_serial_t + @rb_serial_t ||= CType::Immediate.parse("unsigned long long") + end + + def C.VALUE + @VALUE ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(VALUE)"), Primitive.cexpr!("SIGNED_TYPE_P(VALUE)")) + end + + def C._Bool + CType::Bool.new + end + + def C.ID + CType::Stub.new(:ID) + end + + def C.rb_thread_struct + CType::Stub.new(:rb_thread_struct) + end + + def C.vm_call_handler + CType::Stub.new(:vm_call_handler) + end + + def C.method_missing_reason + CType::Stub.new(:method_missing_reason) + end + + def C.rb_callinfo_kwarg + CType::Stub.new(:rb_callinfo_kwarg) + end + + def C.rb_cref_struct + CType::Stub.new(:rb_cref_struct) + end + + def C.rb_scope_visibility_t + CType::Stub.new(:rb_scope_visibility_t) + end + + def C.rb_vm_tag + CType::Stub.new(:rb_vm_tag) + end + + def C.rb_atomic_t + CType::Stub.new(:rb_atomic_t) + end + + def C.rb_fiber_t + CType::Stub.new(:rb_fiber_t) + end + + def C.rb_id_table + CType::Stub.new(:rb_id_table) + end + + def C.rb_ensure_list_t + CType::Stub.new(:rb_ensure_list_t) + end + + def C.rb_trace_arg_struct + CType::Stub.new(:rb_trace_arg_struct) + end + + def C.rb_iseq_type + CType::Stub.new(:rb_iseq_type) + end + + def C.rb_iseq_param_keyword + CType::Stub.new(:rb_iseq_param_keyword) + end + + def C.iseq_insn_info + CType::Stub.new(:iseq_insn_info) + end + + def C.iseq_catch_table + CType::Stub.new(:iseq_catch_table) + end + + def C.rb_snum_t + CType::Stub.new(:rb_snum_t) + end + + def C.iseq_bits_t + CType::Stub.new(:iseq_bits_t) + end + + def C.rb_code_location_t + CType::Stub.new(:rb_code_location_t) + end + + def C.iseq_compile_data + CType::Stub.new(:iseq_compile_data) + end + + def C.rb_hook_list_struct + CType::Stub.new(:rb_hook_list_struct) + end + + def C.rb_event_flag_t + CType::Stub.new(:rb_event_flag_t) + end + + def C.rb_method_cfunc_t + CType::Stub.new(:rb_method_cfunc_t) + end + + def C.rb_method_attr_t + CType::Stub.new(:rb_method_attr_t) + end + + def C.rb_method_alias_t + CType::Stub.new(:rb_method_alias_t) + end + + def C.rb_method_refined_t + CType::Stub.new(:rb_method_refined_t) + end + + def C.rb_method_bmethod_t + CType::Stub.new(:rb_method_bmethod_t) + end + + def C.rb_method_optimized_t + CType::Stub.new(:rb_method_optimized_t) + end + + def C.ccan_list_node + CType::Stub.new(:ccan_list_node) + end + + ### MJIT bindgen end ### +end if RubyVM::MJIT.enabled? diff --git a/mjit_compiler.c b/mjit_compiler.c index 5163bbcf6bf7a6..1982ad925cc590 100644 --- a/mjit_compiler.c +++ b/mjit_compiler.c @@ -164,7 +164,15 @@ has_cache_for_send(rb_execution_context_t *ec, VALUE self, VALUE cc_addr, VALUE extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); +// An offsetof implementation that works for unnamed struct and union. +// Multiplying 8 for compatibility with libclang's offsetof. +#define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8) + +#define SIZEOF(type) RB_SIZE2NUM(sizeof(type)) +#define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(1)) + +#include "mjit_c.rbinc" + #include "mjit_compiler.rbinc" -#include "mjit_instruction.rbinc" #endif // USE_MJIT diff --git a/mjit_compiler.rb b/mjit_compiler.rb index 9e032fc747703e..51dc9e774f7159 100644 --- a/mjit_compiler.rb +++ b/mjit_compiler.rb @@ -2,135 +2,13 @@ # TODO: Merge this to mjit.rb if RubyVM::MJIT.enabled? begin - require 'etc' require 'fiddle' + require 'fiddle/import' rescue LoadError return # skip miniruby end - if Fiddle::SIZEOF_VOIDP == 8 - require 'mjit/c_64' - else - require 'mjit/c_32' - end - - class << RubyVM::MJIT::C - def ROBJECT_EMBED_LEN_MAX - Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' - end - - def cdhash_to_hash(cdhash_addr) - Primitive.cdhash_to_hash(cdhash_addr) - end - - def builtin_compiler(f, bf, index, stack_size, builtin_inline_p) - Primitive.builtin_compile(f, bf.to_i, index, stack_size, builtin_inline_p) - end - - def has_cache_for_send(cc, insn) - Primitive.has_cache_for_send(cc.to_i, insn) - end - - def rb_iseq_check(iseq) - _iseq_addr = iseq.to_i - iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseq_check((rb_iseq_t *)NUM2PTR(_iseq_addr)))' - rb_iseq_t.new(iseq_addr) - end - - def rb_iseq_path(iseq) - _iseq_addr = iseq.to_i - Primitive.cexpr! 'rb_iseq_path((rb_iseq_t *)NUM2PTR(_iseq_addr))' - end - - def vm_ci_argc(ci) - _ci_addr = ci.to_i - Primitive.cexpr! 'UINT2NUM(vm_ci_argc((CALL_INFO)NUM2PTR(_ci_addr)))' - end - - def vm_ci_flag(ci) - _ci_addr = ci.to_i - Primitive.cexpr! 'UINT2NUM(vm_ci_flag((CALL_INFO)NUM2PTR(_ci_addr)))' - end - - def rb_splat_or_kwargs_p(ci) - _ci_addr = ci.to_i - Primitive.cexpr! 'RBOOL(rb_splat_or_kwargs_p((CALL_INFO)NUM2PTR(_ci_addr)))' - end - - def fastpath_applied_iseq_p(ci, cc, iseq) - _ci_addr = ci.to_i - _cc_addr = cc.to_i - _iseq_addr = iseq.to_i - Primitive.cexpr! 'RBOOL(fastpath_applied_iseq_p((CALL_INFO)NUM2PTR(_ci_addr), (CALL_CACHE)NUM2PTR(_cc_addr), (rb_iseq_t *)NUM2PTR(_iseq_addr)))' - end - - def mjit_opts - addr = Primitive.cexpr! 'PTR2NUM((VALUE)&mjit_opts)' - mjit_options.new(addr) - end - - def mjit_call_attribute_sp_inc(insn, operands) - _operands_addr = operands.to_i - Primitive.cexpr! 'LONG2NUM(mjit_call_attribute_sp_inc(NUM2INT(insn), (VALUE *)NUM2PTR(_operands_addr)))' - end - - def mjit_capture_cc_entries(compiled_body, captured_body) - _compiled_body_addr = compiled_body.to_i - _captured_body_addr = captured_body.to_i - Primitive.cexpr! 'INT2NUM(mjit_capture_cc_entries((struct rb_iseq_constant_body *)NUM2PTR(_compiled_body_addr), (struct rb_iseq_constant_body *)NUM2PTR(_captured_body_addr)))' - end - - #const struct rb_iseq_constant_body *body, union iseq_inline_storage_entry *is_entries - def mjit_capture_is_entries(body, is_entries) - _body_addr = body.to_i - _is_entries_addr = is_entries.to_i - Primitive.cstmt! %{ - mjit_capture_is_entries((struct rb_iseq_constant_body *)NUM2PTR(_body_addr), (union iseq_inline_storage_entry *)NUM2PTR(_is_entries_addr)); - return Qnil; - } - end - - # Convert encoded VM pointers to insn BINs. - def rb_vm_insn_decode(encoded) - Primitive.cexpr! 'INT2NUM(rb_vm_insn_decode(NUM2PTR(encoded)))' - end - - # Convert insn BINs to encoded VM pointers. This one is not used by the compiler, but useful for debugging. - def rb_vm_insn_encode(bin) - Primitive.cexpr! 'PTR2NUM((VALUE)rb_vm_get_insns_address_table()[NUM2INT(bin)])' - end - - def insn_may_depend_on_sp_or_pc(insn, opes) - _opes_addr = opes.to_i - Primitive.cexpr! 'RBOOL(insn_may_depend_on_sp_or_pc(NUM2INT(insn), (VALUE *)NUM2PTR(_opes_addr)))' - end - - # Convert Integer VALUE to an actual Ruby object - def to_ruby(value) - Primitive.cexpr! '(VALUE)NUM2PTR(value)' - end - - # Convert RubyVM::InstructionSequence to C.rb_iseq_t. Not used by the compiler, but useful for debugging. - def rb_iseqw_to_iseq(iseqw) - iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseqw_to_iseq(iseqw))' - rb_iseq_t.new(iseq_addr) - end - - # TODO: remove this after migration - def fprintf(f, str) - Primitive.cstmt! %{ - fprintf((FILE *)NUM2PTR(f), "%s", RSTRING_PTR(str)); - return Qnil; - } - end - - def rb_cFalseClass; Primitive.cexpr! 'PTR2NUM(rb_cFalseClass)' end - def rb_cNilClass; Primitive.cexpr! 'PTR2NUM(rb_cNilClass)' end - def rb_cTrueClass; Primitive.cexpr! 'PTR2NUM(rb_cTrueClass)' end - def rb_cInteger; Primitive.cexpr! 'PTR2NUM(rb_cInteger)' end - def rb_cSymbol; Primitive.cexpr! 'PTR2NUM(rb_cSymbol)' end - def rb_cFloat; Primitive.cexpr! 'PTR2NUM(rb_cFloat)' end - end - + require "mjit/c_type" + require "mjit/instruction" require "mjit/compiler" end diff --git a/object.c b/object.c index 358dba9c32ed4a..2f1f9ae74f81c5 100644 --- a/object.c +++ b/object.c @@ -3185,6 +3185,11 @@ rb_opts_exception_p(VALUE opts, int default_value) * using +to_int+ first and +to_i+ second; * see below for exceptions. * + * With a non-zero +base+, +object+ must be a string or convertible + * to a string. + * + * ==== numeric objects + * * With integer argument +object+ given, returns +object+: * * Integer(1) # => 1 @@ -3196,6 +3201,8 @@ rb_opts_exception_p(VALUE opts, int default_value) * Integer(1.9) # => 1 # Rounds toward zero. * Integer(-1.9) # => -1 # Rounds toward zero. * + * ==== string objects + * * With string argument +object+ and zero +base+ given, * returns +object+ converted to an integer in base 10: * @@ -3203,32 +3210,48 @@ rb_opts_exception_p(VALUE opts, int default_value) * Integer('-100') # => -100 * * With +base+ zero, string +object+ may contain leading characters - * to specify the actual base: + * to specify the actual base (radix indicator): * * Integer('0100') # => 64 # Leading '0' specifies base 8. * Integer('0b100') # => 4 # Leading '0b', specifies base 2. * Integer('0x100') # => 256 # Leading '0x' specifies base 16. * - * With a non-zero +base+ (in range 2..36) given - * (in which case +object+ must be a string), - * returns +object+ converted to an integer in the given base: + * With a positive +base+ (in range 2..36) given, returns +object+ + * converted to an integer in the given base: * * Integer('100', 2) # => 4 * Integer('100', 8) # => 64 * Integer('-100', 16) # => -256 * + * With a negative +base+ (in range -36..-2) given, returns +object+ + * converted to an integer in the radix indicator if exists or + * +-base+: + * + * Integer('0x100', -2) # => 256 + * Integer('100', -2) # => 4 + * Integer('0b100', -8) # => 4 + * Integer('100', -8) # => 64 + * Integer('0o100', -10) # => 64 + * Integer('100', -10) # => 100 + * + * +base+ -1 is equal the -10 case. + * * When converting strings, surrounding whitespace and embedded underscores * are allowed and ignored: * * Integer(' 100 ') # => 100 * Integer('-1_0_0', 16) # => -256 * + * ==== other classes + * * Examples with +object+ of various other classes: * * Integer(Rational(9, 10)) # => 0 # Rounds toward zero. * Integer(Complex(2, 0)) # => 2 # Imaginary part must be zero. * Integer(Time.now) # => 1650974042 * + * ==== keywords + * * With optional keyword argument +exception+ given as +true+ (the default): * * - Raises TypeError if +object+ does not respond to +to_int+ or +to_i+. diff --git a/parse.y b/parse.y index e6fee29595e26e..b16dc35b88b42a 100644 --- a/parse.y +++ b/parse.y @@ -70,7 +70,7 @@ struct lex_context { BITFIELD(enum shareability, shareable_constant_value, 2); }; -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(__clang__) // Suppress "parameter passing for argument of type 'struct // lex_context' changed" notes. `struct lex_context` is file scope, // and has no ABI compatibility issue. @@ -1774,14 +1774,18 @@ expr : command_call p->ctxt.in_kwarg = 1; $$ = push_pvtbl(p); } + { + $$ = push_pktbl(p); + } p_top_expr_body { + pop_pktbl(p, $4); pop_pvtbl(p, $3); p->ctxt.in_kwarg = $2.in_kwarg; /*%%%*/ - $$ = NEW_CASE3($1, NEW_IN($4, 0, 0, &@4), &@$); + $$ = NEW_CASE3($1, NEW_IN($5, 0, 0, &@5), &@$); /*% %*/ - /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/ + /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/ } | arg keyword_in { @@ -1792,14 +1796,18 @@ expr : command_call p->ctxt.in_kwarg = 1; $$ = push_pvtbl(p); } + { + $$ = push_pktbl(p); + } p_top_expr_body { + pop_pktbl(p, $4); pop_pvtbl(p, $3); p->ctxt.in_kwarg = $2.in_kwarg; /*%%%*/ - $$ = NEW_CASE3($1, NEW_IN($4, NEW_TRUE(&@4), NEW_FALSE(&@4), &@4), &@$); + $$ = NEW_CASE3($1, NEW_IN($5, NEW_TRUE(&@5), NEW_FALSE(&@5), &@5), &@$); /*% %*/ - /*% ripper: case!($1, in!($4, Qnil, Qnil)) %*/ + /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/ } | arg %prec tLBRACE_ARG ; @@ -10553,13 +10561,7 @@ static NODE * kwd_append(NODE *kwlist, NODE *kw) { if (kwlist) { - NODE *kws = kwlist; - kws->nd_loc.end_pos = kw->nd_loc.end_pos; - while (kws->nd_next) { - kws = kws->nd_next; - kws->nd_loc.end_pos = kw->nd_loc.end_pos; - } - kws->nd_next = kw; + opt_arg_append(kwlist, kw); } return kwlist; } diff --git a/proc.c b/proc.c index 200a592701d807..50db2daa460935 100644 --- a/proc.c +++ b/proc.c @@ -1363,7 +1363,7 @@ iseq_location(const rb_iseq_t *iseq) if (!iseq) return Qnil; rb_iseq_check(iseq); loc[0] = rb_iseq_path(iseq); - loc[1] = ISEQ_BODY(iseq)->location.first_lineno; + loc[1] = RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno); return rb_ary_new4(2, loc); } @@ -1544,7 +1544,7 @@ rb_block_to_s(VALUE self, const struct rb_block *block, const char *additional_i const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq); rb_str_catf(str, "%p %"PRIsVALUE":%d", (void *)self, rb_iseq_path(iseq), - FIX2INT(ISEQ_BODY(iseq)->location.first_lineno)); + ISEQ_BODY(iseq)->location.first_lineno); } break; case block_type_symbol: @@ -2452,8 +2452,7 @@ method_clone(VALUE self) static VALUE rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method) { - VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil; - return rb_method_call_with_block_kw(argc, argv, method, procval, RB_PASS_CALLED_KEYWORDS); + return rb_method_call_kw(argc, argv, method, RB_PASS_CALLED_KEYWORDS); } VALUE @@ -3507,7 +3506,7 @@ proc_binding(VALUE self) if (iseq) { rb_iseq_check(iseq); RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj); - bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq)); + bind->first_lineno = ISEQ_BODY(iseq)->location.first_lineno; } else { RB_OBJ_WRITE(bindval, &bind->pathobj, diff --git a/process.c b/process.c index 405c7edcb31c84..d95b717960d097 100644 --- a/process.c +++ b/process.c @@ -4222,6 +4222,7 @@ rb_mjit_fork(void) after_fork_ruby(); disable_child_handler_fork_parent(&old); + if (pid == 0) rb_thread_atfork(); return pid; } @@ -5547,6 +5548,9 @@ rlimit_resource_name2int(const char *name, long len, int casetype) #ifdef RLIMIT_NPROC RESCHECK(NPROC); #endif +#ifdef RLIMIT_NPTS + RESCHECK(NPTS); +#endif #ifdef RLIMIT_NICE RESCHECK(NICE); #endif @@ -5775,6 +5779,7 @@ proc_getrlimit(VALUE obj, VALUE resource) * [NICE] ceiling on process's nice(2) value (number) (GNU/Linux) * [NOFILE] file descriptors (number) (SUSv3) * [NPROC] number of processes for the user (number) (4.4BSD, GNU/Linux) + * [NPTS] number of pseudo terminals (number) (FreeBSD) * [RSS] resident memory size (bytes) (4.2BSD, GNU/Linux) * [RTPRIO] ceiling on the process's real-time priority (number) (GNU/Linux) * [RTTIME] CPU time for real-time process (us) (GNU/Linux) @@ -7108,19 +7113,14 @@ rb_daemon(int nochdir, int noclose) #else int n; -#define fork_daemon() \ - switch (rb_fork_ruby(NULL)) { \ - case -1: return -1; \ - case 0: break; \ - default: _exit(EXIT_SUCCESS); \ + switch (rb_fork_ruby(NULL)) { + case -1: return -1; + case 0: break; + default: _exit(EXIT_SUCCESS); } - fork_daemon(); - - if (setsid() < 0) return -1; - - /* must not be process-leader */ - fork_daemon(); + /* ignore EPERM which means already being process-leader */ + if (setsid() < 0) (void)0; if (!nochdir) err = chdir("/"); @@ -8726,7 +8726,7 @@ get_PROCESS_ID(ID _x, VALUE *_y) /* * call-seq: - * Process.kill(signal, pid, ...) -> integer + * Process.kill(signal, pid, *pids) -> integer * * Sends the given signal to the specified process id(s) if _pid_ is positive. * If _pid_ is zero, _signal_ is sent to all processes whose group ID is equal @@ -8979,6 +8979,14 @@ InitVM_process(void) */ rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC)); #endif +#ifdef RLIMIT_NPTS + /* The maximum number of pseudo-terminals that can be created for the + * real user ID of the calling process. + * + * see the system getrlimit(2) manual for details. + */ + rb_define_const(rb_mProcess, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS)); +#endif #ifdef RLIMIT_RSS /* Specifies the limit (in pages) of the process's resident set. * diff --git a/ractor.c b/ractor.c index 0eddc165fa4b23..3bd6c04af00b35 100644 --- a/ractor.c +++ b/ractor.c @@ -2685,7 +2685,7 @@ obj_refer_only_shareables_p_i(VALUE obj, void *ptr) int *pcnt = (int *)ptr; if (!rb_ractor_shareable_p(obj)) { - pcnt++; + *pcnt++; } } diff --git a/scheduler.c b/scheduler.c index 06658356b176e9..e13733722cebf5 100644 --- a/scheduler.c +++ b/scheduler.c @@ -242,7 +242,7 @@ rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t lengt } VALUE -rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset) +rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset) { VALUE arguments[] = { io, buffer, SIZET2NUM(length), OFFT2NUM(offset) @@ -262,7 +262,7 @@ rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t leng } VALUE -rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset) +rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, rb_off_t offset) { VALUE arguments[] = { io, buffer, SIZET2NUM(length), OFFT2NUM(offset) diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb index f4701529968dd8..9b9e2ddf0a8bd3 100644 --- a/spec/bundler/bundler/dependency_spec.rb +++ b/spec/bundler/bundler/dependency_spec.rb @@ -34,4 +34,110 @@ end end end + + describe "PLATFORM_MAP" do + subject { described_class::PLATFORM_MAP } + + # rubocop:disable Naming/VariableNumber + let(:platforms) do + { :ruby => Gem::Platform::RUBY, + :ruby_18 => Gem::Platform::RUBY, + :ruby_19 => Gem::Platform::RUBY, + :ruby_20 => Gem::Platform::RUBY, + :ruby_21 => Gem::Platform::RUBY, + :ruby_22 => Gem::Platform::RUBY, + :ruby_23 => Gem::Platform::RUBY, + :ruby_24 => Gem::Platform::RUBY, + :ruby_25 => Gem::Platform::RUBY, + :ruby_26 => Gem::Platform::RUBY, + :ruby_27 => Gem::Platform::RUBY, + :ruby_30 => Gem::Platform::RUBY, + :ruby_31 => Gem::Platform::RUBY, + :mri => Gem::Platform::RUBY, + :mri_18 => Gem::Platform::RUBY, + :mri_19 => Gem::Platform::RUBY, + :mri_20 => Gem::Platform::RUBY, + :mri_21 => Gem::Platform::RUBY, + :mri_22 => Gem::Platform::RUBY, + :mri_23 => Gem::Platform::RUBY, + :mri_24 => Gem::Platform::RUBY, + :mri_25 => Gem::Platform::RUBY, + :mri_26 => Gem::Platform::RUBY, + :mri_27 => Gem::Platform::RUBY, + :mri_30 => Gem::Platform::RUBY, + :mri_31 => Gem::Platform::RUBY, + :rbx => Gem::Platform::RUBY, + :truffleruby => Gem::Platform::RUBY, + :jruby => Gem::Platform::JAVA, + :jruby_18 => Gem::Platform::JAVA, + :jruby_19 => Gem::Platform::JAVA, + :windows => Gem::Platform::WINDOWS, + :windows_18 => Gem::Platform::WINDOWS, + :windows_19 => Gem::Platform::WINDOWS, + :windows_20 => Gem::Platform::WINDOWS, + :windows_21 => Gem::Platform::WINDOWS, + :windows_22 => Gem::Platform::WINDOWS, + :windows_23 => Gem::Platform::WINDOWS, + :windows_24 => Gem::Platform::WINDOWS, + :windows_25 => Gem::Platform::WINDOWS, + :windows_26 => Gem::Platform::WINDOWS, + :windows_27 => Gem::Platform::WINDOWS, + :windows_30 => Gem::Platform::WINDOWS, + :windows_31 => Gem::Platform::WINDOWS, + :mswin => Gem::Platform::MSWIN, + :mswin_18 => Gem::Platform::MSWIN, + :mswin_19 => Gem::Platform::MSWIN, + :mswin_20 => Gem::Platform::MSWIN, + :mswin_21 => Gem::Platform::MSWIN, + :mswin_22 => Gem::Platform::MSWIN, + :mswin_23 => Gem::Platform::MSWIN, + :mswin_24 => Gem::Platform::MSWIN, + :mswin_25 => Gem::Platform::MSWIN, + :mswin_26 => Gem::Platform::MSWIN, + :mswin_27 => Gem::Platform::MSWIN, + :mswin_30 => Gem::Platform::MSWIN, + :mswin_31 => Gem::Platform::MSWIN, + :mswin64 => Gem::Platform::MSWIN64, + :mswin64_19 => Gem::Platform::MSWIN64, + :mswin64_20 => Gem::Platform::MSWIN64, + :mswin64_21 => Gem::Platform::MSWIN64, + :mswin64_22 => Gem::Platform::MSWIN64, + :mswin64_23 => Gem::Platform::MSWIN64, + :mswin64_24 => Gem::Platform::MSWIN64, + :mswin64_25 => Gem::Platform::MSWIN64, + :mswin64_26 => Gem::Platform::MSWIN64, + :mswin64_27 => Gem::Platform::MSWIN64, + :mswin64_30 => Gem::Platform::MSWIN64, + :mswin64_31 => Gem::Platform::MSWIN64, + :mingw => Gem::Platform::MINGW, + :mingw_18 => Gem::Platform::MINGW, + :mingw_19 => Gem::Platform::MINGW, + :mingw_20 => Gem::Platform::MINGW, + :mingw_21 => Gem::Platform::MINGW, + :mingw_22 => Gem::Platform::MINGW, + :mingw_23 => Gem::Platform::MINGW, + :mingw_24 => Gem::Platform::MINGW, + :mingw_25 => Gem::Platform::MINGW, + :mingw_26 => Gem::Platform::MINGW, + :mingw_27 => Gem::Platform::MINGW, + :mingw_30 => Gem::Platform::MINGW, + :mingw_31 => Gem::Platform::MINGW, + :x64_mingw => Gem::Platform::X64_MINGW, + :x64_mingw_20 => Gem::Platform::X64_MINGW, + :x64_mingw_21 => Gem::Platform::X64_MINGW, + :x64_mingw_22 => Gem::Platform::X64_MINGW, + :x64_mingw_23 => Gem::Platform::X64_MINGW, + :x64_mingw_24 => Gem::Platform::X64_MINGW, + :x64_mingw_25 => Gem::Platform::X64_MINGW, + :x64_mingw_26 => Gem::Platform::X64_MINGW, + :x64_mingw_27 => Gem::Platform::X64_MINGW, + :x64_mingw_30 => Gem::Platform::X64_MINGW, + :x64_mingw_31 => Gem::Platform::X64_MINGW } + end + # rubocop:enable Naming/VariableNumber + + it "includes all platforms" do + expect(subject).to eq(platforms) + end + end end diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb index 81fcd76e0135c3..7dd6925007bb0e 100644 --- a/spec/bundler/bundler/endpoint_specification_spec.rb +++ b/spec/bundler/bundler/endpoint_specification_spec.rb @@ -54,8 +54,6 @@ let(:required_ruby_version) { existing_value } it "should return the current value when already set on endpoint specification" do - remote_spec = double(:remote_spec, :required_ruby_version => "remote_value", :required_rubygems_version => nil) - expect(spec.required_ruby_version). eql?(existing_value) end end diff --git a/spec/bundler/bundler/vendored_persistent_spec.rb b/spec/bundler/bundler/vendored_persistent_spec.rb deleted file mode 100644 index 3ed899dbcfdfde..00000000000000 --- a/spec/bundler/bundler/vendored_persistent_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_persistent" - -RSpec.describe Bundler::PersistentHTTP do - describe "#warn_old_tls_version_rubygems_connection" do - let(:uri) { "https://index.rubygems.org" } - let(:connection) { instance_double(Bundler::Persistent::Net::HTTP::Persistent::Connection) } - let(:tls_version) { "TLSv1.2" } - let(:socket) { double("Socket") } - let(:socket_io) { double("SocketIO") } - - before do - allow(connection).to receive_message_chain(:http, :use_ssl?).and_return(!tls_version.nil?) - allow(socket).to receive(:io).and_return(socket_io) if socket - connection.instance_variable_set(:@socket, socket) - - if tls_version - allow(socket_io).to receive(:ssl_version).and_return(tls_version) - end - end - - shared_examples_for "does not warn" do - it "does not warn" do - allow(Bundler.ui).to receive(:warn).never - subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection) - end - end - - shared_examples_for "does warn" do |*expected| - it "warns" do - expect(Bundler.ui).to receive(:warn).with(*expected) - subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection) - end - end - - context "an HTTPS uri with TLSv1.2" do - include_examples "does not warn" - end - - context "without SSL" do - let(:tls_version) { nil } - - include_examples "does not warn" - end - - context "without a socket" do - let(:socket) { nil } - - include_examples "does not warn" - end - - context "with a different TLD" do - let(:uri) { "https://foo.bar" } - include_examples "does not warn" - - context "and an outdated TLS version" do - let(:tls_version) { "TLSv1" } - include_examples "does not warn" - end - end - - context "with a nonsense TLS version" do - let(:tls_version) { "BlahBlah2.0Blah" } - include_examples "does not warn" - end - - context "with an outdated TLS version" do - let(:tls_version) { "TLSv1" } - include_examples "does warn", - "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \ - "Starting in January 2018, RubyGems.org will refuse connection requests from these very old versions of OpenSSL. " \ - "If you will need to continue installing gems after January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.", - :wrap => true - end - end -end diff --git a/spec/bundler/commands/fund_spec.rb b/spec/bundler/commands/fund_spec.rb index 5a0c5411dabcf4..5415b88eeb00e8 100644 --- a/spec/bundler/commands/fund_spec.rb +++ b/spec/bundler/commands/fund_spec.rb @@ -5,20 +5,20 @@ build_repo2 do build_gem "has_funding_and_other_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end build_gem "has_funding", "1.2.3" do |s| s.metadata = { - "funding_uri" => "https://example.com/has_funding/funding", + "funding_uri" => "https://example.com/has_funding/funding", } end diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb index f72763900efc28..409c49f9e165d0 100644 --- a/spec/bundler/commands/help_spec.rb +++ b/spec/bundler/commands/help_spec.rb @@ -23,8 +23,8 @@ end it "still outputs the old help for commands that do not have man pages yet" do - bundle "help version" - expect(out).to include("Prints the bundler's version information") + bundle "help fund" + expect(out).to include("Lists information about gems seeking funding assistance") end it "looks for a binary and executes it with --help option if it's named bundler-" do diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb index e4b970eb34c15e..2e17ee6dd83a18 100644 --- a/spec/bundler/commands/info_spec.rb +++ b/spec/bundler/commands/info_spec.rb @@ -6,13 +6,13 @@ build_repo2 do build_gem "has_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end end diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb index 62e6bda4cd9449..1bae0bb3715d40 100644 --- a/spec/bundler/install/gemfile/platform_spec.rb +++ b/spec/bundler/install/gemfile/platform_spec.rb @@ -475,7 +475,7 @@ gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby] + gem "rack", :platform => [:windows, :mingw, :mswin, :x64_mingw, :jruby] G bundle "install" diff --git a/spec/bundler/install/gems/fund_spec.rb b/spec/bundler/install/gems/fund_spec.rb index 436454c1f404e8..9aadc9ed250e16 100644 --- a/spec/bundler/install/gems/fund_spec.rb +++ b/spec/bundler/install/gems/fund_spec.rb @@ -6,20 +6,20 @@ build_repo2 do build_gem "has_funding_and_other_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end build_gem "has_funding", "1.2.3" do |s| s.metadata = { - "funding_uri" => "https://example.com/has_funding/funding", + "funding_uri" => "https://example.com/has_funding/funding", } end diff --git a/spec/bundler/realworld/dependency_api_spec.rb b/spec/bundler/realworld/dependency_api_spec.rb index 08c6acf1903960..14f99bd262f805 100644 --- a/spec/bundler/realworld/dependency_api_spec.rb +++ b/spec/bundler/realworld/dependency_api_spec.rb @@ -13,12 +13,12 @@ require_relative "../support/artifice/endpoint_timeout" @t = Thread.new do - server = Rack::Server.start(:app => EndpointTimeout, - :Host => "0.0.0.0", - :Port => port, - :server => "webrick", + server = Rack::Server.start(:app => EndpointTimeout, + :Host => "0.0.0.0", + :Port => port, + :server => "webrick", :AccessLog => [], - :Logger => Spec::SilentLogger.new) + :Logger => Spec::SilentLogger.new) server.start end @t.run diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb index ada2fc92ee5d6e..60c0055a621106 100644 --- a/spec/bundler/realworld/gemfile_source_header_spec.rb +++ b/spec/bundler/realworld/gemfile_source_header_spec.rb @@ -40,12 +40,12 @@ def setup_server require_relative "../support/artifice/endpoint_mirror_source" @t = Thread.new do - Rack::Server.start(:app => EndpointMirrorSource, - :Host => "0.0.0.0", - :Port => @port, - :server => "webrick", + Rack::Server.start(:app => EndpointMirrorSource, + :Host => "0.0.0.0", + :Port => @port, + :server => "webrick", :AccessLog => [], - :Logger => Spec::SilentLogger.new) + :Logger => Spec::SilentLogger.new) end.run wait_for_server("127.0.0.1", @port) diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb index 241424d4d6e139..f2ce477c107393 100644 --- a/spec/bundler/realworld/mirror_probe_spec.rb +++ b/spec/bundler/realworld/mirror_probe_spec.rb @@ -113,12 +113,12 @@ def setup_server require_relative "../support/artifice/endpoint" @server_thread = Thread.new do - Rack::Server.start(:app => Endpoint, - :Host => host, - :Port => @server_port, - :server => "webrick", + Rack::Server.start(:app => Endpoint, + :Host => host, + :Port => @server_port, + :server => "webrick", :AccessLog => [], - :Logger => Spec::SilentLogger.new) + :Logger => Spec::SilentLogger.new) end.run wait_for_server(host, @server_port) diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb index c00113b28f3c96..b0e3f750ead324 100644 --- a/spec/bundler/support/artifice/endpoint.rb +++ b/spec/bundler/support/artifice/endpoint.rb @@ -70,9 +70,9 @@ def dependencies_for(gem_names, gem_repo = default_gem_repo) spec = load_spec(name, version, platform, gem_repo) next unless gem_names.include?(spec.name) { - :name => spec.name, - :number => spec.version.version, - :platform => spec.platform.to_s, + :name => spec.name, + :number => spec.version.version, + :platform => spec.platform.to_s, :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep| [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")] end, diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb index d3aefe004ac3d8..3776901ce3c0fd 100644 --- a/spec/bundler/support/platforms.rb +++ b/spec/bundler/support/platforms.rb @@ -68,14 +68,14 @@ def local_tag if RUBY_PLATFORM == "java" :jruby elsif ["x64-mingw32", "x64-mingw-ucrt"].include?(RUBY_PLATFORM) - :x64_mingw + :windows else :ruby end end def not_local_tag - [:jruby, :x64_mingw, :ruby].find {|tag| tag != local_tag } + [:jruby, :windows, :ruby].find {|tag| tag != local_tag } end def local_ruby_engine @@ -88,7 +88,7 @@ def local_engine_version def not_local_engine_version case not_local_tag - when :ruby, :x64_mingw + when :ruby, :windows not_local_ruby_version when :jruby "1.6.1" diff --git a/spec/bundler/update/gems/fund_spec.rb b/spec/bundler/update/gems/fund_spec.rb index 0dfe63d36d8b0c..d80f4018f3ad97 100644 --- a/spec/bundler/update/gems/fund_spec.rb +++ b/spec/bundler/update/gems/fund_spec.rb @@ -5,20 +5,20 @@ build_repo2 do build_gem "has_funding_and_other_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end build_gem "has_funding", "1.2.3" do |s| s.metadata = { - "funding_uri" => "https://example.com/has_funding/funding", + "funding_uri" => "https://example.com/has_funding/funding", } end end diff --git a/spec/mspec/lib/mspec/utils/script.rb b/spec/mspec/lib/mspec/utils/script.rb index b9f8b17fdc65f6..dd1603c20a053d 100644 --- a/spec/mspec/lib/mspec/utils/script.rb +++ b/spec/mspec/lib/mspec/utils/script.rb @@ -84,7 +84,12 @@ def try_load(target) names.each do |name| config[:path].each do |dir| - file = File.expand_path name, dir + begin + file = File.expand_path name, dir + rescue ArgumentError + # File.expand_path can issue error e.g. if HOME is not available + next + end if @loaded.include?(file) return true elsif File.exist? file diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb index 45727a5c0d4494..848415eeb4e7ca 100644 --- a/spec/ruby/core/encoding/replicate_spec.rb +++ b/spec/ruby/core/encoding/replicate_spec.rb @@ -2,66 +2,74 @@ require_relative '../../spec_helper' describe "Encoding#replicate" do - before :all do - @i = 0 - end + ruby_version_is ""..."3.3" do + before :all do + @i = 0 + end - before :each do - @i += 1 - @prefix = "RS#{@i}" - end + before :each do + @i += 1 + @prefix = "RS#{@i}" + end - it "returns a replica of ASCII" do - name = @prefix + '-ASCII' - e = Encoding::ASCII.replicate(name) - e.name.should == name - Encoding.find(name).should == e + it "returns a replica of ASCII" do + name = @prefix + '-ASCII' + e = suppress_warning { Encoding::ASCII.replicate(name) } + e.name.should == name + Encoding.find(name).should == e - "a".force_encoding(e).valid_encoding?.should be_true - "\x80".force_encoding(e).valid_encoding?.should be_false - end + "a".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end - it "returns a replica of UTF-8" do - name = @prefix + 'UTF-8' - e = Encoding::UTF_8.replicate(name) - e.name.should == name - Encoding.find(name).should == e + it "returns a replica of UTF-8" do + name = @prefix + 'UTF-8' + e = suppress_warning { Encoding::UTF_8.replicate(name) } + e.name.should == name + Encoding.find(name).should == e - "a".force_encoding(e).valid_encoding?.should be_true - "\u3042".force_encoding(e).valid_encoding?.should be_true - "\x80".force_encoding(e).valid_encoding?.should be_false - end + "a".force_encoding(e).valid_encoding?.should be_true + "\u3042".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end - it "returns a replica of UTF-16BE" do - name = @prefix + 'UTF-16-BE' - e = Encoding::UTF_16BE.replicate(name) - e.name.should == name - Encoding.find(name).should == e + it "returns a replica of UTF-16BE" do + name = @prefix + 'UTF-16-BE' + e = suppress_warning { Encoding::UTF_16BE.replicate(name) } + e.name.should == name + Encoding.find(name).should == e - "a".force_encoding(e).valid_encoding?.should be_false - "\x30\x42".force_encoding(e).valid_encoding?.should be_true - "\x80".force_encoding(e).valid_encoding?.should be_false - end + "a".force_encoding(e).valid_encoding?.should be_false + "\x30\x42".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end - it "returns a replica of ISO-2022-JP" do - name = @prefix + 'ISO-2022-JP' - e = Encoding::ISO_2022_JP.replicate(name) - Encoding.find(name).should == e + it "returns a replica of ISO-2022-JP" do + name = @prefix + 'ISO-2022-JP' + e = suppress_warning { Encoding::ISO_2022_JP.replicate(name) } + Encoding.find(name).should == e - e.name.should == name - e.dummy?.should be_true - end + e.name.should == name + e.dummy?.should be_true + end - # NOTE: it's unclear of the value of this (for the complexity cost of it), - # but it is the current CRuby behavior. - it "can be associated with a String" do - name = @prefix + '-US-ASCII' - e = Encoding::US_ASCII.replicate(name) - e.name.should == name - Encoding.find(name).should == e + # NOTE: it's unclear of the value of this (for the complexity cost of it), + # but it is the current CRuby behavior. + it "can be associated with a String" do + name = @prefix + '-US-ASCII' + e = suppress_warning { Encoding::US_ASCII.replicate(name) } + e.name.should == name + Encoding.find(name).should == e + + s = "abc".force_encoding(e) + s.encoding.should == e + s.encoding.name.should == name + end + end - s = "abc".force_encoding(e) - s.encoding.should == e - s.encoding.name.should == name + ruby_version_is "3.3" do + it "has been removed" do + Encoding::US_ASCII.should_not.respond_to?(:replicate, true) + end end end diff --git a/spec/ruby/core/main/fixtures/using.rb b/spec/ruby/core/main/fixtures/using.rb new file mode 100644 index 00000000000000..30713ef309dea1 --- /dev/null +++ b/spec/ruby/core/main/fixtures/using.rb @@ -0,0 +1 @@ +using Module.new diff --git a/spec/ruby/core/main/fixtures/using_in_main.rb b/spec/ruby/core/main/fixtures/using_in_main.rb new file mode 100644 index 00000000000000..a4a71c89ccebed --- /dev/null +++ b/spec/ruby/core/main/fixtures/using_in_main.rb @@ -0,0 +1,5 @@ +MAIN = self + +module X + MAIN.send(:using, Module.new) +end diff --git a/spec/ruby/core/main/fixtures/using_in_method.rb b/spec/ruby/core/main/fixtures/using_in_method.rb new file mode 100644 index 00000000000000..d9ea2e9ef08e1c --- /dev/null +++ b/spec/ruby/core/main/fixtures/using_in_method.rb @@ -0,0 +1,5 @@ +def foo + using Module.new +end + +foo diff --git a/spec/ruby/core/main/using_spec.rb b/spec/ruby/core/main/using_spec.rb index f9f709f7dc3256..8a23970c4bf248 100644 --- a/spec/ruby/core/main/using_spec.rb +++ b/spec/ruby/core/main/using_spec.rb @@ -129,4 +129,24 @@ def bar; 'quux'; end x.call_bar(cls2.new).should == 'bar' end + + it "raises error when called from method in wrapped script" do + -> do + load File.expand_path('../fixtures/using_in_method.rb', __FILE__), true + end.should raise_error(RuntimeError) + end + + it "raises error when called on toplevel from module" do + -> do + load File.expand_path('../fixtures/using_in_main.rb', __FILE__), true + end.should raise_error(RuntimeError) + end + + ruby_version_is "3.2" do + it "does not raise error when wrapped with module" do + -> do + load File.expand_path('../fixtures/using.rb', __FILE__), true + end.should_not raise_error + end + end end diff --git a/spec/ruby/core/time/shared/local.rb b/spec/ruby/core/time/shared/local.rb index 2dba23dbd7ea23..c4aa7a7ea9b4d2 100644 --- a/spec/ruby/core/time/shared/local.rb +++ b/spec/ruby/core/time/shared/local.rb @@ -6,7 +6,6 @@ end end -=begin platform_is_not :windows do describe "timezone changes" do it "correctly adjusts the timezone change to 'CET' on 'Europe/Amsterdam'" do @@ -17,7 +16,6 @@ end end end -=end end describe :time_local_10_arg, shared: true do diff --git a/spec/ruby/library/coverage/result_spec.rb b/spec/ruby/library/coverage/result_spec.rb index 4cc43e8462ae87..61283e4545a710 100644 --- a/spec/ruby/library/coverage/result_spec.rb +++ b/spec/ruby/library/coverage/result_spec.rb @@ -91,15 +91,31 @@ Coverage.result.should_not include(@config_file) end - it 'returns the correct results when eval is used' do - Coverage.start - require @eval_code_file.chomp('.rb') - result = Coverage.result + ruby_version_is '3.1'...'3.2' do + it 'returns the correct results when eval is used' do + Coverage.start + require @eval_code_file.chomp('.rb') + result = Coverage.result - result.should == { - @eval_code_file => [ - 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1 - ] - } + result.should == { + @eval_code_file => [ + 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1 + ] + } + end + end + + ruby_version_is '3.2' do + it 'returns the correct results when eval is used' do + Coverage.start + require @eval_code_file.chomp('.rb') + result = Coverage.result + + result.should == { + @eval_code_file => [ + 1, nil, 1, nil, 1, 1, nil, nil, nil, nil, 1 + ] + } + end end end diff --git a/string.c b/string.c index 8ad5863094d750..45281c8aefa8ad 100644 --- a/string.c +++ b/string.c @@ -359,40 +359,10 @@ rb_debug_rstring_null_ptr(const char *func) /* symbols for [up|down|swap]case/capitalize options */ static VALUE sym_ascii, sym_turkic, sym_lithuanian, sym_fold; -static rb_encoding * -get_actual_encoding(const int encidx, VALUE str) -{ - const unsigned char *q; - - switch (encidx) { - case ENCINDEX_UTF_16: - if (RSTRING_LEN(str) < 2) break; - q = (const unsigned char *)RSTRING_PTR(str); - if (q[0] == 0xFE && q[1] == 0xFF) { - return rb_enc_get_from_index(ENCINDEX_UTF_16BE); - } - if (q[0] == 0xFF && q[1] == 0xFE) { - return rb_enc_get_from_index(ENCINDEX_UTF_16LE); - } - return rb_ascii8bit_encoding(); - case ENCINDEX_UTF_32: - if (RSTRING_LEN(str) < 4) break; - q = (const unsigned char *)RSTRING_PTR(str); - if (q[0] == 0 && q[1] == 0 && q[2] == 0xFE && q[3] == 0xFF) { - return rb_enc_get_from_index(ENCINDEX_UTF_32BE); - } - if (q[3] == 0 && q[2] == 0 && q[1] == 0xFE && q[0] == 0xFF) { - return rb_enc_get_from_index(ENCINDEX_UTF_32LE); - } - return rb_ascii8bit_encoding(); - } - return rb_enc_from_index(encidx); -} - static rb_encoding * get_encoding(VALUE str) { - return get_actual_encoding(ENCODING_GET(str), str); + return rb_enc_from_index(ENCODING_GET(str)); } static void @@ -836,21 +806,15 @@ rb_enc_cr_str_exact_copy(VALUE dest, VALUE src) } static int -enc_coderange_scan(VALUE str, rb_encoding *enc, int encidx) +enc_coderange_scan(VALUE str, rb_encoding *enc) { - if (rb_enc_mbminlen(enc) > 1 && rb_enc_dummy_p(enc) && - rb_enc_mbminlen(enc = get_actual_encoding(encidx, str)) == 1) { - return ENC_CODERANGE_BROKEN; - } - else { - return coderange_scan(RSTRING_PTR(str), RSTRING_LEN(str), enc); - } + return coderange_scan(RSTRING_PTR(str), RSTRING_LEN(str), enc); } int rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc) { - return enc_coderange_scan(str, enc, rb_enc_to_index(enc)); + return enc_coderange_scan(str, enc); } int @@ -859,9 +823,7 @@ rb_enc_str_coderange(VALUE str) int cr = ENC_CODERANGE(str); if (cr == ENC_CODERANGE_UNKNOWN) { - int encidx = ENCODING_GET(str); - rb_encoding *enc = rb_enc_from_index(encidx); - cr = enc_coderange_scan(str, enc, encidx); + cr = enc_coderange_scan(str, get_encoding(str)); ENC_CODERANGE_SET(str, cr); } return cr; @@ -1127,7 +1089,7 @@ is_enc_ascii_string(VALUE str, rb_encoding *enc) int encidx = rb_enc_to_index(enc); if (rb_enc_get_index(str) == encidx) return is_ascii_string(str); - return enc_coderange_scan(str, enc, encidx) == ENC_CODERANGE_7BIT; + return enc_coderange_scan(str, enc) == ENC_CODERANGE_7BIT; } VALUE @@ -2839,26 +2801,35 @@ rb_str_sublen(VALUE str, long pos) } } -VALUE -rb_str_subseq(VALUE str, long beg, long len) +static VALUE +str_subseq(VALUE str, long beg, long len) { VALUE str2; - if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && - SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str))) { - long olen; - str2 = rb_str_new_shared(rb_str_new_frozen_String(str)); - RSTRING(str2)->as.heap.ptr += beg; - olen = RSTRING(str2)->as.heap.len; - if (olen > len) RSTRING(str2)->as.heap.len = len; + const long rstring_embed_capa_max = ((sizeof(struct RString) - offsetof(struct RString, as.embed.ary)) / sizeof(char)) - 1; + + if (!SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str)) || + len <= rstring_embed_capa_max) { + str2 = rb_str_new(RSTRING_PTR(str) + beg, len); + RB_GC_GUARD(str); } else { - str2 = rb_str_new(RSTRING_PTR(str)+beg, len); - RB_GC_GUARD(str); + str2 = str_new_shared(rb_cString, str); + ENC_CODERANGE_CLEAR(str2); + RSTRING(str2)->as.heap.ptr += beg; + if (RSTRING(str2)->as.heap.len > len) { + RSTRING(str2)->as.heap.len = len; + } } - rb_enc_cr_str_copy_for_substr(str2, str); + return str2; +} +VALUE +rb_str_subseq(VALUE str, long beg, long len) +{ + VALUE str2 = str_subseq(str, beg, len); + rb_enc_cr_str_copy_for_substr(str2, str); return str2; } @@ -2958,26 +2929,15 @@ rb_str_substr(VALUE str, long beg, long len) static VALUE str_substr(VALUE str, long beg, long len, int empty) { - VALUE str2; char *p = rb_str_subpos(str, beg, &len); if (!p) return Qnil; - if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && - SHARABLE_SUBSTRING_P(p, len, RSTRING_END(str))) { - long ofs = p - RSTRING_PTR(str); - str2 = rb_str_new_frozen(str); - str2 = str_new_shared(rb_cString, str2); - RSTRING(str2)->as.heap.ptr += ofs; - RSTRING(str2)->as.heap.len = len; - ENC_CODERANGE_CLEAR(str2); - } - else { - if (!len && !empty) return Qnil; - str2 = rb_str_new(p, len); - RB_GC_GUARD(str); - } - rb_enc_cr_str_copy_for_substr(str2, str); + if (!len && !empty) return Qnil; + + beg = p - RSTRING_PTR(str); + VALUE str2 = str_subseq(str, beg, len); + rb_enc_cr_str_copy_for_substr(str2, str); return str2; } @@ -3397,8 +3357,7 @@ rb_str_concat_literals(size_t num, const VALUE *strary) const VALUE v = strary[i]; int encidx = ENCODING_GET(v); - rb_enc_cr_str_buf_cat(str, RSTRING_PTR(v), RSTRING_LEN(v), - encidx, ENC_CODERANGE(v), NULL); + rb_str_buf_append(str, v); if (encidx != ENCINDEX_US_ASCII) { if (ENCODING_GET_INLINED(str) == ENCINDEX_US_ASCII) rb_enc_set_index(str, encidx); @@ -5364,7 +5323,6 @@ rb_str_update(VALUE str, long beg, long len, VALUE val) if (len > slen - beg) { len = slen - beg; } - str_modify_keep_cr(str); p = str_nth(RSTRING_PTR(str), RSTRING_END(str), beg, enc, singlebyte); if (!p) p = RSTRING_END(str); e = str_nth(p, RSTRING_END(str), len, enc, singlebyte); @@ -6186,9 +6144,7 @@ rb_str_setbyte(VALUE str, VALUE index, VALUE value) static VALUE str_byte_substr(VALUE str, long beg, long len, int empty) { - char *p, *s = RSTRING_PTR(str); long n = RSTRING_LEN(str); - VALUE str2; if (beg > n || len < 0) return Qnil; if (beg < 0) { @@ -6200,20 +6156,9 @@ str_byte_substr(VALUE str, long beg, long len, int empty) if (len <= 0) { if (!empty) return Qnil; len = 0; - p = 0; } - else - p = s + beg; - if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && SHARABLE_SUBSTRING_P(beg, len, n)) { - str2 = rb_str_new_frozen(str); - str2 = str_new_shared(rb_cString, str2); - RSTRING(str2)->as.heap.ptr += beg; - RSTRING(str2)->as.heap.len = len; - } - else { - str2 = rb_str_new(p, len); - } + VALUE str2 = str_subseq(str, beg, len); str_enc_copy(str2, str); @@ -6500,7 +6445,7 @@ rb_str_reverse_bang(VALUE str) * */ -static VALUE +VALUE rb_str_include(VALUE str, VALUE arg) { long i; @@ -6517,11 +6462,21 @@ rb_str_include(VALUE str, VALUE arg) * to_i(base = 10) -> integer * * Returns the result of interpreting leading characters in +self+ - * as an integer in the given +base+ (which must be in (2..36)): + * as an integer in the given +base+ (which must be in (0, 2..36)): * * '123456'.to_i # => 123456 * '123def'.to_i(16) # => 1195503 * + * With +base+ zero, string +object+ may contain leading characters + * to specify the actual base: + * + * '123def'.to_i(0) # => 123 + * '0123def'.to_i(0) # => 83 + * '0b123def'.to_i(0) # => 1 + * '0o123def'.to_i(0) # => 83 + * '0d123def'.to_i(0) # => 123 + * '0x123def'.to_i(0) # => 1195503 + * * Characters past a leading valid number (in the given +base+) are ignored: * * '12.345'.to_i # => 12 @@ -6726,7 +6681,7 @@ VALUE rb_str_inspect(VALUE str) { int encidx = ENCODING_GET(str); - rb_encoding *enc = rb_enc_from_index(encidx), *actenc; + rb_encoding *enc = rb_enc_from_index(encidx); const char *p, *pend, *prev; char buf[CHAR_ESC_LEN + 1]; VALUE result = rb_str_buf_new(0); @@ -6741,11 +6696,6 @@ rb_str_inspect(VALUE str) p = RSTRING_PTR(str); pend = RSTRING_END(str); prev = p; - actenc = get_actual_encoding(encidx, str); - if (actenc != enc) { - enc = actenc; - if (unicode_p) unicode_p = rb_enc_unicode_p(enc); - } while (p < pend) { unsigned int c, cc; int n; @@ -9370,7 +9320,7 @@ rb_str_each_grapheme_cluster_size(VALUE str, VALUE args, VALUE eobj) { size_t grapheme_cluster_count = 0; regex_t *reg_grapheme_cluster = NULL; - rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + rb_encoding *enc = get_encoding(str); const char *ptr, *end; if (!rb_enc_unicode_p(enc)) { @@ -9398,7 +9348,7 @@ rb_str_enumerate_grapheme_clusters(VALUE str, VALUE ary) { VALUE orig = str; regex_t *reg_grapheme_cluster = NULL; - rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + rb_encoding *enc = get_encoding(str); const char *ptr0, *ptr, *end; if (!rb_enc_unicode_p(enc)) { @@ -10087,9 +10037,9 @@ rb_str_hex(VALUE str) * returns zero if there is no such leading substring: * * '123'.oct # => 83 - '-377'.oct # => -255 - '0377non-numeric'.oct # => 255 - 'non-numeric'.oct # => 0 + * '-377'.oct # => -255 + * '0377non-numeric'.oct # => 255 + * 'non-numeric'.oct # => 0 * * If +self+ starts with 0, radix indicators are honored; * see Kernel#Integer. @@ -10810,20 +10760,22 @@ rb_str_b(VALUE str) } str_replace_shared_without_enc(str2, str); - // BINARY strings can never be broken; they're either 7-bit ASCII or VALID. - // If we know the receiver's code range then we know the result's code range. - int cr = ENC_CODERANGE(str); - switch (cr) { - case ENC_CODERANGE_7BIT: - ENC_CODERANGE_SET(str2, ENC_CODERANGE_7BIT); - break; - case ENC_CODERANGE_BROKEN: - case ENC_CODERANGE_VALID: - ENC_CODERANGE_SET(str2, ENC_CODERANGE_VALID); - break; - default: - ENC_CODERANGE_CLEAR(str2); - break; + if (rb_enc_asciicompat(STR_ENC_GET(str))) { + // BINARY strings can never be broken; they're either 7-bit ASCII or VALID. + // If we know the receiver's code range then we know the result's code range. + int cr = ENC_CODERANGE(str); + switch (cr) { + case ENC_CODERANGE_7BIT: + ENC_CODERANGE_SET(str2, ENC_CODERANGE_7BIT); + break; + case ENC_CODERANGE_BROKEN: + case ENC_CODERANGE_VALID: + ENC_CODERANGE_SET(str2, ENC_CODERANGE_VALID); + break; + default: + ENC_CODERANGE_CLEAR(str2); + break; + } } return str2; diff --git a/struct.c b/struct.c index 7085042b43c2b3..1e7294eb5ef42b 100644 --- a/struct.c +++ b/struct.c @@ -471,8 +471,8 @@ rb_struct_define_under(VALUE outer, const char *name, ...) * * - May be anonymous, or may have the name given by +class_name+. * - May have members as given by +member_names+. - * - May have initialization via ordinary arguments (the default) - * or via keyword arguments (if keyword_init: true is given). + * - May have initialization via ordinary arguments (unless + * keyword_init: true is given), or via keyword arguments * * The new subclass has its own method ::new; thus: * @@ -557,7 +557,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...) * By default, the arguments for initializing an instance of the new subclass * are ordinary arguments (not keyword arguments). * With optional keyword argument keyword_init: true, - * the new subclass is initialized with keyword arguments: + * the new subclass must be initialized with keyword arguments: * * # Without keyword_init: true. * Foo = Struct.new('Foo', :foo, :bar) @@ -567,6 +567,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...) * Bar = Struct.new(:foo, :bar, keyword_init: true) * Bar # => # => Bar(keyword_init: true) * Bar.new(bar: 1, foo: 0) # => # + * Bar.new(0, 1) # Raises ArgumentError: wrong number of arguments * */ diff --git a/template/Makefile.in b/template/Makefile.in index 01ad030ce437d3..edcceae71d3ea2 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -170,6 +170,7 @@ yes_baseruby = $(HAVE_BASERUBY:no=) no_baseruby = $(HAVE_BASERUBY:yes=) BOOTSTRAPRUBY = $(yes_baseruby:yes=$(BASERUBY)) $(no_baseruby:no=$(MINIRUBY)) BOOTSTRAPRUBY_OPT = $(yes_baseruby:yes=-r./$(arch)-fake) +BOOTSTRAPRUBY_FAKE = $(yes_baseruby:yes=$(arch)-fake.rb) COROUTINE_H = @X_COROUTINE_H@ COROUTINE_OBJ = $(COROUTINE_H:.h=.$(OBJEXT)) @@ -380,7 +381,7 @@ install-cross: $(arch)-fake.rb $(RBCONFIG) rbconfig.rb $(arch_hdrdir)/ruby/confi Makefile: $(srcdir)/template/Makefile.in $(srcdir)/enc/Makefile.in -$(MKFILES): config.status $(srcdir)/version.h $(ABI_VERSION_HDR) +$(MKFILES): config.status $(srcdir)/version.h $(srcdir)/revision.h $(ABI_VERSION_HDR) @[ -f $@ ] && mv $@ $@.old MAKE=$(MAKE) $(SHELL) ./config.status $@ @cmp $@ $@.old > /dev/null 2>&1 && echo $@ unchanged && exit 0; \ @@ -668,10 +669,6 @@ $(INSNS): $(srcdir)/insns.def vm_opts.h \ $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ -$(srcdir)/mjit_instruction.rb: $(tooldir)/ruby_vm/views/mjit_instruction.rb.erb - $(ECHO) generating $@ - $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ - loadpath: verconf.h @$(CPP) $(XCFLAGS) $(CPPFLAGS) $(srcdir)/loadpath.c | \ sed -e '1,/^const char ruby_initial_load_paths/d;/;/,$$d' \ diff --git a/template/id.c.tmpl b/template/id.c.tmpl index 4f30875c044769..5b9e8797303341 100644 --- a/template/id.c.tmpl +++ b/template/id.c.tmpl @@ -12,7 +12,7 @@ **********************************************************************/ <% defs = File.join(File.dirname(File.dirname(erb.filename)), "defs/id.def") -ids = eval(File.read(defs), binding, defs) +ids = eval(File.read(defs), nil, defs) ops = ids[:token_op].uniq {|id, op, token| token && op} %> % ops.each do |_id, _op, token| diff --git a/template/id.h.tmpl b/template/id.h.tmpl index 687cbbbe4072f2..9c588305eb830c 100644 --- a/template/id.h.tmpl +++ b/template/id.h.tmpl @@ -11,10 +11,8 @@ **********************************************************************/ <% -op_id_offset = 128 - defs = File.join(File.dirname(File.dirname(erb.filename)), "defs/id.def") -ids = eval(File.read(defs), binding, defs) +ids = eval(File.read(defs), nil, defs) types = ids.keys.grep(/^[A-Z]/) %> #ifndef RUBY_ID_H @@ -49,11 +47,9 @@ enum ruby_id_types { #define symIFUNC ID2SYM(idIFUNC) #define symCFUNC ID2SYM(idCFUNC) -% index = op_id_offset -% ids[:token_op].each do |_id, _op, token| +% ids[:token_op].each do |_id, _op, token, index| % next unless token #define RUBY_TOKEN_<%=token%> <%=index%> -% index += 1 % end #define RUBY_TOKEN(t) RUBY_TOKEN_##t @@ -66,7 +62,7 @@ enum ruby_method_ids { % ids[:token_op].uniq {|_, op| op}.each do |id, op, token| id<%=id%> = <%=token ? "RUBY_TOKEN(#{token})" : "'#{op}'"%>, % end - tPRESERVED_ID_BEGIN = <%=index-1%>, + tPRESERVED_ID_BEGIN = <%=ids[:last_token]%>, % ids[:preserved].each do |token| id<%=token%>, % end diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl index 3d792867f3bcf2..58453636bf15ce 100644 --- a/template/prelude.c.tmpl +++ b/template/prelude.c.tmpl @@ -20,7 +20,6 @@ class Prelude def initialize(output, preludes, vpath) @output = output - @have_sublib = false @vpath = vpath @prelude_count = 0 @builtin_count = 0 @@ -48,7 +47,7 @@ class Prelude line.rstrip! lineno += 1 @preludes[filename] ||= result - comment = ($1 || '' if line.sub!(/(?:^|\s+)\#(?:$|[#\s](.*))/, '')) + comment = ($1 || '' if line.sub!(/(?:^|\s+)\#(?!\{)(?:$|(?:[#\s]|(?=\W))(.*))/, '')) if !line.empty? or start_line line << "\n" start_line ||= lineno @@ -64,8 +63,10 @@ class Prelude end path = translate("#{path}.rb", true) rescue nil if path - @have_sublib = true - "TMP_RUBY_PREFIX.require(#{path[0]})" + # This library will be loaded before this, + # the order cannot be preserved + comment = "#{orig} #{comment}".rstrip + "" else orig end @@ -131,24 +132,6 @@ static const struct { COMPILER_WARNING_POP -% if @have_sublib -#define PRELUDE_COUNT <%=preludes.size%> - -struct prelude_env { - volatile VALUE prefix_path; -#if PRELUDE_COUNT > 0 - char loaded[PRELUDE_COUNT]; -#endif -}; - -static VALUE -prelude_prefix_path(VALUE self) -{ - struct prelude_env *ptr = DATA_PTR(self); - return ptr->prefix_path; -} - -% end % unless preludes.empty? #define PRELUDE_NAME(n) rb_usascii_str_new_static(prelude_name##n, sizeof(prelude_name##n)-1) #define PRELUDE_CODE(n) rb_utf8_str_new_static(prelude_code##n.L0, sizeof(prelude_code##n)) @@ -179,7 +162,7 @@ rb_builtin_ast(const char *feature_name, VALUE *name_str) rb_ast_t *ast = 0; % @preludes.each_value do |i, prelude, lines, sub, start_line| -% if sub and sub != true +% if sub if ((ast = PRELUDE_AST(<%=i%><%=%>, *name_str, <%=start_line%>)) != 0) return ast; % end % end @@ -216,36 +199,6 @@ prelude_eval(VALUE code, VALUE name, int line) } COMPILER_WARNING_POP -% end -% if @have_sublib -static VALUE -prelude_require(VALUE self, VALUE nth) -{ - struct prelude_env *ptr = DATA_PTR(self); - VALUE code, name; - int n = FIX2INT(nth); - int start_line; - - if (n > PRELUDE_COUNT) return Qfalse; - if (ptr->loaded[n]) return Qfalse; - ptr->loaded[n] = 1; - switch (n) { -% @preludes.each_value do |i, prelude, lines, sub, start_line| -% if sub == true - case <%=i%><%=%>: - code = PRELUDE_CODE(<%=i%><%=%>); - name = PRELUDE_NAME(<%=i%><%=%>); - start_line = <%=start_line%>; - break; -% end -% end - default: - return Qfalse; - } - prelude_eval(code, name, start_line); - return Qtrue; -} - % end %end % init_name = @output && @output[/\w+(?=_prelude.c\b)/] || 'prelude' @@ -253,19 +206,6 @@ void Init_<%=init_name%><%=%>(void) { %unless @prelude_count.zero? -% if @have_sublib - struct prelude_env memo; - ID name = rb_intern("TMP_RUBY_PREFIX"); - VALUE prelude = Data_Wrap_Struct(rb_cObject, 0, 0, &memo); - - memo.prefix_path = rb_const_remove(rb_cObject, name); - rb_const_set(rb_cObject, name, prelude); - rb_define_singleton_method(prelude, "to_s", prelude_prefix_path, 0); -% end -% if @have_sublib - memset(memo.loaded, 0, sizeof(memo.loaded)); - rb_define_singleton_method(prelude, "require", prelude_require, 1); -% end % preludes.each do |i, prelude, lines, sub, start_line| % next if sub prelude_eval(PRELUDE_CODE(<%=i%><%=%>), PRELUDE_NAME(<%=i%><%=%>), <%=start_line%>); diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb index eefe7e7da6a47c..1bb85b67b3b14d 100644 --- a/test/coverage/test_coverage.rb +++ b/test/coverage/test_coverage.rb @@ -136,7 +136,7 @@ def test_eval f.puts 'REPEATS = 400' f.puts 'def add_method(target)' f.puts ' REPEATS.times do' - f.puts ' target.class_eval(<<~RUBY, __FILE__, __LINE__ + 1)' + f.puts ' target.class_eval(<<~RUBY)' f.puts ' def foo' f.puts ' #{"\n" * rand(REPEATS)}' f.puts ' end' @@ -157,6 +157,21 @@ def test_eval } end + def test_eval_coverage + assert_in_out_err(%w[-rcoverage], <<-"end;", ["[1, nil, 1, nil]"], []) + Coverage.start + + eval(<<-RUBY, TOPLEVEL_BINDING, "test.rb") + s = String.new + begin + s << "foo + bar".freeze; end + RUBY + + p Coverage.result["test.rb"] + end; + end + def test_nocoverage_optimized_line assert_ruby_status(%w[], "#{<<-"begin;"}\n#{<<-'end;'}") begin; diff --git a/test/date/test_date_strptime.rb b/test/date/test_date_strptime.rb index fc42ebf7cda1ef..521bf92916ab8d 100644 --- a/test/date/test_date_strptime.rb +++ b/test/date/test_date_strptime.rb @@ -180,6 +180,10 @@ def test__strptime__3 [['fri1feb034pm+5', '%a%d%b%y%H%p%Z'], [2003,2,1,16,nil,nil,'+5',5*3600,5]], [['E. Australia Standard Time', '%Z'], [nil,nil,nil,nil,nil,nil,'E. Australia Standard Time',10*3600,nil], __LINE__], + + # out of range + [['+0.9999999999999999999999', '%Z'], [nil,nil,nil,nil,nil,nil,'+0.9999999999999999999999',+1*3600,nil], __LINE__], + [['+9999999999999999999999.0', '%Z'], [nil,nil,nil,nil,nil,nil,'+9999999999999999999999.0',nil,nil], __LINE__], ].each do |x, y| h = Date._strptime(*x) a = h.values_at(:year,:mon,:mday,:hour,:min,:sec,:zone,:offset,:wday) diff --git a/test/drb/test_drb.rb b/test/drb/test_drb.rb index 1ee1b265d0f77f..11e2219badcf16 100644 --- a/test/drb/test_drb.rb +++ b/test/drb/test_drb.rb @@ -335,6 +335,7 @@ def test_any_to_s class TestDRbTCP < Test::Unit::TestCase def test_immediate_close + omit 'MinGW leaks a thread in this test' if /mingw/ =~ RUBY_PLATFORM server = DRb::DRbServer.new('druby://localhost:0') host, port, = DRb::DRbTCPSocket.send(:parse_uri, server.uri) socket = TCPSocket.open host, port @@ -345,8 +346,8 @@ def test_immediate_close ensure client&.close socket&.close - server.stop_service - server.thread.join + server&.stop_service + server&.thread&.join DRb::DRbConn.stop_pool end end diff --git a/test/fiddle/test_pack.rb b/test/fiddle/test_pack.rb new file mode 100644 index 00000000000000..ade1dd5040c8bf --- /dev/null +++ b/test/fiddle/test_pack.rb @@ -0,0 +1,37 @@ +begin + require_relative 'helper' + require 'fiddle/pack' +rescue LoadError + return +end + +module Fiddle + class TestPack < TestCase + def test_pack_map + if defined?(TYPE_LONG_LONG) + assert_equal [0xffff_ffff_ffff_ffff], [0xffff_ffff_ffff_ffff].pack(PackInfo::PACK_MAP[-TYPE_LONG_LONG]).unpack(PackInfo::PACK_MAP[-TYPE_LONG_LONG]) + end + + case Fiddle::SIZEOF_VOIDP + when 8 + assert_equal [0xffff_ffff_ffff_ffff], [0xffff_ffff_ffff_ffff].pack(PackInfo::PACK_MAP[TYPE_VOIDP]).unpack(PackInfo::PACK_MAP[TYPE_VOIDP]) + when 4 + assert_equal [0xffff_ffff], [0xffff_ffff].pack(PackInfo::PACK_MAP[TYPE_VOIDP]).unpack(PackInfo::PACK_MAP[TYPE_VOIDP]) + end + + case Fiddle::SIZEOF_LONG + when 8 + assert_equal [0xffff_ffff_ffff_ffff], [0xffff_ffff_ffff_ffff].pack(PackInfo::PACK_MAP[-TYPE_LONG]).unpack(PackInfo::PACK_MAP[-TYPE_LONG]) + when 4 + assert_equal [0xffff_ffff], [0xffff_ffff].pack(PackInfo::PACK_MAP[-TYPE_LONG]).unpack(PackInfo::PACK_MAP[-TYPE_LONG]) + end + + if Fiddle::SIZEOF_INT == 4 + assert_equal [0xffff_ffff], [0xffff_ffff].pack(PackInfo::PACK_MAP[-TYPE_INT]).unpack(PackInfo::PACK_MAP[-TYPE_INT]) + end + + assert_equal [0xffff], [0xffff].pack(PackInfo::PACK_MAP[-TYPE_SHORT]).unpack(PackInfo::PACK_MAP[-TYPE_SHORT]) + assert_equal [0xff], [0xff].pack(PackInfo::PACK_MAP[-TYPE_CHAR]).unpack(PackInfo::PACK_MAP[-TYPE_CHAR]) + end + end +end diff --git a/test/io/wait/test_io_wait.rb b/test/io/wait/test_io_wait.rb index fb42b6a70087a1..8a2b6c2d12a2b4 100644 --- a/test/io/wait/test_io_wait.rb +++ b/test/io/wait/test_io_wait.rb @@ -50,6 +50,7 @@ def test_buffered_ready? end def test_wait + omit 'unstable on MinGW' if /mingw/ =~ RUBY_PLATFORM assert_nil @r.wait(0) @w.syswrite "." sleep 0.1 diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb index 6ad64a0ae24133..dc394f9d68ab52 100644 --- a/test/irb/test_color.rb +++ b/test/irb/test_color.rb @@ -88,6 +88,9 @@ def test_colorize_code "foo(*%W(bar))" => "foo(*#{RED}#{BOLD}%W(#{CLEAR}#{RED}bar#{CLEAR}#{RED}#{BOLD})#{CLEAR})", "$stdout" => "#{GREEN}#{BOLD}$stdout#{CLEAR}", "__END__" => "#{GREEN}__END__#{CLEAR}", + "foo\n__END__\nbar" => "foo\n#{GREEN}__END__#{CLEAR}\nbar", + "foo\n< "foo\n#{RED}< "#{RED}< "#{GREEN}def#{CLEAR} #{BLUE}#{BOLD}req#{CLEAR}(#{RED}#{REVERSE}@a#{CLEAR}) #{GREEN}end#{CLEAR}", }) else - tests.merge!({ - "[1]]]\u0013" => "[1]]]^S", + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7.0') + tests.merge!({ + "[1]]]\u0013" => "[#{BLUE}#{BOLD}1#{CLEAR}]#{RED}#{REVERSE}]#{CLEAR}]^S", + "def req(true) end" => "#{GREEN}def#{CLEAR} #{BLUE}#{BOLD}req#{CLEAR}(#{RED}#{REVERSE}true#{CLEAR}) end", + }) + else + tests.merge!({ + "[1]]]\u0013" => "[#{BLUE}#{BOLD}1#{CLEAR}]]]^S", + "def req(true) end" => "#{GREEN}def#{CLEAR} #{BLUE}#{BOLD}req#{CLEAR}(#{CYAN}#{BOLD}true#{CLEAR}) end", }) + end tests.merge!({ - "def req(true) end" => "def req(true) end", "nil = 1" => "#{CYAN}#{BOLD}nil#{CLEAR} = #{BLUE}#{BOLD}1#{CLEAR}", "alias $x $1" => "#{GREEN}alias#{CLEAR} #{GREEN}#{BOLD}$x#{CLEAR} $1", "class bad; end" => "#{GREEN}class#{CLEAR} bad; #{GREEN}end#{CLEAR}", diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb index 3aa99d74d34c4f..a3349bc049f111 100644 --- a/test/irb/test_completion.rb +++ b/test/irb/test_completion.rb @@ -1,9 +1,15 @@ # frozen_string_literal: false require "test/unit" +require "pathname" require "irb" module TestIRB class TestCompletion < Test::Unit::TestCase + def setup + # make sure require completion candidates are not cached + IRB::InputCompletor.class_variable_set(:@@files_from_load_path, nil) + end + def test_nonstring_module_name begin require "irb/completion" @@ -84,6 +90,45 @@ def test_complete_require end end + def test_complete_require_with_pathname_in_load_path + temp_dir = Dir.mktmpdir + File.write(File.join(temp_dir, "foo.rb"), "test") + test_path = Pathname.new(temp_dir) + $LOAD_PATH << test_path + + candidates = IRB::InputCompletor::CompletionProc.("'foo", "require ", "") + assert_include candidates, "'foo" + ensure + $LOAD_PATH.pop if test_path + FileUtils.remove_entry(temp_dir) if temp_dir + end + + def test_complete_require_with_string_convertable_in_load_path + temp_dir = Dir.mktmpdir + File.write(File.join(temp_dir, "foo.rb"), "test") + object = Object.new + object.define_singleton_method(:to_s) { temp_dir } + $LOAD_PATH << object + + candidates = IRB::InputCompletor::CompletionProc.("'foo", "require ", "") + assert_include candidates, "'foo" + ensure + $LOAD_PATH.pop if object + FileUtils.remove_entry(temp_dir) if temp_dir + end + + def test_complete_require_with_malformed_object_in_load_path + object = Object.new + def object.to_s; raise; end + $LOAD_PATH << object + + assert_nothing_raised do + IRB::InputCompletor::CompletionProc.("'foo", "require ", "") + end + ensure + $LOAD_PATH.pop if object + end + def test_complete_require_library_name_first pend 'Need to use virtual library paths' candidates = IRB::InputCompletor::CompletionProc.("'csv", "require ", "") diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index 81b7fe8679eec8..fab38074470ed5 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -158,6 +158,31 @@ def test_history_concurrent_use end end + def test_history_concurrent_use_not_present + backup_home = ENV["HOME"] + backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") + backup_irbrc = ENV.delete("IRBRC") + IRB.conf[:LC_MESSAGES] = IRB::Locale.new + IRB.conf[:SAVE_HISTORY] = 1 + Dir.mktmpdir("test_irb_history_") do |tmpdir| + ENV["HOME"] = tmpdir + io = TestInputMethod.new + io.class::HISTORY.clear + io.load_history + io.class::HISTORY.concat(%w"line1 line2") + + history_file = IRB.rc_file("_history") + assert_not_send [File, :file?, history_file] + File.write(history_file, "line0\n") + io.save_history + assert_equal(%w"line0 line1 line2", File.read(history_file).split) + end + ensure + ENV["HOME"] = backup_home + ENV["XDG_CONFIG_HOME"] = backup_xdg_config_home + ENV["IRBRC"] = backup_irbrc + end + private def assert_history(expected_history, initial_irb_history, input) @@ -166,7 +191,7 @@ def assert_history(expected_history, initial_irb_history, input) backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") IRB.conf[:LC_MESSAGES] = IRB::Locale.new actual_history = nil - Dir.mktmpdir("test_irb_history_#{$$}") do |tmpdir| + Dir.mktmpdir("test_irb_history_") do |tmpdir| ENV["HOME"] = tmpdir open(IRB.rc_file("_history"), "w") do |f| f.write(initial_irb_history) diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb index d8c7c79263be4a..3293b98d345264 100644 --- a/test/irb/test_init.rb +++ b/test/irb/test_init.rb @@ -16,6 +16,7 @@ def setup def teardown ENV.update(@backup_env) FileUtils.rm_rf(@tmpdir) + IRB.conf.delete(:SCRIPT) end def test_setup_with_argv_preserves_global_argv @@ -87,6 +88,50 @@ def test_no_color_environment_variable IRB.conf[:USE_COLORIZE] = orig_use_colorize end + def test_noscript + argv = %w[--noscript -- -f] + IRB.setup(eval("__FILE__"), argv: argv) + assert_nil IRB.conf[:SCRIPT] + assert_equal(['-f'], argv) + + argv = %w[--noscript -- a] + IRB.setup(eval("__FILE__"), argv: argv) + assert_nil IRB.conf[:SCRIPT] + assert_equal(['a'], argv) + + argv = %w[--noscript a] + IRB.setup(eval("__FILE__"), argv: argv) + assert_nil IRB.conf[:SCRIPT] + assert_equal(['a'], argv) + + argv = %w[--script --noscript a] + IRB.setup(eval("__FILE__"), argv: argv) + assert_nil IRB.conf[:SCRIPT] + assert_equal(['a'], argv) + + argv = %w[--noscript --script a] + IRB.setup(eval("__FILE__"), argv: argv) + assert_equal('a', IRB.conf[:SCRIPT]) + assert_equal([], argv) + end + + def test_dash + argv = %w[-] + IRB.setup(eval("__FILE__"), argv: argv) + assert_equal('-', IRB.conf[:SCRIPT]) + assert_equal([], argv) + + argv = %w[-- -] + IRB.setup(eval("__FILE__"), argv: argv) + assert_equal('-', IRB.conf[:SCRIPT]) + assert_equal([], argv) + + argv = %w[-- - -f] + IRB.setup(eval("__FILE__"), argv: argv) + assert_equal('-', IRB.conf[:SCRIPT]) + assert_equal(['-f'], argv) + end + private def with_argv(argv) diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb index e9471273f472c5..0508645ac55b28 100644 --- a/test/net/http/test_http.rb +++ b/test/net/http/test_http.rb @@ -178,13 +178,8 @@ def test_proxy_eh_ENV_with_user http = Net::HTTP.new 'hostname.example' assert_equal true, http.proxy? - if Net::HTTP::ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE - assert_equal 'foo', http.proxy_user - assert_equal 'bar', http.proxy_pass - else - assert_nil http.proxy_user - assert_nil http.proxy_pass - end + assert_equal 'foo', http.proxy_user + assert_equal 'bar', http.proxy_pass end end @@ -195,13 +190,8 @@ def test_proxy_eh_ENV_with_urlencoded_user http = Net::HTTP.new 'hostname.example' assert_equal true, http.proxy? - if Net::HTTP::ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE - assert_equal "Y\\X", http.proxy_user - assert_equal "R%S] ?X", http.proxy_pass - else - assert_nil http.proxy_user - assert_nil http.proxy_pass - end + assert_equal "Y\\X", http.proxy_user + assert_equal "R%S] ?X", http.proxy_pass end end diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb index ef8ad91668abcd..76fb28808c6c52 100644 --- a/test/rdoc/test_rdoc_parser_ruby.rb +++ b/test/rdoc/test_rdoc_parser_ruby.rb @@ -1960,10 +1960,10 @@ def meth_with_args_before def test_parse_method_bracket util_parser <<-RUBY class C - def [] end - def self.[] end - def []= end - def self.[]= end + def []; end + def self.[]; end + def []=; end + def self.[]=; end end RUBY diff --git a/test/reline/test_key_actor_vi.rb b/test/reline/test_key_actor_vi.rb index b3d49c9bbb2ddb..6e1d9dbbdabeaa 100644 --- a/test/reline/test_key_actor_vi.rb +++ b/test/reline/test_key_actor_vi.rb @@ -1454,4 +1454,12 @@ def test_vi_kill_line_prev assert_cursor_max(1) assert_line('c') end + + def test_vi_motion_operators + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + + assert_nothing_raised do + input_keys("test = { foo: bar }\C-[BBBldt}b") + end + end end diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb index 67b3a936d4f28e..88e2ddcb7b6f6e 100644 --- a/test/ruby/test_call.rb +++ b/test/ruby/test_call.rb @@ -47,12 +47,19 @@ def test_safe_call assert_equal(5, o.y) o&.z ||= 6 assert_equal(6, o.z) + o&.z &&= 7 + assert_equal(7, o.z) o = nil assert_nil(o&.x) assert_nothing_raised(NoMethodError) {o&.x = raise} + assert_nothing_raised(NoMethodError) {o&.x = raise; nil} assert_nothing_raised(NoMethodError) {o&.x *= raise} assert_nothing_raised(NoMethodError) {o&.x *= raise; nil} + assert_nothing_raised(NoMethodError) {o&.x ||= raise} + assert_nothing_raised(NoMethodError) {o&.x ||= raise; nil} + assert_nothing_raised(NoMethodError) {o&.x &&= raise} + assert_nothing_raised(NoMethodError) {o&.x &&= raise; nil} end def test_safe_call_evaluate_arguments_only_method_call_is_made diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb index 4a6dd932ed2eff..64af8b488a6c58 100644 --- a/test/ruby/test_encoding.rb +++ b/test/ruby/test_encoding.rb @@ -57,6 +57,7 @@ def test_find def test_replicate assert_separately([], "#{<<~'END;'}") + Warning[:deprecated] = false assert_instance_of(Encoding, Encoding::UTF_8.replicate("UTF-8-ANOTHER#{Time.now.to_f}")) assert_instance_of(Encoding, Encoding::ISO_2022_JP.replicate("ISO-2022-JP-ANOTHER#{Time.now.to_f}")) bug3127 = '[ruby-dev:40954]' @@ -69,7 +70,7 @@ def test_extra_encoding assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; 200.times {|i| - Encoding::UTF_8.replicate("dummy#{i}") + EnvUtil.suppress_warning { Encoding::UTF_8.replicate("dummy#{i}") } } e = Encoding.list.last format = "%d".force_encoding(e) @@ -82,7 +83,7 @@ def test_extra_encoding name = "A" * 64 Encoding.list.each do |enc| - assert_raise(ArgumentError) {enc.replicate(name)} + assert_raise(ArgumentError) { EnvUtil.suppress_warning { enc.replicate(name) } } name.succ! end end; diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 0b05ff7c51bdfd..b87cef3fb637d2 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -478,6 +478,12 @@ def test_inspect def to_s; ""; end end assert_equal(e.inspect, e.new.inspect) + + # https://bugs.ruby-lang.org/issues/18170#note-13 + assert_equal('#', Exception.new("foo\nbar").inspect) + assert_equal('#', Exception.new("foo bar").inspect) + assert_equal('#', Exception.new("foo\\bar").inspect) + assert_equal('#', Exception.new('"foo\nbar"').inspect) end def test_to_s @@ -1053,7 +1059,7 @@ def capture_warning_warn(category: false) warning << [str, category] end else - define_method(:warn) do |str| + define_method(:warn) do |str, category: nil| warning << str end end diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index d6fcf16ddd1ee2..f791b4415dd828 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -2213,6 +2213,14 @@ def test_sysread_with_not_empty_buffer end) end + def test_sysread_with_negative_length + make_tempfile {|t| + open(t.path) do |f| + assert_raise(ArgumentError) { f.sysread(-1) } + end + } + end + def test_flag make_tempfile {|t| assert_raise(ArgumentError) do diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 0f8a9c5e809edb..88b0a0280ae3c6 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -235,17 +235,59 @@ def test_get_string :F64 => [-1.0, 0.0, 0.5, 1.0, 128.0], } - def test_get_set_primitives + def test_get_set_value buffer = IO::Buffer.new(128) - RANGES.each do |type, values| + RANGES.each do |data_type, values| values.each do |value| - buffer.set_value(type, 0, value) - assert_equal value, buffer.get_value(type, 0), "Converting #{value} as #{type}." + buffer.set_value(data_type, 0, value) + assert_equal value, buffer.get_value(data_type, 0), "Converting #{value} as #{data_type}." end end end + def test_get_set_values + buffer = IO::Buffer.new(128) + + RANGES.each do |data_type, values| + format = [data_type] * values.size + + buffer.set_values(format, 0, values) + assert_equal values, buffer.get_values(format, 0), "Converting #{values} as #{format}." + end + end + + def test_values + buffer = IO::Buffer.new(128) + + RANGES.each do |data_type, values| + format = [data_type] * values.size + + buffer.set_values(format, 0, values) + assert_equal values, buffer.values(data_type, 0, values.size), "Reading #{values} as #{format}." + end + end + + def test_each + buffer = IO::Buffer.new(128) + + RANGES.each do |data_type, values| + format = [data_type] * values.size + data_type_size = IO::Buffer.size_of(data_type) + values_with_offsets = values.map.with_index{|value, index| [index * data_type_size, value]} + + buffer.set_values(format, 0, values) + assert_equal values_with_offsets, buffer.each(data_type, 0, values.size).to_a, "Reading #{values} as #{data_type}." + end + end + + def test_each_byte + string = "The quick brown fox jumped over the lazy dog." + buffer = IO::Buffer.for(string) + + assert_equal string.bytes, buffer.each_byte.to_a + end + def test_clear buffer = IO::Buffer.new(16) buffer.set_string("Hello World!") diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index 9978072744299c..94b5323ab07b21 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -247,17 +247,17 @@ def self.assert_equal_not_same(kw, res) assert_not_same(kw, res) end - def self.y(**kw) kw end - m = method(:y) - assert_equal(false, y(**{}).frozen?) - assert_equal_not_same(kw, y(**kw)) - assert_equal_not_same(h, y(**h)) - assert_equal(false, send(:y, **{}).frozen?) - assert_equal_not_same(kw, send(:y, **kw)) - assert_equal_not_same(h, send(:y, **h)) - assert_equal(false, public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, public_send(:y, **kw)) - assert_equal_not_same(h, public_send(:y, **h)) + def self.yo(**kw) kw end + m = method(:yo) + assert_equal(false, yo(**{}).frozen?) + assert_equal_not_same(kw, yo(**kw)) + assert_equal_not_same(h, yo(**h)) + assert_equal(false, send(:yo, **{}).frozen?) + assert_equal_not_same(kw, send(:yo, **kw)) + assert_equal_not_same(h, send(:yo, **h)) + assert_equal(false, public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, public_send(:yo, **kw)) + assert_equal_not_same(h, public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -266,25 +266,25 @@ def self.y(**kw) kw end assert_equal_not_same(h, m.send(:call, **h)) m = method(:send) - assert_equal(false, m.(:y, **{}).frozen?) - assert_equal_not_same(kw, m.(:y, **kw)) - assert_equal_not_same(h, m.(:y, **h)) - assert_equal(false, m.send(:call, :y, **{}).frozen?) - assert_equal_not_same(kw, m.send(:call, :y, **kw)) - assert_equal_not_same(h, m.send(:call, :y, **h)) - - singleton_class.send(:remove_method, :y) - define_singleton_method(:y) { |**kw| kw } - m = method(:y) - assert_equal(false, y(**{}).frozen?) - assert_equal_not_same(kw, y(**kw)) - assert_equal_not_same(h, y(**h)) - assert_equal(false, send(:y, **{}).frozen?) - assert_equal_not_same(kw, send(:y, **kw)) - assert_equal_not_same(h, send(:y, **h)) - assert_equal(false, public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, public_send(:y, **kw)) - assert_equal_not_same(h, public_send(:y, **h)) + assert_equal(false, m.(:yo, **{}).frozen?) + assert_equal_not_same(kw, m.(:yo, **kw)) + assert_equal_not_same(h, m.(:yo, **h)) + assert_equal(false, m.send(:call, :yo, **{}).frozen?) + assert_equal_not_same(kw, m.send(:call, :yo, **kw)) + assert_equal_not_same(h, m.send(:call, :yo, **h)) + + singleton_class.send(:remove_method, :yo) + define_singleton_method(:yo) { |**kw| kw } + m = method(:yo) + assert_equal(false, yo(**{}).frozen?) + assert_equal_not_same(kw, yo(**kw)) + assert_equal_not_same(h, yo(**h)) + assert_equal(false, send(:yo, **{}).frozen?) + assert_equal_not_same(kw, send(:yo, **kw)) + assert_equal_not_same(h, send(:yo, **h)) + assert_equal(false, public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, public_send(:yo, **kw)) + assert_equal_not_same(h, public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -292,17 +292,17 @@ def self.y(**kw) kw end assert_equal_not_same(kw, m.send(:call, **kw)) assert_equal_not_same(h, m.send(:call, **h)) - y = lambda { |**kw| kw } - m = y.method(:call) - assert_equal(false, y.(**{}).frozen?) - assert_equal_not_same(kw, y.(**kw)) - assert_equal_not_same(h, y.(**h)) - assert_equal(false, y.send(:call, **{}).frozen?) - assert_equal_not_same(kw, y.send(:call, **kw)) - assert_equal_not_same(h, y.send(:call, **h)) - assert_equal(false, y.public_send(:call, **{}).frozen?) - assert_equal_not_same(kw, y.public_send(:call, **kw)) - assert_equal_not_same(h, y.public_send(:call, **h)) + yo = lambda { |**kw| kw } + m = yo.method(:call) + assert_equal(false, yo.(**{}).frozen?) + assert_equal_not_same(kw, yo.(**kw)) + assert_equal_not_same(h, yo.(**h)) + assert_equal(false, yo.send(:call, **{}).frozen?) + assert_equal_not_same(kw, yo.send(:call, **kw)) + assert_equal_not_same(h, yo.send(:call, **h)) + assert_equal(false, yo.public_send(:call, **{}).frozen?) + assert_equal_not_same(kw, yo.public_send(:call, **kw)) + assert_equal_not_same(h, yo.public_send(:call, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -310,17 +310,17 @@ def self.y(**kw) kw end assert_equal_not_same(kw, m.send(:call, **kw)) assert_equal_not_same(h, m.send(:call, **h)) - y = :y.to_proc - m = y.method(:call) - assert_equal(false, y.(self, **{}).frozen?) - assert_equal_not_same(kw, y.(self, **kw)) - assert_equal_not_same(h, y.(self, **h)) - assert_equal(false, y.send(:call, self, **{}).frozen?) - assert_equal_not_same(kw, y.send(:call, self, **kw)) - assert_equal_not_same(h, y.send(:call, self, **h)) - assert_equal(false, y.public_send(:call, self, **{}).frozen?) - assert_equal_not_same(kw, y.public_send(:call, self, **kw)) - assert_equal_not_same(h, y.public_send(:call, self, **h)) + yo = :yo.to_proc + m = yo.method(:call) + assert_equal(false, yo.(self, **{}).frozen?) + assert_equal_not_same(kw, yo.(self, **kw)) + assert_equal_not_same(h, yo.(self, **h)) + assert_equal(false, yo.send(:call, self, **{}).frozen?) + assert_equal_not_same(kw, yo.send(:call, self, **kw)) + assert_equal_not_same(h, yo.send(:call, self, **h)) + assert_equal(false, yo.public_send(:call, self, **{}).frozen?) + assert_equal_not_same(kw, yo.public_send(:call, self, **kw)) + assert_equal_not_same(h, yo.public_send(:call, self, **h)) assert_equal(false, m.(self, **{}).frozen?) assert_equal_not_same(kw, m.(self, **kw)) assert_equal_not_same(h, m.(self, **h)) @@ -329,20 +329,20 @@ def self.y(**kw) kw end assert_equal_not_same(h, m.send(:call, self, **h)) c = Class.new do - def y(**kw) kw end + def yo(**kw) kw end end o = c.new - def o.y(**kw) super end - m = o.method(:y) - assert_equal(false, o.y(**{}).frozen?) - assert_equal_not_same(kw, o.y(**kw)) - assert_equal_not_same(h, o.y(**h)) - assert_equal(false, o.send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.send(:y, **kw)) - assert_equal_not_same(h, o.send(:y, **h)) - assert_equal(false, o.public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.public_send(:y, **kw)) - assert_equal_not_same(h, o.public_send(:y, **h)) + def o.yo(**kw) super end + m = o.method(:yo) + assert_equal(false, o.yo(**{}).frozen?) + assert_equal_not_same(kw, o.yo(**kw)) + assert_equal_not_same(h, o.yo(**h)) + assert_equal(false, o.send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.send(:yo, **kw)) + assert_equal_not_same(h, o.send(:yo, **h)) + assert_equal(false, o.public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.public_send(:yo, **kw)) + assert_equal_not_same(h, o.public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -350,17 +350,17 @@ def o.y(**kw) super end assert_equal_not_same(kw, m.send(:call, **kw)) assert_equal_not_same(h, m.send(:call, **h)) - o.singleton_class.send(:remove_method, :y) - def o.y(**kw) super(**kw) end - assert_equal(false, o.y(**{}).frozen?) - assert_equal_not_same(kw, o.y(**kw)) - assert_equal_not_same(h, o.y(**h)) - assert_equal(false, o.send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.send(:y, **kw)) - assert_equal_not_same(h, o.send(:y, **h)) - assert_equal(false, o.public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.public_send(:y, **kw)) - assert_equal_not_same(h, o.public_send(:y, **h)) + o.singleton_class.send(:remove_method, :yo) + def o.yo(**kw) super(**kw) end + assert_equal(false, o.yo(**{}).frozen?) + assert_equal_not_same(kw, o.yo(**kw)) + assert_equal_not_same(h, o.yo(**h)) + assert_equal(false, o.send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.send(:yo, **kw)) + assert_equal_not_same(h, o.send(:yo, **h)) + assert_equal(false, o.public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.public_send(:yo, **kw)) + assert_equal_not_same(h, o.public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -372,17 +372,17 @@ def o.y(**kw) super(**kw) end def method_missing(_, **kw) kw end end o = c.new - def o.y(**kw) super end - m = o.method(:y) - assert_equal(false, o.y(**{}).frozen?) - assert_equal_not_same(kw, o.y(**kw)) - assert_equal_not_same(h, o.y(**h)) - assert_equal(false, o.send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.send(:y, **kw)) - assert_equal_not_same(h, o.send(:y, **h)) - assert_equal(false, o.public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.public_send(:y, **kw)) - assert_equal_not_same(h, o.public_send(:y, **h)) + def o.yo(**kw) super end + m = o.method(:yo) + assert_equal(false, o.yo(**{}).frozen?) + assert_equal_not_same(kw, o.yo(**kw)) + assert_equal_not_same(h, o.yo(**h)) + assert_equal(false, o.send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.send(:yo, **kw)) + assert_equal_not_same(h, o.send(:yo, **h)) + assert_equal(false, o.public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.public_send(:yo, **kw)) + assert_equal_not_same(h, o.public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -390,17 +390,17 @@ def o.y(**kw) super end assert_equal_not_same(kw, m.send(:call, **kw)) assert_equal_not_same(h, m.send(:call, **h)) - o.singleton_class.send(:remove_method, :y) - def o.y(**kw) super(**kw) end - assert_equal(false, o.y(**{}).frozen?) - assert_equal_not_same(kw, o.y(**kw)) - assert_equal_not_same(h, o.y(**h)) - assert_equal(false, o.send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.send(:y, **kw)) - assert_equal_not_same(h, o.send(:y, **h)) - assert_equal(false, o.public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, o.public_send(:y, **kw)) - assert_equal_not_same(h, o.public_send(:y, **h)) + o.singleton_class.send(:remove_method, :yo) + def o.yo(**kw) super(**kw) end + assert_equal(false, o.yo(**{}).frozen?) + assert_equal_not_same(kw, o.yo(**kw)) + assert_equal_not_same(h, o.yo(**h)) + assert_equal(false, o.send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.send(:yo, **kw)) + assert_equal_not_same(h, o.send(:yo, **h)) + assert_equal(false, o.public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, o.public_send(:yo, **kw)) + assert_equal_not_same(h, o.public_send(:yo, **h)) assert_equal(false, m.(**{}).frozen?) assert_equal_not_same(kw, m.(**kw)) assert_equal_not_same(h, m.(**h)) @@ -436,17 +436,17 @@ def initialize(**kw) @kw = kw end assert_equal_not_same(h, m.(**h)) assert_equal_not_same(h, m.send(:call, **h)) - singleton_class.send(:remove_method, :y) + singleton_class.send(:remove_method, :yo) def self.method_missing(_, **kw) kw end - assert_equal(false, y(**{}).frozen?) - assert_equal_not_same(kw, y(**kw)) - assert_equal_not_same(h, y(**h)) - assert_equal(false, send(:y, **{}).frozen?) - assert_equal_not_same(kw, send(:y, **kw)) - assert_equal_not_same(h, send(:y, **h)) - assert_equal(false, public_send(:y, **{}).frozen?) - assert_equal_not_same(kw, public_send(:y, **kw)) - assert_equal_not_same(h, public_send(:y, **h)) + assert_equal(false, yo(**{}).frozen?) + assert_equal_not_same(kw, yo(**kw)) + assert_equal_not_same(h, yo(**h)) + assert_equal(false, send(:yo, **{}).frozen?) + assert_equal_not_same(kw, send(:yo, **kw)) + assert_equal_not_same(h, send(:yo, **h)) + assert_equal(false, public_send(:yo, **{}).frozen?) + assert_equal_not_same(kw, public_send(:yo, **kw)) + assert_equal_not_same(h, public_send(:yo, **h)) end def test_regular_kwsplat diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb index da04ae7fa72029..28293ffffcbc55 100644 --- a/test/ruby/test_m17n.rb +++ b/test/ruby/test_m17n.rb @@ -226,38 +226,16 @@ def test_string_inspect_encoding end end - STR_WITHOUT_BOM = "\u3042".freeze - STR_WITH_BOM = "\uFEFF\u3042".freeze - bug8940 = '[ruby-core:59757] [Bug #8940]' - bug9415 = '[ruby-dev:47895] [Bug #9415]' - %w/UTF-16 UTF-32/.each do |enc| - %w/BE LE/.each do |endian| - bom = "\uFEFF".encode("#{enc}#{endian}").force_encoding(enc) - - define_method("test_utf_16_32_inspect(#{enc}#{endian})") do - s = STR_WITHOUT_BOM.encode(enc + endian) - # When a UTF-16/32 string doesn't have a BOM, - # inspect as a dummy encoding string. - assert_equal(s.dup.force_encoding("ISO-2022-JP").inspect, - s.dup.force_encoding(enc).inspect) - assert_normal_exit("#{bom.b.dump}.force_encoding('#{enc}').inspect", bug8940) - end - - define_method("test_utf_16_32_codepoints(#{enc}#{endian})") do - assert_equal([0xFEFF], bom.codepoints, bug9415) - end - - define_method("test_utf_16_32_ord(#{enc}#{endian})") do - assert_equal(0xFEFF, bom.ord, bug9415) - end - - define_method("test_utf_16_32_inspect(#{enc}#{endian}-BOM)") do - s = STR_WITH_BOM.encode(enc + endian) - # When a UTF-16/32 string has a BOM, - # inspect as a particular encoding string. - assert_equal(s.inspect, - s.dup.force_encoding(enc).inspect) - end + def test_utf_dummy_are_like_regular_dummy_encodings + [Encoding::UTF_16, Encoding::UTF_32].each do |enc| + s = "\u3042".encode("UTF-32BE") + assert_equal(s.dup.force_encoding("ISO-2022-JP").inspect, s.dup.force_encoding(enc).inspect) + s = "\x00\x00\xFE\xFF" + assert_equal(s.dup.force_encoding("ISO-2022-JP").inspect, s.dup.force_encoding(enc).inspect) + + assert_equal [0, 0, 254, 255], "\x00\x00\xFE\xFF".force_encoding(enc).codepoints + assert_equal 0, "\x00\x00\xFE\xFF".force_encoding(enc).ord + assert_equal 255, "\xFF\xFE\x00\x00".force_encoding(enc).ord end end diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index 36731e14f9609d..fbb934dc84a290 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -1570,6 +1570,18 @@ def test_one_line assert_equal false, (1 in 2) end + def test_bug18990 + {a: 0} => a: + assert_equal 0, a + {a: 0} => a: + assert_equal 0, a + + {a: 0} in a: + assert_equal 0, a + {a: 0} in a: + assert_equal 0, a + end + ################################################################ def test_single_pattern_error_value_pattern diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index c4888598a8930a..10c4aadadf2605 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1876,6 +1876,28 @@ def test_daemon_pid assert_not_equal(cpid, dpid) end + def test_daemon_detached + IO.popen("-", "r+") do |f| + if f + assert_equal(f.pid, Process.wait(f.pid)) + + dpid, ppid, dsid = 3.times.map {Integer(f.gets)} + + message = "daemon #{dpid} should be detached" + assert_not_equal($$, ppid, message) # would be 1 almost always + assert_raise(Errno::ECHILD, message) {Process.wait(dpid)} + assert_kind_of(Integer, Process.kill(0, dpid), message) + assert_equal(dpid, dsid) + + break # close f, and let the daemon resume and exit + end + Process.setsid rescue nil + Process.daemon(false, true) + puts $$, Process.ppid, Process.getsid + $stdin.gets # wait for the above assertions using signals + end + end + if File.directory?("/proc/self/task") && /netbsd[a-z]*[1-6]/ !~ RUBY_PLATFORM def test_daemon_no_threads pid, data = IO.popen("-", "r+") do |f| diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb index 73737be0ad7457..24ee9b9533c8a2 100644 --- a/test/ruby/test_transcode.rb +++ b/test/ruby/test_transcode.rb @@ -2232,12 +2232,12 @@ def fallback.[](x) assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback)) end - bug8940 = '[ruby-core:57318] [Bug #8940]' - %w[UTF-32 UTF-16].each do |enc| - define_method("test_pseudo_encoding_inspect(#{enc})") do - assert_normal_exit("'aaa'.encode('#{enc}').inspect", bug8940) - assert_equal(4, 'aaa'.encode(enc).length, "should count in #{enc} with BOM") - end + def test_pseudo_encoding_inspect + s = 'aaa'.encode "UTF-16" + assert_equal '"\xFE\xFF\x00\x61\x00\x61\x00\x61"', s.inspect + + s = 'aaa'.encode "UTF-32" + assert_equal '"\x00\x00\xFE\xFF\x00\x00\x00\x61\x00\x00\x00\x61\x00\x00\x00\x61"', s.inspect end def test_encode_with_invalid_chars diff --git a/test/rubygems/packages/Bluebie-legs-0.6.2.gem b/test/rubygems/packages/Bluebie-legs-0.6.2.gem new file mode 100644 index 00000000000000..60918f3bc53abc Binary files /dev/null and b/test/rubygems/packages/Bluebie-legs-0.6.2.gem differ diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index bbb3e6dd0a1ed1..9070b9342ed210 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -1357,8 +1357,8 @@ def test_self_use_paths_with_nils def test_setting_paths_does_not_warn_about_unknown_keys stdout, stderr = capture_output do - Gem.paths = { "foo" => [], - "bar" => Object.new, + Gem.paths = { "foo" => [], + "bar" => Object.new, "GEM_HOME" => Gem.paths.home, "GEM_PATH" => "foo" } end diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb index 58bc7576da8c12..1b70eac9eb959a 100644 --- a/test/rubygems/test_gem_commands_update_command.rb +++ b/test/rubygems/test_gem_commands_update_command.rb @@ -669,10 +669,10 @@ def test_handle_options_system @cmd.handle_options %w[--system] expected = { - :args => [], + :args => [], :document => %w[ri], - :force => false, - :system => true, + :force => false, + :system => true, } assert_equal expected, @cmd.options @@ -688,10 +688,10 @@ def test_handle_options_system_specific @cmd.handle_options %w[--system 1.3.7] expected = { - :args => [], + :args => [], :document => %w[ri], - :force => false, - :system => "1.3.7", + :force => false, + :system => "1.3.7", } assert_equal expected, @cmd.options diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb index e23773a133c9d1..14adddf1c4097d 100644 --- a/test/rubygems/test_gem_config_file.rb +++ b/test/rubygems/test_gem_config_file.rb @@ -330,7 +330,7 @@ def test_load_api_keys util_config_file assert_equal({ :rubygems => "701229f217cdf23b1344c7b4b54ca97", - :other => "a5fdbb6ba150cbb83aad2bb2fede64c" }, @cfg.api_keys) + :other => "a5fdbb6ba150cbb83aad2bb2fede64c" }, @cfg.api_keys) end def test_load_api_keys_bad_permission diff --git a/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb b/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb index 88581ea4d8039a..92bd893a1820e1 100644 --- a/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb +++ b/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb @@ -5,22 +5,22 @@ class TestGemExtCargoBuilderLinkFlagConverter < Gem::TestCase CASES = { - test_search_path_basic: ["-L/usr/local/lib", ["-L", "native=/usr/local/lib"]], - test_search_path_space: ["-L /usr/local/lib", ["-L", "native=/usr/local/lib"]], - test_search_path_space_in_path: ["-L/usr/local/my\ lib", ["-L", "native=/usr/local/my\ lib"]], - test_simple_lib: ["-lfoo", ["-l", "foo"]], - test_lib_with_nonascii: ["-lws2_32", ["-l", "ws2_32"]], - test_simple_lib_space: ["-l foo", ["-l", "foo"]], - test_verbose_lib_space: ["--library=foo", ["-l", "foo"]], - test_libstatic_with_colon: ["-l:libssp.a", ["-l", "static=ssp"]], - test_libstatic_with_colon_space: ["-l :libssp.a", ["-l", "static=ssp"]], + test_search_path_basic: ["-L/usr/local/lib", ["-L", "native=/usr/local/lib"]], + test_search_path_space: ["-L /usr/local/lib", ["-L", "native=/usr/local/lib"]], + test_search_path_space_in_path: ["-L/usr/local/my\ lib", ["-L", "native=/usr/local/my\ lib"]], + test_simple_lib: ["-lfoo", ["-l", "foo"]], + test_lib_with_nonascii: ["-lws2_32", ["-l", "ws2_32"]], + test_simple_lib_space: ["-l foo", ["-l", "foo"]], + test_verbose_lib_space: ["--library=foo", ["-l", "foo"]], + test_libstatic_with_colon: ["-l:libssp.a", ["-l", "static=ssp"]], + test_libstatic_with_colon_space: ["-l :libssp.a", ["-l", "static=ssp"]], test_unconventional_lib_with_colon: ["-l:ssp.a", ["-C", "link_arg=-l:ssp.a"]], - test_dylib_with_colon_space: ["-l :libssp.dylib", ["-l", "dylib=ssp"]], - test_so_with_colon_space: ["-l :libssp.so", ["-l", "dylib=ssp"]], - test_dll_with_colon_space: ["-l :libssp.dll", ["-l", "dylib=ssp"]], - test_framework: ["-F/some/path", ["-l", "framework=/some/path"]], - test_framework_space: ["-F /some/path", ["-l", "framework=/some/path"]], - test_non_lib_dash_l: ["test_rubygems_20220413-976-lemgf9/prefix", ["-C", "link_arg=test_rubygems_20220413-976-lemgf9/prefix"]], + test_dylib_with_colon_space: ["-l :libssp.dylib", ["-l", "dylib=ssp"]], + test_so_with_colon_space: ["-l :libssp.so", ["-l", "dylib=ssp"]], + test_dll_with_colon_space: ["-l :libssp.dll", ["-l", "dylib=ssp"]], + test_framework: ["-F/some/path", ["-l", "framework=/some/path"]], + test_framework_space: ["-F /some/path", ["-l", "framework=/some/path"]], + test_non_lib_dash_l: ["test_rubygems_20220413-976-lemgf9/prefix", ["-C", "link_arg=test_rubygems_20220413-976-lemgf9/prefix"]], }.freeze CASES.each do |test_name, (arg, expected)| diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb index 9295f42dba0140..eebe4d86d0a14a 100644 --- a/test/rubygems/test_gem_package.rb +++ b/test/rubygems/test_gem_package.rb @@ -510,6 +510,21 @@ def test_extract_files_empty assert_path_exist @destination end + def test_extract_file_permissions + pend "chmod not supported" if win_platform? + + gem_with_long_permissions = File.expand_path("packages/Bluebie-legs-0.6.2.gem", __dir__) + + package = Gem::Package.new gem_with_long_permissions + + package.extract_files @destination + + filepath = File.join @destination, "README.rdoc" + assert_path_exist filepath + + assert_equal 0104444, File.stat(filepath).mode + end + def test_extract_tar_gz_absolute package = Gem::Package.new @gem diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb index 3ff4f0b30b214a..ccb0db739ed622 100644 --- a/test/rubygems/test_gem_package_tar_header.rb +++ b/test/rubygems/test_gem_package_tar_header.rb @@ -7,19 +7,19 @@ def setup super header = { - :name => "x", - :mode => 0644, - :uid => 1000, - :gid => 10000, - :size => 100, - :mtime => 12345, + :name => "x", + :mode => 0644, + :uid => 1000, + :gid => 10000, + :size => 100, + :mtime => 12345, :typeflag => "0", :linkname => "link", - :uname => "user", - :gname => "group", + :uname => "user", + :gname => "group", :devmajor => 1, :devminor => 2, - :prefix => "y", + :prefix => "y", } @tar_header = Gem::Package::TarHeader.new header @@ -76,10 +76,10 @@ def test_initialize_bad def test_initialize_typeflag header = { - :mode => "", - :name => "", - :prefix => "", - :size => "", + :mode => "", + :name => "", + :prefix => "", + :size => "", :typeflag => "", } diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index 576f150219cd3a..3bd4a862c524a0 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -85,66 +85,66 @@ def test_self_new def test_initialize test_cases = { - "amd64-freebsd6" => ["amd64", "freebsd", "6"], - "hppa2.0w-hpux11.31" => ["hppa2.0w", "hpux", "11"], - "java" => [nil, "java", nil], - "jruby" => [nil, "java", nil], - "universal-dotnet" => ["universal", "dotnet", nil], - "universal-dotnet2.0" => ["universal", "dotnet", "2.0"], - "universal-dotnet4.0" => ["universal", "dotnet", "4.0"], - "powerpc-aix5.3.0.0" => ["powerpc", "aix", "5"], - "powerpc-darwin7" => ["powerpc", "darwin", "7"], - "powerpc-darwin8" => ["powerpc", "darwin", "8"], - "powerpc-linux" => ["powerpc", "linux", nil], - "powerpc64-linux" => ["powerpc64", "linux", nil], - "sparc-solaris2.10" => ["sparc", "solaris", "2.10"], - "sparc-solaris2.8" => ["sparc", "solaris", "2.8"], - "sparc-solaris2.9" => ["sparc", "solaris", "2.9"], - "universal-darwin8" => ["universal", "darwin", "8"], - "universal-darwin9" => ["universal", "darwin", "9"], - "universal-macruby" => ["universal", "macruby", nil], - "i386-cygwin" => ["x86", "cygwin", nil], - "i686-darwin" => ["x86", "darwin", nil], - "i686-darwin8.4.1" => ["x86", "darwin", "8"], - "i386-freebsd4.11" => ["x86", "freebsd", "4"], - "i386-freebsd5" => ["x86", "freebsd", "5"], - "i386-freebsd6" => ["x86", "freebsd", "6"], - "i386-freebsd7" => ["x86", "freebsd", "7"], - "i386-freebsd" => ["x86", "freebsd", nil], - "universal-freebsd" => ["universal", "freebsd", nil], - "i386-java1.5" => ["x86", "java", "1.5"], - "x86-java1.6" => ["x86", "java", "1.6"], - "i386-java1.6" => ["x86", "java", "1.6"], - "i686-linux" => ["x86", "linux", nil], - "i586-linux" => ["x86", "linux", nil], - "i486-linux" => ["x86", "linux", nil], - "i386-linux" => ["x86", "linux", nil], - "i586-linux-gnu" => ["x86", "linux", "gnu"], - "i386-linux-gnu" => ["x86", "linux", "gnu"], - "i386-mingw32" => ["x86", "mingw32", nil], - "x64-mingw-ucrt" => ["x64", "mingw", "ucrt"], - "i386-mswin32" => ["x86", "mswin32", nil], - "i386-mswin32_80" => ["x86", "mswin32", "80"], - "i386-mswin32-80" => ["x86", "mswin32", "80"], - "x86-mswin32" => ["x86", "mswin32", nil], - "x86-mswin32_60" => ["x86", "mswin32", "60"], - "x86-mswin32-60" => ["x86", "mswin32", "60"], - "i386-netbsdelf" => ["x86", "netbsdelf", nil], - "i386-openbsd4.0" => ["x86", "openbsd", "4.0"], - "i386-solaris2.10" => ["x86", "solaris", "2.10"], - "i386-solaris2.8" => ["x86", "solaris", "2.8"], - "mswin32" => ["x86", "mswin32", nil], - "x86_64-linux" => ["x86_64", "linux", nil], - "x86_64-linux-gnu" => ["x86_64", "linux", "gnu"], - "x86_64-linux-musl" => ["x86_64", "linux", "musl"], - "x86_64-linux-uclibc" => ["x86_64", "linux", "uclibc"], - "arm-linux-eabi" => ["arm", "linux", "eabi"], - "arm-linux-gnueabi" => ["arm", "linux", "gnueabi"], - "arm-linux-musleabi" => ["arm", "linux", "musleabi"], - "arm-linux-uclibceabi" => ["arm", "linux", "uclibceabi"], - "x86_64-openbsd3.9" => ["x86_64", "openbsd", "3.9"], - "x86_64-openbsd4.0" => ["x86_64", "openbsd", "4.0"], - "x86_64-openbsd" => ["x86_64", "openbsd", nil], + "amd64-freebsd6" => ["amd64", "freebsd", "6"], + "hppa2.0w-hpux11.31" => ["hppa2.0w", "hpux", "11"], + "java" => [nil, "java", nil], + "jruby" => [nil, "java", nil], + "universal-dotnet" => ["universal", "dotnet", nil], + "universal-dotnet2.0" => ["universal", "dotnet", "2.0"], + "universal-dotnet4.0" => ["universal", "dotnet", "4.0"], + "powerpc-aix5.3.0.0" => ["powerpc", "aix", "5"], + "powerpc-darwin7" => ["powerpc", "darwin", "7"], + "powerpc-darwin8" => ["powerpc", "darwin", "8"], + "powerpc-linux" => ["powerpc", "linux", nil], + "powerpc64-linux" => ["powerpc64", "linux", nil], + "sparc-solaris2.10" => ["sparc", "solaris", "2.10"], + "sparc-solaris2.8" => ["sparc", "solaris", "2.8"], + "sparc-solaris2.9" => ["sparc", "solaris", "2.9"], + "universal-darwin8" => ["universal", "darwin", "8"], + "universal-darwin9" => ["universal", "darwin", "9"], + "universal-macruby" => ["universal", "macruby", nil], + "i386-cygwin" => ["x86", "cygwin", nil], + "i686-darwin" => ["x86", "darwin", nil], + "i686-darwin8.4.1" => ["x86", "darwin", "8"], + "i386-freebsd4.11" => ["x86", "freebsd", "4"], + "i386-freebsd5" => ["x86", "freebsd", "5"], + "i386-freebsd6" => ["x86", "freebsd", "6"], + "i386-freebsd7" => ["x86", "freebsd", "7"], + "i386-freebsd" => ["x86", "freebsd", nil], + "universal-freebsd" => ["universal", "freebsd", nil], + "i386-java1.5" => ["x86", "java", "1.5"], + "x86-java1.6" => ["x86", "java", "1.6"], + "i386-java1.6" => ["x86", "java", "1.6"], + "i686-linux" => ["x86", "linux", nil], + "i586-linux" => ["x86", "linux", nil], + "i486-linux" => ["x86", "linux", nil], + "i386-linux" => ["x86", "linux", nil], + "i586-linux-gnu" => ["x86", "linux", "gnu"], + "i386-linux-gnu" => ["x86", "linux", "gnu"], + "i386-mingw32" => ["x86", "mingw32", nil], + "x64-mingw-ucrt" => ["x64", "mingw", "ucrt"], + "i386-mswin32" => ["x86", "mswin32", nil], + "i386-mswin32_80" => ["x86", "mswin32", "80"], + "i386-mswin32-80" => ["x86", "mswin32", "80"], + "x86-mswin32" => ["x86", "mswin32", nil], + "x86-mswin32_60" => ["x86", "mswin32", "60"], + "x86-mswin32-60" => ["x86", "mswin32", "60"], + "i386-netbsdelf" => ["x86", "netbsdelf", nil], + "i386-openbsd4.0" => ["x86", "openbsd", "4.0"], + "i386-solaris2.10" => ["x86", "solaris", "2.10"], + "i386-solaris2.8" => ["x86", "solaris", "2.8"], + "mswin32" => ["x86", "mswin32", nil], + "x86_64-linux" => ["x86_64", "linux", nil], + "x86_64-linux-gnu" => ["x86_64", "linux", "gnu"], + "x86_64-linux-musl" => ["x86_64", "linux", "musl"], + "x86_64-linux-uclibc" => ["x86_64", "linux", "uclibc"], + "arm-linux-eabi" => ["arm", "linux", "eabi"], + "arm-linux-gnueabi" => ["arm", "linux", "gnueabi"], + "arm-linux-musleabi" => ["arm", "linux", "musleabi"], + "arm-linux-uclibceabi" => ["arm", "linux", "uclibceabi"], + "x86_64-openbsd3.9" => ["x86_64", "openbsd", "3.9"], + "x86_64-openbsd4.0" => ["x86_64", "openbsd", "4.0"], + "x86_64-openbsd" => ["x86_64", "openbsd", nil], } test_cases.each do |arch, expected| @@ -452,6 +452,13 @@ def test_inspect assert_equal 1, result.scan(/@version=/).size end + def test_gem_platform_match_with_string_argument + util_set_arch "x86_64-linux-musl" + + assert(Gem::Platform.match(Gem::Platform.new("x86_64-linux")), "should match Gem::Platform") + assert(Gem::Platform.match("x86_64-linux"), "should match String platform") + end + def assert_local_match(name) assert_match Gem::Platform.local, name end diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index d8b55a5f7aa389..5f54230c77f901 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -1174,10 +1174,10 @@ def start_ssl_server(config = {}) def start_server(data) null_logger = NilLog.new s = WEBrick::HTTPServer.new( - :Port => 0, - :DocumentRoot => nil, - :Logger => null_logger, - :AccessLog => null_logger + :Port => 0, + :DocumentRoot => nil, + :Logger => null_logger, + :AccessLog => null_logger ) s.mount_proc("/kill") {|req, res| s.shutdown } s.mount_proc("/yaml") do |req, res| diff --git a/test/rubygems/test_gem_request_set.rb b/test/rubygems/test_gem_request_set.rb index 6d14321126e346..0a27400adeadfe 100644 --- a/test/rubygems/test_gem_request_set.rb +++ b/test/rubygems/test_gem_request_set.rb @@ -110,7 +110,7 @@ def test_install_from_gemdeps_install_dir end options = { - :gemdeps => "gem.deps.rb", + :gemdeps => "gem.deps.rb", :install_dir => "#{@gemhome}2", } @@ -575,7 +575,7 @@ def test_install_into_development_shallow rs.resolve options = { - :development => true, + :development => true, :development_shallow => true, } diff --git a/test/rubygems/test_gem_resolver.rb b/test/rubygems/test_gem_resolver.rb index c816d5484ba7ba..54497c6e4f95d8 100644 --- a/test/rubygems/test_gem_resolver.rb +++ b/test/rubygems/test_gem_resolver.rb @@ -391,6 +391,39 @@ def test_does_not_pick_musl_variants_on_non_musl_linux end end + def test_pick_generic_linux_variants_on_musl_linux + util_set_arch "aarch64-linux-musl" do + is = Gem::Resolver::IndexSpecification + + linux = Gem::Platform.new("aarch64-linux") + + spec_fetcher do |fetcher| + fetcher.spec "libv8-node", "15.14.0.1" do |s| + s.platform = linux + end + + fetcher.spec "libv8-node", "15.14.0.1" + end + + v15 = v("15.14.0.1") + source = Gem::Source.new @gem_repo + + s = set + + v15_ruby = is.new s, "libv8-node", v15, source, Gem::Platform::RUBY + v15_linux = is.new s, "libv8-node", v15, source, linux.to_s + + s.add v15_linux + s.add v15_ruby + + ad = make_dep "libv8-node", "= 15.14.0.1" + + res = Gem::Resolver.new([ad], s) + + assert_resolves_to [v15_linux.spec], res + end + end + def test_only_returns_spec_once a1 = util_spec "a", "1", "c" => "= 1" b1 = util_spec "b", "1", "c" => "= 1" diff --git a/test/rubygems/test_gem_resolver_api_set.rb b/test/rubygems/test_gem_resolver_api_set.rb index 5c13311b1b3868..17c6c49a701bcd 100644 --- a/test/rubygems/test_gem_resolver_api_set.rb +++ b/test/rubygems/test_gem_resolver_api_set.rb @@ -36,9 +36,9 @@ def test_find_all spec_fetcher data = [ - { :name => "a", - :number => "1", - :platform => "ruby", + { :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [] }, ] @@ -59,13 +59,13 @@ def test_find_all_prereleases spec_fetcher data = [ - { :name => "a", - :number => "1", - :platform => "ruby", + { :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [] }, - { :name => "a", - :number => "2.a", - :platform => "ruby", + { :name => "a", + :number => "2.a", + :platform => "ruby", :dependencies => [] }, ] @@ -88,9 +88,9 @@ def test_find_all_cache spec_fetcher data = [ - { :name => "a", - :number => "1", - :platform => "ruby", + { :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [] }, ] diff --git a/test/rubygems/test_gem_resolver_api_specification.rb b/test/rubygems/test_gem_resolver_api_specification.rb index 98981e53e94527..96e2f3c8c67815 100644 --- a/test/rubygems/test_gem_resolver_api_specification.rb +++ b/test/rubygems/test_gem_resolver_api_specification.rb @@ -5,8 +5,8 @@ class TestGemResolverAPISpecification < Gem::TestCase def test_initialize set = Gem::Resolver::APISet.new data = { - :name => "rails", - :number => "3.0.3", + :name => "rails", + :number => "3.0.3", :platform => Gem::Platform.local.to_s, :dependencies => [ ["bundler", "~> 1.0"], @@ -44,8 +44,8 @@ def test_fetch_development_dependencies set = Gem::Resolver::APISet.new repo data = { - :name => "rails", - :number => "3.0.3", + :name => "rails", + :number => "3.0.3", :platform => "ruby", :dependencies => [ ["bundler", "~> 1.0"], @@ -71,8 +71,8 @@ def test_fetch_development_dependencies def test_installable_platform_eh set = Gem::Resolver::APISet.new data = { - :name => "a", - :number => "1", + :name => "a", + :number => "1", :platform => "ruby", :dependencies => [], } @@ -82,8 +82,8 @@ def test_installable_platform_eh assert a_spec.installable_platform? data = { - :name => "b", - :number => "1", + :name => "b", + :number => "1", :platform => "cpu-other_platform-1", :dependencies => [], } @@ -93,8 +93,8 @@ def test_installable_platform_eh refute b_spec.installable_platform? data = { - :name => "c", - :number => "1", + :name => "c", + :number => "1", :platform => Gem::Platform.local.to_s, :dependencies => [], } @@ -107,9 +107,9 @@ def test_installable_platform_eh def test_source set = Gem::Resolver::APISet.new data = { - :name => "a", - :number => "1", - :platform => "ruby", + :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [], } @@ -126,9 +126,9 @@ def test_spec dep_uri = URI(@gem_repo) + "info" set = Gem::Resolver::APISet.new dep_uri data = { - :name => "a", - :number => "1", - :platform => "ruby", + :name => "a", + :number => "1", + :platform => "ruby", :dependencies => [], } @@ -150,9 +150,9 @@ def test_spec_jruby_platform dep_uri = URI(@gem_repo) + "info" set = Gem::Resolver::APISet.new dep_uri data = { - :name => "j", - :number => "1", - :platform => "jruby", + :name => "j", + :number => "1", + :platform => "jruby", :dependencies => [], } diff --git a/test/rubygems/test_gem_security_policy.rb b/test/rubygems/test_gem_security_policy.rb index 6680238245c7e4..9217f5a818a8e2 100644 --- a/test/rubygems/test_gem_security_policy.rb +++ b/test/rubygems/test_gem_security_policy.rb @@ -43,22 +43,22 @@ def setup @chain = Gem::Security::Policy.new( "Chain", - :verify_data => true, + :verify_data => true, :verify_signer => true, - :verify_chain => true, - :verify_root => false, - :only_trusted => false, - :only_signed => false + :verify_chain => true, + :verify_root => false, + :only_trusted => false, + :only_signed => false ) @root = Gem::Security::Policy.new( "Root", - :verify_data => true, + :verify_data => true, :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => false, - :only_signed => false + :verify_chain => true, + :verify_root => true, + :only_trusted => false, + :only_signed => false ) end diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 8ce8293f33a742..24a726146685c6 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -1866,9 +1866,9 @@ def test_full_name def test_full_name_windows test_cases = { - "i386-mswin32" => "a-1-x86-mswin32-60", - "i386-mswin32_80" => "a-1-x86-mswin32-80", - "i386-mingw32" => "a-1-x86-mingw32", + "i386-mswin32" => "a-1-x86-mswin32-60", + "i386-mswin32_80" => "a-1-x86-mswin32-80", + "i386-mingw32" => "a-1-x86-mingw32", } test_cases.each do |arch, expected| @@ -1969,10 +1969,10 @@ def test_platform_equals assert_equal Gem::Platform::RUBY, @a1.platform test_cases = { - "i386-mswin32" => ["x86", "mswin32", "60"], + "i386-mswin32" => ["x86", "mswin32", "60"], "i386-mswin32_80" => ["x86", "mswin32", "80"], - "i386-mingw32" => ["x86", "mingw32", nil ], - "x86-darwin8" => ["x86", "darwin", "8" ], + "i386-mingw32" => ["x86", "mingw32", nil ], + "x86-darwin8" => ["x86", "darwin", "8" ], } test_cases.each do |arch, expected| @@ -3449,10 +3449,10 @@ def test_metadata_validates_ok @m1 = quick_gem "m", "1" do |s| s.files = %w[lib/code.rb] s.metadata = { - "one" => "two", - "home" => "three", + "one" => "two", + "home" => "three", "homepage_uri" => "https://example.com/user/repo", - "funding_uri" => "https://example.com/donate", + "funding_uri" => "https://example.com/donate", } end diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index e81a9be0fff3ab..5fcf0d481de91d 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -372,7 +372,7 @@ def test_uninstall_user_install @user_spec = Gem::Specification.find_by_name "b" uninstaller = Gem::Uninstaller.new(@user_spec.name, - :executables => true, + :executables => true, :user_install => true) gem_dir = File.join @user_spec.gem_dir diff --git a/test/test_extlibs.rb b/test/test_extlibs.rb index 958c9ff73ef1cc..1d7f616424701a 100644 --- a/test/test_extlibs.rb +++ b/test/test_extlibs.rb @@ -45,7 +45,6 @@ def windows? check_existence "continuation" check_existence "coverage" check_existence "date" - #check_existence "dbm" # depend on libdbm check_existence "digest" check_existence "digest/bubblebabble" check_existence "digest/md5" @@ -56,7 +55,6 @@ def windows? check_existence "fcntl" check_existence "fiber" check_existence "fiddle" - #check_existence "gdbm" # depend on libgdbm check_existence "io/console" check_existence "io/nonblock" check_existence "io/wait" diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 76de38949def1f..2d3dd162457759 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -159,4 +159,17 @@ def test_fork assert_equal 'timeout', r.read r.close end + + def test_threadgroup + assert_separately(%w[-rtimeout], <<-'end;') + tg = ThreadGroup.new + thr = Thread.new do + tg.add(Thread.current) + Timeout.timeout(10){} + end + thr.join + assert_equal [].to_s, tg.list.to_s + end; + end + end diff --git a/thread.c b/thread.c index ee90386f61d7e7..c9e025d4188fce 100644 --- a/thread.c +++ b/thread.c @@ -2332,16 +2332,16 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing) ret |= rb_signal_exec(th, sig); } th->status = prev_status; + } #if USE_MJIT - // Handle waitpid_signal for MJIT issued by ruby_sigchld_handler. This needs to be done - // outside ruby_sigchld_handler to avoid recursively relying on the SIGCHLD handler. - if (mjit_waitpid_finished) { - mjit_waitpid_finished = false; - mjit_notify_waitpid(WIFEXITED(mjit_waitpid_status) ? WEXITSTATUS(mjit_waitpid_status) : -1); - } -#endif + // Handle waitpid_signal for MJIT issued by ruby_sigchld_handler. This needs to be done + // outside ruby_sigchld_handler to avoid recursively relying on the SIGCHLD handler. + if (mjit_waitpid_finished && th == th->vm->ractor.main_thread) { + mjit_waitpid_finished = false; + mjit_notify_waitpid(WIFEXITED(mjit_waitpid_status) ? WEXITSTATUS(mjit_waitpid_status) : -1); } +#endif /* exception from another thread */ if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) { diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock index 9b097ba710c7cb..2e47e5a894b7ae 100644 --- a/tool/bundler/rubocop_gems.rb.lock +++ b/tool/bundler/rubocop_gems.rb.lock @@ -47,11 +47,13 @@ PLATFORMS aarch64-linux arm64-darwin-20 arm64-darwin-21 + arm64-darwin-22 universal-java-11 universal-java-18 x64-mingw-ucrt x86_64-darwin-19 x86_64-darwin-20 + x86_64-darwin-21 x86_64-linux DEPENDENCIES diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock index 03d81955577a6c..bf21ad5da2c34d 100644 --- a/tool/bundler/standard_gems.rb.lock +++ b/tool/bundler/standard_gems.rb.lock @@ -53,11 +53,13 @@ PLATFORMS aarch64-linux arm64-darwin-20 arm64-darwin-21 + arm64-darwin-22 universal-java-11 universal-java-18 x64-mingw-ucrt x86_64-darwin-19 x86_64-darwin-20 + x86_64-darwin-21 x86_64-linux DEPENDENCIES diff --git a/tool/downloader.rb b/tool/downloader.rb index d3a9f7563754bb..3020322a980a86 100644 --- a/tool/downloader.rb +++ b/tool/downloader.rb @@ -36,6 +36,12 @@ def File.directory? files end class Downloader + def self.find(dlname) + constants.find do |name| + return const_get(name) if dlname.casecmp(name.to_s) == 0 + end + end + def self.https=(https) @@https = https end @@ -48,6 +54,10 @@ def self.https @@https end + def self.get_option(argv, options) + false + end + class GNU < self def self.download(name, *rest) if https? @@ -78,6 +88,21 @@ class Unicode < self INDEX = {} # cache index file information across files in the same directory UNICODE_PUBLIC = "https://www.unicode.org/Public/" + def self.get_option(argv, options) + case argv[0] + when '--unicode-beta' + options[:unicode_beta] = argv[1] + argv.shift(2) + true + when /\A--unicode-beta=(.*)/m + options[:unicode_beta] = $1 + argv.shift + true + else + super + end + end + def self.download(name, dir = nil, since = true, options = {}) options = options.dup unicode_beta = options.delete(:unicode_beta) @@ -173,7 +198,6 @@ def self.download(url, name, dir = nil, since = true, options = {}) options = options.dup url = URI(url) dryrun = options.delete(:dryrun) - options.delete(:unicode_beta) # just to be on the safe side for gems and gcc if name file = Pathname.new(under(dir, name)) @@ -351,45 +375,57 @@ def self.with_retry(max_times, &block) if $0 == __FILE__ since = true options = {} + dl = nil + (args = []).singleton_class.__send__(:define_method, :downloader?) do |arg| + !dl and args.empty? and (dl = Downloader.find(arg)) + end until ARGV.empty? + if ARGV[0] == '--' + ARGV.shift + break if ARGV.empty? + ARGV.shift if args.downloader? ARGV[0] + args.concat(ARGV) + break + end + + if dl and dl.get_option(ARGV, options) + # the downloader dealt with the arguments, and should be removed + # from ARGV. + next + end + case ARGV[0] - when '-d' + when '-d', '--destdir' destdir = ARGV[1] ARGV.shift - when '-p' + when '-p', '--prefix' # strip directory names from the name to download, and add the # prefix instead. prefix = ARGV[1] ARGV.shift - when '-e' + when '-e', '--exist', '--non-existent-only' since = nil - when '-a' + when '-a', '--always' since = false + when '-u', '--update', '--if-modified' + since = true when '-n', '--dryrun' options[:dryrun] = true when '--cache-dir' options[:cache_dir] = ARGV[1] ARGV.shift - when '--unicode-beta' - options[:unicode_beta] = ARGV[1] - ARGV.shift when /\A--cache-dir=(.*)/m options[:cache_dir] = $1 when /\A-/ abort "#{$0}: unknown option #{ARGV[0]}" else - break + args << ARGV[0] unless args.downloader? ARGV[0] end ARGV.shift end - dl = Downloader.constants.find do |name| - ARGV[0].casecmp(name.to_s) == 0 - end unless ARGV.empty? $VERBOSE = true if dl - dl = Downloader.const_get(dl) - ARGV.shift - ARGV.each do |name| + args.each do |name| dir = destdir if prefix name = name.sub(/\A\.\//, '') @@ -409,7 +445,7 @@ def self.with_retry(max_times, &block) dl.download(name, dir, since, options) end else - abort "usage: #{$0} url name" unless ARGV.size == 2 - Downloader.download(ARGV[0], ARGV[1], destdir, since, options) + abort "usage: #{$0} url name" unless args.size == 2 + Downloader.download(args[0], args[1], destdir, since, options) end end diff --git a/enc/unicode/case-folding.rb b/tool/enc-case-folding.rb old mode 100644 new mode 100755 similarity index 98% rename from enc/unicode/case-folding.rb rename to tool/enc-case-folding.rb index 4a29fdebf755ff..6d43a27df854ce --- a/enc/unicode/case-folding.rb +++ b/tool/enc-case-folding.rb @@ -3,12 +3,12 @@ # Usage (for case folding only): # $ wget http://www.unicode.org/Public/UNIDATA/CaseFolding.txt -# $ ruby case-folding.rb CaseFolding.txt -o casefold.h +# $ ruby enc-case-folding.rb CaseFolding.txt -o casefold.h # or (for case folding and case mapping): # $ wget http://www.unicode.org/Public/UNIDATA/CaseFolding.txt # $ wget http://www.unicode.org/Public/UNIDATA/UnicodeData.txt # $ wget http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt -# $ ruby case-folding.rb -m . -o casefold.h +# $ ruby enc-case-folding.rb -m . -o casefold.h # using -d or --debug will include UTF-8 characters in comments for debugging class CaseFolding @@ -158,7 +158,7 @@ def lookup_hash(key, type, data) def display(dest, mapping_data) # print the header dest.print("/* DO NOT EDIT THIS FILE. */\n") - dest.print("/* Generated by enc/unicode/case-folding.rb */\n\n") + dest.print("/* Generated by enc-case-folding.rb */\n\n") versions = version.scan(/\d+/) dest.print("#if defined ONIG_UNICODE_VERSION_STRING && !( \\\n") diff --git a/tool/enc-unicode.rb b/tool/enc-unicode.rb index 93f6e869f842fb..6f2576cc37b1c8 100755 --- a/tool/enc-unicode.rb +++ b/tool/enc-unicode.rb @@ -311,18 +311,19 @@ def get_file(name) def data_foreach(name, &block) fn = get_file(name) warn "Reading #{name}" - if /^emoji/ =~ name - sep = "" - pat = /^# #{Regexp.quote(File.basename(name))}.*^# Version: ([\d.]+)/m - type = :Emoji - else - sep = "\n" - pat = /^# #{File.basename(name).sub(/\./, '-([\\d.]+)\\.')}/ - type = :Unicode - end File.open(fn, 'rb') do |f| - line = f.gets(sep) - unless version = line[pat, 1] + if /^emoji/ =~ name + line = f.gets("") + # Headers till Emoji 13 or 15 + version = line[/^# #{Regexp.quote(File.basename(name))}.*(?:^# Version:|Emoji Version) ([\d.]+)/m, 1] + type = :Emoji + else + # Headers since Emoji 14 or other Unicode data + line = f.gets("\n") + type = :Unicode + end + version ||= line[/^# #{File.basename(name).sub(/\./, '-([\\d.]+)\\.')}/, 1] + unless version raise ArgumentError, <<-ERROR #{name}: no #{type} version #{line.gsub(/^/, '> ')} @@ -330,7 +331,7 @@ def data_foreach(name, &block) end if !(v = $versions[type]) $versions[type] = version - elsif v != version + elsif v != version and "#{v}.0" != version raise ArgumentError, <<-ERROR #{name}: #{type} version mismatch: #{version} to #{v} #{line.gsub(/^/, '> ')} @@ -464,11 +465,7 @@ def write(str) }; #define uniname2ctype_offset(str) offsetof(struct uniname2ctype_pool_t, uniname2ctype_pool_##str) -static const struct uniname2ctype_struct *uniname2ctype_p( -#if !(/*ANSI*/+0) /* if ANSI, old style not to conflict with generated prototype */ - const char *, unsigned int -#endif -); +static const struct uniname2ctype_struct *uniname2ctype_p(register const char *str, register size_t len); %} struct uniname2ctype_struct; %% diff --git a/tool/file2lastrev.rb b/tool/file2lastrev.rb index 008e4b55e5bca6..a3cdcf05c3769f 100755 --- a/tool/file2lastrev.rb +++ b/tool/file2lastrev.rb @@ -66,9 +66,13 @@ def self.output=(output) new_vcs["."] end } -exit unless vcs +unless vcs + # Output only release_date when .git is missing + puts VCS.release_date if @output == :revision_h + exit +end -@output = +output = case @output when :changed, nil Proc.new {|last, changed| @@ -76,7 +80,7 @@ def self.output=(output) } when :revision_h Proc.new {|last, changed, modified, branch, title| - vcs.revision_header(last, modified, branch, title, limit: @limit) + vcs.revision_header(last, modified, modified, branch, title, limit: @limit) } when :doxygen Proc.new {|last, changed| @@ -93,9 +97,12 @@ def self.output=(output) ok = true (ARGV.empty? ? [nil] : ARGV).each do |arg| begin - puts @output[*vcs.get_revisions(arg)] + puts output[*vcs.get_revisions(arg)] rescue => e - next if @suppress_not_found and VCS::NotFoundError === e + if @suppress_not_found and VCS::NotFoundError === e + puts VCS.release_date if @output == :revision_h + next + end warn "#{File.basename(Program)}: #{e.message}" ok = false end diff --git a/tool/id2token.rb b/tool/id2token.rb index d12ea9c08e5b7a..cf730958422389 100755 --- a/tool/id2token.rb +++ b/tool/id2token.rb @@ -5,18 +5,15 @@ BEGIN { require 'optparse' - require_relative 'lib/vpath' - vpath = VPath.new - header = nil opt = OptionParser.new do |o| - vpath.def_options(o) - header = o.order!(ARGV).shift + o.order!(ARGV) end or abort opt.opt_s TOKENS = {} - h = vpath.read(header) rescue abort("#{header} not found in #{vpath.inspect}") - h.scan(/^#define\s+RUBY_TOKEN_(\w+)\s+(\d+)/) do |token, id| + defs = File.join(File.dirname(File.dirname(__FILE__)), "defs/id.def") + ids = eval(File.read(defs), nil, defs) + ids[:token_op].each do |_id, _op, token, id| TOKENS[token] = id end diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 8cb6d8f651cbbf..b2190843c8bc4e 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1,5 +1,20 @@ # frozen_string_literal: true +# Enable deprecation warnings for test-all, so deprecated methods/constants/functions are dealt with early. +Warning[:deprecated] = true + +if ENV['BACKTRACE_FOR_DEPRECATION_WARNINGS'] + Warning.extend Module.new { + def warn(message, category: nil, **kwargs) + if category == :deprecated and $stderr.respond_to?(:puts) + $stderr.puts nil, message, caller, nil + else + super + end + end + } +end + require_relative '../envutil' require_relative '../colorize' require_relative '../leakchecker' diff --git a/tool/lib/vcs.rb b/tool/lib/vcs.rb index 65ab3d4eec1739..87ed15f7d16a44 100644 --- a/tool/lib/vcs.rb +++ b/tool/lib/vcs.rb @@ -95,6 +95,15 @@ def self.define_options(parser, opts = {}) opts end + def self.release_date(time = Time.now - 10) # the same default as make-snapshot + t = time.utc + [ + t.strftime('#define RUBY_RELEASE_YEAR %Y'), + t.strftime('#define RUBY_RELEASE_MONTH %-m'), + t.strftime('#define RUBY_RELEASE_DAY %-d'), + ] + end + attr_reader :srcdir def initialize(path) @@ -204,7 +213,8 @@ def short_revision(rev) revision_handler(rev).short_revision(rev) end - def revision_header(last, modified = nil, branch = nil, title = nil, limit: 20) + # make-snapshot generates only release_date whereas file2lastrev generates both release_date and release_datetime + def revision_header(last, release_date, release_datetime = nil, branch = nil, title = nil, limit: 20) short = short_revision(last) if /[^\x00-\x7f]/ =~ title and title.respond_to?(:force_encoding) title = title.dup.force_encoding("US-ASCII") @@ -225,10 +235,11 @@ def revision_header(last, modified = nil, branch = nil, title = nil, limit: 20) title = title.dump.sub(/\\#/, '#') code << "#define RUBY_LAST_COMMIT_TITLE #{title}" end - if modified - t = modified.utc + if release_datetime + t = release_datetime.utc code << t.strftime('#define RUBY_RELEASE_DATETIME "%FT%TZ"') end + code += VCS.release_date(release_date) code end @@ -386,7 +397,7 @@ def commit end class GIT < self - register(".git") {|path, dir| File.exist?(File.join(path, dir))} + register(".git") { |path, dir| File.exist?(File.join(path, dir)) } COMMAND = ENV["GIT"] || 'git' def cmd_args(cmds, srcdir = nil) @@ -659,7 +670,7 @@ def format_changelog(path, arg, base_url = nil) if %r[^ +(https://github\.com/[^/]+/[^/]+/)commit/\h+\n(?=(?: +\n(?i: +Co-authored-by: .*\n)+)?(?:\n|\Z))] =~ s issue = "#{$1}pull/" - s.gsub!(/\b[Ff]ix(?:e[sd])? \K#(?=\d+)/) {issue} + s.gsub!(/\b(?:(?i:fix(?:e[sd])?) +|GH-)\K#(?=\d+\b)|\(\K#(?=\d+\))/) {issue} end s.gsub!(/ +\n/, "\n") diff --git a/tool/ln_sr.rb b/tool/ln_sr.rb index 6ab412edde9533..2aa8391e17592f 100755 --- a/tool/ln_sr.rb +++ b/tool/ln_sr.rb @@ -3,6 +3,7 @@ target_directory = true noop = false force = false +quiet = false until ARGV.empty? case ARGV[0] @@ -12,6 +13,8 @@ force = true when '-T' target_directory = false + when '-q' + quiet = true else break end @@ -114,9 +117,12 @@ def fu_starting_path?(path) end if File.respond_to?(:symlink) + if quiet and File.identical?(src, dest) + exit + end begin ln_sr(src, dest, verbose: true, target_directory: target_directory, force: force, noop: noop) - rescue NotImplementedError, Errno::EPERM + rescue NotImplementedError, Errno::EPERM, Errno::EACCES else exit end diff --git a/tool/make-snapshot b/tool/make-snapshot index 02b5d182f5a213..22ae360c3ea8e4 100755 --- a/tool/make-snapshot +++ b/tool/make-snapshot @@ -347,7 +347,7 @@ def package(vcs, rev, destdir, tmp = nil) end File.open("#{v}/revision.h", "wb") {|f| - f.puts vcs.revision_header(revision) + f.puts vcs.revision_header(revision, modified) } version ||= (versionhdr = IO.read("#{v}/version.h"))[RUBY_VERSION_PATTERN, 1] version ||= diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 9fded4a98548a6..d0f9bf527bf4c3 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -1,5 +1,9 @@ #!/usr/bin/env ruby # frozen_string_literal: true + +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('./Gemfile', __dir__) +require 'bundler/setup' + require 'etc' require 'fiddle/import' require 'set' @@ -8,20 +12,9 @@ abort "Usage: #{$0} BUILD_DIR" end -if Fiddle::SIZEOF_VOIDP == 8 - arch_bits = 64 -else - arch_bits = 32 -end - # Help ffi-clang find libclang -if arch_bits == 64 - # apt install libclang1 - ENV['LIBCLANG'] ||= Dir.glob("/lib/#{RUBY_PLATFORM}-gnu/libclang-*.so*").grep_v(/-cpp/).sort.last -else - # apt install libclang1:i386 - ENV['LIBCLANG'] ||= Dir.glob("/lib/i386-linux-gnu/libclang-*.so*").sort.last -end +# Hint: apt install libclang1 +ENV['LIBCLANG'] ||= Dir.glob("/lib/#{RUBY_PLATFORM}-gnu/libclang-*.so*").grep_v(/-cpp/).sort.last require 'ffi/clang' class Node < Struct.new( @@ -32,7 +25,6 @@ class Node < Struct.new( :bitwidth, :sizeof_type, :offsetof, - :tokens, :enum_value, :children, keyword_init: true, @@ -43,9 +35,7 @@ class Node < Struct.new( # To ease the maintenance, ffi-clang should be used only inside this class. class HeaderParser def initialize(header, cflags:) - @translation_unit = FFI::Clang::Index.new.parse_translation_unit( - header, cflags, [], { detailed_preprocessing_record: true } - ) + @translation_unit = FFI::Clang::Index.new.parse_translation_unit(header, cflags, [], {}) end def parse @@ -57,10 +47,7 @@ def parse def parse_children(cursor) children = [] cursor.visit_children do |cursor, _parent| - child = parse_cursor(cursor) - if child.kind != :macro_expansion - children << child - end + children << parse_cursor(cursor) next :continue end children @@ -85,11 +72,6 @@ def parse_cursor(cursor) sizeof_type = cursor.type.sizeof end - tokens = nil - if kind == :macro_definition - tokens = @translation_unit.tokenize(cursor.extent).map(&:spelling) - end - enum_value = nil if kind == :enum_constant_decl enum_value = cursor.enum_value @@ -103,7 +85,6 @@ def parse_cursor(cursor) bitwidth: cursor.bitwidth, sizeof_type: sizeof_type, offsetof: offsetof, - tokens: tokens, enum_value: enum_value, children: children, ) @@ -112,54 +93,56 @@ def parse_cursor(cursor) # Convert Node objects to a Ruby binding source. class BindingGenerator + BINDGEN_BEG = '### MJIT bindgen begin ###' + BINDGEN_END = '### MJIT bindgen end ###' DEFAULTS = { '_Bool' => 'CType::Bool.new' } DEFAULTS.default_proc = proc { |_h, k| "CType::Stub.new(:#{k})" } attr_reader :src - # @param macros [Array] Imported macros - # @param enums [Hash{ Symbol => Array }] Imported enum values - # @param types [Array] Imported types + # @param src_path [String] + # @param uses [Array] + # @param values [Hash{ Symbol => Array }] + # @param types [Array] + # @param dynamic_types [Array] #ifdef-dependent immediate types, which need Primitive.cexpr! for type detection + # @param skip_fields [Hash{ Symbol => Array }] Struct fields that are skipped from bindgen # @param ruby_fields [Hash{ Symbol => Array }] Struct VALUE fields that are considered Ruby objects - def initialize(macros:, enums:, types:, ruby_fields:) + def initialize(src_path:, uses:, values:, types:, dynamic_types:, skip_fields:, ruby_fields:) + @preamble, @postamble = split_ambles(src_path) @src = String.new - @macros = macros.sort - @enums = enums.transform_keys(&:to_s).transform_values(&:sort).sort.to_h + @uses = uses.sort + @values = values.transform_values(&:sort) @types = types.sort + @dynamic_types = dynamic_types.sort + @skip_fields = skip_fields.transform_keys(&:to_s) @ruby_fields = ruby_fields.transform_keys(&:to_s) @references = Set.new end def generate(nodes) - # TODO: Support nested declarations - nodes_index = nodes.group_by(&:spelling).transform_values(&:last) - - println "require_relative 'c_type'" - println - println "module RubyVM::MJIT" - println " C = Object.new" - println + println @preamble - # Define macros - @macros.each do |macro| - unless definition = generate_macro(nodes_index[macro]) - raise "Failed to generate macro: #{macro}" - end - println " def C.#{macro} = #{definition}" + # Define USE_* macros + @uses.each do |use| + println " def C.#{use}" + println " Primitive.cexpr! %q{ RBOOL(#{use} != 0) }" + println " end" println end - # Define enum values - @enums.each do |enum, values| + # Define macros/enums + @values.each do |type, values| values.each do |value| - unless definition = generate_enum(nodes_index[enum], value) - raise "Failed to generate enum value: #{value}" - end - println " def C.#{value} = #{definition}" + println " def C.#{value}" + println " Primitive.cexpr! %q{ #{type}2NUM(#{value}) }" + println " end" println end end + # TODO: Support nested declarations + nodes_index = nodes.group_by(&:spelling).transform_values(&:last) + # Define types @types.each do |type| unless definition = generate_node(nodes_index[type]) @@ -171,68 +154,69 @@ def generate(nodes) println end + # Define dynamic types + @dynamic_types.each do |type| + unless generate_node(nodes_index[type])&.start_with?('CType::Immediate') + raise "Non-immediate type is given to dynamic_types: #{type}" + end + println " def C.#{type}" + println " @#{type} ||= CType::Immediate.find(Primitive.cexpr!(\"SIZEOF(#{type})\"), Primitive.cexpr!(\"SIGNED_TYPE_P(#{type})\"))" + println " end" + println + end + # Leave a stub for types that are referenced but not targeted - (@references - @types).each do |type| - println " def C.#{type} = #{DEFAULTS[type]}" + (@references - @types - @dynamic_types).each do |type| + println " def C.#{type}" + println " #{DEFAULTS[type]}" + println " end" println end - chomp - println "end" + print @postamble end private - def generate_macro(node) - if node.spelling.start_with?('USE_') - # Special case: Always force USE_* to be true or false - case node - in Node[kind: :macro_definition, tokens: [_, '0' | '1' => token], children: []] - (Integer(token) == 1).to_s - end - else - # Otherwise, convert a C expression to a Ruby expression when possible - case node - in Node[kind: :macro_definition, tokens: tokens, children: []] - if tokens.first != node.spelling - raise "unexpected first token: '#{tokens.first}' != '#{node.spelling}'" - end - tokens.drop(1).map do |token| - case token - when /\A(0x)?\d+\z/, '(', '-', '<<', ')' - token - when *@enums.values.flatten - "self.#{token}" - else - raise "unexpected macro token: #{token}" - end - end.join(' ') - end - end - end + # Return code before BINDGEN_BEG and code after BINDGEN_END + def split_ambles(src_path) + lines = File.read(src_path).lines - def generate_enum(node, value) - case node - in Node[kind: :enum_decl, children:] - children.find { |c| c.spelling == value }&.enum_value - in Node[kind: :typedef_decl, children: [child]] - generate_enum(child, value) - end + preamble_end = lines.index { |l| l.include?(BINDGEN_BEG) } + raise "`#{BINDGEN_BEG}` was not found in '#{src_path}'" if preamble_end.nil? + + postamble_beg = lines.index { |l| l.include?(BINDGEN_END) } + raise "`#{BINDGEN_END}` was not found in '#{src_path}'" if postamble_beg.nil? + raise "`#{BINDGEN_BEG}` was found after `#{BINDGEN_END}`" if preamble_end >= postamble_beg + + return lines[0..preamble_end].join, lines[postamble_beg..-1].join end # Generate code from a node. Used for constructing a complex nested node. # @param node [Node] - def generate_node(node) + def generate_node(node, sizeof_type: nil) case node&.kind when :struct, :union # node.spelling is often empty for union, but we'd like to give it a name when it has one. buf = +"CType::#{node.kind.to_s.sub(/\A[a-z]/, &:upcase)}.new(\n" - buf << " \"#{node.spelling}\", #{node.sizeof_type},\n" - node.children.each do |child| + buf << " \"#{node.spelling}\", Primitive.cexpr!(\"SIZEOF(#{sizeof_type || node.type})\"),\n" + bit_fields_end = node.children.index { |c| c.bitwidth == -1 } || node.children.size # first non-bit field index + node.children.each_with_index do |child, i| + skip_type = sizeof_type&.gsub(/\(\(struct ([^\)]+) \*\)NULL\)->/, '\1.') || node.spelling + next if @skip_fields.fetch(skip_type, []).include?(child.spelling) field_builder = proc do |field, type| if node.kind == :struct to_ruby = @ruby_fields.fetch(node.spelling, []).include?(field) - " #{field}: [#{node.offsetof.fetch(field)}, #{type}#{', true' if to_ruby}],\n" + if child.bitwidth > 0 + if bit_fields_end <= i # give up offsetof calculation for non-leading bit fields + raise "non-leading bit fields are not supported. consider including '#{field}' in skip_fields." + end + offsetof = node.offsetof.fetch(field) + else + off_type = sizeof_type || "(*((#{node.type} *)NULL))" + offsetof = "Primitive.cexpr!(\"OFFSETOF(#{off_type}, #{field})\")" + end + " #{field}: [#{type}, #{offsetof}#{', true' if to_ruby}],\n" else " #{field}: #{type},\n" end @@ -242,12 +226,17 @@ def generate_node(node) # BitField is struct-specific. So it must be handled here. in Node[kind: :field_decl, spelling:, bitwidth:, children: [_grandchild]] if bitwidth > 0 buf << field_builder.call(spelling, "CType::BitField.new(#{bitwidth}, #{node.offsetof.fetch(spelling) % 8})") + # "(unnamed ...)" struct and union are handled here, which are also struct-specific. + in Node[kind: :field_decl, spelling:, type:, children: [grandchild]] if type.match?(/\((unnamed|anonymous) [^)]+\)\z/) + if sizeof_type + child_type = "#{sizeof_type}.#{child.spelling}" + else + child_type = "((#{node.type} *)NULL)->#{child.spelling}" + end + buf << field_builder.call(spelling, generate_node(grandchild, sizeof_type: child_type).gsub(/^/, ' ').sub(/\A +/, '')) # In most cases, we'd like to let generate_type handle the type unless it's "(unnamed ...)". - in Node[kind: :field_decl, spelling:, type:] if !type.empty? && !type.match?(/\((unnamed|anonymous) [^)]+\)\z/) + in Node[kind: :field_decl, spelling:, type:] if !type.empty? buf << field_builder.call(spelling, generate_type(type)) - # Lastly, "(unnamed ...)" struct and union are handled here, which are also struct-specific. - in Node[kind: :field_decl, spelling:, children: [grandchild]] - buf << field_builder.call(spelling, generate_node(grandchild).gsub(/^/, ' ').sub(/\A +/, '')) else # forward declarations are ignored end end @@ -287,10 +276,16 @@ def generate_type(type) else begin ctype = Fiddle::Importer.parse_ctype(type) - "CType::Immediate.new(#{ctype})" rescue Fiddle::DLError push_target(type) "self.#{type}" + else + # Convert any function pointers to void* to workaround FILE* vs int* + if ctype == Fiddle::TYPE_VOIDP + "CType::Immediate.parse(\"void *\")" + else + "CType::Immediate.parse(#{type.dump})" + end end end end @@ -320,6 +315,7 @@ def push_target(target) end src_dir = File.expand_path('../..', __dir__) +src_path = File.join(src_dir, 'mjit_c.rb') build_dir = File.expand_path(build_dir) cflags = [ src_dir, @@ -330,21 +326,20 @@ def push_target(target) nodes = HeaderParser.new(File.join(src_dir, 'mjit_compiler.h'), cflags: cflags).parse generator = BindingGenerator.new( - macros: %w[ - NOT_COMPILED_STACK_SIZE + src_path: src_path, + uses: %w[ USE_LAZY_LOAD USE_RVARGC - VM_CALL_KW_SPLAT - VM_CALL_TAILCALL ], - enums: { - rb_method_type_t: %w[ - VM_METHOD_TYPE_CFUNC - VM_METHOD_TYPE_ISEQ - ], - vm_call_flag_bits: %w[ + values: { + INT: %w[ + NOT_COMPILED_STACK_SIZE + VM_CALL_KW_SPLAT VM_CALL_KW_SPLAT_bit + VM_CALL_TAILCALL VM_CALL_TAILCALL_bit + VM_METHOD_TYPE_CFUNC + VM_METHOD_TYPE_ISEQ ], }, types: %w[ @@ -352,7 +347,6 @@ def push_target(target) IC IVC RB_BUILTIN - VALUE compile_branch compile_status inlined_call_context @@ -367,6 +361,9 @@ def push_target(target) rb_callcache rb_callinfo rb_cref_t + rb_control_frame_t + rb_execution_context_t + rb_execution_context_struct rb_iseq_constant_body rb_iseq_location_t rb_iseq_struct @@ -379,6 +376,14 @@ def push_target(target) rb_mjit_unit rb_serial_t ], + dynamic_types: %w[ + VALUE + ], + skip_fields: { + 'rb_execution_context_struct.machine': %w[regs], # differs between macOS and Linux + rb_execution_context_struct: %w[method_missing_reason], # non-leading bit fields not supported + rb_iseq_constant_body: %w[yjit_payload], # conditionally defined + }, ruby_fields: { rb_iseq_location_struct: %w[ base_label @@ -390,4 +395,4 @@ def push_target(target) ) generator.generate(nodes) -File.write(File.join(src_dir, "lib/mjit/c_#{arch_bits}.rb"), generator.src) +File.write(src_path, generator.src) diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 23e6a01017b572..6b4ae121d92b22 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -4,6 +4,9 @@ require 'stringio' require_relative 'ruby_vm/helpers/c_escape' +SUBLIBS = {} +REQUIRED = {} + def string_literal(lit, str = []) while lit case lit.first @@ -84,7 +87,7 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil tree = tree[2] next when :method_add_arg - _, mid, (_, (_, args)) = tree + _method_add_arg, mid, (_arg_paren, args) = tree case mid.first when :call _, recv, sep, mid = mid @@ -93,6 +96,11 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil else mid = nil end + # w/ trailing comma: [[:method_add_arg, ...]] + # w/o trailing comma: [:args_add_block, [[:method_add_arg, ...]], false] + if args && args.first == :args_add_block + args = args[1] + end when :vcall _, mid = tree when :command # FCALL @@ -174,6 +182,21 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil end bs[func_name] = [argc, cfunc_name] if func_name + elsif /\Arequire(?:_relative)\z/ =~ mid and args.size == 1 and + (arg1 = args[0])[0] == :string_literal and + (arg1 = arg1[1])[0] == :string_content and + (arg1 = arg1[1])[0] == :@tstring_content and + sublib = arg1[1] + if File.exist?(f = File.join(@dir, sublib)+".rb") + puts "- #{@base}.rb requires #{sublib}" + if REQUIRED[sublib] + warn "!!! #{sublib} is required from #{REQUIRED[sublib]} already; ignored" + else + REQUIRED[sublib] = @base + (SUBLIBS[@base] ||= []) << sublib + end + ARGV.push(f) + end end break unless tree = args end @@ -242,7 +265,9 @@ def generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_nam end def mk_builtin_header file + @dir = File.dirname(file) base = File.basename(file, '.rb') + @base = base ofile = "#{file}inc" # bs = { func_name => argc } @@ -331,6 +356,14 @@ def mk_builtin_header file f.puts } + if SUBLIBS[base] + f.puts "// sub libraries" + SUBLIBS[base].each do |sub| + f.puts %[#include #{(sub+".rbinc").dump}] + end + f.puts + end + f.puts "void Init_builtin_#{base}(void)" f.puts "{" @@ -354,6 +387,14 @@ def mk_builtin_header file } f.puts "COMPILER_WARNING_POP" + if SUBLIBS[base] + f.puts + f.puts " // sub libraries" + SUBLIBS[base].each do |sub| + f.puts " Init_builtin_#{sub}();" + end + end + f.puts f.puts " // load" f.puts " rb_load_with_builtin_functions(#{base.dump}, #{table});" diff --git a/tool/rbinstall.rb b/tool/rbinstall.rb index e3085c8065bc9e..684b505b9d5a8c 100755 --- a/tool/rbinstall.rb +++ b/tool/rbinstall.rb @@ -693,7 +693,13 @@ class << (w = []) RbConfig.expand(File.read(src), conf) } end - install File.join(srcdir, "misc/lldb_cruby.py"), File.join(rubylibdir, "lldb_cruby.py") + Dir.glob(File.join(srcdir, "misc/lldb_*")) do |src| + if File.directory?(src) + install_recursive src, File.join(rubylibdir, File.basename(src)) + else + install src, rubylibdir + end + end install File.join(srcdir, ".gdbinit"), File.join(rubylibdir, "gdbinit") if $debug_symbols { diff --git a/tool/ruby_vm/controllers/application_controller.rb b/tool/ruby_vm/controllers/application_controller.rb index 25c10947ed5cd4..e03e54e3971a5c 100644 --- a/tool/ruby_vm/controllers/application_controller.rb +++ b/tool/ruby_vm/controllers/application_controller.rb @@ -16,10 +16,11 @@ require_relative '../loaders/vm_opts_h' class ApplicationController - def generate i, destdir + def generate i, destdir, basedir path = Pathname.new i dst = destdir ? Pathname.new(destdir).join(i) : Pathname.new(i) - dumper = RubyVM::Dumper.new dst + base = basedir ? Pathname.new(basedir) : Pathname.pwd + dumper = RubyVM::Dumper.new dst, base.expand_path return [path, dumper] end end diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb index 7aec9c76312e9a..c083dffa7abf53 100644 --- a/tool/ruby_vm/helpers/dumper.rb +++ b/tool/ruby_vm/helpers/dumper.rb @@ -28,8 +28,8 @@ def new_erb spec path = Pathname.new(__FILE__) path = (path.relative_path_from(Pathname.pwd) rescue path).dirname path += '../views' - path += File.basename(spec) - src = path.read mode: 'rt:utf-8:utf-8' + path += Pathname.pwd.join(spec).expand_path.to_s.sub("#{@base}/", '') + src = path.expand_path.read mode: 'rt:utf-8:utf-8' rescue Errno::ENOENT raise "don't know how to generate #{path}" else @@ -85,10 +85,11 @@ def replace_pragma str . join end - def initialize dst + def initialize dst, base @erb = {} @empty = new_binding @file = cstr dst.to_path + @base = base end def render partial, opts = { :locals => {} } diff --git a/tool/ruby_vm/scripts/insns2vm.rb b/tool/ruby_vm/scripts/insns2vm.rb index 8325dd364f8fa5..47d8da5513434f 100644 --- a/tool/ruby_vm/scripts/insns2vm.rb +++ b/tool/ruby_vm/scripts/insns2vm.rb @@ -15,10 +15,10 @@ module RubyVM::Insns2VM def self.router argv - options = { destdir: nil } + options = { destdir: nil, basedir: nil } targets = generate_parser(options).parse argv return targets.map do |i| - next ApplicationController.new.generate i, options[:destdir] + next ApplicationController.new.generate i, options[:destdir], options[:basedir] end end @@ -84,6 +84,14 @@ def self.generate_parser(options) options[:destdir] = dir end + this.on "--basedir=DIR", <<-'begin' do |dir| + Change the base directory from the current working directory + to the given path. Used for searching the source template. + begin + raise "directory was not found in '#{dir}'" unless Dir.exist?(dir) + options[:basedir] = dir + end + this.on "-V", "--[no-]verbose", <<-'end' Please let us ignore this and be modest. end diff --git a/tool/ruby_vm/views/mjit_instruction.rb.erb b/tool/ruby_vm/views/lib/mjit/instruction.rb.erb similarity index 97% rename from tool/ruby_vm/views/mjit_instruction.rb.erb rename to tool/ruby_vm/views/lib/mjit/instruction.rb.erb index 1c462de53a437c..dac53668a030a9 100644 --- a/tool/ruby_vm/views/mjit_instruction.rb.erb +++ b/tool/ruby_vm/views/lib/mjit/instruction.rb.erb @@ -37,4 +37,4 @@ module RubyVM::MJIT } private_constant(*constants) -end if RubyVM::MJIT.enabled? +end diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 5415e0f2b4346d..1e8c086dcf1409 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -397,7 +397,7 @@ def message_filter(repo, sha) log = STDIN.read log.delete!("\r") url = "https://github.com/#{repo}" - print "[#{repo}] ", log.gsub(/\b(?i:fix) +\K#(?=\d+\b)|\(\K#(?=\d+\))|\bGH-(?=\d+\b)/) { + print "[#{repo}] ", log.gsub(/\b(?:(?i:fix(?:e[sd])?) +|GH-)\K#(?=\d+\b)|\(\K#(?=\d+\))/) { "#{url}/pull/" }.gsub(%r{(?local_table_size); return 0; } - if (lineno) *lineno = FIX2INT(ISEQ_BODY(iseq)->location.first_lineno); + if (lineno) *lineno = ISEQ_BODY(iseq)->location.first_lineno; #ifdef USE_ISEQ_NODE_ID if (node_id) *node_id = -1; #endif @@ -105,7 +105,7 @@ rb_vm_get_sourceline(const rb_control_frame_t *cfp) return line; } else { - return FIX2INT(rb_iseq_first_lineno(iseq)); + return ISEQ_BODY(iseq)->location.first_lineno; } } else { diff --git a/vm_core.h b/vm_core.h index cf7a5ce0d01bc3..b923f5a49707fc 100644 --- a/vm_core.h +++ b/vm_core.h @@ -314,7 +314,7 @@ typedef struct rb_iseq_location_struct { VALUE pathobj; /* String (path) or Array [path, realpath]. Frozen. */ VALUE base_label; /* String */ VALUE label; /* String */ - VALUE first_lineno; /* TODO: may be unsigned short */ + int first_lineno; int node_id; rb_code_location_t code_location; } rb_iseq_location_t; @@ -1136,8 +1136,8 @@ RUBY_SYMBOL_EXPORT_BEGIN rb_iseq_t *rb_iseq_new (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type); rb_iseq_t *rb_iseq_new_top (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent); rb_iseq_t *rb_iseq_new_main (const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt); -rb_iseq_t *rb_iseq_new_eval (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth); -rb_iseq_t *rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth, +rb_iseq_t *rb_iseq_new_eval (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth); +rb_iseq_t *rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t*); struct iseq_link_anchor; @@ -1155,7 +1155,7 @@ rb_iseq_new_with_callback_new_callback( return (struct rb_iseq_new_with_callback_callback_func *)memo; } rb_iseq_t *rb_iseq_new_with_callback(const struct rb_iseq_new_with_callback_callback_func * ifunc, - VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, + VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, enum rb_iseq_type, const rb_compile_option_t*); VALUE rb_iseq_disasm(const rb_iseq_t *iseq); @@ -1201,7 +1201,7 @@ extern const rb_data_type_t ruby_binding_data_type; typedef struct { const struct rb_block block; const VALUE pathobj; - unsigned short first_lineno; + int first_lineno; } rb_binding_t; /* used by compile time and send insn */ diff --git a/vm_eval.c b/vm_eval.c index db8ca455d94ec6..c0558fce2bcb0a 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -408,7 +408,7 @@ cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme) } if (cc == NULL) { - const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, false); // TODO: proper ci + const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, NULL); // TODO: proper ci cc = vm_cc_new(klass, cme, vm_call_general); METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme); vm_ccs_push(klass, ccs, ci, cc); @@ -1672,6 +1672,8 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, rb_iseq_t *iseq = NULL; rb_ast_t *ast; int isolated_depth = 0; + int coverage_enabled = Qtrue; + { int depth = 1; const VALUE *ep = vm_block_ep(base_block); @@ -1703,14 +1705,20 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, rb_gc_register_mark_object(eval_default_path); } fname = eval_default_path; + coverage_enabled = Qfalse; } rb_parser_set_context(parser, parent, FALSE); ast = rb_parser_compile_string_path(parser, fname, src, line); if (ast->body.root) { + if (ast->body.compile_option == Qnil) { + ast->body.compile_option = rb_obj_hide(rb_ident_hash_new()); + } + rb_hash_aset(ast->body.compile_option, rb_sym_intern_ascii_cstr("coverage_enabled"), coverage_enabled); + iseq = rb_iseq_new_eval(&ast->body, ISEQ_BODY(parent)->location.label, - fname, Qnil, INT2FIX(line), + fname, Qnil, line, parent, isolated_depth); } rb_ast_dispose(ast); diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 9ccfdff4a023a4..a662de468dda9a 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -799,7 +799,7 @@ cref_replace_with_duplicated_cref_each_frame(const VALUE *vptr, int can_be_svar, break; } } - return FALSE; + return NULL; } static rb_cref_t * @@ -1980,8 +1980,33 @@ vm_search_method(VALUE cd_owner, struct rb_call_data *cd, VALUE recv) return vm_search_method_fastpath(cd_owner, cd, klass); } +#if __has_attribute(transparent_union) +typedef union { + VALUE (*anyargs)(ANYARGS); + VALUE (*f00)(VALUE); + VALUE (*f01)(VALUE, VALUE); + VALUE (*f02)(VALUE, VALUE, VALUE); + VALUE (*f03)(VALUE, VALUE, VALUE, VALUE); + VALUE (*f04)(VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f05)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f06)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f07)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f08)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f09)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f10)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f11)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f12)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f13)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f14)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*f15)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE); + VALUE (*fm1)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE); +} __attribute__((__transparent_union__)) cfunc_type; +#else +typedef VALUE (*cfunc_type)(ANYARGS); +#endif + static inline int -check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)(ANYARGS)) +check_cfunc(const rb_callable_method_entry_t *me, cfunc_type func) { if (! me) { return false; @@ -1994,13 +2019,17 @@ check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)(ANYARGS)) return false; } else { +#if __has_attribute(transparent_union) + return me->def->body.cfunc.func == func.anyargs; +#else return me->def->body.cfunc.func == func; +#endif } } } static inline int -vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, VALUE (*func)(ANYARGS)) +vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, cfunc_type func) { VM_ASSERT(iseq != NULL); const struct rb_callcache *cc = vm_search_method((VALUE)iseq, cd, recv); @@ -2700,14 +2729,16 @@ static VALUE call_cfunc_m2(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) { ractor_unsafe_check(); - return (*func)(recv, rb_ary_new4(argc, argv)); + VALUE(*f)(VALUE, VALUE) = (VALUE(*)(VALUE, VALUE))func; + return (*f)(recv, rb_ary_new4(argc, argv)); } static VALUE call_cfunc_m1(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) { ractor_unsafe_check(); - return (*func)(argc, argv, recv); + VALUE(*f)(int, const VALUE *, VALUE) = (VALUE(*)(int, const VALUE *, VALUE))func; + return (*f)(argc, argv, recv); } static VALUE @@ -2841,13 +2872,15 @@ call_cfunc_15(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) static VALUE ractor_safe_call_cfunc_m2(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) { - return (*func)(recv, rb_ary_new4(argc, argv)); + VALUE(*f)(VALUE, VALUE) = (VALUE(*)(VALUE, VALUE))func; + return (*f)(recv, rb_ary_new4(argc, argv)); } static VALUE ractor_safe_call_cfunc_m1(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS)) { - return (*func)(argc, argv, recv); + VALUE(*f)(int, const VALUE *, VALUE) = (VALUE(*)(int, const VALUE *, VALUE))func; + return (*f)(argc, argv, recv); } static VALUE @@ -4851,11 +4884,14 @@ VALUE rb_mod_name(VALUE); static VALUE vm_objtostring(const rb_iseq_t *iseq, VALUE recv, CALL_DATA cd) { + int type = TYPE(recv); + if (type == T_STRING) { + return recv; + } + const struct rb_callcache *cc = vm_search_method((VALUE)iseq, cd, recv); - switch (TYPE(recv)) { - case T_STRING: - return recv; + switch (type) { case T_SYMBOL: if (check_cfunc(vm_cc_cme(cc), rb_sym_to_s)) { // rb_sym_to_s() allocates a mutable string, but since we are only diff --git a/vm_method.c b/vm_method.c index e88a30c5f3eec4..08e91ea0392f91 100644 --- a/vm_method.c +++ b/vm_method.c @@ -372,7 +372,7 @@ void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi) { if (argc < -2 || 15 < argc) rb_raise(rb_eArgError, "arity out of range: %d for -2..15", argc); - if (func != rb_f_notimplement) { + if (func != (VALUE(*)(ANYARGS))rb_f_notimplement) { rb_method_cfunc_t opt; opt.func = func; opt.argc = argc; @@ -561,7 +561,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de RB_OBJ_WRITE(me, &def->body.bmethod.defined_ractor, rb_ractor_self(GET_RACTOR())); return; case VM_METHOD_TYPE_NOTIMPLEMENTED: - setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), rb_f_notimplement_internal, -1); + setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), (VALUE(*)(ANYARGS))rb_f_notimplement_internal, -1); return; case VM_METHOD_TYPE_OPTIMIZED: def->body.optimized = *(rb_method_optimized_t *)opts; @@ -909,7 +909,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil } if (iseq) { rb_compile_warning(RSTRING_PTR(rb_iseq_path(iseq)), - FIX2INT(ISEQ_BODY(iseq)->location.first_lineno), + ISEQ_BODY(iseq)->location.first_lineno, "previous definition of %"PRIsVALUE" was here", rb_id2str(old_def->original_id)); } @@ -1347,7 +1347,7 @@ negative_cme(ID mid) } static const rb_callable_method_entry_t * -callable_method_entry(VALUE klass, ID mid, VALUE *defined_class_ptr) +callable_method_entry_or_negative(VALUE klass, ID mid, VALUE *defined_class_ptr) { const rb_callable_method_entry_t *cme; @@ -1376,6 +1376,20 @@ callable_method_entry(VALUE klass, ID mid, VALUE *defined_class_ptr) } RB_VM_LOCK_LEAVE(); + return cme; +} + +// This is exposed for YJIT so that we can make assumptions that methods are +// not defined. +const rb_callable_method_entry_t * +rb_callable_method_entry_or_negative(VALUE klass, ID mid) { + return callable_method_entry_or_negative(klass, mid, NULL); +} + +static const rb_callable_method_entry_t * +callable_method_entry(VALUE klass, ID mid, VALUE *defined_class_ptr) { + const rb_callable_method_entry_t *cme; + cme = callable_method_entry_or_negative(klass, mid, defined_class_ptr); return !UNDEFINED_METHOD_ENTRY_P(cme) ? cme : NULL; } diff --git a/win32/Makefile.sub b/win32/Makefile.sub index e4b9a3cac88875..e84f978bb72e4f 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -288,6 +288,9 @@ LIBS = user32.lib advapi32.lib shell32.lib ws2_32.lib !if $(MSC_VER) >= 1400 LIBS = $(LIBS) iphlpapi.lib !endif +!if defined(USE_GMP) +LIBS = $(LIBS) gmp.lib +!endif LIBS = $(LIBS) imagehlp.lib shlwapi.lib bcrypt.lib $(EXTLIBS) !endif !if !defined(MISSING) @@ -632,6 +635,10 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub #define ULL_TO_DOUBLE(n) ((double)(unsigned long)((n)>>32) * (1I64 << 32) + (unsigned long)(n)) !endif #define HAVE_OFF_T 1 +#define rb_off_t __int64 +#define SIGNEDNESS_OF_OFF_T -1 +#define OFFT2NUM(v) LL2NUM(v) +#define NUM2OFFT(v) NUM2LL(v) #define SIZEOF_INT 4 #define SIZEOF_SHORT 2 #define SIZEOF_LONG 4 @@ -900,6 +907,9 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub !if "$(EXTSTATIC)" == "static" #define EXTSTATIC 1 !endif +!if "$(USE_GMP)" != "" +#define USE_GMP 1 +!endif #define EXECUTABLE_EXTS $(EXECUTABLE_EXTS) #define RUBY_COREDLL "$(RT)" #define RUBY_PLATFORM "$(arch)" @@ -972,7 +982,7 @@ s,@target_alias@,$(ARCH)-$(PLATFORM),;t t s,@target_cpu@,$(ARCH),;t t s,@target_vendor@,pc,;t t s,@target_os@,$(PLATFORM),;t t -s,@NULLCMD@,:,;t t +s,@NULLCMD@,$(NULLCMD),;t t s,@CC@,$(CC),;t t s,@CPP@,$(CPP),;t t s,@CXX@,$$(CC),;t t @@ -1341,10 +1351,6 @@ INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \ ! endif !endif -$(srcdir)/mjit_instruction.rb: $(tooldir)/ruby_vm/views/mjit_instruction.rb.erb - $(ECHO) generating $@ - $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb $(INSNS2VMOPT) $@ - verconf.h: verconf.mk loadpath: verconf.h diff --git a/win32/configure.bat b/win32/configure.bat index a3df0bb4eb8092..7253ade28bd0ff 100755 --- a/win32/configure.bat +++ b/win32/configure.bat @@ -48,6 +48,7 @@ if "%1" == "--with-git" goto :git if "%1" == "--without-git" goto :nogit if "%1" == "--without-ext" goto :witharg if "%1" == "--without-extensions" goto :witharg +if "%1" == "--with-gmp" goto :gmp if "%opt:~0,10%" == "--without-" goto :withoutarg if "%opt:~0,7%" == "--with-" goto :witharg if "%1" == "-h" goto :help @@ -210,6 +211,12 @@ goto :loop ; echo>>confargs.tmp %1 \ shift goto :loop ; +:gmp + echo>> ~tmp~.mak "WITH_GMP=yes" \ + echo>>confargs.tmp %1=1 \ + shift + shift +goto :loop ; :witharg echo>>confargs.tmp %1=%2\ set witharg=1 diff --git a/win32/ifchange.bat b/win32/ifchange.bat index c7db628a0428c8..fb9ad27337c77b 100755 --- a/win32/ifchange.bat +++ b/win32/ifchange.bat @@ -89,6 +89,7 @@ if exist %dest% ( ) ) for %%I in (%1) do echo %%~I updated +del /f %dest% copy %src% %dest% > nul del %src% diff --git a/win32/mkexports.rb b/win32/mkexports.rb index 70b7f3f22864cc..6808a38ea00189 100755 --- a/win32/mkexports.rb +++ b/win32/mkexports.rb @@ -113,6 +113,7 @@ def each_export(objs) end case filetype when /OBJECT/, /LIBRARY/ + l.chomp! next if /^[[:xdigit:]]+ 0+ UNDEF / =~ l next unless /External/ =~ l next if /(?:_local_stdio_printf_options|v(f|sn?)printf(_s)?_l)\Z/ =~ l diff --git a/win32/setup.mak b/win32/setup.mak index 17e321b98427a1..ddad0614420858 100644 --- a/win32/setup.mak +++ b/win32/setup.mak @@ -68,7 +68,7 @@ MJIT_SUPPORT = $(MJIT_SUPPORT) !if defined(BASERUBY) @echo BASERUBY = $(BASERUBY:/=\)>> $(MAKEFILE) !else - @for %I in (ruby.exe) do @echo BASERUBY = %~s$$PATH:I>> $(MAKEFILE) + @for %I in (ruby.exe) do @echo BASERUBY = %~s$$PATH:I --disable=gems>> $(MAKEFILE) !endif @type << >> $(MAKEFILE) $(BANG)if "$$(BASERUBY)" == "" @@ -90,6 +90,10 @@ $(BANG)endif @echo HAVE_GIT = $(HAVE_GIT)>> $(MAKEFILE) !endif +!if "$(WITH_GMP)" == "yes" + @echo>>$(MAKEFILE) USE_GMP = 1 +!endif + -osname-section-: @$(APPEND) @echo # TARGET>>$(MAKEFILE) diff --git a/win32/win32.c b/win32/win32.c index edf89be4b18305..865edc8aa68f41 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -3102,7 +3102,12 @@ is_readable_console(SOCKET sock) /* call this for console only */ RUBY_CRITICAL { if (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n) && n > 0) { if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown && - ir.Event.KeyEvent.uChar.AsciiChar) { + ir.Event.KeyEvent.uChar.UnicodeChar) { + ret = 1; + } + else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown && + ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU /* ALT key */ && + ir.Event.KeyEvent.uChar.UnicodeChar) { ret = 1; } else { @@ -5899,8 +5904,8 @@ rb_w32_lstati128(const char *path, struct stati128 *st) } /* License: Ruby's */ -off_t -rb_w32_lseek(int fd, off_t ofs, int whence) +rb_off_t +rb_w32_lseek(int fd, rb_off_t ofs, int whence) { SOCKET sock = TO_SOCKET(fd); if (is_socket(sock) || is_pipe(sock)) { @@ -5941,7 +5946,7 @@ rb_w32_uaccess(const char *path, int mode) /* License: Ruby's */ static int -rb_chsize(HANDLE h, off_t size) +rb_chsize(HANDLE h, rb_off_t size) { long upos, lpos, usize, lsize; int ret = -1; @@ -5970,7 +5975,7 @@ rb_chsize(HANDLE h, off_t size) /* License: Ruby's */ static int -w32_truncate(const char *path, off_t length, UINT cp) +w32_truncate(const char *path, rb_off_t length, UINT cp) { HANDLE h; int ret; @@ -5992,21 +5997,21 @@ w32_truncate(const char *path, off_t length, UINT cp) /* License: Ruby's */ int -rb_w32_utruncate(const char *path, off_t length) +rb_w32_utruncate(const char *path, rb_off_t length) { return w32_truncate(path, length, CP_UTF8); } /* License: Ruby's */ int -rb_w32_truncate(const char *path, off_t length) +rb_w32_truncate(const char *path, rb_off_t length) { return w32_truncate(path, length, filecp()); } /* License: Ruby's */ int -rb_w32_ftruncate(int fd, off_t length) +rb_w32_ftruncate(int fd, rb_off_t length) { HANDLE h; @@ -8214,7 +8219,7 @@ VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notim #endif void * -rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset) { void *ptr; //DWORD protect = 0; diff --git a/yjit.c b/yjit.c index a8341910706e9d..d3ec27ab1e03fd 100644 --- a/yjit.c +++ b/yjit.c @@ -89,6 +89,8 @@ rb_yjit_icache_invalidate(void *start, void *end) // On Darwin it's the same as calling sys_icache_invalidate(). #ifdef __GNUC__ __builtin___clear_cache(start, end); +#elif defined(__aarch64__) +#error No instruction cache clear available with this compiler on Aarch64! #endif } @@ -486,7 +488,11 @@ rb_METHOD_ENTRY_VISI(const rb_callable_method_entry_t *me) rb_method_type_t rb_get_cme_def_type(const rb_callable_method_entry_t *cme) { - return cme->def->type; + if (UNDEFINED_METHOD_ENTRY_P(cme)) { + return VM_METHOD_TYPE_UNDEF; + } else { + return cme->def->type; + } } ID @@ -603,6 +609,12 @@ rb_get_iseq_flags_has_rest(const rb_iseq_t *iseq) return iseq->body->param.flags.has_rest; } +bool +rb_get_iseq_flags_ruby2_keywords(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.ruby2_keywords; +} + bool rb_get_iseq_flags_has_block(const rb_iseq_t *iseq) { diff --git a/yjit/bindgen/Cargo.lock b/yjit/bindgen/Cargo.lock index b6ee5ae6f8abe3..15746ee3edf217 100644 --- a/yjit/bindgen/Cargo.lock +++ b/yjit/bindgen/Cargo.lock @@ -11,15 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "atty" version = "0.2.14" @@ -31,11 +22,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bindgen" -version = "0.59.2" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" dependencies = [ "bitflags", "cexpr", @@ -88,17 +85,26 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ - "ansi_term", "atty", "bitflags", + "clap_lex", + "indexmap", "strsim", + "termcolor", "textwrap", - "unicode-width", - "vec_map", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", ] [[package]] @@ -126,6 +132,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -141,6 +153,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -200,6 +222,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -255,9 +283,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "termcolor" @@ -270,18 +298,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.11.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "unicode-xid" @@ -289,12 +308,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "which" version = "4.2.5" diff --git a/yjit/bindgen/Cargo.toml b/yjit/bindgen/Cargo.toml index 57fd874939142a..3b13b8855e89f8 100644 --- a/yjit/bindgen/Cargo.toml +++ b/yjit/bindgen/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bindgen = "0.59.2" +bindgen = "0.60.1" env_logger = "0.9.0" diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index eaf030a1ded6ab..c3d4a39a2ba656 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -225,6 +225,7 @@ fn main() { .allowlist_var(".*_REDEFINED_OP_FLAG") .allowlist_type("rb_num_t") .allowlist_function("rb_callable_method_entry") + .allowlist_function("rb_callable_method_entry_or_negative") .allowlist_function("rb_vm_frame_method_entry") .allowlist_type("IVC") // pointer to iseq_inline_iv_cache_entry .allowlist_type("IC") // pointer to iseq_inline_constant_cache @@ -342,6 +343,7 @@ fn main() { .allowlist_function("rb_get_iseq_flags_has_kwrest") .allowlist_function("rb_get_iseq_flags_has_block") .allowlist_function("rb_get_iseq_flags_has_accepts_no_kwarg") + .allowlist_function("rb_get_iseq_flags_ruby2_keywords") .allowlist_function("rb_get_iseq_body_local_table_size") .allowlist_function("rb_get_iseq_body_param_keyword") .allowlist_function("rb_get_iseq_body_param_size") @@ -366,6 +368,7 @@ fn main() { .allowlist_function("rb_vm_ci_kwarg") .allowlist_function("rb_METHOD_ENTRY_VISI") .allowlist_function("rb_RCLASS_ORIGIN") + .allowlist_function("rb_method_basic_definition_p") // We define VALUE manually, don't import it .blocklist_type("VALUE") diff --git a/yjit/not_gmake.mk b/yjit/not_gmake.mk new file mode 100644 index 00000000000000..cbc60c09f19dfe --- /dev/null +++ b/yjit/not_gmake.mk @@ -0,0 +1,14 @@ +# This file is included into the Makefile when +# we're *not* using GNU make. Stick to basic features. + +# Rebuild every time since we don't want to list Rust source +# file dependencies. +.PHONY: yjit-static-lib +$(YJIT_LIBS): yjit-static-lib + $(empty) + +yjit-static-lib: + $(ECHO) 'building Rust YJIT (release mode)' + $(Q) $(RUSTC) $(YJIT_RUSTC_ARGS) + +miniruby$(EXEEXT): $(YJIT_LIBS) diff --git a/yjit/src/asm/arm64/arg/inst_offset.rs b/yjit/src/asm/arm64/arg/inst_offset.rs new file mode 100644 index 00000000000000..f4a6bc73a06711 --- /dev/null +++ b/yjit/src/asm/arm64/arg/inst_offset.rs @@ -0,0 +1,47 @@ +/// There are a lot of instructions in the AArch64 architectrue that take an +/// offset in terms of number of instructions. Usually they are jump +/// instructions or instructions that load a value relative to the current PC. +/// +/// This struct is used to mark those locations instead of a generic operand in +/// order to give better clarity to the developer when reading the AArch64 +/// backend code. It also helps to clarify that everything is in terms of a +/// number of instructions and not a number of bytes (i.e., the offset is the +/// number of bytes divided by 4). +#[derive(Copy, Clone)] +pub struct InstructionOffset(i32); + +impl InstructionOffset { + /// Create a new instruction offset. + pub fn from_insns(insns: i32) -> Self { + InstructionOffset(insns) + } + + /// Create a new instruction offset from a number of bytes. + pub fn from_bytes(bytes: i32) -> Self { + assert_eq!(bytes % 4, 0, "Byte offset must be a multiple of 4"); + InstructionOffset(bytes / 4) + } +} + +impl From for InstructionOffset { + /// Convert an i64 into an instruction offset. + fn from(value: i32) -> Self { + InstructionOffset(value) + } +} + +impl From for i32 { + /// Convert an instruction offset into a number of instructions as an i32. + fn from(offset: InstructionOffset) -> Self { + offset.0 + } +} + +impl From for i64 { + /// Convert an instruction offset into a number of instructions as an i64. + /// This is useful for when we're checking how many bits this offset fits + /// into. + fn from(offset: InstructionOffset) -> Self { + offset.0.into() + } +} diff --git a/yjit/src/asm/arm64/arg/mod.rs b/yjit/src/asm/arm64/arg/mod.rs index 9bf4a8ea1322b3..7eb37834f9aada 100644 --- a/yjit/src/asm/arm64/arg/mod.rs +++ b/yjit/src/asm/arm64/arg/mod.rs @@ -3,6 +3,7 @@ mod bitmask_imm; mod condition; +mod inst_offset; mod sf; mod shifted_imm; mod sys_reg; @@ -10,6 +11,7 @@ mod truncate; pub use bitmask_imm::BitmaskImmediate; pub use condition::Condition; +pub use inst_offset::InstructionOffset; pub use sf::Sf; pub use shifted_imm::ShiftedImmediate; pub use sys_reg::SystemRegister; diff --git a/yjit/src/asm/arm64/inst/branch_cond.rs b/yjit/src/asm/arm64/inst/branch_cond.rs index c489bacef05ac0..4338cf0f4ffc2a 100644 --- a/yjit/src/asm/arm64/inst/branch_cond.rs +++ b/yjit/src/asm/arm64/inst/branch_cond.rs @@ -1,4 +1,4 @@ -use super::super::arg::{Condition, truncate_imm}; +use super::super::arg::{Condition, InstructionOffset, truncate_imm}; /// The struct that represents an A64 conditional branch instruction that can be /// encoded. @@ -14,14 +14,14 @@ pub struct BranchCond { cond: u8, /// The instruction offset from this instruction to branch to. - imm19: i32 + offset: InstructionOffset } impl BranchCond { /// B.cond /// https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/B-cond--Branch-conditionally- - pub fn bcond(cond: u8, imm19: i32) -> Self { - Self { cond, imm19 } + pub fn bcond(cond: u8, offset: InstructionOffset) -> Self { + Self { cond, offset } } } @@ -34,7 +34,7 @@ impl From for u32 { 0 | (1 << 30) | (FAMILY << 26) - | (truncate_imm::<_, 19>(inst.imm19) << 5) + | (truncate_imm::<_, 19>(inst.offset) << 5) | (inst.cond as u32) } } @@ -53,25 +53,25 @@ mod tests { #[test] fn test_b_eq() { - let result: u32 = BranchCond::bcond(Condition::EQ, 32).into(); + let result: u32 = BranchCond::bcond(Condition::EQ, 32.into()).into(); assert_eq!(0x54000400, result); } #[test] fn test_b_vs() { - let result: u32 = BranchCond::bcond(Condition::VS, 32).into(); + let result: u32 = BranchCond::bcond(Condition::VS, 32.into()).into(); assert_eq!(0x54000406, result); } #[test] fn test_b_eq_max() { - let result: u32 = BranchCond::bcond(Condition::EQ, (1 << 18) - 1).into(); + let result: u32 = BranchCond::bcond(Condition::EQ, ((1 << 18) - 1).into()).into(); assert_eq!(0x547fffe0, result); } #[test] fn test_b_eq_min() { - let result: u32 = BranchCond::bcond(Condition::EQ, -(1 << 18)).into(); + let result: u32 = BranchCond::bcond(Condition::EQ, (-(1 << 18)).into()).into(); assert_eq!(0x54800000, result); } } diff --git a/yjit/src/asm/arm64/inst/call.rs b/yjit/src/asm/arm64/inst/call.rs index 32d924f799186f..74debac7f75bc8 100644 --- a/yjit/src/asm/arm64/inst/call.rs +++ b/yjit/src/asm/arm64/inst/call.rs @@ -1,4 +1,4 @@ -use super::super::arg::truncate_imm; +use super::super::arg::{InstructionOffset, truncate_imm}; /// The operation to perform for this instruction. enum Op { @@ -20,8 +20,8 @@ enum Op { /// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ /// pub struct Call { - /// The PC-relative offset to jump to (which will be multiplied by 4). - imm26: i32, + /// The PC-relative offset to jump to in terms of number of instructions. + offset: InstructionOffset, /// The operation to perform for this instruction. op: Op @@ -30,14 +30,14 @@ pub struct Call { impl Call { /// B /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/B--Branch- - pub fn b(imm26: i32) -> Self { - Self { imm26, op: Op::Branch } + pub fn b(offset: InstructionOffset) -> Self { + Self { offset, op: Op::Branch } } /// BL /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/BL--Branch-with-Link-?lang=en - pub fn bl(imm26: i32) -> Self { - Self { imm26, op: Op::BranchWithLink } + pub fn bl(offset: InstructionOffset) -> Self { + Self { offset, op: Op::BranchWithLink } } } @@ -50,7 +50,7 @@ impl From for u32 { 0 | ((inst.op as u32) << 31) | (FAMILY << 26) - | truncate_imm::<_, 26>(inst.imm26) + | truncate_imm::<_, 26>(inst.offset) } } @@ -68,37 +68,37 @@ mod tests { #[test] fn test_bl() { - let result: u32 = Call::bl(0).into(); + let result: u32 = Call::bl(0.into()).into(); assert_eq!(0x94000000, result); } #[test] fn test_bl_positive() { - let result: u32 = Call::bl(256).into(); + let result: u32 = Call::bl(256.into()).into(); assert_eq!(0x94000100, result); } #[test] fn test_bl_negative() { - let result: u32 = Call::bl(-256).into(); + let result: u32 = Call::bl((-256).into()).into(); assert_eq!(0x97ffff00, result); } #[test] fn test_b() { - let result: u32 = Call::b(0).into(); + let result: u32 = Call::b(0.into()).into(); assert_eq!(0x14000000, result); } #[test] fn test_b_positive() { - let result: u32 = Call::b((1 << 25) - 1).into(); + let result: u32 = Call::b(((1 << 25) - 1).into()).into(); assert_eq!(0x15ffffff, result); } #[test] fn test_b_negative() { - let result: u32 = Call::b(-(1 << 25)).into(); + let result: u32 = Call::b((-(1 << 25)).into()).into(); assert_eq!(0x16000000, result); } } diff --git a/yjit/src/asm/arm64/inst/load_literal.rs b/yjit/src/asm/arm64/inst/load_literal.rs index c5ab09713c0ea1..3eade205c808fc 100644 --- a/yjit/src/asm/arm64/inst/load_literal.rs +++ b/yjit/src/asm/arm64/inst/load_literal.rs @@ -1,4 +1,4 @@ -use super::super::arg::truncate_imm; +use super::super::arg::{InstructionOffset, truncate_imm}; /// The size of the operands being operated on. enum Opc { @@ -32,7 +32,7 @@ pub struct LoadLiteral { rt: u8, /// The PC-relative number of instructions to load the value from. - imm19: i32, + offset: InstructionOffset, /// The size of the operands being operated on. opc: Opc @@ -41,8 +41,8 @@ pub struct LoadLiteral { impl LoadLiteral { /// LDR (load literal) /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDR--literal---Load-Register--literal--?lang=en - pub fn ldr_literal(rt: u8, imm19: i32, num_bits: u8) -> Self { - Self { rt, imm19, opc: num_bits.into() } + pub fn ldr_literal(rt: u8, offset: InstructionOffset, num_bits: u8) -> Self { + Self { rt, offset, opc: num_bits.into() } } } @@ -56,7 +56,7 @@ impl From for u32 { | ((inst.opc as u32) << 30) | (1 << 28) | (FAMILY << 25) - | (truncate_imm::<_, 19>(inst.imm19) << 5) + | (truncate_imm::<_, 19>(inst.offset) << 5) | (inst.rt as u32) } } @@ -75,14 +75,14 @@ mod tests { #[test] fn test_ldr_positive() { - let inst = LoadLiteral::ldr_literal(0, 5, 64); + let inst = LoadLiteral::ldr_literal(0, 5.into(), 64); let result: u32 = inst.into(); assert_eq!(0x580000a0, result); } #[test] fn test_ldr_negative() { - let inst = LoadLiteral::ldr_literal(0, -5, 64); + let inst = LoadLiteral::ldr_literal(0, (-5).into(), 64); let result: u32 = inst.into(); assert_eq!(0x58ffff60, result); } diff --git a/yjit/src/asm/arm64/inst/load_store.rs b/yjit/src/asm/arm64/inst/load_store.rs index ea42f2d17f2d87..e877c6de774e9e 100644 --- a/yjit/src/asm/arm64/inst/load_store.rs +++ b/yjit/src/asm/arm64/inst/load_store.rs @@ -2,6 +2,7 @@ use super::super::arg::truncate_imm; /// The size of the operands being operated on. enum Size { + Size8 = 0b00, Size32 = 0b10, Size64 = 0b11, } @@ -81,6 +82,12 @@ impl LoadStore { Self { rt, rn, idx: Index::None, imm9, opc: Opc::LDR, size: num_bits.into() } } + /// LDURB (load register, byte, unscaled) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDURB--Load-Register-Byte--unscaled--?lang=en + pub fn ldurb(rt: u8, rn: u8, imm9: i16) -> Self { + Self { rt, rn, idx: Index::None, imm9, opc: Opc::LDR, size: Size::Size8 } + } + /// LDURSW (load register, unscaled, signed) /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDURSW--Load-Register-Signed-Word--unscaled--?lang=en pub fn ldursw(rt: u8, rn: u8, imm9: i16) -> Self { @@ -157,6 +164,13 @@ mod tests { assert_eq!(0xf8400020, result); } + #[test] + fn test_ldurb() { + let inst = LoadStore::ldurb(0, 1, 0); + let result: u32 = inst.into(); + assert_eq!(0x38400020, result); + } + #[test] fn test_ldur_with_imm() { let inst = LoadStore::ldur(0, 1, 123, 64); diff --git a/yjit/src/asm/arm64/inst/load_store_exclusive.rs b/yjit/src/asm/arm64/inst/load_store_exclusive.rs new file mode 100644 index 00000000000000..8216c2200af505 --- /dev/null +++ b/yjit/src/asm/arm64/inst/load_store_exclusive.rs @@ -0,0 +1,109 @@ +/// The operation being performed for this instruction. +enum Op { + Store = 0, + Load = 1 +} + +/// The size of the registers being operated on. +enum Size { + Size32 = 0b10, + Size64 = 0b11 +} + +/// A convenience function so that we can convert the number of bits of an +/// register operand directly into a Size enum variant. +impl From for Size { + fn from(num_bits: u8) -> Self { + match num_bits { + 64 => Size::Size64, + 32 => Size::Size32, + _ => panic!("Invalid number of bits: {}", num_bits) + } + } +} + +/// The struct that represents an A64 load or store exclusive instruction that +/// can be encoded. +/// +/// LDAXR/STLXR +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 | +/// | 1 0 0 1 0 0 0 0 0 1 1 1 1 1 1 | +/// | size. op rs.............. rn.............. rt.............. | +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// +pub struct LoadStoreExclusive { + /// The number of the register to be loaded. + rt: u8, + + /// The base register with which to form the address. + rn: u8, + + /// The register to be used for the status result if it applies to this + /// operation. Otherwise it's the zero register. + rs: u8, + + /// The operation being performed for this instruction. + op: Op, + + /// The size of the registers being operated on. + size: Size +} + +impl LoadStoreExclusive { + /// LDAXR + /// https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/LDAXR--Load-Acquire-Exclusive-Register- + pub fn ldaxr(rt: u8, rn: u8, num_bits: u8) -> Self { + Self { rt, rn, rs: 31, op: Op::Load, size: num_bits.into() } + } + + /// STLXR + /// https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/STLXR--Store-Release-Exclusive-Register- + pub fn stlxr(rs: u8, rt: u8, rn: u8, num_bits: u8) -> Self { + Self { rt, rn, rs, op: Op::Store, size: num_bits.into() } + } +} + +/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Loads-and-Stores?lang=en +const FAMILY: u32 = 0b0100; + +impl From for u32 { + /// Convert an instruction into a 32-bit value. + fn from(inst: LoadStoreExclusive) -> Self { + 0 + | ((inst.size as u32) << 30) + | (FAMILY << 25) + | ((inst.op as u32) << 22) + | ((inst.rs as u32) << 16) + | (0b111111 << 10) + | ((inst.rn as u32) << 5) + | (inst.rt as u32) + } +} + +impl From for [u8; 4] { + /// Convert an instruction into a 4 byte array. + fn from(inst: LoadStoreExclusive) -> [u8; 4] { + let result: u32 = inst.into(); + result.to_le_bytes() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ldaxr() { + let inst = LoadStoreExclusive::ldaxr(16, 0, 64); + let result: u32 = inst.into(); + assert_eq!(0xc85ffc10, result); + } + + #[test] + fn test_stlxr() { + let inst = LoadStoreExclusive::stlxr(17, 16, 0, 64); + let result: u32 = inst.into(); + assert_eq!(0xc811fc10, result); + } +} diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs index b3a77e73c98eb7..9821e6a334fd7c 100644 --- a/yjit/src/asm/arm64/inst/mod.rs +++ b/yjit/src/asm/arm64/inst/mod.rs @@ -13,6 +13,7 @@ mod halfword_imm; mod load_literal; mod load_register; mod load_store; +mod load_store_exclusive; mod logical_imm; mod logical_reg; mod mov; @@ -36,6 +37,7 @@ pub use halfword_imm::HalfwordImm; pub use load_literal::LoadLiteral; pub use load_register::LoadRegister; pub use load_store::LoadStore; +pub use load_store_exclusive::LoadStoreExclusive; pub use logical_imm::LogicalImm; pub use logical_reg::LogicalReg; pub use mov::Mov; diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs index b73b3125e27646..88431ce30a9c41 100644 --- a/yjit/src/asm/arm64/mod.rs +++ b/yjit/src/asm/arm64/mod.rs @@ -190,15 +190,9 @@ pub const fn b_offset_fits_bits(offset: i64) -> bool { } /// B - branch without link (offset is number of instructions to jump) -pub fn b(cb: &mut CodeBlock, imm26: A64Opnd) { - let bytes: [u8; 4] = match imm26 { - A64Opnd::Imm(imm26) => { - assert!(b_offset_fits_bits(imm26), "The immediate operand must be 26 bits or less."); - - Call::b(imm26 as i32).into() - }, - _ => panic!("Invalid operand combination to b instruction.") - }; +pub fn b(cb: &mut CodeBlock, offset: InstructionOffset) { + assert!(b_offset_fits_bits(offset.into()), "The immediate operand must be 26 bits or less."); + let bytes: [u8; 4] = Call::b(offset).into(); cb.write_bytes(&bytes); } @@ -208,33 +202,21 @@ pub fn b(cb: &mut CodeBlock, imm26: A64Opnd) { /// value into a register first, then use the b.cond instruction to skip past a /// direct jump. pub const fn bcond_offset_fits_bits(offset: i64) -> bool { - imm_fits_bits(offset, 21) && (offset & 0b11 == 0) + imm_fits_bits(offset, 19) } /// B.cond - branch to target if condition is true -pub fn bcond(cb: &mut CodeBlock, cond: u8, byte_offset: A64Opnd) { - let bytes: [u8; 4] = match byte_offset { - A64Opnd::Imm(imm) => { - assert!(bcond_offset_fits_bits(imm), "The immediate operand must be 21 bits or less and be aligned to a 2-bit boundary."); - - BranchCond::bcond(cond, (imm / 4) as i32).into() - }, - _ => panic!("Invalid operand combination to bcond instruction."), - }; +pub fn bcond(cb: &mut CodeBlock, cond: u8, offset: InstructionOffset) { + assert!(bcond_offset_fits_bits(offset.into()), "The offset must be 19 bits or less."); + let bytes: [u8; 4] = BranchCond::bcond(cond, offset).into(); cb.write_bytes(&bytes); } /// BL - branch with link (offset is number of instructions to jump) -pub fn bl(cb: &mut CodeBlock, imm26: A64Opnd) { - let bytes: [u8; 4] = match imm26 { - A64Opnd::Imm(imm26) => { - assert!(b_offset_fits_bits(imm26), "The immediate operand must be 26 bits or less."); - - Call::bl(imm26 as i32).into() - }, - _ => panic!("Invalid operand combination to bl instruction.") - }; +pub fn bl(cb: &mut CodeBlock, offset: InstructionOffset) { + assert!(b_offset_fits_bits(offset.into()), "The offset must be 26 bits or less."); + let bytes: [u8; 4] = Call::bl(offset).into(); cb.write_bytes(&bytes); } @@ -349,6 +331,20 @@ pub fn ldaddal(cb: &mut CodeBlock, rs: A64Opnd, rt: A64Opnd, rn: A64Opnd) { cb.write_bytes(&bytes); } +/// LDAXR - atomic load with acquire semantics +pub fn ldaxr(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rt, rn) { + (A64Opnd::Reg(rt), A64Opnd::Reg(rn)) => { + assert_eq!(rn.num_bits, 64, "rn must be a 64-bit register."); + + LoadStoreExclusive::ldaxr(rt.reg_no, rn.reg_no, rt.num_bits).into() + }, + _ => panic!("Invalid operand combination to ldaxr instruction."), + }; + + cb.write_bytes(&bytes); +} + /// LDP (signed offset) - load a pair of registers from memory pub fn ldp(cb: &mut CodeBlock, rt1: A64Opnd, rt2: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt1, rt2, rn) { @@ -413,7 +409,7 @@ pub fn ldr(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd, rm: A64Opnd) { } /// LDR - load a PC-relative memory address into a register -pub fn ldr_literal(cb: &mut CodeBlock, rt: A64Opnd, rn: i32) { +pub fn ldr_literal(cb: &mut CodeBlock, rt: A64Opnd, rn: InstructionOffset) { let bytes: [u8; 4] = match rt { A64Opnd::Reg(rt) => { LoadLiteral::ldr_literal(rt.reg_no, rn, rt.num_bits).into() @@ -525,6 +521,22 @@ pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { cb.write_bytes(&bytes); } +/// LDURB - load a byte from memory, zero-extend it, and write it to a register +pub fn ldurb(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rt, rn) { + (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => { + assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size"); + assert!(rt.num_bits == 8, "Expected registers to have size 8"); + assert!(mem_disp_fits_bits(rn.disp), "Expected displacement to be 9 bits or less"); + + LoadStore::ldurb(rt.reg_no, rn.base_reg_no, rn.disp as i16).into() + }, + _ => panic!("Invalid operands for LDURB") + }; + + cb.write_bytes(&bytes); +} + /// LDURSW - load a 32-bit memory address into a register and sign-extend it pub fn ldursw(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt, rn) { @@ -709,6 +721,21 @@ pub fn orr(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { cb.write_bytes(&bytes); } +/// STLXR - store a value to memory, release exclusive access +pub fn stlxr(cb: &mut CodeBlock, rs: A64Opnd, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rs, rt, rn) { + (A64Opnd::Reg(rs), A64Opnd::Reg(rt), A64Opnd::Reg(rn)) => { + assert_eq!(rs.num_bits, 32, "rs must be a 32-bit register."); + assert_eq!(rn.num_bits, 64, "rn must be a 64-bit register."); + + LoadStoreExclusive::stlxr(rs.reg_no, rt.reg_no, rn.reg_no, rn.num_bits).into() + }, + _ => panic!("Invalid operand combination to stlxr instruction.") + }; + + cb.write_bytes(&bytes); +} + /// STP (signed offset) - store a pair of registers to memory pub fn stp(cb: &mut CodeBlock, rt1: A64Opnd, rt2: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt1, rt2, rn) { @@ -1087,45 +1114,52 @@ mod tests { #[test] fn test_bcond() { - check_bytes("01200054", |cb| bcond(cb, Condition::NE, A64Opnd::new_imm(0x400))); + let offset = InstructionOffset::from_insns(0x100); + check_bytes("01200054", |cb| bcond(cb, Condition::NE, offset)); } #[test] fn test_b() { - check_bytes("ffffff15", |cb| b(cb, A64Opnd::new_imm((1 << 25) - 1))); + let offset = InstructionOffset::from_insns((1 << 25) - 1); + check_bytes("ffffff15", |cb| b(cb, offset)); } #[test] #[should_panic] fn test_b_too_big() { // There are 26 bits available - check_bytes("", |cb| b(cb, A64Opnd::new_imm(1 << 25))); + let offset = InstructionOffset::from_insns(1 << 25); + check_bytes("", |cb| b(cb, offset)); } #[test] #[should_panic] fn test_b_too_small() { // There are 26 bits available - check_bytes("", |cb| b(cb, A64Opnd::new_imm(-(1 << 25) - 1))); + let offset = InstructionOffset::from_insns(-(1 << 25) - 1); + check_bytes("", |cb| b(cb, offset)); } #[test] fn test_bl() { - check_bytes("00000096", |cb| bl(cb, A64Opnd::new_imm(-(1 << 25)))); + let offset = InstructionOffset::from_insns(-(1 << 25)); + check_bytes("00000096", |cb| bl(cb, offset)); } #[test] #[should_panic] fn test_bl_too_big() { // There are 26 bits available - check_bytes("", |cb| bl(cb, A64Opnd::new_imm(1 << 25))); + let offset = InstructionOffset::from_insns(1 << 25); + check_bytes("", |cb| bl(cb, offset)); } #[test] #[should_panic] fn test_bl_too_small() { // There are 26 bits available - check_bytes("", |cb| bl(cb, A64Opnd::new_imm(-(1 << 25) - 1))); + let offset = InstructionOffset::from_insns(-(1 << 25) - 1); + check_bytes("", |cb| bl(cb, offset)); } #[test] @@ -1178,6 +1212,11 @@ mod tests { check_bytes("8b01eaf8", |cb| ldaddal(cb, X10, X11, X12)); } + #[test] + fn test_ldaxr() { + check_bytes("6afd5fc8", |cb| ldaxr(cb, X10, X11)); + } + #[test] fn test_ldp() { check_bytes("8a2d4da9", |cb| ldp(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); @@ -1200,7 +1239,7 @@ mod tests { #[test] fn test_ldr_literal() { - check_bytes("40010058", |cb| ldr_literal(cb, X0, 10)); + check_bytes("40010058", |cb| ldr_literal(cb, X0, 10.into())); } #[test] @@ -1328,6 +1367,11 @@ mod tests { check_bytes("80025fd6", |cb| ret(cb, X20)); } + #[test] + fn test_stlxr() { + check_bytes("8bfd0ac8", |cb| stlxr(cb, W10, X11, X12)); + } + #[test] fn test_stp() { check_bytes("8a2d0da9", |cb| stp(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); diff --git a/yjit/src/asm/arm64/opnd.rs b/yjit/src/asm/arm64/opnd.rs index c89481fb03db2f..0dc614ab4e08ce 100644 --- a/yjit/src/asm/arm64/opnd.rs +++ b/yjit/src/asm/arm64/opnd.rs @@ -12,10 +12,8 @@ pub struct A64Reg } impl A64Reg { - pub fn sub_reg(&self, num_bits: u8) -> Self { - assert!(num_bits == 32 || num_bits == 64); - assert!(num_bits <= self.num_bits); - + pub fn with_num_bits(&self, num_bits: u8) -> Self { + assert!(num_bits == 8 || num_bits == 16 || num_bits == 32 || num_bits == 64); Self { num_bits, reg_no: self.reg_no } } } @@ -86,6 +84,14 @@ impl A64Opnd { _ => false } } + + /// Unwrap a register from an operand. + pub fn unwrap_reg(&self) -> A64Reg { + match self { + A64Opnd::Reg(reg) => *reg, + _ => panic!("Expected register operand") + } + } } // argument registers @@ -104,6 +110,8 @@ pub const X12_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 12 }; pub const X13_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 13 }; pub const X14_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 14 }; pub const X15_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 15 }; +pub const X16_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 16 }; +pub const X17_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 17 }; // callee-save registers pub const X19_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 19 }; diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs index 22b46e6acadc37..f5501a4bc767a7 100644 --- a/yjit/src/asm/mod.rs +++ b/yjit/src/asm/mod.rs @@ -220,6 +220,8 @@ impl CodeBlock { /// Allocate a new label with a given name pub fn new_label(&mut self, name: String) -> usize { + assert!(!name.contains(" "), "use underscores in label names, not spaces"); + // This label doesn't have an address yet self.label_addrs.push(0); self.label_names.push(name); diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs index 3f865b82a52fe4..d310e3bf129137 100644 --- a/yjit/src/asm/x86_64/mod.rs +++ b/yjit/src/asm/x86_64/mod.rs @@ -89,16 +89,13 @@ pub enum X86Opnd } impl X86Reg { - pub fn sub_reg(&self, num_bits: u8) -> Self { + pub fn with_num_bits(&self, num_bits: u8) -> Self { assert!( num_bits == 8 || num_bits == 16 || num_bits == 32 || num_bits == 64 ); - - assert!(num_bits <= self.num_bits); - Self { num_bits, reg_type: self.reg_type, diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 3e926c93870336..79dff530d16fd2 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -70,7 +70,8 @@ impl Assembler { // A special scratch register for intermediate processing. // This register is caller-saved (so we don't have to save it before using it) - const SCRATCH0: A64Opnd = A64Opnd::Reg(X15_REG); + const SCRATCH0: A64Opnd = A64Opnd::Reg(X16_REG); + const SCRATCH1: A64Opnd = A64Opnd::Reg(X17_REG); /// Get the list of registers from which we will allocate on this platform /// These are caller-saved registers @@ -137,9 +138,17 @@ impl Assembler /// to be split in case their displacement doesn't fit into 9 bits. fn split_load_operand(asm: &mut Assembler, opnd: Opnd) -> Opnd { match opnd { + Opnd::Reg(_) | Opnd::InsnOut { .. } => opnd, Opnd::Mem(_) => { let split_opnd = split_memory_address(asm, opnd); - asm.load(split_opnd) + let out_opnd = asm.load(split_opnd); + // Many Arm insns support only 32-bit or 64-bit operands. asm.load with fewer + // bits zero-extends the value, so it's safe to recognize it as a 32-bit value. + if out_opnd.rm_num_bits() < 32 { + out_opnd.with_num_bits(32).unwrap() + } else { + out_opnd + } }, _ => asm.load(opnd) } @@ -235,7 +244,7 @@ impl Assembler // such that only the Op::Load instruction needs to handle that // case. If the values aren't heap objects then we'll treat them as // if they were just unsigned integer. - let is_load = matches!(insn, Insn::Load { .. }); + let is_load = matches!(insn, Insn::Load { .. } | Insn::LoadInto { .. }); let mut opnd_iter = insn.opnd_iter_mut(); while let Some(opnd) = opnd_iter.next() { @@ -284,9 +293,8 @@ impl Assembler Insn::CCall { opnds, target, .. } => { assert!(opnds.len() <= C_ARG_OPNDS.len()); - // For each of the operands we're going to first load them - // into a register and then move them into the correct - // argument register. + // Load each operand into the corresponding argument + // register. // Note: the iteration order is reversed to avoid corrupting x0, // which is both the return value and first argument register for (idx, opnd) in opnds.into_iter().enumerate().rev() { @@ -295,10 +303,11 @@ impl Assembler // a UImm of 0 along as the argument to the move. let value = match opnd { Opnd::UImm(0) | Opnd::Imm(0) => Opnd::UImm(0), - _ => split_load_operand(asm, opnd) + Opnd::Mem(_) => split_memory_address(asm, opnd), + _ => opnd }; - asm.mov(C_ARG_OPNDS[idx], value); + asm.load_into(C_ARG_OPNDS[idx], value); } // Now we push the CCall without any arguments so that it @@ -306,18 +315,29 @@ impl Assembler asm.ccall(target.unwrap_fun_ptr(), vec![]); }, Insn::Cmp { left, right } => { - let opnd0 = match left { - Opnd::Reg(_) | Opnd::InsnOut { .. } => left, - _ => split_load_operand(asm, left) - }; - + let opnd0 = split_load_operand(asm, left); let opnd1 = split_shifted_immediate(asm, right); asm.cmp(opnd0, opnd1); }, Insn::CRet(opnd) => { - if opnd != Opnd::Reg(C_RET_REG) { - let value = split_load_operand(asm, opnd); - asm.mov(C_RET_OPND, value); + match opnd { + // If the value is already in the return register, then + // we don't need to do anything. + Opnd::Reg(C_RET_REG) => {}, + + // If the value is a memory address, we need to first + // make sure the displacement isn't too large and then + // load it into the return register. + Opnd::Mem(_) => { + let split = split_memory_address(asm, opnd); + asm.load_into(C_RET_OPND, split); + }, + + // Otherwise we just need to load the value into the + // return register. + _ => { + asm.load_into(C_RET_OPND, opnd); + } } asm.cret(C_RET_OPND); }, @@ -354,17 +374,12 @@ impl Assembler asm.csel_ge(opnd0, opnd1); }, Insn::IncrCounter { mem, value } => { - // We'll use LDADD later which only works with registers - // ... Load pointer into register - let counter_addr = split_lea_operand(asm, mem); - - // Load immediates into a register - let addend = match value { - opnd @ Opnd::Imm(_) | opnd @ Opnd::UImm(_) => asm.load(opnd), - opnd => opnd, + let counter_addr = match mem { + Opnd::Mem(_) => split_lea_operand(asm, mem), + _ => mem }; - asm.incr_counter(counter_addr, addend); + asm.incr_counter(counter_addr, value); }, Insn::JmpOpnd(opnd) => { if let Opnd::Mem(_) = opnd { @@ -375,7 +390,20 @@ impl Assembler } }, Insn::Load { opnd, .. } => { - split_load_operand(asm, opnd); + let value = match opnd { + Opnd::Mem(_) => split_memory_address(asm, opnd), + _ => opnd + }; + + asm.load(value); + }, + Insn::LoadInto { dest, opnd } => { + let value = match opnd { + Opnd::Mem(_) => split_memory_address(asm, opnd), + _ => opnd + }; + + asm.load_into(dest, value); }, Insn::LoadSExt { opnd, .. } => { match opnd { @@ -442,28 +470,24 @@ impl Assembler // The value being stored must be in a register, so if it's // not already one we'll load it first. let opnd1 = match src { - Opnd::Reg(_) | Opnd::InsnOut { .. } => src, + // If the first operand is zero, then we can just use + // the zero register. + Opnd::UImm(0) | Opnd::Imm(0) => Opnd::Reg(XZR_REG), + // Otherwise we'll check if we need to load it first. _ => split_load_operand(asm, src) }; asm.store(opnd0, opnd1); }, Insn::Sub { left, right, .. } => { - let opnd0 = match left { - Opnd::Reg(_) | Opnd::InsnOut { .. } => left, - _ => split_load_operand(asm, left) - }; - + let opnd0 = split_load_operand(asm, left); let opnd1 = split_shifted_immediate(asm, right); asm.sub(opnd0, opnd1); }, Insn::Test { left, right } => { // The value being tested must be in a register, so if it's // not already one we'll load it first. - let opnd0 = match left { - Opnd::Reg(_) | Opnd::InsnOut { .. } => left, - _ => split_load_operand(asm, left) - }; + let opnd0 = split_load_operand(asm, left); // The second value must be either a register or an // unsigned immediate that can be encoded as a bitmask @@ -568,13 +592,15 @@ impl Assembler Target::CodePtr(dst_ptr) => { let dst_addr = dst_ptr.into_i64(); let src_addr = cb.get_write_ptr().into_i64(); - let offset = dst_addr - src_addr; - let num_insns = if bcond_offset_fits_bits(offset) { + let num_insns = if bcond_offset_fits_bits((dst_addr - src_addr) / 4) { // If the jump offset fits into the conditional jump as // an immediate value and it's properly aligned, then we - // can use the b.cond instruction directly. - bcond(cb, CONDITION, A64Opnd::new_imm(offset)); + // can use the b.cond instruction directly. We're safe + // to use as i32 here since we already checked that it + // fits. + let bytes = (dst_addr - src_addr) as i32; + bcond(cb, CONDITION, InstructionOffset::from_bytes(bytes)); // Here we're going to return 1 because we've only // written out 1 instruction. @@ -583,12 +609,12 @@ impl Assembler // Otherwise, we need to load the address into a // register and use the branch register instruction. let dst_addr = dst_ptr.into_u64(); - let load_insns: i64 = emit_load_size(dst_addr).into(); + let load_insns: i32 = emit_load_size(dst_addr).into(); // We're going to write out the inverse condition so // that if it doesn't match it will skip over the // instructions used for branching. - bcond(cb, Condition::inverse(CONDITION), A64Opnd::new_imm((load_insns + 2) * 4)); + bcond(cb, Condition::inverse(CONDITION), (load_insns + 2).into()); emit_load_value(cb, Assembler::SCRATCH0, dst_addr); br(cb, Assembler::SCRATCH0); @@ -609,7 +635,8 @@ impl Assembler // offset. We're going to assume we can fit into a single // b.cond instruction. It will panic otherwise. cb.label_ref(label_idx, 4, |cb, src_addr, dst_addr| { - bcond(cb, CONDITION, A64Opnd::new_imm(dst_addr - (src_addr - 4))); + let bytes: i32 = (dst_addr - (src_addr - 4)).try_into().unwrap(); + bcond(cb, CONDITION, InstructionOffset::from_bytes(bytes)); }); }, Target::FunPtr(_) => unreachable!() @@ -710,7 +737,8 @@ impl Assembler // our IR we have the address first and the register second. stur(cb, src.into(), dest.into()); }, - Insn::Load { opnd, out } => { + Insn::Load { opnd, out } | + Insn::LoadInto { opnd, dest: out } => { match *opnd { Opnd::Reg(_) | Opnd::InsnOut { .. } => { mov(cb, out.into(), opnd.into()); @@ -722,7 +750,11 @@ impl Assembler emit_load_value(cb, out.into(), imm as u64); }, Opnd::Mem(_) => { - ldur(cb, out.into(), opnd.into()); + match opnd.rm_num_bits() { + 64 | 32 => ldur(cb, out.into(), opnd.into()), + 8 => ldurb(cb, out.into(), opnd.into()), + num_bits => panic!("unexpected num_bits: {}", num_bits) + }; }, Opnd::Value(value) => { // We dont need to check if it's a special const @@ -734,8 +766,8 @@ impl Assembler // references to GC'd Value operands. If the value // being loaded is a heap object, we'll report that // back out to the gc_offsets list. - ldr_literal(cb, out.into(), 2); - b(cb, A64Opnd::new_imm(1 + (SIZEOF_VALUE as i64) / 4)); + ldr_literal(cb, out.into(), 2.into()); + b(cb, InstructionOffset::from_bytes(4 + (SIZEOF_VALUE as i32))); cb.write_bytes(&value.as_u64().to_le_bytes()); let ptr_offset: u32 = (cb.get_write_pos() as u32) - (SIZEOF_VALUE as u32); @@ -822,14 +854,11 @@ impl Assembler // The offset to the call target in bytes let src_addr = cb.get_write_ptr().into_i64(); let dst_addr = target.unwrap_fun_ptr() as i64; - let offset = dst_addr - src_addr; - // The offset in instruction count for BL's immediate - let offset = offset / 4; // Use BL if the offset is short enough to encode as an immediate. // Otherwise, use BLR with a register. - if b_offset_fits_bits(offset) { - bl(cb, A64Opnd::new_imm(offset)); + if b_offset_fits_bits((dst_addr - src_addr) / 4) { + bl(cb, InstructionOffset::from_bytes((dst_addr - src_addr) as i32)); } else { emit_load_value(cb, Self::SCRATCH0, dst_addr as u64); blr(cb, Self::SCRATCH0); @@ -853,19 +882,22 @@ impl Assembler let src_addr = cb.get_write_ptr().into_i64(); let dst_addr = dst_ptr.into_i64(); - // The offset between the two instructions in bytes. - // Note that when we encode this into a b - // instruction, we'll divide by 4 because it accepts - // the number of instructions to jump over. - let offset = dst_addr - src_addr; - let offset = offset / 4; - // If the offset is short enough, then we'll use the // branch instruction. Otherwise, we'll move the // destination into a register and use the branch // register instruction. - let num_insns = emit_load_value(cb, Self::SCRATCH0, dst_addr as u64); - br(cb, Self::SCRATCH0); + let num_insns = if b_offset_fits_bits((dst_addr - src_addr) / 4) { + b(cb, InstructionOffset::from_bytes((dst_addr - src_addr) as i32)); + 0 + } else { + let num_insns = emit_load_value(cb, Self::SCRATCH0, dst_addr as u64); + br(cb, Self::SCRATCH0); + num_insns + }; + + // Make sure it's always a consistent number of + // instructions in case it gets patched and has to + // use the other branch. for _ in num_insns..4 { nop(cb); } @@ -877,7 +909,8 @@ impl Assembler // to assume we can fit into a single b instruction. // It will panic otherwise. cb.label_ref(*label_idx, 4, |cb, src_addr, dst_addr| { - b(cb, A64Opnd::new_imm((dst_addr - (src_addr - 4)) / 4)); + let bytes: i32 = (dst_addr - (src_addr - 4)).try_into().unwrap(); + b(cb, InstructionOffset::from_bytes(bytes)); }); }, _ => unreachable!() @@ -899,7 +932,21 @@ impl Assembler emit_conditional_jump::<{Condition::VS}>(cb, *target); }, Insn::IncrCounter { mem, value } => { - ldaddal(cb, value.into(), value.into(), mem.into()); + let label = cb.new_label("incr_counter_loop".to_string()); + cb.write_label(label); + + ldaxr(cb, Self::SCRATCH0, mem.into()); + add(cb, Self::SCRATCH0, Self::SCRATCH0, value.into()); + + // The status register that gets used to track whether or + // not the store was successful must be 32 bytes. Since we + // store the SCRATCH registers as their 64-bit versions, we + // need to rewrap it here. + let status = A64Opnd::Reg(Self::SCRATCH1.unwrap_reg().with_num_bits(32)); + stlxr(cb, status, Self::SCRATCH0, mem.into()); + + cmp(cb, Self::SCRATCH1, A64Opnd::new_uimm(0)); + emit_conditional_jump::<{Condition::NE}>(cb, Target::Label(label)); }, Insn::Breakpoint => { brk(cb, A64Opnd::None); @@ -934,17 +981,6 @@ impl Assembler }; } - // Invalidate icache for newly written out region so we don't run - // stale code. - #[cfg(not(test))] - { - let start = cb.get_ptr(start_write_pos).raw_ptr(); - let write_ptr = cb.get_write_ptr().raw_ptr(); - let codeblock_end = cb.get_ptr(cb.get_mem_size()).raw_ptr(); - let end = std::cmp::min(write_ptr, codeblock_end); - unsafe { rb_yjit_icache_invalidate(start as _, end as _) }; - } - gc_offsets } @@ -959,12 +995,23 @@ impl Assembler assert!(label_idx == idx); } + let start_write_pos = cb.get_write_pos(); let gc_offsets = asm.arm64_emit(cb); if !cb.has_dropped_bytes() { cb.link_labels(); } + // Invalidate icache for newly written out region so we don't run stale code. + #[cfg(not(test))] + { + let start = cb.get_ptr(start_write_pos).raw_ptr(); + let write_ptr = cb.get_write_ptr().raw_ptr(); + let codeblock_end = cb.get_ptr(cb.get_mem_size()).raw_ptr(); + let end = std::cmp::min(write_ptr, codeblock_end); + unsafe { rb_yjit_icache_invalidate(start as _, end as _) }; + } + gc_offsets } } diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 0b96af7f62cbdc..609ca8eaf4d8cf 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -151,6 +151,16 @@ impl Opnd } } + pub fn with_num_bits(&self, num_bits: u8) -> Option { + assert!(num_bits == 8 || num_bits == 16 || num_bits == 32 || num_bits == 64); + match *self { + Opnd::Reg(reg) => Some(Opnd::Reg(reg.with_num_bits(num_bits))), + Opnd::Mem(Mem { base, disp, .. }) => Some(Opnd::Mem(Mem { base, disp, num_bits })), + Opnd::InsnOut { idx, .. } => Some(Opnd::InsnOut { idx, num_bits }), + _ => None, + } + } + /// Get the size in bits for register/memory operands. pub fn rm_num_bits(&self) -> u8 { self.num_bits().unwrap() @@ -401,6 +411,9 @@ pub enum Insn { // A low-level instruction that loads a value into a register. Load { opnd: Opnd, out: Opnd }, + // A low-level instruction that loads a value into a specified register. + LoadInto { dest: Opnd, opnd: Opnd }, + // A low-level instruction that loads a value into a register and // sign-extends it to a 64-bit value. LoadSExt { opnd: Opnd, out: Opnd }, @@ -502,6 +515,7 @@ impl Insn { Insn::Lea { .. } => "Lea", Insn::LiveReg { .. } => "LiveReg", Insn::Load { .. } => "Load", + Insn::LoadInto { .. } => "LoadInto", Insn::LoadSExt { .. } => "LoadSExt", Insn::LShift { .. } => "LShift", Insn::Mov { .. } => "Mov", @@ -675,6 +689,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> { Insn::CSelNZ { truthy: opnd0, falsy: opnd1, .. } | Insn::CSelZ { truthy: opnd0, falsy: opnd1, .. } | Insn::IncrCounter { mem: opnd0, value: opnd1, .. } | + Insn::LoadInto { dest: opnd0, opnd: opnd1 } | Insn::LShift { opnd: opnd0, shift: opnd1, .. } | Insn::Mov { dest: opnd0, src: opnd1 } | Insn::Or { left: opnd0, right: opnd1, .. } | @@ -771,6 +786,7 @@ impl<'a> InsnOpndMutIterator<'a> { Insn::CSelNZ { truthy: opnd0, falsy: opnd1, .. } | Insn::CSelZ { truthy: opnd0, falsy: opnd1, .. } | Insn::IncrCounter { mem: opnd0, value: opnd1, .. } | + Insn::LoadInto { dest: opnd0, opnd: opnd1 } | Insn::LShift { opnd: opnd0, shift: opnd1, .. } | Insn::Mov { dest: opnd0, src: opnd1 } | Insn::Or { left: opnd0, right: opnd1, .. } | @@ -1046,21 +1062,21 @@ impl Assembler // output operand on this instruction because the live range // extends beyond the index of the instruction. let out = insn.out_opnd_mut().unwrap(); - *out = Opnd::Reg(out_reg.unwrap().sub_reg(out_num_bits)); + *out = Opnd::Reg(out_reg.unwrap().with_num_bits(out_num_bits)); } // Replace InsnOut operands by their corresponding register let mut opnd_iter = insn.opnd_iter_mut(); while let Some(opnd) = opnd_iter.next() { match *opnd { - Opnd::InsnOut { idx, .. } => { - *opnd = *asm.insns[idx].out_opnd().unwrap(); + Opnd::InsnOut { idx, num_bits } => { + *opnd = (*asm.insns[idx].out_opnd().unwrap()).with_num_bits(num_bits).unwrap(); }, Opnd::Mem(Mem { base: MemBase::InsnOut(idx), disp, num_bits }) => { let base = MemBase::Reg(asm.insns[idx].out_opnd().unwrap().unwrap_reg().reg_no); *opnd = Opnd::Mem(Mem { base, disp, num_bits }); } - _ => {}, + _ => {}, } } @@ -1422,6 +1438,10 @@ impl Assembler { out } + pub fn load_into(&mut self, dest: Opnd, opnd: Opnd) { + self.push_insn(Insn::LoadInto { dest, opnd }); + } + #[must_use] pub fn load_sext(&mut self, opnd: Opnd) -> Opnd { let out = self.next_opnd_out(Opnd::match_num_bits(&[opnd])); diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index 4e230822f1661a..2f770c2eac7923 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -131,7 +131,7 @@ impl Assembler // VALUEs alive. This is a sort of canonicalization. let mut unmapped_opnds: Vec = vec![]; - let is_load = matches!(insn, Insn::Load { .. }); + let is_load = matches!(insn, Insn::Load { .. } | Insn::LoadInto { .. }); let mut opnd_iter = insn.opnd_iter_mut(); while let Some(opnd) = opnd_iter.next() { @@ -145,7 +145,7 @@ impl Assembler if !value.special_const_p() || imm_num_bits(value.as_i64()) > 32 { asm.load(iterator.map_opnd(*opnd)) } else { - iterator.map_opnd(*opnd) + Opnd::UImm(value.as_u64()) } } else { iterator.map_opnd(*opnd) @@ -221,18 +221,25 @@ impl Assembler Insn::CSelLE { truthy, falsy, out } | Insn::CSelG { truthy, falsy, out } | Insn::CSelGE { truthy, falsy, out } => { - match truthy { - Opnd::Reg(_) | Opnd::InsnOut { .. } => {}, - _ => { + match unmapped_opnds[0] { + // If we have an instruction output whose live range + // spans beyond this instruction, we have to load it. + Opnd::InsnOut { idx, .. } => { + if live_ranges[idx] > index { + *truthy = asm.load(*truthy); + } + }, + Opnd::UImm(_) | Opnd::Imm(_) | Opnd::Value(_) => { *truthy = asm.load(*truthy); - } + }, + _ => {} }; match falsy { - Opnd::Reg(_) | Opnd::InsnOut { .. } => {}, - _ => { + Opnd::UImm(_) | Opnd::Imm(_) => { *falsy = asm.load(*falsy); - } + }, + _ => {} }; *out = asm.next_opnd_out(Opnd::match_num_bits(&[*truthy, *falsy])); @@ -289,6 +296,19 @@ impl Assembler asm.not(opnd0); }, + Insn::CCall { opnds, target, .. } => { + assert!(opnds.len() <= C_ARG_OPNDS.len()); + + // Load each operand into the corresponding argument + // register. + for (idx, opnd) in opnds.into_iter().enumerate() { + asm.load_into(C_ARG_OPNDS[idx], *opnd); + } + + // Now we push the CCall without any arguments so that it + // just performs the call. + asm.ccall(target.unwrap_fun_ptr(), vec![]); + }, _ => { if insn.out_opnd().is_some() { let out_num_bits = Opnd::match_num_bits_iter(insn.opnd_iter()); @@ -337,6 +357,14 @@ impl Assembler } } + + fn emit_csel(cb: &mut CodeBlock, truthy: Opnd, falsy: Opnd, out: Opnd, cmov_fn: fn(&mut CodeBlock, X86Opnd, X86Opnd)) { + if out != truthy { + mov(cb, out.into(), truthy.into()); + } + cmov_fn(cb, out.into(), falsy.into()); + } + //dbg!(&self.insns); // List of GC offsets @@ -421,7 +449,8 @@ impl Assembler }, // This assumes only load instructions can contain references to GC'd Value operands - Insn::Load { opnd, out } => { + Insn::Load { opnd, out } | + Insn::LoadInto { dest: out, opnd } => { mov(cb, out.into(), opnd.into()); // If the value being loaded is a heap object @@ -490,17 +519,8 @@ impl Assembler }, // C function call - Insn::CCall { opnds, target, .. } => { - // Temporary - assert!(opnds.len() <= _C_ARG_OPNDS.len()); - - // For each operand - for (idx, opnd) in opnds.iter().enumerate() { - mov(cb, X86Opnd::Reg(_C_ARG_OPNDS[idx].unwrap_reg()), opnds[idx].into()); - } - - let ptr = target.unwrap_fun_ptr(); - call_ptr(cb, RAX, ptr); + Insn::CCall { target, .. } => { + call_ptr(cb, RAX, target.unwrap_fun_ptr()); }, Insn::CRet(opnd) => { @@ -604,36 +624,28 @@ impl Assembler Insn::Breakpoint => int3(cb), Insn::CSelZ { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovnz(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovnz); }, Insn::CSelNZ { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovz(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovz); }, Insn::CSelE { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovne(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovne); }, Insn::CSelNE { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmove(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmove); }, Insn::CSelL { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovge(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovge); }, Insn::CSelLE { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovg(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovg); }, Insn::CSelG { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovle(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovle); }, Insn::CSelGE { truthy, falsy, out } => { - mov(cb, out.into(), truthy.into()); - cmovl(cb, out.into(), falsy.into()); + emit_csel(cb, *truthy, *falsy, *out, cmovl); } Insn::LiveReg { .. } => (), // just a reg alloc signal, no code Insn::PadEntryExit => { diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 58b30d5c85bd25..c246c7b48febd8 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -209,6 +209,7 @@ macro_rules! gen_counter_incr { let ptr = ptr_to_counter!($counter_name); // Load the pointer into a register + $asm.comment(&format!("increment counter {}", stringify!($counter_name))); let ptr_reg = $asm.load(Opnd::const_ptr(ptr as *const u8)); let counter_opnd = Opnd::mem(64, ptr_reg, 0); @@ -260,6 +261,7 @@ fn jit_save_pc(jit: &JITState, asm: &mut Assembler) { pc.offset(cur_insn_len) }; + asm.comment("save PC to CFP"); asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::const_ptr(ptr as *const u8)); } @@ -269,6 +271,7 @@ fn jit_save_pc(jit: &JITState, asm: &mut Assembler) { /// which could invalidate memory operands fn gen_save_sp(jit: &JITState, asm: &mut Assembler, ctx: &mut Context) { if ctx.get_sp_offset() != 0 { + asm.comment("save SP to CFP"); let stack_pointer = ctx.sp_opnd(0); let sp_addr = asm.lea(stack_pointer); asm.mov(SP, sp_addr); @@ -482,7 +485,7 @@ fn gen_outlined_exit(exit_pc: *mut VALUE, ctx: &Context, ocb: &mut OutlinedCb) - // // No guards change the logic for reconstructing interpreter state at the // moment, so there is one unique side exit for each context. Note that -// it's incorrect to jump to the side exit after any ctx stack push/pop operations +// it's incorrect to jump to the side exit after any ctx stack push operations // since they change the logic required for reconstructing interpreter state. fn get_side_exit(jit: &mut JITState, ocb: &mut OutlinedCb, ctx: &Context) -> CodePtr { match jit.side_exit_for_pc { @@ -1147,7 +1150,7 @@ fn gen_opt_plus( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands from the stack let arg1 = ctx.stack_pop(1); @@ -1184,7 +1187,8 @@ fn gen_newarray( let values_ptr = if n == 0 { Opnd::UImm(0) } else { - let offset_magnitude = SIZEOF_VALUE as u32 * n; + asm.comment("load pointer to array elts"); + let offset_magnitude = (SIZEOF_VALUE as u32) * n; let values_opnd = ctx.sp_opnd(-(offset_magnitude as isize)); asm.lea(values_opnd) }; @@ -1427,7 +1431,7 @@ fn gen_expandarray( return KeepCompiling; } - // Move the array from the stack into REG0 and check that it's an array. + // Move the array from the stack and check that it's an array. let array_reg = asm.load(array_opnd); guard_object_is_heap( asm, @@ -2316,7 +2320,13 @@ fn gen_concatstrings( KeepCompiling } -fn guard_two_fixnums(ctx: &mut Context, asm: &mut Assembler, side_exit: CodePtr) { +fn guard_two_fixnums( + jit: &mut JITState, + ctx: &mut Context, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + side_exit: CodePtr +) { // Get the stack operand types let arg1_type = ctx.get_opnd_type(StackOpnd(0)); let arg0_type = ctx.get_opnd_type(StackOpnd(1)); @@ -2348,16 +2358,34 @@ fn guard_two_fixnums(ctx: &mut Context, asm: &mut Assembler, side_exit: CodePtr) let arg1 = ctx.stack_opnd(0); let arg0 = ctx.stack_opnd(1); - // If not fixnums, fall back + // If not fixnums at run-time, fall back if arg0_type != Type::Fixnum { asm.comment("guard arg0 fixnum"); asm.test(arg0, Opnd::UImm(RUBY_FIXNUM_FLAG as u64)); - asm.jz(side_exit.into()); + + jit_chain_guard( + JCC_JZ, + jit, + &ctx, + asm, + ocb, + SEND_MAX_DEPTH, + side_exit, + ); } if arg1_type != Type::Fixnum { asm.comment("guard arg1 fixnum"); asm.test(arg1, Opnd::UImm(RUBY_FIXNUM_FLAG as u64)); - asm.jz(side_exit.into()); + + jit_chain_guard( + JCC_JZ, + jit, + &ctx, + asm, + ocb, + SEND_MAX_DEPTH, + side_exit, + ); } // Set stack types in context @@ -2394,7 +2422,7 @@ fn gen_fixnum_cmp( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands from the stack let arg1 = ctx.stack_pop(1); @@ -2471,7 +2499,7 @@ fn gen_equality_specialized( return false; } - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); asm.cmp(a_opnd, b_opnd); @@ -2483,7 +2511,8 @@ fn gen_equality_specialized( asm.mov(dst, val); true - } else if unsafe { comptime_a.class_of() == rb_cString && comptime_b.class_of() == rb_cString } + } + else if unsafe { comptime_a.class_of() == rb_cString && comptime_b.class_of() == rb_cString } { if !assume_bop_not_redefined(jit, ocb, STRING_REDEFINED_OP_FLAG, BOP_EQ) { // if overridden, emit the generic version @@ -2847,7 +2876,7 @@ fn gen_opt_and( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands and destination from the stack let arg1 = ctx.stack_pop(1); @@ -2892,7 +2921,7 @@ fn gen_opt_or( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands and destination from the stack let arg1 = ctx.stack_pop(1); @@ -2937,7 +2966,7 @@ fn gen_opt_minus( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands and destination from the stack let arg1 = ctx.stack_pop(1); @@ -3004,7 +3033,7 @@ fn gen_opt_mod( } // Check that both operands are fixnums - guard_two_fixnums(ctx, asm, side_exit); + guard_two_fixnums(jit, ctx, asm, ocb, side_exit); // Get the operands and destination from the stack let arg1 = ctx.stack_pop(1); @@ -3197,12 +3226,6 @@ fn gen_branchif( gen_check_ints(asm, side_exit); } - // Test if any bit (outside of the Qnil bit) is on - // RUBY_Qfalse /* ...0000 0000 */ - // RUBY_Qnil /* ...0000 1000 */ - let val_opnd = ctx.stack_pop(1); - asm.test(val_opnd, Opnd::Imm(!Qnil.as_i64())); - // Get the branch target instruction offsets let next_idx = jit_next_insn_idx(jit); let jump_idx = (next_idx as i32) + jump_offset; @@ -3215,18 +3238,31 @@ fn gen_branchif( idx: jump_idx as u32, }; - // Generate the branch instructions - gen_branch( - jit, - ctx, - asm, - ocb, - jump_block, - ctx, - Some(next_block), - Some(ctx), - gen_branchif_branch, - ); + // Test if any bit (outside of the Qnil bit) is on + // RUBY_Qfalse /* ...0000 0000 */ + // RUBY_Qnil /* ...0000 1000 */ + let val_type = ctx.get_opnd_type(StackOpnd(0)); + let val_opnd = ctx.stack_pop(1); + + if let Some(result) = val_type.known_truthy() { + let target = if result { jump_block } else { next_block }; + gen_direct_jump(jit, ctx, target, asm); + } else { + asm.test(val_opnd.into(), Opnd::Imm(!Qnil.as_i64())); + + // Generate the branch instructions + gen_branch( + jit, + ctx, + asm, + ocb, + jump_block, + ctx, + Some(next_block), + Some(ctx), + gen_branchif_branch, + ); + } EndBlock } @@ -3261,13 +3297,6 @@ fn gen_branchunless( gen_check_ints(asm, side_exit); } - // Test if any bit (outside of the Qnil bit) is on - // RUBY_Qfalse /* ...0000 0000 */ - // RUBY_Qnil /* ...0000 1000 */ - let val_opnd = ctx.stack_pop(1); - let not_qnil = !Qnil.as_i64(); - asm.test(val_opnd, not_qnil.into()); - // Get the branch target instruction offsets let next_idx = jit_next_insn_idx(jit) as i32; let jump_idx = next_idx + jump_offset; @@ -3280,18 +3309,32 @@ fn gen_branchunless( idx: jump_idx.try_into().unwrap(), }; - // Generate the branch instructions - gen_branch( - jit, - ctx, - asm, - ocb, - jump_block, - ctx, - Some(next_block), - Some(ctx), - gen_branchunless_branch, - ); + let val_type = ctx.get_opnd_type(StackOpnd(0)); + let val_opnd = ctx.stack_pop(1); + + if let Some(result) = val_type.known_truthy() { + let target = if result { next_block } else { jump_block }; + gen_direct_jump(jit, ctx, target, asm); + } else { + // Test if any bit (outside of the Qnil bit) is on + // RUBY_Qfalse /* ...0000 0000 */ + // RUBY_Qnil /* ...0000 1000 */ + let not_qnil = !Qnil.as_i64(); + asm.test(val_opnd.into(), not_qnil.into()); + + // Generate the branch instructions + gen_branch( + jit, + ctx, + asm, + ocb, + jump_block, + ctx, + Some(next_block), + Some(ctx), + gen_branchunless_branch, + ); + } EndBlock } @@ -3326,11 +3369,6 @@ fn gen_branchnil( gen_check_ints(asm, side_exit); } - // Test if the value is Qnil - // RUBY_Qnil /* ...0000 1000 */ - let val_opnd = ctx.stack_pop(1); - asm.cmp(val_opnd, Opnd::UImm(Qnil.into())); - // Get the branch target instruction offsets let next_idx = jit_next_insn_idx(jit) as i32; let jump_idx = next_idx + jump_offset; @@ -3343,18 +3381,29 @@ fn gen_branchnil( idx: jump_idx.try_into().unwrap(), }; - // Generate the branch instructions - gen_branch( - jit, - ctx, - asm, - ocb, - jump_block, - ctx, - Some(next_block), - Some(ctx), - gen_branchnil_branch, - ); + let val_type = ctx.get_opnd_type(StackOpnd(0)); + let val_opnd = ctx.stack_pop(1); + + if let Some(result) = val_type.known_nil() { + let target = if result { jump_block } else { next_block }; + gen_direct_jump(jit, ctx, target, asm); + } else { + // Test if the value is Qnil + // RUBY_Qnil /* ...0000 1000 */ + asm.cmp(val_opnd, Opnd::UImm(Qnil.into())); + // Generate the branch instructions + gen_branch( + jit, + ctx, + asm, + ocb, + jump_block, + ctx, + Some(next_block), + Some(ctx), + gen_branchnil_branch, + ); + } EndBlock } @@ -3457,8 +3506,7 @@ fn jit_guard_known_klass( asm.comment("guard object is static symbol"); assert!(RUBY_SPECIAL_SHIFT == 8); - let flag_bits = asm.and(obj_opnd, Opnd::UImm(0xf)); - asm.cmp(flag_bits, Opnd::UImm(RUBY_SYMBOL_FLAG as u64)); + asm.cmp(obj_opnd.with_num_bits(8).unwrap(), Opnd::UImm(RUBY_SYMBOL_FLAG as u64)); jit_chain_guard(JCC_JNE, jit, ctx, asm, ocb, max_chain_depth, side_exit); ctx.upgrade_opnd_type(insn_opnd, Type::ImmSymbol); } @@ -3822,6 +3870,102 @@ fn jit_rb_str_concat( true } +fn jit_obj_respond_to( + jit: &mut JITState, + ctx: &mut Context, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + _ci: *const rb_callinfo, + _cme: *const rb_callable_method_entry_t, + _block: Option, + argc: i32, + known_recv_class: *const VALUE, +) -> bool { + // respond_to(:sym) or respond_to(:sym, true) + if argc != 1 && argc != 2 { + return false; + } + + if known_recv_class.is_null() { + return false; + } + + let recv_class = unsafe { *known_recv_class }; + + // Get the method_id from compile time. We will later add a guard against it. + let mid_sym = jit_peek_at_stack(jit, ctx, (argc - 1) as isize); + if !mid_sym.static_sym_p() { + return false + } + let mid = unsafe { rb_sym2id(mid_sym) }; + + // Option representing the value of the "include_all" argument and whether it's known + let allow_priv = if argc == 1 { + // Default is false + Some(false) + } else { + // Get value from type information (may or may not be known) + ctx.get_opnd_type(StackOpnd(0)).known_truthy() + }; + + let mut target_cme = unsafe { rb_callable_method_entry_or_negative(recv_class, mid) }; + + // Should never be null, as in that case we will be returned a "negative CME" + assert!(!target_cme.is_null()); + + let cme_def_type = unsafe { get_cme_def_type(target_cme) }; + + if cme_def_type == VM_METHOD_TYPE_REFINED { + return false; + } + + let visibility = if cme_def_type == VM_METHOD_TYPE_UNDEF { + METHOD_VISI_UNDEF + } else { + unsafe { METHOD_ENTRY_VISI(target_cme) } + }; + + let result = match (visibility, allow_priv) { + (METHOD_VISI_UNDEF, _) => Qfalse, // No method => false + (METHOD_VISI_PUBLIC, _) => Qtrue, // Public method => true regardless of include_all + (_, Some(true)) => Qtrue, // include_all => always true + (_, _) => return false // not public and include_all not known, can't compile + }; + + if result != Qtrue { + // Only if respond_to_missing? hasn't been overridden + // In the future, we might want to jit the call to respond_to_missing? + if !assume_method_basic_definition(jit, ocb, recv_class, idRespond_to_missing.into()) { + return false; + } + } + + // Invalidate this block if method lookup changes for the method being queried. This works + // both for the case where a method does or does not exist, as for the latter we asked for a + // "negative CME" earlier. + assume_method_lookup_stable(jit, ocb, recv_class, target_cme); + + // Generate a side exit + let side_exit = get_side_exit(jit, ocb, ctx); + + if argc == 2 { + // pop include_all argument (we only use its type info) + ctx.stack_pop(1); + } + + let sym_opnd = ctx.stack_pop(1); + let recv_opnd = ctx.stack_pop(1); + + // This is necessary because we have no guarantee that sym_opnd is a constant + asm.comment("guard known mid"); + asm.cmp(sym_opnd, mid_sym.into()); + asm.jne(side_exit.into()); + + jit_putobject(jit, ctx, asm, result); + + true +} + fn jit_thread_s_current( _jit: &mut JITState, ctx: &mut Context, @@ -3879,6 +4023,135 @@ unsafe extern "C" fn build_kwhash(ci: *const rb_callinfo, sp: *const VALUE) -> V hash } +enum BlockHandler { + None, + CurrentFrame, +} + +struct ControlFrame { + recv: Opnd, + sp: Opnd, + iseq: Option, + pc: Option, + frame_type: u32, + block_handler: BlockHandler, + cme: *const rb_callable_method_entry_t, + local_size: i32 +} + +// Codegen performing a similar (but not identical) function to vm_push_frame +// +// This will generate the code to: +// * initialize locals to Qnil +// * push the environment (cme, block handler, frame type) +// * push a new CFP +// * save the new CFP to ec->cfp +// +// Notes: +// * Provided sp should point to the new frame's sp, immediately following locals and the environment +// * At entry, CFP points to the caller (not callee) frame +// * At exit, ec->cfp is updated to the pushed CFP +// * CFP and SP registers are updated only if switch_in_jit is set +// * Stack overflow is not checked (should be done by the caller) +// * Interrupts are not checked (should be done by the caller) +fn gen_push_frame( + jit: &mut JITState, + ctx: &mut Context, + asm: &mut Assembler, + set_pc_cfp: bool, // if true CFP and SP will be switched to the callee + frame: ControlFrame, +) { + assert!(frame.local_size >= 0); + + let sp = frame.sp; + + let num_locals = frame.local_size; + if num_locals > 0 { + asm.comment("initialize locals"); + + // Initialize local variables to Qnil + for i in 0..num_locals { + let offs = (SIZEOF_VALUE as i32) * (i - num_locals - 3); + asm.store(Opnd::mem(64, sp, offs), Qnil.into()); + } + } + + asm.comment("push cme, block handler, frame type"); + + // Write method entry at sp[-3] + // sp[-3] = me; + // Use compile time cme. It's assumed to be valid because we are notified when + // any cme we depend on become outdated. See yjit_method_lookup_change(). + asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -3), VALUE::from(frame.cme).into()); + + // Write block handler at sp[-2] + // sp[-2] = block_handler; + let block_handler: Opnd = match frame.block_handler { + BlockHandler::None => { + VM_BLOCK_HANDLER_NONE.into() + }, + BlockHandler::CurrentFrame => { + let cfp_self = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)); + asm.or(cfp_self, Opnd::Imm(1)) + }, + }; + asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2), block_handler); + + // Write env flags at sp[-1] + // sp[-1] = frame_type; + asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -1), frame.frame_type.into()); + + // Allocate a new CFP (ec->cfp--) + fn cfp_opnd(offset: i32) -> Opnd { + Opnd::mem(64, CFP, offset - (RUBY_SIZEOF_CONTROL_FRAME as i32)) + } + + // Setup the new frame + // *cfp = (const struct rb_control_frame_struct) { + // .pc = , + // .sp = sp, + // .iseq = , + // .self = recv, + // .ep = , + // .block_code = 0, + // .__bp__ = sp, + // }; + asm.comment("push callee control frame"); + + // For an iseq call PC may be None, in which case we will not set PC and will allow jitted code + // to set it as necessary. + let pc = if let Some(pc) = frame.pc { + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_PC), pc.into()); + }; + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_BP), sp); + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_SP), sp); + let iseq: Opnd = if let Some(iseq) = frame.iseq { + VALUE::from(iseq).into() + } else { + 0.into() + }; + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_ISEQ), iseq); + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_SELF), frame.recv); + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_BLOCK_CODE), 0.into()); + + if set_pc_cfp { + // Saving SP before calculating ep avoids a dependency on a register + // However this must be done after referencing frame.recv, which may be SP-relative + asm.mov(SP, sp); + } + let ep = asm.sub(sp, SIZEOF_VALUE.into()); + asm.mov(cfp_opnd(RUBY_OFFSET_CFP_EP), ep); + + asm.comment("switch to new CFP"); + let new_cfp = asm.lea(cfp_opnd(0)); + if set_pc_cfp { + asm.mov(CFP, new_cfp); + asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP); + } else { + asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), new_cfp); + } +} + fn gen_send_cfunc( jit: &mut JITState, ctx: &mut Context, @@ -3892,6 +4165,12 @@ fn gen_send_cfunc( ) -> CodegenStatus { let cfunc = unsafe { get_cme_def_body_cfunc(cme) }; let cfunc_argc = unsafe { get_mct_argc(cfunc) }; + let mut argc = argc; + + // Create a side-exit to fall back to the interpreter + let side_exit = get_side_exit(jit, ocb, ctx); + + let flags = unsafe { vm_ci_flag(ci) }; // If the function expects a Ruby array of arguments if cfunc_argc < 0 && cfunc_argc != -1 { @@ -3899,6 +4178,11 @@ fn gen_send_cfunc( return CantCompile; } + if flags & VM_CALL_ARGS_SPLAT != 0 { + gen_counter_incr!(asm, send_args_splat_cfunc); + return CantCompile; + } + let kw_arg = unsafe { vm_ci_kwarg(ci) }; let kw_arg_num = if kw_arg.is_null() { 0 @@ -3906,26 +4190,6 @@ fn gen_send_cfunc( unsafe { get_cikw_keyword_len(kw_arg) } }; - // Number of args which will be passed through to the callee - // This is adjusted by the kwargs being combined into a hash. - let passed_argc = if kw_arg.is_null() { - argc - } else { - argc - kw_arg_num + 1 - }; - - // If the argument count doesn't match - if cfunc_argc >= 0 && cfunc_argc != passed_argc { - gen_counter_incr!(asm, send_cfunc_argc_mismatch); - return CantCompile; - } - - // Don't JIT functions that need C stack arguments for now - if cfunc_argc >= 0 && passed_argc + 1 > (C_ARG_OPNDS.len() as i32) { - gen_counter_incr!(asm, send_cfunc_toomany_args); - return CantCompile; - } - if c_method_tracing_currently_enabled(jit) { // Don't JIT if tracing c_call or c_return gen_counter_incr!(asm, send_cfunc_tracing); @@ -3945,9 +4209,6 @@ fn gen_send_cfunc( } } - // Create a side-exit to fall back to the interpreter - let side_exit = get_side_exit(jit, ocb, ctx); - // Check for interrupts gen_check_ints(asm, side_exit); @@ -3959,6 +4220,26 @@ fn gen_send_cfunc( asm.cmp(CFP, stack_limit); asm.jbe(counted_exit!(ocb, side_exit, send_se_cf_overflow).into()); + // Number of args which will be passed through to the callee + // This is adjusted by the kwargs being combined into a hash. + let passed_argc = if kw_arg.is_null() { + argc + } else { + argc - kw_arg_num + 1 + }; + + // If the argument count doesn't match + if cfunc_argc >= 0 && cfunc_argc != passed_argc { + gen_counter_incr!(asm, send_cfunc_argc_mismatch); + return CantCompile; + } + + // Don't JIT functions that need C stack arguments for now + if cfunc_argc >= 0 && passed_argc + 1 > (C_ARG_OPNDS.len() as i32) { + gen_counter_incr!(asm, send_cfunc_toomany_args); + return CantCompile; + } + // Points to the receiver operand on the stack let recv = ctx.stack_opnd(argc); @@ -3976,70 +4257,27 @@ fn gen_send_cfunc( // sp += 3 let sp = asm.lea(ctx.sp_opnd((SIZEOF_VALUE as isize) * 3)); - // Write method entry at sp[-3] - // sp[-3] = me; - // Put compile time cme into REG1. It's assumed to be valid because we are notified when - // any cme we depend on become outdated. See yjit_method_lookup_change(). - asm.mov(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -3), Opnd::UImm(cme as u64)); - - // Write block handler at sp[-2] - // sp[-2] = block_handler; - if let Some(_block_iseq) = block { - // reg1 = VM_BH_FROM_ISEQ_BLOCK(VM_CFP_TO_CAPTURED_BLOCK(reg_cfp)); - let cfp_self = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)); - let block_handler = asm.or(cfp_self, Opnd::Imm(1)); - asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2), block_handler); + let frame_block_handler = if let Some(_block_iseq) = block { + BlockHandler::CurrentFrame } else { - let dst_opnd = Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2); - asm.store(dst_opnd, Opnd::UImm(VM_BLOCK_HANDLER_NONE.into())); - } + BlockHandler::None + }; - // Write env flags at sp[-1] - // sp[-1] = frame_type; let mut frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL; if !kw_arg.is_null() { frame_type |= VM_FRAME_FLAG_CFRAME_KW } - asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -1), Opnd::UImm(frame_type.into())); - - // Allocate a new CFP (ec->cfp--) - let ec_cfp_opnd = Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP); - let new_cfp = asm.sub(ec_cfp_opnd, Opnd::UImm(RUBY_SIZEOF_CONTROL_FRAME as u64)); - asm.mov(ec_cfp_opnd, new_cfp); - - // Setup the new frame - // *cfp = (const struct rb_control_frame_struct) { - // .pc = 0, - // .sp = sp, - // .iseq = 0, - // .self = recv, - // .ep = sp - 1, - // .block_code = 0, - // .__bp__ = sp, - // }; - - // Can we re-use ec_cfp_opnd from above? - let ec_cfp_opnd = asm.load(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_PC), Opnd::Imm(0)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_SP), sp); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_ISEQ), Opnd::Imm(0)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_BLOCK_CODE), Opnd::Imm(0)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_BP), sp); - let ep = asm.sub(sp, Opnd::UImm(SIZEOF_VALUE as u64)); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_EP), ep); - asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_SELF), recv); - /* - // Verify that we are calling the right function - if (YJIT_CHECK_MODE > 0) { // TODO: will we have a YJIT_CHECK_MODE? - // Call check_cfunc_dispatch - mov(cb, C_ARG_REGS[0], recv); - jit_mov_gc_ptr(jit, cb, C_ARG_REGS[1], (VALUE)ci); - mov(cb, C_ARG_REGS[2], const_ptr_opnd((void *)cfunc->func)); - jit_mov_gc_ptr(jit, cb, C_ARG_REGS[3], (VALUE)cme); - call_ptr(cb, REG0, (void *)&check_cfunc_dispatch); - } - */ + gen_push_frame(jit, ctx, asm, false, ControlFrame { + frame_type, + block_handler: frame_block_handler, + cme, + recv, + sp, + pc: Some(0), + iseq: None, + local_size: 0, + }); if !kw_arg.is_null() { // Build a hash from all kwargs passed @@ -4102,10 +4340,10 @@ fn gen_send_cfunc( asm.mov(stack_ret, ret); // Pop the stack frame (ec->cfp++) - // Can we reuse ec_cfp_opnd from above? + // Instead of recalculating, we can reuse the previous CFP, which is stored in a callee-saved + // register let ec_cfp_opnd = Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP); - let new_cfp = asm.add(ec_cfp_opnd, Opnd::UImm(RUBY_SIZEOF_CONTROL_FRAME as u64)); - asm.store(ec_cfp_opnd, new_cfp); + asm.store(ec_cfp_opnd, CFP); // cfunc calls may corrupt types ctx.clear_local_types(); @@ -4133,6 +4371,78 @@ fn gen_return_branch( } } + + +/// Pushes arguments from an array to the stack that are passed with a splat (i.e. *args) +/// It optimistically compiles to a static size that is the exact number of arguments +/// needed for the function. +fn push_splat_args(required_args: i32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut OutlinedCb, side_exit: CodePtr) { + + asm.comment("push_splat_args"); + + let array_opnd = ctx.stack_opnd(0); + + let array_reg = asm.load(array_opnd); + guard_object_is_heap( + asm, + array_reg, + counted_exit!(ocb, side_exit, send_splat_not_array), + ); + guard_object_is_array( + asm, + array_reg, + counted_exit!(ocb, side_exit, send_splat_not_array), + ); + + // Pull out the embed flag to check if it's an embedded array. + let flags_opnd = Opnd::mem((8 * SIZEOF_VALUE) as u8, array_reg, RUBY_OFFSET_RBASIC_FLAGS); + + // Get the length of the array + let emb_len_opnd = asm.and(flags_opnd, (RARRAY_EMBED_LEN_MASK as u64).into()); + let emb_len_opnd = asm.rshift(emb_len_opnd, (RARRAY_EMBED_LEN_SHIFT as u64).into()); + + // Conditionally move the length of the heap array + let flags_opnd = Opnd::mem((8 * SIZEOF_VALUE) as u8, array_reg, RUBY_OFFSET_RBASIC_FLAGS); + asm.test(flags_opnd, (RARRAY_EMBED_FLAG as u64).into()); + let array_len_opnd = Opnd::mem( + (8 * size_of::()) as u8, + asm.load(array_opnd), + RUBY_OFFSET_RARRAY_AS_HEAP_LEN, + ); + let array_len_opnd = asm.csel_nz(emb_len_opnd, array_len_opnd); + + // Only handle the case where the number of values in the array is equal to the number requested + asm.cmp(array_len_opnd, required_args.into()); + asm.jne(counted_exit!(ocb, side_exit, send_splatarray_length_not_equal).into()); + + let array_opnd = ctx.stack_pop(1); + + if required_args > 0 { + + // Load the address of the embedded array + // (struct RArray *)(obj)->as.ary + let array_reg = asm.load(array_opnd); + let ary_opnd = asm.lea(Opnd::mem((8 * SIZEOF_VALUE) as u8, array_reg, RUBY_OFFSET_RARRAY_AS_ARY)); + + // Conditionally load the address of the heap array + // (struct RArray *)(obj)->as.heap.ptr + let flags_opnd = Opnd::mem((8 * SIZEOF_VALUE) as u8, array_reg, RUBY_OFFSET_RBASIC_FLAGS); + asm.test(flags_opnd, Opnd::UImm(RARRAY_EMBED_FLAG as u64)); + let heap_ptr_opnd = Opnd::mem( + (8 * size_of::()) as u8, + asm.load(array_opnd), + RUBY_OFFSET_RARRAY_AS_HEAP_PTR, + ); + + let ary_opnd = asm.csel_nz(ary_opnd, heap_ptr_opnd); + + for i in (0..required_args as i32) { + let top = ctx.stack_push(Type::Unknown); + asm.mov(top, Opnd::mem(64, ary_opnd, i * (SIZEOF_VALUE as i32))); + } + } +} + fn gen_send_iseq( jit: &mut JITState, ctx: &mut Context, @@ -4146,6 +4456,11 @@ fn gen_send_iseq( let iseq = unsafe { get_def_iseq_ptr((*cme).def) }; let mut argc = argc; + let flags = unsafe { vm_ci_flag(ci) }; + + // Create a side-exit to fall back to the interpreter + let side_exit = get_side_exit(jit, ocb, ctx); + // When you have keyword arguments, there is an extra object that gets // placed on the stack the represents a bitmap of the keywords that were not // specified at the call site. We need to keep track of the fact that this @@ -4171,6 +4486,18 @@ fn gen_send_iseq( return CantCompile; } + // In order to handle backwards compatibility between ruby 3 and 2 + // ruby2_keywords was introduced. It is called only on methods + // with splat and changes they way they handle them. + // We are just going to not compile these. + // https://www.rubydoc.info/stdlib/core/Proc:ruby2_keywords + if unsafe { + get_iseq_flags_ruby2_keywords(jit.iseq) && flags & VM_CALL_ARGS_SPLAT != 0 + } { + gen_counter_incr!(asm, send_iseq_ruby2_keywords); + return CantCompile; + } + // If we have keyword arguments being passed to a callee that only takes // positionals, then we need to allocate a hash. For now we're going to // call that too complex and bail. @@ -4202,6 +4529,16 @@ fn gen_send_iseq( } } + + if flags & VM_CALL_ARGS_SPLAT != 0 && flags & VM_CALL_ZSUPER != 0 { + // zsuper methods are super calls without any arguments. + // They are also marked as splat, but don't actually have an array + // they pull arguments from, instead we need to change to call + // a different method with the current stack. + gen_counter_incr!(asm, send_iseq_zsuper); + return CantCompile; + } + let mut start_pc_offset = 0; let required_num = unsafe { get_iseq_body_param_lead_num(iseq) }; @@ -4219,7 +4556,25 @@ fn gen_send_iseq( let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) }; let opts_missing: i32 = opt_num - opts_filled; - if opts_filled < 0 || opts_filled > opt_num { + + if opt_num > 0 && flags & VM_CALL_ARGS_SPLAT != 0 { + gen_counter_incr!(asm, send_iseq_complex_callee); + return CantCompile; + } + + if doing_kw_call && flags & VM_CALL_ARGS_SPLAT != 0 { + gen_counter_incr!(asm, send_iseq_complex_callee); + return CantCompile; + } + + if opts_filled < 0 && flags & VM_CALL_ARGS_SPLAT == 0 { + // Too few arguments and no splat to make up for it + gen_counter_incr!(asm, send_iseq_arity_error); + return CantCompile; + } + + if opts_filled > opt_num { + // Too many arguments gen_counter_incr!(asm, send_iseq_arity_error); return CantCompile; } @@ -4315,9 +4670,6 @@ fn gen_send_iseq( // Number of locals that are not parameters let num_locals = unsafe { get_iseq_body_local_table_size(iseq) as i32 } - (num_params as i32); - // Create a side-exit to fall back to the interpreter - let side_exit = get_side_exit(jit, ocb, ctx); - // Check for interrupts gen_check_ints(asm, side_exit); @@ -4365,6 +4717,16 @@ fn gen_send_iseq( asm.cmp(CFP, stack_limit); asm.jbe(counted_exit!(ocb, side_exit, send_se_cf_overflow).into()); + // push_splat_args does stack manipulation so we can no longer side exit + if flags & VM_CALL_ARGS_SPLAT != 0 { + let required_args = num_params as i32 - (argc - 1); + // We are going to assume that the splat fills + // all the remaining arguments. In the generated code + // we test if this is true and if not side exit. + argc = num_params as i32; + push_splat_args(required_args, ctx, asm, ocb, side_exit) + } + if doing_kw_call { // Here we're calling a method with keyword arguments and specifying // keyword arguments at this call site. @@ -4525,62 +4887,25 @@ fn gen_send_iseq( (SIZEOF_VALUE as isize) * (3 + (num_locals as isize) + if doing_kw_call { 1 } else { 0 }); let callee_sp = asm.lea(ctx.sp_opnd(offs)); - // Initialize local variables to Qnil - for i in 0..num_locals { - let offs = (SIZEOF_VALUE as i32) * (i - num_locals - 3); - asm.store(Opnd::mem(64, callee_sp, offs), Qnil.into()); - } - - // Write the callee CME on the stack. It's assumed to be valid because we are notified when - // any cme we depend on become outdated. See yjit_method_lookup_change(). - // Write method entry at sp[-3] - // sp[-3] = me; - asm.comment("push cme, block handler, frame type"); - asm.store(Opnd::mem(64, callee_sp, SIZEOF_VALUE_I32 * -3), VALUE(cme as usize).into()); - - // Write block handler at sp[-2] - // sp[-2] = block_handler; - match block { - Some(_) => { - // reg1 = VM_BH_FROM_ISEQ_BLOCK(VM_CFP_TO_CAPTURED_BLOCK(reg_cfp)); - let block_handler = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)); - let block_handler = asm.or(block_handler, 1.into()); - asm.store(Opnd::mem(64, callee_sp, SIZEOF_VALUE_I32 * -2), block_handler); - } - None => { - asm.store(Opnd::mem(64, callee_sp, SIZEOF_VALUE_I32 * -2), VM_BLOCK_HANDLER_NONE.into()); - } - } + let frame_block_handler = if let Some(_) = block { + BlockHandler::CurrentFrame + } else { + BlockHandler::None + }; - // Write env flags at sp[-1] - // sp[-1] = frame_type; let frame_type = VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL; - asm.store(Opnd::mem(64, callee_sp, SIZEOF_VALUE_I32 * -1), frame_type.into()); - - asm.comment("push callee control frame"); - // Allocate a new CFP (ec->cfp--) - let new_cfp = asm.sub(CFP, (RUBY_SIZEOF_CONTROL_FRAME as u64).into()); - asm.mov(CFP, new_cfp); - asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP); // Setup the new frame - // *cfp = (const struct rb_control_frame_struct) { - // .pc = pc, - // .sp = sp, - // .iseq = iseq, - // .self = recv, - // .ep = sp - 1, - // .block_code = 0, - // .__bp__ = sp, - // }; - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF), recv); - asm.mov(SP, callee_sp); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP), callee_sp); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BP), callee_sp); - let callee_ep = asm.sub(callee_sp, (SIZEOF_VALUE as u64).into()); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_EP), callee_ep); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_ISEQ), VALUE(iseq as usize).into()); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE), 0.into()); + gen_push_frame(jit, ctx, asm, true, ControlFrame { + frame_type, + block_handler: frame_block_handler, + cme, + recv, + sp: callee_sp, + iseq: Some(iseq), + pc: None, // We are calling into jitted code, which will set the PC as necessary + local_size: num_locals + }); // No need to set cfp->pc since the callee sets it whenever calling into routines // that could look at it through jit_save_pc(). @@ -4768,12 +5093,6 @@ fn gen_send_general( return CantCompile; } - // Don't JIT calls that aren't simple - // Note, not using VM_CALL_ARGS_SIMPLE because sometimes we pass a block. - if flags & VM_CALL_ARGS_SPLAT != 0 { - gen_counter_incr!(asm, send_args_splat); - return CantCompile; - } if flags & VM_CALL_ARGS_BLOCKARG != 0 { gen_counter_incr!(asm, send_block_arg); return CantCompile; @@ -4846,6 +5165,12 @@ fn gen_send_general( // To handle the aliased method case (VM_METHOD_TYPE_ALIAS) loop { let def_type = unsafe { get_cme_def_type(cme) }; + + if flags & VM_CALL_ARGS_SPLAT != 0 && def_type != VM_METHOD_TYPE_ISEQ { + gen_counter_incr!(asm, send_args_splat_non_iseq); + return CantCompile; + } + match def_type { VM_METHOD_TYPE_ISEQ => { return gen_send_iseq(jit, ctx, asm, ocb, ci, cme, block, argc); @@ -5068,10 +5393,7 @@ fn gen_invokesuper( // Don't JIT calls that aren't simple // Note, not using VM_CALL_ARGS_SIMPLE because sometimes we pass a block. - if ci_flags & VM_CALL_ARGS_SPLAT != 0 { - gen_counter_incr!(asm, send_args_splat); - return CantCompile; - } + if ci_flags & VM_CALL_KWARG != 0 { gen_counter_incr!(asm, send_keywords); return CantCompile; @@ -5189,7 +5511,7 @@ fn gen_leave( asm.comment("pop stack frame"); let incr_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into()); asm.mov(CFP, incr_cfp); - asm.mov(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), incr_cfp); + asm.mov(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP); // Load the return value let retval_opnd = ctx.stack_pop(1); @@ -6145,6 +6467,8 @@ impl CodegenGlobals { self.yjit_reg_method(rb_cString, "<<", jit_rb_str_concat); self.yjit_reg_method(rb_cString, "+@", jit_rb_str_uplus); + self.yjit_reg_method(rb_mKernel, "respond_to?", jit_obj_respond_to); + // Thread.current self.yjit_reg_method( rb_singleton_class(rb_cThread), diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 687dc21013202a..102988db11c78c 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -172,7 +172,7 @@ impl Type { } } - /// Returns an Option with the exact value if it is known, otherwise None + /// Returns an Option boolean representing whether the value is truthy if known, otherwise None pub fn known_truthy(&self) -> Option { match self { Type::Nil => Some(false), @@ -183,6 +183,16 @@ impl Type { } } + /// Returns an Option boolean representing whether the value is equal to nil if known, otherwise None + pub fn known_nil(&self) -> Option { + match (self, self.known_truthy()) { + (Type::Nil, _) => Some(true), + (Type::False, _) => Some(false), // Qfalse is not nil + (_, Some(true)) => Some(false), // if truthy, can't be nil + (_, _) => None // otherwise unknown + } + } + /// Compute a difference between two value types /// Returns 0 if the two are the same /// Returns > 0 if different but compatible diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 2f823e1b615354..25149ab7304d45 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -164,6 +164,7 @@ pub use rb_get_iseq_body_stack_max as get_iseq_body_stack_max; pub use rb_get_iseq_flags_has_opt as get_iseq_flags_has_opt; pub use rb_get_iseq_flags_has_kw as get_iseq_flags_has_kw; pub use rb_get_iseq_flags_has_rest as get_iseq_flags_has_rest; +pub use rb_get_iseq_flags_ruby2_keywords as get_iseq_flags_ruby2_keywords; pub use rb_get_iseq_flags_has_post as get_iseq_flags_has_post; pub use rb_get_iseq_flags_has_kwrest as get_iseq_flags_has_kwrest; pub use rb_get_iseq_flags_has_block as get_iseq_flags_has_block; @@ -619,6 +620,7 @@ mod manual_defs { pub const VM_CALL_KWARG: u32 = 1 << VM_CALL_KWARG_bit; pub const VM_CALL_KW_SPLAT: u32 = 1 << VM_CALL_KW_SPLAT_bit; pub const VM_CALL_TAILCALL: u32 = 1 << VM_CALL_TAILCALL_bit; + pub const VM_CALL_ZSUPER : u32 = 1 << VM_CALL_ZSUPER_bit; // From internal/struct.h - in anonymous enum, so we can't easily import it pub const RSTRUCT_EMBED_LEN_MASK: usize = (RUBY_FL_USER2 | RUBY_FL_USER1) as usize; diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 98699987884bf0..f58bf1ca05879b 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1,4 +1,4 @@ -/* automatically generated by rust-bindgen 0.59.2 */ +/* automatically generated by rust-bindgen 0.60.1 */ pub const USE_RVARGC: u32 = 1; pub const INTEGER_REDEFINED_OP_FLAG: u32 = 1; @@ -26,6 +26,9 @@ pub type rb_alloc_func_t = ::std::option::Option rb_alloc_func_t; } +extern "C" { + pub fn rb_method_basic_definition_p(klass: VALUE, mid: ID) -> ::std::os::raw::c_int; +} #[repr(C)] pub struct RBasic { pub flags: VALUE, @@ -575,6 +578,12 @@ extern "C" { extern "C" { pub fn rb_callable_method_entry(klass: VALUE, id: ID) -> *const rb_callable_method_entry_t; } +extern "C" { + pub fn rb_callable_method_entry_or_negative( + klass: VALUE, + id: ID, + ) -> *const rb_callable_method_entry_t; +} pub type rb_num_t = ::std::os::raw::c_ulong; #[repr(C)] pub struct iseq_inline_constant_cache_entry { @@ -1145,6 +1154,9 @@ extern "C" { extern "C" { pub fn rb_get_iseq_flags_has_rest(iseq: *const rb_iseq_t) -> bool; } +extern "C" { + pub fn rb_get_iseq_flags_ruby2_keywords(iseq: *const rb_iseq_t) -> bool; +} extern "C" { pub fn rb_get_iseq_flags_has_block(iseq: *const rb_iseq_t) -> bool; } diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs index 4ed21118cc3b85..ee79b2938a43a4 100644 --- a/yjit/src/invariants.rs +++ b/yjit/src/invariants.rs @@ -154,6 +154,25 @@ pub fn assume_method_lookup_stable( .insert(block); } +// Checks rb_method_basic_definition_p and registers the current block for invalidation if method +// lookup changes. +// A "basic method" is one defined during VM boot, so we can use this to check assumptions based on +// default behavior. +pub fn assume_method_basic_definition( + jit: &mut JITState, + ocb: &mut OutlinedCb, + klass: VALUE, + mid: ID + ) -> bool { + if unsafe { rb_method_basic_definition_p(klass, mid) } != 0 { + let mut cme = unsafe { rb_callable_method_entry(klass, mid) }; + assume_method_lookup_stable(jit, ocb, klass, cme); + true + } else { + false + } +} + /// Tracks that a block is assuming it is operating in single-ractor mode. #[must_use] pub fn assume_single_ractor_mode(jit: &mut JITState, ocb: &mut OutlinedCb) -> bool { diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index 4843cecf92ab50..5fc83c38961bf2 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -164,7 +164,8 @@ make_counters! { send_keywords, send_kw_splat, - send_args_splat, + send_args_splat_super, + send_iseq_zsuper, send_block_arg, send_ivar_set_method, send_zsuper_method, @@ -192,6 +193,11 @@ make_counters! { send_getter_arity, send_se_cf_overflow, send_se_protected_check_failed, + send_splatarray_length_not_equal, + send_splat_not_array, + send_args_splat_non_iseq, + send_args_splat_cfunc, + send_iseq_ruby2_keywords, traced_cfunc_return, diff --git a/yjit/yjit.mk b/yjit/yjit.mk index 9e3155deb33195..fc0061eb297a91 100644 --- a/yjit/yjit.mk +++ b/yjit/yjit.mk @@ -5,48 +5,57 @@ CARGO_VERBOSE_0 = -q CARGO_VERBOSE_1 = CARGO_VERBOSE = $(CARGO_VERBOSE_$(V)) -# Select between different build profiles with macro substitution -.PHONY: yjit-static-lib -yjit-static-lib: yjit-static-lib-$(YJIT_SUPPORT) +YJIT_SRC_FILES = $(wildcard \ + $(top_srcdir)/yjit/Cargo.* \ + $(top_srcdir)/yjit/src/*.rs \ + $(top_srcdir)/yjit/src/*/*.rs \ + $(top_srcdir)/yjit/src/*/*/*.rs \ + $(top_srcdir)/yjit/src/*/*/*/*.rs \ + ) + +# Because of Cargo cache, if the actual binary is not changed from the +# previous build, the mtime is preserved as the cached file. +# This means the target is not updated actually, and it will need to +# rebuild at the next build. +YJIT_LIB_TOUCH = touch $@ # YJIT_SUPPORT=yes when `configure` gets `--enable-yjit` -yjit-static-lib-yes: +ifeq ($(YJIT_SUPPORT),yes) +$(YJIT_LIBS): $(YJIT_SRC_FILES) $(ECHO) 'building Rust YJIT (release mode)' - $(Q) $(RUSTC) \ - --crate-name=yjit \ - --crate-type=staticlib \ - --edition=2021 \ - -C opt-level=3 \ - -C overflow-checks=on \ - '--out-dir=$(CARGO_TARGET_DIR)/release/' \ - $(top_srcdir)/yjit/src/lib.rs - -yjit-static-lib-no: + $(Q) $(RUSTC) $(YJIT_RUSTC_ARGS) + $(YJIT_LIB_TOUCH) +else ifeq ($(YJIT_SUPPORT),no) +$(YJIT_LIBS): $(ECHO) 'Error: Tried to build YJIT without configuring it first. Check `make showconfig`?' @false - -yjit-static-lib-cargo: +else ifeq ($(YJIT_SUPPORT),$(filter dev dev_nodebug stats,$(YJIT_SUPPORT))) +$(YJIT_LIBS): $(YJIT_SRC_FILES) $(ECHO) 'building Rust YJIT ($(YJIT_SUPPORT) mode)' $(Q)$(CHDIR) $(top_srcdir)/yjit && \ CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \ CARGO_TERM_PROGRESS_WHEN='never' \ $(CARGO) $(CARGO_VERBOSE) build $(CARGO_BUILD_ARGS) - -yjit-static-lib-dev: yjit-static-lib-cargo -yjit-static-lib-dev_nodebug: yjit-static-lib-cargo -yjit-static-lib-stats: yjit-static-lib-cargo - -# This PHONY prerequisite makes it so that we always run cargo. When there are -# no Rust changes on rebuild, Cargo does not touch the mtime of the static -# library and GNU make avoids relinking. $(empty) seems to be important to -# trigger rebuild each time in release mode. -$(YJIT_LIBS): yjit-static-lib - $(empty) + $(YJIT_LIB_TOUCH) +else +endif # Put this here instead of in common.mk to avoid breaking nmake builds -# TODO: might need to move for BSD Make support miniruby$(EXEEXT): $(YJIT_LIBS) +# By using YJIT_BENCH_OPTS instead of RUN_OPTS, you can skip passing the options to `make install` +YJIT_BENCH_OPTS = $(RUN_OPTS) --enable-gems +YJIT_BENCH = benchmarks/railsbench/benchmark.rb + +# Run yjit-bench's ./run_once.sh for CI +yjit-bench: install update-yjit-bench PHONY + $(Q) cd $(srcdir)/yjit-bench && PATH=$(prefix)/bin:$$PATH \ + ./run_once.sh $(YJIT_BENCH_OPTS) $(YJIT_BENCH) + +update-yjit-bench: + $(Q) $(tooldir)/git-refresh -C $(srcdir) --branch main \ + https://github.com/Shopify/yjit-bench yjit-bench $(GIT_OPTS) + # Generate Rust bindings. See source for details. # Needs `./configure --enable-yjit=dev` and Clang. ifneq ($(strip $(CARGO)),) # if configure found Cargo