diff --git a/.appveyor.yml b/.appveyor.yml index 6803edff9b..05ff204541 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -26,8 +26,9 @@ environment: # is limited, and compatibility issues that happen only in newer versions are rare. # You may test some other stuff on GitHub Actions instead. - build: vs - vs: 120 + vs: 120 # Visual Studio 2013 ssl: OpenSSL-v111 + # The worker image name. This is NOT the Visual Studio version we're using here. APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 GEMS_FOR_TEST: "" RELINE_TEST_ENCODING: "UTF-8" @@ -47,6 +48,7 @@ for: - cd C:\Tools\vcpkg - git pull -q - .\bootstrap-vcpkg.bat + - ps: Start-FileDownload 'https://github.com/microsoft/vcpkg-tool/releases/download/2023-08-09/vcpkg.exe' -FileName 'C:\Tools\vcpkg\vcpkg.exe' - cd %APPVEYOR_BUILD_FOLDER% - vcpkg --triplet %Platform%-windows install --x-use-aria2 libffi libyaml readline zlib - CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat @@ -75,6 +77,7 @@ for: - attrib +r /s /d - mkdir %Platform%-mswin_%vs% build_script: + - set HAVE_GIT=no - cd %APPVEYOR_BUILD_FOLDER% - cd %Platform%-mswin_%vs% - >- @@ -91,7 +94,7 @@ for: - nmake -l "TESTOPTS=-v -q" btest - nmake -l "TESTOPTS=-v -q" test-basic - >- - nmake -l "TESTOPTS=-v --timeout-scale=3.0 + nmake -l "TESTOPTS=--timeout-scale=3.0 --excludes=../test/excludes/_appveyor -j%JOBS% --exclude win32ole --exclude test_bignum @@ -102,7 +105,7 @@ for: # separately execute tests without -j which may crash worker with -j. - >- nmake -l - "TESTOPTS=-v --timeout-scale=3.0 --excludes=../test/excludes/_appveyor" + "TESTOPTS=--timeout-scale=3.0 --excludes=../test/excludes/_appveyor" TESTS=" ../test/win32ole ../test/ruby/test_bignum.rb diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 6c47159921..0000000000 --- a/.cirrus.yml +++ /dev/null @@ -1,133 +0,0 @@ -# This CI is used to test Arm cases. We can set the maximum 16 tasks. -# The entire testing design is inspired from .github/workflows/compilers.yml. - -# By default, Cirrus mounts an empty volume to `/tmp` -# which triggers all sorts of warnings like "system temporary path is world-writable: /tmp". -# Lets workaround it by specifying a custom volume mount point. -env: - CIRRUS_VOLUME: /cirrus-ci-volume - LANG: C.UTF-8 - -task: - name: Arm64 Graviton2 / $CC - skip: "changesIncludeOnly('doc/**', '**.{md,rdoc,ronn,[1-8]}', '.document')" - arm_container: - # We use the arm64 images at https://github.com/ruby/ruby-ci-image/pkgs/container/ruby-ci-image . - image: ghcr.io/ruby/ruby-ci-image:$CC - # Define the used cpu core in each matrix task. We can use total 16 cpu - # cores in entire matrix. [cpu] = [total cpu: 16] / [number of tasks] - cpu: 8 - # We can request maximum 4 GB per cpu. - # [memory per task] = [memory per cpu: 4 GB] * [cpu] - memory: 32G - env: - CIRRUS_CLONE_DEPTH: 50 - optflags: '-O1' - debugflags: '-ggdb3' - RUBY_PREFIX: /tmp/ruby-prefix - RUBY_DEBUG: ci rgengc - RUBY_TESTOPTS: >- - -q - --color=always - --tty=no - matrix: - CC: clang-12 - CC: gcc-11 - id_script: id - set_env_script: - # Set `GNUMAKEFLAGS`, because the flags are GNU make specific. Note using - # the `make` environment variable used in compilers.yml causes some rubygems - # tests to fail. - # https://github.com/rubygems/rubygems/issues/4921 - - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> "$CIRRUS_ENV" - - cat "$CIRRUS_ENV" - # Arm containers are executed in AWS's EKS, and it's not yet supporting IPv6 - # See https://github.com/aws/containers-roadmap/issues/835 - disable_ipv6_script: sudo ./tool/disable_ipv6.sh - autogen_script: ./autogen.sh - configure_script: >- - ./configure -C - --enable-debug-env - --disable-install-doc - --with-ext=-test-/cxxanyargs,+ - --prefix="$RUBY_PREFIX" - make_extract-extlibs_script: make extract-extlibs - make_incs_script: make incs - make_script: make - make_leaked-globals_script: make leaked-globals - make_test_script: make test - make_install_script: make install - install_gems_for_test_script: $RUBY_PREFIX/bin/gem install --no-doc timezone tzinfo - make_test-tool_script: make test-tool - make_test-all_script: make test-all - make_test-spec_script: make test-spec - -# The following is to test YJIT on ARM64 CPUs available on Cirrus CI -yjit_task: - name: Arm64 Graviton2 / $CC YJIT - auto_cancellation: $CIRRUS_BRANCH != 'master' - skip: "changesIncludeOnly('doc/**', '**.{md,rdoc,ronn,[1-8]}', '.document')" - arm_container: - # We use the arm64 images at https://github.com/ruby/ruby-ci-image/pkgs/container/ruby-ci-image . - image: ghcr.io/ruby/ruby-ci-image:$CC - # Define the used cpu core in each matrix task. We can use total 16 cpu - # cores in entire matrix. [cpu] = [total cpu: 16] / [number of tasks] - cpu: 8 - # We can request maximum 4 GB per cpu. - # [memory per task] = [memory per cpu: 4 GB] * [cpu] - memory: 32G - env: - CIRRUS_CLONE_DEPTH: 50 - optflags: '-O1' - debugflags: '-ggdb3' - RUBY_PREFIX: /tmp/ruby-prefix - RUBY_DEBUG: ci rgengc - RUBY_TESTOPTS: >- - -q - --color=always - --tty=no - matrix: - - CC: clang-12 - configure: --enable-yjit=dev - rustup_init: --default-toolchain=1.58.0 - - CC: gcc-11 - configure: --enable-yjit - id_script: id - set_env_script: - # Set `GNUMAKEFLAGS`, because the flags are GNU make specific. Note using - # the `make` environment variable used in compilers.yml causes some rubygems - # tests to fail. - # https://github.com/rubygems/rubygems/issues/4921 - - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> "$CIRRUS_ENV" - - echo RUST_BACKTRACE=1 >> "$CIRRUS_ENV" - - cat "$CIRRUS_ENV" - # Arm containers are executed in AWS's EKS, and it's not yet supporting IPv6 - # See https://github.com/aws/containers-roadmap/issues/835 - disable_ipv6_script: sudo ./tool/disable_ipv6.sh - 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 $rustup_init" - autogen_script: ./autogen.sh - configure_script: >- - source $HOME/.cargo/env && ./configure -C - --enable-debug-env - --disable-install-doc - --with-ext=-test-/cxxanyargs,+ - --prefix="$RUBY_PREFIX" - $configure - make_miniruby_script: source $HOME/.cargo/env && make miniruby - make_bindgen_script: | - if [[ "$CC" = "clang-12" ]]; then - source $HOME/.cargo/env && make yjit-bindgen - else - echo "only running bindgen on clang image" - fi - boot_miniruby_script: ./miniruby --yjit-call-threshold=1 -e0 - test_dump_insns_script: ./miniruby --yjit-call-threshold=1 --yjit-dump-insns -e0 - output_stats_script: ./miniruby --yjit-call-threshold=1 --yjit-stats -e0 - full_build_script: source $HOME/.cargo/env && make - cargo_test_script: source $HOME/.cargo/env && cd yjit && cargo test - make_test_script: source $HOME/.cargo/env && make test RUN_OPTS="--yjit-call-threshold=1 --yjit-verify-ctx" - make_test_all_script: source $HOME/.cargo/env && make test-all RUN_OPTS="--yjit-call-threshold=1 --yjit-verify-ctx" TESTOPTS="$RUBY_TESTOPTS" - make_test_spec_script: source $HOME/.cargo/env && make test-spec RUN_OPTS="--yjit-call-threshold=1 --yjit-verify-ctx" @@ -18,6 +18,7 @@ gc.rb io.rb kernel.rb marshal.rb +mjit.rb numeric.rb nilclass.rb pack.rb @@ -28,6 +29,7 @@ timev.rb thread_sync.rb trace_point.rb warning.rb +yjit.rb # the lib/ directory (which has its own .document file) lib diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml deleted file mode 100644 index d058b6ca00..0000000000 --- a/.github/auto_request_review.yml +++ /dev/null @@ -1,9 +0,0 @@ -files: - 'yjit*': [team:yjit] - 'yjit/**/*': [team:yjit] - 'doc/yjit/*': [team:yjit] - 'bootstraptest/test_yjit*': [team:yjit] - 'test/ruby/test_yjit*': [team:yjit] - '.github/workflows/yjit*': [team:yjit] -options: - ignore_draft: true diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml deleted file mode 100644 index f837905fe6..0000000000 --- a/.github/workflows/auto_request_review.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Auto Request Review -on: - pull_request_target: - types: [opened, ready_for_review, reopened] - -permissions: - contents: read - -jobs: - auto-request-review: - name: Auto Request Review - runs-on: ubuntu-latest - if: ${{ github.repository == 'ruby/ruby' }} - steps: - - name: Request review based on files changes and/or groups the author belongs to - uses: necojackarc/auto-request-review@b5e81876454003a4ccb9b89cb205c67d77d7035b # v0.8.0 - with: - # scope: public_repo - token: ${{ secrets.MATZBOT_GITHUB_TOKEN }} diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml index 620847597d..ebaafe3bf0 100644 --- a/.github/workflows/baseruby.yml +++ b/.github/workflows/baseruby.yml @@ -4,19 +4,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -28,7 +33,7 @@ permissions: jobs: baseruby: name: BASERUBY - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} strategy: matrix: @@ -43,12 +48,12 @@ jobs: - ruby-3.1 steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: .downloaded-cache key: downloaded-cache - - uses: ruby/setup-ruby@c7079efafd956afb5d823e8999c2506e1053aefa # v1.126.0 + - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0 with: ruby-version: ${{ matrix.ruby }} bundler: none @@ -60,7 +65,7 @@ jobs: - run: make incs - run: make all - run: make test - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml index 942988c7d8..070c0fa1dd 100644 --- a/.github/workflows/bundled_gems.yml +++ b/.github/workflows/bundled_gems.yml @@ -2,10 +2,17 @@ name: bundled_gems on: push: + branches: [ "master" ] paths: - '.github/workflows/bundled_gems.yml' - 'gems/bundled_gems' pull_request: + branches: [ "master" ] + paths: + - '.github/workflows/bundled_gems.yml' + - 'gems/bundled_gems' + merge_group: + branches: [ "master" ] paths: - '.github/workflows/bundled_gems.yml' - 'gems/bundled_gems' @@ -34,9 +41,9 @@ jobs: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV echo "TODAY=$(date +%F)" >> $GITHUB_ENV - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: .downloaded-cache key: downloaded-cache-${{ github.sha }} @@ -81,7 +88,7 @@ jobs: news.sub!(/^\*( +)The following default gems are now bundled gems\.\n+\K(?: \1\*( +).*\n)*/) do mark = "#{$1} *#{$2}" added.map {|g, v|"#{mark}#{g} #{v}\n"}.join("") - end or next if added + end or next unless added.empty? File.write("NEWS.md", news) end shell: ruby {0} @@ -143,3 +150,17 @@ jobs: GIT_AUTHOR_NAME: git GIT_COMMITTER_NAME: git if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.show.outcome == 'failure' }} + + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 + with: + payload: | + { + "ci": "GitHub Actions", + "env": "${{ github.workflow }} / update", + "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", + "commit": "${{ github.sha }}", + "branch": "${{ github.ref_name }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot + if: ${{ failure() && github.event_name == 'push' }} diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index cf9b5e8b60..79b2916feb 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -3,19 +3,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -28,7 +33,7 @@ jobs: update-deps: strategy: matrix: - os: [ubuntu-20.04] + os: [ubuntu-22.04] fail-fast: true runs-on: ${{ matrix.os }} if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} @@ -41,15 +46,14 @@ jobs: if: ${{ contains(matrix.os, 'ubuntu') }} - name: Install libraries run: | - brew upgrade brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline if: ${{ contains(matrix.os, 'macos') }} - name: git config run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: .downloaded-cache key: downloaded-cache @@ -59,7 +63,7 @@ jobs: - run: make all golf - run: ruby tool/update-deps --fix - run: git diff --no-ext-diff --ignore-submodules --exit-code - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml deleted file mode 100644 index 2a8cfabefd..0000000000 --- a/.github/workflows/check_misc.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: Miscellaneous checks -on: [push, pull_request] - -concurrency: - group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} - cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }} - -permissions: - contents: read - -jobs: - checks: - permissions: - contents: write # for Git to git push - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 - - name: Check if C-sources are US-ASCII - run: | - ! grep -r -n '[^ -~]' -- *.[chy] include internal win32/*.[ch] - - name: Check for trailing spaces - run: | - ! git grep -n '[ ]$' -- '*.rb' '*.[chy]' - ! git grep -n '^[ ][ ]*$' -- '*.md' - - name: Check for bash specific substitution in configure.ac - run: | - ! git grep -n '\${[A-Za-z_0-9]*/' -- configure.ac - - name: Check for header macros - run: | - ! for header in ruby/*.h; do \ - git grep -l -F -e $header -e HAVE_`echo $header | tr a-z./ A-Z__` -- . > /dev/null || echo $header - done | grep -F . - working-directory: include - - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 - with: - path: .downloaded-cache - key: downloaded-cache-${{ github.sha }} - restore-keys: | - downloaded-cache - - - name: Download previous gems list - run: | - data=default_gems.json - mkdir -p .downloaded-cache - ln -s .downloaded-cache/$data . - curl -O -R -z ./$data https://stdgems.org/$data - - - name: Make default gems list - run: | - #!ruby - require 'rubygems' - $:.unshift "lib" - rgver = File.foreach("lib/rubygems.rb") do |line| - break $1 if /^\s*VERSION\s*=\s*"([^"]+)"/ =~ line - end - gems = Dir.glob("{ext,lib}/**/*.gemspec").map do |f| - spec = Gem::Specification.load(f) - "#{spec.name} #{spec.version}" - end.sort - File.open("gems/default_gems", "w") do |f| - f.puts "RubyGems #{rgver}" - f.puts gems - end - shell: ruby --disable=gems {0} - - - name: Maintain updated gems list in NEWS - run: | - #!ruby - require 'json' - news = File.read("NEWS.md") - prev = news[/since the \*+(\d+\.\d+\.\d+)\*+/, 1] - prevs = [prev, prev.sub(/\.\d+\z/, '')] - %W[default].each do |type| - last = JSON.parse(File.read("#{type}_gems.json"))['gems'].filter_map do |g| - v = g['versions'].values_at(*prevs).compact.first - g = g['gem'] - g = 'RubyGems' if g == 'rubygems' - [g, v] if v - end.to_h - changed = File.foreach("gems/#{type}_gems").filter_map do |l| - next if l.start_with?("#") - g, v = l.split(" ", 3) - [g, v] unless last[g] == v - end - news.sub!(/^\*( +)The following #{type} gems? are updated\.\n+\K(?: \1\* .*\n)*/) do - mark = "#{$1} * " - changed.map {|g, v|"#{mark}#{g} #{v}\n"}.join("") - end or next - File.write("NEWS.md", news) - end - shell: ruby {0} - - - name: Check diffs - id: diff - run: | - git diff --color --no-ext-diff --ignore-submodules --exit-code NEWS.md - continue-on-error: true - - name: Commit - run: | - git pull --ff-only origin ${GITHUB_REF#refs/heads/} - git commit --message="Update default gems list at ${GITHUB_SHA:0:30} [ci skip]" NEWS.md - git push origin ${GITHUB_REF#refs/heads/} - env: - EMAIL: svn-admin@ruby-lang.org - GIT_AUTHOR_NAME: git - GIT_COMMITTER_NAME: git - if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.diff.outcome == 'failure' }} - - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 - with: - payload: | - { - "ci": "GitHub Actions", - "env": "${{ github.workflow }}", - "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", - "commit": "${{ github.sha }}", - "branch": "${{ github.ref_name }}" - } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot - if: ${{ failure() && github.event_name == 'push' }} diff --git a/.github/workflows/cirrus-notify.yml b/.github/workflows/cirrus-notify.yml deleted file mode 100644 index 5edba0ae8f..0000000000 --- a/.github/workflows/cirrus-notify.yml +++ /dev/null @@ -1,46 +0,0 @@ -on: - check_suite: - type: ['completed'] -name: Cirrus CI failure notification - -permissions: - contents: read - -jobs: - cirrus-notify: - name: After Cirrus CI Failure - if: >- - github.event.check_suite.app.name == 'Cirrus CI' - && github.event.check_suite.conclusion != 'success' - && github.event.check_suite.conclusion != 'cancelled' - && github.event.check_suite.conclusion != 'skipped' - && github.event.check_suite.head_branch == 'master' - runs-on: ubuntu-latest - steps: - - uses: octokit/request-action@52ce92ce3185e00e2425f043c3e9509121929aea # v2.x - id: get_failed_check_run - with: - route: GET /repos/${{ github.repository }}/check-suites/${{ github.event.check_suite.id }}/check-runs?status=completed - mediaType: '{"previews": ["antiope"]}' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Dump check_runs - env: - CHECK_RUNS: ${{ steps.get_failed_check_run.outputs.data }} - run: echo "$CHECK_RUNS" - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 - with: - payload: | - { - "ci": "Cirrus CI", - "env": "Cirrus CI", - "url": "${{ fromJson(steps.get_failed_check_run.outputs.data).check_runs[0].html_url }}", - "commit": "${{ github.event.check_suite.head_commit.id }}", - "branch": "${{ github.event.check_suite.head_branch }}" - } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 643d6ddf92..8dba76fbe2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -4,19 +4,17 @@ on: # push: # paths-ignore: # - 'doc/**' + # - '**/man' # - '**.md' # - '**.rdoc' # - '**/.document' - # - '**.[1-8]' - # - '**.ronn' # pull_request: # paths-ignore: # - 'doc/**' + # - '**/man' # - '**.md' # - '**.rdoc' # - '**/.document' - # - '**.[1-8]' - # - '**.ronn' schedule: - cron: '0 12 * * *' workflow_dispatch: @@ -51,9 +49,9 @@ jobs: sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby - name: Checkout repository - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: .downloaded-cache key: downloaded-cache @@ -62,15 +60,16 @@ jobs: run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb - name: Initialize CodeQL - uses: github/codeql-action/init@b2a92eb56d8cb930006a1c6ed86b0782dd8a4297 # v2.1.35 + uses: github/codeql-action/init@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37 with: config-file: ./.github/codeql/codeql-config.yml + trap-caching: false - name: Set ENV run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - name: Autobuild - uses: github/codeql-action/autobuild@b2a92eb56d8cb930006a1c6ed86b0782dd8a4297 # v2.1.35 + uses: github/codeql-action/autobuild@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b2a92eb56d8cb930006a1c6ed86b0782dd8a4297 # v2.1.35 + uses: github/codeql-action/analyze@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37 diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index b6db2138fd..caf12cc0f4 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -4,19 +4,22 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' - - '**.md' + - '**/man' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -77,27 +80,13 @@ jobs: - { name: gcc-9, env: { default_cc: gcc-9 } } - { name: gcc-8, env: { default_cc: gcc-8 } } - { name: gcc-7, env: { default_cc: gcc-7 } } - - { name: gcc-6, env: { default_cc: gcc-6 } } - - { name: gcc-5, env: { default_cc: gcc-5 } } - - { name: gcc-4.8, env: { default_cc: gcc-4.8 } } - - name: 'gcc-11 LTO' - container: gcc-11 + - name: 'gcc-13 LTO' + container: gcc-13 env: - default_cc: 'gcc-11 -flto=auto -ffat-lto-objects' + default_cc: 'gcc-13 -flto=auto -ffat-lto-objects -Werror=lto-type-mismatch' optflags: '-O2' shared: disable # check: true - - name: 'gcc-11 annocheck' - container: gcc-11 - env: - # Minimal flags to pass the check. - default_cc: 'gcc-11 -O2 -fcf-protection -Wa,--generate-missing-build-notes=yes' - LDFLAGS: '-Wl,-z,now' - # FIXME: Drop skipping options - # https://bugs.ruby-lang.org/issues/18061 - # 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 } } @@ -105,17 +94,15 @@ jobs: - { name: clang-12, env: { default_cc: clang-12 } } - { name: clang-11, env: { default_cc: clang-11 } } - { name: clang-10, env: { default_cc: clang-10 } } - - { name: clang-9, env: { default_cc: clang-9 } } - - { name: clang-8, env: { default_cc: clang-8 } } - - { name: clang-7, env: { default_cc: clang-7 } } - - { name: clang-6.0, env: { default_cc: clang-6.0 } } - - { name: clang-5.0, env: { default_cc: clang-5.0 } } - - { name: clang-4.0, env: { default_cc: clang-4.0 } } - - { name: clang-3.9, env: { default_cc: clang-3.9 } } - - name: 'clang-14 LTO' - container: clang-14 + # llvm-objcopy<=9 doesn't have --wildcard. It compiles, but leaves Rust symbols in libyjit.o. + - { name: clang-9, env: { default_cc: clang-9, append_configure: '--disable-yjit' } } + - { name: clang-8, env: { default_cc: clang-8, append_configure: '--disable-yjit' } } + - { name: clang-7, env: { default_cc: clang-7, append_configure: '--disable-yjit' } } + - { name: clang-6.0, env: { default_cc: clang-6.0, append_configure: '--disable-yjit' } } + - name: 'clang-16 LTO' + container: clang-16 env: - default_cc: 'clang-14 -flto=auto' + default_cc: 'clang-16 -flto=auto' optflags: '-O2' shared: disable # check: true @@ -181,7 +168,11 @@ jobs: # - { name: VM_CHECK_MODE, env: { cppflags: '-DVM_CHECK_MODE' } } - { name: USE_EMBED_CI=0, env: { cppflags: '-DUSE_EMBED_CI=0' } } - - { name: USE_FLONUM=0, env: { cppflags: '-DUSE_FLONUM=0' } } + - name: USE_FLONUM=0, + env: + cppflags: '-DUSE_FLONUM=0' + # yjit requires FLONUM for the pointer tagging scheme + append_configure: '--disable-yjit' # - { name: USE_GC_MALLOC_OBJ_INFO_DETAILS, env: { cppflags: '-DUSE_GC_MALLOC_OBJ_INFO_DETAILS' } } - { name: USE_LAZY_LOAD, env: { cppflags: '-DUSE_LAZY_LOAD' } } # - { name: USE_RINCGC=0, env: { cppflags: '-DUSE_RINCGC=0' } } @@ -234,10 +225,10 @@ jobs: - name: setenv run: | echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> $GITHUB_ENV - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src/.downloaded-cache key: downloaded-cache @@ -271,7 +262,7 @@ jobs: - run: make test-annocheck if: ${{ matrix.entry.check && endsWith(matrix.entry.name, 'annocheck') }} - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 92ea46388e..d8dc58b119 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -3,19 +3,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -30,8 +35,9 @@ jobs: matrix: test_task: ["check"] # "test-bundler-parallel", "test-bundled-gems" os: - - macos-11 - - macos-12 + - macos-13 + - macos-14 + - macos-15 fail-fast: false env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} @@ -44,22 +50,21 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src/.downloaded-cache key: downloaded-cache - name: Install libraries run: | - brew upgrade brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline bison working-directory: src - name: Set ENV run: | echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV - echo "PATH="/usr/local/opt/bison/bin:$PATH"" >> $GITHUB_ENV + echo "PATH="/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH"" >> $GITHUB_ENV - run: ./autogen.sh working-directory: src - name: Run configure @@ -89,7 +94,7 @@ jobs: PRECHECK_BUNDLED_GEMS: "no" if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }} continue-on-error: ${{ matrix.continue-on-skipped_tests || false }} - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index c5c24d9c17..0df917d3d8 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -3,19 +3,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -60,15 +65,15 @@ jobs: git config --global core.eol lf git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src/.downloaded-cache key: downloaded-cache - name: Set up Ruby & MSYS2 - uses: ruby/setup-ruby@c7079efafd956afb5d823e8999c2506e1053aefa # v1.126.0 + uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0 with: ruby-version: ${{ matrix.base_ruby }} - name: set env @@ -154,7 +159,7 @@ jobs: make ${{ StartsWith(matrix.test_task, 'spec/') && matrix.test_task || 'test-spec' }} if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-spec' || StartsWith(matrix.test_task, 'spec/')}} - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/mjit-bindgen.yml b/.github/workflows/mjit-bindgen.yml index f6d3ff793a..26f8a1b2aa 100644 --- a/.github/workflows/mjit-bindgen.yml +++ b/.github/workflows/mjit-bindgen.yml @@ -3,19 +3,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -31,7 +36,7 @@ jobs: include: - task: mjit-bindgen fail-fast: false - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build @@ -47,21 +52,21 @@ jobs: build-essential \ libssl-dev libyaml-dev libreadline6-dev \ zlib1g-dev libncurses5-dev libffi-dev \ - libclang1-10 \ + libclang1-14 \ bison autoconf sudo apt-get install -q -y pkg-config || : - name: Set up Ruby - uses: ruby/setup-ruby@c7079efafd956afb5d823e8999c2506e1053aefa # v1.126.0 + uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0 with: ruby-version: '3.1' - name: git config run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src/.downloaded-cache key: downloaded-cache @@ -80,7 +85,7 @@ jobs: - run: make ${{ matrix.task }} - run: git diff --exit-code working-directory: src - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml index f2fd9ad076..6f7181489a 100644 --- a/.github/workflows/mjit.yml +++ b/.github/workflows/mjit.yml @@ -3,12 +3,20 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + pull_request: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - '**.[1-8]' - '**.ronn' - pull_request: + merge_group: paths-ignore: - 'doc/**' - '**.md' @@ -49,10 +57,10 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src/.downloaded-cache key: downloaded-cache @@ -71,10 +79,9 @@ jobs: - run: make incs - run: make - run: sudo make -s install - - run: sudo apt-get install gdb # used by test / test-all failure - name: Run test run: | - ulimit -c unlimited + unset GNUMAKEFLAGS make -s test RUN_OPTS="$RUN_OPTS" timeout-minutes: 60 # - name: Run test-all @@ -84,10 +91,10 @@ jobs: # timeout-minutes: 60 - name: Run test-spec run: | - ulimit -c unlimited + unset GNUMAKEFLAGS make -s test-spec RUN_OPTS="$RUN_OPTS" timeout-minutes: 60 - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000000..5d4474d978 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,18 @@ +name: Start release workflow +on: + push: + tags: + - '*' + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Build release package + run: | + curl -L -X POST \ + -H "Authorization: Bearer ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }}" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/ruby/actions/dispatches \ + -d '{"event_type": "${{ github.ref }}"}' diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 8236a2cd1a..c12a95362d 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -32,12 +32,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # v2.0.6 + uses: ossf/scorecard-action@ea651e62978af7915d09fe2e282747c798bf2dab # v2.4.1 with: results_file: results.sarif results_format: sarif @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: SARIF file path: results.sarif @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@b2a92eb56d8cb930006a1c6ed86b0782dd8a4297 # v2.1.27 + uses: github/codeql-action/upload-sarif@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.27 with: sarif_file: results.sarif diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml index 2936e892a3..4521195a2b 100644 --- a/.github/workflows/spec_guards.yml +++ b/.github/workflows/spec_guards.yml @@ -6,9 +6,10 @@ on: - 'spec/**' - '!spec/*.md' pull_request: - paths-ignore: + paths: - 'spec/**' - '!spec/*.md' + merge_group: concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -20,28 +21,42 @@ permissions: jobs: rubyspec: name: Rubyspec - runs-on: ubuntu-20.04 - if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} + + runs-on: ubuntu-22.04 + + if: >- + ${{!(false + || contains(github.event.head_commit.message, '[DOC]') + || contains(github.event.head_commit.message, 'Document') + || contains(github.event.pull_request.title, '[DOC]') + || contains(github.event.pull_request.title, 'Document') + || contains(github.event.pull_request.labels.*.name, 'Document') + || (github.event_name == 'push' && github.actor == 'dependabot[bot]') + )}} + strategy: matrix: # Specs from ruby/spec should still run on all supported Ruby versions. # This also ensures the needed ruby_version_is guards are there, see spec/README.md. ruby: - - ruby-2.7 - ruby-3.1 + - ruby-3.2 steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 - - uses: ruby/setup-ruby@c7079efafd956afb5d823e8999c2506e1053aefa # v1.126.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0 with: ruby-version: ${{ matrix.ruby }} bundler: none + - run: gem install webrick + - run: ruby ../mspec/bin/mspec working-directory: spec/ruby env: CHECK_LEAKS: true - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { @@ -53,4 +68,4 @@ jobs: } env: SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot - if: ${{ failure() && github.event_name == 'push' }} + if: ${{ failure() }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index f5b259c84a..4fbca1170e 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -3,19 +3,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -41,7 +46,6 @@ jobs: - test_task: check configure: "--enable-shared --enable-load-relative" - 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 @@ -49,7 +53,7 @@ jobs: GITPULLOPTIONS: --no-tags origin ${{github.ref}} RUBY_DEBUG: ci SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build @@ -75,10 +79,10 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src/.downloaded-cache key: downloaded-cache @@ -123,7 +127,7 @@ jobs: TESTS: ${{ matrix.skipped_tests }} if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }} continue-on-error: ${{ matrix.continue-on-skipped_tests || false }} - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 3114078256..27920b5821 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -3,19 +3,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -45,7 +50,7 @@ jobs: WASI_SDK_VERSION_MINOR: 0 BINARYEN_VERSION: 109 WASMTIME_VERSION: v0.33.0 - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build @@ -54,7 +59,7 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - name: Install libraries @@ -86,6 +91,18 @@ jobs: echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV - run: ./autogen.sh working-directory: src + + - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0 + with: + ruby-version: '3.0' + bundler: none + + - name: Download config.guess with wasi version + run: | + rm tool/config.guess tool/config.sub + ruby tool/downloader.rb -d tool -e gnu config.guess config.sub + working-directory: src + - name: Run configure run: | ../src/configure \ @@ -110,6 +127,20 @@ jobs: ruby ./bootstraptest/runner.rb --ruby="$(which wasmtime) run $PWD/../build/ruby --mapdir /::./ -- " --verbose "--sets=$NO_THREAD_TESTS" working-directory: src + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 + with: + payload: | + { + "ci": "GitHub Actions", + "env": "${{ github.workflow }} / ${{ matrix.name }}", + "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", + "commit": "${{ github.sha }}", + "branch": "${{ github.ref_name }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot + if: ${{ failure() && github.event_name == 'push' }} + defaults: run: working-directory: build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 674f627ef9..c2bd4881c2 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -3,19 +3,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -29,85 +34,68 @@ jobs: strategy: matrix: include: - - vs: 2019 - vs: 2022 + vcvers: -vcvars_ver=14.2 fail-fast: false - runs-on: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }} + runs-on: windows-${{ matrix.vs }} if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} name: VisualStudio ${{ matrix.vs }} env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} PATCH: C:\msys64\usr\bin\patch.exe - OS_VER: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }} + OS_VER: windows-${{ matrix.vs }} steps: - run: md build working-directory: - - uses: msys2/setup-msys2@d40200dc2db4c351366b048a9565ad82919e1c24 # v2 + - uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2.27.0 id: setup-msys2 with: update: true - install: >- - patch - if: ${{ env.OS_VER != 'windows-2019' }} + install: bison patch - name: patch path shell: msys2 {0} run: echo PATCH=$(cygpath -wa $(command -v patch)) >> $GITHUB_ENV if: ${{ steps.setup-msys2.outcome == 'success' }} - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 - with: - path: C:\vcpkg\downloads - key: ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}- - ${{ runner.os }}-vcpkg-download- - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: C:\vcpkg\installed - key: ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}-${{ github.sha }} + key: ${{ runner.os }}-vcpkg-installed-windows-${{ matrix.vs }}-${{ github.sha }} restore-keys: | - ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}- - ${{ runner.os }}-vcpkg-installed- + ${{ runner.os }}-vcpkg-installed-windows-${{ matrix.vs }}- + ${{ runner.os }}-vcpkg-installed-windows- - name: Install libraries with vcpkg run: | + iex "& {$(irm get.scoop.sh)} -RunAsAdmin" + Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH + scoop install cmake@3.31.6 vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 - with: - path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey - key: ${{ runner.os }}-chocolatey-${{ env.OS_VER }}-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-chocolatey-${{ env.OS_VER }}- - ${{ runner.os }}-chocolatey- - - name: Install libraries with chocolatey - run: | - # Using Choco-Install for retries, but it doesn't detect failures properly - # if you pass multiple package names in a single command. - Choco-Install -PackageName winflexbison3 - shell: pwsh + shell: + pwsh - name: git config run: | git config --global core.autocrlf false git config --global core.eol lf git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src/.downloaded-cache key: downloaded-cache - name: setup env # %TEMP% is inconsistent with %TMP% and test-all expects they are consistent. # https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302 + # msys2/setup-msys2 installs MSYS2 to D:/a/_temp/msys64/usr/bin run: | - set VS=${{ matrix.vs }} - set VCVARS=${{ matrix.vcvars }} + set Path=D:/a/_temp/msys64/usr/bin;%Path% if not "%VCVARS%" == "" goto :vcset - set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat" - if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat" :vcset set | C:\msys64\usr\bin\sort > old.env - call %VCVARS% + call %VCVARS% ${{ matrix.vcvers || ''}} set TMP=%USERPROFILE%\AppData\Local\Temp set TEMP=%USERPROFILE%\AppData\Local\Temp set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul @@ -132,7 +120,7 @@ jobs: - run: nmake extract-extlibs - run: nmake env: - YACC: win_bison + YACC: bison.exe - run: nmake test timeout-minutes: 5 - run: nmake test-spec @@ -141,7 +129,7 @@ jobs: env: RUBY_TESTOPTS: -j${{env.TEST_JOBS}} --job-status=normal timeout-minutes: 60 - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 011420c157..0b7b9046e9 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -3,19 +3,24 @@ on: push: paths-ignore: - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' pull_request: paths-ignore: - 'doc/**' + - '**/man' + - '**.md' + - '**.rdoc' + - '**/.document' + merge_group: + paths-ignore: + - 'doc/**' + - '**/man' - '**.md' - '**.rdoc' - '**/.document' - - '**.[1-8]' - - '**.ronn' concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} @@ -28,9 +33,9 @@ jobs: cargo: name: Rust cargo test # GitHub Action's image seems to already contain a Rust 1.58.0. - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 # For now we can't run cargo test --offline because it complains about the # capstone dependency, even though the dependency is optional #- run: cargo test --offline @@ -47,9 +52,10 @@ jobs: fail-fast: false matrix: include: - - test_task: "yjit-bindgen" - hint: "To fix: use patch in logs" - configure: "--with-gcc=clang-12 --enable-yjit=dev" + - test_task: 'yjit-bindgen' + hint: 'To fix: use patch in logs' + configure: '--with-gcc=clang-14 --enable-yjit=dev' + libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1' - test_task: "check" # YJIT should be automatically built in release mode on x86-64 Linux with rustc present @@ -79,7 +85,7 @@ jobs: YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }} RUBY_DEBUG: ci BUNDLE_JOBS: 8 # for yjit-bench - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build @@ -96,10 +102,10 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: src - - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src/.downloaded-cache key: downloaded-cache @@ -137,6 +143,7 @@ jobs: RUBY_TESTOPTS: "-q --tty=no" TEST_BUNDLED_GEMS_ALLOW_FAILURES: "" PRECHECK_BUNDLED_GEMS: "no" + LIBCLANG_PATH: ${{ matrix.libclang_path }} continue-on-error: ${{ matrix.test_task == 'yjit-bench' }} - name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison run: echo "https://github.com/${BASE_REPO}/commit/${BASE_SHA}" @@ -144,7 +151,7 @@ jobs: 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: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 + - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1 with: payload: | { diff --git a/.gitignore b/.gitignore index 535c4fb789..99d32a1825 100644 --- a/.gitignore +++ b/.gitignore @@ -238,7 +238,7 @@ lcov*.info # MJIT /include/ruby-*/*/rb_mjit_min_header-*.h -/lib/mjit/instruction.rb +/lib/ruby_vm/mjit/instruction.rb /mjit_config.h /rb_mjit_header.h diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 665feba914..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,235 +0,0 @@ -# -*- YAML -*- -# Copyright (C) 2011 Urabe, Shyouhei. All rights reserved. -# -# This file is a part of the programming language Ruby. Permission is hereby -# granted, to either redistribute or modify this file, provided that the -# conditions mentioned in the file COPYING are met. Consult the file for -# details. - -# We only manage non-amd64 free pipelines. -# https://docs.travis-ci.com/user/billing-overview/ - -language: c - -os: linux - -if: commit_message !~ /\[DOC\]/ - -dist: focal - -git: - quiet: true - -cache: - ccache: true - directories: - - $HOME/config_2nd - - $HOME/.downloaded-cache - -env: - global: - # The tests skipped in `make test-all`. - - TEST_ALL_SKIPPED_TESTS= - # The tests executed separately by `make test-all`. - - TEST_ALL_SEPARATED_TESTS= - # Reset timestamps early - - _=$(touch NEWS && find . -type f -exec touch -r NEWS {} +) - - CONFIGURE_TTY=no - - CCACHE_COMPILERCHECK=none - - CCACHE_NOCOMPRESS=1 - - CCACHE_MAXSIZE=512Mi - - NPROC="`nproc`" - # JOBS and SETARCH are overridden when necessary; see below. - - JOBS=-j$((1+${NPROC})) - - SETARCH= - - RUBY_PREFIX=/tmp/ruby-prefix - - GEMS_FOR_TEST='timezone tzinfo' - # https://github.com/travis-ci/travis-build/blob/e411371dda21430a60f61b8f3f57943d2fe4d344/lib/travis/build/bash/travis_apt_get_options.bash#L7 - - travis_apt_get_options='--allow-downgrades --allow-remove-essential --allow-change-held-packages' - - travis_apt_get_options="-yq --no-install-suggests --no-install-recommends $travis_apt_get_options" - # -O1 is faster than -O3 in our tests. - - optflags=-O1 - # -g0 disables backtraces when SEGV. Do not set that. - - debugflags=-ggdb3 - -.org.ruby-lang.ci.matrix-definitions: - - - &gcc-10 - compiler: gcc-10 - before_install: - - tool/travis_retry.sh sudo bash -c "rm -rf '${TRAVIS_ROOT}/var/lib/apt/lists/'* && exec apt-get update -yq" - - >- - tool/travis_retry.sh sudo -E apt-get $travis_apt_get_options install - ccache - gcc-10 - g++-10 - libffi-dev - libncurses-dev - libncursesw5-dev - libreadline-dev - libssl-dev - libyaml-dev - openssl - zlib1g-dev - - # -------- - - - &arm64-linux - name: arm64-linux - arch: arm64 - <<: *gcc-10 - - - &ppc64le-linux - name: ppc64le-linux - arch: ppc64le - <<: *gcc-10 - - - &s390x-linux - name: s390x-linux - arch: s390x - <<: *gcc-10 - - - &arm32-linux - name: arm32-linux - arch: arm64 - # https://packages.ubuntu.com/focal/crossbuild-essential-armhf - compiler: arm-linux-gnueabihf-gcc - env: - - SETARCH='setarch linux32 --verbose --32bit' - # The "TestReadline#test_interrupt_in_other_thread" started failing on arm32 - # from https://www.travis-ci.com/github/ruby/ruby/jobs/529005145 - - TEST_ALL_SKIPPED_TESTS=test_interrupt_in_other_thread - before_install: - - sudo dpkg --add-architecture armhf - - tool/travis_retry.sh sudo bash -c "rm -rf '${TRAVIS_ROOT}/var/lib/apt/lists/'* && exec apt-get update -yq" - - >- - tool/travis_retry.sh sudo -E apt-get $travis_apt_get_options install - ccache - crossbuild-essential-armhf - libc6:armhf - libstdc++-10-dev:armhf - libffi-dev:armhf - libncurses-dev:armhf - libncursesw5-dev:armhf - libreadline-dev:armhf - libssl-dev:armhf - libyaml-dev:armhf - linux-libc-dev:armhf - zlib1g-dev:armhf - -matrix: - include: - # Build every commit (Allowed Failures): - - <<: *arm32-linux - # Comment out as the 2nd arm64 pipeline is unstable. - # - <<: *arm64-linux - - <<: *ppc64le-linux - - <<: *s390x-linux - allow_failures: - # We see multiple errors indicating errors on the Travis environment itself in a short while: - # https://app.travis-ci.com/github/ruby/ruby/jobs/544382885 - # https://app.travis-ci.com/github/ruby/ruby/jobs/544361370 - # It's not a fault of Ruby's arm32 support but just Travis arm32 seems unsable. - - name: arm32-linux - # - name: arm64-linux - # We see "Some worker was crashed." in about 40% of recent ppc64le-linux jobs - # e.g. https://app.travis-ci.com/github/ruby/ruby/jobs/530959548 - - name: ppc64le-linux - # Tentatively disable, because often hungs up **after** all tests - # have finished successfully and saving caches. - - name: s390x-linux - fast_finish: true - -before_script: - - . tool/ci_functions.sh - - |- - if [ -n "${TEST_ALL_SKIPPED_TESTS}" ]; then - TEST_ALL_OPTS="${TEST_ALL_OPTS} $(ci_to_excluded_test_opts "${TEST_ALL_SKIPPED_TESTS}")" - if [ -z "${TEST_ALL_SEPARATED_TESTS}" ]; then - TEST_ALL_SEPARATED_TESTS="${TEST_ALL_SKIPPED_TESTS}" - fi - fi - - |- - if [ -n "${TEST_ALL_SEPARATED_TESTS}" ]; then - TEST_ALL_OPTS_SEPARATED="$(ci_to_included_test_opts "${TEST_ALL_SEPARATED_TESTS}")" - fi - - echo TEST_ALL_OPTS="${TEST_ALL_OPTS}" TEST_ALL_OPTS_SEPARATED="${TEST_ALL_OPTS_SEPARATED}" - - rm -fr .ext autom4te.cache - - |- - [ -d ~/.downloaded-cache ] || - mkdir ~/.downloaded-cache - - ln -s ~/.downloaded-cache - - "> config.status" - - "> .rbconfig.time" - - sed -f tool/prereq.status template/Makefile.in common.mk > Makefile - - make -s $JOBS up - - make -s $JOBS srcs - - rm -f config.status Makefile rbconfig.rb .rbconfig.time - - |- - if [ -d ~/config_2nd ]; then - cp -pr ~/config_2nd build - else - mkdir build - fi - - mkdir config_1st config_2nd gems/src - - chmod -R a-w . - - chmod -R u+w build config_1st config_2nd gems/src - - cd build - - |- - case "$CC" in - gcc*) CC="ccache $CC${GCC_FLAGS:+ }$GCC_FLAGS -fno-diagnostics-color";; - clang*) CC="ccache $CC${GCC_FLAGS:+ }$GCC_FLAGS -fno-color-diagnostics";; - esac - - |- - [ ! -f config.cache ] || - [ "$CC" = "`sed -n s/^ac_cv_prog_CC=//p config.cache`" ] || - (set -x; exec rm config.cache) - - $SETARCH ../configure -C --disable-install-doc --prefix=$RUBY_PREFIX $CONFIG_FLAG - - cp -pr config.cache config.status .ext/include ../config_1st - - $SETARCH make reconfig - - cp -pr config.cache config.status .ext/include ../config_2nd - - (cd .. && exec diff -ru config_1st config_2nd) - - chmod u+w .. - - rm -rf ~/config_2nd - - mv ../config_2nd ~ - - chmod u-w .. - - $SETARCH make -s $JOBS - - make -s install - - |- - [ -z "${GEMS_FOR_TEST}" ] || - $RUBY_PREFIX/bin/gem install --no-document $GEMS_FOR_TEST - - echo "raise 'do not load ~/.irbrc in test'" > ~/.irbrc - -script: - - $SETARCH make -s test -o showflags TESTOPTS="${TESTOPTS=$JOBS -q --tty=no}" - - ../tool/travis_wait.sh $SETARCH make -s test-all -o exts TESTOPTS="$JOBS -q --tty=no ${TEST_ALL_OPTS}" RUBYOPT="-w" - # Run the failing tests separately returning ok status to check if it works, - # visualize them. - - | - if [ -n "${TEST_ALL_OPTS_SEPARATED}" ]; then - $SETARCH make -s test-all -o exts TESTOPTS="$JOBS -v --tty=no ${TEST_ALL_OPTS_SEPARATED}" RUBYOPT="-w" || : - fi - - $SETARCH make -s test-spec MSPECOPT=-ff # not using `-j` because sometimes `mspec -j` silently dies - - $SETARCH make -s -o showflags leaked-globals - -# We enable Travis on the specific branches or forked repositories here. -if: (repo = ruby/ruby AND (branch = master OR branch =~ /^ruby_\d_\d$/)) OR repo != ruby/ruby - -# We want to be notified when something happens. -notifications: - irc: - channels: - - "chat.freenode.net#ruby-core" - on_success: change # [always|never|change] # default: always - on_failure: always # [always|never|change] # default: always - template: - - "%{message} by @%{author}: See %{build_url}" - - webhooks: - urls: - - secure: mRsoS/UbqDkKkW5p3AEqM27d4SZnV6Gsylo3bm8T/deltQzTsGzZwrm7OIBXZv0UFZdE68XmPlyHfZFLSP2V9QZ7apXMf9/vw0GtcSe1gchtnjpAPF6lYBn7nMCbVPPx9cS0dwL927fjdRM1vj7IKZ2bk4F0lAJ25R25S6teqdk= # ruby-lang slack: ruby/simpler-alerts-bot (travis) - on_success: never - on_failure: always - - email: - - jaruga@ruby-lang.org @@ -979,7 +979,6 @@ mentioned below. {MIT License}[rdoc-label:label-MIT+License] [lib/rubygems/resolver/molinillo] -[lib/bundler/vendor/molinillo] molinillo is under the following license. @@ -988,6 +987,15 @@ mentioned below. {MIT License}[rdoc-label:label-MIT+License] +[lib/bundler/vendor/pub_grub] + + pub_grub is under the following license. + + >>> + Copyright (c) 2018 John Hawthorn + + {MIT License}[rdoc-label:label-MIT+License] + [lib/bundler/vendor/connection_pool] connection_pool is under the following license. @@ -90,14 +90,6 @@ 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. Enabled using - `Coverage.setup(:all)` or `Coverage.setup(eval: true)`. [[Feature #19008]] - -* `Coverage.supported?(mode)` enables detection of what coverage modes are - supported. [[Feature #19026]] - -## Command line options - ## Core classes updates Note: We're only listing outstanding class updates. @@ -111,8 +103,8 @@ Note: We're only listing outstanding class updates. fiber. [[Feature #19078]] Existing Thread and Fiber local variables can be tricky to use. - Thread local variables are shared between all fibers, making it - hard to isolate, while Fiber local variables can be hard to + Thread-local variables are shared between all fibers, making it + hard to isolate, while Fiber-local variables can be hard to share. It is often desirable to define unit of execution ("execution context") such that some state is shared between all fibers and threads created in that context. This is what Fiber @@ -159,11 +151,8 @@ Note: We're only listing outstanding class updates. STDIN.read # => Blocking operation timed out! (IO::TimeoutError) ``` -* UNIXSocket - - * Add support for UNIXSocket on Windows. Emulate anonymous sockets. Add - support for File.socket? and File::Stat#socket? where possible. - [[Feature #19135]] + * Introduce `IO.new(..., path:)` and promote `File#path` to `IO#path`. + [[Feature #19036]] * Class @@ -187,6 +176,15 @@ Note: We're only listing outstanding class updates. similar to Struct and partially shares an implementation, but has more lean and strict API. [[Feature #16122]] + ```ruby + Measure = Data.define(:amount, :unit) + distance = Measure.new(100, 'km') #=> #<data Measure amount=100, unit="km"> + weight = Measure.new(amount: 50, unit: 'kg') #=> #<data Measure amount=50, unit="kg"> + weight.with(amount: 40) #=> #<data Measure amount=40, unit="kg"> + weight.amount #=> 50 + weight.amount = 40 #=> NoMethodError: undefined method `amount=' + ``` + * Encoding * Encoding#replicate has been deprecated and will be removed in 3.3. [[Feature #18949]] @@ -194,6 +192,8 @@ Note: We're only listing outstanding class updates. 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]] + * Limit maximum encoding set size by 256. + If exceeding maximum size, `EncodingError` will be raised. [[Feature #18949]] * Enumerator @@ -243,6 +243,13 @@ Note: We're only listing outstanding class updates. * Regexp + * The cache-based optimization is introduced. + Many (but not all) Regexp matching is now in linear time, which + will prevent regular expression denial of service (ReDoS) + vulnerability. [[Feature #19104]] + + * Regexp.linear_time? is introduced. [[Feature #19194]] + * Regexp.new now supports passing the regexp flags not only as an Integer, but also as a String. Unknown flags raise ArgumentError. Otherwise, anything other than `true`, `false`, `nil` or Integer will be warned. @@ -313,17 +320,6 @@ 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]] @@ -337,24 +333,35 @@ Note: We're only listing outstanding class updates. * A Struct class can also be initialized with keyword arguments without `keyword_init: true` on Struct.new [[Feature #16806]] + ```ruby + Post = Struct.new(:id, :name) + Post.new(1, "hello") #=> #<struct Post id=1, name="hello"> + # From Ruby 3.2, the following code also works without keyword_init: true. + Post.new(id: 1, name: "hello") #=> #<struct Post id=1, name="hello"> + ``` + * Thread - * Thread#each_caller_location is added. [[Feature #16663]] + * Thread.each_caller_location is added. [[Feature #16663]] * Thread::Queue - * Thread::Queue.pop(timeout: sec) is added. [[Feature #18774]] + * Thread::Queue#pop(timeout: sec) is added. [[Feature #18774]] * Thread::SizedQueue - * Thread::SizedQueue.pop(timeout: sec) is added. [[Feature #18774]] - * Thread::SizedQueue.push(timeout: sec) is added. [[Feature #18944]] + * Thread::SizedQueue#pop(timeout: sec) is added. [[Feature #18774]] + * Thread::SizedQueue#push(timeout: sec) is added. [[Feature #18944]] * Time * Time#deconstruct_keys is added, allowing to use Time instances in pattern-matching expressions [[Feature #19071]] + * Time.new now can parse a string like generated by Time#inspect + and return a Time instance based on the given argument. + [[Feature #18033]] + * SyntaxError * SyntaxError#path has been added. [[Feature #19138]] @@ -363,8 +370,8 @@ Note: We're only listing outstanding class updates. * TracePoint#binding now returns `nil` for `c_call`/`c_return` TracePoints. [[Bug #18487]] * TracePoint#enable `target_thread` keyword argument now defaults to the - current thread if `target` and `target_line` keyword arguments are not - passed. [[Bug #16889]] + current thread if a block is given and `target` and `target_line` keyword + arguments are not passed. [[Bug #16889]] * UnboundMethod @@ -377,10 +384,52 @@ Note: We're only listing outstanding class updates. `"#<UnboundMethod: Kernel#object_id()>"` (was `"#<UnboundMethod: String(Kernel)#object_id()>"`). +* GC + + * Expose `need_major_gc` via `GC.latest_gc_info`. [GH-6791] + +* ObjectSpace + + * `ObjectSpace.dump_all` dump shapes as well. [GH-6868] + ## Stdlib updates +* Bundler + + * Bundler now uses [PubGrub] resolver instead of [Molinillo] for performance improvement. + * Add --ext=rust support to bundle gem for creating simple gems with Rust extensions. + [[GH-rubygems-6149]] + * Make cloning git repos faster [[GH-rubygems-4475]] + +* RubyGems + + * Add mswin support for cargo builder. [[GH-rubygems-6167]] + +* CGI + + * `CGI.escapeURIComponent` and `CGI.unescapeURIComponent` are added. + [[Feature #18822]] + +* Coverage + + * `Coverage.setup` now accepts `eval: true`. By this, `eval` and related methods are + able to generate code coverage. [[Feature #19008]] + + * `Coverage.supported?(mode)` enables detection of what coverage modes are + supported. [[Feature #19026]] + +* Date + + * Added `Date#deconstruct_keys` and `DateTime#deconstruct_keys` same as [[Feature #19071]] + * ERB + * `ERB::Util.html_escape` is made faster than `CGI.escapeHTML`. + * It no longer allocates a String object when no character needs to be escaped. + * It skips calling `#to_s` method when an argument is already a String. + * `ERB::Escape.html_escape` is added as an alias to `ERB::Util.html_escape`, + which has not been monkey-patched by Rails. + * `ERB::Util.url_encode` is made faster using `CGI.escapeURIComponent`. * `-S` option is removed from `erb` command. * FileUtils @@ -388,23 +437,60 @@ Note: We're only listing outstanding class updates. * Add FileUtils.ln_sr method and `relative:` option to FileUtils.ln_s. [[Feature #18925]] +* IRB + + * debug.gem integration commands have been added: `debug`, `break`, `catch`, + `next`, `delete`, `step`, `continue`, `finish`, `backtrace`, `info` + * They work even if you don't have `gem "debug"` in your Gemfile. + * See also: [What's new in Ruby 3.2's IRB?](https://st0012.dev/whats-new-in-ruby-3-2-irb) + * More Pry-like commands and features have been added. + * `edit` and `show_cmds` (like Pry's `help`) are added. + * `ls` takes `-g` or `-G` option to filter out outputs. + * `show_source` is aliased from `$` and accepts unquoted inputs. + * `whereami` is aliased from `@`. + +* Net::Protocol + + * Improve `Net::BufferedIO` performance. [[GH-net-protocol-14]] + +* Pathname + + * Added `Pathname#lutime`. [[GH-pathname-20]] + +* 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` + * SyntaxSuggest * The feature of `syntax_suggest` formerly `dead_end` is integrated in Ruby. [[Feature #18159]] +* UNIXSocket + + * Add support for UNIXSocket on Windows. Emulate anonymous sockets. Add + support for File.socket? and File::Stat#socket? where possible. + [[Feature #19135]] + * The following default gems are updated. - * RubyGems 3.4.0.dev + * RubyGems 3.4.1 * abbrev 0.1.1 * benchmark 0.2.1 * bigdecimal 3.1.3 - * bundler 2.4.0.dev + * bundler 2.4.1 * cgi 0.3.6 * csv 3.2.6 - * date 3.3.1 + * date 3.3.3 * delegate 0.3.0 - * did_you_mean 1.6.2 + * did_you_mean 1.6.3 * digest 3.1.1 * drb 2.1.1 * english 0.7.2 @@ -416,27 +502,27 @@ Note: We're only listing outstanding class updates. * fileutils 1.7.0 * forwardable 1.3.3 * getoptlong 0.2.0 - * io-console 0.5.11 + * io-console 0.6.0 * io-nonblock 0.2.0 - * io-wait 0.3.0.pre + * io-wait 0.3.0 * ipaddr 1.2.5 - * irb 1.6.1 + * irb 1.6.2 * json 2.6.3 - * logger 1.5.2 + * logger 1.5.3 * mutex_m 0.1.2 - * net-http 0.3.1 + * net-http 0.4.0 * net-protocol 0.2.1 * nkf 0.1.2 * open-uri 0.3.0 * open3 0.1.2 - * openssl 3.1.0.pre - * optparse 0.3.0 + * openssl 3.1.0 + * optparse 0.3.1 * ostruct 0.5.5 * pathname 0.2.1 * pp 0.4.0 * pstore 0.1.2 * psych 5.0.1 - * racc 1.6.1 + * racc 1.6.2 * rdoc 6.5.0 * readline-ext 0.1.5 * reline 0.3.2 @@ -446,7 +532,7 @@ Note: We're only listing outstanding class updates. * set 1.0.3 * stringio 3.0.4 * strscan 3.0.5 - * syntax_suggest 1.0.1 + * syntax_suggest 1.0.2 * syslog 0.1.1 * tempfile 0.1.3 * time 0.2.1 @@ -463,23 +549,29 @@ Note: We're only listing outstanding class updates. * The following bundled gems are updated. * minitest 5.16.3 - * power_assert 2.0.2 - * test-unit 3.5.5 + * power_assert 2.0.3 + * test-unit 3.5.7 * net-ftp 0.2.0 - * net-imap 0.3.1 + * net-imap 0.3.4 * net-pop 0.1.2 * net-smtp 0.3.3 - * rbs 2.8.1 + * rbs 2.8.2 * typeprof 0.21.3 - * debug 1.7.0 + * debug 1.7.1 + +See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/logger/releases) or changelog for details of the default gems or bundled gems. -* The following default gems are now bundled gems. +## Supported platforms + +* WebAssembly/WASI is added. See [wasm/README.md] and [ruby.wasm] for more details. [[Feature #18462]] ## Compatibility issues * `String#to_c` currently treat a sequence of underscores as an end of Complex string. [[Bug #19087]] +* Now `ENV.clone` raises `TypeError` as well as `ENV.dup` [[Bug #17767]] + ### Removed constants The following deprecated constants are removed. @@ -517,9 +609,9 @@ The following deprecated methods are removed. ### Constant lookup when defining a class/module * When defining a class/module directly under the Object class by class/module - statement, if there is already a class/module with the same name, the statement - was handled as "open class" in Ruby 3.1 or before. Since Ruby 3.2, a new class - is defined instead. [[Feature #18832]] + statement, if there is already a class/module defined by `Module#include` + with the same name, the statement was handled as "open class" in Ruby 3.1 or before. + Since Ruby 3.2, a new class is defined instead. [[Feature #18832]] ## Stdlib compatibility issues @@ -543,6 +635,10 @@ The following deprecated methods are removed. [[Feature #18571]] +* Check cookie name/path/domain characters in `CGI::Cookie`. [[CVE-2021-33621]] + +* `URI.parse` return empty string in host instead of nil. [[sec-156615]] + ## C API updates ### Updated C APIs @@ -551,9 +647,9 @@ The following APIs are updated. * PRNG update - `rb_random_interface_t` updated and versioned. - Extension libraries which use this interface and built for older versions. - Also `init_int32` function needs to be defined. + `rb_random_interface_t` in ruby/random.h updated and versioned. + Extension libraries which use this interface and built for older + versions need to rebuild with adding `init_int32` function. ### Added C APIs @@ -565,6 +661,7 @@ The following APIs are updated. * `RUBY_INTERNAL_THREAD_EVENT_RESUMED` * `RUBY_INTERNAL_THREAD_EVENT_SUSPENDED` * `RUBY_INTERNAL_THREAD_EVENT_EXITED` +* `rb_debug_inspector_current_depth` and `rb_debug_inspector_frame_depth` are added for debuggers. ### Removed C APIs @@ -585,8 +682,16 @@ The following deprecated APIs are removed. [[Feature #18589]] * The cache-based optimization for Regexp matching is introduced. [[Feature #19104]] -* Variable Width Allocation is now enabled by default. - [[Feature #18239]]. +* [Variable Width Allocation](https://shopify.engineering/ruby-variable-width-allocation) + is now enabled by default. [[Feature #18239]] +* Added a new instance variable caching mechanism, called object shapes, which + improves inline cache hits for most objects and allows us to generate very + efficient JIT code. Objects whose instance variables are defined in a + consistent order will see the most performance benefits. + [[Feature #18776]] +* Speed up marking instruction sequences by using a bitmap to find "markable" + objects. This change results in faster major collections. + [[Feature #18875]] ## JIT @@ -618,7 +723,7 @@ The following deprecated APIs are removed. ### MJIT -* The MJIT compiler is re-implemented in Ruby as a standard library `mjit`. +* The MJIT compiler is re-implemented in Ruby as `ruby_vm/mjit/compiler`. * MJIT compiler is executed under a forked Ruby process instead of doing it in a native thread called MJIT worker. [[Feature #18968]] * As a result, Microsoft Visual Studio (MSWIN) is no longer supported. @@ -626,70 +731,90 @@ The following deprecated APIs are removed. * Rename `--mjit-min-calls` to `--mjit-call-threshold`. * Change default `--mjit-max-cache` back from 10000 to 100. -[Feature #12005]: https://bugs.ruby-lang.org/issues/12005 -[Feature #12084]: https://bugs.ruby-lang.org/issues/12084 -[Feature #12655]: https://bugs.ruby-lang.org/issues/12655 -[Feature #12737]: https://bugs.ruby-lang.org/issues/12737 -[Feature #13110]: https://bugs.ruby-lang.org/issues/13110 -[Feature #14332]: https://bugs.ruby-lang.org/issues/14332 -[Feature #15231]: https://bugs.ruby-lang.org/issues/15231 -[Feature #15357]: https://bugs.ruby-lang.org/issues/15357 -[Bug #15928]: https://bugs.ruby-lang.org/issues/15928 -[Feature #16122]: https://bugs.ruby-lang.org/issues/16122 -[Feature #16131]: https://bugs.ruby-lang.org/issues/16131 -[Bug #16466]: https://bugs.ruby-lang.org/issues/16466 -[Feature #16663]: https://bugs.ruby-lang.org/issues/16663 -[Feature #16806]: https://bugs.ruby-lang.org/issues/16806 -[Bug #16889]: https://bugs.ruby-lang.org/issues/16889 -[Bug #16908]: https://bugs.ruby-lang.org/issues/16908 -[Feature #16989]: https://bugs.ruby-lang.org/issues/16989 -[Feature #17351]: https://bugs.ruby-lang.org/issues/17351 -[Feature #17391]: https://bugs.ruby-lang.org/issues/17391 -[Bug #17545]: https://bugs.ruby-lang.org/issues/17545 -[Feature #17837]: https://bugs.ruby-lang.org/issues/17837 -[Feature #17881]: https://bugs.ruby-lang.org/issues/17881 -[Feature #18159]: https://bugs.ruby-lang.org/issues/18159 -[Feature #18239]: https://bugs.ruby-lang.org/issues/18239#note-17 -[Feature #18351]: https://bugs.ruby-lang.org/issues/18351 -[Feature #18367]: https://bugs.ruby-lang.org/issues/18367 -[Bug #18435]: https://bugs.ruby-lang.org/issues/18435 -[Feature #18481]: https://bugs.ruby-lang.org/issues/18481 -[Bug #18487]: https://bugs.ruby-lang.org/issues/18487 -[Feature #18564]: https://bugs.ruby-lang.org/issues/18564 -[Feature #18571]: https://bugs.ruby-lang.org/issues/18571 -[Feature #18585]: https://bugs.ruby-lang.org/issues/18585 -[Feature #18589]: https://bugs.ruby-lang.org/issues/18589 -[Feature #18595]: https://bugs.ruby-lang.org/issues/18595 -[Feature #18598]: https://bugs.ruby-lang.org/issues/18598 -[Bug #18625]: https://bugs.ruby-lang.org/issues/18625 -[Feature #18630]: https://bugs.ruby-lang.org/issues/18630 -[Bug #18633]: https://bugs.ruby-lang.org/issues/18633 -[Feature #18639]: https://bugs.ruby-lang.org/issues/18639 -[Feature #18685]: https://bugs.ruby-lang.org/issues/18685 -[Bug #18729]: https://bugs.ruby-lang.org/issues/18729 -[Bug #18751]: https://bugs.ruby-lang.org/issues/18751 -[Feature #18774]: https://bugs.ruby-lang.org/issues/18774 -[Feature #18776]: https://bugs.ruby-lang.org/issues/18776 -[Bug #18782]: https://bugs.ruby-lang.org/issues/18782 -[Feature #18788]: https://bugs.ruby-lang.org/issues/18788 -[Feature #18798]: https://bugs.ruby-lang.org/issues/18798 -[Feature #18809]: https://bugs.ruby-lang.org/issues/18809 -[Feature #18821]: https://bugs.ruby-lang.org/issues/18821 -[Feature #18824]: https://bugs.ruby-lang.org/issues/18824 -[Feature #18832]: https://bugs.ruby-lang.org/issues/18832 -[Feature #18925]: https://bugs.ruby-lang.org/issues/18925 -[Feature #18944]: https://bugs.ruby-lang.org/issues/18944 -[Feature #18949]: https://bugs.ruby-lang.org/issues/18949 -[Feature #18968]: https://bugs.ruby-lang.org/issues/18968 -[Feature #19008]: https://bugs.ruby-lang.org/issues/19008 -[Feature #19013]: https://bugs.ruby-lang.org/issues/19013 -[Feature #19026]: https://bugs.ruby-lang.org/issues/19026 -[Feature #19060]: https://bugs.ruby-lang.org/issues/19060 -[Feature #19070]: https://bugs.ruby-lang.org/issues/19070 -[Feature #19071]: https://bugs.ruby-lang.org/issues/19071 -[Feature #19078]: https://bugs.ruby-lang.org/issues/19078 -[Bug #19087]: https://bugs.ruby-lang.org/issues/19087 -[Bug #19100]: https://bugs.ruby-lang.org/issues/19100 -[Feature #19104]: https://bugs.ruby-lang.org/issues/19104 -[Feature #19135]: https://bugs.ruby-lang.org/issues/19135 -[Feature #19138]: https://bugs.ruby-lang.org/issues/19138 +[Feature #12005]: https://bugs.ruby-lang.org/issues/12005 +[Feature #12084]: https://bugs.ruby-lang.org/issues/12084 +[Feature #12655]: https://bugs.ruby-lang.org/issues/12655 +[Feature #12737]: https://bugs.ruby-lang.org/issues/12737 +[Feature #13110]: https://bugs.ruby-lang.org/issues/13110 +[Feature #14332]: https://bugs.ruby-lang.org/issues/14332 +[Feature #15231]: https://bugs.ruby-lang.org/issues/15231 +[Feature #15357]: https://bugs.ruby-lang.org/issues/15357 +[Bug #15928]: https://bugs.ruby-lang.org/issues/15928 +[Feature #16122]: https://bugs.ruby-lang.org/issues/16122 +[Feature #16131]: https://bugs.ruby-lang.org/issues/16131 +[Bug #16466]: https://bugs.ruby-lang.org/issues/16466 +[Feature #16663]: https://bugs.ruby-lang.org/issues/16663 +[Feature #16806]: https://bugs.ruby-lang.org/issues/16806 +[Bug #16889]: https://bugs.ruby-lang.org/issues/16889 +[Bug #16908]: https://bugs.ruby-lang.org/issues/16908 +[Feature #16989]: https://bugs.ruby-lang.org/issues/16989 +[Feature #17351]: https://bugs.ruby-lang.org/issues/17351 +[Feature #17391]: https://bugs.ruby-lang.org/issues/17391 +[Bug #17545]: https://bugs.ruby-lang.org/issues/17545 +[Bug #17767]: https://bugs.ruby-lang.org/issues/17767 +[Feature #17837]: https://bugs.ruby-lang.org/issues/17837 +[Feature #17881]: https://bugs.ruby-lang.org/issues/17881 +[Feature #18033]: https://bugs.ruby-lang.org/issues/18033 +[Feature #18159]: https://bugs.ruby-lang.org/issues/18159 +[Feature #18239]: https://bugs.ruby-lang.org/issues/18239#note-17 +[Feature #18351]: https://bugs.ruby-lang.org/issues/18351 +[Feature #18367]: https://bugs.ruby-lang.org/issues/18367 +[Bug #18435]: https://bugs.ruby-lang.org/issues/18435 +[Feature #18462]: https://bugs.ruby-lang.org/issues/18462 +[Feature #18481]: https://bugs.ruby-lang.org/issues/18481 +[Bug #18487]: https://bugs.ruby-lang.org/issues/18487 +[Feature #18564]: https://bugs.ruby-lang.org/issues/18564 +[Feature #18571]: https://bugs.ruby-lang.org/issues/18571 +[Feature #18585]: https://bugs.ruby-lang.org/issues/18585 +[Feature #18589]: https://bugs.ruby-lang.org/issues/18589 +[Feature #18595]: https://bugs.ruby-lang.org/issues/18595 +[Feature #18598]: https://bugs.ruby-lang.org/issues/18598 +[Bug #18625]: https://bugs.ruby-lang.org/issues/18625 +[Feature #18630]: https://bugs.ruby-lang.org/issues/18630 +[Bug #18633]: https://bugs.ruby-lang.org/issues/18633 +[Feature #18639]: https://bugs.ruby-lang.org/issues/18639 +[Feature #18685]: https://bugs.ruby-lang.org/issues/18685 +[Bug #18729]: https://bugs.ruby-lang.org/issues/18729 +[Bug #18751]: https://bugs.ruby-lang.org/issues/18751 +[Feature #18774]: https://bugs.ruby-lang.org/issues/18774 +[Feature #18776]: https://bugs.ruby-lang.org/issues/18776 +[Bug #18782]: https://bugs.ruby-lang.org/issues/18782 +[Feature #18788]: https://bugs.ruby-lang.org/issues/18788 +[Feature #18798]: https://bugs.ruby-lang.org/issues/18798 +[Feature #18809]: https://bugs.ruby-lang.org/issues/18809 +[Feature #18821]: https://bugs.ruby-lang.org/issues/18821 +[Feature #18822]: https://bugs.ruby-lang.org/issues/18822 +[Feature #18824]: https://bugs.ruby-lang.org/issues/18824 +[Feature #18832]: https://bugs.ruby-lang.org/issues/18832 +[Feature #18875]: https://bugs.ruby-lang.org/issues/18875 +[Feature #18925]: https://bugs.ruby-lang.org/issues/18925 +[Feature #18944]: https://bugs.ruby-lang.org/issues/18944 +[Feature #18949]: https://bugs.ruby-lang.org/issues/18949 +[Feature #18968]: https://bugs.ruby-lang.org/issues/18968 +[Feature #19008]: https://bugs.ruby-lang.org/issues/19008 +[Feature #19013]: https://bugs.ruby-lang.org/issues/19013 +[Feature #19026]: https://bugs.ruby-lang.org/issues/19026 +[Feature #19036]: https://bugs.ruby-lang.org/issues/19036 +[Feature #19060]: https://bugs.ruby-lang.org/issues/19060 +[Feature #19070]: https://bugs.ruby-lang.org/issues/19070 +[Feature #19071]: https://bugs.ruby-lang.org/issues/19071 +[Feature #19078]: https://bugs.ruby-lang.org/issues/19078 +[Bug #19087]: https://bugs.ruby-lang.org/issues/19087 +[Bug #19100]: https://bugs.ruby-lang.org/issues/19100 +[Feature #19104]: https://bugs.ruby-lang.org/issues/19104 +[Feature #19135]: https://bugs.ruby-lang.org/issues/19135 +[Feature #19138]: https://bugs.ruby-lang.org/issues/19138 +[Feature #19194]: https://bugs.ruby-lang.org/issues/19194 +[Molinillo]: https://github.com/CocoaPods/Molinillo +[PubGrub]: https://github.com/jhawthorn/pub_grub +[GH-net-protocol-14]: https://github.com/ruby/net-protocol/pull/14 +[GH-pathname-20]: https://github.com/ruby/pathname/pull/20 +[GH-6791]: https://github.com/ruby/ruby/pull/6791 +[GH-6868]: https://github.com/ruby/ruby/pull/6868 +[GH-rubygems-4475]: https://github.com/rubygems/rubygems/pull/4475 +[GH-rubygems-6149]: https://github.com/rubygems/rubygems/pull/6149 +[GH-rubygems-6167]: https://github.com/rubygems/rubygems/pull/6167 +[sec-156615]: https://hackerone.com/reports/156615 +[CVE-2021-33621]: https://www.ruby-lang.org/en/news/2022/11/22/http-response-splitting-in-cgi-cve-2021-33621/ +[wasm/README.md]: https://github.com/ruby/ruby/blob/master/wasm/README.md +[ruby.wasm]: https://github.com/ruby/ruby.wasm diff --git a/README.ja.md b/README.ja.md index bb69c09055..93c0131690 100644 --- a/README.ja.md +++ b/README.ja.md @@ -4,7 +4,6 @@ [](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows") [](https://ci.appveyor.com/project/ruby/ruby/branch/master) [](https://app.travis-ci.com/ruby/ruby) -[](https://cirrus-ci.com/github/ruby/ruby/master) # Rubyとは @@ -4,7 +4,6 @@ [](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows") [](https://ci.appveyor.com/project/ruby/ruby/branch/master) [](https://app.travis-ci.com/ruby/ruby) -[](https://cirrus-ci.com/github/ruby/ruby/master) # What is Ruby? diff --git a/addr2line.c b/addr2line.c index 0d45ec9414..e5f25293e2 100644 --- a/addr2line.c +++ b/addr2line.c @@ -159,12 +159,15 @@ typedef struct obj_info { struct dwarf_section debug_info; struct dwarf_section debug_line; struct dwarf_section debug_ranges; + struct dwarf_section debug_str_offsets; + struct dwarf_section debug_addr; struct dwarf_section debug_rnglists; struct dwarf_section debug_str; + struct dwarf_section debug_line_str; struct obj_info *next; } obj_info_t; -#define DWARF_SECTION_COUNT 6 +#define DWARF_SECTION_COUNT 9 static struct dwarf_section * obj_dwarf_section_at(obj_info_t *obj, int n) @@ -174,8 +177,11 @@ obj_dwarf_section_at(obj_info_t *obj, int n) &obj->debug_info, &obj->debug_line, &obj->debug_ranges, + &obj->debug_str_offsets, + &obj->debug_addr, &obj->debug_rnglists, - &obj->debug_str + &obj->debug_str, + &obj->debug_line_str }; if (n < 0 || DWARF_SECTION_COUNT <= n) { abort(); @@ -248,39 +254,51 @@ get_nth_dirname(unsigned long dir, const char *p) return p; } +static const char *parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index); + static void -fill_filename(int file, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj) +fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj) { int i; const char *p = filenames; const char *filename; unsigned long dir; - for (i = 1; i <= file; i++) { - filename = p; - if (!*p) { - /* Need to output binary file name? */ - kprintf("Unexpected file number %d in %s at %tx\n", - file, binary_filename, filenames - obj->mapped); - return; - } - while (*p) p++; - p++; - dir = uleb128(&p); - /* last modified. */ - uleb128(&p); - /* size of the file. */ - uleb128(&p); - - if (i == file) { - line->filename = filename; - line->dirname = get_nth_dirname(dir, include_directories); - } + if (version >= 5) { + const char *path; + uint64_t directory_index = -1; + parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index); + line->filename = path; + parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL); + line->dirname = path; + } + else { + for (i = 1; i <= file; i++) { + filename = p; + if (!*p) { + /* Need to output binary file name? */ + kprintf("Unexpected file number %d in %s at %tx\n", + file, binary_filename, filenames - obj->mapped); + return; + } + while (*p) p++; + p++; + dir = uleb128(&p); + /* last modified. */ + uleb128(&p); + /* size of the file. */ + uleb128(&p); + + if (i == file) { + line->filename = filename; + line->dirname = get_nth_dirname(dir, include_directories); + } + } } } static void fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line, - const char *include_directories, const char *filenames, + uint8_t format, uint16_t version, const char *include_directories, const char *filenames, obj_info_t *obj, line_info_t *lines, int offset) { int i; @@ -290,7 +308,7 @@ fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line, /* We assume one line code doesn't result >100 bytes of native code. We may want more reliable way eventually... */ if (addr < a && a < addr + 100) { - fill_filename(file, include_directories, filenames, &lines[i], obj); + fill_filename(file, format, version, include_directories, filenames, &lines[i], obj); lines[i].line = line; } } @@ -315,7 +333,7 @@ struct LineNumberProgramHeader { }; static int -parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header) +parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header) { const char *p = *pp; header->unit_length = *(uint32_t *)p; @@ -332,7 +350,13 @@ parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header) header->version = *(uint16_t *)p; p += sizeof(uint16_t); - if (header->version > 4) return -1; + if (header->version > 5) return -1; + + if (header->version >= 5) { + /* address_size = *(uint8_t *)p++; */ + /* segment_selector_size = *(uint8_t *)p++; */ + p += 2; + } header->header_length = header->format == 4 ? *(uint32_t *)p : *(uint64_t *)p; p += header->format; @@ -353,20 +377,27 @@ parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header) /* header->standard_opcode_lengths = (uint8_t *)p - 1; */ p += header->opcode_base - 1; - header->include_directories = p; + if (header->version >= 5) { + header->include_directories = p; + p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL); + header->filenames = p; + } + else { + header->include_directories = p; - /* temporary measure for compress-debug-sections */ - if (p >= header->cu_end) return -1; + /* temporary measure for compress-debug-sections */ + if (p >= header->cu_end) return -1; - /* skip include directories */ - while (*p) { - p = memchr(p, '\0', header->cu_end - p); - if (!p) return -1; - p++; - } - p++; + /* skip include directories */ + while (*p) { + p = memchr(p, '\0', header->cu_end - p); + if (!p) return -1; + p++; + } + p++; - header->filenames = p; + header->filenames = p; + } *pp = header->cu_start; @@ -392,13 +423,15 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line, /* int epilogue_begin = 0; */ /* unsigned int isa = 0; */ - if (parse_debug_line_header(&p, &header)) + if (parse_debug_line_header(obj, &p, &header)) return -1; is_stmt = header.default_is_stmt; #define FILL_LINE() \ do { \ fill_line(num_traces, traces, addr, file, line, \ + header.format, \ + header.version, \ header.include_directories, \ header.filenames, \ obj, lines, offset); \ @@ -827,16 +860,23 @@ enum { VAL_cstr = 1, VAL_data = 2, VAL_uint = 3, - VAL_int = 4 + VAL_int = 4, + VAL_addr = 5 }; # define ABBREV_TABLE_SIZE 256 typedef struct { obj_info_t *obj; const char *file; + uint8_t current_version; const char *current_cu; uint64_t current_low_pc; + uint64_t current_str_offsets_base; + uint64_t current_addr_base; + uint64_t current_rnglists_base; const char *debug_line_cu_end; + uint8_t debug_line_format; + uint16_t debug_line_version; const char *debug_line_files; const char *debug_line_directories; const char *p; @@ -861,6 +901,7 @@ typedef struct { const char *ptr; uint64_t uint64; int64_t int64; + uint64_t addr_idx; } as; uint64_t off; uint64_t at; @@ -976,6 +1017,9 @@ debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj) reader->pend = obj->debug_info.ptr + obj->debug_info.size; reader->debug_line_cu_end = obj->debug_line.ptr; reader->current_low_pc = 0; + reader->current_str_offsets_base = 0; + reader->current_addr_base = 0; + reader->current_rnglists_base = 0; } static void @@ -1020,10 +1064,12 @@ di_read_debug_line_cu(DebugInfoReader *reader) struct LineNumberProgramHeader header; p = (const char *)reader->debug_line_cu_end; - if (parse_debug_line_header(&p, &header)) + if (parse_debug_line_header(reader->obj, &p, &header)) return -1; reader->debug_line_cu_end = (char *)header.cu_end; + reader->debug_line_format = header.format; + reader->debug_line_version = header.version; reader->debug_line_directories = (char *)header.include_directories; reader->debug_line_files = (char *)header.filenames; @@ -1031,6 +1077,13 @@ di_read_debug_line_cu(DebugInfoReader *reader) } static void +set_addr_idx_value(DebugInfoValue *v, uint64_t n) +{ + v->as.addr_idx = n; + v->type = VAL_addr; +} + +static void set_uint_value(DebugInfoValue *v, uint64_t n) { v->as.uint64 = n; @@ -1077,19 +1130,39 @@ get_cstr_value(DebugInfoValue *v) } } +static const char * +resolve_strx(DebugInfoReader *reader, uint64_t idx) +{ + const char *p = reader->obj->debug_str_offsets.ptr + reader->current_str_offsets_base; + uint64_t off; + if (reader->format == 4) { + off = ((uint32_t *)p)[idx]; + } + else { + off = ((uint64_t *)p)[idx]; + } + return reader->obj->debug_str.ptr + off; +} + +static void +debug_info_reader_read_addr_value(DebugInfoReader *reader, DebugInfoValue *v) +{ + if (reader->address_size == 4) { + set_uint_value(v, read_uint32(&reader->p)); + } else if (reader->address_size == 8) { + set_uint_value(v, read_uint64(&reader->p)); + } else { + fprintf(stderr,"unknown address_size:%d", reader->address_size); + abort(); + } +} + static void debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v) { switch (form) { case DW_FORM_addr: - if (reader->address_size == 4) { - set_uint_value(v, read_uint32(&reader->p)); - } else if (reader->address_size == 8) { - set_uint_value(v, read_uint64(&reader->p)); - } else { - fprintf(stderr,"unknown address_size:%d", reader->address_size); - abort(); - } + debug_info_reader_read_addr_value(reader, v); break; case DW_FORM_block2: v->size = read_uint16(&reader->p); @@ -1141,13 +1214,19 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa set_uint_value(v, read_uleb128(reader)); break; case DW_FORM_ref_addr: - if (reader->format == 4) { - set_uint_value(v, read_uint32(&reader->p)); - } else if (reader->format == 8) { - set_uint_value(v, read_uint64(&reader->p)); + if (reader->current_version <= 2) { + // DWARF Version 2 specifies that references have + // the same size as an address on the target system + debug_info_reader_read_addr_value(reader, v); } else { - fprintf(stderr,"unknown format:%d", reader->format); - abort(); + if (reader->format == 4) { + set_uint_value(v, read_uint32(&reader->p)); + } else if (reader->format == 8) { + set_uint_value(v, read_uint64(&reader->p)); + } else { + fprintf(stderr,"unknown format:%d", reader->format); + abort(); + } } break; case DW_FORM_ref1: @@ -1189,11 +1268,10 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa set_uint_value(v, 1); break; case DW_FORM_strx: - set_uint_value(v, uleb128(&reader->p)); + set_cstr_value(v, resolve_strx(reader, uleb128(&reader->p))); break; case DW_FORM_addrx: - /* TODO: read .debug_addr */ - set_uint_value(v, uleb128(&reader->p)); + set_addr_idx_value(v, uleb128(&reader->p)); break; case DW_FORM_ref_sup4: set_uint_value(v, read_uint32(&reader->p)); @@ -1208,8 +1286,7 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa reader->p += v->size; break; case DW_FORM_line_strp: - set_uint_value(v, read_uint(reader)); - /* *p = reader->file + reader->line->sh_offset + ret; */ + set_cstrp_value(v, reader->obj->debug_line_str.ptr, read_uint(reader)); break; case DW_FORM_ref_sig8: set_uint_value(v, read_uint64(&reader->p)); @@ -1227,28 +1304,28 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa set_uint_value(v, read_uint64(&reader->p)); break; case DW_FORM_strx1: - set_uint_value(v, read_uint8(&reader->p)); + set_cstr_value(v, resolve_strx(reader, read_uint8(&reader->p))); break; case DW_FORM_strx2: - set_uint_value(v, read_uint16(&reader->p)); + set_cstr_value(v, resolve_strx(reader, read_uint16(&reader->p))); break; case DW_FORM_strx3: - set_uint_value(v, read_uint24(&reader->p)); + set_cstr_value(v, resolve_strx(reader, read_uint24(&reader->p))); break; case DW_FORM_strx4: - set_uint_value(v, read_uint32(&reader->p)); + set_cstr_value(v, resolve_strx(reader, read_uint32(&reader->p))); break; case DW_FORM_addrx1: - set_uint_value(v, read_uint8(&reader->p)); + set_addr_idx_value(v, read_uint8(&reader->p)); break; case DW_FORM_addrx2: - set_uint_value(v, read_uint16(&reader->p)); + set_addr_idx_value(v, read_uint16(&reader->p)); break; case DW_FORM_addrx3: - set_uint_value(v, read_uint24(&reader->p)); + set_addr_idx_value(v, read_uint24(&reader->p)); break; case DW_FORM_addrx4: - set_uint_value(v, read_uint32(&reader->p)); + set_addr_idx_value(v, read_uint32(&reader->p)); break; case 0: goto fail; @@ -1376,6 +1453,76 @@ di_skip_records(DebugInfoReader *reader) } } +typedef struct addr_header { + const char *ptr; + uint64_t unit_length; + uint8_t format; + uint8_t address_size; + /* uint8_t segment_selector_size; */ +} addr_header_t; + +static void +addr_header_init(obj_info_t *obj, addr_header_t *header) { + const char *p = obj->debug_addr.ptr; + + header->ptr = p; + + if (!p) return; + + header->unit_length = *(uint32_t *)p; + p += sizeof(uint32_t); + + header->format = 4; + if (header->unit_length == 0xffffffff) { + header->unit_length = *(uint64_t *)p; + p += sizeof(uint64_t); + header->format = 8; + } + + p += 2; /* version */ + header->address_size = *p++; + p++; /* segment_selector_size */ +} + +static uint64_t +read_addr(addr_header_t *header, uint64_t addr_base, uint64_t idx) { + if (header->address_size == 4) { + return ((uint32_t*)(header->ptr + addr_base))[idx]; + } + else { + return ((uint64_t*)(header->ptr + addr_base))[idx]; + } +} + +typedef struct rnglists_header { + uint64_t unit_length; + uint8_t format; + uint8_t address_size; + uint32_t offset_entry_count; +} rnglists_header_t; + +static void +rnglists_header_init(obj_info_t *obj, rnglists_header_t *header) { + const char *p = obj->debug_rnglists.ptr; + + if (!p) return; + + header->unit_length = *(uint32_t *)p; + p += sizeof(uint32_t); + + header->format = 4; + if (header->unit_length == 0xffffffff) { + header->unit_length = *(uint64_t *)p; + p += sizeof(uint64_t); + header->format = 8; + } + + p += 2; /* version */ + header->address_size = *p++; + p++; /* segment_selector_size */ + header->offset_entry_count = *(uint32_t *)p; +} + typedef struct { uint64_t low_pc; uint64_t high_pc; @@ -1386,24 +1533,31 @@ typedef struct { } ranges_t; static void -ranges_set(ranges_t *ptr, DebugInfoValue *v) +ranges_set(ranges_t *ptr, DebugInfoValue *v, addr_header_t *addr_header, uint64_t addr_base) { + uint64_t n = 0; + if (v->type == VAL_uint) { + n = v->as.uint64; + } + else if (v->type == VAL_addr) { + n = read_addr(addr_header, addr_base, v->as.addr_idx); + } switch (v->at) { case DW_AT_low_pc: - ptr->low_pc = v->as.uint64; + ptr->low_pc = n; ptr->low_pc_set = true; break; case DW_AT_high_pc: if (v->form == DW_FORM_addr) { - ptr->high_pc = v->as.uint64; + ptr->high_pc = n; } else { - ptr->high_pc = ptr->low_pc + v->as.uint64; + ptr->high_pc = ptr->low_pc + n; } ptr->high_pc_set = true; break; case DW_AT_ranges: - ptr->ranges = v->as.uint64; + ptr->ranges = n; ptr->ranges_set = true; break; } @@ -1425,7 +1579,7 @@ read_dw_form_addr(DebugInfoReader *reader, const char **ptr) } static uintptr_t -ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) +ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header) { if (ptr->high_pc_set) { if (ptr->ranges_set || !ptr->low_pc_set) { @@ -1440,8 +1594,21 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) const char *p; uint64_t base = ptr->low_pc_set ? ptr->low_pc : reader->current_low_pc; bool base_valid = true; - if (reader->obj->debug_rnglists.ptr) { - p = reader->obj->debug_rnglists.ptr + ptr->ranges; + if (reader->current_version >= 5) { + if (rnglists_header->offset_entry_count == 0) { + // DW_FORM_sec_offset + p = reader->obj->debug_rnglists.ptr + ptr->ranges + reader->current_rnglists_base; + } + else { + // DW_FORM_rnglistx + const char *offset_array = reader->obj->debug_rnglists.ptr + reader->current_rnglists_base; + if (rnglists_header->format == 4) { + p = offset_array + ((uint32_t *)offset_array)[ptr->ranges]; + } + else { + p = offset_array + ((uint64_t *)offset_array)[ptr->ranges]; + } + } for (;;) { uint8_t rle = read_uint8(&p); uintptr_t from = 0, to = 0; @@ -1551,6 +1718,7 @@ di_read_cu(DebugInfoReader *reader) } reader->cu_end = reader->p + unit_length; version = read_uint16(&reader->p); + reader->current_version = version; if (version > 5) { return -1; } @@ -1583,16 +1751,45 @@ di_read_cu(DebugInfoReader *reader) break; } + reader->current_str_offsets_base = 0; + reader->current_addr_base = 0; + reader->current_rnglists_base = 0; + + DebugInfoValue low_pc = {{}}; /* enumerate abbrev */ for (;;) { DebugInfoValue v = {{}}; if (!di_read_record(reader, &v)) break; switch (v.at) { case DW_AT_low_pc: - reader->current_low_pc = v.as.uint64; + // clang may output DW_AT_addr_base after DW_AT_low_pc. + // We need to resolve the DW_FORM_addr* after DW_AT_addr_base is parsed. + low_pc = v; + break; + case DW_AT_str_offsets_base: + reader->current_str_offsets_base = v.as.uint64; + break; + case DW_AT_addr_base: + reader->current_addr_base = v.as.uint64; + break; + case DW_AT_rnglists_base: + reader->current_rnglists_base = v.as.uint64; break; } } + // Resolve the DW_FORM_addr of DW_AT_low_pc + switch (low_pc.type) { + case VAL_uint: + reader->current_low_pc = low_pc.as.uint64; + break; + case VAL_addr: + { + addr_header_t header; + addr_header_init(reader->obj, &header); + reader->current_low_pc = read_addr(&header, reader->current_addr_base, low_pc.as.addr_idx); + } + break; + } } while (0); #endif return 0; @@ -1646,6 +1843,13 @@ read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_o static void debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, line_info_t *lines, int offset) { + + addr_header_t addr_header = {}; + addr_header_init(reader->obj, &addr_header); + + rnglists_header_t rnglists_header = {}; + rnglists_header_init(reader->obj, &rnglists_header); + while (reader->p < reader->cu_end) { DIE die; ranges_t ranges = {}; @@ -1672,7 +1876,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, line.sname = get_cstr_value(&v); break; case DW_AT_call_file: - fill_filename((int)v.as.uint64, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj); + fill_filename((int)v.as.uint64, reader->debug_line_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj); break; case DW_AT_call_line: line.line = (int)v.as.uint64; @@ -1680,7 +1884,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges: - ranges_set(&ranges, &v); + ranges_set(&ranges, &v, &addr_header, reader->current_addr_base); break; case DW_AT_declaration: goto skip_die; @@ -1697,9 +1901,9 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, for (int i=offset; i < num_traces; i++) { uintptr_t addr = (uintptr_t)traces[i]; uintptr_t offset = addr - reader->obj->base_addr + reader->obj->vmaddr; - uintptr_t saddr = ranges_include(reader, &ranges, offset); + uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header); if (saddr) { - /* fprintf(stderr, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */ + /* fprintf(stdout, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */ if (lines[i].sname) { line_info_t *lp = malloc(sizeof(line_info_t)); memcpy(lp, &lines[i], sizeof(line_info_t)); @@ -1718,6 +1922,54 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, } } +// This function parses the following attributes of Line Number Program Header in DWARF 5: +// +// * directory_entry_format_count +// * directory_entry_format +// * directories_count +// * directories +// +// or +// +// * file_name_entry_format_count +// * file_name_entry_format +// * file_names_count +// * file_names +// +// It records DW_LNCT_path and DW_LNCT_directory_index at the index "idx". +static const char * +parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index) { + int i, j; + int entry_format_count = *(uint8_t *)p++; + const char *entry_format = p; + + /* skip the part of entry_format */ + for (i = 0; i < entry_format_count * 2; i++) uleb128(&p); + + int entry_count = (int)uleb128(&p); + + DebugInfoReader reader; + debug_info_reader_init(&reader, obj); + reader.format = format; + reader.p = p; + for (j = 0; j < entry_count; j++) { + const char *format = entry_format; + for (i = 0; i < entry_format_count; i++) { + DebugInfoValue v = {{}}; + unsigned long dw_lnct = uleb128(&format); + unsigned long dw_form = uleb128(&format); + debug_info_reader_read_value(&reader, dw_form, &v); + if (dw_lnct == 1 /* DW_LNCT_path */ && v.type == VAL_cstr && out_path) + *out_path = v.as.ptr + v.off; + if (dw_lnct == 2 /* DW_LNCT_directory_index */ && v.type == VAL_uint && out_directory_index) + *out_directory_index = v.as.uint64; + } + if (j == idx) return 0; + } + + return reader.p; +} + #ifdef USE_ELF static unsigned long uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr) @@ -1846,8 +2098,11 @@ fill_lines(int num_traces, void **traces, int check_debuglink, ".debug_info", ".debug_line", ".debug_ranges", + ".debug_str_offsets", + ".debug_addr", ".debug_rnglists", - ".debug_str" + ".debug_str", + ".debug_line_str" }; for (j=0; j < DWARF_SECTION_COUNT; j++) { @@ -2103,8 +2358,11 @@ found_mach_header: "__debug_info", "__debug_line", "__debug_ranges", + "__debug_str_offsets", + "__debug_addr", "__debug_rnglists", - "__debug_str" + "__debug_str", + "__debug_line_str", }; struct LP(segment_command) *scmd = (struct LP(segment_command) *)lcmd; if (strcmp(scmd->segname, "__TEXT") == 0) { @@ -2302,6 +2560,7 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces) obj_info_t *obj = NULL; /* 2 is NULL + main executable */ void **dladdr_fbases = (void **)calloc(num_traces+2, sizeof(void *)); + #ifdef HAVE_MAIN_EXE_PATH char *main_path = NULL; /* used on printing backtrace */ ssize_t len; @@ -226,8 +226,15 @@ ary_embeddable_p(long capa) bool rb_ary_embeddable_p(VALUE ary) { - // if the array is shared or a shared root then it's not moveable - return !(ARY_SHARED_P(ary) || ARY_SHARED_ROOT_P(ary)); + /* An array cannot be turned embeddable when the array is: + * - Shared root: other objects may point to the buffer of this array + * so we cannot make it embedded. + * - Frozen: this array may also be a shared root without the shared root + * flag. + * - Shared: we don't want to re-embed an array that points to a shared + * root (to save memory). + */ + return !(ARY_SHARED_ROOT_P(ary) || OBJ_FROZEN(ary) || ARY_SHARED_P(ary)); } size_t @@ -242,7 +249,7 @@ rb_ary_size_as_embedded(VALUE ary) real_size = ary_embed_size(ARY_HEAP_CAPA(ary)); } else { - real_size = sizeof(struct RString); + real_size = sizeof(struct RArray); } return real_size; } @@ -3729,8 +3736,8 @@ rb_ary_bsearch_index(VALUE ary) const VALUE zero = INT2FIX(0); switch (rb_cmpint(rb_funcallv(v, id_cmp, 1, &zero), v, zero)) { case 0: return INT2FIX(mid); - case 1: smaller = 1; break; - case -1: smaller = 0; + case 1: smaller = 0; break; + case -1: smaller = 1; } } else { @@ -202,6 +202,11 @@ static VALUE node_id_for_backtrace_location(rb_execution_context_t *ec, VALUE module, VALUE location) { int node_id; + + if (!rb_frame_info_p(location)) { + rb_raise(rb_eTypeError, "Thread::Backtrace::Location object expected"); + } + node_id = rb_get_node_id_from_frame_info(location); if (node_id == -1) { return Qnil; @@ -20,21 +20,47 @@ module RubyVM::AbstractSyntaxTree # call-seq: - # RubyVM::AbstractSyntaxTree.parse(string) -> RubyVM::AbstractSyntaxTree::Node + # RubyVM::AbstractSyntaxTree.parse(string, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node # # Parses the given _string_ into an abstract syntax tree, # returning the root node of that tree. # - # SyntaxError is raised if the given _string_ is invalid syntax. - # # RubyVM::AbstractSyntaxTree.parse("x = 1 + 2") # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9> + # + # If <tt>keep_script_lines: true</tt> option is provided, the text of the parsed + # source is associated with nodes and is available via Node#script_lines. + # + # If <tt>keep_tokens: true</tt> option is provided, Node#tokens are populated. + # + # SyntaxError is raised if the given _string_ is invalid syntax. To overwrite this + # behavior, <tt>error_tolerant: true</tt> can be provided. In this case, the parser + # will produce a tree where expressions with syntax errors would be represented by + # Node with <tt>type=:ERROR</tt>. + # + # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2") + # # <internal:ast>:33:in `parse': syntax error, unexpected ';', expecting ')' (SyntaxError) + # # x = 1; p(x; y=2 + # # ^ + # + # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2", error_tolerant: true) + # # (SCOPE@1:0-1:15 + # # tbl: [:x, :y] + # # args: nil + # # body: (BLOCK@1:0-1:15 (LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)) (ERROR@1:7-1:11) (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2)))) + # root.children.last.children + # # [(LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)), + # # (ERROR@1:7-1:11), + # # (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))] + # + # Note that parsing continues even after the errored expresion. + # def self.parse string, keep_script_lines: false, error_tolerant: false, keep_tokens: false Primitive.ast_s_parse string, keep_script_lines, error_tolerant, keep_tokens end # call-seq: - # RubyVM::AbstractSyntaxTree.parse_file(pathname) -> RubyVM::AbstractSyntaxTree::Node + # RubyVM::AbstractSyntaxTree.parse_file(pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node # # Reads the file from _pathname_, then parses it like ::parse, # returning the root node of the abstract syntax tree. @@ -44,13 +70,15 @@ module RubyVM::AbstractSyntaxTree # # RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb") # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3> + # + # See ::parse for explanation of keyword argument meaning and usage. def self.parse_file pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false Primitive.ast_s_parse_file pathname, keep_script_lines, error_tolerant, keep_tokens end # call-seq: - # RubyVM::AbstractSyntaxTree.of(proc) -> RubyVM::AbstractSyntaxTree::Node - # RubyVM::AbstractSyntaxTree.of(method) -> RubyVM::AbstractSyntaxTree::Node + # RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node + # RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node # # Returns AST nodes of the given _proc_ or _method_. # @@ -63,6 +91,8 @@ module RubyVM::AbstractSyntaxTree # # RubyVM::AbstractSyntaxTree.of(method(:hello)) # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3> + # + # See ::parse for explanation of keyword argument meaning and usage. def self.of body, keep_script_lines: false, error_tolerant: false, keep_tokens: false Primitive.ast_s_of body, keep_script_lines, error_tolerant, keep_tokens end diff --git a/benchmark/time_parse.yml b/benchmark/time_parse.yml index a6d6948b9c..6060b58bc6 100644 --- a/benchmark/time_parse.yml +++ b/benchmark/time_parse.yml @@ -6,3 +6,5 @@ benchmark: - Time.iso8601(iso8601) - Time.parse(iso8601) - Time.parse(inspect) + - Time.new(iso8601) rescue Time.iso8601(iso8601) + - Time.new(inspect) rescue Time.parse(inspect) @@ -4184,7 +4184,6 @@ rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits, } if (!c || ISSPACE(c)) --str; if (end) len = end - str; - ASSERT_LEN(); } c = *str; c = conv_digit(c); @@ -4587,11 +4586,14 @@ big_shift3(VALUE x, int lshift_p, size_t shift_numdigits, int shift_numbits) if (lshift_p) { if (LONG_MAX < shift_numdigits) { - rb_raise(rb_eArgError, "too big number"); + too_big: + rb_raise(rb_eRangeError, "shift width too big"); } s1 = shift_numdigits; s2 = shift_numbits; + if ((size_t)s1 != shift_numdigits) goto too_big; xn = BIGNUM_LEN(x); + if (LONG_MAX/SIZEOF_BDIGIT <= xn+s1) goto too_big; z = bignew(xn+s1+1, BIGNUM_SIGN(x)); zds = BDIGITS(z); BDIGITS_ZERO(zds, s1); diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index deffa476e5..67e66b03ee 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -480,7 +480,6 @@ assert_equal 'ok', %q{ } # multiple Ractors can receive (wait) from one Ractor -yjit_enabled = ENV.key?('RUBY_YJIT_ENABLE') || ENV.fetch('RUN_OPTS', '').include?('yjit') assert_equal '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]', %q{ pipe = Ractor.new do loop do @@ -503,7 +502,7 @@ assert_equal '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]', %q{ rs.delete r n }.sort -} unless yjit_enabled # flaky with YJIT https://github.com/ruby/ruby/actions/runs/3603398545/jobs/6071549328#step:18:33 +} unless /mswin/ =~ RUBY_PLATFORM # randomly hangs on mswin https://github.com/ruby/ruby/actions/runs/3753871445/jobs/6377551069#step:20:131 # Ractor.select also support multiple take, receive and yield assert_equal '[true, true, true]', %q{ @@ -1474,7 +1473,7 @@ assert_equal "#{N}#{N}", %Q{ } # enc_table -assert_equal "#{N/10}", %Q{ +assert_equal "100", %Q{ Ractor.new do loop do Encoding.find("test-enc-#{rand(5_000)}").inspect @@ -1483,7 +1482,7 @@ assert_equal "#{N/10}", %Q{ end src = Encoding.find("UTF-8") - #{N/10}.times{|i| + 100.times{|i| src.replicate("test-enc-\#{i}") } } @@ -1502,7 +1501,7 @@ assert_equal "#{n}#{n}", %Q{ end end }.map{|r| r.take}.join -} unless yjit_enabled # flaky with YJIT https://github.com/ruby/ruby/actions/runs/3692339025/jobs/6251137785 +} # NameError assert_equal "ok", %q{ @@ -1543,7 +1542,7 @@ assert_equal "ok", %q{ 1_000.times { idle_worker, tmp_reporter = Ractor.select(*workers) } "ok" -} unless yjit_enabled # flaky with YJIT https://github.com/ruby/ruby/actions/runs/3575374374/jobs/6011846425 +} assert_equal "ok", %q{ def foo(*); ->{ super }; end diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 349417595f..5c655b8f25 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -1,3 +1,33 @@ +# Regression test for yielding with autosplat to block with +# optional parameters. https://github.com/Shopify/yjit/issues/313 +assert_equal '[:a, :b, :a, :b]', %q{ + def yielder(arg) = yield(arg) + yield(arg) + + yielder([:a, :b]) do |c = :c, d = :d| + [c, d] + end +} + +# Regression test for GC mishap while doing shape transition +assert_equal '[:ok]', %q{ + # [Bug #19601] + class RegressionTest + def initialize + @a = @b = @fourth_ivar_does_shape_transition = nil + end + + def extender + @first_extended_ivar = [:ok] + end + end + + GC.stress = true + + # Used to crash due to GC run in rb_ensure_iv_list_size() + # not marking the newly allocated [:ok]. + RegressionTest.new.extender.itself +} + assert_equal 'true', %q{ # regression test for tracking type of locals for too long def local_setting_cmp(five) @@ -221,6 +251,8 @@ assert_equal 'string', %q{ # Check that exceptions work when getting global variable assert_equal 'rescued', %q{ + Warning[:deprecated] = true + module Warning def warn(message) raise @@ -1141,6 +1173,38 @@ assert_equal '42', %q{ run } +# splatting an empty array on a specialized method +assert_equal 'ok', %q{ + def run + "ok".to_s(*[]) + end + + run + run +} + +# splatting an single element array on a specialized method +assert_equal '[1]', %q{ + def run + [].<<(*[1]) + end + + run + run +} + +# specialized method with wrong args +assert_equal 'ok', %q{ + def run(x) + "bad".to_s(123) if x + rescue + :ok + end + + run(false) + run(true) +} + # getinstancevariable on Symbol assert_equal '[nil, nil]', %q{ # @foo to exercise the getinstancevariable instruction @@ -2173,7 +2237,7 @@ assert_equal '[[:c_return, :String, :string_alias, "events_to_str"]]', %q{ events.compiled(events) events -} +} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods # test enabling a TracePoint that targets a particular line in a C method call assert_equal '[true]', %q{ @@ -2255,7 +2319,7 @@ assert_equal '[[:c_call, :itself]]', %q{ tp.enable { shouldnt_compile } events -} +} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods # test enabling c_return tracing before compiling assert_equal '[[:c_return, :itself, main]]', %q{ @@ -2270,7 +2334,7 @@ assert_equal '[[:c_return, :itself, main]]', %q{ tp.enable { shouldnt_compile } events -} +} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods # test c_call invalidation assert_equal '[[:c_call, :itself]]', %q{ @@ -3456,3 +3520,11 @@ assert_equal 'ok', %q{ cw(4) } + +assert_normal_exit %{ + class Bug20997 + def foo(&) = self.class.name(&) + + new.foo + end +} @@ -404,6 +404,30 @@ class_init_copy_check(VALUE clone, VALUE orig) } } +struct cvc_table_copy_ctx { + VALUE clone; + struct rb_id_table * new_table; +}; + +static enum rb_id_table_iterator_result +cvc_table_copy(ID id, VALUE val, void *data) { + struct cvc_table_copy_ctx *ctx = (struct cvc_table_copy_ctx *)data; + struct rb_cvar_class_tbl_entry * orig_entry; + orig_entry = (struct rb_cvar_class_tbl_entry *)val; + + struct rb_cvar_class_tbl_entry *ent; + + ent = ALLOC(struct rb_cvar_class_tbl_entry); + ent->class_value = ctx->clone; + ent->cref = orig_entry->cref; + ent->global_cvar_state = orig_entry->global_cvar_state; + rb_id_table_insert(ctx->new_table, id, (VALUE)ent); + + RB_OBJ_WRITTEN(ctx->clone, Qundef, ent->cref); + + return ID_TABLE_CONTINUE; +} + static void copy_tables(VALUE clone, VALUE orig) { @@ -411,6 +435,17 @@ copy_tables(VALUE clone, VALUE orig) rb_free_const_table(RCLASS_CONST_TBL(clone)); RCLASS_CONST_TBL(clone) = 0; } + if (RCLASS_CVC_TBL(orig)) { + struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(orig); + struct rb_id_table *rb_cvc_tbl_dup = rb_id_table_create(rb_id_table_size(rb_cvc_tbl)); + + struct cvc_table_copy_ctx ctx; + ctx.clone = clone; + ctx.new_table = rb_cvc_tbl_dup; + rb_id_table_foreach(rb_cvc_tbl, cvc_table_copy, &ctx); + RCLASS_CVC_TBL(clone) = rb_cvc_tbl_dup; + } + rb_id_table_free(RCLASS_M_TBL(clone)); RCLASS_M_TBL(clone) = 0; if (!RB_TYPE_P(clone, T_ICLASS)) { st_data_t id; @@ -923,7 +958,7 @@ rb_define_class_under(VALUE outer, const char *name, VALUE super) } VALUE -rb_define_class_id_under(VALUE outer, ID id, VALUE super) +rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super) { VALUE klass; @@ -940,8 +975,6 @@ rb_define_class_id_under(VALUE outer, ID id, VALUE super) " (%"PRIsVALUE" is given but was %"PRIsVALUE")", outer, rb_id2str(id), RCLASS_SUPER(klass), super); } - /* Class may have been defined in Ruby and not pin-rooted */ - rb_vm_add_root_module(klass); return klass; } @@ -953,12 +986,19 @@ rb_define_class_id_under(VALUE outer, ID id, VALUE super) rb_set_class_path_string(klass, outer, rb_id2str(id)); rb_const_set(outer, id, klass); rb_class_inherited(super, klass); - rb_vm_add_root_module(klass); return klass; } VALUE +rb_define_class_id_under(VALUE outer, ID id, VALUE super) +{ + VALUE klass = rb_define_class_id_under_no_pin(outer, id, super); + rb_vm_add_root_module(klass); + return klass; +} + +VALUE rb_module_s_alloc(VALUE klass) { VALUE mod = class_alloc(T_MODULE, klass); @@ -1105,8 +1145,8 @@ rb_include_module(VALUE klass, VALUE module) iclass = iclass->next; } - int do_include = 1; while (iclass) { + int do_include = 1; VALUE check_class = iclass->klass; /* During lazy sweeping, iclass->klass could be a dead object that * has not yet been swept. */ @@ -1173,7 +1213,7 @@ static int do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super, bool check_cyclic) { VALUE p, iclass, origin_stack = 0; - int method_changed = 0, add_subclass; + int method_changed = 0; long origin_len; VALUE klass_origin = RCLASS_ORIGIN(klass); VALUE original_klass = klass; @@ -1237,7 +1277,6 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super iclass = rb_include_class_new(module, super_class); c = RCLASS_SET_SUPER(c, iclass); RCLASS_SET_INCLUDER(iclass, klass); - add_subclass = TRUE; if (module != RCLASS_ORIGIN(module)) { if (!origin_stack) origin_stack = rb_ary_hidden_new(2); VALUE origin[2] = {iclass, RCLASS_ORIGIN(module)}; @@ -1248,14 +1287,11 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super RCLASS_SET_ORIGIN(RARRAY_AREF(origin_stack, (origin_len -= 2)), iclass); RICLASS_SET_ORIGIN_SHARED_MTBL(iclass); rb_ary_resize(origin_stack, origin_len); - add_subclass = FALSE; } - if (add_subclass) { - VALUE m = module; - if (BUILTIN_TYPE(m) == T_ICLASS) m = METACLASS_OF(m); - rb_module_add_to_subclasses_list(m, iclass); - } + VALUE m = module; + if (BUILTIN_TYPE(m) == T_ICLASS) m = METACLASS_OF(m); + rb_module_add_to_subclasses_list(m, iclass); if (BUILTIN_TYPE(klass) == T_MODULE && FL_TEST(klass, RMODULE_IS_REFINEMENT)) { VALUE refined_class = @@ -43,7 +43,7 @@ RUN_OPTS = --disable-gems # GITPULLOPTIONS = --no-tags -INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(UNICODE_HDR_DIR) +INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(UNICODE_HDR_DIR) $(incflags) GEM_HOME = GEM_PATH = @@ -235,9 +235,9 @@ 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 +main: $(srcdir)/lib/ruby_vm/mjit/instruction.rb +srcs: $(srcdir)/lib/ruby_vm/mjit/instruction.rb +$(srcdir)/lib/ruby_vm/mjit/instruction.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/views/lib/ruby_vm/mjit/instruction.rb.erb $(srcdir)/insns.def $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@ @@ -640,7 +640,7 @@ clean-local:: clean-runnable $(Q)$(RM) probes.h probes.$(OBJEXT) probes.stamp ruby-glommed.$(OBJEXT) ruby.imp ChangeLog $(STATIC_RUBY)$(EXEEXT) $(Q)$(RM) GNUmakefile.old Makefile.old $(arch)-fake.rb bisect.sh $(ENC_TRANS_D) builtin_binary.inc -$(Q)$(RMALL) yjit/target - -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine 2> $(NULL) || $(NULLCMD) + -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine yjit 2> $(NULL) || $(NULLCMD) bin/clean-runnable:: PHONY $(Q)$(CHDIR) bin 2>$(NULL) && $(RM) $(PROGRAM) $(WPROGRAM) $(GORUBY)$(EXEEXT) bin/*.$(DLEXT) 2>$(NULL) || $(NULLCMD) @@ -766,7 +766,7 @@ clean-spec: PHONY -$(Q) $(RMDIRS) $(RUBYSPEC_CAPIEXT) 2> $(NULL) || $(NULLCMD) -$(Q) $(RMALL) rubyspec_temp -check: main $(DOT_WAIT) test $(DOT_WAIT) test-tool $(DOT_WAIT) test-all $(DOT_WAIT) test-spec +check: main $(DOT_WAIT) test $(DOT_WAIT) test-tool $(DOT_WAIT) test-all $(ECHO) check succeeded -$(Q) : : "run only on sh"; \ if [ x"$(GIT)" != x ] && $(CHDIR) "$(srcdir)" && \ @@ -790,10 +790,11 @@ $(arch:noarch=ignore)-fake.rb: $(srcdir)/template/fake.rb.in $(tooldir)/generic_ $(ECHO) generating $@ $(Q) $(CPP) -DRUBY_EXPORT $(INCFLAGS) $(CPPFLAGS) "$(srcdir)/version.c" | \ $(BOOTSTRAPRUBY) "$(tooldir)/generic_erb.rb" -o $@ "$(srcdir)/template/fake.rb.in" \ - i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)" + i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)" \ + LIBPATHENV="$(LIBPATHENV)" PRELOADENV="$(PRELOADENV)" LIBRUBY_SO="$(LIBRUBY_SO)" noarch-fake.rb: # prerequisite of yes-fake - touch $@ + $(Q) exit > $@ btest: $(TEST_RUNNABLE)-btest no-btest: PHONY @@ -902,6 +903,8 @@ yes-test-spec: test-spec-precheck $(ACTIONS_ENDGROUP) no-test-spec: +check: $(DOT_WAIT) test-spec + RUNNABLE = $(LIBRUBY_RELATIVE:no=un)-runnable runnable: $(RUNNABLE) prog $(tooldir)/mkrunnable.rb PHONY $(Q) $(MINIRUBY) $(tooldir)/mkrunnable.rb -v $(EXTOUT) @@ -1221,7 +1224,7 @@ $(srcdir)/revision.h$(no_baseruby:no=~disabled~): $(REVISION_H) $(REVISION_H)$(no_baseruby:no=~disabled~): $(Q) $(BASERUBY) $(tooldir)/file2lastrev.rb -q --revision.h --srcdir="$(srcdir)" --output=revision.h --timestamp=$@ $(REVISION_H)$(yes_baseruby:yes=~disabled~): - $(Q) touch $@ + $(Q) exit > $@ $(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y $(srcdir)/defs/id.def $(srcdir)/ext/ripper/depend $(ECHO) generating $@ @@ -1357,7 +1360,7 @@ after-update:: extract-gems update-src:: $(Q) $(RM) $(REVISION_H) revision.h "$(srcdir)/$(REVISION_H)" "$(srcdir)/revision.h" - $(Q) touch "$(srcdir)/revision.h" + $(Q) exit > "$(srcdir)/revision.h" update-remote:: update-src update-download update-download:: $(ALWAYS_UPDATE_UNICODE:yes=update-unicode) @@ -1373,7 +1376,6 @@ update-config_files: PHONY refresh-gems: update-bundled_gems prepare-gems prepare-gems: $(HAVE_BASERUBY:yes=update-gems) $(HAVE_BASERUBY:yes=extract-gems) -prepare-gems: $(DOT_WAIT) $(HAVE_BASERUBY:yes=outdate-bundled-gems) extract-gems: $(HAVE_BASERUBY:yes=update-gems) update-gems$(gnumake:yes=-sequential): PHONY @@ -1435,7 +1437,7 @@ no-test-bundled-gems-prepare: no-test-bundled-gems-precheck yes-test-bundled-gems-prepare: yes-test-bundled-gems-precheck $(ACTIONS_GROUP) $(XRUBY) -C "$(srcdir)" bin/gem install --no-document \ - --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "yard" "pry" "packnga" "rexml" "json-schema" "test-unit-rr" + --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "rexml" "json-schema:5.1.0" "test-unit-rr" $(ACTIONS_ENDGROUP) PREPARE_BUNDLED_GEMS = test-bundled-gems-prepare @@ -1454,6 +1456,7 @@ test-syntax-suggest-precheck: $(TEST_RUNNABLE)-test-syntax-suggest-precheck no-test-syntax-suggest-precheck: yes-test-syntax-suggest-precheck: main +test-syntax-suggest-prepare: $(TEST_RUNNABLE)-test-syntax-suggest-prepare no-test-syntax-suggest-prepare: no-test-syntax-suggest-precheck yes-test-syntax-suggest-prepare: yes-test-syntax-suggest-precheck $(ACTIONS_GROUP) @@ -1463,12 +1466,15 @@ yes-test-syntax-suggest-prepare: yes-test-syntax-suggest-precheck RSPECOPTS = SYNTAX_SUGGEST_SPECS = +PREPARE_SYNTAX_SUGGEST = test-syntax-suggest-prepare test-syntax-suggest: $(TEST_RUNNABLE)-test-syntax-suggest -yes-test-syntax-suggest: yes-test-syntax-suggest-prepare +yes-test-syntax-suggest: yes-$(PREPARE_SYNTAX_SUGGEST) $(XRUBY) -C $(srcdir) -Ispec/syntax_suggest .bundle/bin/rspec \ --require spec_helper $(RSPECOPTS) spec/syntax_suggest/$(SYNTAX_SUGGEST_SPECS) no-test-syntax-suggest: +check: $(DOT_WAIT) $(TEST_RUNNABLE)-$(PREPARE_SYNTAX_SUGGEST) test-syntax-suggest + test-bundler-precheck: $(TEST_RUNNABLE)-test-bundler-precheck no-test-bundler-precheck: yes-test-bundler-precheck: main $(arch)-fake.rb @@ -1614,7 +1620,7 @@ $(UNICODE_SRC_DATA_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=.unicode-tables.time): \ touch-unicode-files: $(MAKEDIRS) $(UNICODE_SRC_DATA_DIR) - touch $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time $(UNICODE_DATA_HEADERS) + $(Q) $(TOUCH) $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time $(UNICODE_DATA_HEADERS) UNICODE_TABLES_DATA_FILES = \ $(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \ @@ -1626,7 +1632,7 @@ UNICODE_TABLES_DEPENDENTS = $(UNICODE_TABLES_DEPENDENTS_1:noneyes=force) UNICODE_TABLES_TIMESTAMP = yes $(UNICODE_SRC_DATA_DIR)/.unicode-tables.$(UNICODE_TABLES_DEPENDENTS:none=time): $(Q) $(MAKEDIRS) $(@D) - touch $(@) || $(NULLCMD) + $(Q) exit > $(@) || $(NULLCMD) $(UNICODE_SRC_DATA_DIR)/.unicode-tables.$(UNICODE_TABLES_DEPENDENTS:force=time): \ $(tooldir)/generic_erb.rb \ $(srcdir)/template/unicode_norm_gen.tmpl \ @@ -3215,8 +3221,13 @@ compile.$(OBJEXT): {$(VPATH)}vm_callinfo.h compile.$(OBJEXT): {$(VPATH)}vm_core.h compile.$(OBJEXT): {$(VPATH)}vm_debug.h compile.$(OBJEXT): {$(VPATH)}vm_opts.h +complex.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +complex.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +complex.$(OBJEXT): $(CCAN_DIR)/list/list.h +complex.$(OBJEXT): $(CCAN_DIR)/str/str.h complex.$(OBJEXT): $(hdrdir)/ruby/ruby.h complex.$(OBJEXT): $(top_srcdir)/internal/array.h +complex.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h complex.$(OBJEXT): $(top_srcdir)/internal/bignum.h complex.$(OBJEXT): $(top_srcdir)/internal/bits.h complex.$(OBJEXT): $(top_srcdir)/internal/class.h @@ -3224,6 +3235,7 @@ complex.$(OBJEXT): $(top_srcdir)/internal/compilers.h complex.$(OBJEXT): $(top_srcdir)/internal/complex.h complex.$(OBJEXT): $(top_srcdir)/internal/fixnum.h complex.$(OBJEXT): $(top_srcdir)/internal/gc.h +complex.$(OBJEXT): $(top_srcdir)/internal/imemo.h complex.$(OBJEXT): $(top_srcdir)/internal/math.h complex.$(OBJEXT): $(top_srcdir)/internal/numeric.h complex.$(OBJEXT): $(top_srcdir)/internal/object.h @@ -3234,6 +3246,7 @@ complex.$(OBJEXT): $(top_srcdir)/internal/variable.h complex.$(OBJEXT): $(top_srcdir)/internal/vm.h complex.$(OBJEXT): $(top_srcdir)/internal/warnings.h complex.$(OBJEXT): {$(VPATH)}assert.h +complex.$(OBJEXT): {$(VPATH)}atomic.h complex.$(OBJEXT): {$(VPATH)}backward/2/assume.h complex.$(OBJEXT): {$(VPATH)}backward/2/attributes.h complex.$(OBJEXT): {$(VPATH)}backward/2/bool.h @@ -3391,11 +3404,18 @@ complex.$(OBJEXT): {$(VPATH)}internal/value_type.h complex.$(OBJEXT): {$(VPATH)}internal/variable.h complex.$(OBJEXT): {$(VPATH)}internal/warning_push.h complex.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +complex.$(OBJEXT): {$(VPATH)}method.h complex.$(OBJEXT): {$(VPATH)}missing.h +complex.$(OBJEXT): {$(VPATH)}node.h complex.$(OBJEXT): {$(VPATH)}ruby_assert.h +complex.$(OBJEXT): {$(VPATH)}ruby_atomic.h complex.$(OBJEXT): {$(VPATH)}shape.h complex.$(OBJEXT): {$(VPATH)}st.h complex.$(OBJEXT): {$(VPATH)}subst.h +complex.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h +complex.$(OBJEXT): {$(VPATH)}thread_native.h +complex.$(OBJEXT): {$(VPATH)}vm_core.h +complex.$(OBJEXT): {$(VPATH)}vm_opts.h cont.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h cont.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h cont.$(OBJEXT): $(CCAN_DIR)/list/list.h @@ -3406,12 +3426,14 @@ cont.$(OBJEXT): $(top_srcdir)/internal/array.h cont.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h cont.$(OBJEXT): $(top_srcdir)/internal/compilers.h cont.$(OBJEXT): $(top_srcdir)/internal/cont.h +cont.$(OBJEXT): $(top_srcdir)/internal/error.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/string.h cont.$(OBJEXT): $(top_srcdir)/internal/variable.h cont.$(OBJEXT): $(top_srcdir)/internal/vm.h cont.$(OBJEXT): $(top_srcdir)/internal/warnings.h @@ -3432,6 +3454,7 @@ cont.$(OBJEXT): {$(VPATH)}constant.h cont.$(OBJEXT): {$(VPATH)}cont.c cont.$(OBJEXT): {$(VPATH)}debug_counter.h cont.$(OBJEXT): {$(VPATH)}defines.h +cont.$(OBJEXT): {$(VPATH)}encoding.h cont.$(OBJEXT): {$(VPATH)}eval_intern.h cont.$(OBJEXT): {$(VPATH)}fiber/scheduler.h cont.$(OBJEXT): {$(VPATH)}gc.h @@ -3509,6 +3532,15 @@ cont.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h cont.$(OBJEXT): {$(VPATH)}internal/ctype.h cont.$(OBJEXT): {$(VPATH)}internal/dllexport.h cont.$(OBJEXT): {$(VPATH)}internal/dosish.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/re.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/string.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h +cont.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h cont.$(OBJEXT): {$(VPATH)}internal/error.h cont.$(OBJEXT): {$(VPATH)}internal/eval.h cont.$(OBJEXT): {$(VPATH)}internal/event.h @@ -3584,6 +3616,8 @@ cont.$(OBJEXT): {$(VPATH)}method.h cont.$(OBJEXT): {$(VPATH)}missing.h cont.$(OBJEXT): {$(VPATH)}mjit.h cont.$(OBJEXT): {$(VPATH)}node.h +cont.$(OBJEXT): {$(VPATH)}onigmo.h +cont.$(OBJEXT): {$(VPATH)}oniguruma.h cont.$(OBJEXT): {$(VPATH)}ractor.h cont.$(OBJEXT): {$(VPATH)}ractor_core.h cont.$(OBJEXT): {$(VPATH)}ruby_assert.h @@ -5870,10 +5904,16 @@ enum.$(OBJEXT): {$(VPATH)}st.h enum.$(OBJEXT): {$(VPATH)}subst.h enum.$(OBJEXT): {$(VPATH)}symbol.h enum.$(OBJEXT): {$(VPATH)}util.h +enumerator.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +enumerator.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +enumerator.$(OBJEXT): $(CCAN_DIR)/list/list.h +enumerator.$(OBJEXT): $(CCAN_DIR)/str/str.h enumerator.$(OBJEXT): $(hdrdir)/ruby/ruby.h enumerator.$(OBJEXT): $(top_srcdir)/internal/array.h +enumerator.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h enumerator.$(OBJEXT): $(top_srcdir)/internal/bignum.h enumerator.$(OBJEXT): $(top_srcdir)/internal/bits.h +enumerator.$(OBJEXT): $(top_srcdir)/internal/class.h enumerator.$(OBJEXT): $(top_srcdir)/internal/compilers.h enumerator.$(OBJEXT): $(top_srcdir)/internal/enumerator.h enumerator.$(OBJEXT): $(top_srcdir)/internal/error.h @@ -5891,6 +5931,7 @@ enumerator.$(OBJEXT): $(top_srcdir)/internal/struct.h enumerator.$(OBJEXT): $(top_srcdir)/internal/vm.h enumerator.$(OBJEXT): $(top_srcdir)/internal/warnings.h enumerator.$(OBJEXT): {$(VPATH)}assert.h +enumerator.$(OBJEXT): {$(VPATH)}atomic.h enumerator.$(OBJEXT): {$(VPATH)}backward/2/assume.h enumerator.$(OBJEXT): {$(VPATH)}backward/2/attributes.h enumerator.$(OBJEXT): {$(VPATH)}backward/2/bool.h @@ -5905,6 +5946,7 @@ enumerator.$(OBJEXT): {$(VPATH)}defines.h enumerator.$(OBJEXT): {$(VPATH)}encoding.h enumerator.$(OBJEXT): {$(VPATH)}enumerator.c enumerator.$(OBJEXT): {$(VPATH)}id.h +enumerator.$(OBJEXT): {$(VPATH)}id_table.h enumerator.$(OBJEXT): {$(VPATH)}intern.h enumerator.$(OBJEXT): {$(VPATH)}internal.h enumerator.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -6056,13 +6098,20 @@ enumerator.$(OBJEXT): {$(VPATH)}internal/value_type.h enumerator.$(OBJEXT): {$(VPATH)}internal/variable.h enumerator.$(OBJEXT): {$(VPATH)}internal/warning_push.h enumerator.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +enumerator.$(OBJEXT): {$(VPATH)}method.h enumerator.$(OBJEXT): {$(VPATH)}missing.h +enumerator.$(OBJEXT): {$(VPATH)}node.h enumerator.$(OBJEXT): {$(VPATH)}onigmo.h enumerator.$(OBJEXT): {$(VPATH)}oniguruma.h enumerator.$(OBJEXT): {$(VPATH)}ruby_assert.h +enumerator.$(OBJEXT): {$(VPATH)}ruby_atomic.h enumerator.$(OBJEXT): {$(VPATH)}shape.h enumerator.$(OBJEXT): {$(VPATH)}st.h enumerator.$(OBJEXT): {$(VPATH)}subst.h +enumerator.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h +enumerator.$(OBJEXT): {$(VPATH)}thread_native.h +enumerator.$(OBJEXT): {$(VPATH)}vm_core.h +enumerator.$(OBJEXT): {$(VPATH)}vm_opts.h error.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h error.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h error.$(OBJEXT): $(CCAN_DIR)/list/list.h @@ -7031,6 +7080,7 @@ goruby.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h goruby.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h goruby.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h goruby.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +goruby.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h goruby.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h goruby.$(OBJEXT): {$(VPATH)}internal/attr/pure.h goruby.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h @@ -8534,7 +8584,6 @@ loadpath.$(OBJEXT): {$(VPATH)}internal/warning_push.h loadpath.$(OBJEXT): {$(VPATH)}internal/xmalloc.h loadpath.$(OBJEXT): {$(VPATH)}loadpath.c loadpath.$(OBJEXT): {$(VPATH)}missing.h -loadpath.$(OBJEXT): {$(VPATH)}revision.h loadpath.$(OBJEXT): {$(VPATH)}st.h loadpath.$(OBJEXT): {$(VPATH)}subst.h loadpath.$(OBJEXT): {$(VPATH)}verconf.h @@ -8869,8 +8918,13 @@ main.$(OBJEXT): {$(VPATH)}missing.h main.$(OBJEXT): {$(VPATH)}st.h main.$(OBJEXT): {$(VPATH)}subst.h main.$(OBJEXT): {$(VPATH)}vm_debug.h +marshal.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +marshal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +marshal.$(OBJEXT): $(CCAN_DIR)/list/list.h +marshal.$(OBJEXT): $(CCAN_DIR)/str/str.h marshal.$(OBJEXT): $(hdrdir)/ruby/ruby.h marshal.$(OBJEXT): $(top_srcdir)/internal/array.h +marshal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h marshal.$(OBJEXT): $(top_srcdir)/internal/bignum.h marshal.$(OBJEXT): $(top_srcdir)/internal/bits.h marshal.$(OBJEXT): $(top_srcdir)/internal/class.h @@ -8880,6 +8934,7 @@ marshal.$(OBJEXT): $(top_srcdir)/internal/error.h marshal.$(OBJEXT): $(top_srcdir)/internal/fixnum.h marshal.$(OBJEXT): $(top_srcdir)/internal/gc.h marshal.$(OBJEXT): $(top_srcdir)/internal/hash.h +marshal.$(OBJEXT): $(top_srcdir)/internal/imemo.h marshal.$(OBJEXT): $(top_srcdir)/internal/numeric.h marshal.$(OBJEXT): $(top_srcdir)/internal/object.h marshal.$(OBJEXT): $(top_srcdir)/internal/serial.h @@ -8892,6 +8947,7 @@ marshal.$(OBJEXT): $(top_srcdir)/internal/variable.h marshal.$(OBJEXT): $(top_srcdir)/internal/vm.h marshal.$(OBJEXT): $(top_srcdir)/internal/warnings.h marshal.$(OBJEXT): {$(VPATH)}assert.h +marshal.$(OBJEXT): {$(VPATH)}atomic.h marshal.$(OBJEXT): {$(VPATH)}backward/2/assume.h marshal.$(OBJEXT): {$(VPATH)}backward/2/attributes.h marshal.$(OBJEXT): {$(VPATH)}backward/2/bool.h @@ -8907,6 +8963,7 @@ marshal.$(OBJEXT): {$(VPATH)}constant.h marshal.$(OBJEXT): {$(VPATH)}defines.h marshal.$(OBJEXT): {$(VPATH)}encindex.h marshal.$(OBJEXT): {$(VPATH)}encoding.h +marshal.$(OBJEXT): {$(VPATH)}id.h marshal.$(OBJEXT): {$(VPATH)}id_table.h marshal.$(OBJEXT): {$(VPATH)}intern.h marshal.$(OBJEXT): {$(VPATH)}internal.h @@ -8947,6 +9004,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h @@ -9062,13 +9120,21 @@ marshal.$(OBJEXT): {$(VPATH)}internal/xmalloc.h marshal.$(OBJEXT): {$(VPATH)}io.h marshal.$(OBJEXT): {$(VPATH)}marshal.c marshal.$(OBJEXT): {$(VPATH)}marshal.rbinc +marshal.$(OBJEXT): {$(VPATH)}method.h marshal.$(OBJEXT): {$(VPATH)}missing.h +marshal.$(OBJEXT): {$(VPATH)}node.h marshal.$(OBJEXT): {$(VPATH)}onigmo.h marshal.$(OBJEXT): {$(VPATH)}oniguruma.h +marshal.$(OBJEXT): {$(VPATH)}ruby_assert.h +marshal.$(OBJEXT): {$(VPATH)}ruby_atomic.h marshal.$(OBJEXT): {$(VPATH)}shape.h marshal.$(OBJEXT): {$(VPATH)}st.h marshal.$(OBJEXT): {$(VPATH)}subst.h +marshal.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h +marshal.$(OBJEXT): {$(VPATH)}thread_native.h marshal.$(OBJEXT): {$(VPATH)}util.h +marshal.$(OBJEXT): {$(VPATH)}vm_core.h +marshal.$(OBJEXT): {$(VPATH)}vm_opts.h math.$(OBJEXT): $(hdrdir)/ruby/ruby.h math.$(OBJEXT): $(top_srcdir)/internal/bignum.h math.$(OBJEXT): $(top_srcdir)/internal/class.h @@ -9244,12 +9310,16 @@ math.$(OBJEXT): {$(VPATH)}shape.h math.$(OBJEXT): {$(VPATH)}st.h math.$(OBJEXT): {$(VPATH)}subst.h memory_view.$(OBJEXT): $(hdrdir)/ruby/ruby.h +memory_view.$(OBJEXT): $(top_srcdir)/internal/compilers.h +memory_view.$(OBJEXT): $(top_srcdir)/internal/gc.h memory_view.$(OBJEXT): $(top_srcdir)/internal/hash.h memory_view.$(OBJEXT): $(top_srcdir)/internal/variable.h +memory_view.$(OBJEXT): $(top_srcdir)/internal/warnings.h memory_view.$(OBJEXT): {$(VPATH)}assert.h memory_view.$(OBJEXT): {$(VPATH)}backward/2/assume.h memory_view.$(OBJEXT): {$(VPATH)}backward/2/attributes.h memory_view.$(OBJEXT): {$(VPATH)}backward/2/bool.h +memory_view.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h memory_view.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h memory_view.$(OBJEXT): {$(VPATH)}backward/2/limits.h memory_view.$(OBJEXT): {$(VPATH)}backward/2/long_long.h @@ -9489,6 +9559,7 @@ miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/pure.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h @@ -10456,8 +10527,13 @@ numeric.$(OBJEXT): {$(VPATH)}shape.h numeric.$(OBJEXT): {$(VPATH)}st.h numeric.$(OBJEXT): {$(VPATH)}subst.h numeric.$(OBJEXT): {$(VPATH)}util.h +object.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +object.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +object.$(OBJEXT): $(CCAN_DIR)/list/list.h +object.$(OBJEXT): $(CCAN_DIR)/str/str.h object.$(OBJEXT): $(hdrdir)/ruby/ruby.h object.$(OBJEXT): $(top_srcdir)/internal/array.h +object.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h object.$(OBJEXT): $(top_srcdir)/internal/bignum.h object.$(OBJEXT): $(top_srcdir)/internal/bits.h object.$(OBJEXT): $(top_srcdir)/internal/class.h @@ -10466,6 +10542,7 @@ object.$(OBJEXT): $(top_srcdir)/internal/error.h object.$(OBJEXT): $(top_srcdir)/internal/eval.h object.$(OBJEXT): $(top_srcdir)/internal/fixnum.h object.$(OBJEXT): $(top_srcdir)/internal/gc.h +object.$(OBJEXT): $(top_srcdir)/internal/imemo.h object.$(OBJEXT): $(top_srcdir)/internal/inits.h object.$(OBJEXT): $(top_srcdir)/internal/numeric.h object.$(OBJEXT): $(top_srcdir)/internal/object.h @@ -10478,6 +10555,7 @@ object.$(OBJEXT): $(top_srcdir)/internal/variable.h object.$(OBJEXT): $(top_srcdir)/internal/vm.h object.$(OBJEXT): $(top_srcdir)/internal/warnings.h object.$(OBJEXT): {$(VPATH)}assert.h +object.$(OBJEXT): {$(VPATH)}atomic.h object.$(OBJEXT): {$(VPATH)}backward/2/assume.h object.$(OBJEXT): {$(VPATH)}backward/2/attributes.h object.$(OBJEXT): {$(VPATH)}backward/2/bool.h @@ -10646,22 +10724,31 @@ object.$(OBJEXT): {$(VPATH)}internal/variable.h object.$(OBJEXT): {$(VPATH)}internal/warning_push.h object.$(OBJEXT): {$(VPATH)}internal/xmalloc.h object.$(OBJEXT): {$(VPATH)}kernel.rbinc +object.$(OBJEXT): {$(VPATH)}method.h object.$(OBJEXT): {$(VPATH)}missing.h object.$(OBJEXT): {$(VPATH)}nilclass.rbinc +object.$(OBJEXT): {$(VPATH)}node.h object.$(OBJEXT): {$(VPATH)}object.c object.$(OBJEXT): {$(VPATH)}onigmo.h object.$(OBJEXT): {$(VPATH)}oniguruma.h object.$(OBJEXT): {$(VPATH)}probes.dmyh object.$(OBJEXT): {$(VPATH)}probes.h +object.$(OBJEXT): {$(VPATH)}ruby_assert.h +object.$(OBJEXT): {$(VPATH)}ruby_atomic.h object.$(OBJEXT): {$(VPATH)}shape.h object.$(OBJEXT): {$(VPATH)}st.h object.$(OBJEXT): {$(VPATH)}subst.h +object.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h +object.$(OBJEXT): {$(VPATH)}thread_native.h object.$(OBJEXT): {$(VPATH)}util.h object.$(OBJEXT): {$(VPATH)}variable.h +object.$(OBJEXT): {$(VPATH)}vm_core.h +object.$(OBJEXT): {$(VPATH)}vm_opts.h pack.$(OBJEXT): $(hdrdir)/ruby/ruby.h pack.$(OBJEXT): $(top_srcdir)/internal/array.h pack.$(OBJEXT): $(top_srcdir)/internal/bits.h pack.$(OBJEXT): $(top_srcdir)/internal/compilers.h +pack.$(OBJEXT): $(top_srcdir)/internal/gc.h pack.$(OBJEXT): $(top_srcdir)/internal/static_assert.h pack.$(OBJEXT): $(top_srcdir)/internal/string.h pack.$(OBJEXT): $(top_srcdir)/internal/symbol.h @@ -11070,6 +11157,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/compilers.h proc.$(OBJEXT): $(top_srcdir)/internal/error.h proc.$(OBJEXT): $(top_srcdir)/internal/eval.h proc.$(OBJEXT): $(top_srcdir)/internal/gc.h +proc.$(OBJEXT): $(top_srcdir)/internal/hash.h proc.$(OBJEXT): $(top_srcdir)/internal/imemo.h proc.$(OBJEXT): $(top_srcdir)/internal/object.h proc.$(OBJEXT): $(top_srcdir)/internal/proc.h @@ -11493,6 +11581,7 @@ ractor.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h ractor.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h ractor.$(OBJEXT): $(CCAN_DIR)/list/list.h ractor.$(OBJEXT): $(CCAN_DIR)/str/str.h +ractor.$(OBJEXT): $(hdrdir)/ruby.h ractor.$(OBJEXT): $(hdrdir)/ruby/ruby.h ractor.$(OBJEXT): $(top_srcdir)/internal/array.h ractor.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h @@ -11688,6 +11777,7 @@ ractor.$(OBJEXT): {$(VPATH)}internal/warning_push.h ractor.$(OBJEXT): {$(VPATH)}internal/xmalloc.h ractor.$(OBJEXT): {$(VPATH)}method.h ractor.$(OBJEXT): {$(VPATH)}missing.h +ractor.$(OBJEXT): {$(VPATH)}mjit.h ractor.$(OBJEXT): {$(VPATH)}node.h ractor.$(OBJEXT): {$(VPATH)}onigmo.h ractor.$(OBJEXT): {$(VPATH)}oniguruma.h @@ -11716,6 +11806,7 @@ random.$(OBJEXT): $(top_srcdir)/internal/bignum.h random.$(OBJEXT): $(top_srcdir)/internal/bits.h random.$(OBJEXT): $(top_srcdir)/internal/compilers.h random.$(OBJEXT): $(top_srcdir)/internal/fixnum.h +random.$(OBJEXT): $(top_srcdir)/internal/gc.h random.$(OBJEXT): $(top_srcdir)/internal/numeric.h random.$(OBJEXT): $(top_srcdir)/internal/random.h random.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h @@ -14316,6 +14407,7 @@ signal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h signal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +signal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h signal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h signal.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h @@ -14806,6 +14898,7 @@ st.$(OBJEXT): {$(VPATH)}internal/variable.h st.$(OBJEXT): {$(VPATH)}internal/warning_push.h st.$(OBJEXT): {$(VPATH)}internal/xmalloc.h st.$(OBJEXT): {$(VPATH)}missing.h +st.$(OBJEXT): {$(VPATH)}ruby_assert.h st.$(OBJEXT): {$(VPATH)}st.c st.$(OBJEXT): {$(VPATH)}st.h st.$(OBJEXT): {$(VPATH)}subst.h @@ -15071,6 +15164,7 @@ string.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h string.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h string.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h string.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +string.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h string.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h string.$(OBJEXT): {$(VPATH)}internal/attr/pure.h string.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h @@ -15512,6 +15606,7 @@ symbol.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/pure.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h @@ -1243,6 +1243,32 @@ new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line) return adjust; } +static void +iseq_insn_each_markable_object(INSN *insn, void (*func)(VALUE *, VALUE), VALUE data) +{ + const char *types = insn_op_types(insn->insn_id); + for (int j = 0; types[j]; j++) { + char type = types[j]; + switch (type) { + case TS_CDHASH: + case TS_ISEQ: + case TS_VALUE: + case TS_IC: // constant path array + case TS_CALLDATA: // ci is stored. + func(&OPERAND_AT(insn, j), data); + break; + default: + break; + } + } +} + +static void +iseq_insn_each_object_write_barrier(VALUE *obj_ptr, VALUE iseq) +{ + RB_OBJ_WRITTEN(iseq, Qundef, *obj_ptr); +} + static INSN * new_insn_core(rb_iseq_t *iseq, const NODE *line_node, int insn_id, int argc, VALUE *argv) @@ -1260,6 +1286,9 @@ new_insn_core(rb_iseq_t *iseq, const NODE *line_node, iobj->operands = argv; iobj->operand_size = argc; iobj->sc_state = 0; + + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq); + return iobj; } @@ -1832,6 +1861,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG); + body->param.flags.ruby2_keywords = args->ruby2_keywords; body->param.lead_num = arg_size = (int)args->pre_args_num; if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE; debugs(" - argc: %d\n", body->param.lead_num); @@ -2630,9 +2660,11 @@ iseq_set_exception_table(rb_iseq_t *iseq) struct iseq_catch_table_entry *entry; ISEQ_BODY(iseq)->catch_table = NULL; - if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) return COMPILE_OK; - tlen = (int)RARRAY_LEN(ISEQ_COMPILE_DATA(iseq)->catch_table_ary); - tptr = RARRAY_CONST_PTR_TRANSIENT(ISEQ_COMPILE_DATA(iseq)->catch_table_ary); + + VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary; + if (NIL_P(catch_table_ary)) return COMPILE_OK; + tlen = (int)RARRAY_LEN(catch_table_ary); + tptr = RARRAY_CONST_PTR_TRANSIENT(catch_table_ary); if (tlen > 0) { struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen)); @@ -2668,6 +2700,8 @@ iseq_set_exception_table(rb_iseq_t *iseq) RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */ } + RB_GC_GUARD(catch_table_ary); + return COMPILE_OK; } @@ -2806,7 +2840,6 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i) break; } else if ((lab = find_destination((INSN *)i)) != 0) { - if (lab->unremovable) break; unref_counts[lab->label_no]++; } } @@ -2823,8 +2856,7 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i) /* do nothing */ } else if (IS_ADJUST(i)) { - LABEL *dest = ((ADJUST *)i)->label; - if (dest && dest->unremovable) return 0; + return 0; } end = i; } while ((i = i->next) != 0); @@ -4588,7 +4620,12 @@ compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node) } else { CHECK(COMPILE_(ret, "array element", node, FALSE)); - ADD_INSN1(ret, node, newarray, INT2FIX(1)); + if (keyword_node_p(node)) { + ADD_INSN1(ret, node, newarraykwsplat, INT2FIX(1)); + } + else { + ADD_INSN1(ret, node, newarray, INT2FIX(1)); + } } return 1; @@ -5039,12 +5076,17 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node)); + bool safenav_call = false; LINK_ELEMENT *insn_element = LAST_ELEMENT(pre); iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */ ASSUME(iobj); - ELEM_REMOVE(LAST_ELEMENT(pre)); - ELEM_REMOVE((LINK_ELEMENT *)iobj); - pre->last = iobj->link.prev; + ELEM_REMOVE(insn_element); + if (!IS_INSN_ID(iobj, send)) { + safenav_call = true; + iobj = (INSN *)get_prev_insn(iobj); + ELEM_INSERT_NEXT(&iobj->link, insn_element); + } + (pre->last = iobj->link.prev)->next = 0; const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0); int argc = vm_ci_argc(ci) + 1; @@ -5063,7 +5105,9 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const return COMPILE_NG; } - ADD_ELEM(lhs, (LINK_ELEMENT *)iobj); + iobj->link.prev = lhs->last; + lhs->last->next = &iobj->link; + for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next); if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) { int argc = vm_ci_argc(ci); ci = ci_argc_set(iseq, ci, argc - 1); @@ -5072,9 +5116,11 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1)); INSERT_BEFORE_INSN(iobj, line_node, concatarray); } - ADD_INSN(lhs, line_node, pop); - if (argc != 1) { + if (!safenav_call) { ADD_INSN(lhs, line_node, pop); + if (argc != 1) { + ADD_INSN(lhs, line_node, pop); + } } for (int i=0; i < argc; i++) { ADD_INSN(post, line_node, pop); @@ -7611,7 +7657,6 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in add_ensure_iseq(ret, iseq, 0); ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label); ADD_ADJUST_RESTORE(ret, splabel); - splabel->unremovable = FALSE; if (!popped) { ADD_INSN(ret, line_node, putnil); @@ -9276,7 +9321,8 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node /* optimization shortcut * obj["literal"] = value -> opt_aset_with(obj, "literal", value) */ - if (mid == idASET && !private_recv_p(node) && node->nd_args && + if (!ISEQ_COMPILE_DATA(iseq)->in_masgn && + mid == idASET && !private_recv_p(node) && node->nd_args && nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 2 && nd_type_p(node->nd_args->nd_head, NODE_STR) && ISEQ_COMPILE_DATA(iseq)->current_block == NULL && @@ -9474,7 +9520,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_MASGN:{ + bool prev_in_masgn = ISEQ_COMPILE_DATA(iseq)->in_masgn; + ISEQ_COMPILE_DATA(iseq)->in_masgn = true; compile_massign(iseq, ret, node, popped); + ISEQ_COMPILE_DATA(iseq)->in_masgn = prev_in_masgn; break; } @@ -9716,7 +9765,12 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no case NODE_LIT:{ debugp_param("lit", node->nd_lit); if (!popped) { - ADD_INSN1(ret, node, putobject, node->nd_lit); + if (UNLIKELY(node->nd_lit == rb_mRubyVMFrozenCore)) { + ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); // [Bug #20569] + } + else { + ADD_INSN1(ret, node, putobject, node->nd_lit); + } RB_OBJ_WRITTEN(iseq, Qundef, node->nd_lit); } break; @@ -10734,6 +10788,12 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords) return keyword; } +static void +iseq_insn_each_object_mark(VALUE *obj_ptr, VALUE _) +{ + rb_gc_mark(*obj_ptr); +} + void rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage) { @@ -10760,29 +10820,7 @@ rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage) iobj = (INSN *)&storage->buff[pos]; if (iobj->operands) { - int j; - const char *types = insn_op_types(iobj->insn_id); - - for (j = 0; types[j]; j++) { - char type = types[j]; - switch (type) { - case TS_CDHASH: - case TS_ISEQ: - case TS_VALUE: - case TS_IC: // constant path array - case TS_CALLDATA: // ci is stored. - { - VALUE op = OPERAND_AT(iobj, j); - - if (!SPECIAL_CONST_P(op)) { - rb_gc_mark(op); - } - } - break; - default: - break; - } - } + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark, (VALUE)0); } pos += (int)size; } @@ -12310,7 +12348,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) verify_call_cache(iseq); RB_GC_GUARD(dummy_frame); - rb_vm_pop_frame(ec); + rb_vm_pop_frame_no_int(ec); } struct ibf_dump_iseq_list_arg @@ -13061,7 +13099,7 @@ ibf_dump_memsize(const void *ptr) static const rb_data_type_t ibf_dump_type = { "ibf_dump", {ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,}, - 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; static void @@ -2116,8 +2116,11 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise) return a1; /* should raise exception for consistency */ if (!k_numeric_p(a1)) { - if (!raise) - return rb_protect(to_complex, a1, NULL); + if (!raise) { + a1 = rb_protect(to_complex, a1, NULL); + rb_set_errinfo(Qnil); + return a1; + } return to_complex(a1); } } diff --git a/configure.ac b/configure.ac index ce47ab7b16..220392d120 100644 --- a/configure.ac +++ b/configure.ac @@ -214,15 +214,23 @@ AS_CASE(["/${rb_CC} "], [*clang*], [ # Ditto for LLVM. Note however that llvm-as is a LLVM-IR to LLVM bitcode # assembler that does not target your machine native binary. - : ${LD:="${CC}"} # ... try -fuse-ld=lld ? - RUBY_CHECK_PROG_FOR_CC([AR], [s/clang/llvm-ar/]) -# RUBY_CHECK_PROG_FOR_CC([AS], [s/clang/llvm-as/]) + + # Xcode has its own version tools that may be incompatible with + # genuine LLVM tools, use the tools in the same directory. + + AS_IF([$rb_CC -E -dM -xc - < /dev/null | grep -F __apple_build_version__ > /dev/null], + [llvm_prefix=], [llvm_prefix=llvm-]) + # AC_PREPROC_IFELSE cannot be used before AC_USE_SYSTEM_EXTENSIONS + + RUBY_CHECK_PROG_FOR_CC([LD], [s/clang/ld/]) # ... maybe try lld ? + RUBY_CHECK_PROG_FOR_CC([AR], [s/clang/${llvm_prefix}ar/]) +# RUBY_CHECK_PROG_FOR_CC([AS], [s/clang/${llvm_prefix}as/]) RUBY_CHECK_PROG_FOR_CC([CXX], [s/clang/clang++/]) - RUBY_CHECK_PROG_FOR_CC([NM], [s/clang/llvm-nm/]) - RUBY_CHECK_PROG_FOR_CC([OBJCOPY], [s/clang/llvm-objcopy/]) - RUBY_CHECK_PROG_FOR_CC([OBJDUMP], [s/clang/llvm-objdump/]) - RUBY_CHECK_PROG_FOR_CC([RANLIB], [s/clang/llvm-ranlib/]) - RUBY_CHECK_PROG_FOR_CC([STRIP], [s/clang/llvm-strip/]) + RUBY_CHECK_PROG_FOR_CC([NM], [s/clang/${llvm_prefix}nm/]) + RUBY_CHECK_PROG_FOR_CC([OBJCOPY], [s/clang/${llvm_prefix}objcopy/]) + RUBY_CHECK_PROG_FOR_CC([OBJDUMP], [s/clang/${llvm_prefix}objdump/]) + RUBY_CHECK_PROG_FOR_CC([RANLIB], [s/clang/${llvm_prefix}ranlib/]) + RUBY_CHECK_PROG_FOR_CC([STRIP], [s/clang/${llvm_prefix}strip/]) ]) AS_UNSET(rb_CC) AS_UNSET(rb_dummy) @@ -366,12 +374,6 @@ AS_CASE(["$target_os"], [AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no) AC_MSG_ERROR([Unsupported OS X version is required])]) - AC_CACHE_CHECK([if thread-local storage is supported], [rb_cv_tls_supported], - [AC_LINK_IFELSE([AC_LANG_PROGRAM([[int __thread conftest;]])], - [rb_cv_tls_supported=yes], - [rb_cv_tls_supported=no])]) - AS_IF([test x"$rb_cv_tls_supported" != xyes], - [AC_DEFINE(RB_THREAD_LOCAL_SPECIFIER_IS_UNSUPPORTED)]) ]) RUBY_MINGW32 @@ -391,6 +393,13 @@ AS_IF([test "$GCC" = yes], [ AS_IF([test "$gcc_major" -lt 4], [ AC_MSG_ERROR([too old GCC: $gcc_major.$gcc_minor]) ]) + + AC_CACHE_CHECK([if thread-local storage is supported], [rb_cv_tls_supported], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[int __thread conftest;]])], + [rb_cv_tls_supported=yes], + [rb_cv_tls_supported=no])]) + AS_IF([test x"$rb_cv_tls_supported" != xyes], + [AC_DEFINE(RB_THREAD_LOCAL_SPECIFIER_IS_UNSUPPORTED)]) ], [ linker_flag= ]) @@ -524,7 +533,9 @@ AS_IF([test "$cross_compiling:$ac_cv_prog_DTRACE" = no: -a -n "$ac_tool_prefix"] AC_CHECK_PROGS(DOT, dot) AC_CHECK_PROGS(DOXYGEN, doxygen) +tool_warned=$ac_tool_warned ac_tool_warned=no AC_CHECK_TOOL(PKG_CONFIG, pkg-config) +ac_tool_warned=$tool_warned AS_IF([test -z "$PKG_CONFIG"], [], ["$PKG_CONFIG" --print-errors --version > /dev/null 2>&1], [], [ @@ -964,9 +975,10 @@ AS_IF([test "x$OPT_DIR" != x], [ LDFLAGS="${LDFLAGS:+$LDFLAGS }$val" DLDFLAGS="${DLDFLAGS:+$DLDFLAGS }$val" LDFLAGS_OPTDIR="$val" - CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }"`echo "$OPT_DIR" | tr "${PATH_SEPARATOR}" '\012' | + INCFLAGS="${INCFLAGS:+$INCFLAGS }"`echo "$OPT_DIR" | tr "${PATH_SEPARATOR}" '\012' | sed '/^$/d;s|^|-I|;s|$|/include|' | tr '\012' ' ' | sed 's/ *$//'` ]) +AC_SUBST(incflags, "$INCFLAGS") test -z "${ac_env_CFLAGS_set}" -a -n "${cflags+set}" && eval CFLAGS="\"$cflags $ARCH_FLAG\"" test -z "${ac_env_CXXFLAGS_set}" -a -n "${cxxflags+set}" && eval CXXFLAGS="\"$cxxflags $ARCH_FLAG\"" @@ -1191,8 +1203,6 @@ main() ac_cv_func_gmtime_r=yes rb_cv_large_fd_select=yes ac_cv_type_struct_timeval=yes - ac_cv_func_clock_gettime=yes - ac_cv_func_clock_getres=yes ac_cv_func_malloc_usable_size=no ac_cv_type_off_t=yes ac_cv_sizeof_off_t=8 @@ -1338,6 +1348,8 @@ AC_CHECK_HEADERS(syscall.h) AC_CHECK_HEADERS(time.h) AC_CHECK_HEADERS(ucontext.h) AC_CHECK_HEADERS(utime.h) +AC_CHECK_HEADERS(stdatomic.h) + AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [ AC_CHECK_HEADERS(x86intrin.h) ]) @@ -1354,6 +1366,8 @@ AC_ARG_WITH([jemalloc], [with_jemalloc=$withval], [with_jemalloc=no]) AS_IF([test "x$with_jemalloc" != xno],[ # find jemalloc header first + save_CPPFLAGS="${CPPFLAGS}" + CPPFLAGS="${INCFLAGS} ${CPPFLAGS}" malloc_header= AC_CHECK_HEADER(jemalloc/jemalloc.h, [malloc_header=jemalloc/jemalloc.h], [ AC_CHECK_HEADER(jemalloc.h, [malloc_header=jemalloc.h]) @@ -1385,6 +1399,8 @@ AS_IF([test "x$with_jemalloc" != xno],[ done done ]) + CPPFLAGS="${save_CPPFLAGS}" + unset save_CPPFLAGS with_jemalloc=${rb_cv_jemalloc_library} AS_CASE(["$with_jemalloc"], [no], @@ -1995,6 +2011,7 @@ AC_CHECK_FUNCS(_longjmp) # used for AC_ARG_WITH(setjmp-type) test x$ac_cv_func__longjmp = xno && ac_cv_func__setjmp=no AC_CHECK_FUNCS(arc4random_buf) AC_CHECK_FUNCS(atan2l atan2f) +AC_CHECK_DECLS(atomic_signal_fence, [], [], [#include <stdatomic.h>]) AC_CHECK_FUNCS(chmod) AC_CHECK_FUNCS(chown) AC_CHECK_FUNCS(chroot) @@ -3049,7 +3066,7 @@ AC_SUBST(EXTOBJS) [darwin*], [ : ${LDSHARED='$(CC) -dynamic -bundle'} : ${DLDSHARED='$(CC) -dynamiclib'} : ${LDFLAGS=""} - : ${LIBPATHENV=DYLD_FALLBACK_LIBRARY_PATH} + : ${LIBPATHENV=DYLD_LIBRARY_PATH} : ${PRELOADENV=DYLD_INSERT_LIBRARIES} AS_IF([test x"$enable_shared" = xyes], [ # Resolve symbols from libruby.dylib when --enable-shared @@ -3352,6 +3369,10 @@ AS_IF([test x"$cross_compiling" = xyes], [ AC_SUBST(XRUBY_RUBYLIBDIR) AC_SUBST(XRUBY_RUBYHDRDIR) PREP='$(arch)-fake.rb' + AS_CASE(["$enable_shared:$EXTSTATIC:$target_os"], [no::darwin*], [ + # darwin target requires miniruby for linking ext bundles + PREP="$PREP"' miniruby$(EXEEXT)' + ]) RUNRUBY_COMMAND='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`' RUNRUBY='$(RUNRUBY_COMMAND)' XRUBY='$(MINIRUBY)' @@ -3785,7 +3806,7 @@ AS_IF([test "$cross_compiling" = no], dnl build YJIT in release mode if rustc >= 1.58.0 is present and we are on a supported platform AC_ARG_ENABLE(yjit, AS_HELP_STRING([--enable-yjit], - [enable in-process JIT compiler that requires Rust build tools [default=no]]), + [enable in-process JIT compiler that requires Rust build tools. enabled by default on supported platforms if rustc 1.58.0+ is available]), [YJIT_SUPPORT=$enableval], [AS_CASE(["$enable_jit_support:$YJIT_TARGET_OK:$YJIT_RUSTC_OK"], [yes:yes:yes|:yes:yes], [ @@ -29,6 +29,7 @@ extern int madvise(caddr_t, size_t, int); #include "gc.h" #include "internal.h" #include "internal/cont.h" +#include "internal/error.h" #include "internal/proc.h" #include "internal/sanitizers.h" #include "internal/warnings.h" @@ -1254,6 +1255,8 @@ jit_cont_new(rb_execution_context_t *ec) static void jit_cont_free(struct rb_jit_cont *cont) { + if (!cont) return; + rb_native_mutex_lock(&jit_cont_lock); if (cont == first_jit_cont) { first_jit_cont = cont->next; @@ -1578,7 +1581,7 @@ cont_restore_1(rb_context_t *cont) cont_restore_thread(cont); /* restore machine stack */ -#ifdef _M_AMD64 +#if defined(_M_AMD64) && !defined(__MINGW64__) { /* workaround for x64 SEH */ jmp_buf buf; @@ -1958,7 +1961,7 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval) * the current thread, blocking and non-blocking fibers' behavior is identical. * * Ruby doesn't provide a scheduler class: it is expected to be implemented by - * the user and correspond to Fiber::SchedulerInterface. + * the user and correspond to Fiber::Scheduler. * * There is also Fiber.schedule method, which is expected to immediately perform * the given block in a non-blocking manner. Its actual implementation is up to @@ -2068,21 +2071,33 @@ fiber_storage_get(rb_fiber_t *fiber) return storage; } +static void +storage_access_must_be_from_same_fiber(VALUE self) +{ + rb_fiber_t *fiber = fiber_ptr(self); + rb_fiber_t *current = fiber_current(); + if (fiber != current) { + rb_raise(rb_eArgError, "Fiber storage can only be accessed from the Fiber it belongs to"); + } +} + /** - * call-seq: Fiber.current.storage -> hash (dup) + * call-seq: fiber.storage -> hash (dup) * - * Returns a copy of the storage hash for the current fiber. + * Returns a copy of the storage hash for the fiber. The method can only be called on the + * Fiber.current. */ static VALUE rb_fiber_storage_get(VALUE self) { + storage_access_must_be_from_same_fiber(self); return rb_obj_dup(fiber_storage_get(fiber_ptr(self))); } static int fiber_storage_validate_each(VALUE key, VALUE value, VALUE _argument) { - rb_check_id(&key); + Check_Type(key, T_SYMBOL); return ST_CONTINUE; } @@ -2105,16 +2120,17 @@ fiber_storage_validate(VALUE value) } /** - * call-seq: Fiber.current.storage = hash + * call-seq: fiber.storage = hash * - * Sets the storage hash for the current fiber. This feature is experimental - * and may change in the future. + * Sets the storage hash for the fiber. This feature is experimental + * and may change in the future. The method can only be called on the + * Fiber.current. * * You should be careful about using this method as you may inadvertently clear * important fiber-storage state. You should mostly prefer to assign specific - * keys in the storage using Fiber#[]=. + * keys in the storage using Fiber::[]=. * - * You can also use Fiber.new(storage: nil) to create a fiber with an empty + * You can also use <tt>Fiber.new(storage: nil)</tt> to create a fiber with an empty * storage. * * Example: @@ -2128,6 +2144,12 @@ fiber_storage_validate(VALUE value) static VALUE rb_fiber_storage_set(VALUE self, VALUE value) { + if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) { + rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL, + "Fiber#storage= is experimental and may be removed in the future!"); + } + + storage_access_must_be_from_same_fiber(self); fiber_storage_validate(value); fiber_ptr(self)->cont.saved_ec.storage = rb_obj_dup(value); @@ -2137,18 +2159,17 @@ rb_fiber_storage_set(VALUE self, VALUE value) /** * call-seq: Fiber[key] -> value * - * Returns the value of the fiber-local variable identified by +key+. + * Returns the value of the fiber storage variable identified by +key+. * * The +key+ must be a symbol, and the value is set by Fiber#[]= or * Fiber#store. * - * See also Fiber[]=. + * See also Fiber::[]=. */ static VALUE rb_fiber_storage_aref(VALUE class, VALUE key) { - ID id = rb_check_id(&key); - if (!id) return Qnil; + Check_Type(key, T_SYMBOL); VALUE storage = fiber_storage_get(fiber_current()); @@ -2160,18 +2181,17 @@ rb_fiber_storage_aref(VALUE class, VALUE key) /** * call-seq: Fiber[key] = value * - * Assign +value+ to the fiber-local variable identified by +key+. + * Assign +value+ to the fiber storage variable identified by +key+. * The variable is created if it doesn't exist. * * +key+ must be a Symbol, otherwise a TypeError is raised. * - * See also Fiber[]. + * See also Fiber::[]. */ static VALUE rb_fiber_storage_aset(VALUE class, VALUE key, VALUE value) { - ID id = rb_check_id(&key); - if (!id) return Qnil; + Check_Type(key, T_SYMBOL); VALUE storage = fiber_storage_get(fiber_current()); @@ -2185,9 +2205,6 @@ fiber_initialize(VALUE self, VALUE proc, struct fiber_pool * fiber_pool, unsigne // The default, inherit storage (dup) from the current fiber: storage = inherit_fiber_storage(); } - else if (storage == Qfalse) { - storage = current_fiber_storage(); - } else /* nil, hash, etc. */ { fiber_storage_validate(storage); storage = rb_obj_dup(storage); @@ -2299,18 +2316,6 @@ rb_fiber_initialize_kw(int argc, VALUE* argv, VALUE self, int kw_splat) * end.resume * Fiber[:x] # => 1 * - * If the <tt>storage</tt> is <tt>false</tt>, this function uses the current - * fiber's storage by reference. This is used for Enumerator to create - * hidden fiber. - * - * Fiber[:count] = 0 - * enumerator = Enumerator.new do |y| - * loop{y << (Fiber[:count] += 1)} - * end - * Fiber[:count] # => 0 - * enumerator.next # => 1 - * Fiber[:count] # => 1 - * * If the given <tt>storage</tt> is <tt>nil</tt>, this function will lazy * initialize the internal storage, which starts as an empty hash. * @@ -2322,7 +2327,7 @@ rb_fiber_initialize_kw(int argc, VALUE* argv, VALUE self, int kw_splat) * Otherwise, the given <tt>storage</tt> is used as the new fiber's storage, * and it must be an instance of Hash. * - * Explicitly using `storage: true/false` is currently experimental and may + * Explicitly using <tt>storage: true</tt> is currently experimental and may * change in the future. */ static VALUE @@ -2334,7 +2339,7 @@ rb_fiber_initialize(int argc, VALUE* argv, VALUE self) VALUE rb_fiber_new_storage(rb_block_call_func_t func, VALUE obj, VALUE storage) { - return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 1, storage); + return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 0, storage); } VALUE @@ -2394,7 +2399,7 @@ rb_fiber_s_schedule_kw(int argc, VALUE* argv, int kw_splat) * * Note that the behavior described above is how the method is <em>expected</em> * to behave, actual behavior is up to the current scheduler's implementation of - * Fiber::SchedulerInterface#fiber method. Ruby doesn't enforce this method to + * Fiber::Scheduler#fiber method. Ruby doesn't enforce this method to * behave in any particular way. * * If the scheduler is not set, the method raises @@ -2413,7 +2418,7 @@ rb_fiber_s_schedule(int argc, VALUE *argv, VALUE obj) * * Returns the Fiber scheduler, that was last set for the current thread with Fiber.set_scheduler. * Returns +nil+ if no scheduler is set (which is the default), and non-blocking fibers' - # behavior is the same as blocking. + * behavior is the same as blocking. * (see "Non-blocking fibers" section in class docs for details about the scheduler concept). * */ @@ -2447,7 +2452,7 @@ rb_fiber_current_scheduler(VALUE klass) * thread will call scheduler's +close+ method on finalization (allowing the scheduler to * properly manage all non-finished fibers). * - * +scheduler+ can be an object of any class corresponding to Fiber::SchedulerInterface. Its + * +scheduler+ can be an object of any class corresponding to Fiber::Scheduler. Its * implementation is up to the user. * * See also the "Non-blocking fibers" section in class docs. @@ -2794,7 +2799,8 @@ rb_fiber_blocking(VALUE class) // If we are already blocking, this is essentially a no-op: if (fiber->blocking) { return rb_yield(fiber_value); - } else { + } + else { return rb_ensure(fiber_blocking_yield, fiber_value, fiber_blocking_ensure, fiber_value); } } diff --git a/coroutine/asyncify/Context.h b/coroutine/asyncify/Context.h index 7dba829a1d..71791a4004 100644 --- a/coroutine/asyncify/Context.h +++ b/coroutine/asyncify/Context.h @@ -13,6 +13,7 @@ #include <stddef.h> #include <stdio.h> +#include <stdint.h> #include "wasm/asyncify.h" #include "wasm/machine.h" #include "wasm/fiber.h" @@ -47,10 +48,13 @@ static inline void coroutine_initialize_main(struct coroutine_context * context) static inline void coroutine_initialize(struct coroutine_context *context, coroutine_start start, void *stack, size_t size) { - if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)stack + size); + // Linear stack pointer must be always aligned down to 16 bytes. + // https://github.com/WebAssembly/tool-conventions/blob/c74267a5897c1bdc9aa60adeaf41816387d3cd12/BasicCABI.md#the-linear-stack + uintptr_t sp = ((uintptr_t)stack + size) & ~0xF; + if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)sp); rb_wasm_init_context(&context->fc, coroutine_trampoline, start, context); // record the initial stack pointer position to restore it after resumption - context->current_sp = (char *)stack + size; + context->current_sp = (char *)sp; context->stack_base = stack; context->size = size; } diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in index 43e92a27f0..f342d2fcf7 100644 --- a/cygwin/GNUmakefile.in +++ b/cygwin/GNUmakefile.in @@ -2,6 +2,9 @@ gnumake = yes include Makefile +MUNICODE_FLAG := $(if $(filter mingw%,$(target_os)),-municode) +override EXE_LDFLAGS += $(MUNICODE_FLAG) + DLLWRAP = @DLLWRAP@ --target=$(target_os) --driver-name="$(CC)" windres-cpp := $(CPP) -xc windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \ @@ -63,7 +66,7 @@ $(PROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT) $(WPROGRAM): $(RUBYW_INSTALL_NAME).res.$(OBJEXT) @rm -f $@ $(ECHO) linking $@ - $(Q) $(PURIFY) $(CC) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \ + $(Q) $(PURIFY) $(CC) $(MUNICODE_FLAG) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \ $(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(LIBS) -o $@ $(STUBPROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT) diff --git a/debug_counter.h b/debug_counter.h index b0047685f0..6e0b8dee60 100644 --- a/debug_counter.h +++ b/debug_counter.h @@ -243,6 +243,7 @@ RB_DEBUG_COUNTER(obj_wb_unprotect) RB_DEBUG_COUNTER(obj_obj_embed) RB_DEBUG_COUNTER(obj_obj_transient) RB_DEBUG_COUNTER(obj_obj_ptr) +RB_DEBUG_COUNTER(obj_obj_too_complex) RB_DEBUG_COUNTER(obj_str_ptr) RB_DEBUG_COUNTER(obj_str_embed) diff --git a/defs/gmake.mk b/defs/gmake.mk index dc9d31f49e..54fef6685f 100644 --- a/defs/gmake.mk +++ b/defs/gmake.mk @@ -27,7 +27,7 @@ TEST_DEPENDS := $(filter-out commit $(TEST_TARGETS),$(MAKECMDGOALS)) TEST_TARGETS := $(patsubst great,exam,$(TEST_TARGETS)) TEST_DEPENDS := $(filter-out great $(TEST_TARGETS),$(TEST_DEPENDS)) TEST_TARGETS := $(patsubst exam,check,$(TEST_TARGETS)) -TEST_TARGETS := $(patsubst check,test-spec test-all test-tool test-short,$(TEST_TARGETS)) +TEST_TARGETS := $(patsubst check,test-syntax-suggest test-spec test-all test-tool test-short,$(TEST_TARGETS)) TEST_TARGETS := $(patsubst test-rubyspec,test-spec,$(TEST_TARGETS)) TEST_DEPENDS := $(filter-out exam check test-spec $(TEST_TARGETS),$(TEST_DEPENDS)) TEST_TARGETS := $(patsubst love,check,$(TEST_TARGETS)) @@ -40,6 +40,7 @@ TEST_TARGETS := $(patsubst test-short,btest-ruby test-knownbug test-basic,$(TEST TEST_TARGETS := $(patsubst test-bundled-gems,test-bundled-gems-run,$(TEST_TARGETS)) TEST_TARGETS := $(patsubst test-bundled-gems-run,test-bundled-gems-run $(PREPARE_BUNDLED_GEMS),$(TEST_TARGETS)) TEST_TARGETS := $(patsubst test-bundled-gems-prepare,test-bundled-gems-prepare $(PRECHECK_BUNDLED_GEMS) test-bundled-gems-fetch,$(TEST_TARGETS)) +TEST_TARGETS := $(patsubst test-syntax-suggest,test-syntax-suggest $(PREPARE_SYNTAX_SUGGEST),$(TEST_TARGETS)) TEST_DEPENDS := $(filter-out test-short $(TEST_TARGETS),$(TEST_DEPENDS)) TEST_DEPENDS += $(if $(filter great exam love check,$(MAKECMDGOALS)),all exts) endif @@ -83,7 +84,8 @@ endif ORDERED_TEST_TARGETS := $(filter $(TEST_TARGETS), \ btest-ruby test-knownbug test-basic \ test-testframework test-tool test-ruby test-all \ - test-spec test-bundler-prepare test-bundler test-bundler-parallel \ + test-spec test-syntax-suggest-prepare test-syntax-suggest \ + test-bundler-prepare test-bundler test-bundler-parallel \ test-bundled-gems-precheck test-bundled-gems-fetch \ test-bundled-gems-prepare test-bundled-gems-run \ ) diff --git a/defs/id.def b/defs/id.def index a3a383f532..ebf00506ea 100644 --- a/defs/id.def +++ b/defs/id.def @@ -76,6 +76,7 @@ firstline, predefined = __LINE__+1, %[\ "/*NULL*/" NULL empty? eql? + default respond_to? Respond_to respond_to_missing? Respond_to_missing <IFUNC> diff --git a/doc/.document b/doc/.document index 5ef2d99651..f589dda07c 100644 --- a/doc/.document +++ b/doc/.document @@ -6,3 +6,4 @@ NEWS syntax optparse rdoc +yjit diff --git a/doc/encodings.rdoc b/doc/encodings.rdoc index c61ab11e9a..1f3c54d740 100644 --- a/doc/encodings.rdoc +++ b/doc/encodings.rdoc @@ -467,12 +467,13 @@ These keyword-value pairs specify encoding options: with a carriage-return character (<tt>"\r"</tt>). - <tt>:crlf_newline: true</tt>: Replace each line-feed character (<tt>"\n"</tt>) with a carriage-return/line-feed string (<tt>"\r\n"</tt>). - - <tt>:universal_newline: true</tt>: Replace each carriage-return/line-feed string + - <tt>:universal_newline: true</tt>: Replace each carriage-return + character (<tt>"\r"</tt>) and each carriage-return/line-feed string (<tt>"\r\n"</tt>) with a line-feed character (<tt>"\n"</tt>). Examples: - s = "\n \r\n" # => "\n \r\n" - s.encode('ASCII', cr_newline: true) # => "\r \r\r" - s.encode('ASCII', crlf_newline: true) # => "\r\n \r\r\n" - s.encode('ASCII', universal_newline: true) # => "\n \n" + s = "\n \r \r\n" # => "\n \r \r\n" + s.encode('ASCII', cr_newline: true) # => "\r \r \r\r" + s.encode('ASCII', crlf_newline: true) # => "\r\n \r \r\r\n" + s.encode('ASCII', universal_newline: true) # => "\n \n \n" diff --git a/doc/mjit.md b/doc/mjit.md deleted file mode 100644 index 9622a3bc26..0000000000 --- a/doc/mjit.md +++ /dev/null @@ -1,78 +0,0 @@ -# MJIT - -This document has some tips that might be useful when you work on MJIT. - -## Supported platforms - -The following platforms are either tested on CI or assumed to work. - -* OS: Linux, macOS -* Arch: x86\_64, aarch64, arm64, i686, i386 - -### Not supported - -The MJIT support for the following platforms is no longer maintained. - -* OS: Windows (mswin, MinGW), Solaris -* Arch: SPARC, s390x - -### Architectures - -## Bindgen - -If you see an "MJIT bindgen" GitHub Actions failure, please commit the `git diff` shown on the failed job. - -Refer to the following instructions for doing the same thing locally. -Similar to `make yjit-bindgen`, `make mjit-bindgen` requires libclang. -See also: [mjit-bindgen.yml](../.github/workflows/mjit-bindgen.yml) - -macOS seems to have libclang by default, but I'm not sure how to deal with 32bit architectures. -For now, you may generate c\_64.rb with a 64bit binary, and then manually modify c\_32.rb accordingly. - -### x86\_64-linux - -```sh -sudo apt install \ - build-essential \ - libssl-dev libyaml-dev libreadline6-dev \ - zlib1g-dev libncurses5-dev libffi-dev \ - libclang1 -./autogen.sh -./configure --enable-yjit=dev_nodebug --disable-install-doc -make -j -make mjit-bindgen -``` - -### i686-linux - -```sh -sudo dpkg --add-architecture i386 -sudo apt install \ - crossbuild-essential:i386 \ - libssl-dev:i386 libyaml-dev:i386 libreadline6-dev:i386 \ - zlib1g-dev:i386 libncurses5-dev:i386 libffi-dev:i386 \ - libclang1:i386 -./autogen.sh -./configure --disable-install-doc -make -j -make mjit-bindgen -``` - -Note that you cannot run x86\_64 bindgen with an i686 binary, and vice versa. -Also, when you install libclang1:i386, libclang1 will be uninstalled. -You can have only either of these at a time. - -## Local development - -### Always run make install - -Always run `make install` before running MJIT. It could easily cause a SEGV if you don't. -MJIT looks for the installed header for security reasons. - -### --mjit-debug vs --mjit-debug=-ggdb3 - -`--mjit-debug=[flags]` allows you to specify arbitrary flags while keeping other compiler flags like `-O3`, -which is useful for profiling benchmarks. - -`--mjit-debug` alone, on the other hand, disables `-O3` and adds debug flags. -If you're debugging MJIT, what you need to use is not `--mjit-debug=-ggdb3` but `--mjit-debug`. diff --git a/doc/mjit/mjit.md b/doc/mjit/mjit.md new file mode 100644 index 0000000000..6f19ab3ea7 --- /dev/null +++ b/doc/mjit/mjit.md @@ -0,0 +1,39 @@ +# MJIT + +This document has some tips that might be useful when you work on MJIT. + +## Supported platforms + +The following platforms are either tested on CI or assumed to work. + +* OS: Linux, macOS +* Arch: x86\_64, aarch64, arm64, i686, i386 + +### Not supported + +The MJIT support for the following platforms is no longer maintained. + +* OS: Windows (mswin, MinGW), Solaris +* Arch: SPARC, s390x + +## Developing MJIT + +### Bindgen + +If you see an "MJIT bindgen" GitHub Actions failure, please commit the `git diff` shown on the failed job. + +For doing the same thing locally, run `make mjit-bindgen` after installing libclang. +macOS seems to have libclang by default. On Ubuntu, you can install it with `apt install libclang1`. + +### Always run make install + +Always run `make install` before running MJIT. It could easily cause a SEGV if you don't. +MJIT looks for the installed header for security reasons. + +### --mjit-debug vs --mjit-debug=-ggdb3 + +`--mjit-debug=[flags]` allows you to specify arbitrary flags while keeping other compiler flags like `-O3`, +which is useful for profiling benchmarks. + +`--mjit-debug` alone, on the other hand, disables `-O3` and adds debug flags. +If you're debugging MJIT, what you need to use is not `--mjit-debug=-ggdb3` but `--mjit-debug`. diff --git a/doc/net-http/examples.rdoc b/doc/net-http/examples.rdoc index dd4acecda6..c1366e7ad1 100644 --- a/doc/net-http/examples.rdoc +++ b/doc/net-http/examples.rdoc @@ -10,9 +10,10 @@ Many code examples here use these example websites: Some examples also assume these variables: - uri = URI('https://jsonplaceholder.typicode.com') + uri = URI('https://jsonplaceholder.typicode.com/') uri.freeze # Examples may not modify. hostname = uri.hostname # => "jsonplaceholder.typicode.com" + path = uri.path # => "/" port = uri.port # => 443 So that example requests may be written as: diff --git a/doc/net-http/included_getters.rdoc b/doc/net-http/included_getters.rdoc new file mode 100644 index 0000000000..7ac327f4b4 --- /dev/null +++ b/doc/net-http/included_getters.rdoc @@ -0,0 +1,3 @@ +This class also includes (indirectly) module Net::HTTPHeader, +which gives access to its +{methods for getting headers}[rdoc-ref:Net::HTTPHeader@Getters]. diff --git a/doc/packed_data.rdoc b/doc/packed_data.rdoc index 9ba585c90b..ec13b24c69 100644 --- a/doc/packed_data.rdoc +++ b/doc/packed_data.rdoc @@ -60,6 +60,10 @@ Any directive may be followed by either of these modifiers: Note: Directives in <tt>%w[A a Z m]</tt> use +count+ differently; see {String Directives}[rdoc-ref:packed_data.rdoc@String+Directives]. +If elements don't fit the provided directive, only least significant bits are encoded: + + [257].pack("C").unpack("C") # => [1] + === Packing \Method \Method Array#pack accepts optional keyword argument diff --git a/doc/syntax/literals.rdoc b/doc/syntax/literals.rdoc index 5e10e6a140..b641433249 100644 --- a/doc/syntax/literals.rdoc +++ b/doc/syntax/literals.rdoc @@ -277,6 +277,12 @@ the content. Note that empty lines and lines consisting solely of literal tabs and spaces will be ignored for the purposes of determining indentation, but escaped tabs and spaces are considered non-indentation characters. +For the purpose of measuring an indentation, a horizontal tab is regarded as a +sequence of one to eight spaces such that the column position corresponding to +its end is a multiple of eight. The amount to be removed is counted in terms +of the number of spaces. If the boundary appears in the middle of a tab, that +tab is not removed. + A heredoc allows interpolation and escaped characters. You may disable interpolation and escaping by surrounding the opening identifier with single quotes: diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md index 1af5aeb417..67b2ffa5f0 100644 --- a/doc/yjit/yjit.md +++ b/doc/yjit/yjit.md @@ -9,9 +9,9 @@ YJIT - Yet Another Ruby JIT =========================== YJIT is a lightweight, minimalistic Ruby JIT built inside CRuby. -It lazily compiles code using a Basic Block Versioning (BBV) architecture. The target use case is that of servers running -Ruby on Rails, an area where MJIT has not yet managed to deliver speedups. -YJIT is currently supported for macOS and Linux on x86-64 and arm64/aarch64 CPUs. +It lazily compiles code using a Basic Block Versioning (BBV) architecture. +The target use case is that of servers running Ruby on Rails. +YJIT is currently supported for macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs. This project is open source and falls under the same license as CRuby. <p align="center"><b> @@ -20,6 +20,8 @@ This project is open source and falls under the same license as CRuby. </b></p> If you wish to learn more about the approach taken, here are some conference talks and publications: +- RubyKaigi 2022 keynote: [Stories from developing YJIT](https://www.youtube.com/watch?v=EMchdR9C8XM) +- RubyKaigi 2022 talk: [Building a Lightweight IR and Backend for YJIT](https://www.youtube.com/watch?v=BbLGqTxTRp0) - RubyKaigi 2021 talk: [YJIT: Building a New JIT Compiler Inside CRuby](https://www.youtube.com/watch?v=PBVLf3yfMs8) - Blog post: [YJIT: Building a New JIT Compiler Inside CRuby](https://pointersgonewild.com/2021/06/02/yjit-building-a-new-jit-compiler-inside-cruby/) - VMIL 2021 paper: [YJIT: A Basic Block Versioning JIT Compiler for CRuby](https://dl.acm.org/doi/10.1145/3486606.3486781) @@ -52,8 +54,7 @@ series = {VMIL 2021} ## Current Limitations -YJIT may not be suitable for certain applications. It currently only supports macOS and Linux on x86-64 and arm64/aarch64 CPUs. YJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state -information. +YJIT may not be suitable for certain applications. It currently only supports macOS and Linux on x86-64 and arm64/aarch64 CPUs. YJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state information. You can change how much executable memory is allocated using [YJIT's command-line options](#command-line-options). There is a slight performance tradeoff because allocating less executable memory could result in the generated machine code being collected more often. ## Installation @@ -75,44 +76,53 @@ To install the Rust build toolchain, we suggest following the [recommended insta Start by cloning the `ruby/ruby` repository: -``` +```sh git clone https://github.com/ruby/ruby yjit cd yjit ``` -The YJIT `ruby` binary can be built with either GCC or Clang. It can be built either in dev (debug) mode or in release mode. For maximum performance, compile YJIT in release mode with GCC. More detailed build instructions are provided in the [Ruby README](https://github.com/ruby/ruby#how-to-compile-and-install). +The YJIT `ruby` binary can be built with either GCC or Clang. It can be built either in dev (debug) mode or in release mode. For maximum performance, compile YJIT in release mode with GCC. More detailed build instructions are provided in the [Ruby README](https://github.com/ruby/ruby#how-to-build). -``` +```sh # Configure in release mode for maximum performance, build and install ./autogen.sh ./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc -make -j install +make -j && make install ``` or -``` -# Configure in dev (debug) mode for development, build and install +```sh +# Configure in lower-performance dev (debug) mode for development, build and install ./autogen.sh ./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc -make -j install +make -j && make install ``` -On macOS, you may need to specify where to find some libraries: +Dev mode includes extended YJIT statistics, but can be slow. For only statistics you can configure in stats mode: +```sh +# Configure in extended-stats mode without slow runtime checks, build and install +./autogen.sh +./configure --enable-yjit=stats --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc +make -j && make install ``` + +On macOS, you may need to specify where to find some libraries: + +```sh # Install dependencies 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 --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)" -make -j install +make -j && make install ``` Typically configure will choose the default C compiler. To specify the C compiler, use -``` +```sh # Choosing a specific c compiler export CC=/path/to/my/chosen/c/compiler ``` @@ -121,7 +131,7 @@ before running `./configure`. You can test that YJIT works correctly by running: -``` +```sh # Quick tests found in /bootstraptest make btest @@ -136,14 +146,14 @@ make -j test-all Once YJIT is built, you can either use `./miniruby` from within your build directory, or switch to the YJIT version of `ruby` by using the `chruby` tool: -``` +```sh chruby ruby-yjit ruby myscript.rb ``` You can dump statistics about compilation and execution by running YJIT with the `--yjit-stats` command-line option: -``` +```sh ./miniruby --yjit-stats myscript.rb ``` @@ -155,11 +165,9 @@ YJIT supports all command-line options supported by upstream CRuby, but also add - `--yjit`: enable YJIT (disabled by default) - `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function (default 30) -- `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 128 MiB) -- `--yjit-stats`: produce statistics after the execution of a program -- `--yjit-trace-exits`: produce a Marshal dump of backtraces from specific exits. Automatically enables `--yjit-stats` (must configure and build with `--enable-yjit=stats` to use this) -- `--yjit-max-versions=N`: maximum number of versions to generate per basic block (default 4) -- `--yjit-greedy-versioning`: greedy versioning mode (disabled by default, may increase code size) +- `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 64 MiB) +- `--yjit-stats`: print statistics after the execution of a program (incurs a run-time cost) +- `--yjit-trace-exits`: produce a Marshal dump of backtraces from specific exits. Automatically enables `--yjit-stats` Note that there is also an environment variable `RUBY_YJIT_ENABLE` which can be used to enable YJIT. This can be useful for some deployment scripts where specifying an extra command-line option to Ruby is not practical. @@ -168,28 +176,79 @@ This can be useful for some deployment scripts where specifying an extra command We have collected a set of benchmarks and implemented a simple benchmarking harness in the [yjit-bench](https://github.com/Shopify/yjit-bench) repository. This benchmarking harness is designed to disable CPU frequency scaling, set process affinity and disable address space randomization so that the variance between benchmarking runs will be as small as possible. Please kindly note that we are at an early stage in this project. -### Performance Tips +## Performance Tips for Production Deployments + +While YJIT options default to what we think would work well for most workloads, +they might not necessarily be the best configuration for your application. + +This section covers tips on improving YJIT performance in case YJIT does not +speed up your application in production. + +### Increasing --yjit-exec-mem-size + +When JIT code size (`RubyVM::YJIT.runtime_stats[:code_region_size]`) reaches this value, +YJIT triggers "code GC" that frees all JIT code and starts recompiling everything. +Compiling code takes some time, so scheduling code GC too frequently slows down your application. +Increasing `--yjit-exec-mem-size` may speed up your application if `RubyVM::YJIT.runtime_stats[:code_gc_count]` is not 0 or 1. + +### Running workers as long as possible + +It's helpful to call the same code as many times as possible before a process restarts. +If a process is killed too frequently, the time taken for compiling methods may outweigh +the speedup obtained by compiling them. + +You should monitor the number of requests each process has served. +If you're periodically killing worker processes, e.g. with `unicorn-worker-killer` or `puma_worker_killer`, +you may want to reduce the killing frequency or increase the limit. + +## Saving YJIT Memory Usage + +YJIT allocates memory for JIT code and metadata. Enabling YJIT generally results in more memory usage. -This section contains tips on writing Ruby code that will run as fast as possible on YJIT. Some of this advice is based on current limitations of YJIT, while other advice is broadly applicable. It probably won't be practical to apply these tips everywhere in your codebase, but you can profile your code using a tool such as [stackprof](https://github.com/tmm1/stackprof) and refactor the specific methods that make up the largest fractions of the execution time. +This section goes over tips on minimizing YJIT memory usage in case it uses more than your capacity. -- Use exceptions for error recovery only, not as part of normal control-flow +### Increasing --yjit-call-threshold + +As of Ruby 3.2, `--yjit-call-threshold` defaults to 30. With this default, some applications end up +compiling methods that are used only during the application boot. Increasing this option may help +you reduce the size of JIT code and metadata. It's worth trying different values like `--yjit-call-threshold=100`. + +Note that increasing the value too much may result in compiling code too late. +You should monitor how many requests each worker processes before it's restarted. For example, +if each process only handles 1000 requests, `--yjit-call-threshold=1000` might be too large for your application. + +### Decreasing --yjit-exec-mem-size + +`--yjit-exec-mem-size` specifies the JIT code size, but YJIT also uses memory for its metadata, +which often consumes more memory than JIT code. Generally, YJIT adds memory overhead by roughly +3-4x of `--yjit-exec-mem-size` in production as of Ruby 3.2. You should multiply that by the number +of worker processes to estimate the worst case memory overhead. + +Running code GC adds overhead, but it could be still faster than recovering from a whole process killed by OOM. + +## Code Optimization Tips + +This section contains tips on writing Ruby code that will run as fast as possible on YJIT. Some of this advice is based on current limitations of YJIT, while other advice is broadly applicable. It probably won't be practical to apply these tips everywhere in your codebase. You should ideally start by profiling your application using a tool such as [stackprof](https://github.com/tmm1/stackprof) so that you can determine which methods make up most of the execution time. You can then refactor the specific methods that make up the largest fractions of the execution time. We do not recommend modifying your entire codebase based on the current limitations of YJIT. + +- Avoid using `OpenStruct` - Avoid redefining basic integer operations (i.e. +, -, <, >, etc.) - Avoid redefining the meaning of `nil`, equality, etc. - Avoid allocating objects in the hot parts of your code -- Use while loops if you can, instead of `integer.times` - Minimize layers of indirection - Avoid classes that wrap objects if you can - Avoid methods that just call another method, trivial one liner methods -- CRuby method calls are costly. Favor larger methods over smaller methods. - Try to write code so that the same variables always have the same type +- Use `while` loops if you can, instead of C methods like `Array#each` + - This is not idiomatic Ruby, but could help in hot methods +- CRuby method calls are costly. Avoid things such as methods that only return a value from a hash or return a constant. You can also use the `--yjit-stats` command-line option to see which bytecodes cause YJIT to exit, and refactor your code to avoid using these instructions in the hottest methods of your code. ### Other Statistics -If you compile Ruby with `RUBY_DEBUG` and/or `YJIT_STATS` defined and run with `--yjit --yjit-stats`, YJIT will track and return performance statistics in `RubyVM::YJIT.runtime_stats`. +If you run `ruby` with `--yjit --yjit-stats`, YJIT will track and return performance statistics in `RubyVM::YJIT.runtime_stats`. -``` +```rb $ RUBYOPT="--yjit --yjit-stats" irb irb(main):001:0> RubyVM::YJIT.runtime_stats => @@ -205,16 +264,24 @@ irb(main):001:0> RubyVM::YJIT.runtime_stats Some of the counters include: -:exec_instruction - how many Ruby bytecode instructions have been executed -:binding_allocations - number of bindings allocated -:binding_set - number of variables set via a binding -:vm_insns_count - number of instructions executed by the Ruby interpreter -:compiled_iseq_count - number of bytecode sequences compiled +* :exec_instruction - how many Ruby bytecode instructions have been executed +* :binding_allocations - number of bindings allocated +* :binding_set - number of variables set via a binding +* :code_gc_count - number of garbage collections of compiled code since process start +* :vm_insns_count - number of instructions executed by the Ruby interpreter +* :compiled_iseq_count - number of bytecode sequences compiled +* :inline_code_size - size in bytes of compiled YJIT blocks +* :outline_code_size - size in bytes of YJIT error-handling compiled code +* :side_exit_count - number of side exits taken at runtime +* :total_exit_count - number of exits, including side exits, taken at runtime +* :avg_len_in_yjit - avg. number of instructions in compiled blocks before exiting to interpreter Counters starting with "exit_" show reasons for YJIT code taking a side exit (return to the interpreter.) See yjit_hacking.md for more details. Performance counter names are not guaranteed to remain the same between Ruby versions. If you're curious what one does, it's usually best to search the source code for it — but it may change in a later Ruby version. +The printed text after a --yjit-stats run includes other information that may be named differently than the information in runtime_stats. + ## Contributing We welcome open source contributors. You should feel free to open new issues to report bugs or just to ask questions. @@ -242,8 +309,6 @@ The YJIT source code is divided between: - `yjit/src/options.rs`: handling of command-line options - `yjit/bindgen/src/main.rs`: C bindings exposed to the Rust codebase through bindgen - `yjit/src/cruby.rs`: C bindings manually exposed to the Rust codebase -- `misc/test_yjit_asm.sh`: script to compile and run the in-memory assembler tests -- `misc/yjit_asm_tests.c`: tests for the in-memory assembler The core of CRuby's interpreter logic is found in: - `insns.def`: defines Ruby's bytecode instructions (gets compiled into `vm.inc`) @@ -274,38 +339,38 @@ There are 3 test suites: The tests can be run in parallel like this: -``` +```sh make -j test-all RUN_OPTS="--yjit-call-threshold=1" ``` Or single-threaded like this, to more easily identify which specific test is failing: -``` +```sh make test-all TESTOPTS=--verbose RUN_OPTS="--yjit-call-threshold=1" ``` To debug a single test in `test-all`: -``` +```sh make test-all TESTS='test/-ext-/marshal/test_usrmarshal.rb' RUNRUBYOPT=--debugger=lldb RUN_OPTS="--yjit-call-threshold=1" ``` You can also run one specific test in `btest`: -``` +```sh make btest BTESTS=bootstraptest/test_ractor.rb RUN_OPTS="--yjit-call-threshold=1" ``` There are shortcuts to run/debug your own test/repro in `test.rb`: -``` +```sh make run # runs ./miniruby test.rb make lldb # launches ./miniruby test.rb in lldb ``` You can use the Intel syntax for disassembly in LLDB, keeping it consistent with YJIT's disassembly: -``` +```sh echo "settings set target.x86-disassembly-flavor intel" >> ~/.lldbinit ``` @@ -316,7 +381,7 @@ instructions below, but there are a few caveats listed further down. First, install Rosetta: -``` +```sh $ softwareupdate --install-rosetta ``` @@ -324,13 +389,13 @@ Now any command can be run with Rosetta via the `arch` command line tool. Then you can start your shell in an x86 environment: -``` +```sh $ arch -x86_64 zsh ``` You can double check your current architecture via the `arch` command: -``` +```sh $ arch -x86_64 zsh $ arch i386 @@ -338,7 +403,7 @@ i386 You may need to set the default target for `rustc` to x86-64, e.g. -``` +```sh $ rustup default stable-x86_64-apple-darwin ``` diff --git a/enc/Makefile.in b/enc/Makefile.in index dd8ca1b528..9d0c367134 100644 --- a/enc/Makefile.in +++ b/enc/Makefile.in @@ -51,7 +51,7 @@ optflags = @optflags@ debugflags = @debugflags@ warnflags = @warnflags@ CCDLFLAGS = @CCDLFLAGS@ -INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(top_srcdir) +INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(top_srcdir) @incflags@ DEFS = @DEFS@ CPPFLAGS = @CPPFLAGS@ -DONIG_ENC_REGISTER=rb_enc_register LDFLAGS = @LDFLAGS@ diff --git a/enc/cesu_8.c b/enc/cesu_8.c index decbb928f4..75f62df280 100644 --- a/enc/cesu_8.c +++ b/enc/cesu_8.c @@ -42,6 +42,8 @@ #define VALID_CODE_LIMIT 0x0010ffff #define utf8_islead(c) ((UChar )((c) & 0xc0) != 0x80) +#define utf16_is_high_surrogate(v) ((v >> 10) == 0x36) +#define utf16_is_low_surrogate(v) ((v >> 10) == 0x37) static const int EncLen_CESU8[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -283,6 +285,12 @@ is_mbc_newline(const UChar* p, const UChar* end, OnigEncoding enc) return 0; } +static int +utf8_decode_3byte_sequence(const UChar* p) +{ + return ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); +} + static OnigCodePoint mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc) { @@ -295,11 +303,11 @@ mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc) case 2: return ((p[0] & 0x1F) << 6) | (p[1] & 0x3f); case 3: - return ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); + return utf8_decode_3byte_sequence(p); case 6: { - int high = ((p[0] & 0xF) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); - int low = ((p[3] & 0xF) << 12) | ((p[4] & 0x3f) << 6) | (p[5] & 0x3f); + int high = utf8_decode_3byte_sequence(p); + int low = utf8_decode_3byte_sequence(p + 3); return ((high & 0x03ff) << 10) + (low & 0x03ff) + 0x10000; } } @@ -410,7 +418,6 @@ get_ctype_code_range(OnigCtype ctype, OnigCodePoint *sb_out, return onigenc_unicode_ctype_code_range(ctype, ranges); } - static UChar* left_adjust_char_head(const UChar* start, const UChar* s, const UChar* end, OnigEncoding enc ARG_UNUSED) { @@ -420,6 +427,14 @@ left_adjust_char_head(const UChar* start, const UChar* s, const UChar* end, Onig p = s; while (!utf8_islead(*p) && p > start) p--; + + if (p > start && s - p == 2 && utf16_is_low_surrogate(utf8_decode_3byte_sequence(p))) { + const UChar *p_surrogate_pair = p - 1; + while (!utf8_islead(*p_surrogate_pair) && p_surrogate_pair > start) p_surrogate_pair--; + if (p - p_surrogate_pair == 3 && utf16_is_high_surrogate(utf8_decode_3byte_sequence(p_surrogate_pair))) { + return (UChar* )p_surrogate_pair; + } + } return (UChar* )p; } diff --git a/enc/depend b/enc/depend index 60c5a3ebb2..973ad93010 100644 --- a/enc/depend +++ b/enc/depend @@ -7018,6 +7018,7 @@ enc/trans/iso2022.$(OBJEXT): internal/attr/nodiscard.h enc/trans/iso2022.$(OBJEXT): internal/attr/noexcept.h enc/trans/iso2022.$(OBJEXT): internal/attr/noinline.h enc/trans/iso2022.$(OBJEXT): internal/attr/nonnull.h +enc/trans/iso2022.$(OBJEXT): internal/attr/nonstring.h enc/trans/iso2022.$(OBJEXT): internal/attr/noreturn.h enc/trans/iso2022.$(OBJEXT): internal/attr/pure.h enc/trans/iso2022.$(OBJEXT): internal/attr/restrict.h diff --git a/encoding.c b/encoding.c index b8e7f790b8..2f4b47bdfa 100644 --- a/encoding.c +++ b/encoding.c @@ -56,9 +56,8 @@ int rb_encdb_alias(const char *alias, const char *orig); static ID id_encoding; VALUE rb_cEncoding; -#define DEFAULT_ENCODING_LIST_CAPA 128 -static VALUE rb_default_encoding_list; -static VALUE rb_additional_encoding_list; +#define ENCODING_LIST_CAPA 256 +static VALUE rb_encoding_list; struct rb_encoding_entry { const char *name; @@ -67,9 +66,8 @@ struct rb_encoding_entry { }; static struct enc_table { - struct rb_encoding_entry *list; + struct rb_encoding_entry list[ENCODING_LIST_CAPA]; int count; - int size; st_table *names; } global_enc_table; @@ -128,47 +126,25 @@ enc_new(rb_encoding *encoding) static void enc_list_update(int index, rb_raw_encoding *encoding) { - if (index < DEFAULT_ENCODING_LIST_CAPA) { - VALUE list = rb_default_encoding_list; - if (list && NIL_P(rb_ary_entry(list, index))) { - /* initialize encoding data */ - rb_ary_store(list, index, enc_new(encoding)); - } - } - else { - RB_VM_LOCK_ENTER(); - { - VALUE list = rb_additional_encoding_list; - if (list && NIL_P(rb_ary_entry(list, index))) { - /* initialize encoding data */ - rb_ary_store(list, index - DEFAULT_ENCODING_LIST_CAPA, enc_new(encoding)); - } - } - RB_VM_LOCK_LEAVE(); + RUBY_ASSERT(index < ENCODING_LIST_CAPA); + + VALUE list = rb_encoding_list; + if (list && NIL_P(rb_ary_entry(list, index))) { + /* initialize encoding data */ + rb_ary_store(list, index, enc_new(encoding)); } } static VALUE enc_list_lookup(int idx) { - VALUE list, enc; + VALUE list, enc = Qnil; - if (idx < DEFAULT_ENCODING_LIST_CAPA) { - if (!(list = rb_default_encoding_list)) { - rb_bug("rb_enc_from_encoding_index(%d): no rb_default_encoding_list", idx); - } + if (idx < ENCODING_LIST_CAPA) { + list = rb_encoding_list; + RUBY_ASSERT(list); enc = rb_ary_entry(list, idx); } - else { - RB_VM_LOCK_ENTER(); - { - if (!(list = rb_additional_encoding_list)) { - rb_bug("rb_enc_from_encoding_index(%d): no rb_additional_encoding_list", idx); - } - enc = rb_ary_entry(list, idx - DEFAULT_ENCODING_LIST_CAPA); - } - RB_VM_LOCK_LEAVE(); - } if (NIL_P(enc)) { rb_bug("rb_enc_from_encoding_index(%d): not created yet", idx); @@ -345,16 +321,10 @@ rb_find_encoding(VALUE enc) static int enc_table_expand(struct enc_table *enc_table, int newsize) { - struct rb_encoding_entry *ent; - int count = newsize; - - if (enc_table->size >= newsize) return newsize; - newsize = (newsize + 7) / 8 * 8; - ent = REALLOC_N(enc_table->list, struct rb_encoding_entry, newsize); - memset(ent + enc_table->size, 0, sizeof(*ent)*(newsize - enc_table->size)); - enc_table->list = ent; - enc_table->size = newsize; - return count; + if (newsize > ENCODING_LIST_CAPA) { + rb_raise(rb_eEncodingError, "too many encoding (> %d)", ENCODING_LIST_CAPA); + } + return newsize; } static int @@ -413,17 +383,7 @@ enc_from_index(struct enc_table *enc_table, int index) rb_encoding * rb_enc_from_index(int index) { - rb_encoding *enc; - - switch (index) { - case ENCINDEX_ASCII_8BIT: return global_enc_ascii; - case ENCINDEX_UTF_8: return global_enc_utf_8; - case ENCINDEX_US_ASCII: return global_enc_us_ascii; - default: - GLOBAL_ENC_TABLE_EVAL(enc_table, - enc = enc_from_index(enc_table, index)); - return enc; - } + return enc_from_index(&global_enc_table, index); } int @@ -462,7 +422,7 @@ enc_registered(struct enc_table *enc_table, const char *name) st_data_t idx = 0; if (!name) return -1; - if (!enc_table->list) return -1; + if (!enc_table->names) return -1; if (st_lookup(enc_table->names, (st_data_t)name, &idx)) { return (int)idx; } @@ -484,11 +444,14 @@ rb_encdb_declare(const char *name) } static void -enc_check_duplication(struct enc_table *enc_table, const char *name) +enc_check_addable(struct enc_table *enc_table, const char *name) { if (enc_registered(enc_table, name) >= 0) { rb_raise(rb_eArgError, "encoding %s is already registered", name); } + else if (!valid_encoding_name_p(name)) { + rb_raise(rb_eArgError, "invalid encoding name: %s", name); + } } static rb_encoding* @@ -524,11 +487,7 @@ rb_enc_set_base(const char *name, const char *orig) int rb_enc_set_dummy(int index) { - rb_encoding *enc; - - GLOBAL_ENC_TABLE_EVAL(enc_table, - enc = enc_table->list[index].enc); - + rb_encoding *enc = global_enc_table.list[index].enc; ENC_SET_DUMMY((rb_raw_encoding *)enc); return index; } @@ -538,7 +497,7 @@ enc_replicate(struct enc_table *enc_table, const char *name, rb_encoding *encodi { int idx; - enc_check_duplication(enc_table, name); + enc_check_addable(enc_table, name); idx = enc_register(enc_table, name, encoding); if (idx < 0) rb_raise(rb_eArgError, "invalid encoding name: %s", name); set_base_encoding(enc_table, idx, encoding); @@ -727,7 +686,7 @@ rb_enc_alias(const char *alias, const char *orig) GLOBAL_ENC_TABLE_ENTER(enc_table); { - enc_check_duplication(enc_table, alias); + enc_check_addable(enc_table, alias); if ((idx = rb_enc_find_index(orig)) < 0) { r = -1; } @@ -764,7 +723,7 @@ rb_enc_init(struct enc_table *enc_table) { enc_table_expand(enc_table, ENCODING_COUNT + 1); if (!enc_table->names) { - enc_table->names = st_init_strcasetable(); + enc_table->names = st_init_strcasetable_with_size(ENCODING_LIST_CAPA); } #define OnigEncodingASCII_8BIT OnigEncodingASCII #define ENC_REGISTER(enc) enc_register_at(enc_table, ENCINDEX_##enc, rb_enc_name(&OnigEncoding##enc), &OnigEncoding##enc) @@ -877,11 +836,9 @@ rb_enc_autoload(rb_encoding *enc) int rb_enc_find_index(const char *name) { - int i; + int i = enc_registered(&global_enc_table, name); rb_encoding *enc; - GLOBAL_ENC_TABLE_EVAL(enc_table, i = enc_registered(enc_table, name)); - if (i < 0) { i = load_encoding(name); } @@ -1368,10 +1325,7 @@ enc_names(VALUE self) args[0] = (VALUE)rb_to_encoding_index(self); args[1] = rb_ary_new2(0); - - GLOBAL_ENC_TABLE_EVAL(enc_table, - st_foreach(enc_table->names, enc_names_i, (st_data_t)args)); - + st_foreach(global_enc_table.names, enc_names_i, (st_data_t)args); return args[1]; } @@ -1397,14 +1351,7 @@ static VALUE enc_list(VALUE klass) { VALUE ary = rb_ary_new2(0); - - RB_VM_LOCK_ENTER(); - { - rb_ary_replace(ary, rb_default_encoding_list); - rb_ary_concat(ary, rb_additional_encoding_list); - } - RB_VM_LOCK_LEAVE(); - + rb_ary_replace(ary, rb_encoding_list); return ary; } @@ -1553,15 +1500,17 @@ rb_locale_encindex(void) if (idx < 0) idx = ENCINDEX_UTF_8; - GLOBAL_ENC_TABLE_ENTER(enc_table); - if (enc_registered(enc_table, "locale") < 0) { + if (enc_registered(&global_enc_table, "locale") < 0) { # if defined _WIN32 void Init_w32_codepage(void); Init_w32_codepage(); # endif - enc_alias_internal(enc_table, "locale", idx); + GLOBAL_ENC_TABLE_ENTER(enc_table); + { + enc_alias_internal(enc_table, "locale", idx); + } + GLOBAL_ENC_TABLE_LEAVE(); } - GLOBAL_ENC_TABLE_LEAVE(); return idx; } @@ -1575,13 +1524,8 @@ rb_locale_encoding(void) int rb_filesystem_encindex(void) { - int idx; - - GLOBAL_ENC_TABLE_EVAL(enc_table, - idx = enc_registered(enc_table, "filesystem")); - - if (idx < 0) - idx = ENCINDEX_ASCII_8BIT; + int idx = enc_registered(&global_enc_table, "filesystem"); + if (idx < 0) idx = ENCINDEX_ASCII_8BIT; return idx; } @@ -1612,7 +1556,14 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha if (NIL_P(encoding)) { def->index = -1; def->enc = 0; - st_insert(enc_table->names, (st_data_t)strdup(name), + char *name_dup = strdup(name); + + st_data_t existing_name = (st_data_t)name_dup; + if (st_delete(enc_table->names, &existing_name, NULL)) { + xfree((void *)existing_name); + } + + st_insert(enc_table->names, (st_data_t)name_dup, (st_data_t)UNSPECIFIED_ENCODING); } else { @@ -1872,15 +1823,8 @@ rb_enc_name_list_i(st_data_t name, st_data_t idx, st_data_t arg) static VALUE rb_enc_name_list(VALUE klass) { - VALUE ary; - - GLOBAL_ENC_TABLE_ENTER(enc_table); - { - ary = rb_ary_new2(enc_table->names->num_entries); - st_foreach(enc_table->names, rb_enc_name_list_i, (st_data_t)ary); - } - GLOBAL_ENC_TABLE_LEAVE(); - + VALUE ary = rb_ary_new2(global_enc_table.names->num_entries); + st_foreach(global_enc_table.names, rb_enc_name_list_i, (st_data_t)ary); return ary; } @@ -1926,8 +1870,7 @@ rb_enc_aliases(VALUE klass) aliases[0] = rb_hash_new(); aliases[1] = rb_ary_new(); - GLOBAL_ENC_TABLE_EVAL(enc_table, - st_foreach(enc_table->names, rb_enc_aliases_enc_i, (st_data_t)aliases)); + st_foreach(global_enc_table.names, rb_enc_aliases_enc_i, (st_data_t)aliases); return aliases[0]; } @@ -1996,13 +1939,7 @@ Init_Encoding(void) struct enc_table *enc_table = &global_enc_table; - if (DEFAULT_ENCODING_LIST_CAPA < enc_table->count) rb_bug("DEFAULT_ENCODING_LIST_CAPA is too small"); - - list = rb_additional_encoding_list = rb_ary_new(); - RBASIC_CLEAR_CLASS(list); - rb_gc_register_mark_object(list); - - list = rb_default_encoding_list = rb_ary_new2(DEFAULT_ENCODING_LIST_CAPA); + list = rb_encoding_list = rb_ary_new2(ENCODING_LIST_CAPA); RBASIC_CLEAR_CLASS(list); rb_gc_register_mark_object(list); @@ -2024,5 +1961,5 @@ Init_encodings(void) void rb_enc_foreach_name(int (*func)(st_data_t name, st_data_t idx, st_data_t arg), st_data_t arg) { - GLOBAL_ENC_TABLE_EVAL(enc_table, st_foreach(enc_table->names, func, arg)); + st_foreach(global_enc_table.names, func, arg); } @@ -4459,7 +4459,7 @@ sum_iter(VALUE i, struct enum_sum_memo *memo) } else switch (TYPE(memo->v)) { default: sum_iter_some_value(i, memo); return; - case T_FLOAT: sum_iter_Kahan_Babuska(i, memo); return; + case T_FLOAT: case T_FIXNUM: case T_BIGNUM: case T_RATIONAL: diff --git a/enumerator.c b/enumerator.c index a0c51b585c..d587b63d32 100644 --- a/enumerator.c +++ b/enumerator.c @@ -20,6 +20,7 @@ #include "id.h" #include "internal.h" +#include "internal/class.h" #include "internal/enumerator.h" #include "internal/error.h" #include "internal/hash.h" @@ -72,6 +73,8 @@ * puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" } * # => ["0:foo", "1:bar", "2:baz"] * + * == External Iteration + * * An Enumerator can also be used as an external iterator. * For example, Enumerator#next returns the next value of the iterator * or raises StopIteration if the Enumerator is at the end. @@ -82,15 +85,44 @@ * puts e.next # => 3 * puts e.next # raises StopIteration * - * Note that enumeration sequence by +next+, +next_values+, +peek+ and - * +peek_values+ do not affect other non-external - * enumeration methods, unless the underlying iteration method itself has - * side-effect, e.g. IO#each_line. + * +next+, +next_values+, +peek+ and +peek_values+ are the only methods + * which use external iteration (and Array#zip(Enumerable-not-Array) which uses +next+). + * + * These methods do not affect other internal enumeration methods, + * unless the underlying iteration method itself has side-effect, e.g. IO#each_line. + * + * External iteration differs *significantly* from internal iteration + * due to using a Fiber: + * - The Fiber adds some overhead compared to internal enumeration. + * - The stacktrace will only include the stack from the Enumerator, not above. + * - Fiber-local variables are *not* inherited inside the Enumerator Fiber, + * which instead starts with no Fiber-local variables. + * - Fiber storage variables *are* inherited and are designed + * to handle Enumerator Fibers. Assigning to a Fiber storage variable + * only affects the current Fiber, so if you want to change state + * in the caller Fiber of the Enumerator Fiber, you need to use an + * extra indirection (e.g., use some object in the Fiber storage + * variable and mutate some ivar of it). + * + * Concretely: + * Thread.current[:fiber_local] = 1 + * Fiber[:storage_var] = 1 + * e = Enumerator.new do |y| + * p Thread.current[:fiber_local] # for external iteration: nil, for internal iteration: 1 + * p Fiber[:storage_var] # => 1, inherited + * Fiber[:storage_var] += 1 + * y << 42 + * end + * + * p e.next # => 42 + * p Fiber[:storage_var] # => 1 (it ran in a different Fiber) * - * Moreover, implementation typically uses fibers so performance could be - * slower and exception stacktraces different than expected. + * e.each { p _1 } + * p Fiber[:storage_var] # => 2 (it ran in the same Fiber/"stack" as the current Fiber) * - * You can use this to implement an internal iterator as follows: + * == Convert External Iteration to Internal Iteration + * + * You can use an external iterator to implement an internal iterator as follows: * * def ext_each(e) * while true @@ -132,7 +164,10 @@ static VALUE sym_each, sym_cycle, sym_yield; static VALUE lazy_use_super_method; +extern ID ruby_static_id_cause; + #define id_call idCall +#define id_cause ruby_static_id_cause #define id_each idEach #define id_eqq idEqq #define id_initialize idInitialize @@ -766,8 +801,7 @@ next_init(VALUE obj, struct enumerator *e) { VALUE curr = rb_fiber_current(); e->dst = curr; - // We inherit the fiber storage by reference, not by copy, by specifying Qfalse here. - e->fib = rb_fiber_new_storage(next_i, obj, Qfalse); + e->fib = rb_fiber_new(next_i, obj); e->lookahead = Qundef; } @@ -776,8 +810,16 @@ get_next_values(VALUE obj, struct enumerator *e) { VALUE curr, vs; - if (e->stop_exc) - rb_exc_raise(e->stop_exc); + if (e->stop_exc) { + VALUE exc = e->stop_exc; + VALUE result = rb_attr_get(exc, id_result); + VALUE mesg = rb_attr_get(exc, idMesg); + if (!NIL_P(mesg)) mesg = rb_str_dup(mesg); + VALUE stop_exc = rb_exc_new_str(rb_eStopIteration, mesg); + rb_ivar_set(stop_exc, id_cause, exc); + rb_ivar_set(stop_exc, id_result, result); + rb_exc_raise(stop_exc); + } curr = rb_fiber_current(); @@ -3403,7 +3445,7 @@ enumerator_plus(VALUE obj, VALUE eobj) * * The method used against each enumerable object is `each_entry` * instead of `each` so that the product of N enumerable objects - * yields exactly N arguments in each iteration. + * yields an array of exactly N elements in each iteration. * * When no enumerator is given, it calls a given block once yielding * an empty argument list. @@ -3481,9 +3523,16 @@ enum_product_allocate(VALUE klass) * e.size #=> 6 */ static VALUE -enum_product_initialize(VALUE obj, VALUE enums) +enum_product_initialize(int argc, VALUE *argv, VALUE obj) { struct enum_product *ptr; + VALUE enums = Qnil, options = Qnil; + + rb_scan_args(argc, argv, "*:", &enums, &options); + + if (!NIL_P(options) && !RHASH_EMPTY_P(options)) { + rb_exc_raise(rb_keyword_error_new("unknown", rb_hash_keys(options))); + } rb_check_frozen(obj); TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr); @@ -3589,7 +3638,7 @@ product_each(VALUE obj, struct product_state *pstate) rb_block_call(eobj, id_each_entry, 0, NULL, product_each_i, (VALUE)pstate); } else { - rb_funcallv(pstate->block, id_call, pstate->argc, pstate->argv); + rb_funcall(pstate->block, id_call, 1, rb_ary_new_from_values(pstate->argc, pstate->argv)); } return obj; @@ -3687,6 +3736,7 @@ enum_product_inspect(VALUE obj) /* * call-seq: * Enumerator.product(*enums) -> enumerator + * Enumerator.product(*enums) { |elts| ... } -> enumerator * * Generates a new enumerator object that generates a Cartesian * product of given enumerable objects. This is equivalent to @@ -3695,18 +3745,29 @@ enum_product_inspect(VALUE obj) * e = Enumerator.product(1..3, [4, 5]) * e.to_a #=> [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]] * e.size #=> 6 + * + * When a block is given, calls the block with each N-element array + * generated and returns +nil+. */ static VALUE -enumerator_s_product(VALUE klass, VALUE enums) +enumerator_s_product(int argc, VALUE *argv, VALUE klass) { - VALUE obj = enum_product_initialize(enum_product_allocate(rb_cEnumProduct), enums); + VALUE enums = Qnil, options = Qnil, block = Qnil; - if (rb_block_given_p()) { - return enum_product_run(obj, rb_block_proc()); + rb_scan_args(argc, argv, "*:&", &enums, &options, &block); + + if (!NIL_P(options) && !RHASH_EMPTY_P(options)) { + rb_exc_raise(rb_keyword_error_new("unknown", rb_hash_keys(options))); } - else { - return obj; + + VALUE obj = enum_product_initialize(argc, argv, enum_product_allocate(rb_cEnumProduct)); + + if (!NIL_P(block)) { + enum_product_run(obj, block); + return Qnil; } + + return obj; } /* @@ -4586,7 +4647,7 @@ InitVM_Enumerator(void) /* Product */ rb_cEnumProduct = rb_define_class_under(rb_cEnumerator, "Product", rb_cEnumerator); rb_define_alloc_func(rb_cEnumProduct, enum_product_allocate); - rb_define_method(rb_cEnumProduct, "initialize", enum_product_initialize, -2); + rb_define_method(rb_cEnumProduct, "initialize", enum_product_initialize, -1); rb_define_method(rb_cEnumProduct, "initialize_copy", enum_product_init_copy, 1); rb_define_method(rb_cEnumProduct, "each", enum_product_each, 0); rb_define_method(rb_cEnumProduct, "size", enum_product_size, 0); @@ -4597,7 +4658,7 @@ InitVM_Enumerator(void) rb_undef_method(rb_cEnumProduct, "next_values"); rb_undef_method(rb_cEnumProduct, "peek"); rb_undef_method(rb_cEnumProduct, "peek_values"); - rb_define_singleton_method(rb_cEnumerator, "product", enumerator_s_product, -2); + rb_define_singleton_method(rb_cEnumerator, "product", enumerator_s_product, -1); /* ArithmeticSequence */ rb_cArithSeq = rb_define_class_under(rb_cEnumerator, "ArithmeticSequence", rb_cEnumerator); @@ -420,7 +420,7 @@ rb_warn(const char *fmt, ...) void rb_category_warn(rb_warning_category_t category, const char *fmt, ...) { - if (!NIL_P(ruby_verbose)) { + if (!NIL_P(ruby_verbose) && rb_warning_category_enabled_p(category)) { with_warning_string(mesg, 0, fmt) { rb_warn_category(mesg, rb_warning_category_to_name(category)); } @@ -452,7 +452,7 @@ rb_warning(const char *fmt, ...) void rb_category_warning(rb_warning_category_t category, const char *fmt, ...) { - if (RTEST(ruby_verbose)) { + if (RTEST(ruby_verbose) && rb_warning_category_enabled_p(category)) { with_warning_string(mesg, 0, fmt) { rb_warn_category(mesg, rb_warning_category_to_name(category)); } @@ -1818,7 +1818,9 @@ name_err_init_attr(VALUE exc, VALUE recv, VALUE method) cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp); rb_ivar_set(exc, id_name, method); err_init_recv(exc, recv); - if (cfp) rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq)); + if (cfp && VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_DUMMY) { + rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq)); + } return exc; } @@ -2959,6 +2961,8 @@ ivar_copy_i(st_data_t key, st_data_t val, st_data_t exc) return ST_CONTINUE; } +void rb_exc_check_circular_cause(VALUE exc); + static VALUE exception_loader(VALUE exc, VALUE obj) { @@ -2973,6 +2977,8 @@ exception_loader(VALUE exc, VALUE obj) rb_ivar_foreach(obj, ivar_copy_i, exc); + rb_exc_check_circular_cause(exc); + if (rb_attr_get(exc, id_bt) == rb_attr_get(exc, id_bt_locations)) { rb_ivar_set(exc, id_bt_locations, Qnil); } @@ -3024,12 +3030,16 @@ Init_Exception(void) rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError); rb_define_method(rb_eSyntaxError, "initialize", syntax_error_initialize, -1); + /* RDoc will use literal name value while parsing rb_attr, + * and will render `idPath` as an attribute name without this trick */ + ID path = idPath; + /* the path failed to parse */ - rb_attr(rb_eSyntaxError, idPath, TRUE, FALSE, FALSE); + rb_attr(rb_eSyntaxError, path, TRUE, FALSE, FALSE); rb_eLoadError = rb_define_class("LoadError", rb_eScriptError); /* the path failed to load */ - rb_attr(rb_eLoadError, idPath, TRUE, FALSE, FALSE); + rb_attr(rb_eLoadError, path, TRUE, FALSE, FALSE); rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError); @@ -537,12 +537,16 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause) } if (!nocircular && !NIL_P(*cause) && !UNDEF_P(*cause) && *cause != mesg) { +#if 0 /* maybe critical for some cases */ + rb_exc_check_circular_cause(*cause); +#else VALUE c = *cause; while (!NIL_P(c = rb_attr_get(c, id_cause))) { if (c == mesg) { rb_raise(rb_eArgError, "circular causes"); } } +#endif } return mesg; } @@ -643,6 +647,10 @@ rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause) cause = get_ec_errinfo(ec); } if (cause != mesg) { + if (THROW_DATA_P(cause)) { + cause = Qnil; + } + rb_ivar_set(mesg, id_cause, cause); } } @@ -1769,6 +1777,16 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj) return obj; } +VALUE +rb_top_main_class(const char *method) +{ + VALUE klass = GET_THREAD()->top_wrapper; + + if (!klass) return rb_cObject; + rb_warning("main.%s in the wrapped load is effective only in wrapper module", method); + return klass; +} + /* * call-seq: * include(module, ...) -> self @@ -1781,13 +1799,7 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj) static VALUE top_include(int argc, VALUE *argv, VALUE self) { - rb_thread_t *th = GET_THREAD(); - - if (th->top_wrapper) { - rb_warning("main.include in the wrapped load is effective only in wrapper module"); - return rb_mod_include(argc, argv, th->top_wrapper); - } - return rb_mod_include(argc, argv, rb_cObject); + return rb_mod_include(argc, argv, rb_top_main_class("include")); } /* diff --git a/eval_error.c b/eval_error.c index e02f54e8e6..9806683000 100644 --- a/eval_error.c +++ b/eval_error.c @@ -291,6 +291,17 @@ show_cause(VALUE errinfo, VALUE str, VALUE opt, VALUE highlight, VALUE reverse, } void +rb_exc_check_circular_cause(VALUE exc) +{ + VALUE cause = exc, shown_causes = 0; + do { + if (shown_cause_p(cause, &shown_causes)) { + rb_raise(rb_eArgError, "circular causes"); + } + } while (!NIL_P(cause = rb_attr_get(cause, id_cause))); +} + +void rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse) { volatile VALUE eclass; @@ -444,7 +455,12 @@ exiting_split(VALUE errinfo, volatile int *exitcode, volatile int *sigstatus) if (NIL_P(errinfo)) return 0; - if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) { + if (THROW_DATA_P(errinfo)) { + int throw_state = ((const struct vm_throw_data *)errinfo)->throw_state; + ex = throw_state & VM_THROW_STATE_MASK; + result |= EXITING_WITH_STATUS; + } + else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) { ex = sysexit_status(errinfo); result |= EXITING_WITH_STATUS; } diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c index 2374319fe3..64f079251d 100644 --- a/ext/-test-/string/fstring.c +++ b/ext/-test-/string/fstring.c @@ -12,13 +12,13 @@ bug_s_fstring(VALUE self, VALUE str) VALUE bug_s_rb_enc_interned_str(VALUE self, VALUE encoding) { - return rb_enc_interned_str("foo", 3, RDATA(encoding)->data); + return rb_enc_interned_str("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data); } VALUE bug_s_rb_enc_str_new(VALUE self, VALUE encoding) { - return rb_enc_str_new("foo", 3, RDATA(encoding)->data); + return rb_enc_str_new("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data); } void diff --git a/ext/-test-/string/set_len.c b/ext/-test-/string/set_len.c index 219cea404c..049da2cdb5 100644 --- a/ext/-test-/string/set_len.c +++ b/ext/-test-/string/set_len.c @@ -7,8 +7,18 @@ bug_str_set_len(VALUE str, VALUE len) return str; } +static VALUE +bug_str_append(VALUE str, VALUE addendum) +{ + StringValue(addendum); + rb_str_modify_expand(str, RSTRING_LEN(addendum)); + memcpy(RSTRING_END(str), RSTRING_PTR(addendum), RSTRING_LEN(addendum)); + return str; +} + void Init_string_set_len(VALUE klass) { rb_define_method(klass, "set_len", bug_str_set_len, 1); + rb_define_method(klass, "append", bug_str_append, 1); } diff --git a/ext/date/date_core.c b/ext/date/date_core.c index fb974bd54e..21367c0ddf 100644 --- a/ext/date/date_core.c +++ b/ext/date/date_core.c @@ -64,7 +64,8 @@ static VALUE datetime_initialize(int argc, VALUE *argv, VALUE self); #define RETURN_FALSE_UNLESS_NUMERIC(obj) if(!RTEST(rb_obj_is_kind_of((obj), rb_cNumeric))) return Qfalse inline static void -check_numeric(VALUE obj, const char* field) { +check_numeric(VALUE obj, const char* field) +{ if(!RTEST(rb_obj_is_kind_of(obj, rb_cNumeric))) { rb_raise(rb_eTypeError, "invalid %s (not numeric)", field); } @@ -7437,7 +7438,8 @@ d_lite_jisx0301(VALUE self) } static VALUE -deconstruct_keys(VALUE self, VALUE keys, int is_datetime) { +deconstruct_keys(VALUE self, VALUE keys, int is_datetime) +{ VALUE h = rb_hash_new(); long i; @@ -7520,7 +7522,8 @@ deconstruct_keys(VALUE self, VALUE keys, int is_datetime) { * */ static VALUE -d_lite_deconstruct_keys(VALUE self, VALUE keys) { +d_lite_deconstruct_keys(VALUE self, VALUE keys) +{ return deconstruct_keys(self, keys, /* is_datetime=false */ 0); } @@ -8868,7 +8871,8 @@ dt_lite_jisx0301(int argc, VALUE *argv, VALUE self) * */ static VALUE -dt_lite_deconstruct_keys(VALUE self, VALUE keys) { +dt_lite_deconstruct_keys(VALUE self, VALUE keys) +{ return deconstruct_keys(self, keys, /* is_datetime=true */ 1); } diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c index 7b06a31471..f731629df1 100644 --- a/ext/date/date_strptime.c +++ b/ext/date/date_strptime.c @@ -10,28 +10,15 @@ static const char *day_names[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", - "Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat" }; +static const int ABBREVIATED_DAY_NAME_LENGTH = 3; static const char *month_names[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -static const char *merid_names[] = { - "am", "pm", - "a.m.", "p.m." -}; - -static const char *extz_pats[] = { - ":z", - "::z", - ":::z" }; +static const int ABBREVIATED_MONTH_NAME_LENGTH = 3; #define sizeof_array(o) (sizeof o / sizeof o[0]) @@ -75,7 +62,7 @@ num_pattern_p(const char *s) #define NUM_PATTERN_P() num_pattern_p(&fmt[fi + 1]) static long -read_digits(const char *s, VALUE *n, size_t width) +read_digits(const char *s, size_t slen, VALUE *n, size_t width) { size_t l; @@ -83,7 +70,7 @@ read_digits(const char *s, VALUE *n, size_t width) return 0; l = 0; - while (ISDIGIT(s[l])) { + while (l < slen && ISDIGIT(s[l])) { if (++l == width) break; } @@ -131,7 +118,7 @@ do { \ #define READ_DIGITS(n,w) \ do { \ size_t l; \ - l = read_digits(&str[si], &n, w); \ + l = read_digits(&str[si], slen - si, &n, w); \ if (l == 0) \ fail(); \ si += l; \ @@ -161,6 +148,12 @@ do { \ VALUE date_zone_to_diff(VALUE); +static inline int +head_match_p(size_t len, const char *name, const char *str, size_t slen, size_t si) +{ + return slen - si >= len && strncasecmp(name, &str[si], len) == 0; +} + static size_t date__strptime_internal(const char *str, size_t slen, const char *fmt, size_t flen, VALUE hash) @@ -168,9 +161,18 @@ date__strptime_internal(const char *str, size_t slen, size_t si, fi; int c; +#define HEAD_MATCH_P(len, name) head_match_p(len, name, str, slen, si) si = fi = 0; while (fi < flen) { + if (isspace((unsigned char)fmt[fi])) { + while (si < slen && isspace((unsigned char)str[si])) + si++; + while (++fi < flen && isspace((unsigned char)fmt[fi])); + continue; + } + + if (si >= slen) fail(); switch (fmt[fi]) { case '%': @@ -194,12 +196,11 @@ date__strptime_internal(const char *str, size_t slen, { int i; - for (i = 0; i < (int)sizeof_array(extz_pats); i++) - if (strncmp(extz_pats[i], &fmt[fi], - strlen(extz_pats[i])) == 0) { - fi += i; - goto again; - } + for (i = 1; i < 3 && fi + i < flen && fmt[fi+i] == ':'; ++i); + if (fmt[fi+i] == 'z') { + fi += i - 1; + goto again; + } fail(); } @@ -209,10 +210,12 @@ date__strptime_internal(const char *str, size_t slen, int i; for (i = 0; i < (int)sizeof_array(day_names); i++) { - size_t l = strlen(day_names[i]); - if (strncasecmp(day_names[i], &str[si], l) == 0) { + const char *day_name = day_names[i]; + size_t l = strlen(day_name); + if (HEAD_MATCH_P(l, day_name) || + HEAD_MATCH_P(l = ABBREVIATED_DAY_NAME_LENGTH, day_name)) { si += l; - set_hash("wday", INT2FIX(i % 7)); + set_hash("wday", INT2FIX(i)); goto matched; } } @@ -225,10 +228,12 @@ date__strptime_internal(const char *str, size_t slen, int i; for (i = 0; i < (int)sizeof_array(month_names); i++) { - size_t l = strlen(month_names[i]); - if (strncasecmp(month_names[i], &str[si], l) == 0) { + const char *month_name = month_names[i]; + size_t l = strlen(month_name); + if (HEAD_MATCH_P(l, month_name) || + HEAD_MATCH_P(l = ABBREVIATED_MONTH_NAME_LENGTH, month_name)) { si += l; - set_hash("mon", INT2FIX((i % 12) + 1)); + set_hash("mon", INT2FIX(i + 1)); goto matched; } } @@ -402,18 +407,19 @@ date__strptime_internal(const char *str, size_t slen, case 'P': case 'p': + if (slen - si < 2) fail(); { - int i; - - for (i = 0; i < 4; i++) { - size_t l = strlen(merid_names[i]); - if (strncasecmp(merid_names[i], &str[si], l) == 0) { - si += l; - set_hash("_merid", INT2FIX((i % 2) == 0 ? 0 : 12)); - goto matched; - } + char c = str[si]; + const int hour = (c == 'P' || c == 'p') ? 12 : 0; + if (!hour && !(c == 'A' || c == 'a')) fail(); + if ((c = str[si+1]) == '.') { + if (slen - si < 4 || str[si+3] != '.') fail(); + c = str[si += 2]; } - fail(); + if (!(c == 'M' || c == 'm')) fail(); + si += 2; + set_hash("_merid", INT2FIX(hour)); + goto matched; } case 'Q': @@ -587,7 +593,7 @@ date__strptime_internal(const char *str, size_t slen, b = rb_backref_get(); rb_match_busy(b); - m = f_match(pat, rb_usascii_str_new2(&str[si])); + m = f_match(pat, rb_usascii_str_new(&str[si], slen - si)); if (!NIL_P(m)) { VALUE s, l, o; @@ -619,22 +625,13 @@ date__strptime_internal(const char *str, size_t slen, if (str[si] != '%') fail(); si++; - if (fi < flen) - if (str[si] != fmt[fi]) + if (fi < flen) { + if (si >= slen || str[si] != fmt[fi]) fail(); - si++; + si++; + } goto matched; } - case ' ': - case '\t': - case '\n': - case '\v': - case '\f': - case '\r': - while (isspace((unsigned char)str[si])) - si++; - fi++; - break; default: ordinal: if (str[si] != fmt[fi]) diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb index 3a1dd132e7..a9fe3ce4b0 100644 --- a/ext/date/lib/date.rb +++ b/ext/date/lib/date.rb @@ -4,7 +4,7 @@ require 'date_core' class Date - VERSION = "3.3.1" # :nodoc: + VERSION = "3.3.3" # :nodoc: # call-seq: # infinite? -> false diff --git a/ext/extmk.rb b/ext/extmk.rb index cab9a519c1..4e77a7167b 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -136,6 +136,14 @@ def extract_makefile(makefile, keep = true) true end +def create_makefile(target, srcprefix = nil) + if $static and target.include?("/") + base = File.basename(target) + $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}" + end + super +end + def extmake(target, basedir = 'ext', maybestatic = true) FileUtils.mkpath target unless File.directory?(target) begin @@ -163,8 +171,6 @@ def extmake(target, basedir = 'ext', maybestatic = true) $mdir = target $srcdir = File.join($top_srcdir, basedir, $mdir) $preload = nil - $objs = [] - $srcs = [] $extso = [] makefile = "./Makefile" static = $static @@ -198,7 +204,7 @@ def extmake(target, basedir = 'ext', maybestatic = true) begin $extconf_h = nil ok &&= extract_makefile(makefile) - old_objs = $objs + old_objs = $objs || [] old_cleanfiles = $distcleanfiles | $cleanfiles conf = ["#{$srcdir}/makefile.rb", "#{$srcdir}/extconf.rb"].find {|f| File.exist?(f)} if (!ok || ($extconf_h && !File.exist?($extconf_h)) || @@ -261,6 +267,8 @@ def extmake(target, basedir = 'ext', maybestatic = true) unless $destdir.to_s.empty? or $mflags.defined?("DESTDIR") args += ["DESTDIR=" + relative_from($destdir, "../"+prefix)] end + $objs ||= [] + $srcs ||= [] if $static and ok and !$objs.empty? and !noinstall args += ["static"] $extlist.push [(maybestatic ? $static : false), target, $target, $preload] @@ -549,7 +557,13 @@ extend Module.new { end def create_makefile(*args, &block) - return super unless @gemname + unless @gemname + if $static and (target = args.first).include?("/") + base = File.basename(target) + $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}" + end + return super + end super(*args) do |conf| conf.find do |s| s.sub!(%r(^(srcdir *= *)\$\(top_srcdir\)/\.bundle/gems/[^/]+(?=/))) { diff --git a/ext/io/console/depend b/ext/io/console/depend index 06ccdde70d..36747ef583 100644 --- a/ext/io/console/depend +++ b/ext/io/console/depend @@ -185,7 +185,7 @@ win32_vk.inc: win32_vk.list -e 'n=$$F[1] and (n.strip!; /\AVK_/=~n) and' \ -e 'puts(%[#ifndef #{n}\n# define #{n} UNDEFINED_VK\n#endif])' \ $< && \ - gperf --ignore-case -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \ + gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \ | sed -f $(top_srcdir)/tool/gperf.sed \ ) > $(@F) diff --git a/ext/io/console/io-console.gemspec b/ext/io/console/io-console.gemspec index aa57f8ac52..d26a757b01 100644 --- a/ext/io/console/io-console.gemspec +++ b/ext/io/console/io-console.gemspec @@ -1,5 +1,5 @@ # -*- ruby -*- -_VERSION = "0.5.11" +_VERSION = "0.6.0" Gem::Specification.new do |s| s.name = "io-console" diff --git a/ext/io/console/win32_vk.inc b/ext/io/console/win32_vk.inc index cbec7bef15..d15b1219fb 100644 --- a/ext/io/console/win32_vk.inc +++ b/ext/io/console/win32_vk.inc @@ -480,7 +480,7 @@ # define VK_OEM_CLEAR UNDEFINED_VK #endif /* ANSI-C code produced by gperf version 3.1 */ -/* Command-line: gperf --ignore-case -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */ +/* Command-line: gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ @@ -509,18 +509,17 @@ #error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." #endif -#define gperf_offsetof(s, n) (short)offsetof(struct s##_t, s##_str##n) #line 1 "win32_vk.list" struct vktable {short ofs; unsigned short vk;}; -static const struct vktable *console_win32_vk(/*const char *, unsigned int*/); +static const struct vktable *console_win32_vk(const char *, size_t); #line 5 "win32_vk.list" struct vktable; /* maximum key range = 245, duplicates = 0 */ #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, @@ -1007,368 +1006,368 @@ console_win32_vk (register const char *str, register size_t len) {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 40 "win32_vk.list" - {gperf_offsetof(stringpool, 12), VK_UP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str12, VK_UP}, #line 52 "win32_vk.list" - {gperf_offsetof(stringpool, 13), VK_APPS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, VK_APPS}, #line 159 "win32_vk.list" - {gperf_offsetof(stringpool, 14), VK_CRSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, VK_CRSEL}, #line 34 "win32_vk.list" - {gperf_offsetof(stringpool, 15), VK_SPACE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15, VK_SPACE}, #line 95 "win32_vk.list" - {gperf_offsetof(stringpool, 16), VK_SCROLL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str16, VK_SCROLL}, #line 29 "win32_vk.list" - {gperf_offsetof(stringpool, 17), VK_ESCAPE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17, VK_ESCAPE}, #line 9 "win32_vk.list" - {gperf_offsetof(stringpool, 18), VK_CANCEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str18, VK_CANCEL}, #line 32 "win32_vk.list" - {gperf_offsetof(stringpool, 19), VK_ACCEPT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, VK_ACCEPT}, #line 66 "win32_vk.list" - {gperf_offsetof(stringpool, 20), VK_SEPARATOR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20, VK_SEPARATOR}, #line 43 "win32_vk.list" - {gperf_offsetof(stringpool, 21), VK_SELECT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, VK_SELECT}, #line 18 "win32_vk.list" - {gperf_offsetof(stringpool, 22), VK_CONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, VK_CONTROL}, #line 166 "win32_vk.list" - {gperf_offsetof(stringpool, 23), VK_OEM_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str23, VK_OEM_CLEAR}, #line 145 "win32_vk.list" - {gperf_offsetof(stringpool, 24), VK_OEM_RESET}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24, VK_OEM_RESET}, #line 155 "win32_vk.list" - {gperf_offsetof(stringpool, 25), VK_OEM_AUTO}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25, VK_OEM_AUTO}, #line 151 "win32_vk.list" - {gperf_offsetof(stringpool, 26), VK_OEM_CUSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, VK_OEM_CUSEL}, {-1}, #line 22 "win32_vk.list" - {gperf_offsetof(stringpool, 28), VK_KANA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, VK_KANA}, #line 127 "win32_vk.list" - {gperf_offsetof(stringpool, 29), VK_OEM_PLUS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, VK_OEM_PLUS}, #line 35 "win32_vk.list" - {gperf_offsetof(stringpool, 30), VK_PRIOR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, VK_PRIOR}, #line 152 "win32_vk.list" - {gperf_offsetof(stringpool, 31), VK_OEM_ATTN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, VK_OEM_ATTN}, #line 20 "win32_vk.list" - {gperf_offsetof(stringpool, 32), VK_PAUSE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32, VK_PAUSE}, #line 13 "win32_vk.list" - {gperf_offsetof(stringpool, 33), VK_BACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, VK_BACK}, #line 144 "win32_vk.list" - {gperf_offsetof(stringpool, 34), VK_PACKET}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str34, VK_PACKET}, #line 105 "win32_vk.list" - {gperf_offsetof(stringpool, 35), VK_RCONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, VK_RCONTROL}, #line 104 "win32_vk.list" - {gperf_offsetof(stringpool, 36), VK_LCONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, VK_LCONTROL}, #line 37 "win32_vk.list" - {gperf_offsetof(stringpool, 37), VK_END}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, VK_END}, #line 38 "win32_vk.list" - {gperf_offsetof(stringpool, 38), VK_HOME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str38, VK_HOME}, #line 44 "win32_vk.list" - {gperf_offsetof(stringpool, 39), VK_PRINT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str39, VK_PRINT}, #line 94 "win32_vk.list" - {gperf_offsetof(stringpool, 40), VK_NUMLOCK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str40, VK_NUMLOCK}, #line 39 "win32_vk.list" - {gperf_offsetof(stringpool, 41), VK_LEFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str41, VK_LEFT}, #line 25 "win32_vk.list" - {gperf_offsetof(stringpool, 42), VK_JUNJA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42, VK_JUNJA}, #line 19 "win32_vk.list" - {gperf_offsetof(stringpool, 43), VK_MENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43, VK_MENU}, #line 150 "win32_vk.list" - {gperf_offsetof(stringpool, 44), VK_OEM_WSCTRL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str44, VK_OEM_WSCTRL}, #line 156 "win32_vk.list" - {gperf_offsetof(stringpool, 45), VK_OEM_ENLW}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str45, VK_OEM_ENLW}, #line 36 "win32_vk.list" - {gperf_offsetof(stringpool, 46), VK_NEXT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46, VK_NEXT}, #line 51 "win32_vk.list" - {gperf_offsetof(stringpool, 47), VK_RWIN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, VK_RWIN}, #line 50 "win32_vk.list" - {gperf_offsetof(stringpool, 48), VK_LWIN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str48, VK_LWIN}, #line 21 "win32_vk.list" - {gperf_offsetof(stringpool, 49), VK_CAPITAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str49, VK_CAPITAL}, #line 49 "win32_vk.list" - {gperf_offsetof(stringpool, 50), VK_HELP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str50, VK_HELP}, #line 164 "win32_vk.list" - {gperf_offsetof(stringpool, 51), VK_NONAME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str51, VK_NONAME}, #line 8 "win32_vk.list" - {gperf_offsetof(stringpool, 52), VK_RBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, VK_RBUTTON}, #line 7 "win32_vk.list" - {gperf_offsetof(stringpool, 53), VK_LBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53, VK_LBUTTON}, #line 96 "win32_vk.list" - {gperf_offsetof(stringpool, 54), VK_OEM_NEC_EQUAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str54, VK_OEM_NEC_EQUAL}, {-1}, #line 47 "win32_vk.list" - {gperf_offsetof(stringpool, 56), VK_INSERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str56, VK_INSERT}, #line 27 "win32_vk.list" - {gperf_offsetof(stringpool, 57), VK_HANJA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57, VK_HANJA}, {-1}, {-1}, #line 46 "win32_vk.list" - {gperf_offsetof(stringpool, 60), VK_SNAPSHOT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str60, VK_SNAPSHOT}, #line 158 "win32_vk.list" - {gperf_offsetof(stringpool, 61), VK_ATTN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str61, VK_ATTN}, #line 14 "win32_vk.list" - {gperf_offsetof(stringpool, 62), VK_TAB}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, VK_TAB}, #line 157 "win32_vk.list" - {gperf_offsetof(stringpool, 63), VK_OEM_BACKTAB}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, VK_OEM_BACKTAB}, #line 143 "win32_vk.list" - {gperf_offsetof(stringpool, 64), VK_ICO_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, VK_ICO_CLEAR}, #line 30 "win32_vk.list" - {gperf_offsetof(stringpool, 65), VK_CONVERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str65, VK_CONVERT}, #line 16 "win32_vk.list" - {gperf_offsetof(stringpool, 66), VK_RETURN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66, VK_RETURN}, #line 146 "win32_vk.list" - {gperf_offsetof(stringpool, 67), VK_OEM_JUMP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67, VK_OEM_JUMP}, {-1}, {-1}, {-1}, #line 111 "win32_vk.list" - {gperf_offsetof(stringpool, 71), VK_BROWSER_STOP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, VK_BROWSER_STOP}, #line 26 "win32_vk.list" - {gperf_offsetof(stringpool, 72), VK_FINAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72, VK_FINAL}, #line 163 "win32_vk.list" - {gperf_offsetof(stringpool, 73), VK_ZOOM}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str73, VK_ZOOM}, #line 28 "win32_vk.list" - {gperf_offsetof(stringpool, 74), VK_KANJI}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str74, VK_KANJI}, #line 48 "win32_vk.list" - {gperf_offsetof(stringpool, 75), VK_DELETE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str75, VK_DELETE}, #line 128 "win32_vk.list" - {gperf_offsetof(stringpool, 76), VK_OEM_COMMA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str76, VK_OEM_COMMA}, #line 67 "win32_vk.list" - {gperf_offsetof(stringpool, 77), VK_SUBTRACT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str77, VK_SUBTRACT}, {-1}, #line 10 "win32_vk.list" - {gperf_offsetof(stringpool, 79), VK_MBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str79, VK_MBUTTON}, #line 78 "win32_vk.list" - {gperf_offsetof(stringpool, 80), VK_F9}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str80, VK_F9}, #line 17 "win32_vk.list" - {gperf_offsetof(stringpool, 81), VK_SHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str81, VK_SHIFT}, #line 103 "win32_vk.list" - {gperf_offsetof(stringpool, 82), VK_RSHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str82, VK_RSHIFT}, #line 102 "win32_vk.list" - {gperf_offsetof(stringpool, 83), VK_LSHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str83, VK_LSHIFT}, #line 65 "win32_vk.list" - {gperf_offsetof(stringpool, 84), VK_ADD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str84, VK_ADD}, #line 31 "win32_vk.list" - {gperf_offsetof(stringpool, 85), VK_NONCONVERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str85, VK_NONCONVERT}, #line 160 "win32_vk.list" - {gperf_offsetof(stringpool, 86), VK_EXSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str86, VK_EXSEL}, #line 126 "win32_vk.list" - {gperf_offsetof(stringpool, 87), VK_OEM_1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str87, VK_OEM_1}, #line 138 "win32_vk.list" - {gperf_offsetof(stringpool, 88), VK_OEM_AX}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str88, VK_OEM_AX}, #line 108 "win32_vk.list" - {gperf_offsetof(stringpool, 89), VK_BROWSER_BACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str89, VK_BROWSER_BACK}, #line 137 "win32_vk.list" - {gperf_offsetof(stringpool, 90), VK_OEM_8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str90, VK_OEM_8}, #line 129 "win32_vk.list" - {gperf_offsetof(stringpool, 91), VK_OEM_MINUS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str91, VK_OEM_MINUS}, #line 162 "win32_vk.list" - {gperf_offsetof(stringpool, 92), VK_PLAY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str92, VK_PLAY}, #line 131 "win32_vk.list" - {gperf_offsetof(stringpool, 93), VK_OEM_2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str93, VK_OEM_2}, #line 15 "win32_vk.list" - {gperf_offsetof(stringpool, 94), VK_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str94, VK_CLEAR}, #line 99 "win32_vk.list" - {gperf_offsetof(stringpool, 95), VK_OEM_FJ_TOUROKU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str95, VK_OEM_FJ_TOUROKU}, #line 147 "win32_vk.list" - {gperf_offsetof(stringpool, 96), VK_OEM_PA1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str96, VK_OEM_PA1}, #line 140 "win32_vk.list" - {gperf_offsetof(stringpool, 97), VK_ICO_HELP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str97, VK_ICO_HELP}, #line 112 "win32_vk.list" - {gperf_offsetof(stringpool, 98), VK_BROWSER_SEARCH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str98, VK_BROWSER_SEARCH}, #line 53 "win32_vk.list" - {gperf_offsetof(stringpool, 99), VK_SLEEP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str99, VK_SLEEP}, {-1}, #line 70 "win32_vk.list" - {gperf_offsetof(stringpool, 101), VK_F1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str101, VK_F1}, #line 148 "win32_vk.list" - {gperf_offsetof(stringpool, 102), VK_OEM_PA2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str102, VK_OEM_PA2}, #line 154 "win32_vk.list" - {gperf_offsetof(stringpool, 103), VK_OEM_COPY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str103, VK_OEM_COPY}, #line 77 "win32_vk.list" - {gperf_offsetof(stringpool, 104), VK_F8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str104, VK_F8}, #line 88 "win32_vk.list" - {gperf_offsetof(stringpool, 105), VK_F19}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str105, VK_F19}, #line 41 "win32_vk.list" - {gperf_offsetof(stringpool, 106), VK_RIGHT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str106, VK_RIGHT}, #line 71 "win32_vk.list" - {gperf_offsetof(stringpool, 107), VK_F2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str107, VK_F2}, #line 135 "win32_vk.list" - {gperf_offsetof(stringpool, 108), VK_OEM_6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str108, VK_OEM_6}, #line 87 "win32_vk.list" - {gperf_offsetof(stringpool, 109), VK_F18}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str109, VK_F18}, {-1}, #line 117 "win32_vk.list" - {gperf_offsetof(stringpool, 111), VK_VOLUME_UP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str111, VK_VOLUME_UP}, {-1}, {-1}, #line 120 "win32_vk.list" - {gperf_offsetof(stringpool, 114), VK_MEDIA_STOP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str114, VK_MEDIA_STOP}, #line 130 "win32_vk.list" - {gperf_offsetof(stringpool, 115), VK_OEM_PERIOD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str115, VK_OEM_PERIOD}, {-1}, #line 161 "win32_vk.list" - {gperf_offsetof(stringpool, 117), VK_EREOF}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str117, VK_EREOF}, {-1}, {-1}, {-1}, #line 114 "win32_vk.list" - {gperf_offsetof(stringpool, 121), VK_BROWSER_HOME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str121, VK_BROWSER_HOME}, #line 75 "win32_vk.list" - {gperf_offsetof(stringpool, 122), VK_F6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str122, VK_F6}, {-1}, #line 110 "win32_vk.list" - {gperf_offsetof(stringpool, 124), VK_BROWSER_REFRESH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str124, VK_BROWSER_REFRESH}, {-1}, #line 165 "win32_vk.list" - {gperf_offsetof(stringpool, 126), VK_PA1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str126, VK_PA1}, #line 142 "win32_vk.list" - {gperf_offsetof(stringpool, 127), VK_PROCESSKEY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str127, VK_PROCESSKEY}, #line 68 "win32_vk.list" - {gperf_offsetof(stringpool, 128), VK_DECIMAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str128, VK_DECIMAL}, #line 132 "win32_vk.list" - {gperf_offsetof(stringpool, 129), VK_OEM_3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str129, VK_OEM_3}, #line 107 "win32_vk.list" - {gperf_offsetof(stringpool, 130), VK_RMENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str130, VK_RMENU}, #line 106 "win32_vk.list" - {gperf_offsetof(stringpool, 131), VK_LMENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str131, VK_LMENU}, #line 98 "win32_vk.list" - {gperf_offsetof(stringpool, 132), VK_OEM_FJ_MASSHOU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str132, VK_OEM_FJ_MASSHOU}, #line 54 "win32_vk.list" - {gperf_offsetof(stringpool, 133), VK_NUMPAD0}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str133, VK_NUMPAD0}, #line 24 "win32_vk.list" - {gperf_offsetof(stringpool, 134), VK_HANGUL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str134, VK_HANGUL}, #line 63 "win32_vk.list" - {gperf_offsetof(stringpool, 135), VK_NUMPAD9}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str135, VK_NUMPAD9}, #line 23 "win32_vk.list" - {gperf_offsetof(stringpool, 136), VK_HANGEUL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str136, VK_HANGEUL}, #line 134 "win32_vk.list" - {gperf_offsetof(stringpool, 137), VK_OEM_5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str137, VK_OEM_5}, #line 149 "win32_vk.list" - {gperf_offsetof(stringpool, 138), VK_OEM_PA3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str138, VK_OEM_PA3}, #line 115 "win32_vk.list" - {gperf_offsetof(stringpool, 139), VK_VOLUME_MUTE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str139, VK_VOLUME_MUTE}, #line 133 "win32_vk.list" - {gperf_offsetof(stringpool, 140), VK_OEM_4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str140, VK_OEM_4}, #line 122 "win32_vk.list" - {gperf_offsetof(stringpool, 141), VK_LAUNCH_MAIL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str141, VK_LAUNCH_MAIL}, #line 97 "win32_vk.list" - {gperf_offsetof(stringpool, 142), VK_OEM_FJ_JISHO}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str142, VK_OEM_FJ_JISHO}, #line 72 "win32_vk.list" - {gperf_offsetof(stringpool, 143), VK_F3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str143, VK_F3}, #line 101 "win32_vk.list" - {gperf_offsetof(stringpool, 144), VK_OEM_FJ_ROYA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str144, VK_OEM_FJ_ROYA}, #line 100 "win32_vk.list" - {gperf_offsetof(stringpool, 145), VK_OEM_FJ_LOYA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str145, VK_OEM_FJ_LOYA}, {-1}, #line 42 "win32_vk.list" - {gperf_offsetof(stringpool, 147), VK_DOWN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str147, VK_DOWN}, {-1}, #line 153 "win32_vk.list" - {gperf_offsetof(stringpool, 149), VK_OEM_FINISH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str149, VK_OEM_FINISH}, {-1}, #line 74 "win32_vk.list" - {gperf_offsetof(stringpool, 151), VK_F5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str151, VK_F5}, {-1}, #line 136 "win32_vk.list" - {gperf_offsetof(stringpool, 153), VK_OEM_7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str153, VK_OEM_7}, #line 73 "win32_vk.list" - {gperf_offsetof(stringpool, 154), VK_F4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str154, VK_F4}, #line 86 "win32_vk.list" - {gperf_offsetof(stringpool, 155), VK_F17}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str155, VK_F17}, #line 55 "win32_vk.list" - {gperf_offsetof(stringpool, 156), VK_NUMPAD1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str156, VK_NUMPAD1}, #line 141 "win32_vk.list" - {gperf_offsetof(stringpool, 157), VK_ICO_00}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str157, VK_ICO_00}, {-1}, #line 62 "win32_vk.list" - {gperf_offsetof(stringpool, 159), VK_NUMPAD8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str159, VK_NUMPAD8}, {-1}, {-1}, #line 56 "win32_vk.list" - {gperf_offsetof(stringpool, 162), VK_NUMPAD2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str162, VK_NUMPAD2}, {-1}, #line 124 "win32_vk.list" - {gperf_offsetof(stringpool, 164), VK_LAUNCH_APP1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str164, VK_LAUNCH_APP1}, #line 109 "win32_vk.list" - {gperf_offsetof(stringpool, 165), VK_BROWSER_FORWARD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str165, VK_BROWSER_FORWARD}, {-1}, #line 76 "win32_vk.list" - {gperf_offsetof(stringpool, 167), VK_F7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str167, VK_F7}, {-1}, {-1}, #line 125 "win32_vk.list" - {gperf_offsetof(stringpool, 170), VK_LAUNCH_APP2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str170, VK_LAUNCH_APP2}, #line 64 "win32_vk.list" - {gperf_offsetof(stringpool, 171), VK_MULTIPLY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str171, VK_MULTIPLY}, {-1}, {-1}, #line 45 "win32_vk.list" - {gperf_offsetof(stringpool, 174), VK_EXECUTE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str174, VK_EXECUTE}, {-1}, #line 113 "win32_vk.list" - {gperf_offsetof(stringpool, 176), VK_BROWSER_FAVORITES}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str176, VK_BROWSER_FAVORITES}, #line 60 "win32_vk.list" - {gperf_offsetof(stringpool, 177), VK_NUMPAD6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str177, VK_NUMPAD6}, {-1}, #line 85 "win32_vk.list" - {gperf_offsetof(stringpool, 179), VK_F16}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str179, VK_F16}, {-1}, {-1}, #line 79 "win32_vk.list" - {gperf_offsetof(stringpool, 182), VK_F10}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str182, VK_F10}, {-1}, {-1}, #line 116 "win32_vk.list" - {gperf_offsetof(stringpool, 185), VK_VOLUME_DOWN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str185, VK_VOLUME_DOWN}, {-1}, {-1}, #line 89 "win32_vk.list" - {gperf_offsetof(stringpool, 188), VK_F20}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str188, VK_F20}, #line 119 "win32_vk.list" - {gperf_offsetof(stringpool, 189), VK_MEDIA_PREV_TRACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str189, VK_MEDIA_PREV_TRACK}, {-1}, #line 33 "win32_vk.list" - {gperf_offsetof(stringpool, 191), VK_MODECHANGE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str191, VK_MODECHANGE}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 83 "win32_vk.list" - {gperf_offsetof(stringpool, 197), VK_F14}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str197, VK_F14}, #line 57 "win32_vk.list" - {gperf_offsetof(stringpool, 198), VK_NUMPAD3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str198, VK_NUMPAD3}, #line 11 "win32_vk.list" - {gperf_offsetof(stringpool, 199), VK_XBUTTON1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str199, VK_XBUTTON1}, {-1}, {-1}, {-1}, #line 93 "win32_vk.list" - {gperf_offsetof(stringpool, 203), VK_F24}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str203, VK_F24}, {-1}, #line 12 "win32_vk.list" - {gperf_offsetof(stringpool, 205), VK_XBUTTON2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str205, VK_XBUTTON2}, #line 59 "win32_vk.list" - {gperf_offsetof(stringpool, 206), VK_NUMPAD5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str206, VK_NUMPAD5}, {-1}, {-1}, #line 58 "win32_vk.list" - {gperf_offsetof(stringpool, 209), VK_NUMPAD4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str209, VK_NUMPAD4}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 121 "win32_vk.list" - {gperf_offsetof(stringpool, 215), VK_MEDIA_PLAY_PAUSE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str215, VK_MEDIA_PLAY_PAUSE}, {-1}, #line 123 "win32_vk.list" - {gperf_offsetof(stringpool, 217), VK_LAUNCH_MEDIA_SELECT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str217, VK_LAUNCH_MEDIA_SELECT}, #line 80 "win32_vk.list" - {gperf_offsetof(stringpool, 218), VK_F11}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str218, VK_F11}, {-1}, #line 139 "win32_vk.list" - {gperf_offsetof(stringpool, 220), VK_OEM_102}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str220, VK_OEM_102}, #line 118 "win32_vk.list" - {gperf_offsetof(stringpool, 221), VK_MEDIA_NEXT_TRACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str221, VK_MEDIA_NEXT_TRACK}, #line 61 "win32_vk.list" - {gperf_offsetof(stringpool, 222), VK_NUMPAD7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str222, VK_NUMPAD7}, {-1}, #line 90 "win32_vk.list" - {gperf_offsetof(stringpool, 224), VK_F21}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str224, VK_F21}, {-1}, #line 82 "win32_vk.list" - {gperf_offsetof(stringpool, 226), VK_F13}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str226, VK_F13}, {-1}, {-1}, #line 81 "win32_vk.list" - {gperf_offsetof(stringpool, 229), VK_F12}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str229, VK_F12}, {-1}, {-1}, #line 92 "win32_vk.list" - {gperf_offsetof(stringpool, 232), VK_F23}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str232, VK_F23}, {-1}, {-1}, #line 91 "win32_vk.list" - {gperf_offsetof(stringpool, 235), VK_F22}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str235, VK_F22}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 84 "win32_vk.list" - {gperf_offsetof(stringpool, 242), VK_F15}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str242, VK_F15}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 69 "win32_vk.list" - {gperf_offsetof(stringpool, 256), VK_DIVIDE} + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str256, VK_DIVIDE} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) diff --git a/ext/io/console/win32_vk.list b/ext/io/console/win32_vk.list index 7909a4d1f0..5df3d6da57 100644 --- a/ext/io/console/win32_vk.list +++ b/ext/io/console/win32_vk.list @@ -1,6 +1,6 @@ %{ struct vktable {short ofs; unsigned short vk;}; -static const struct vktable *console_win32_vk(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/); +static const struct vktable *console_win32_vk(const char *, size_t); %} struct vktable %% diff --git a/ext/io/wait/extconf.rb b/ext/io/wait/extconf.rb index eecdcce99f..c6230b7783 100644 --- a/ext/io/wait/extconf.rb +++ b/ext/io/wait/extconf.rb @@ -5,7 +5,7 @@ if RUBY_VERSION < "2.6" File.write("Makefile", dummy_makefile($srcdir).join("")) else target = "io/wait" - have_func("rb_io_wait") + have_func("rb_io_wait", "ruby/io.h") unless macro_defined?("DOSISH", "#include <ruby.h>") have_header(ioctl_h = "sys/ioctl.h") or ioctl_h = nil fionread = %w[sys/ioctl.h sys/filio.h sys/socket.h].find do |h| diff --git a/ext/io/wait/io-wait.gemspec b/ext/io/wait/io-wait.gemspec index 7b73d64200..ebc1f6f5c7 100644 --- a/ext/io/wait/io-wait.gemspec +++ b/ext/io/wait/io-wait.gemspec @@ -1,4 +1,4 @@ -_VERSION = "0.3.0.pre" +_VERSION = "0.3.0" Gem::Specification.new do |spec| spec.name = "io-wait" diff --git a/ext/json/parser/extconf.rb b/ext/json/parser/extconf.rb index feb586e1b4..4723a02aee 100644 --- a/ext/json/parser/extconf.rb +++ b/ext/json/parser/extconf.rb @@ -1,8 +1,8 @@ # frozen_string_literal: false require 'mkmf' -have_func("rb_enc_raise", "ruby.h") -have_func("rb_enc_interned_str", "ruby.h") +have_func("rb_enc_raise", "ruby/encoding.h") +have_func("rb_enc_interned_str", "ruby/encoding.h") # checking if String#-@ (str_uminus) dedupes... ' begin diff --git a/ext/objspace/depend b/ext/objspace/depend index f83607236a..52797664e0 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -158,12 +158,14 @@ object_tracing.o: $(hdrdir)/ruby/missing.h object_tracing.o: $(hdrdir)/ruby/ruby.h object_tracing.o: $(hdrdir)/ruby/st.h object_tracing.o: $(hdrdir)/ruby/subst.h +object_tracing.o: $(top_srcdir)/gc.h object_tracing.o: $(top_srcdir)/internal.h object_tracing.o: object_tracing.c object_tracing.o: objspace.h objspace.o: $(RUBY_EXTCONF_H) objspace.o: $(arch_hdrdir)/ruby/config.h objspace.o: $(hdrdir)/ruby/assert.h +objspace.o: $(hdrdir)/ruby/atomic.h objspace.o: $(hdrdir)/ruby/backward.h objspace.o: $(hdrdir)/ruby/backward/2/assume.h objspace.o: $(hdrdir)/ruby/backward/2/attributes.h @@ -336,10 +338,16 @@ objspace.o: $(hdrdir)/ruby/regex.h objspace.o: $(hdrdir)/ruby/ruby.h objspace.o: $(hdrdir)/ruby/st.h objspace.o: $(hdrdir)/ruby/subst.h +objspace.o: $(hdrdir)/ruby/thread_native.h +objspace.o: $(top_srcdir)/ccan/check_type/check_type.h +objspace.o: $(top_srcdir)/ccan/container_of/container_of.h +objspace.o: $(top_srcdir)/ccan/list/list.h +objspace.o: $(top_srcdir)/ccan/str/str.h objspace.o: $(top_srcdir)/gc.h objspace.o: $(top_srcdir)/id_table.h objspace.o: $(top_srcdir)/internal.h objspace.o: $(top_srcdir)/internal/array.h +objspace.o: $(top_srcdir)/internal/basic_operators.h objspace.o: $(top_srcdir)/internal/class.h objspace.o: $(top_srcdir)/internal/compilers.h objspace.o: $(top_srcdir)/internal/gc.h @@ -348,10 +356,17 @@ objspace.o: $(top_srcdir)/internal/imemo.h objspace.o: $(top_srcdir)/internal/sanitizers.h objspace.o: $(top_srcdir)/internal/serial.h objspace.o: $(top_srcdir)/internal/static_assert.h +objspace.o: $(top_srcdir)/internal/vm.h objspace.o: $(top_srcdir)/internal/warnings.h +objspace.o: $(top_srcdir)/method.h objspace.o: $(top_srcdir)/node.h +objspace.o: $(top_srcdir)/ruby_assert.h +objspace.o: $(top_srcdir)/ruby_atomic.h objspace.o: $(top_srcdir)/shape.h objspace.o: $(top_srcdir)/symbol.h +objspace.o: $(top_srcdir)/thread_pthread.h +objspace.o: $(top_srcdir)/vm_core.h +objspace.o: $(top_srcdir)/vm_opts.h objspace.o: objspace.c objspace.o: {$(VPATH)}id.h objspace_dump.o: $(RUBY_EXTCONF_H) @@ -540,6 +555,7 @@ objspace_dump.o: $(top_srcdir)/id_table.h objspace_dump.o: $(top_srcdir)/internal.h objspace_dump.o: $(top_srcdir)/internal/array.h objspace_dump.o: $(top_srcdir)/internal/basic_operators.h +objspace_dump.o: $(top_srcdir)/internal/class.h objspace_dump.o: $(top_srcdir)/internal/compilers.h objspace_dump.o: $(top_srcdir)/internal/gc.h objspace_dump.o: $(top_srcdir)/internal/hash.h diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c index 0bf866a8f1..8c54d51eab 100644 --- a/ext/objspace/object_tracing.c +++ b/ext/objspace/object_tracing.c @@ -13,6 +13,7 @@ **********************************************************************/ +#include "gc.h" #include "internal.h" #include "ruby/debug.h" #include "objspace.h" @@ -121,6 +122,10 @@ freeobj_i(VALUE tpval, void *data) st_data_t v; struct allocation_info *info; + /* Modifying the st table can cause allocations, which can trigger GC. + * Since freeobj_i is called during GC, it must not trigger another GC. */ + VALUE gc_disabled = rb_gc_disable_no_rest(); + if (arg->keep_remains) { if (st_lookup(arg->object_table, obj, &v)) { info = (struct allocation_info *)v; @@ -135,6 +140,8 @@ freeobj_i(VALUE tpval, void *data) ruby_xfree(info); } } + + if (gc_disabled == Qfalse) rb_gc_enable(); } static int diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c index a4858d44b9..c3cc9a1e7b 100644 --- a/ext/objspace/objspace_dump.c +++ b/ext/objspace/objspace_dump.c @@ -13,8 +13,10 @@ **********************************************************************/ #include "gc.h" +#include "id_table.h" #include "internal.h" #include "internal/array.h" +#include "internal/class.h" #include "internal/hash.h" #include "internal/string.h" #include "internal/sanitizers.h" @@ -404,10 +406,8 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, "\""); size_t shape_id = rb_shape_get_shape_id(obj); - if (shape_id) { - dump_append(dc, ", \"shape_id\":"); - dump_append_sizet(dc, shape_id); - } + dump_append(dc, ", \"shape_id\":"); + dump_append_sizet(dc, shape_id); dump_append(dc, ", \"slot_size\":"); dump_append_sizet(dc, dc->cur_page_slot_size); @@ -498,6 +498,9 @@ dump_object(VALUE obj, struct dump_config *dc) break; case T_CLASS: + dump_append(dc, ", \"variation_count\":"); + dump_append_d(dc, RCLASS_EXT(obj)->variation_count); + case T_MODULE: if (rb_class_get_superclass(obj)) { dump_append(dc, ", \"superclass\":"); @@ -542,7 +545,10 @@ dump_object(VALUE obj, struct dump_config *dc) case T_OBJECT: dump_append(dc, ", \"ivars\":"); - dump_append_lu(dc, ROBJECT_IV_CAPACITY(obj)); + dump_append_lu(dc, ROBJECT_IV_COUNT(obj)); + if (rb_shape_obj_too_complex(obj)) { + dump_append(dc, ", \"too_complex_shape\":true"); + } break; case T_FILE: @@ -731,7 +737,7 @@ shape_i(rb_shape_t *shape, void *data) dump_append_sizet(dc, rb_shape_depth(shape)); dump_append(dc, ", \"shape_type\":"); - switch(shape->type) { + switch((enum shape_type)shape->type) { case SHAPE_ROOT: dump_append(dc, "\"ROOT\""); break; @@ -758,6 +764,9 @@ shape_i(rb_shape_t *shape, void *data) case SHAPE_T_OBJECT: dump_append(dc, "\"T_OBJECT\""); break; + case SHAPE_OBJ_TOO_COMPLEX: + dump_append(dc, "\"OBJ_TOO_COMPLEX\""); + break; default: rb_bug("[objspace] unexpected shape type"); } diff --git a/ext/openssl/History.md b/ext/openssl/History.md index a4f6bd7fd6..1e0df7dd87 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -1,3 +1,53 @@ +Version 3.1.0 +============= + +Ruby/OpenSSL 3.1 will be maintained for the lifetime of Ruby 3.2. + +Merged bug fixes in 2.2.3 and 3.0.2. Among the new features and changes are: + +Notable changes +--------------- + +* Add `OpenSSL::SSL::SSLContext#ciphersuites=` to allow setting TLS 1.3 cipher + suites. + [[GitHub #493]](https://github.com/ruby/openssl/pull/493) +* Add `OpenSSL::SSL::SSLSocket#export_keying_material` for exporting keying + material of the session, as defined in RFC 5705. + [[GitHub #530]](https://github.com/ruby/openssl/pull/530) +* Add `OpenSSL::SSL::SSLContext#keylog_cb=` for setting the TLS key logging + callback, which is useful for supporting NSS's SSLKEYLOGFILE debugging output. + [[GitHub #536]](https://github.com/ruby/openssl/pull/536) +* Remove the default digest algorithm from `OpenSSL::OCSP::BasicResponse#sign` + and `OpenSSL::OCSP::Request#sign`. Omitting the 5th parameter of these + methods used to be equivalent of specifying SHA-1. This default value is now + removed and we will let the underlying OpenSSL library decide instead. + [[GitHub #507]](https://github.com/ruby/openssl/pull/507) +* Add `OpenSSL::BN#mod_sqrt`. + [[GitHub #553]](https://github.com/ruby/openssl/pull/553) +* Allow calling `OpenSSL::Cipher#update` with an empty string. This was + prohibited to workaround an ancient bug in OpenSSL. + [[GitHub #568]](https://github.com/ruby/openssl/pull/568) +* Fix build on platforms without socket support, such as WASI. `OpenSSL::SSL` + will not be defined if OpenSSL is compiled with `OPENSSL_NO_SOCK`. + [[GitHub #558]](https://github.com/ruby/openssl/pull/558) +* Improve support for recent LibreSSL versions. This includes HKDF support in + LibreSSL 3.6 and Ed25519 support in LibreSSL 3.7. + + +Version 3.0.2 +============= + +Merged changes in 2.2.3. Additionally, the following issues are fixed by this +release. + +Bug fixes +--------- + +* Fix OpenSSL::PKey::EC#check_key not working correctly on OpenSSL 3.0. + [[GitHub #563]](https://github.com/ruby/openssl/issues/563) + [[GitHub #580]](https://github.com/ruby/openssl/pull/580) + + Version 3.0.1 ============= @@ -124,6 +174,21 @@ Notable changes [[GitHub #342]](https://github.com/ruby/openssl/issues/342) +Version 2.2.3 +============= + +Bug fixes +--------- + +* Fix serveral methods in OpenSSL::PKey::EC::Point attempting to raise an error + with an incorrect class, which would end up with a TypeError. + [[GitHub #570]](https://github.com/ruby/openssl/pull/570) +* Fix OpenSSL::PKey::EC::Point#eql? and OpenSSL::PKey::EC::Group#eql? + incorrectly treated OpenSSL's internal errors as "not equal". + [[GitHub #564]](https://github.com/ruby/openssl/pull/564) +* Fix build with LibreSSL 3.5 or later. + + Version 2.2.2 ============= diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index af20c8e00c..bc3e4d3a21 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -25,8 +25,9 @@ Logging::message "=== OpenSSL for Ruby configurator ===\n" if with_config("debug") or enable_config("debug") $defs.push("-DOSSL_DEBUG") end +$defs.push("-D""OPENSSL_SUPPRESS_DEPRECATED") -have_func("rb_io_maybe_wait") # Ruby 3.1 +have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1 Logging::message "=== Checking for system dependent stuff... ===\n" have_library("nsl", "t_open") @@ -126,7 +127,7 @@ ts_h = "openssl/ts.h".freeze ssl_h = "openssl/ssl.h".freeze # compile options -have_func("RAND_egd", "openssl/rand.h") +have_func("RAND_egd()", "openssl/rand.h") engines = %w{dynamic 4758cca aep atalla chil cswift nuron sureware ubsec padlock capi gmp gost cryptodev} engines.each { |name| @@ -137,56 +138,56 @@ engines.each { |name| if !have_struct_member("SSL", "ctx", "openssl/ssl.h") || is_libressl $defs.push("-DHAVE_OPAQUE_OPENSSL") end -have_func("EVP_MD_CTX_new", evp_h) -have_func("EVP_MD_CTX_free", evp_h) -have_func("EVP_MD_CTX_pkey_ctx", evp_h) -have_func("X509_STORE_get_ex_data", x509_h) -have_func("X509_STORE_set_ex_data", x509_h) +have_func("EVP_MD_CTX_new()", evp_h) +have_func("EVP_MD_CTX_free(NULL)", evp_h) +have_func("EVP_MD_CTX_pkey_ctx(NULL)", evp_h) +have_func("X509_STORE_get_ex_data(NULL, 0)", x509_h) +have_func("X509_STORE_set_ex_data(NULL, 0, NULL)", x509_h) have_func("X509_STORE_get_ex_new_index(0, NULL, NULL, NULL, NULL)", x509_h) -have_func("X509_CRL_get0_signature", x509_h) -have_func("X509_REQ_get0_signature", x509_h) -have_func("X509_REVOKED_get0_serialNumber", x509_h) -have_func("X509_REVOKED_get0_revocationDate", x509_h) -have_func("X509_get0_tbs_sigalg", x509_h) -have_func("X509_STORE_CTX_get0_untrusted", x509_h) -have_func("X509_STORE_CTX_get0_cert", x509_h) -have_func("X509_STORE_CTX_get0_chain", x509_h) -have_func("OCSP_SINGLERESP_get0_id", "openssl/ocsp.h") -have_func("SSL_CTX_get_ciphers", ssl_h) -have_func("X509_up_ref", x509_h) -have_func("X509_CRL_up_ref", x509_h) -have_func("X509_STORE_up_ref", x509_h) -have_func("SSL_SESSION_up_ref", ssl_h) -have_func("EVP_PKEY_up_ref", evp_h) +have_func("X509_CRL_get0_signature(NULL, NULL, NULL)", x509_h) +have_func("X509_REQ_get0_signature(NULL, NULL, NULL)", x509_h) +have_func("X509_REVOKED_get0_serialNumber(NULL)", x509_h) +have_func("X509_REVOKED_get0_revocationDate(NULL)", x509_h) +have_func("X509_get0_tbs_sigalg(NULL)", x509_h) +have_func("X509_STORE_CTX_get0_untrusted(NULL)", x509_h) +have_func("X509_STORE_CTX_get0_cert(NULL)", x509_h) +have_func("X509_STORE_CTX_get0_chain(NULL)", x509_h) +have_func("OCSP_SINGLERESP_get0_id(NULL)", "openssl/ocsp.h") +have_func("SSL_CTX_get_ciphers(NULL)", ssl_h) +have_func("X509_up_ref(NULL)", x509_h) +have_func("X509_CRL_up_ref(NULL)", x509_h) +have_func("X509_STORE_up_ref(NULL)", x509_h) +have_func("SSL_SESSION_up_ref(NULL)", ssl_h) +have_func("EVP_PKEY_up_ref(NULL)", evp_h) have_func("SSL_CTX_set_min_proto_version(NULL, 0)", ssl_h) -have_func("SSL_CTX_get_security_level", ssl_h) -have_func("X509_get0_notBefore", x509_h) -have_func("SSL_SESSION_get_protocol_version", ssl_h) -have_func("TS_STATUS_INFO_get0_status", ts_h) -have_func("TS_STATUS_INFO_get0_text", ts_h) -have_func("TS_STATUS_INFO_get0_failure_info", ts_h) +have_func("SSL_CTX_get_security_level(NULL)", ssl_h) +have_func("X509_get0_notBefore(NULL)", x509_h) +have_func("SSL_SESSION_get_protocol_version(NULL)", ssl_h) +have_func("TS_STATUS_INFO_get0_status(NULL)", ts_h) +have_func("TS_STATUS_INFO_get0_text(NULL)", ts_h) +have_func("TS_STATUS_INFO_get0_failure_info(NULL)", ts_h) have_func("TS_VERIFY_CTS_set_certs(NULL, NULL)", ts_h) -have_func("TS_VERIFY_CTX_set_store", ts_h) -have_func("TS_VERIFY_CTX_add_flags", ts_h) -have_func("TS_RESP_CTX_set_time_cb", ts_h) -have_func("EVP_PBE_scrypt", evp_h) -have_func("SSL_CTX_set_post_handshake_auth", ssl_h) +have_func("TS_VERIFY_CTX_set_store(NULL, NULL)", ts_h) +have_func("TS_VERIFY_CTX_add_flags(NULL, 0)", ts_h) +have_func("TS_RESP_CTX_set_time_cb(NULL, NULL, NULL)", ts_h) +have_func("EVP_PBE_scrypt(\"\", 0, (unsigned char *)\"\", 0, 0, 0, 0, 0, NULL, 0)", evp_h) +have_func("SSL_CTX_set_post_handshake_auth(NULL, 0)", ssl_h) # added in 1.1.1 -have_func("EVP_PKEY_check", evp_h) -have_func("EVP_PKEY_new_raw_private_key", evp_h) -have_func("SSL_CTX_set_ciphersuites", ssl_h) +have_func("EVP_PKEY_check(NULL)", evp_h) +have_func("EVP_PKEY_new_raw_private_key(0, NULL, (unsigned char *)\"\", 0)", evp_h) +have_func("SSL_CTX_set_ciphersuites(NULL, \"\")", ssl_h) # added in 3.0.0 -have_func("SSL_set0_tmp_dh_pkey", ssl_h) -have_func("ERR_get_error_all", "openssl/err.h") +have_func("SSL_set0_tmp_dh_pkey(NULL, NULL)", ssl_h) +have_func("ERR_get_error_all(NULL, NULL, NULL, NULL, NULL)", "openssl/err.h") have_func("TS_VERIFY_CTX_set_certs(NULL, NULL)", ts_h) -have_func("SSL_CTX_load_verify_file", ssl_h) -have_func("BN_check_prime", "openssl/bn.h") -have_func("EVP_MD_CTX_get0_md", evp_h) -have_func("EVP_MD_CTX_get_pkey_ctx", evp_h) -have_func("EVP_PKEY_eq", evp_h) -have_func("EVP_PKEY_dup", evp_h) +have_func("SSL_CTX_load_verify_file(NULL, \"\")", ssl_h) +have_func("BN_check_prime(NULL, NULL, NULL)", "openssl/bn.h") +have_func("EVP_MD_CTX_get0_md(NULL)", evp_h) +have_func("EVP_MD_CTX_get_pkey_ctx(NULL)", evp_h) +have_func("EVP_PKEY_eq(NULL, NULL)", evp_h) +have_func("EVP_PKEY_dup(NULL)", evp_h) Logging::message "=== Checking done. ===\n" diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb index a9103ecd27..ea8bb2a18e 100644 --- a/ext/openssl/lib/openssl/ssl.rb +++ b/ext/openssl/lib/openssl/ssl.rb @@ -11,6 +11,9 @@ =end require "openssl/buffering" + +if defined?(OpenSSL::SSL) + require "io/nonblock" require "ipaddr" require "socket" @@ -540,3 +543,5 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 end end end + +end diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb index 80597c0743..4163f55064 100644 --- a/ext/openssl/lib/openssl/version.rb +++ b/ext/openssl/lib/openssl/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module OpenSSL - VERSION = "3.1.0.pre" + VERSION = "3.1.0" end diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index d3d8be05db..8d83b69193 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "3.1.0.pre" + spec.version = "3.1.0" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.} diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 2ab8aeaebb..facb80aa73 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -52,6 +52,12 @@ (LIBRESSL_VERSION_NUMBER >= ((maj << 28) | (min << 20) | (pat << 12))) #endif +#if OSSL_OPENSSL_PREREQ(3, 0, 0) +# define OSSL_3_const const +#else +# define OSSL_3_const /* const */ +#endif + #if !defined(OPENSSL_NO_ENGINE) && !OSSL_OPENSSL_PREREQ(3, 0, 0) # define OSSL_USE_ENGINE #endif diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 38fb9fad10..10669b824c 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -92,7 +92,7 @@ void Init_ossl_ec(void); */ \ static VALUE ossl_##_keytype##_get_##_name(VALUE self) \ { \ - _type *obj; \ + const _type *obj; \ const BIGNUM *bn; \ \ Get##_type(self, obj); \ diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 696455dcfd..83c41378fe 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -178,7 +178,7 @@ ossl_dh_initialize_copy(VALUE self, VALUE other) static VALUE ossl_dh_is_public(VALUE self) { - DH *dh; + OSSL_3_const DH *dh; const BIGNUM *bn; GetDH(self, dh); @@ -197,14 +197,14 @@ ossl_dh_is_public(VALUE self) static VALUE ossl_dh_is_private(VALUE self) { - DH *dh; + OSSL_3_const DH *dh; const BIGNUM *bn; GetDH(self, dh); DH_get0_key(dh, NULL, &bn); #if !defined(OPENSSL_NO_ENGINE) - return (bn || DH_get0_engine(dh)) ? Qtrue : Qfalse; + return (bn || DH_get0_engine((DH *)dh)) ? Qtrue : Qfalse; #else return bn ? Qtrue : Qfalse; #endif @@ -223,7 +223,7 @@ ossl_dh_is_private(VALUE self) static VALUE ossl_dh_export(VALUE self) { - DH *dh; + OSSL_3_const DH *dh; BIO *out; VALUE str; @@ -252,7 +252,7 @@ ossl_dh_export(VALUE self) static VALUE ossl_dh_to_der(VALUE self) { - DH *dh; + OSSL_3_const DH *dh; unsigned char *p; long len; VALUE str; @@ -280,7 +280,7 @@ ossl_dh_to_der(VALUE self) static VALUE ossl_dh_get_params(VALUE self) { - DH *dh; + OSSL_3_const DH *dh; VALUE hash; const BIGNUM *p, *q, *g, *pub_key, *priv_key; diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 25404aa7f5..b097f8c9d2 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -24,7 +24,7 @@ } while (0) static inline int -DSA_HAS_PRIVATE(DSA *dsa) +DSA_HAS_PRIVATE(OSSL_3_const DSA *dsa) { const BIGNUM *bn; DSA_get0_key(dsa, NULL, &bn); @@ -32,7 +32,7 @@ DSA_HAS_PRIVATE(DSA *dsa) } static inline int -DSA_PRIVATE(VALUE obj, DSA *dsa) +DSA_PRIVATE(VALUE obj, OSSL_3_const DSA *dsa) { return DSA_HAS_PRIVATE(dsa) || OSSL_PKEY_IS_PRIVATE(obj); } @@ -179,7 +179,7 @@ ossl_dsa_initialize_copy(VALUE self, VALUE other) static VALUE ossl_dsa_is_public(VALUE self) { - DSA *dsa; + const DSA *dsa; const BIGNUM *bn; GetDSA(self, dsa); @@ -198,7 +198,7 @@ ossl_dsa_is_public(VALUE self) static VALUE ossl_dsa_is_private(VALUE self) { - DSA *dsa; + OSSL_3_const DSA *dsa; GetDSA(self, dsa); @@ -225,7 +225,7 @@ ossl_dsa_is_private(VALUE self) static VALUE ossl_dsa_export(int argc, VALUE *argv, VALUE self) { - DSA *dsa; + OSSL_3_const DSA *dsa; GetDSA(self, dsa); if (DSA_HAS_PRIVATE(dsa)) @@ -244,7 +244,7 @@ ossl_dsa_export(int argc, VALUE *argv, VALUE self) static VALUE ossl_dsa_to_der(VALUE self) { - DSA *dsa; + OSSL_3_const DSA *dsa; GetDSA(self, dsa); if (DSA_HAS_PRIVATE(dsa)) @@ -265,7 +265,7 @@ ossl_dsa_to_der(VALUE self) static VALUE ossl_dsa_get_params(VALUE self) { - DSA *dsa; + OSSL_3_const DSA *dsa; VALUE hash; const BIGNUM *p, *q, *g, *pub_key, *priv_key; diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index 06d59c2a4f..92842f95ac 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -227,7 +227,7 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other) static VALUE ossl_ec_key_get_group(VALUE self) { - EC_KEY *ec; + OSSL_3_const EC_KEY *ec; const EC_GROUP *group; GetEC(self, ec); @@ -272,7 +272,7 @@ ossl_ec_key_set_group(VALUE self, VALUE group_v) */ static VALUE ossl_ec_key_get_private_key(VALUE self) { - EC_KEY *ec; + OSSL_3_const EC_KEY *ec; const BIGNUM *bn; GetEC(self, ec); @@ -323,7 +323,7 @@ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key) */ static VALUE ossl_ec_key_get_public_key(VALUE self) { - EC_KEY *ec; + OSSL_3_const EC_KEY *ec; const EC_POINT *point; GetEC(self, ec); @@ -375,7 +375,7 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key) */ static VALUE ossl_ec_key_is_public(VALUE self) { - EC_KEY *ec; + OSSL_3_const EC_KEY *ec; GetEC(self, ec); @@ -391,7 +391,7 @@ static VALUE ossl_ec_key_is_public(VALUE self) */ static VALUE ossl_ec_key_is_private(VALUE self) { - EC_KEY *ec; + OSSL_3_const EC_KEY *ec; GetEC(self, ec); @@ -411,7 +411,7 @@ static VALUE ossl_ec_key_is_private(VALUE self) static VALUE ossl_ec_key_export(int argc, VALUE *argv, VALUE self) { - EC_KEY *ec; + OSSL_3_const EC_KEY *ec; GetEC(self, ec); if (EC_KEY_get0_public_key(ec) == NULL) @@ -431,7 +431,7 @@ ossl_ec_key_export(int argc, VALUE *argv, VALUE self) static VALUE ossl_ec_key_to_der(VALUE self) { - EC_KEY *ec; + OSSL_3_const EC_KEY *ec; GetEC(self, ec); if (EC_KEY_get0_public_key(ec) == NULL) @@ -483,16 +483,28 @@ static VALUE ossl_ec_key_check_key(VALUE self) #ifdef HAVE_EVP_PKEY_CHECK EVP_PKEY *pkey; EVP_PKEY_CTX *pctx; - int ret; + const EC_KEY *ec; GetPKey(self, pkey); + GetEC(self, ec); pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); if (!pctx) - ossl_raise(eDHError, "EVP_PKEY_CTX_new"); - ret = EVP_PKEY_public_check(pctx); + ossl_raise(eECError, "EVP_PKEY_CTX_new"); + + if (EC_KEY_get0_private_key(ec) != NULL) { + if (EVP_PKEY_check(pctx) != 1) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eECError, "EVP_PKEY_check"); + } + } + else { + if (EVP_PKEY_public_check(pctx) != 1) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eECError, "EVP_PKEY_public_check"); + } + } + EVP_PKEY_CTX_free(pctx); - if (ret != 1) - ossl_raise(eECError, "EVP_PKEY_public_check"); #else EC_KEY *ec; @@ -668,10 +680,11 @@ static VALUE ossl_ec_group_eql(VALUE a, VALUE b) GetECGroup(a, group1); GetECGroup(b, group2); - if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1) - return Qfalse; - - return Qtrue; + switch (EC_GROUP_cmp(group1, group2, ossl_bn_ctx)) { + case 0: return Qtrue; + case 1: return Qfalse; + default: ossl_raise(eEC_GROUP, "EC_GROUP_cmp"); + } } /* @@ -1232,10 +1245,13 @@ static VALUE ossl_ec_point_eql(VALUE a, VALUE b) GetECPoint(b, point2); GetECGroup(group_v1, group); - if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1) - return Qfalse; + switch (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx)) { + case 0: return Qtrue; + case 1: return Qfalse; + default: ossl_raise(eEC_POINT, "EC_POINT_cmp"); + } - return Qtrue; + UNREACHABLE; } /* @@ -1253,7 +1269,7 @@ static VALUE ossl_ec_point_is_at_infinity(VALUE self) switch (EC_POINT_is_at_infinity(group, point)) { case 1: return Qtrue; case 0: return Qfalse; - default: ossl_raise(cEC_POINT, "EC_POINT_is_at_infinity"); + default: ossl_raise(eEC_POINT, "EC_POINT_is_at_infinity"); } UNREACHABLE; @@ -1274,7 +1290,7 @@ static VALUE ossl_ec_point_is_on_curve(VALUE self) switch (EC_POINT_is_on_curve(group, point, ossl_bn_ctx)) { case 1: return Qtrue; case 0: return Qfalse; - default: ossl_raise(cEC_POINT, "EC_POINT_is_on_curve"); + default: ossl_raise(eEC_POINT, "EC_POINT_is_on_curve"); } UNREACHABLE; @@ -1297,7 +1313,7 @@ static VALUE ossl_ec_point_make_affine(VALUE self) rb_warn("OpenSSL::PKey::EC::Point#make_affine! is deprecated"); #if !OSSL_OPENSSL_PREREQ(3, 0, 0) if (EC_POINT_make_affine(group, point, ossl_bn_ctx) != 1) - ossl_raise(cEC_POINT, "EC_POINT_make_affine"); + ossl_raise(eEC_POINT, "EC_POINT_make_affine"); #endif return self; @@ -1316,7 +1332,7 @@ static VALUE ossl_ec_point_invert(VALUE self) GetECPointGroup(self, group); if (EC_POINT_invert(group, point, ossl_bn_ctx) != 1) - ossl_raise(cEC_POINT, "EC_POINT_invert"); + ossl_raise(eEC_POINT, "EC_POINT_invert"); return self; } @@ -1334,7 +1350,7 @@ static VALUE ossl_ec_point_set_to_infinity(VALUE self) GetECPointGroup(self, group); if (EC_POINT_set_to_infinity(group, point) != 1) - ossl_raise(cEC_POINT, "EC_POINT_set_to_infinity"); + ossl_raise(eEC_POINT, "EC_POINT_set_to_infinity"); return self; } diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 4d66010f49..072adabe62 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -24,7 +24,7 @@ } while (0) static inline int -RSA_HAS_PRIVATE(RSA *rsa) +RSA_HAS_PRIVATE(OSSL_3_const RSA *rsa) { const BIGNUM *e, *d; @@ -33,7 +33,7 @@ RSA_HAS_PRIVATE(RSA *rsa) } static inline int -RSA_PRIVATE(VALUE obj, RSA *rsa) +RSA_PRIVATE(VALUE obj, OSSL_3_const RSA *rsa) { return RSA_HAS_PRIVATE(rsa) || OSSL_PKEY_IS_PRIVATE(obj); } @@ -174,7 +174,7 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other) static VALUE ossl_rsa_is_public(VALUE self) { - RSA *rsa; + OSSL_3_const RSA *rsa; GetRSA(self, rsa); /* @@ -193,7 +193,7 @@ ossl_rsa_is_public(VALUE self) static VALUE ossl_rsa_is_private(VALUE self) { - RSA *rsa; + OSSL_3_const RSA *rsa; GetRSA(self, rsa); @@ -203,7 +203,7 @@ ossl_rsa_is_private(VALUE self) static int can_export_rsaprivatekey(VALUE self) { - RSA *rsa; + OSSL_3_const RSA *rsa; const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; GetRSA(self, rsa); @@ -453,7 +453,7 @@ ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) static VALUE ossl_rsa_get_params(VALUE self) { - RSA *rsa; + OSSL_3_const RSA *rsa; VALUE hash; const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 478ff869a4..f63992664a 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -11,11 +11,15 @@ */ #include "ossl.h" +#ifndef OPENSSL_NO_SOCK #define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0])) +#if !defined(OPENSSL_NO_NEXTPROTONEG) && !OSSL_IS_LIBRESSL +# define OSSL_USE_NEXTPROTONEG +#endif + #if !defined(TLS1_3_VERSION) && \ - defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER >= 0x3020000fL + OSSL_LIBRESSL_PREREQ(3, 2, 0) && !OSSL_LIBRESSL_PREREQ(3, 4, 0) # define TLS1_3_VERSION 0x0304 #endif @@ -30,7 +34,6 @@ } while (0) VALUE mSSL; -static VALUE mSSLExtConfig; static VALUE eSSLError; VALUE cSSLContext; VALUE cSSLSocket; @@ -291,7 +294,7 @@ ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength) if (!pkey) return NULL; - return EVP_PKEY_get0_DH(pkey); + return (DH *)EVP_PKEY_get0_DH(pkey); } #endif /* OPENSSL_NO_DH */ @@ -703,7 +706,7 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, return SSL_TLSEXT_ERR_OK; } -#ifndef OPENSSL_NO_NEXTPROTONEG +#ifdef OSSL_USE_NEXTPROTONEG static int ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) @@ -900,7 +903,7 @@ ossl_sslctx_setup(VALUE self) val = rb_attr_get(self, id_i_verify_depth); if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val)); -#ifndef OPENSSL_NO_NEXTPROTONEG +#ifdef OSSL_USE_NEXTPROTONEG val = rb_attr_get(self, id_i_npn_protocols); if (!NIL_P(val)) { VALUE encoded = ssl_encode_npn_protocols(val); @@ -1538,7 +1541,6 @@ ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self) /* * SSLSocket class */ -#ifndef OPENSSL_NO_SOCK static inline int ssl_started(SSL *ssl) { @@ -2446,7 +2448,7 @@ ossl_ssl_get_client_ca_list(VALUE self) return ossl_x509name_sk2ary(ca); } -# ifndef OPENSSL_NO_NEXTPROTONEG +# ifdef OSSL_USE_NEXTPROTONEG /* * call-seq: * ssl.npn_protocol => String | nil @@ -2566,6 +2568,7 @@ Init_ossl_ssl(void) rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable"); #endif +#ifndef OPENSSL_NO_SOCK id_call = rb_intern_const("call"); ID_callback_state = rb_intern_const("callback_state"); @@ -2588,16 +2591,6 @@ Init_ossl_ssl(void) */ mSSL = rb_define_module_under(mOSSL, "SSL"); - /* Document-module: OpenSSL::ExtConfig - * - * This module contains configuration information about the SSL extension, - * for example if socket support is enabled, or the host name TLS extension - * is enabled. Constants in this module will always be defined, but contain - * +true+ or +false+ values depending on the configuration of your OpenSSL - * installation. - */ - mSSLExtConfig = rb_define_module_under(mOSSL, "ExtConfig"); - /* Document-class: OpenSSL::SSL::SSLError * * Generic error class raised by SSLSocket and SSLContext. @@ -2760,8 +2753,6 @@ Init_ossl_ssl(void) */ rb_attr(cSSLContext, rb_intern_const("session_remove_cb"), 1, 1, Qfalse); - rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue); - /* * A callback invoked whenever a new handshake is initiated on an * established connection. May be used to disable renegotiation entirely. @@ -2782,7 +2773,7 @@ Init_ossl_ssl(void) * end */ rb_attr(cSSLContext, rb_intern_const("renegotiation_cb"), 1, 1, Qfalse); -#ifndef OPENSSL_NO_NEXTPROTONEG +#ifdef OSSL_USE_NEXTPROTONEG /* * An Enumerable of Strings. Each String represents a protocol to be * advertised as the list of supported protocols for Next Protocol @@ -2952,11 +2943,6 @@ Init_ossl_ssl(void) * Document-class: OpenSSL::SSL::SSLSocket */ cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject); -#ifdef OPENSSL_NO_SOCK - rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qtrue); - rb_define_method(cSSLSocket, "initialize", rb_f_notimplement, -1); -#else - rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qfalse); rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc); rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1); rb_undef_method(cSSLSocket, "initialize_copy"); @@ -2988,10 +2974,9 @@ Init_ossl_ssl(void) rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0); rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0); rb_define_method(cSSLSocket, "export_keying_material", ossl_ssl_export_keying_material, -1); -# ifndef OPENSSL_NO_NEXTPROTONEG +# ifdef OSSL_USE_NEXTPROTONEG rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0); # endif -#endif rb_define_const(mSSL, "VERIFY_NONE", INT2NUM(SSL_VERIFY_NONE)); rb_define_const(mSSL, "VERIFY_PEER", INT2NUM(SSL_VERIFY_PEER)); @@ -3153,4 +3138,5 @@ Init_ossl_ssl(void) DefIVarID(io); DefIVarID(context); DefIVarID(hostname); +#endif /* !defined(OPENSSL_NO_SOCK) */ } diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index 92eb1365fe..139a474b04 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -4,6 +4,7 @@ #include "ossl.h" +#ifndef OPENSSL_NO_SOCK VALUE cSSLSession; static VALUE eSSLSession; @@ -299,6 +300,7 @@ static VALUE ossl_ssl_session_to_text(VALUE self) return ossl_membio2str(out); } +#endif /* !defined(OPENSSL_NO_SOCK) */ void Init_ossl_ssl_session(void) { @@ -307,6 +309,7 @@ void Init_ossl_ssl_session(void) mSSL = rb_define_module_under(mOSSL, "SSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif +#ifndef OPENSSL_NO_SOCK cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError); @@ -324,4 +327,5 @@ void Init_ossl_ssl_session(void) rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); +#endif /* !defined(OPENSSL_NO_SOCK) */ } diff --git a/ext/racc/cparse/cparse.c b/ext/racc/cparse/cparse.c index f71ed2bba9..f752eb7749 100644 --- a/ext/racc/cparse/cparse.c +++ b/ext/racc/cparse/cparse.c @@ -7,8 +7,6 @@ This library is free software. You can distribute/modify this program under the same terms of ruby. - $originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $ - */ #include <ruby.h> @@ -24,7 +22,7 @@ Important Constants ----------------------------------------------------------------------- */ -#define RACC_VERSION "1.4.15" +#define RACC_VERSION "1.6.2" #define DEFAULT_TOKEN -1 #define ERROR_TOKEN 1 diff --git a/ext/ripper/lib/ripper/lexer.rb b/ext/ripper/lib/ripper/lexer.rb index 19c59e2ccc..a0f1cbeaa8 100644 --- a/ext/ripper/lib/ripper/lexer.rb +++ b/ext/ripper/lib/ripper/lexer.rb @@ -228,7 +228,7 @@ class Ripper def on_heredoc_end(tok) @buf.push Elem.new([lineno(), column()], __callee__, tok, state()) - @buf = @stack.pop + @buf = @stack.pop unless @stack.empty? end def _push_token(tok) @@ -242,7 +242,12 @@ class Ripper end def on_error2(mesg, elem) - @errors.push Elem.new(elem.pos, __callee__, elem.tok, elem.state, mesg) + if elem + elem = Elem.new(elem.pos, __callee__, elem.tok, elem.state, mesg) + else + elem = Elem.new([lineno(), column()], __callee__, token(), state(), mesg) + end + @errors.push elem end PARSER_EVENTS.grep(/_error\z/) do |e| arity = PARSER_EVENT_TABLE.fetch(e) diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index 269edc4dad..45b4cad38f 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -287,8 +287,9 @@ numeric_getaddrinfo(const char *node, const char *service, void rb_freeaddrinfo(struct rb_addrinfo *ai) { - if (!ai->allocated_by_malloc) - freeaddrinfo(ai->ai); + if (!ai->allocated_by_malloc) { + if (ai->ai) freeaddrinfo(ai->ai); + } else { struct addrinfo *ai1, *ai2; ai1 = ai->ai; diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index f82103a160..0054766dac 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -1340,8 +1340,9 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr) str = strio_substr(ptr, ptr->pos, e - s - w, enc); } else { - if (n < e - s) { - if (e - s < 1024) { + if (n < e - s + arg->chomp) { + /* unless chomping, RS at the end does not matter */ + if (e - s < 1024 || n == e - s) { for (p = s; p + n <= e; ++p) { if (MEMCMP(p, RSTRING_PTR(str), char, n) == 0) { e = p + n; diff --git a/ext/strscan/extconf.rb b/ext/strscan/extconf.rb index b53b63e455..3c311d2364 100644 --- a/ext/strscan/extconf.rb +++ b/ext/strscan/extconf.rb @@ -2,7 +2,8 @@ require 'mkmf' if RUBY_ENGINE == 'ruby' $INCFLAGS << " -I$(top_srcdir)" if $extmk - have_func("onig_region_memsize", "ruby.h") + have_func("onig_region_memsize(NULL)") + have_func("rb_reg_onig_match", "ruby/re.h") create_makefile 'strscan' else File.write('Makefile', dummy_makefile("").join) diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c index 9b646ab678..16d669d8a5 100644 --- a/ext/strscan/strscan.c +++ b/ext/strscan/strscan.c @@ -22,7 +22,7 @@ extern size_t onig_region_memsize(const struct re_registers *regs); #include <stdbool.h> -#define STRSCAN_VERSION "3.0.5" +#define STRSCAN_VERSION "3.0.7" /* ======================================================================= Data Type Definitions @@ -539,6 +539,68 @@ adjust_register_position(struct strscanner *p, long position) } } +/* rb_reg_onig_match is available in Ruby 3.3 and later. */ +#ifndef HAVE_RB_REG_ONIG_MATCH +static OnigPosition +rb_reg_onig_match(VALUE re, VALUE str, + OnigPosition (*match)(regex_t *reg, VALUE str, struct re_registers *regs, void *args), + void *args, struct re_registers *regs) +{ + regex_t *reg = rb_reg_prepare_re(re, str); + + bool tmpreg = reg != RREGEXP_PTR(re); + if (!tmpreg) RREGEXP(re)->usecnt++; + + OnigPosition result = match(reg, str, regs, args); + + if (!tmpreg) RREGEXP(re)->usecnt--; + if (tmpreg) { + if (RREGEXP(re)->usecnt) { + onig_free(reg); + } + else { + onig_free(RREGEXP_PTR(re)); + RREGEXP_PTR(re) = reg; + } + } + + if (result < 0) { + if (result != ONIG_MISMATCH) { + rb_raise(ScanError, "regexp buffer overflow"); + } + } + + return result; +} +#endif + +static OnigPosition +strscan_match(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr) +{ + struct strscanner *p = (struct strscanner *)args_ptr; + + return onig_match(reg, + match_target(p), + (UChar* )(CURPTR(p) + S_RESTLEN(p)), + (UChar* )CURPTR(p), + regs, + ONIG_OPTION_NONE); +} + +static OnigPosition +strscan_search(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr) +{ + struct strscanner *p = (struct strscanner *)args_ptr; + + return onig_search(reg, + match_target(p), + (UChar *)(CURPTR(p) + S_RESTLEN(p)), + (UChar *)CURPTR(p), + (UChar *)(CURPTR(p) + S_RESTLEN(p)), + regs, + ONIG_OPTION_NONE); +} + static VALUE strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly) { @@ -560,47 +622,14 @@ strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly } if (RB_TYPE_P(pattern, T_REGEXP)) { - regex_t *rb_reg_prepare_re(VALUE re, VALUE str); - regex_t *re; - long ret; - int tmpreg; - p->regex = pattern; - re = rb_reg_prepare_re(pattern, p->str); - tmpreg = re != RREGEXP_PTR(pattern); - if (!tmpreg) RREGEXP(pattern)->usecnt++; - - if (headonly) { - ret = onig_match(re, - match_target(p), - (UChar* )(CURPTR(p) + S_RESTLEN(p)), - (UChar* )CURPTR(p), - &(p->regs), - ONIG_OPTION_NONE); - } - else { - ret = onig_search(re, - match_target(p), - (UChar* )(CURPTR(p) + S_RESTLEN(p)), - (UChar* )CURPTR(p), - (UChar* )(CURPTR(p) + S_RESTLEN(p)), - &(p->regs), - ONIG_OPTION_NONE); - } - if (!tmpreg) RREGEXP(pattern)->usecnt--; - if (tmpreg) { - if (RREGEXP(pattern)->usecnt) { - onig_free(re); - } - else { - onig_free(RREGEXP_PTR(pattern)); - RREGEXP_PTR(pattern) = re; - } - } + OnigPosition ret = rb_reg_onig_match(pattern, + p->str, + headonly ? strscan_match : strscan_search, + (void *)p, + &(p->regs)); - if (ret == -2) rb_raise(ScanError, "regexp buffer overflow"); - if (ret < 0) { - /* not matched */ + if (ret == ONIG_MISMATCH) { return Qnil; } } @@ -1038,8 +1067,9 @@ strscan_empty_p(VALUE self) * This method is obsolete; use #eos? instead. * * s = StringScanner.new('test string') - * s.eos? # These two - * s.rest? # are opposites. + * # These two are opposites + * s.eos? # => false + * s.rest? # => true */ static VALUE strscan_rest_p(VALUE self) @@ -1501,7 +1531,9 @@ strscan_named_captures(VALUE self) named_captures_data data; data.self = self; data.captures = rb_hash_new(); - onig_foreach_name(RREGEXP_PTR(p->regex), named_captures_iter, &data); + if (!RB_NIL_P(p->regex)) { + onig_foreach_name(RREGEXP_PTR(p->regex), named_captures_iter, &data); + } return data.captures; } diff --git a/ext/win32/lib/win32/registry.rb b/ext/win32/lib/win32/registry.rb index b5b99ff684..bda8bb012f 100644 --- a/ext/win32/lib/win32/registry.rb +++ b/ext/win32/lib/win32/registry.rb @@ -69,7 +69,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr WCHAR_NUL = "\0".encode(WCHAR).freeze WCHAR_CR = "\r".encode(WCHAR).freeze WCHAR_SIZE = WCHAR_NUL.bytesize - LOCALE = Encoding.find(Encoding.locale_charmap) + begin + LOCALE = Encoding.find(Encoding.locale_charmap) + rescue ArgumentError + LOCALE = Encoding::UTF_8 + end class Registry @@ -740,14 +744,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr # method returns. # def write(name, type, data) - termsize = 0 case type when REG_SZ, REG_EXPAND_SZ - data = data.encode(WCHAR) - termsize = WCHAR_SIZE + data = data.encode(WCHAR) << WCHAR_NUL when REG_MULTI_SZ data = data.to_a.map {|s| s.encode(WCHAR)}.join(WCHAR_NUL) << WCHAR_NUL - termsize = WCHAR_SIZE when REG_BINARY, REG_NONE data = data.to_s when REG_DWORD @@ -759,7 +760,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr else raise TypeError, "Unsupported type #{Registry.type2name(type)}" end - API.SetValue(@hkey, name, type, data, data.bytesize + termsize) + API.SetValue(@hkey, name, type, data, data.bytesize) end # @@ -574,6 +574,7 @@ struct RMoved { VALUE flags; VALUE dummy; VALUE destination; + shape_id_t original_shape_id; }; #define RMOVED(obj) ((struct RMoved *)(obj)) @@ -2473,6 +2474,7 @@ rb_objspace_set_event_hook(const rb_event_flag_t event) static void gc_event_hook_body(rb_execution_context_t *ec, rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data) { + if (UNLIKELY(!ec->cfp)) return; const VALUE *pc = ec->cfp->pc; if (pc && VM_FRAME_RUBYFRAME_P(ec->cfp)) { /* increment PC because source line is calculated with PC-1 */ @@ -2794,6 +2796,12 @@ newobj_alloc(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx, boo return obj; } +static void +newobj_zero_slot(VALUE obj) +{ + memset((char *)obj + sizeof(struct RBasic), 0, rb_gc_obj_slot_size(obj) - sizeof(struct RBasic)); +} + ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t size_pool_idx)); static inline VALUE @@ -2824,7 +2832,7 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t * #endif newobj_init(klass, flags, wb_protected, objspace, obj); - gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_fill(obj, 0, 0, 0)); + gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_zero_slot(obj)); } RB_VM_LOCK_LEAVE_CR_LEV(cr, &lev); @@ -2965,6 +2973,7 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) ROBJECT_SET_SHAPE_ID(obj, ROBJECT_SHAPE_ID(obj) + SIZE_POOL_COUNT); #if RUBY_DEBUG + RUBY_ASSERT(!rb_shape_obj_too_complex(obj)); VALUE *ptr = ROBJECT_IVPTR(obj); for (size_t i = 0; i < ROBJECT_IV_CAPACITY(obj); i++) { ptr[i] = Qundef; @@ -3451,7 +3460,11 @@ obj_free(rb_objspace_t *objspace, VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) { + if (rb_shape_obj_too_complex(obj)) { + RB_DEBUG_COUNTER_INC(obj_obj_too_complex); + st_free_table(ROBJECT_IV_HASH(obj)); + } + else if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) { RB_DEBUG_COUNTER_INC(obj_obj_embed); } else if (ROBJ_TRANSIENT_P(obj)) { @@ -3649,7 +3662,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj) cc_table_free(objspace, obj, FALSE); rb_class_remove_from_module_subclasses(obj); rb_class_remove_from_super_subclasses(obj); -#if !USE_RVARGC +#if !RCLASS_EXT_EMBEDDED xfree(RCLASS_EXT(obj)); #endif @@ -4345,16 +4358,20 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table) VALUE objid; VALUE final; rb_control_frame_t *cfp; + VALUE *sp; long finished; } saved; + rb_execution_context_t * volatile ec = GET_EC(); #define RESTORE_FINALIZER() (\ ec->cfp = saved.cfp, \ + ec->cfp->sp = saved.sp, \ ec->errinfo = saved.errinfo) saved.errinfo = ec->errinfo; saved.objid = rb_obj_id(obj); saved.cfp = ec->cfp; + saved.sp = ec->cfp->sp; saved.finished = 0; saved.final = Qundef; @@ -4875,7 +4892,10 @@ obj_memsize_of(VALUE obj, int use_all_types) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) { + if (rb_shape_obj_too_complex(obj)) { + size += rb_st_memsize(ROBJECT_IV_HASH(obj)); + } + else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) { size += ROBJECT_IV_CAPACITY(obj) * sizeof(VALUE); } break; @@ -5222,6 +5242,8 @@ unlock_page_body(rb_objspace_t *objspace, struct heap_page_body *body) static bool try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page, VALUE src) { + GC_ASSERT(gc_is_moveable_obj(objspace, src)); + struct heap_page *src_page = GET_HEAP_PAGE(src); if (!free_page) { return false; @@ -5230,35 +5252,33 @@ try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page, /* We should return true if either src is successfully moved, or src is * unmoveable. A false return will cause the sweeping cursor to be * incremented to the next page, and src will attempt to move again */ - if (gc_is_moveable_obj(objspace, src)) { - GC_ASSERT(MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(src), src)); - - asan_unlock_freelist(free_page); - VALUE dest = (VALUE)free_page->freelist; - asan_lock_freelist(free_page); - asan_unpoison_object(dest, false); - if (!dest) { - /* if we can't get something from the freelist then the page must be - * full */ - return false; - } - free_page->freelist = RANY(dest)->as.free.next; - - GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE); + GC_ASSERT(MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(src), src)); + + asan_unlock_freelist(free_page); + VALUE dest = (VALUE)free_page->freelist; + asan_lock_freelist(free_page); + asan_unpoison_object(dest, false); + if (!dest) { + /* if we can't get something from the freelist then the page must be + * full */ + return false; + } + free_page->freelist = RANY(dest)->as.free.next; - if (src_page->slot_size > free_page->slot_size) { - objspace->rcompactor.moved_down_count_table[BUILTIN_TYPE(src)]++; - } - else if (free_page->slot_size > src_page->slot_size) { - objspace->rcompactor.moved_up_count_table[BUILTIN_TYPE(src)]++; - } - objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++; - objspace->rcompactor.total_moved++; + GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE); - gc_move(objspace, src, dest, src_page->slot_size, free_page->slot_size); - gc_pin(objspace, src); - free_page->free_slots--; + if (src_page->slot_size > free_page->slot_size) { + objspace->rcompactor.moved_down_count_table[BUILTIN_TYPE(src)]++; + } + else if (free_page->slot_size > src_page->slot_size) { + objspace->rcompactor.moved_up_count_table[BUILTIN_TYPE(src)]++; } + objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++; + objspace->rcompactor.total_moved++; + + gc_move(objspace, src, dest, src_page->slot_size, free_page->slot_size); + gc_pin(objspace, src); + free_page->free_slots--; return true; } @@ -5835,18 +5855,13 @@ gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool) size_t swept_slots = size_pool->freed_slots + size_pool->empty_slots; size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio); - /* Some size pools may have very few pages (or even no pages). These size pools - * should still have allocatable pages. */ - if (min_free_slots < gc_params.heap_init_slots) { - min_free_slots = gc_params.heap_init_slots; - } /* If we don't have enough slots and we have pages on the tomb heap, move * pages from the tomb heap to the eden heap. This may prevent page * creation thrashing (frequently allocating and deallocting pages) and * GC thrashing (running GC more frequently than required). */ struct heap_page *resurrected_page; - while (swept_slots < min_free_slots && + while ((swept_slots < min_free_slots || swept_slots < gc_params.heap_init_slots) && (resurrected_page = heap_page_resurrect(objspace, size_pool))) { swept_slots += resurrected_page->free_slots; @@ -5854,6 +5869,17 @@ gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool) heap_add_freepage(heap, resurrected_page); } + /* Some size pools may have very few pages (or even no pages). These size pools + * should still have allocatable pages. */ + if (min_free_slots < gc_params.heap_init_slots && swept_slots < gc_params.heap_init_slots) { + int multiple = size_pool->slot_size / BASE_SLOT_SIZE; + size_t extra_slots = gc_params.heap_init_slots - swept_slots; + size_t extend_page_count = CEILDIV(extra_slots * multiple, HEAP_PAGE_OBJ_LIMIT); + if (extend_page_count > size_pool->allocatable_pages) { + size_pool_allocatable_pages_set(objspace, size_pool, extend_page_count); + } + } + if (swept_slots < min_free_slots) { bool grow_heap = is_full_marking(objspace); @@ -6081,10 +6107,19 @@ invalidate_moved_plane(rb_objspace_t *objspace, struct heap_page *page, uintptr_ object = rb_gc_location(forwarding_object); + shape_id_t original_shape_id = 0; + if (RB_TYPE_P(object, T_OBJECT)) { + original_shape_id = RMOVED(forwarding_object)->original_shape_id; + } + gc_move(objspace, object, forwarding_object, GET_HEAP_PAGE(object)->slot_size, page->slot_size); /* forwarding_object is now our actual object, and "object" * is the free slot for the original page */ + if (original_shape_id) { + ROBJECT_SET_SHAPE_ID(forwarding_object, original_shape_id); + } + struct heap_page *orig_page = GET_HEAP_PAGE(object); orig_page->free_slots++; heap_page_add_freeobj(objspace, orig_page, object); @@ -7184,6 +7219,8 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj) } } +static void mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass); + static void gc_mark_children(rb_objspace_t *objspace, VALUE obj) { @@ -7230,6 +7267,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) if (!RCLASS_EXT(obj)) break; mark_m_tbl(objspace, RCLASS_M_TBL(obj)); + mark_cvc_tbl(objspace, obj); cc_table_mark(objspace, obj); for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) { gc_mark(objspace, RCLASS_IVPTR(obj)[i]); @@ -7297,14 +7335,23 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) case T_OBJECT: { - const VALUE * const ptr = ROBJECT_IVPTR(obj); - - uint32_t i, len = ROBJECT_IV_COUNT(obj); - for (i = 0; i < len; i++) { - gc_mark(objspace, ptr[i]); + rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); + if (rb_shape_obj_too_complex(obj)) { + mark_tbl_no_pin(objspace, ROBJECT_IV_HASH(obj)); } + else { + const VALUE * const ptr = ROBJECT_IVPTR(obj); - rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); + uint32_t i, len = ROBJECT_IV_COUNT(obj); + for (i = 0; i < len; i++) { + gc_mark(objspace, ptr[i]); + } + + if (LIKELY(during_gc) && + ROBJ_TRANSIENT_P(obj)) { + rb_transient_heap_mark(obj, ptr); + } + } if (shape) { VALUE klass = RBASIC_CLASS(obj); @@ -7314,11 +7361,6 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) RCLASS_EXT(klass)->max_iv_count = num_of_ivs; } } - - if (LIKELY(during_gc) && - ROBJ_TRANSIENT_P(obj)) { - rb_transient_heap_mark(obj, ptr); - } } break; @@ -8426,7 +8468,12 @@ gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, V break; case T_OBJECT: - obj_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(src)); + if (rb_shape_obj_too_complex(src)) { + return &size_pools[0]; + } + else { + obj_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(src)); + } break; case T_STRING: @@ -8447,13 +8494,30 @@ static bool gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_pool, VALUE src) { GC_ASSERT(BUILTIN_TYPE(src) != T_MOVED); + GC_ASSERT(gc_is_moveable_obj(objspace, src)); - rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(gc_compact_destination_pool(objspace, size_pool, src)); + rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, size_pool, src); + rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(dest_pool); + rb_shape_t *new_shape = NULL; + rb_shape_t *orig_shape = NULL; if (gc_compact_heap_cursors_met_p(dheap)) { return dheap != heap; } + if (RB_TYPE_P(src, T_OBJECT)) { + orig_shape = rb_shape_get_shape(src); + if (dheap != heap && !rb_shape_obj_too_complex(src)) { + rb_shape_t *initial_shape = rb_shape_get_shape_by_id((shape_id_t)((dest_pool - size_pools) + SIZE_POOL_COUNT)); + new_shape = rb_shape_traverse_from_new_root(initial_shape, orig_shape); + + if (!new_shape) { + dest_pool = size_pool; + dheap = heap; + } + } + } + while (!try_move(objspace, dheap, dheap->free_pages, src)) { struct gc_sweep_context ctx = { .page = dheap->sweeping_page, @@ -8475,9 +8539,18 @@ gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_p dheap->sweeping_page = ccan_list_next(&dheap->pages, dheap->sweeping_page, page_node); if (gc_compact_heap_cursors_met_p(dheap)) { - return false; + return dheap != heap; + } + } + + if (orig_shape) { + if (new_shape) { + VALUE dest = rb_gc_location(src); + rb_shape_set_shape(dest, new_shape); } + RMOVED(src)->original_shape_id = rb_shape_id(orig_shape); } + return true; } @@ -8495,9 +8568,11 @@ gc_compact_plane(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t * if (bitset & 1) { objspace->rcompactor.considered_count_table[BUILTIN_TYPE(vp)]++; - if (!gc_compact_move(objspace, heap, size_pool, vp)) { - //the cursors met. bubble up - return false; + if (gc_is_moveable_obj(objspace, vp)) { + if (!gc_compact_move(objspace, heap, size_pool, vp)) { + //the cursors met. bubble up + return false; + } } } p += slot_size; @@ -9206,10 +9281,23 @@ rb_gc_register_address(VALUE *addr) rb_objspace_t *objspace = &rb_objspace; struct gc_list *tmp; + VALUE obj = *addr; + tmp = ALLOC(struct gc_list); tmp->next = global_list; tmp->varptr = addr; global_list = tmp; + + /* + * Because some C extensions have assignment-then-register bugs, + * we guard `obj` here so that it would not get swept defensively. + */ + RB_GC_GUARD(obj); + if (0 && !SPECIAL_CONST_P(obj)) { + rb_warn("Object is assigned to registering address already: %"PRIsVALUE, + rb_obj_class(obj)); + rb_print_backtrace(); + } } void @@ -9847,6 +9935,17 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj) return FALSE; } +/* Used in places that could malloc, which can cause the GC to run. We need to + * temporarily disable the GC to allow the malloc to happen. */ +#define COULD_MALLOC_REGION_START() \ + GC_ASSERT(during_gc); \ + VALUE _already_disabled = rb_gc_disable_no_rest(); \ + during_gc = false; + +#define COULD_MALLOC_REGION_END() \ + during_gc = true; \ + if (_already_disabled == Qfalse) rb_objspace_gc_enable(objspace); + static VALUE gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size) { @@ -9875,11 +9974,12 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS((VALUE)src), (VALUE)src); if (FL_TEST((VALUE)src, FL_EXIVAR)) { - /* Same deal as below. Generic ivars are held in st tables. - * Resizing the table could cause a GC to happen and we can't allow it */ - VALUE already_disabled = rb_gc_disable_no_rest(); - rb_mv_generic_ivar((VALUE)src, (VALUE)dest); - if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); + /* Resizing the st table could cause a malloc */ + COULD_MALLOC_REGION_START(); + { + rb_mv_generic_ivar((VALUE)src, (VALUE)dest); + } + COULD_MALLOC_REGION_END(); } st_data_t srcid = (st_data_t)src, id; @@ -9888,13 +9988,13 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s * the object to object id mapping. */ if (st_lookup(objspace->obj_to_id_tbl, srcid, &id)) { gc_report(4, objspace, "Moving object with seen id: %p -> %p\n", (void *)src, (void *)dest); - /* inserting in the st table can cause the GC to run. We need to - * prevent re-entry in to the GC since `gc_move` is running in the GC, - * so temporarily disable the GC around the st table mutation */ - VALUE already_disabled = rb_gc_disable_no_rest(); - st_delete(objspace->obj_to_id_tbl, &srcid, 0); - st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id); - if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); + /* Resizing the st table could cause a malloc */ + COULD_MALLOC_REGION_START(); + { + st_delete(objspace->obj_to_id_tbl, &srcid, 0); + st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id); + } + COULD_MALLOC_REGION_END(); } /* Move the object */ @@ -10043,14 +10143,17 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v) { VALUE *ptr = ROBJECT_IVPTR(v); -#if USE_RVARGC - uint32_t numiv = ROBJECT_IV_CAPACITY(v); + if (rb_shape_obj_too_complex(v)) { + rb_gc_update_tbl_refs(ROBJECT_IV_HASH(v)); + return; + } +#if USE_RVARGC size_t slot_size = rb_gc_obj_slot_size(v); - size_t embed_size = rb_obj_embedded_size(numiv); + size_t embed_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(v)); if (slot_size >= embed_size && !RB_FL_TEST_RAW(v, ROBJECT_EMBED)) { // Object can be re-embedded - memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * numiv); + memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * ROBJECT_IV_COUNT(v)); RB_FL_SET_RAW(v, ROBJECT_EMBED); if (ROBJ_TRANSIENT_P(v)) { ROBJ_TRANSIENT_UNSET(v); @@ -10059,10 +10162,6 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v) xfree(ptr); } ptr = ROBJECT(v)->as.ary; - size_t size_pool_shape_id = size_pool_idx_for_size(embed_size); - rb_shape_t * initial_shape = rb_shape_get_shape_by_id((shape_id_t)size_pool_shape_id + SIZE_POOL_COUNT); - rb_shape_t * new_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_shape(v)); - rb_shape_set_shape(v, new_shape); } #endif @@ -10393,9 +10492,14 @@ static enum rb_id_table_iterator_result update_cvc_tbl_i(VALUE cvc_entry, void *data) { struct rb_cvar_class_tbl_entry *entry; + rb_objspace_t * objspace = (rb_objspace_t *)data; entry = (struct rb_cvar_class_tbl_entry *)cvc_entry; + if (entry->cref) { + TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, entry->cref); + } + entry->class_value = rb_gc_location(entry->class_value); return ID_TABLE_CONTINUE; @@ -10411,6 +10515,28 @@ update_cvc_tbl(rb_objspace_t *objspace, VALUE klass) } static enum rb_id_table_iterator_result +mark_cvc_tbl_i(VALUE cvc_entry, void *data) +{ + struct rb_cvar_class_tbl_entry *entry; + + entry = (struct rb_cvar_class_tbl_entry *)cvc_entry; + + RUBY_ASSERT(entry->cref == 0 || (BUILTIN_TYPE((VALUE)entry->cref) == T_IMEMO && IMEMO_TYPE_P(entry->cref, imemo_cref))); + rb_gc_mark((VALUE) entry->cref); + + return ID_TABLE_CONTINUE; +} + +static void +mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass) +{ + struct rb_id_table *tbl = RCLASS_CVC_TBL(klass); + if (tbl) { + rb_id_table_foreach_values(tbl, mark_cvc_tbl_i, objspace); + } +} + +static enum rb_id_table_iterator_result update_const_table(VALUE value, void *data) { rb_const_entry_t *ce = (rb_const_entry_t *)value; @@ -10537,16 +10663,18 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj) #if USE_RVARGC VALUE new_root = any->as.string.as.heap.aux.shared; rb_str_update_shared_ary(obj, old_root, new_root); +#endif + } - // if, after move the string is not embedded, and can fit in the - // slot it's been placed in, then re-embed it - if (rb_gc_obj_slot_size(obj) >= rb_str_size_as_embedded(obj)) { - if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) { - rb_str_make_embedded(obj); - } +#if USE_RVARGC + /* If, after move the string is not embedded, and can fit in the + * slot it's been placed in, then re-embed it. */ + if (rb_gc_obj_slot_size(obj) >= rb_str_size_as_embedded(obj)) { + if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) { + rb_str_make_embedded(obj); } -#endif } +#endif break; } @@ -10722,9 +10850,9 @@ gc_update_references(rb_objspace_t *objspace) #if GC_CAN_COMPILE_COMPACTION /* * call-seq: - * GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}} + * GC.latest_compact_info -> hash * - * Returns information about object moved in the most recent GC compaction. + * Returns information about object moved in the most recent \GC compaction. * * The returned hash has two keys :considered and :moved. The hash for * :considered lists the number of objects that were considered for movement @@ -10828,13 +10956,13 @@ heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data) * This function compacts objects together in Ruby's heap. It eliminates * unused space (or fragmentation) in the heap by moving objects in to that * unused space. This function returns a hash which contains statistics about - * which objects were moved. See `GC.latest_gc_info` for details about + * which objects were moved. See <tt>GC.latest_gc_info</tt> for details about * compaction statistics. * * This method is implementation specific and not expected to be implemented * in any implementation besides MRI. * - * To test whether GC compaction is supported, use the idiom: + * To test whether \GC compaction is supported, use the idiom: * * GC.respond_to?(:compact) */ @@ -11645,24 +11773,25 @@ get_envparam_double(const char *name, double *default_value, double lower_bound, } static void -gc_set_initial_pages(void) +gc_set_initial_pages(rb_objspace_t *objspace) { - size_t min_pages; - rb_objspace_t *objspace = &rb_objspace; - gc_rest(objspace); - min_pages = gc_params.heap_init_slots / HEAP_PAGE_OBJ_LIMIT; - - size_t pages_per_class = (min_pages - heap_eden_total_pages(objspace)) / SIZE_POOL_COUNT; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { rb_size_pool_t *size_pool = &size_pools[i]; - heap_add_pages(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool), pages_per_class); + if (gc_params.heap_init_slots > size_pool->eden_heap.total_slots) { + size_t slots = gc_params.heap_init_slots - size_pool->eden_heap.total_slots; + int multiple = size_pool->slot_size / BASE_SLOT_SIZE; + size_pool->allocatable_pages = slots * multiple / HEAP_PAGE_OBJ_LIMIT; + } + else { + /* We already have more slots than heap_init_slots allows, so + * prevent creating more pages. */ + size_pool->allocatable_pages = 0; + } } - - heap_add_pages(objspace, &size_pools[0], SIZE_POOL_EDEN_HEAP(&size_pools[0]), min_pages - heap_eden_total_pages(objspace)); + heap_pages_expand_sorted(objspace); } /* @@ -11718,7 +11847,7 @@ ruby_gc_set_params(void) /* RUBY_GC_HEAP_INIT_SLOTS */ if (get_envparam_size("RUBY_GC_HEAP_INIT_SLOTS", &gc_params.heap_init_slots, 0)) { - gc_set_initial_pages(); + gc_set_initial_pages(objspace); } get_envparam_double("RUBY_GC_HEAP_GROWTH_FACTOR", &gc_params.growth_factor, 1.0, 0.0, FALSE); @@ -12134,6 +12263,16 @@ objspace_malloc_prepare(rb_objspace_t *objspace, size_t size) return size; } +static bool +malloc_during_gc_p(rb_objspace_t *objspace) +{ + /* malloc is not allowed during GC when we're not using multiple ractors + * (since ractors can run while another thread is sweeping) and when we + * have the GVL (since if we don't have the GVL, we'll try to acquire the + * GVL which will block and ensure the other thread finishes GC). */ + return during_gc && !rb_multi_ractor_p() && ruby_thread_has_gvl_p(); +} + static inline void * objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size) { @@ -12192,12 +12331,24 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size) } \ } while (0) +static void +check_malloc_not_in_gc(rb_objspace_t *objspace, const char *msg) +{ + if (UNLIKELY(malloc_during_gc_p(objspace))) { + dont_gc_on(); + during_gc = false; + rb_bug("Cannot %s during GC", msg); + } +} + /* these shouldn't be called directly. * objspace_* functions do not check allocation size. */ static void * objspace_xmalloc0(rb_objspace_t *objspace, size_t size) { + check_malloc_not_in_gc(objspace, "malloc"); + void *mem; size = objspace_malloc_prepare(objspace, size); @@ -12215,6 +12366,8 @@ xmalloc2_size(const size_t count, const size_t elsize) static void * objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t old_size) { + check_malloc_not_in_gc(objspace, "realloc"); + void *mem; if (!ptr) return objspace_xmalloc0(objspace, new_size); @@ -12452,6 +12605,13 @@ ruby_xmalloc2_body(size_t n, size_t size) static void * objspace_xcalloc(rb_objspace_t *objspace, size_t size) { + if (UNLIKELY(malloc_during_gc_p(objspace))) { + rb_warn("calloc during GC detected, this could cause crashes if it triggers another GC"); +#if RGENGC_CHECK_MODE || RUBY_DEBUG + rb_bug("Cannot calloc during GC"); +#endif + } + void *mem; size = objspace_malloc_prepare(objspace, size); @@ -12506,8 +12666,16 @@ ruby_xrealloc2_body(void *ptr, size_t n, size_t size) void ruby_sized_xfree(void *x, size_t size) { - if (x) { - objspace_xfree(&rb_objspace, x, size); + if (LIKELY(x)) { + /* It's possible for a C extension's pthread destructor function set by pthread_key_create + * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in + * that case. */ + if (LIKELY(GET_VM())) { + objspace_xfree(&rb_objspace, x, size); + } + else { + ruby_mimfree(x); + } } } @@ -12701,12 +12869,47 @@ wmap_mark_map(st_data_t key, st_data_t val, st_data_t arg) } #endif +static int +wmap_replace_ref(st_data_t *key, st_data_t *value, st_data_t _argp, int existing) +{ + *key = rb_gc_location((VALUE)*key); + + VALUE *values = (VALUE *)*value; + VALUE size = values[0]; + + for (VALUE index = 1; index <= size; index++) { + values[index] = rb_gc_location(values[index]); + } + + return ST_CONTINUE; +} + +static int +wmap_foreach_replace(st_data_t key, st_data_t value, st_data_t _argp, int error) +{ + if (rb_gc_location((VALUE)key) != (VALUE)key) { + return ST_REPLACE; + } + + VALUE *values = (VALUE *)value; + VALUE size = values[0]; + + for (VALUE index = 1; index <= size; index++) { + VALUE val = values[index]; + if (rb_gc_location(val) != val) { + return ST_REPLACE; + } + } + + return ST_CONTINUE; +} + static void wmap_compact(void *ptr) { struct weakmap *w = ptr; if (w->wmap2obj) rb_gc_update_tbl_refs(w->wmap2obj); - if (w->obj2wmap) rb_gc_update_tbl_refs(w->obj2wmap); + if (w->obj2wmap) st_foreach_with_replace(w->obj2wmap, wmap_foreach_replace, wmap_replace_ref, (st_data_t)NULL); w->final = rb_gc_location(w->final); } @@ -12735,6 +12938,7 @@ wmap_free(void *ptr) st_foreach(w->obj2wmap, wmap_free_map, 0); st_free_table(w->obj2wmap); st_free_table(w->wmap2obj); + xfree(w); } static int @@ -12803,25 +13007,40 @@ wmap_live_p(rb_objspace_t *objspace, VALUE obj) } static int -wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing) +wmap_remove_inverse_ref(st_data_t *key, st_data_t *val, st_data_t arg, int existing) { - VALUE wmap, *ptr, size, i, j; if (!existing) return ST_STOP; - wmap = (VALUE)arg, ptr = (VALUE *)*value; - for (i = j = 1, size = ptr[0]; i <= size; ++i) { - if (ptr[i] != wmap) { - ptr[j++] = ptr[i]; - } - } - if (j == 1) { - ruby_sized_xfree(ptr, i * sizeof(VALUE)); + + VALUE old_ref = (VALUE)arg; + + VALUE *values = (VALUE *)*val; + VALUE size = values[0]; + + if (size == 1) { + // fast path, we only had one backref + RUBY_ASSERT(values[1] == old_ref); + ruby_sized_xfree(values, 2 * sizeof(VALUE)); return ST_DELETE; } - if (j < i) { - SIZED_REALLOC_N(ptr, VALUE, j + 1, i); - ptr[0] = j; - *value = (st_data_t)ptr; + + bool found = false; + VALUE index = 1; + for (; index <= size; index++) { + if (values[index] == old_ref) { + found = true; + break; + } + } + if (!found) return ST_STOP; + + if (size > index) { + MEMMOVE(&values[index], &values[index + 1], VALUE, size - index); } + + size -= 1; + values[0] = size; + SIZED_REALLOC_N(values, VALUE, size + 1, size + 2); + *val = (st_data_t)values; return ST_CONTINUE; } @@ -12854,7 +13073,7 @@ wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self)) wmap = (st_data_t)obj; if (st_delete(w->wmap2obj, &wmap, &orig)) { wmap = (st_data_t)obj; - st_update(w->obj2wmap, orig, wmap_final_func, wmap); + st_update(w->obj2wmap, orig, wmap_remove_inverse_ref, wmap); } return self; } @@ -13070,6 +13289,14 @@ wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing) VALUE size, *ptr, *optr; if (existing) { size = (ptr = optr = (VALUE *)*val)[0]; + + for (VALUE index = 1; index <= size; index++) { + if (ptr[index] == (VALUE)arg) { + // The reference was already registered. + return ST_STOP; + } + } + ++size; SIZED_REALLOC_N(ptr, VALUE, size + 1, size); } @@ -13085,6 +13312,23 @@ wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing) return ST_CONTINUE; } +struct wmap_aset_replace_args { + VALUE new_value; + VALUE old_value; +}; + +static int +wmap_aset_replace_value(st_data_t *key, st_data_t *val, st_data_t _args, int existing) +{ + struct wmap_aset_replace_args *args = (struct wmap_aset_replace_args *)_args; + + if (existing) { + args->old_value = *val; + } + *val = (st_data_t)args->new_value; + return ST_CONTINUE; +} + /* Creates a weak reference from the given key to the given value */ static VALUE wmap_aset(VALUE self, VALUE key, VALUE value) @@ -13099,8 +13343,25 @@ wmap_aset(VALUE self, VALUE key, VALUE value) define_final0(key, w->final); } - st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key); - st_insert(w->wmap2obj, (st_data_t)key, (st_data_t)value); + struct wmap_aset_replace_args aset_args = { + .new_value = value, + .old_value = Qundef, + }; + st_update(w->wmap2obj, (st_data_t)key, wmap_aset_replace_value, (st_data_t)&aset_args); + + // If the value is unchanged, we have nothing to do. + if (value != aset_args.old_value) { + if (!UNDEF_P(aset_args.old_value) && FL_ABLE(aset_args.old_value)) { + // That key existed and had an inverse reference, we need to clear the outdated inverse reference. + st_update(w->obj2wmap, (st_data_t)aset_args.old_value, wmap_remove_inverse_ref, key); + } + + if (FL_ABLE(value)) { + // If the value has no finalizer, we don't need to keep the inverse reference + st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key); + } + } + return nonspecial_obj_id(value); } @@ -13400,7 +13661,7 @@ gc_prof_set_heap_info(rb_objspace_t *objspace) * call-seq: * GC::Profiler.clear -> nil * - * Clears the GC profiler data. + * Clears the \GC profiler data. * */ @@ -13714,7 +13975,7 @@ gc_profile_total_time(VALUE self) * call-seq: * GC::Profiler.enabled? -> true or false * - * The current status of GC profile mode. + * The current status of \GC profile mode. */ static VALUE @@ -13728,7 +13989,7 @@ gc_profile_enable_get(VALUE self) * call-seq: * GC::Profiler.enable -> nil * - * Starts the GC profiler. + * Starts the \GC profiler. * */ @@ -13745,7 +14006,7 @@ gc_profile_enable(VALUE _) * call-seq: * GC::Profiler.disable -> nil * - * Stops the GC profiler. + * Stops the \GC profiler. * */ @@ -14050,7 +14311,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU { const rb_method_entry_t *me = &RANY(obj)->as.imemo.ment; - APPEND_F(":%s (%s%s%s%s) type:%s alias:%d owner:%p defined_class:%p", + APPEND_F(":%s (%s%s%s%s) type:%s aliased:%d owner:%p defined_class:%p", rb_id2name(me->called_id), METHOD_ENTRY_VISI(me) == METHOD_VISI_PUBLIC ? "pub" : METHOD_ENTRY_VISI(me) == METHOD_VISI_PRIVATE ? "pri" : "pro", @@ -14058,7 +14319,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU METHOD_ENTRY_CACHED(me) ? ",cc" : "", METHOD_ENTRY_INVALIDATED(me) ? ",inv" : "", me->def ? rb_method_type_name(me->def->type) : "NULL", - me->def ? me->def->alias_count : -1, + me->def ? me->def->aliased : -1, (void *)me->owner, // obj_info(me->owner), (void *)me->defined_class); //obj_info(me->defined_class))); @@ -14468,7 +14729,7 @@ Init_GC(void) { VALUE opts; - /* GC build options */ + /* \GC build options */ rb_define_const(rb_mGC, "OPTS", opts = rb_ary_new()); #define OPT(o) if (o) rb_ary_push(opts, rb_fstring_lit(#o)) OPT(GC_DEBUG); @@ -116,8 +116,6 @@ int ruby_get_stack_grow_direction(volatile VALUE *addr); const char *rb_obj_info(VALUE obj); const char *rb_raw_obj_info(char *const buff, const size_t buff_size, VALUE obj); -VALUE rb_gc_disable_no_rest(void); - struct rb_thread_struct; size_t rb_size_pool_slot_size(unsigned char pool_id); @@ -142,6 +140,8 @@ void rb_objspace_each_objects_without_setup( size_t rb_gc_obj_slot_size(VALUE obj); +VALUE rb_gc_disable_no_rest(void); + RUBY_SYMBOL_EXPORT_END #endif /* RUBY_GC_H */ @@ -6,7 +6,7 @@ # Some of the underlying methods are also available via the ObjectSpace # module. # -# You may obtain information about the operation of the GC through +# You may obtain information about the operation of the \GC through # GC::Profiler. module GC @@ -24,7 +24,7 @@ module GC # # def GC.start(full_mark: true, immediate_sweep: true); end # - # Use full_mark: false to perform a minor GC. + # Use full_mark: false to perform a minor \GC. # Use immediate_sweep: false to defer sweeping (use lazy sweep). # # Note: These keyword arguments are implementation and version dependent. They @@ -67,7 +67,7 @@ module GC # call-seq: # GC.stress -> integer, true or false # - # Returns current status of GC stress mode. + # Returns current status of \GC stress mode. def self.stress Primitive.gc_stress_get end @@ -75,9 +75,9 @@ module GC # call-seq: # GC.stress = flag -> flag # - # Updates the GC stress mode. + # Updates the \GC stress mode. # - # When stress mode is enabled, the GC is invoked at every GC opportunity: + # When stress mode is enabled, the \GC is invoked at every \GC opportunity: # all memory and object allocations. # # Enabling stress mode will degrade performance, it is only for debugging. @@ -93,9 +93,9 @@ module GC # call-seq: # GC.count -> Integer # - # The number of times GC occurred. + # The number of times \GC occurred. # - # It returns the number of times GC occurred since the process started. + # It returns the number of times \GC occurred since the process started. def self.count Primitive.gc_count end @@ -105,12 +105,12 @@ module GC # GC.stat(hash) -> Hash # GC.stat(:key) -> Numeric # - # Returns a Hash containing information about the GC. + # Returns a Hash containing information about the \GC. # # The contents of the hash are implementation specific and may change in # the future without notice. # - # The hash includes information about internal statistics about GC such as: + # The hash includes information about internal statistics about \GC such as: # # [count] # The total number of garbage collections ran since application start @@ -123,7 +123,7 @@ module GC # The number of pages that can fit into the buffer that holds references to # all pages # [heap_allocatable_pages] - # The total number of pages the application could allocate without additional GC + # The total number of pages the application could allocate without additional \GC # [heap_available_slots] # The total number of slots in all `:heap_allocated_pages` # [heap_live_slots] @@ -133,7 +133,7 @@ module GC # [heap_final_slots] # The total number of slots with pending finalizers to be run # [heap_marked_slots] - # The total number of objects marked in the last GC + # The total number of objects marked in the last \GC # [heap_eden_pages] # The total number of pages which contain at least one live slot # [heap_tomb_pages] @@ -147,9 +147,9 @@ module GC # [total_freed_objects] # The cumulative number of objects freed since application start # [malloc_increase_bytes] - # Amount of memory allocated on the heap for objects. Decreased by any GC + # Amount of memory allocated on the heap for objects. Decreased by any \GC # [malloc_increase_bytes_limit] - # When `:malloc_increase_bytes` crosses this limit, GC is triggered + # When `:malloc_increase_bytes` crosses this limit, \GC is triggered # [minor_gc_count] # The total number of minor garbage collections run since process start # [major_gc_count] @@ -165,15 +165,15 @@ module GC # The total number of objects without write barriers # [remembered_wb_unprotected_objects_limit] # When `:remembered_wb_unprotected_objects` crosses this limit, - # major GC is triggered + # major \GC is triggered # [old_objects] # Number of live, old objects which have survived at least 3 garbage collections # [old_objects_limit] - # When `:old_objects` crosses this limit, major GC is triggered + # When `:old_objects` crosses this limit, major \GC is triggered # [oldmalloc_increase_bytes] - # Amount of memory allocated on the heap for objects. Decreased by major GC + # Amount of memory allocated on the heap for objects. Decreased by major \GC # [oldmalloc_increase_bytes_limit] - # When `:old_malloc_increase_bytes` crosses this limit, major GC is triggered + # When `:old_malloc_increase_bytes` crosses this limit, major \GC is triggered # # If the optional argument, hash, is given, # it is overwritten and returned. @@ -191,7 +191,7 @@ module GC # GC.stat_heap(heap_name, hash) -> Hash # GC.stat_heap(heap_name, :key) -> Numeric # - # Returns information for memory pools in the GC. + # Returns information for memory pools in the \GC. # # If the first optional argument, +heap_name+, is passed in and not +nil+, it # returns a +Hash+ containing information about the particular memory pool. @@ -218,12 +218,12 @@ module GC Primitive.gc_stat_heap heap_name, hash_or_key end - # call-seq: - # GC.latest_gc_info -> {:gc_by=>:newobj} + # call-seq: + # GC.latest_gc_info -> hash # GC.latest_gc_info(hash) -> hash # GC.latest_gc_info(:major_by) -> :malloc # - # Returns information about the most recent garbage collection. + # Returns information about the most recent garbage collection. # # If the optional argument, hash, is given, # it is overwritten and returned. @@ -244,7 +244,7 @@ module GC # # This function expands the heap to ensure room to move all objects, # compacts the heap to make sure everything moves, updates all references, - # then performs a full GC. If any object contains a reference to a T_MOVED + # then performs a full \GC. If any object contains a reference to a T_MOVED # object, that object should be pushed on the mark stack, and will # make a SEGV. def self.verify_compaction_references(toward: nil, double_heap: false, expand_heap: false) @@ -255,9 +255,9 @@ module GC # call-seq: # GC.measure_total_time = true/false # - # Enable to measure GC time. + # Enable to measure \GC time. # You can get the result with <tt>GC.stat(:time)</tt>. - # Note that GC time measurement can cause some performance overhead. + # Note that \GC time measurement can cause some performance overhead. def self.measure_total_time=(flag) Primitive.cstmt! %{ rb_objspace.flags.measure_gc = RTEST(flag) ? TRUE : FALSE; @@ -279,7 +279,7 @@ module GC # call-seq: # GC.total_time -> int # - # Return measured GC total time in nano seconds. + # Return measured \GC total time in nano seconds. def self.total_time Primitive.cexpr! %{ ULL2NUM(rb_objspace.profile.total_time_ns) diff --git a/gems/bundled_gems b/gems/bundled_gems index 64923c906d..d37d869d41 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -1,16 +1,16 @@ # gem-name version-to-bundle repository-url [optional-commit-hash-to-test-or-defaults-to-v-version] -minitest 5.16.3 https://github.com/seattlerb/minitest -power_assert 2.0.2 https://github.com/ruby/power_assert +minitest 5.25.1 https://github.com/seattlerb/minitest +power_assert 2.0.3 https://github.com/ruby/power_assert rake 13.0.6 https://github.com/ruby/rake -test-unit 3.5.5 https://github.com/test-unit/test-unit -rexml 3.2.5 https://github.com/ruby/rexml -rss 0.2.9 https://github.com/ruby/rss -net-ftp 0.2.0 https://github.com/ruby/net-ftp -net-imap 0.3.1 https://github.com/ruby/net-imap +test-unit 3.5.7 https://github.com/test-unit/test-unit +rexml 3.3.9 https://github.com/ruby/rexml +rss 0.3.1 https://github.com/ruby/rss +net-ftp 0.2.1 https://github.com/ruby/net-ftp +net-imap 0.3.9 https://github.com/ruby/net-imap net-pop 0.1.2 https://github.com/ruby/net-pop -net-smtp 0.3.3 https://github.com/ruby/net-smtp +net-smtp 0.3.4 https://github.com/ruby/net-smtp matrix 0.4.2 https://github.com/ruby/matrix prime 0.1.2 https://github.com/ruby/prime -rbs 2.8.1 https://github.com/ruby/rbs 57c07bb9abf86e95694224174ade7521b593926a +rbs 2.8.2 https://github.com/ruby/rbs typeprof 0.21.3 https://github.com/ruby/typeprof -debug 1.7.0 https://github.com/ruby/debug +debug 1.7.1 https://github.com/ruby/debug diff --git a/gems/lib/core_assertions.rb b/gems/lib/core_assertions.rb new file mode 100644 index 0000000000..7334063885 --- /dev/null +++ b/gems/lib/core_assertions.rb @@ -0,0 +1 @@ +require_relative "../../tool/lib/core_assertions.rb" diff --git a/gems/lib/envutil.rb b/gems/lib/envutil.rb new file mode 100644 index 0000000000..d684c22cf2 --- /dev/null +++ b/gems/lib/envutil.rb @@ -0,0 +1 @@ +require_relative "../../tool/lib/envutil.rb" diff --git a/tool/dummy-rake-compiler/rake/extensiontask.rb b/gems/lib/rake/extensiontask.rb index 62b7ff8018..fdbe8d8874 100644 --- a/tool/dummy-rake-compiler/rake/extensiontask.rb +++ b/gems/lib/rake/extensiontask.rb @@ -1,8 +1,11 @@ +require "rake/tasklib" unless defined?(Rake::TaskLib) + module Rake class ExtensionTask < TaskLib def initialize(...) - task :compile do + task :compile do |args| puts "Dummy `compile` task defined in #{__FILE__}" + puts "#{args.name} => #{args.prereqs.join(' ')}" end end end @@ -28,6 +28,7 @@ #include "internal.h" #include "internal/array.h" #include "internal/bignum.h" +#include "internal/basic_operators.h" #include "internal/class.h" #include "internal/cont.h" #include "internal/error.h" @@ -93,9 +94,11 @@ rb_hash_freeze(VALUE hash) VALUE rb_cHash; static VALUE envtbl; -static ID id_hash, id_default, id_flatten_bang; +static ID id_hash, id_flatten_bang; static ID id_hash_iter_lev; +#define id_default idDefault + VALUE rb_hash_set_ifnone(VALUE hash, VALUE ifnone) { @@ -218,7 +221,7 @@ obj_any_hash(VALUE obj) return FIX2LONG(hval); } -static st_index_t +st_index_t rb_any_hash(VALUE a) { return any_hash(a, obj_any_hash); @@ -358,6 +361,9 @@ const struct st_hash_type rb_hashtype_ident = { rb_ident_hash, }; +#define RHASH_IDENTHASH_P(hash) (RHASH_TYPE(hash) == &identhash) +#define RHASH_STRING_KEY_P(hash, key) (!RHASH_IDENTHASH_P(hash) && (rb_obj_class(key) == rb_cString)) + typedef st_index_t st_hash_t; /* @@ -764,31 +770,39 @@ ar_free_and_clear_table(VALUE hash) HASH_ASSERT(RHASH_TRANSIENT_P(hash) == 0); } -static void -ar_try_convert_table(VALUE hash) -{ - if (!RHASH_AR_TABLE_P(hash)) return; - - const unsigned size = RHASH_AR_TABLE_SIZE(hash); +void rb_st_add_direct_with_hash(st_table *tab, st_data_t key, st_data_t value, st_hash_t hash); // st.c - st_table *new_tab; - st_index_t i; +enum ar_each_key_type { + ar_each_key_copy, + ar_each_key_cmp, + ar_each_key_insert, +}; - if (size < RHASH_AR_TABLE_MAX_SIZE) { - return; +static inline int +ar_each_key(ar_table *ar, int max, enum ar_each_key_type type, st_data_t *dst_keys, st_table *new_tab, st_hash_t *hashes) +{ + for (int i = 0; i < max; i++) { + ar_table_pair *pair = &ar->pairs[i]; + + switch (type) { + case ar_each_key_copy: + dst_keys[i] = pair->key; + break; + case ar_each_key_cmp: + if (dst_keys[i] != pair->key) return 1; + break; + case ar_each_key_insert: + if (UNDEF_P(pair->key)) continue; // deleted entry + rb_st_add_direct_with_hash(new_tab, pair->key, pair->val, hashes[i]); + break; + } } - new_tab = st_init_table_with_size(&objhash, size * 2); - - for (i = 0; i < RHASH_AR_TABLE_MAX_BOUND; i++) { - ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i); - st_add_direct(new_tab, pair->key, pair->val); - } - ar_free_and_clear_table(hash); - RHASH_ST_TABLE_SET(hash, new_tab); - return; + return 0; } + + static st_table * ar_force_convert_table(VALUE hash, const char *file, int line) { @@ -799,22 +813,32 @@ ar_force_convert_table(VALUE hash, const char *file, int line) } if (RHASH_AR_TABLE(hash)) { - unsigned i, bound = RHASH_AR_TABLE_BOUND(hash); - -#if defined(RHASH_CONVERT_TABLE_DEBUG) && RHASH_CONVERT_TABLE_DEBUG - rb_obj_info_dump(hash); - fprintf(stderr, "force_convert: %s:%d\n", file, line); - RB_DEBUG_COUNTER_INC(obj_hash_force_convert); -#endif + ar_table *ar = RHASH_AR_TABLE(hash); + st_hash_t hashes[RHASH_AR_TABLE_MAX_SIZE]; + unsigned int bound, size; + + // prepare hash values + do { + st_data_t keys[RHASH_AR_TABLE_MAX_SIZE]; + bound = RHASH_AR_TABLE_BOUND(hash); + size = RHASH_AR_TABLE_SIZE(hash); + ar_each_key(ar, bound, ar_each_key_copy, keys, NULL, NULL); + + for (unsigned int i = 0; i < bound; i++) { + // do_hash calls #hash method and it can modify hash object + hashes[i] = UNDEF_P(keys[i]) ? 0 : ar_do_hash(keys[i]); + } - new_tab = st_init_table_with_size(&objhash, RHASH_AR_TABLE_SIZE(hash)); + // check if modified + if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) return RHASH_ST_TABLE(hash); + if (UNLIKELY(RHASH_AR_TABLE_BOUND(hash) != bound)) continue; + if (UNLIKELY(ar_each_key(ar, bound, ar_each_key_cmp, keys, NULL, NULL))) continue; + } while (0); - for (i = 0; i < bound; i++) { - if (ar_cleared_entry(hash, i)) continue; - ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i); - st_add_direct(new_tab, pair->key, pair->val); - } + // make st + new_tab = st_init_table_with_size(&objhash, size); + ar_each_key(ar, bound, ar_each_key_insert, NULL, new_tab, hashes); ar_free_and_clear_table(hash); } else { @@ -1406,13 +1430,19 @@ iter_lev_in_ivar_set(VALUE hash, int lev) rb_ivar_set_internal(hash, id_hash_iter_lev, INT2FIX(lev)); } -static int +static inline int iter_lev_in_flags(VALUE hash) { unsigned int u = (unsigned int)((RBASIC(hash)->flags >> RHASH_LEV_SHIFT) & RHASH_LEV_MAX); return (int)u; } +static inline void +iter_lev_in_flags_set(VALUE hash, int lev) +{ + RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT)); +} + static int RHASH_ITER_LEV(VALUE hash) { @@ -1436,7 +1466,7 @@ hash_iter_lev_inc(VALUE hash) } else { lev += 1; - RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT)); + iter_lev_in_flags_set(hash, lev); if (lev == RHASH_LEV_MAX) { iter_lev_in_ivar_set(hash, lev); } @@ -1454,7 +1484,7 @@ hash_iter_lev_dec(VALUE hash) } else { HASH_ASSERT(lev > 0); - RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((lev-1) << RHASH_LEV_SHIFT)); + iter_lev_in_flags_set(hash, lev - 1); } } @@ -1533,6 +1563,16 @@ rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg) hash_verify(hash); } +void rb_st_compact_table(st_table *tab); + +static void +compact_after_delete(VALUE hash) +{ + if (RHASH_ITER_LEV(hash) == 0 && RHASH_ST_TABLE_P(hash)) { + rb_st_compact_table(RHASH_ST_TABLE(hash)); + } +} + static VALUE hash_alloc_flags(VALUE klass, VALUE flags, VALUE ifnone) { @@ -1705,7 +1745,7 @@ rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func *func, if (RHASH_AR_TABLE_P(hash)) { int result = ar_update(hash, key, func, arg); if (result == -1) { - ar_try_convert_table(hash); + ar_force_convert_table(hash, __FILE__, __LINE__); } else { return result; @@ -2070,12 +2110,26 @@ call_default_proc(VALUE proc, VALUE hash, VALUE key) return rb_proc_call_with_block(proc, 2, args, Qnil); } +static bool +rb_hash_default_unredefined(VALUE hash) +{ + VALUE klass = RBASIC_CLASS(hash); + if (LIKELY(klass == rb_cHash)) { + return !!BASIC_OP_UNREDEFINED_P(BOP_DEFAULT, HASH_REDEFINED_OP_FLAG); + } + else { + return LIKELY(rb_method_basic_definition_p(klass, id_default)); + } +} + VALUE rb_hash_default_value(VALUE hash, VALUE key) { - if (LIKELY(rb_method_basic_definition_p(CLASS_OF(hash), id_default))) { + RUBY_ASSERT(RB_TYPE_P(hash, T_HASH)); + + if (LIKELY(rb_hash_default_unredefined(hash))) { VALUE ifnone = RHASH_IFNONE(hash); - if (!FL_TEST(hash, RHASH_PROC_DEFAULT)) return ifnone; + if (LIKELY(!FL_TEST_RAW(hash, RHASH_PROC_DEFAULT))) return ifnone; if (UNDEF_P(key)) return Qnil; return call_default_proc(ifnone, hash, key); } @@ -2446,6 +2500,7 @@ rb_hash_delete_m(VALUE hash, VALUE key) val = rb_hash_delete_entry(hash, key); if (!UNDEF_P(val)) { + compact_after_delete(hash); return val; } else { @@ -2566,6 +2621,7 @@ rb_hash_delete_if(VALUE hash) rb_hash_modify_check(hash); if (!RHASH_TABLE_EMPTY_P(hash)) { rb_hash_foreach(hash, delete_if_i, hash); + compact_after_delete(hash); } return hash; } @@ -2629,6 +2685,7 @@ rb_hash_reject(VALUE hash) result = hash_dup_with_compare_by_id(hash); if (!RHASH_EMPTY_P(hash)) { rb_hash_foreach(result, delete_if_i, result); + compact_after_delete(result); } return result; } @@ -2688,6 +2745,7 @@ rb_hash_except(int argc, VALUE *argv, VALUE hash) key = argv[i]; rb_hash_delete(result, key); } + compact_after_delete(result); return result; } @@ -2785,6 +2843,7 @@ rb_hash_select(VALUE hash) result = hash_dup_with_compare_by_id(hash); if (!RHASH_EMPTY_P(hash)) { rb_hash_foreach(result, keep_if_i, result); + compact_after_delete(result); } return result; } @@ -2876,6 +2935,7 @@ rb_hash_clear(VALUE hash) } else { st_clear(RHASH_ST_TABLE(hash)); + compact_after_delete(hash); } return hash; @@ -2949,7 +3009,7 @@ rb_hash_aset(VALUE hash, VALUE key, VALUE val) ar_alloc_table(hash); } - if (RHASH_TYPE(hash) == &identhash || rb_obj_class(key) != rb_cString) { + if (!RHASH_STRING_KEY_P(hash, key)) { RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val); } else { @@ -3321,6 +3381,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash) rb_ary_clear(pairs); rb_hash_clear(new_keys); } + compact_after_delete(hash); return hash; } @@ -3371,6 +3432,7 @@ rb_hash_transform_values(VALUE hash) if (!RHASH_EMPTY_P(hash)) { rb_hash_stlike_foreach_with_replace(result, transform_values_foreach_func, transform_values_foreach_replace, result); + compact_after_delete(result); } return result; @@ -3907,18 +3969,9 @@ rb_hash_invert(VALUE hash) } static int -rb_hash_update_callback(st_data_t *key, st_data_t *value, struct update_arg *arg, int existing) -{ - *value = arg->arg; - return ST_CONTINUE; -} - -NOINSERT_UPDATE_CALLBACK(rb_hash_update_callback) - -static int rb_hash_update_i(VALUE key, VALUE value, VALUE hash) { - RHASH_UPDATE(hash, key, rb_hash_update_callback, value); + rb_hash_aset(hash, key, value); return ST_CONTINUE; } @@ -3930,6 +3983,9 @@ rb_hash_update_block_callback(st_data_t *key, st_data_t *value, struct update_ar if (existing) { newvalue = (st_data_t)rb_yield_values(3, (VALUE)*key, (VALUE)*value, (VALUE)newvalue); } + else if (RHASH_STRING_KEY_P(arg->hash, *key) && !RB_OBJ_FROZEN(*key)) { + *key = rb_hash_key_str(*key); + } *value = newvalue; return ST_CONTINUE; } @@ -4182,7 +4238,7 @@ rb_hash_assoc(VALUE hash, VALUE key) table = RHASH_ST_TABLE(hash); orighash = table->type; - if (orighash != &identhash) { + if (!RHASH_IDENTHASH_P(hash)) { VALUE value; struct reset_hash_type_arg ensure_arg; struct st_hash_type assochash; @@ -4442,7 +4498,7 @@ rb_hash_compare_by_id(VALUE hash) MJIT_FUNC_EXPORTED VALUE rb_hash_compare_by_id_p(VALUE hash) { - return RBOOL(RHASH_ST_TABLE_P(hash) && RHASH_ST_TABLE(hash)->type == &identhash); + return RBOOL(RHASH_IDENTHASH_P(hash)); } VALUE @@ -4760,7 +4816,7 @@ rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val) if (ret != -1) { return ret; } - ar_try_convert_table(hash); + ar_force_convert_table(hash, __FILE__, __LINE__); } tbl = RHASH_TBL_RAW(hash); return st_update(tbl, (st_data_t)key, add_new_i, (st_data_t)args); @@ -4939,7 +4995,7 @@ env_name(volatile VALUE *s) static VALUE env_aset(VALUE nm, VALUE val); static void -reset_by_modified_env(const char *nam) +reset_by_modified_env(const char *nam, const char *val) { /* * ENV['TZ'] = nil has a special meaning. @@ -4948,7 +5004,7 @@ reset_by_modified_env(const char *nam) * This hack might works only on Linux glibc. */ if (ENVMATCH(nam, TZ_ENV)) { - ruby_reset_timezone(); + ruby_reset_timezone(val); } } @@ -4956,7 +5012,7 @@ static VALUE env_delete(VALUE name) { const char *nam = env_name(name); - reset_by_modified_env(nam); + reset_by_modified_env(nam, NULL); VALUE val = getenv_with_lock(nam); if (!NIL_P(val)) { @@ -5416,7 +5472,7 @@ env_aset(VALUE nm, VALUE val) get_env_ptr(value, val); ruby_setenv(name, value); - reset_by_modified_env(name); + reset_by_modified_env(name, value); return val; } @@ -5971,24 +6027,23 @@ env_to_s(VALUE _) static VALUE env_inspect(VALUE _) { - VALUE i; VALUE str = rb_str_buf_new2("{"); + rb_encoding *enc = env_encoding(); ENV_LOCK(); { char **env = GET_ENVIRON(environ); while (*env) { - char *s = strchr(*env, '='); + const char *s = strchr(*env, '='); if (env != environ) { rb_str_buf_cat2(str, ", "); } if (s) { - rb_str_buf_cat2(str, "\""); - rb_str_buf_cat(str, *env, s-*env); - rb_str_buf_cat2(str, "\"=>"); - i = rb_inspect(rb_str_new2(s+1)); - rb_str_buf_append(str, i); + rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(*env, s-*env, enc))); + rb_str_buf_cat2(str, "=>"); + s++; + rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(s, strlen(s), enc))); } env++; } @@ -7164,7 +7219,6 @@ void Init_Hash(void) { id_hash = rb_intern_const("hash"); - id_default = rb_intern_const("default"); id_flatten_bang = rb_intern_const("flatten!"); id_hash_iter_lev = rb_make_internal_id(); diff --git a/id_table.h b/id_table.h index 9d9eb5648e..f72e2d1d92 100644 --- a/id_table.h +++ b/id_table.h @@ -19,7 +19,6 @@ struct rb_id_table *rb_id_table_create(size_t size); void rb_id_table_free(struct rb_id_table *tbl); void rb_id_table_clear(struct rb_id_table *tbl); -size_t rb_id_table_size(const struct rb_id_table *tbl); size_t rb_id_table_memsize(const struct rb_id_table *tbl); int rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val); @@ -33,4 +32,8 @@ void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *fu void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data); void rb_id_table_foreach_values_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, rb_id_table_update_value_callback_func_t *replace, void *data); +RUBY_SYMBOL_EXPORT_BEGIN +size_t rb_id_table_size(const struct rb_id_table *tbl); +RUBY_SYMBOL_EXPORT_END + #endif /* RUBY_ID_TABLE_H */ diff --git a/include/ruby/internal/abi.h b/include/ruby/internal/abi.h index d67aa0d509..44111a0055 100644 --- a/include/ruby/internal/abi.h +++ b/include/ruby/internal/abi.h @@ -31,6 +31,7 @@ #if defined(HAVE_FUNC_WEAK) && !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) # define RUBY_DLN_CHECK_ABI #endif +#endif /* RUBY_ABI_VERSION */ #ifdef RUBY_DLN_CHECK_ABI @@ -41,7 +42,11 @@ extern "C" { RUBY_FUNC_EXPORTED unsigned long long __attribute__((weak)) ruby_abi_version(void) { +# ifdef RUBY_ABI_VERSION return RUBY_ABI_VERSION; +# else + return 0; +# endif } # ifdef __cplusplus @@ -51,5 +56,3 @@ ruby_abi_version(void) #endif #endif - -#endif diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h index e3e1b6166d..e4c6d155cc 100644 --- a/include/ruby/internal/anyargs.h +++ b/include/ruby/internal/anyargs.h @@ -84,12 +84,15 @@ #elif defined(_WIN32) || defined(__CYGWIN__) # /* Skip due to [Bug #16134] */ +# define RBIMPL_CAST_FN_PTR 1 #elif ! RBIMPL_HAS_ATTRIBUTE(transparent_union) # /* :TODO: improve here, please find a way to support. */ +# define RBIMPL_CAST_FN_PTR 1 #elif ! defined(HAVE_VA_ARGS_MACRO) # /* :TODO: improve here, please find a way to support. */ +# define RBIMPL_CAST_FN_PTR 1 #else # /** @cond INTERNAL_MACRO */ @@ -348,6 +351,25 @@ RBIMPL_ANYARGS_DECL(rb_define_method, VALUE, const char *) #endif /* __cplusplus */ +#if defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus) +/* In C23, K&R style prototypes are gone and so `void foo(ANYARGS)` became + * equivalent to `void foo(void)` unlike in earlier versions. This is a problem + * for rb_define_* functions since that makes all valid functions one can pass + * trip -Wincompatible-pointer-types, which we treat as errors. This is mostly + * not a problem for the __builtin_choose_expr path, but outside of that we + * need to add a cast for compatibility. + */ +#define rb_define_method(klass, mid, func, arity) rb_define_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_method_id(klass, mid, func, arity) rb_define_method_id((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_singleton_method(obj, mid, func, arity) rb_define_singleton_method((obj), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_protected_method(klass, mid, func, arity) rb_define_protected_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_private_method(klass, mid, func, arity) rb_define_private_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_module_function(mod, mid, func, arity) rb_define_module_function((mod), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_global_function(mid, func, arity) rb_define_global_function((mid), (VALUE (*)(ANYARGS))(func), (arity)) + +#undef RBIMPL_CAST_FN_PTR +#endif /* defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus) */ + /** * This macro is to properly cast a function parameter of *_define_method * family. It has been around since 1.x era so you can maximise backwards diff --git a/include/ruby/internal/attr/nonstring.h b/include/ruby/internal/attr/nonstring.h new file mode 100644 index 0000000000..de26e926d4 --- /dev/null +++ b/include/ruby/internal/attr/nonstring.h @@ -0,0 +1,32 @@ +#ifndef RBIMPL_ATTR_NONSTRING_H /*-*-C++-*-vi:se ft=cpp:*/ +#define RBIMPL_ATTR_NONSTRING_H +/** + * @file + * @author Ruby developers <ruby-core@ruby-lang.org> + * @copyright This file is a part of the programming language Ruby. + * Permission is hereby granted, to either redistribute and/or + * modify this file, provided that the conditions mentioned in the + * file COPYING are met. Consult the file for details. + * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are + * implementation details. Don't take them as canon. They could + * rapidly appear then vanish. The name (path) of this header file + * is also an implementation detail. Do not expect it to persist + * at the place it is now. Developers are free to move it anywhere + * anytime at will. + * @note To ruby-core: remember that this header can be possibly + * recursively included from extension libraries written in C++. + * Do not expect for instance `__VA_ARGS__` is always available. + * We assume C99 for ruby itself but we don't assume languages of + * extension libraries. They could be written in C++98. + * @brief Defines #RBIMPL_ATTR_NONSTRING. + */ +#include "ruby/internal/has/attribute.h" + +/** Wraps (or simulates) `__attribute__((nonstring))` */ +#if RBIMPL_HAS_ATTRIBUTE(nonstring) +# define RBIMPL_ATTR_NONSTRING() __attribute__((nonstring)) +#else +# define RBIMPL_ATTR_NONSTRING() /* void */ +#endif + +#endif /* RBIMPL_ATTR_NONSTRING_H */ diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h index 66fc14e511..054e4b0f9c 100644 --- a/include/ruby/internal/gc.h +++ b/include/ruby/internal/gc.h @@ -26,10 +26,15 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() /** - * Inform the garbage collector that `valptr` points to a live Ruby object that - * should not be moved. Note that extensions should use this API on global - * constants instead of assuming constants defined in Ruby are always alive. - * Ruby code can remove global constants. + * Inform the garbage collector that the global or static variable pointed by + * `valptr` stores a live Ruby object that should not be moved. Note that + * extensions should use this API on global constants instead of assuming + * constants defined in Ruby are always alive. Ruby code can remove global + * constants. + * + * Because this registration itself has a possibility to trigger a GC, this + * function must be called before any GC-able objects is assigned to the + * address pointed by `valptr`. */ void rb_gc_register_address(VALUE *valptr); diff --git a/include/ruby/internal/intern/cont.h b/include/ruby/internal/intern/cont.h index 1ec78793b7..32647f48aa 100644 --- a/include/ruby/internal/intern/cont.h +++ b/include/ruby/internal/intern/cont.h @@ -45,11 +45,7 @@ VALUE rb_fiber_new(rb_block_call_func_t func, VALUE callback_obj); * If the given storage is Qundef or Qtrue, this function is equivalent to * rb_fiber_new() which inherits storage from the current fiber. * - * If the given storage is Qfalse, this function uses the current fiber's - * storage by reference. - * - * Specifying either Qtrue or Qfalse is experimental and may be changed in the - * future. + * Specifying Qtrue is experimental and may be changed in the future. * * If the given storage is Qnil, this function will lazy initialize the * internal storage which starts of empty (without any inheritance). diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h index 88e5598066..e4b855d8e7 100644 --- a/include/ruby/io/buffer.h +++ b/include/ruby/io/buffer.h @@ -56,14 +56,10 @@ enum rb_io_buffer_endian { RB_IO_BUFFER_LITTLE_ENDIAN = 4, RB_IO_BUFFER_BIG_ENDIAN = 8, -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN, -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#if defined(WORDS_BIGENDIAN) RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN, -#elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN +#else RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN, -#elif REG_DWORD == REG_DWORD_BIG_ENDIAN - RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN, #endif RB_IO_BUFFER_NETWORK_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN diff --git a/include/ruby/st.h b/include/ruby/st.h index 1e4bb80686..f35ab43603 100644 --- a/include/ruby/st.h +++ b/include/ruby/st.h @@ -98,6 +98,8 @@ struct st_table { enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE}; +size_t rb_st_table_size(const struct st_table *tbl); +#define st_table_size rb_st_table_size st_table *rb_st_init_table(const struct st_hash_type *); #define st_init_table rb_st_init_table st_table *rb_st_init_table_with_size(const struct st_hash_type *, st_index_t); diff --git a/include/ruby/win32.h b/include/ruby/win32.h index 197eb8a802..18de3a17d8 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -125,8 +125,15 @@ typedef unsigned int uintptr_t; #define O_SHARE_DELETE 0x20000000 /* for rb_w32_open(), rb_w32_wopen() */ typedef int clockid_t; +#if defined(__MINGW32__) +#undef CLOCK_PROCESS_CPUTIME_ID +#undef CLOCK_THREAD_CPUTIME_ID +#undef CLOCK_REALTIME_COARSE +#endif +#if defined(HAVE_CLOCK_GETTIME) && !defined(CLOCK_REALTIME) #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 1 +#endif #undef utime #undef lseek diff --git a/internal/basic_operators.h b/internal/basic_operators.h |