diff options
599 files changed, 12645 insertions, 6103 deletions
diff --git a/.github/actions/setup/directories/action.yml b/.github/actions/setup/directories/action.yml index 63abd4ae58..5264e0e969 100644 --- a/.github/actions/setup/directories/action.yml +++ b/.github/actions/setup/directories/action.yml @@ -88,7 +88,7 @@ runs: git config --global init.defaultBranch garbage - if: inputs.checkout - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: path: ${{ inputs.srcdir }} fetch-depth: ${{ inputs.fetch-depth }} diff --git a/.github/workflows/annocheck.yml b/.github/workflows/annocheck.yml index f3f496c29a..6c03f52289 100644 --- a/.github/workflows/annocheck.yml +++ b/.github/workflows/annocheck.yml @@ -4,7 +4,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -12,7 +12,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -63,7 +63,7 @@ jobs: - run: id working-directory: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github @@ -74,7 +74,7 @@ jobs: builddir: build makeup: true - - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: '3.0' bundler: none diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml index 50ff7014ff..ce979c9e7a 100644 --- a/.github/workflows/baseruby.yml +++ b/.github/workflows/baseruby.yml @@ -4,7 +4,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -12,7 +12,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -51,12 +51,12 @@ jobs: - ruby-3.3 steps: - - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: ${{ matrix.ruby }} bundler: none - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: ./.github/actions/setup/ubuntu diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml index 96bca3e3aa..6f06451a99 100644 --- a/.github/workflows/bundled_gems.yml +++ b/.github/workflows/bundled_gems.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index a5e491d7a4..8ecae4f223 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -11,7 +11,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -45,7 +45,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: ./.github/actions/setup/ubuntu if: ${{ contains(matrix.os, 'ubuntu') }} @@ -55,7 +55,7 @@ jobs: - uses: ./.github/actions/setup/directories - - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: '3.0' bundler: none diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml index 7996ddb4c0..f26319f448 100644 --- a/.github/workflows/check_misc.yml +++ b/.github/workflows/check_misc.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f0dab96550..d6a2e297be 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,7 +5,7 @@ on: branches: ['master'] paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -13,7 +13,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -63,7 +63,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Install libraries if: ${{ contains(matrix.os, 'macos') }} @@ -80,15 +80,15 @@ jobs: run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb - name: Initialize CodeQL - uses: github/codeql-action/init@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0 + uses: github/codeql-action/init@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0 + uses: github/codeql-action/autobuild@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0 + uses: github/codeql-action/analyze@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: category: '/language:${{ matrix.language }}' upload: False @@ -118,7 +118,7 @@ jobs: continue-on-error: true - name: Upload SARIF - uses: github/codeql-action/upload-sarif@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5 with: sarif_file: sarif-results/${{ matrix.language }}.sarif continue-on-error: true diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 0eebb0eea2..8937a5b15f 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -4,7 +4,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -12,7 +12,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -147,9 +147,9 @@ jobs: - { name: disable-rubygems, env: { append_configure: '--disable-rubygems' } } - { name: RUBY_DEVEL, env: { append_configure: '--enable-devel' } } + - { name: OPT_THREADED_CODE=0, env: { cppflags: '-DOPT_THREADED_CODE=0' } } - { name: OPT_THREADED_CODE=1, env: { cppflags: '-DOPT_THREADED_CODE=1' } } - { name: OPT_THREADED_CODE=2, env: { cppflags: '-DOPT_THREADED_CODE=2' } } - - { name: OPT_THREADED_CODE=3, env: { cppflags: '-DOPT_THREADED_CODE=3' } } - { name: NDEBUG, env: { cppflags: '-DNDEBUG' } } - { name: RUBY_DEBUG, env: { cppflags: '-DRUBY_DEBUG' } } @@ -233,7 +233,7 @@ jobs: - run: id working-directory: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/dependabot_automerge.yml b/.github/workflows/dependabot_automerge.yml index fa50511eb2..80112a0af3 100644 --- a/.github/workflows/dependabot_automerge.yml +++ b/.github/workflows/dependabot_automerge.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Dependabot metadata - uses: dependabot/fetch-metadata@0fb21704c18a42ce5aa8d720ea4b912f5e6babef # v2.0.0 + uses: dependabot/fetch-metadata@5e5f99653a5b510e8555840e80cbf1514ad4af38 # v2.1.0 id: metadata - name: Wait for status checks diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 87231228bc..e71706b186 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -52,7 +52,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index d8c075fc7a..4170369def 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -11,7 +11,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -66,7 +66,7 @@ jobs: steps: - name: Set up Ruby & MSYS2 - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: ${{ matrix.baseruby }} @@ -97,7 +97,7 @@ jobs: $result working-directory: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/prism.yml b/.github/workflows/prism.yml index adb9206c36..78b6469db1 100644 --- a/.github/workflows/prism.yml +++ b/.github/workflows/prism.yml @@ -55,7 +55,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github @@ -92,7 +92,7 @@ jobs: timeout-minutes: 40 env: GNUMAKEFLAGS: '' - RUBY_TESTOPTS: '-q --tty=no --excludes-dir="../src/test/.excludes-prism" --exclude="test_ast.rb" --exclude="error_highlight/test_error_highlight.rb" --exclude="prism/encoding_test.rb" --exclude="prism/locals_test.rb" --exclude="prism/newline_test.rb"' + RUBY_TESTOPTS: '-q --tty=no --excludes-dir="../src/test/.excludes-prism" --exclude="error_highlight/test_error_highlight.rb" --exclude="prism/encoding_test.rb"' RUN_OPTS: ${{ matrix.run_opts }} - name: make test-prism-spec diff --git a/.github/workflows/rjit-bindgen.yml b/.github/workflows/rjit-bindgen.yml index b77d79e83a..c4cfe9ed83 100644 --- a/.github/workflows/rjit-bindgen.yml +++ b/.github/workflows/rjit-bindgen.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -11,7 +11,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -47,11 +47,11 @@ jobs: steps: - name: Set up Ruby - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: '3.1' - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/rjit.yml b/.github/workflows/rjit.yml index b393074919..509088db7d 100644 --- a/.github/workflows/rjit.yml +++ b/.github/workflows/rjit.yml @@ -55,7 +55,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 99f85235ad..85ee3e2a48 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -32,12 +32,12 @@ jobs: steps: - name: 'Checkout code' - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: persist-credentials: false - name: 'Run analysis' - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 with: results_file: results.sarif results_format: 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@df5a14dc28094dc936e103b37d749c6628682b60 # v2.1.27 + uses: github/codeql-action/upload-sarif@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v2.1.27 with: sarif_file: results.sarif diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml index bb4bf8441f..14669a13b3 100644 --- a/.github/workflows/spec_guards.yml +++ b/.github/workflows/spec_guards.yml @@ -45,9 +45,9 @@ jobs: - ruby-3.3 steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: ${{ matrix.ruby }} bundler: none diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 8f70fcfaaf..697056df60 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -58,7 +58,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github @@ -67,7 +67,7 @@ jobs: with: arch: ${{ matrix.arch }} - - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: '3.0' bundler: none diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 1e0a0574be..764ca2d21c 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -11,7 +11,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -61,7 +61,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github @@ -100,7 +100,7 @@ jobs: run: | echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV - - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: '3.0' bundler: none @@ -136,7 +136,7 @@ jobs: - run: tar cfz ../install.tar.gz -C ../install . - name: Upload artifacts - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: ruby-wasm-install path: ${{ github.workspace }}/install.tar.gz @@ -164,7 +164,7 @@ jobs: - name: Save Pull Request number if: ${{ github.event_name == 'pull_request' }} run: echo "${{ github.event.pull_request.number }}" >> ${{ github.workspace }}/github-pr-info.txt - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 if: ${{ github.event_name == 'pull_request' }} with: name: github-pr-info diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e1a1829755..fcf600263b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -11,7 +11,7 @@ on: pull_request: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -91,7 +91,7 @@ jobs: ${{ steps.find-tools.outputs.needs }} if: ${{ steps.find-tools.outputs.needs != '' }} - - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: '3.0' bundler: none @@ -123,7 +123,7 @@ jobs: Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH shell: pwsh - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml index 5e209cc4ca..bd17b302ae 100644 --- a/.github/workflows/yjit-macos.yml +++ b/.github/workflows/yjit-macos.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -37,7 +37,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: RUST_BACKTRACE=1 cargo test working-directory: yjit @@ -60,11 +60,13 @@ jobs: - test_task: 'check' configure: '--enable-yjit=dev' yjit_opts: '--yjit-call-threshold=1 --yjit-verify-ctx --yjit-code-gc' + specopts: '-T --yjit-call-threshold=1 -T --yjit-verify-ctx -T --yjit-code-gc' fail-fast: false env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} RUN_OPTS: ${{ matrix.yjit_opts }} + SPECOPTS: ${{ matrix.specopts }} runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }} @@ -79,7 +81,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github @@ -128,6 +130,7 @@ jobs: run: >- make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} RUN_OPTS="$RUN_OPTS" + SPECOPTS="$SPECOPTS" timeout-minutes: 60 env: RUBY_TESTOPTS: '-q --tty=no' diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index dcf7e1874a..bd7e25a9cd 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -3,7 +3,7 @@ on: push: paths-ignore: - 'doc/**' - - '**/man' + - '**/man/*' - '**.md' - '**.rdoc' - '**/.document' @@ -36,7 +36,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 # For now we can't run cargo test --offline because it complains about the # capstone dependency, even though the dependency is optional @@ -68,7 +68,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 # Check that we don't have linting errors in release mode, too - run: cargo clippy --all-targets --all-features @@ -95,6 +95,7 @@ jobs: - test_task: 'check' configure: '--enable-yjit=dev' yjit_opts: '--yjit-call-threshold=1 --yjit-verify-ctx --yjit-code-gc' + specopts: '-T --yjit-call-threshold=1 -T --yjit-verify-ctx -T --yjit-code-gc' - test_task: 'test-bundled-gems' configure: '--enable-yjit=dev' @@ -108,6 +109,7 @@ jobs: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} RUN_OPTS: ${{ matrix.yjit_opts }} YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }} + SPECOPTS: ${{ matrix.specopts }} RUBY_DEBUG: ci BUNDLE_JOBS: 8 # for yjit-bench RUST_BACKTRACE: 1 @@ -125,7 +127,7 @@ jobs: )}} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: sparse-checkout-cone-mode: false sparse-checkout: /.github @@ -145,7 +147,7 @@ jobs: if: ${{ matrix.rust_version }} run: rustup install ${{ matrix.rust_version }} --profile minimal - - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: ruby-version: '3.0' bundler: none @@ -179,10 +181,10 @@ jobs: - name: make ${{ matrix.test_task }} run: >- - make -s -j ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} - RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug + make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} + RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS" YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS" YJIT_BINDGEN_DIFF_OPTS="$YJIT_BINDGEN_DIFF_OPTS" - timeout-minutes: 60 + timeout-minutes: 90 env: RUBY_TESTOPTS: '-q --tty=no' TEST_BUNDLED_GEMS_ALLOW_FAILURES: '' diff --git a/.gitignore b/.gitignore index de4b972193..b6beba3b3e 100644 --- a/.gitignore +++ b/.gitignore @@ -167,6 +167,7 @@ lcov*.info # /coroutine/ !/coroutine/**/*.s +!/coroutine/**/*.S # /enc/trans/ /enc/trans/*.c @@ -261,8 +262,10 @@ lcov*.info /lib/prism/dispatcher.rb /lib/prism/dot_visitor.rb /lib/prism/dsl.rb +/lib/prism/inspect_visitor.rb /lib/prism/mutation_compiler.rb /lib/prism/node.rb +/lib/prism/reflection.rb /lib/prism/serialize.rb /lib/prism/visitor.rb /prism/api_node.c @@ -45,24 +45,25 @@ The following default gems are updated. * erb 4.0.4 * fiddle 1.1.3 * io-console 0.7.2 -* irb 1.12.0 +* irb 1.13.1 * json 2.7.2 * net-http 0.4.1 * optparse 0.5.0 -* prism 0.25.0 +* prism 0.29.0 * rdoc 6.6.3.1 -* reline 0.5.2 +* reline 0.5.7 * resolv 0.4.0 * stringio 3.1.1 * strscan 3.1.1 The following bundled gems are updated. -* minitest 5.22.3 +* minitest 5.23.0 * rake 13.2.1 * test-unit 3.6.2 +* rexml 3.2.8 * net-ftp 0.3.4 -* net-imap 0.4.10 +* net-imap 0.4.11 * net-smtp 0.5.0 * rbs 3.4.4 * typeprof 0.21.11 @@ -73,7 +74,7 @@ The following bundled gems are promoted from default gems. * mutex_m 0.2.0 * getoptlong 0.2.1 * base64 0.2.0 -* bigdecimal 3.1.7 +* bigdecimal 3.1.8 * observer 0.1.2 * abbrev 0.1.2 * resolv-replace 0.1.1 @@ -90,8 +91,8 @@ See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/log ## Compatibility issues * Error messages and backtrace displays have been changed. - * Use a single quote instead of a backtick as a opening quote. [Feature #16495] - * Display a class name before a method name (only when the class has a permanent name). [Feature #19117] + * Use a single quote instead of a backtick as a opening quote. [[Feature #16495]] + * Display a class name before a method name (only when the class has a permanent name). [[Feature #19117]] * `Kernel#caller`, `Thread::Backtrace::Location`'s methods, etc. are also changed accordingly. ``` Old: @@ -140,3 +141,4 @@ See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/log [Feature #20205]: https://bugs.ruby-lang.org/issues/20205 [Bug #20218]: https://bugs.ruby-lang.org/issues/20218 [Feature #20265]: https://bugs.ruby-lang.org/issues/20265 +[Feature #20429]: https://bugs.ruby-lang.org/issues/20429 @@ -350,7 +350,7 @@ ary_memcpy(VALUE ary, long beg, long argc, const VALUE *argv) } static VALUE * -ary_heap_alloc(size_t capa) +ary_heap_alloc_buffer(size_t capa) { return ALLOC_N(VALUE, capa); } @@ -404,7 +404,7 @@ ary_resize_capa(VALUE ary, long capacity) size_t new_capa = capacity; if (ARY_EMBED_P(ary)) { long len = ARY_EMBED_LEN(ary); - VALUE *ptr = ary_heap_alloc(capacity); + VALUE *ptr = ary_heap_alloc_buffer(capacity); MEMCPY(ptr, ARY_EMBED_PTR(ary), VALUE, len); FL_UNSET_EMBED(ary); @@ -555,7 +555,7 @@ rb_ary_cancel_sharing(VALUE ary) rb_ary_decrement_share(shared_root); } else { - VALUE *ptr = ary_heap_alloc(len); + VALUE *ptr = ary_heap_alloc_buffer(len); MEMCPY(ptr, ARY_HEAP_PTR(ary), VALUE, len); rb_ary_unshare(ary); ARY_SET_CAPA(ary, len); @@ -714,7 +714,7 @@ ary_new(VALUE klass, long capa) ARY_SET_CAPA(ary, capa); RUBY_ASSERT(!ARY_EMBED_P(ary)); - ARY_SET_PTR(ary, ary_heap_alloc(capa)); + ARY_SET_PTR(ary, ary_heap_alloc_buffer(capa)); ARY_SET_HEAP_LEN(ary, 0); } @@ -818,7 +818,7 @@ ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa) ARY_SET_CAPA(ary, capa); RUBY_ASSERT(!ARY_EMBED_P(ary)); - ARY_SET_PTR(ary, ary_heap_alloc(capa)); + ARY_SET_PTR(ary, ary_heap_alloc_buffer(capa)); ARY_SET_HEAP_LEN(ary, 0); } @@ -918,7 +918,7 @@ ary_make_shared(VALUE ary) FL_SET_SHARED_ROOT(shared); if (ARY_EMBED_P(ary)) { - VALUE *ptr = ary_heap_alloc(capa); + VALUE *ptr = ary_heap_alloc_buffer(capa); ARY_SET_PTR(shared, ptr); ary_memcpy(shared, 0, len, RARRAY_CONST_PTR(ary)); @@ -4559,7 +4559,7 @@ rb_ary_replace(VALUE copy, VALUE orig) * contents of orig. */ else if (ARY_EMBED_P(orig)) { long len = ARY_EMBED_LEN(orig); - VALUE *ptr = ary_heap_alloc(len); + VALUE *ptr = ary_heap_alloc_buffer(len); FL_UNSET_EMBED(copy); ARY_SET_PTR(copy, ptr); @@ -16,7 +16,7 @@ static VALUE rb_mAST; static VALUE rb_cNode; struct ASTNodeData { - rb_ast_t *ast; + VALUE ast_value; const NODE *node; }; @@ -24,14 +24,16 @@ static void node_gc_mark(void *ptr) { struct ASTNodeData *data = (struct ASTNodeData *)ptr; - rb_gc_mark((VALUE)data->ast); + rb_gc_mark(data->ast_value); } static size_t node_memsize(const void *ptr) { struct ASTNodeData *data = (struct ASTNodeData *)ptr; - return rb_ast_memsize(data->ast); + rb_ast_t *ast = rb_ruby_ast_data_get(data->ast_value); + + return sizeof(struct ASTNodeData) + rb_ast_memsize(ast); } static const rb_data_type_t rb_node_type = { @@ -44,22 +46,22 @@ static const rb_data_type_t rb_node_type = { static VALUE rb_ast_node_alloc(VALUE klass); static void -setup_node(VALUE obj, rb_ast_t *ast, const NODE *node) +setup_node(VALUE obj, VALUE ast_value, const NODE *node) { struct ASTNodeData *data; TypedData_Get_Struct(obj, struct ASTNodeData, &rb_node_type, data); - data->ast = ast; + data->ast_value = ast_value; data->node = node; } static VALUE -ast_new_internal(rb_ast_t *ast, const NODE *node) +ast_new_internal(VALUE ast_value, const NODE *node) { VALUE obj; obj = rb_ast_node_alloc(rb_cNode); - setup_node(obj, ast, node); + setup_node(obj, ast_value, node); return obj; } @@ -74,14 +76,16 @@ ast_parse_new(void) } static VALUE -ast_parse_done(rb_ast_t *ast) +ast_parse_done(VALUE ast_value) { + rb_ast_t *ast = rb_ruby_ast_data_get(ast_value); + if (!ast->body.root) { rb_ast_dispose(ast); rb_exc_raise(GET_EC()->errinfo); } - return ast_new_internal(ast, (NODE *)ast->body.root); + return ast_new_internal(ast_value, (NODE *)ast->body.root); } static VALUE @@ -93,15 +97,15 @@ ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_scri static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens) { - rb_ast_t *ast = 0; + VALUE ast_value; StringValue(str); VALUE vparser = ast_parse_new(); if (RTEST(keep_script_lines)) rb_parser_set_script_lines(vparser); if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser); if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser); - ast = rb_parser_compile_string_path(vparser, Qnil, str, 1); - return ast_parse_done(ast); + ast_value = rb_parser_compile_string_path(vparser, Qnil, str, 1); + return ast_parse_done(ast_value); } static VALUE @@ -114,7 +118,7 @@ static VALUE rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens) { VALUE f; - rb_ast_t *ast = 0; + VALUE ast_value = Qnil; rb_encoding *enc = rb_utf8_encoding(); f = rb_file_open_str(path, "r"); @@ -123,39 +127,26 @@ rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VAL if (RTEST(keep_script_lines)) rb_parser_set_script_lines(vparser); if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser); if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser); - ast = rb_parser_compile_file_path(vparser, Qnil, f, 1); + ast_value = rb_parser_compile_file_path(vparser, Qnil, f, 1); rb_io_close(f); - return ast_parse_done(ast); -} - -static VALUE -lex_array(VALUE array, int index) -{ - VALUE str = rb_ary_entry(array, index); - if (!NIL_P(str)) { - StringValue(str); - if (!rb_enc_asciicompat(rb_enc_get(str))) { - rb_raise(rb_eArgError, "invalid source encoding"); - } - } - return str; + return ast_parse_done(ast_value); } static VALUE rb_ast_parse_array(VALUE array, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens) { - rb_ast_t *ast = 0; + VALUE ast_value = Qnil; array = rb_check_array_type(array); VALUE vparser = ast_parse_new(); if (RTEST(keep_script_lines)) rb_parser_set_script_lines(vparser); if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser); if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser); - ast = rb_parser_compile_generic(vparser, lex_array, Qnil, array, 1); - return ast_parse_done(ast); + ast_value = rb_parser_compile_array(vparser, Qnil, array, 1); + return ast_parse_done(ast_value); } -static VALUE node_children(rb_ast_t*, const NODE*); +static VALUE node_children(VALUE, const NODE*); static VALUE node_find(VALUE self, const int node_id) @@ -167,7 +158,7 @@ node_find(VALUE self, const int node_id) if (nd_node_id(data->node) == node_id) return self; - ary = node_children(data->ast, data->node); + ary = node_children(data->ast_value, data->node); for (i = 0; i < RARRAY_LEN(ary); i++) { VALUE child = RARRAY_AREF(ary, i); @@ -290,10 +281,10 @@ ast_node_node_id(rb_execution_context_t *ec, VALUE self) return INT2FIX(nd_node_id(data->node)); } -#define NEW_CHILD(ast, node) node ? ast_new_internal(ast, node) : Qnil +#define NEW_CHILD(ast_value, node) (node ? ast_new_internal(ast_value, node) : Qnil) static VALUE -rb_ary_new_from_node_args(rb_ast_t *ast, long n, ...) +rb_ary_new_from_node_args(VALUE ast_value, long n, ...) { va_list ar; VALUE ary; @@ -305,39 +296,39 @@ rb_ary_new_from_node_args(rb_ast_t *ast, long n, ...) for (i=0; i<n; i++) { NODE *node; node = va_arg(ar, NODE *); - rb_ary_push(ary, NEW_CHILD(ast, node)); + rb_ary_push(ary, NEW_CHILD(ast_value, node)); } va_end(ar); return ary; } static VALUE -dump_block(rb_ast_t *ast, const struct RNode_BLOCK *node) +dump_block(VALUE ast_value, const struct RNode_BLOCK *node) { VALUE ary = rb_ary_new(); do { - rb_ary_push(ary, NEW_CHILD(ast, node->nd_head)); + rb_ary_push(ary, NEW_CHILD(ast_value, node->nd_head)); } while (node->nd_next && nd_type_p(node->nd_next, NODE_BLOCK) && (node = RNODE_BLOCK(node->nd_next), 1)); if (node->nd_next) { - rb_ary_push(ary, NEW_CHILD(ast, node->nd_next)); + rb_ary_push(ary, NEW_CHILD(ast_value, node->nd_next)); } return ary; } static VALUE -dump_array(rb_ast_t *ast, const struct RNode_LIST *node) +dump_array(VALUE ast_value, const struct RNode_LIST *node) { VALUE ary = rb_ary_new(); - rb_ary_push(ary, NEW_CHILD(ast, node->nd_head)); + rb_ary_push(ary, NEW_CHILD(ast_value, node->nd_head)); while (node->nd_next && nd_type_p(node->nd_next, NODE_LIST)) { node = RNODE_LIST(node->nd_next); - rb_ary_push(ary, NEW_CHILD(ast, node->nd_head)); + rb_ary_push(ary, NEW_CHILD(ast_value, node->nd_head)); } - rb_ary_push(ary, NEW_CHILD(ast, node->nd_next)); + rb_ary_push(ary, NEW_CHILD(ast_value, node->nd_next)); return ary; } @@ -359,155 +350,155 @@ no_name_rest(void) } static VALUE -rest_arg(rb_ast_t *ast, const NODE *rest_arg) +rest_arg(VALUE ast_value, const NODE *rest_arg) { - return NODE_NAMED_REST_P(rest_arg) ? NEW_CHILD(ast, rest_arg) : no_name_rest(); + return NODE_NAMED_REST_P(rest_arg) ? NEW_CHILD(ast_value, rest_arg) : no_name_rest(); } static VALUE -node_children(rb_ast_t *ast, const NODE *node) +node_children(VALUE ast_value, const NODE *node) { char name[sizeof("$") + DECIMAL_SIZE_OF(long)]; enum node_type type = nd_type(node); switch (type) { case NODE_BLOCK: - return dump_block(ast, RNODE_BLOCK(node)); + return dump_block(ast_value, RNODE_BLOCK(node)); case NODE_IF: - return rb_ary_new_from_node_args(ast, 3, RNODE_IF(node)->nd_cond, RNODE_IF(node)->nd_body, RNODE_IF(node)->nd_else); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_IF(node)->nd_cond, RNODE_IF(node)->nd_body, RNODE_IF(node)->nd_else); case NODE_UNLESS: - return rb_ary_new_from_node_args(ast, 3, RNODE_UNLESS(node)->nd_cond, RNODE_UNLESS(node)->nd_body, RNODE_UNLESS(node)->nd_else); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_UNLESS(node)->nd_cond, RNODE_UNLESS(node)->nd_body, RNODE_UNLESS(node)->nd_else); case NODE_CASE: - return rb_ary_new_from_node_args(ast, 2, RNODE_CASE(node)->nd_head, RNODE_CASE(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_CASE(node)->nd_head, RNODE_CASE(node)->nd_body); case NODE_CASE2: - return rb_ary_new_from_node_args(ast, 2, RNODE_CASE2(node)->nd_head, RNODE_CASE2(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_CASE2(node)->nd_head, RNODE_CASE2(node)->nd_body); case NODE_CASE3: - return rb_ary_new_from_node_args(ast, 2, RNODE_CASE3(node)->nd_head, RNODE_CASE3(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_CASE3(node)->nd_head, RNODE_CASE3(node)->nd_body); case NODE_WHEN: - return rb_ary_new_from_node_args(ast, 3, RNODE_WHEN(node)->nd_head, RNODE_WHEN(node)->nd_body, RNODE_WHEN(node)->nd_next); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_WHEN(node)->nd_head, RNODE_WHEN(node)->nd_body, RNODE_WHEN(node)->nd_next); case NODE_IN: - return rb_ary_new_from_node_args(ast, 3, RNODE_IN(node)->nd_head, RNODE_IN(node)->nd_body, RNODE_IN(node)->nd_next); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_IN(node)->nd_head, RNODE_IN(node)->nd_body, RNODE_IN(node)->nd_next); case NODE_WHILE: case NODE_UNTIL: - return rb_ary_push(rb_ary_new_from_node_args(ast, 2, RNODE_WHILE(node)->nd_cond, RNODE_WHILE(node)->nd_body), + return rb_ary_push(rb_ary_new_from_node_args(ast_value, 2, RNODE_WHILE(node)->nd_cond, RNODE_WHILE(node)->nd_body), RBOOL(RNODE_WHILE(node)->nd_state)); case NODE_ITER: case NODE_FOR: - return rb_ary_new_from_node_args(ast, 2, RNODE_ITER(node)->nd_iter, RNODE_ITER(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_ITER(node)->nd_iter, RNODE_ITER(node)->nd_body); case NODE_FOR_MASGN: - return rb_ary_new_from_node_args(ast, 1, RNODE_FOR_MASGN(node)->nd_var); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_FOR_MASGN(node)->nd_var); case NODE_BREAK: - return rb_ary_new_from_node_args(ast, 1, RNODE_BREAK(node)->nd_stts); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_BREAK(node)->nd_stts); case NODE_NEXT: - return rb_ary_new_from_node_args(ast, 1, RNODE_NEXT(node)->nd_stts); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_NEXT(node)->nd_stts); case NODE_RETURN: - return rb_ary_new_from_node_args(ast, 1, RNODE_RETURN(node)->nd_stts); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_RETURN(node)->nd_stts); case NODE_REDO: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_RETRY: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_BEGIN: - return rb_ary_new_from_node_args(ast, 1, RNODE_BEGIN(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_BEGIN(node)->nd_body); case NODE_RESCUE: - return rb_ary_new_from_node_args(ast, 3, RNODE_RESCUE(node)->nd_head, RNODE_RESCUE(node)->nd_resq, RNODE_RESCUE(node)->nd_else); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_RESCUE(node)->nd_head, RNODE_RESCUE(node)->nd_resq, RNODE_RESCUE(node)->nd_else); case NODE_RESBODY: - return rb_ary_new_from_node_args(ast, 3, RNODE_RESBODY(node)->nd_args, RNODE_RESBODY(node)->nd_body, RNODE_RESBODY(node)->nd_next); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_RESBODY(node)->nd_args, RNODE_RESBODY(node)->nd_body, RNODE_RESBODY(node)->nd_next); case NODE_ENSURE: - return rb_ary_new_from_node_args(ast, 2, RNODE_ENSURE(node)->nd_head, RNODE_ENSURE(node)->nd_ensr); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_ENSURE(node)->nd_head, RNODE_ENSURE(node)->nd_ensr); case NODE_AND: case NODE_OR: { VALUE ary = rb_ary_new(); while (1) { - rb_ary_push(ary, NEW_CHILD(ast, RNODE_AND(node)->nd_1st)); + rb_ary_push(ary, NEW_CHILD(ast_value, RNODE_AND(node)->nd_1st)); if (!RNODE_AND(node)->nd_2nd || !nd_type_p(RNODE_AND(node)->nd_2nd, type)) break; node = RNODE_AND(node)->nd_2nd; } - rb_ary_push(ary, NEW_CHILD(ast, RNODE_AND(node)->nd_2nd)); + rb_ary_push(ary, NEW_CHILD(ast_value, RNODE_AND(node)->nd_2nd)); return ary; } case NODE_MASGN: if (NODE_NAMED_REST_P(RNODE_MASGN(node)->nd_args)) { - return rb_ary_new_from_node_args(ast, 3, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head, RNODE_MASGN(node)->nd_args); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head, RNODE_MASGN(node)->nd_args); } else { - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_MASGN(node)->nd_value), - NEW_CHILD(ast, RNODE_MASGN(node)->nd_head), + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_MASGN(node)->nd_value), + NEW_CHILD(ast_value, RNODE_MASGN(node)->nd_head), no_name_rest()); } case NODE_LASGN: if (NODE_REQUIRED_KEYWORD_P(RNODE_LASGN(node)->nd_value)) { return rb_ary_new_from_args(2, var_name(RNODE_LASGN(node)->nd_vid), ID2SYM(rb_intern("NODE_SPECIAL_REQUIRED_KEYWORD"))); } - return rb_ary_new_from_args(2, var_name(RNODE_LASGN(node)->nd_vid), NEW_CHILD(ast, RNODE_LASGN(node)->nd_value)); + return rb_ary_new_from_args(2, var_name(RNODE_LASGN(node)->nd_vid), NEW_CHILD(ast_value, RNODE_LASGN(node)->nd_value)); case NODE_DASGN: if (NODE_REQUIRED_KEYWORD_P(RNODE_DASGN(node)->nd_value)) { return rb_ary_new_from_args(2, var_name(RNODE_DASGN(node)->nd_vid), ID2SYM(rb_intern("NODE_SPECIAL_REQUIRED_KEYWORD"))); } - return rb_ary_new_from_args(2, var_name(RNODE_DASGN(node)->nd_vid), NEW_CHILD(ast, RNODE_DASGN(node)->nd_value)); + return rb_ary_new_from_args(2, var_name(RNODE_DASGN(node)->nd_vid), NEW_CHILD(ast_value, RNODE_DASGN(node)->nd_value)); case NODE_IASGN: - return rb_ary_new_from_args(2, var_name(RNODE_IASGN(node)->nd_vid), NEW_CHILD(ast, RNODE_IASGN(node)->nd_value)); + return rb_ary_new_from_args(2, var_name(RNODE_IASGN(node)->nd_vid), NEW_CHILD(ast_value, RNODE_IASGN(node)->nd_value)); case NODE_CVASGN: - return rb_ary_new_from_args(2, var_name(RNODE_CVASGN(node)->nd_vid), NEW_CHILD(ast, RNODE_CVASGN(node)->nd_value)); + return rb_ary_new_from_args(2, var_name(RNODE_CVASGN(node)->nd_vid), NEW_CHILD(ast_value, RNODE_CVASGN(node)->nd_value)); case NODE_GASGN: - return rb_ary_new_from_args(2, var_name(RNODE_GASGN(node)->nd_vid), NEW_CHILD(ast, RNODE_GASGN(node)->nd_value)); + return rb_ary_new_from_args(2, var_name(RNODE_GASGN(node)->nd_vid), NEW_CHILD(ast_value, RNODE_GASGN(node)->nd_value)); case NODE_CDECL: if (RNODE_CDECL(node)->nd_vid) { - return rb_ary_new_from_args(2, ID2SYM(RNODE_CDECL(node)->nd_vid), NEW_CHILD(ast, RNODE_CDECL(node)->nd_value)); + return rb_ary_new_from_args(2, ID2SYM(RNODE_CDECL(node)->nd_vid), NEW_CHILD(ast_value, RNODE_CDECL(node)->nd_value)); } - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_CDECL(node)->nd_else), ID2SYM(RNODE_COLON2(RNODE_CDECL(node)->nd_else)->nd_mid), NEW_CHILD(ast, RNODE_CDECL(node)->nd_value)); + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_CDECL(node)->nd_else), ID2SYM(RNODE_COLON2(RNODE_CDECL(node)->nd_else)->nd_mid), NEW_CHILD(ast_value, RNODE_CDECL(node)->nd_value)); case NODE_OP_ASGN1: - return rb_ary_new_from_args(4, NEW_CHILD(ast, RNODE_OP_ASGN1(node)->nd_recv), + return rb_ary_new_from_args(4, NEW_CHILD(ast_value, RNODE_OP_ASGN1(node)->nd_recv), ID2SYM(RNODE_OP_ASGN1(node)->nd_mid), - NEW_CHILD(ast, RNODE_OP_ASGN1(node)->nd_index), - NEW_CHILD(ast, RNODE_OP_ASGN1(node)->nd_rvalue)); + NEW_CHILD(ast_value, RNODE_OP_ASGN1(node)->nd_index), + NEW_CHILD(ast_value, RNODE_OP_ASGN1(node)->nd_rvalue)); case NODE_OP_ASGN2: - return rb_ary_new_from_args(5, NEW_CHILD(ast, RNODE_OP_ASGN2(node)->nd_recv), + return rb_ary_new_from_args(5, NEW_CHILD(ast_value, RNODE_OP_ASGN2(node)->nd_recv), RBOOL(RNODE_OP_ASGN2(node)->nd_aid), ID2SYM(RNODE_OP_ASGN2(node)->nd_vid), ID2SYM(RNODE_OP_ASGN2(node)->nd_mid), - NEW_CHILD(ast, RNODE_OP_ASGN2(node)->nd_value)); + NEW_CHILD(ast_value, RNODE_OP_ASGN2(node)->nd_value)); case NODE_OP_ASGN_AND: - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_OP_ASGN_AND(node)->nd_head), ID2SYM(idANDOP), - NEW_CHILD(ast, RNODE_OP_ASGN_AND(node)->nd_value)); + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_OP_ASGN_AND(node)->nd_head), ID2SYM(idANDOP), + NEW_CHILD(ast_value, RNODE_OP_ASGN_AND(node)->nd_value)); case NODE_OP_ASGN_OR: - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_OP_ASGN_OR(node)->nd_head), ID2SYM(idOROP), - NEW_CHILD(ast, RNODE_OP_ASGN_OR(node)->nd_value)); + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_OP_ASGN_OR(node)->nd_head), ID2SYM(idOROP), + NEW_CHILD(ast_value, RNODE_OP_ASGN_OR(node)->nd_value)); case NODE_OP_CDECL: - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_OP_CDECL(node)->nd_head), + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_OP_CDECL(node)->nd_head), ID2SYM(RNODE_OP_CDECL(node)->nd_aid), - NEW_CHILD(ast, RNODE_OP_CDECL(node)->nd_value)); + NEW_CHILD(ast_value, RNODE_OP_CDECL(node)->nd_value)); case NODE_CALL: - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_CALL(node)->nd_recv), + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_CALL(node)->nd_recv), ID2SYM(RNODE_CALL(node)->nd_mid), - NEW_CHILD(ast, RNODE_CALL(node)->nd_args)); + NEW_CHILD(ast_value, RNODE_CALL(node)->nd_args)); case NODE_OPCALL: - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_OPCALL(node)->nd_recv), + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_OPCALL(node)->nd_recv), ID2SYM(RNODE_OPCALL(node)->nd_mid), - NEW_CHILD(ast, RNODE_OPCALL(node)->nd_args)); + NEW_CHILD(ast_value, RNODE_OPCALL(node)->nd_args)); case NODE_QCALL: - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_QCALL(node)->nd_recv), + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_QCALL(node)->nd_recv), ID2SYM(RNODE_QCALL(node)->nd_mid), - NEW_CHILD(ast, RNODE_QCALL(node)->nd_args)); + NEW_CHILD(ast_value, RNODE_QCALL(node)->nd_args)); case NODE_FCALL: return rb_ary_new_from_args(2, ID2SYM(RNODE_FCALL(node)->nd_mid), - NEW_CHILD(ast, RNODE_FCALL(node)->nd_args)); + NEW_CHILD(ast_value, RNODE_FCALL(node)->nd_args)); case NODE_VCALL: return rb_ary_new_from_args(1, ID2SYM(RNODE_VCALL(node)->nd_mid)); case NODE_SUPER: - return rb_ary_new_from_node_args(ast, 1, RNODE_SUPER(node)->nd_args); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_SUPER(node)->nd_args); case NODE_ZSUPER: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_LIST: - return dump_array(ast, RNODE_LIST(node)); + return dump_array(ast_value, RNODE_LIST(node)); case NODE_ZLIST: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_HASH: - return rb_ary_new_from_node_args(ast, 1, RNODE_HASH(node)->nd_head); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_HASH(node)->nd_head); case NODE_YIELD: - return rb_ary_new_from_node_args(ast, 1, RNODE_YIELD(node)->nd_head); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_YIELD(node)->nd_head); case NODE_LVAR: return rb_ary_new_from_args(1, var_name(RNODE_LVAR(node)->nd_vid)); case NODE_DVAR: @@ -532,11 +523,11 @@ node_children(rb_ast_t *ast, const NODE *node) return rb_ary_new_from_args(1, rb_node_regx_string_val(node)); case NODE_MATCH2: if (RNODE_MATCH2(node)->nd_args) { - return rb_ary_new_from_node_args(ast, 3, RNODE_MATCH2(node)->nd_recv, RNODE_MATCH2(node)->nd_value, RNODE_MATCH2(node)->nd_args); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_MATCH2(node)->nd_recv, RNODE_MATCH2(node)->nd_value, RNODE_MATCH2(node)->nd_args); } - return rb_ary_new_from_node_args(ast, 2, RNODE_MATCH2(node)->nd_recv, RNODE_MATCH2(node)->nd_value); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_MATCH2(node)->nd_recv, RNODE_MATCH2(node)->nd_value); case NODE_MATCH3: - return rb_ary_new_from_node_args(ast, 2, RNODE_MATCH3(node)->nd_recv, RNODE_MATCH3(node)->nd_value); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_MATCH3(node)->nd_recv, RNODE_MATCH3(node)->nd_value); case NODE_STR: case NODE_XSTR: return rb_ary_new_from_args(1, rb_node_str_string_val(node)); @@ -551,7 +542,7 @@ node_children(rb_ast_t *ast, const NODE *node) case NODE_REGX: return rb_ary_new_from_args(1, rb_node_regx_string_val(node)); case NODE_ONCE: - return rb_ary_new_from_node_args(ast, 1, RNODE_ONCE(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_ONCE(node)->nd_body); case NODE_DSTR: case NODE_DXSTR: case NODE_DREGX: @@ -560,91 +551,91 @@ node_children(rb_ast_t *ast, const NODE *node) struct RNode_LIST *n = RNODE_DSTR(node)->nd_next; VALUE head = Qnil, next = Qnil; if (n) { - head = NEW_CHILD(ast, n->nd_head); - next = NEW_CHILD(ast, n->nd_next); + head = NEW_CHILD(ast_value, n->nd_head); + next = NEW_CHILD(ast_value, n->nd_next); } return rb_ary_new_from_args(3, rb_node_dstr_string_val(node), head, next); } case NODE_SYM: return rb_ary_new_from_args(1, rb_node_sym_string_val(node)); case NODE_EVSTR: - return rb_ary_new_from_node_args(ast, 1, RNODE_EVSTR(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_EVSTR(node)->nd_body); case NODE_ARGSCAT: - return rb_ary_new_from_node_args(ast, 2, RNODE_ARGSCAT(node)->nd_head, RNODE_ARGSCAT(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_ARGSCAT(node)->nd_head, RNODE_ARGSCAT(node)->nd_body); case NODE_ARGSPUSH: - return rb_ary_new_from_node_args(ast, 2, RNODE_ARGSPUSH(node)->nd_head, RNODE_ARGSPUSH(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_ARGSPUSH(node)->nd_head, RNODE_ARGSPUSH(node)->nd_body); case NODE_SPLAT: - return rb_ary_new_from_node_args(ast, 1, RNODE_SPLAT(node)->nd_head); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_SPLAT(node)->nd_head); case NODE_BLOCK_PASS: - return rb_ary_new_from_node_args(ast, 2, RNODE_BLOCK_PASS(node)->nd_head, RNODE_BLOCK_PASS(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_BLOCK_PASS(node)->nd_head, RNODE_BLOCK_PASS(node)->nd_body); case NODE_DEFN: - return rb_ary_new_from_args(2, ID2SYM(RNODE_DEFN(node)->nd_mid), NEW_CHILD(ast, RNODE_DEFN(node)->nd_defn)); + return rb_ary_new_from_args(2, ID2SYM(RNODE_DEFN(node)->nd_mid), NEW_CHILD(ast_value, RNODE_DEFN(node)->nd_defn)); case NODE_DEFS: - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_DEFS(node)->nd_recv), ID2SYM(RNODE_DEFS(node)->nd_mid), NEW_CHILD(ast, RNODE_DEFS(node)->nd_defn)); + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_DEFS(node)->nd_recv), ID2SYM(RNODE_DEFS(node)->nd_mid), NEW_CHILD(ast_value, RNODE_DEFS(node)->nd_defn)); case NODE_ALIAS: - return rb_ary_new_from_node_args(ast, 2, RNODE_ALIAS(node)->nd_1st, RNODE_ALIAS(node)->nd_2nd); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_ALIAS(node)->nd_1st, RNODE_ALIAS(node)->nd_2nd); case NODE_VALIAS: return rb_ary_new_from_args(2, ID2SYM(RNODE_VALIAS(node)->nd_alias), ID2SYM(RNODE_VALIAS(node)->nd_orig)); case NODE_UNDEF: - return rb_ary_new_from_node_args(ast, 1, RNODE_UNDEF(node)->nd_undef); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_UNDEF(node)->nd_undef); case NODE_CLASS: - return rb_ary_new_from_node_args(ast, 3, RNODE_CLASS(node)->nd_cpath, RNODE_CLASS(node)->nd_super, RNODE_CLASS(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 3, RNODE_CLASS(node)->nd_cpath, RNODE_CLASS(node)->nd_super, RNODE_CLASS(node)->nd_body); case NODE_MODULE: - return rb_ary_new_from_node_args(ast, 2, RNODE_MODULE(node)->nd_cpath, RNODE_MODULE(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_MODULE(node)->nd_cpath, RNODE_MODULE(node)->nd_body); case NODE_SCLASS: - return rb_ary_new_from_node_args(ast, 2, RNODE_SCLASS(node)->nd_recv, RNODE_SCLASS(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_SCLASS(node)->nd_recv, RNODE_SCLASS(node)->nd_body); case NODE_COLON2: - return rb_ary_new_from_args(2, NEW_CHILD(ast, RNODE_COLON2(node)->nd_head), ID2SYM(RNODE_COLON2(node)->nd_mid)); + return rb_ary_new_from_args(2, NEW_CHILD(ast_value, RNODE_COLON2(node)->nd_head), ID2SYM(RNODE_COLON2(node)->nd_mid)); case NODE_COLON3: return rb_ary_new_from_args(1, ID2SYM(RNODE_COLON3(node)->nd_mid)); case NODE_DOT2: case NODE_DOT3: case NODE_FLIP2: case NODE_FLIP3: - return rb_ary_new_from_node_args(ast, 2, RNODE_DOT2(node)->nd_beg, RNODE_DOT2(node)->nd_end); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_DOT2(node)->nd_beg, RNODE_DOT2(node)->nd_end); case NODE_SELF: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_NIL: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_TRUE: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_FALSE: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_ERRINFO: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_DEFINED: - return rb_ary_new_from_node_args(ast, 1, RNODE_DEFINED(node)->nd_head); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_DEFINED(node)->nd_head); case NODE_POSTEXE: - return rb_ary_new_from_node_args(ast, 1, RNODE_POSTEXE(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_POSTEXE(node)->nd_body); case NODE_ATTRASGN: - return rb_ary_new_from_args(3, NEW_CHILD(ast, RNODE_ATTRASGN(node)->nd_recv), ID2SYM(RNODE_ATTRASGN(node)->nd_mid), NEW_CHILD(ast, RNODE_ATTRASGN(node)->nd_args)); + return rb_ary_new_from_args(3, NEW_CHILD(ast_value, RNODE_ATTRASGN(node)->nd_recv), ID2SYM(RNODE_ATTRASGN(node)->nd_mid), NEW_CHILD(ast_value, RNODE_ATTRASGN(node)->nd_args)); case NODE_LAMBDA: - return rb_ary_new_from_node_args(ast, 1, RNODE_LAMBDA(node)->nd_body); + return rb_ary_new_from_node_args(ast_value, 1, RNODE_LAMBDA(node)->nd_body); case NODE_OPT_ARG: - return rb_ary_new_from_node_args(ast, 2, RNODE_OPT_ARG(node)->nd_body, RNODE_OPT_ARG(node)->nd_next); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_OPT_ARG(node)->nd_body, RNODE_OPT_ARG(node)->nd_next); case NODE_KW_ARG: - return rb_ary_new_from_node_args(ast, 2, RNODE_KW_ARG(node)->nd_body, RNODE_KW_ARG(node)->nd_next); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_KW_ARG(node)->nd_body, RNODE_KW_ARG(node)->nd_next); case NODE_POSTARG: if (NODE_NAMED_REST_P(RNODE_POSTARG(node)->nd_1st)) { - return rb_ary_new_from_node_args(ast, 2, RNODE_POSTARG(node)->nd_1st, RNODE_POSTARG(node)->nd_2nd); + return rb_ary_new_from_node_args(ast_value, 2, RNODE_POSTARG(node)->nd_1st, RNODE_POSTARG(node)->nd_2nd); } return rb_ary_new_from_args(2, no_name_rest(), - NEW_CHILD(ast, RNODE_POSTARG(node)->nd_2nd)); + NEW_CHILD(ast_value, RNODE_POSTARG(node)->nd_2nd)); case NODE_ARGS: { struct rb_args_info *ainfo = &RNODE_ARGS(node)->nd_ainfo; return rb_ary_new_from_args(10, INT2NUM(ainfo->pre_args_num), - NEW_CHILD(ast, ainfo->pre_init), - NEW_CHILD(ast, (NODE *)ainfo->opt_args), + NEW_CHILD(ast_value, ainfo->pre_init), + NEW_CHILD(ast_value, (NODE *)ainfo->opt_args), var_name(ainfo->first_post_arg), INT2NUM(ainfo->post_args_num), - NEW_CHILD(ast, ainfo->post_init), + NEW_CHILD(ast_value, ainfo->post_init), (ainfo->rest_arg == NODE_SPECIAL_EXCESSIVE_COMMA ? ID2SYM(rb_intern("NODE_SPECIAL_EXCESSIVE_COMMA")) : var_name(ainfo->rest_arg)), - (ainfo->no_kwarg ? Qfalse : NEW_CHILD(ast, (NODE *)ainfo->kw_args)), - (ainfo->no_kwarg ? Qfalse : NEW_CHILD(ast, ainfo->kw_rest_arg)), + (ainfo->no_kwarg ? Qfalse : NEW_CHILD(ast_value, (NODE *)ainfo->kw_args)), + (ainfo->no_kwarg ? Qfalse : NEW_CHILD(ast_value, ainfo->kw_rest_arg)), var_name(ainfo->block_arg)); } case NODE_SCOPE: @@ -655,35 +646,35 @@ node_children(rb_ast_t *ast, const NODE *node) for (i = 0; i < size; i++) { rb_ary_push(locals, var_name(tbl->ids[i])); } - return rb_ary_new_from_args(3, locals, NEW_CHILD(ast, (NODE *)RNODE_SCOPE(node)->nd_args), NEW_CHILD(ast, RNODE_SCOPE(node)->nd_body)); + return rb_ary_new_from_args(3, locals, NEW_CHILD(ast_value, (NODE *)RNODE_SCOPE(node)->nd_args), NEW_CHILD(ast_value, RNODE_SCOPE(node)->nd_body)); } case NODE_ARYPTN: { - VALUE rest = rest_arg(ast, RNODE_ARYPTN(node)->rest_arg); + VALUE rest = rest_arg(ast_value, RNODE_ARYPTN(node)->rest_arg); return rb_ary_new_from_args(4, - NEW_CHILD(ast, RNODE_ARYPTN(node)->nd_pconst), - NEW_CHILD(ast, RNODE_ARYPTN(node)->pre_args), + NEW_CHILD(ast_value, RNODE_ARYPTN(node)->nd_pconst), + NEW_CHILD(ast_value, RNODE_ARYPTN(node)->pre_args), rest, - NEW_CHILD(ast, RNODE_ARYPTN(node)->post_args)); + NEW_CHILD(ast_value, RNODE_ARYPTN(node)->post_args)); } case NODE_FNDPTN: { - VALUE pre_rest = rest_arg(ast, RNODE_FNDPTN(node)->pre_rest_arg); - VALUE post_rest = rest_arg(ast, RNODE_FNDPTN(node)->post_rest_arg); + VALUE pre_rest = rest_arg(ast_value, RNODE_FNDPTN(node)->pre_rest_arg); + VALUE post_rest = rest_arg(ast_value, RNODE_FNDPTN(node)->post_rest_arg); return rb_ary_new_from_args(4, - NEW_CHILD(ast, RNODE_FNDPTN(node)->nd_pconst), + NEW_CHILD(ast_value, RNODE_FNDPTN(node)->nd_pconst), pre_rest, - NEW_CHILD(ast, RNODE_FNDPTN(node)->args), + NEW_CHILD(ast_value, RNODE_FNDPTN(node)->args), post_rest); } case NODE_HSHPTN: { VALUE kwrest = RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD ? ID2SYM(rb_intern("NODE_SPECIAL_NO_REST_KEYWORD")) : - NEW_CHILD(ast, RNODE_HSHPTN(node)->nd_pkwrestarg); + NEW_CHILD(ast_value, RNODE_HSHPTN(node)->nd_pkwrestarg); return rb_ary_new_from_args(3, - NEW_CHILD(ast, RNODE_HSHPTN(node)->nd_pconst), - NEW_CHILD(ast, RNODE_HSHPTN(node)->nd_pkwargs), + NEW_CHILD(ast_value, RNODE_HSHPTN(node)->nd_pconst), + NEW_CHILD(ast_value, RNODE_HSHPTN(node)->nd_pkwargs), kwrest); } case NODE_LINE: @@ -693,7 +684,7 @@ node_children(rb_ast_t *ast, const NODE *node) case NODE_ENCODING: return rb_ary_new_from_args(1, rb_node_encoding_val(node)); case NODE_ERROR: - return rb_ary_new_from_node_args(ast, 0); + return rb_ary_new_from_node_args(ast_value, 0); case NODE_ARGS_AUX: case NODE_LAST: break; @@ -708,7 +699,7 @@ ast_node_children(rb_execution_context_t *ec, VALUE self) struct ASTNodeData *data; TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data); - return node_children(data->ast, data->node); + return node_children(data->ast_value, data->node); } static VALUE @@ -752,13 +743,15 @@ ast_node_all_tokens(rb_execution_context_t *ec, VALUE self) { long i; struct ASTNodeData *data; + rb_ast_t *ast; rb_parser_ary_t *parser_tokens; rb_parser_ast_token_t *parser_token; VALUE str, loc, token, all_tokens; TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data); + ast = rb_ruby_ast_data_get(data->ast_value); - parser_tokens = data->ast->node_buffer->tokens; + parser_tokens = ast->node_buffer->tokens; if (parser_tokens == NULL) { return Qnil; } @@ -805,9 +798,10 @@ static VALUE ast_node_script_lines(rb_execution_context_t *ec, VALUE self) { struct ASTNodeData *data; + rb_ast_t *ast; TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data); - rb_parser_ary_t *ret = data->ast->body.script_lines; - if (!ret || FIXNUM_P((VALUE)ret)) return Qnil; + ast = rb_ruby_ast_data_get(data->ast_value); + rb_parser_ary_t *ret = ast->body.script_lines; return rb_parser_build_script_lines_from(ret); } diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb index 20f121cdf4..120b78246c 100755 --- a/bootstraptest/runner.rb +++ b/bootstraptest/runner.rb @@ -76,6 +76,8 @@ bt = Struct.new(:ruby, :width, :indent, :platform, + :timeout, + :timeout_scale, ) BT = Class.new(bt) do def indent=(n) @@ -143,6 +145,10 @@ BT = Class.new(bt) do end super wn end + + def apply_timeout_scale(timeout) + timeout&.*(timeout_scale) + end end.new BT_STATE = Struct.new(:count, :error).new @@ -155,6 +161,8 @@ def main BT.color = nil BT.tty = nil BT.quiet = false + BT.timeout = 180 + BT.timeout_scale = (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? ? 3 : 1) # for --jit-wait # BT.wn = 1 dir = nil quiet = false @@ -185,14 +193,18 @@ def main warn "unknown --tty argument: #$3" if $3 BT.tty = !$1 || !$2 true - when /\A(-q|--q(uiet))\z/ + when /\A(-q|--q(uiet)?)\z/ quiet = true BT.quiet = true true when /\A-j(\d+)?/ BT.wn = $1.to_i true - when /\A(-v|--v(erbose))\z/ + when /\A--timeout=(\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?)(?::(\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?))?/ + BT.timeout = $1.to_f + BT.timeout_scale = $2.to_f if defined?($2) + true + when /\A(-v|--v(erbose)?)\z/ BT.verbose = true BT.quiet = false true @@ -204,6 +216,7 @@ Usage: #{File.basename($0, '.*')} --ruby=PATH [--sets=NAME,NAME,...] default: /tmp/bootstraptestXXXXX.tmpwd --color[=WHEN] Colorize the output. WHEN defaults to 'always' or can be 'never' or 'auto'. + --timeout=TIMEOUT Default timeout in seconds. -s, --stress stress test. -v, --verbose Output test name before exec. -q, --quiet Don\'t print header message. @@ -525,14 +538,16 @@ class Assertion < Struct.new(:src, :path, :lineno, :proc) end end - def get_result_string(opt = '', **argh) + def get_result_string(opt = '', timeout: BT.timeout, **argh) if BT.ruby + timeout = BT.apply_timeout_scale(timeout) filename = make_srcfile(**argh) begin kw = self.err ? {err: self.err} : {} out = IO.popen("#{BT.ruby} -W0 #{opt} #{filename}", **kw) pid = out.pid - out.read.tap{ Process.waitpid(pid); out.close } + th = Thread.new {out.read.tap {Process.waitpid(pid); out.close}} + th.value if th.join(timeout) ensure raise Interrupt if $? and $?.signaled? && $?.termsig == Signal.list["INT"] @@ -618,8 +633,9 @@ def assert_valid_syntax(testsrc, message = '') end end -def assert_normal_exit(testsrc, *rest, timeout: nil, **opt) +def assert_normal_exit(testsrc, *rest, timeout: BT.timeout, **opt) add_assertion testsrc, -> as do + timeout = BT.apply_timeout_scale(timeout) message, ignore_signals = rest message ||= '' as.show_progress(message) { @@ -673,9 +689,7 @@ end def assert_finish(timeout_seconds, testsrc, message = '') add_assertion testsrc, -> as do - if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # for --jit-wait - timeout_seconds *= 3 - end + timeout_seconds = BT.apply_timeout_scale(timeout_seconds) as.show_progress(message) { faildesc = nil diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 4868810308..0390d38f9c 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -601,7 +601,7 @@ assert_equal '{:ok=>3}', %q{ end 3.times.map{Ractor.receive}.tally -} +} unless yjit_enabled? # `[BUG] Bus Error at 0x000000010b7002d0` in jit_exec() # unshareable object are copied assert_equal 'false', %q{ @@ -1466,6 +1466,25 @@ assert_equal '[:ok, :ok]', %q{ end } +# Ractor.select is interruptible +assert_normal_exit %q{ + trap(:INT) do + exit + end + + r = Ractor.new do + loop do + sleep 1 + end + end + + Thread.new do + sleep 0.5 + Process.kill(:INT, Process.pid) + end + Ractor.select(r) +} + # Ractor-local storage assert_equal '[nil, "b", "a"]', %q{ ans = [] diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 6b7d483926..8b8f7d1e39 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -2317,6 +2317,19 @@ assert_equal '123', %q{ foo(Foo) } +# Test EP == BP invalidation with moving ISEQs +assert_equal 'ok', %q{ + def entry + ok = proc { :ok } # set #entry as an EP-escaping ISEQ + [nil].reverse_each do # avoid exiting the JIT frame on the constant + GC.compact # move #entry ISEQ + end + ok # should be read off of escaped EP + end + + entry.call +} + # invokesuper edge case assert_equal '[:A, [:A, :B]]', %q{ class B @@ -4770,6 +4783,19 @@ assert_equal '[:ok, :ok, :ok]', %q{ tests } +# regression test for invalidating an empty block +assert_equal '0', %q{ + def foo = (* = 1).pred + + foo # compile it + + class Integer + def to_ary = [] # invalidate + end + + foo # try again +} unless rjit_enabled? # doesn't work on RJIT + # test integer left shift with constant rhs assert_equal [0x80000000000, 'a+', :ok].inspect, %q{ def shift(val) = val << 43 @@ -4786,3 +4812,171 @@ assert_equal [0x80000000000, 'a+', :ok].inspect, %q{ tests } + +# test String#stebyte with arguments that need conversion +assert_equal "abc", %q{ + str = +"a00" + def change_bytes(str, one, two) + str.setbyte(one, "b".ord) + str.setbyte(2, two) + end + + to_int_1 = Object.new + to_int_99 = Object.new + def to_int_1.to_int = 1 + def to_int_99.to_int = 99 + + change_bytes(str, to_int_1, to_int_99) + str +} + +# test --yjit-verify-ctx for arrays with a singleton class +assert_equal "ok", %q{ + class Array + def foo + self.singleton_class.define_method(:first) { :ok } + first + end + end + + def test = [].foo + + test +} + +assert_equal '["raised", "Module", "Object"]', %q{ + def foo(obj) + obj.superclass.name + end + + ret = [] + + begin + foo(Class.allocate) + rescue TypeError + ret << 'raised' + end + + ret += [foo(Class), foo(Class.new)] +} + +# test TrueClass#=== before and after redefining TrueClass#== +assert_equal '[[true, false, false], [true, true, false], [true, :error, :error]]', %q{ + def true_eqq(x) + true === x + rescue NoMethodError + :error + end + + def test + [ + # first one is always true because rb_equal does object comparison before calling #== + true_eqq(true), + # these will use TrueClass#== + true_eqq(false), + true_eqq(:truthy), + ] + end + + results = [test] + + class TrueClass + def ==(x) + !x + end + end + + results << test + + class TrueClass + undef_method :== + end + + results << test +} unless rjit_enabled? # Not yet working on RJIT + +# test FalseClass#=== before and after redefining FalseClass#== +assert_equal '[[true, false, false], [true, false, true], [true, :error, :error]]', %q{ + def case_equal(x, y) + x === y + rescue NoMethodError + :error + end + + def test + [ + # first one is always true because rb_equal does object comparison before calling #== + case_equal(false, false), + # these will use #== + case_equal(false, true), + case_equal(false, nil), + ] + end + + results = [test] + + class FalseClass + def ==(x) + !x + end + end + + results << test + + class FalseClass + undef_method :== + end + + results << test +} unless rjit_enabled? # Not yet working on RJIT + +# test NilClass#=== before and after redefining NilClass#== +assert_equal '[[true, false, false], [true, false, true], [true, :error, :error]]', %q{ + def case_equal(x, y) + x === y + rescue NoMethodError + :error + end + + def test + [ + # first one is always true because rb_equal does object comparison before calling #== + case_equal(nil, nil), + # these will use #== + case_equal(nil, true), + case_equal(nil, false), + ] + end + + results = [test] + + class NilClass + def ==(x) + !x + end + end + + results << test + + class NilClass + undef_method :== + end + + results << test +} unless rjit_enabled? # Not yet working on RJIT + +# test struct accessors fire c_call events +assert_equal '[[:c_call, :x=], [:c_call, :x]]', %q{ + c = Struct.new(:x) + obj = c.new + + events = [] + TracePoint.new(:c_call) do + events << [_1.event, _1.method_id] + end.enable do + obj.x = 100 + obj.x + end + + events +} @@ -216,6 +216,11 @@ srcs: $(srcdir)/lib/prism/dsl.rb $(srcdir)/lib/prism/dsl.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/dsl.rb.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/dsl.rb $(srcdir)/lib/prism/dsl.rb +main: $(srcdir)/lib/prism/inspect_visitor.rb +srcs: $(srcdir)/lib/prism/inspect_visitor.rb +$(srcdir)/lib/prism/inspect_visitor.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/inspect_visitor.rb.erb + $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/inspect_visitor.rb $(srcdir)/lib/prism/inspect_visitor.rb + main: $(srcdir)/lib/prism/mutation_compiler.rb srcs: $(srcdir)/lib/prism/mutation_compiler.rb $(srcdir)/lib/prism/mutation_compiler.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/mutation_compiler.rb.erb @@ -411,7 +416,7 @@ configure-ext: $(EXTS_MK) build-ext: $(EXTS_MK) $(Q)$(MAKE) -f $(EXTS_MK) $(mflags) libdir="$(libdir)" LIBRUBY_EXTS=$(LIBRUBY_EXTS) \ EXTENCS="$(ENCOBJS)" BASERUBY="$(BASERUBY)" MINIRUBY="$(MINIRUBY)" \ - UPDATE_LIBRARIES=no $(EXTSTATIC) + $(EXTSTATIC) $(Q)$(MAKE) $(EXTS_NOTE) exts-note: $(EXTS_MK) @@ -483,9 +488,9 @@ docs: srcs-doc $(DOCTARGETS) pkgconfig-data: $(ruby_pc) $(ruby_pc): $(srcdir)/template/ruby.pc.in config.status -install-all: pre-install-all docs do-install-all post-install-all +install-all: pre-install-all do-install-all post-install-all pre-install-all:: all pre-install-local pre-install-ext pre-install-gem pre-install-doc -do-install-all: pre-install-all +do-install-all: pre-install-all $(DOT_WAIT) docs $(INSTRUBY) --make="$(MAKE)" $(INSTRUBY_ARGS) --install=all $(INSTALL_DOC_OPTS) post-install-all:: post-install-local post-install-ext post-install-gem post-install-doc @$(NULLCMD) @@ -957,12 +962,16 @@ PRECHECK_TEST_ALL = yes-test-all-precheck test-all: $(TEST_RUNNABLE)-test-all yes-test-all: $(PRECHECK_TEST_ALL) $(ACTIONS_GROUP) - $(gnumake_recursive)$(Q)$(exec) $(RUNRUBY) "$(TESTSDIR)/runner.rb" --ruby="$(RUNRUBY)" \ + $(gnumake_recursive)$(Q)$(exec) $(RUNRUBY) -r$(tooldir)/lib/_tmpdir \ + "$(TESTSDIR)/runner.rb" --ruby="$(RUNRUBY)" \ $(TEST_EXCLUDES) $(TESTOPTS) $(TESTS) $(ACTIONS_ENDGROUP) TESTS_BUILD = mkmf no-test-all: PHONY - $(gnumake_recursive)$(MINIRUBY) -I"$(srcdir)/lib" "$(TESTSDIR)/runner.rb" $(TESTOPTS) $(TESTS_BUILD) + $(ACTIONS_GROUP) + $(gnumake_recursive)$(MINIRUBY) -I"$(srcdir)/lib" -r$(tooldir)/lib/_tmpdir \ + "$(TESTSDIR)/runner.rb" $(TESTOPTS) $(TESTS_BUILD) + $(ACTIONS_ENDGROUP) test-almost: test-all yes-test-almost: yes-test-all @@ -1004,7 +1013,7 @@ test-spec: $(TEST_RUNNABLE)-test-spec yes-test-spec: yes-test-spec-precheck $(ACTIONS_GROUP) $(gnumake_recursive)$(Q) \ - $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/rubyspec_temp \ + $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/lib/_tmpdir \ $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/default.mspec $(MSPECOPT) $(SPECOPTS) $(ACTIONS_ENDGROUP) no-test-spec: @@ -1013,7 +1022,7 @@ test-prism-spec: $(TEST_RUNNABLE)-test-prism-spec yes-test-prism-spec: yes-test-spec-precheck $(ACTIONS_GROUP) $(gnumake_recursive)$(Q) \ - $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/rubyspec_temp \ + $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/lib/_tmpdir \ $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/default.mspec -B $(srcdir)/spec/prism.mspec $(MSPECOPT) $(SPECOPTS) $(ACTIONS_ENDGROUP) no-test-prism-spec: @@ -1606,35 +1615,13 @@ test-bundled-gems-spec: $(TEST_RUNNABLE)-test-bundled-gems-spec yes-test-bundled-gems-spec: yes-test-spec-precheck $(PREPARE_BUNDLED_GEMS) $(ACTIONS_GROUP) $(gnumake_recursive)$(Q) \ - $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/rubyspec_temp \ + $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/lib/_tmpdir \ $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/bundled_gems.mspec $(MSPECOPT) $(SPECOPTS) $(ACTIONS_ENDGROUP) no-test-bundled-gems-spec: -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) - $(XRUBY) -C "$(srcdir)" bin/gem install --no-document \ - --install-dir .bundle --conservative "rspec:~> 3" - $(ACTIONS_ENDGROUP) -RSPECOPTS = -SYNTAX_SUGGEST_SPECS = -PREPARE_SYNTAX_SUGGEST = $(TEST_RUNNABLE)-test-syntax-suggest-prepare -test-syntax-suggest: $(TEST_RUNNABLE)-test-syntax-suggest -yes-test-syntax-suggest: $(PREPARE_SYNTAX_SUGGEST) - $(ACTIONS_GROUP) - $(XRUBY) -C $(srcdir) -Ispec/syntax_suggest:spec/lib .bundle/bin/rspec \ - --require rspec/expectations \ - --require spec_helper --require formatter_overrides --require spec_coverage \ - $(RSPECOPTS) spec/syntax_suggest/$(SYNTAX_SUGGEST_SPECS) - $(ACTIONS_ENDGROUP) -no-test-syntax-suggest: +test-syntax-suggest: check: $(DOT_WAIT) $(PREPARE_SYNTAX_SUGGEST) test-syntax-suggest @@ -2204,6 +2191,7 @@ array.$(OBJEXT): {$(VPATH)}internal/special_consts.h array.$(OBJEXT): {$(VPATH)}internal/static_assert.h array.$(OBJEXT): {$(VPATH)}internal/stdalign.h array.$(OBJEXT): {$(VPATH)}internal/stdbool.h +array.$(OBJEXT): {$(VPATH)}internal/stdckdint.h array.$(OBJEXT): {$(VPATH)}internal/symbol.h array.$(OBJEXT): {$(VPATH)}internal/value.h array.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -2439,6 +2427,7 @@ ast.$(OBJEXT): {$(VPATH)}internal/special_consts.h ast.$(OBJEXT): {$(VPATH)}internal/static_assert.h ast.$(OBJEXT): {$(VPATH)}internal/stdalign.h ast.$(OBJEXT): {$(VPATH)}internal/stdbool.h +ast.$(OBJEXT): {$(VPATH)}internal/stdckdint.h ast.$(OBJEXT): {$(VPATH)}internal/symbol.h ast.$(OBJEXT): {$(VPATH)}internal/value.h ast.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -2651,6 +2640,7 @@ bignum.$(OBJEXT): {$(VPATH)}internal/special_consts.h bignum.$(OBJEXT): {$(VPATH)}internal/static_assert.h bignum.$(OBJEXT): {$(VPATH)}internal/stdalign.h bignum.$(OBJEXT): {$(VPATH)}internal/stdbool.h +bignum.$(OBJEXT): {$(VPATH)}internal/stdckdint.h bignum.$(OBJEXT): {$(VPATH)}internal/symbol.h bignum.$(OBJEXT): {$(VPATH)}internal/value.h bignum.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -2875,6 +2865,7 @@ builtin.$(OBJEXT): {$(VPATH)}internal/special_consts.h builtin.$(OBJEXT): {$(VPATH)}internal/static_assert.h builtin.$(OBJEXT): {$(VPATH)}internal/stdalign.h builtin.$(OBJEXT): {$(VPATH)}internal/stdbool.h +builtin.$(OBJEXT): {$(VPATH)}internal/stdckdint.h builtin.$(OBJEXT): {$(VPATH)}internal/symbol.h builtin.$(OBJEXT): {$(VPATH)}internal/value.h builtin.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -3085,6 +3076,7 @@ class.$(OBJEXT): {$(VPATH)}internal/special_consts.h class.$(OBJEXT): {$(VPATH)}internal/static_assert.h class.$(OBJEXT): {$(VPATH)}internal/stdalign.h class.$(OBJEXT): {$(VPATH)}internal/stdbool.h +class.$(OBJEXT): {$(VPATH)}internal/stdckdint.h class.$(OBJEXT): {$(VPATH)}internal/symbol.h class.$(OBJEXT): {$(VPATH)}internal/value.h class.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -3278,6 +3270,7 @@ compar.$(OBJEXT): {$(VPATH)}internal/special_consts.h compar.$(OBJEXT): {$(VPATH)}internal/static_assert.h compar.$(OBJEXT): {$(VPATH)}internal/stdalign.h compar.$(OBJEXT): {$(VPATH)}internal/stdbool.h +compar.$(OBJEXT): {$(VPATH)}internal/stdckdint.h compar.$(OBJEXT): {$(VPATH)}internal/symbol.h compar.$(OBJEXT): {$(VPATH)}internal/value.h compar.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -3312,6 +3305,7 @@ compile.$(OBJEXT): $(top_srcdir)/internal/imemo.h compile.$(OBJEXT): $(top_srcdir)/internal/io.h compile.$(OBJEXT): $(top_srcdir)/internal/numeric.h compile.$(OBJEXT): $(top_srcdir)/internal/object.h +compile.$(OBJEXT): $(top_srcdir)/internal/parse.h compile.$(OBJEXT): $(top_srcdir)/internal/rational.h compile.$(OBJEXT): $(top_srcdir)/internal/re.h compile.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h @@ -3515,6 +3509,7 @@ compile.$(OBJEXT): {$(VPATH)}internal/special_consts.h compile.$(OBJEXT): {$(VPATH)}internal/static_assert.h compile.$(OBJEXT): {$(VPATH)}internal/stdalign.h compile.$(OBJEXT): {$(VPATH)}internal/stdbool.h +compile.$(OBJEXT): {$(VPATH)}internal/stdckdint.h compile.$(OBJEXT): {$(VPATH)}internal/symbol.h compile.$(OBJEXT): {$(VPATH)}internal/value.h compile.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -3742,6 +3737,7 @@ complex.$(OBJEXT): {$(VPATH)}internal/special_consts.h complex.$(OBJEXT): {$(VPATH)}internal/static_assert.h complex.$(OBJEXT): {$(VPATH)}internal/stdalign.h complex.$(OBJEXT): {$(VPATH)}internal/stdbool.h +complex.$(OBJEXT): {$(VPATH)}internal/stdckdint.h complex.$(OBJEXT): {$(VPATH)}internal/symbol.h complex.$(OBJEXT): {$(VPATH)}internal/value.h complex.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -3975,6 +3971,7 @@ cont.$(OBJEXT): {$(VPATH)}internal/special_consts.h cont.$(OBJEXT): {$(VPATH)}internal/static_assert.h cont.$(OBJEXT): {$(VPATH)}internal/stdalign.h cont.$(OBJEXT): {$(VPATH)}internal/stdbool.h +cont.$(OBJEXT): {$(VPATH)}internal/stdckdint.h cont.$(OBJEXT): {$(VPATH)}internal/symbol.h cont.$(OBJEXT): {$(VPATH)}internal/value.h cont.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -4190,6 +4187,7 @@ debug.$(OBJEXT): {$(VPATH)}internal/special_consts.h debug.$(OBJEXT): {$(VPATH)}internal/static_assert.h debug.$(OBJEXT): {$(VPATH)}internal/stdalign.h debug.$(OBJEXT): {$(VPATH)}internal/stdbool.h +debug.$(OBJEXT): {$(VPATH)}internal/stdckdint.h debug.$(OBJEXT): {$(VPATH)}internal/symbol.h debug.$(OBJEXT): {$(VPATH)}internal/value.h debug.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -4368,6 +4366,7 @@ debug_counter.$(OBJEXT): {$(VPATH)}internal/special_consts.h debug_counter.$(OBJEXT): {$(VPATH)}internal/static_assert.h debug_counter.$(OBJEXT): {$(VPATH)}internal/stdalign.h debug_counter.$(OBJEXT): {$(VPATH)}internal/stdbool.h +debug_counter.$(OBJEXT): {$(VPATH)}internal/stdckdint.h debug_counter.$(OBJEXT): {$(VPATH)}internal/symbol.h debug_counter.$(OBJEXT): {$(VPATH)}internal/value.h debug_counter.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -4568,6 +4567,7 @@ dir.$(OBJEXT): {$(VPATH)}internal/special_consts.h dir.$(OBJEXT): {$(VPATH)}internal/static_assert.h dir.$(OBJEXT): {$(VPATH)}internal/stdalign.h dir.$(OBJEXT): {$(VPATH)}internal/stdbool.h +dir.$(OBJEXT): {$(VPATH)}internal/stdckdint.h dir.$(OBJEXT): {$(VPATH)}internal/symbol.h dir.$(OBJEXT): {$(VPATH)}internal/value.h dir.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -4744,6 +4744,7 @@ dln.$(OBJEXT): {$(VPATH)}internal/special_consts.h dln.$(OBJEXT): {$(VPATH)}internal/static_assert.h dln.$(OBJEXT): {$(VPATH)}internal/stdalign.h dln.$(OBJEXT): {$(VPATH)}internal/stdbool.h +dln.$(OBJEXT): {$(VPATH)}internal/stdckdint.h dln.$(OBJEXT): {$(VPATH)}internal/symbol.h dln.$(OBJEXT): {$(VPATH)}internal/value.h dln.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -4901,6 +4902,7 @@ dln_find.$(OBJEXT): {$(VPATH)}internal/special_consts.h dln_find.$(OBJEXT): {$(VPATH)}internal/static_assert.h dln_find.$(OBJEXT): {$(VPATH)}internal/stdalign.h dln_find.$(OBJEXT): {$(VPATH)}internal/stdbool.h +dln_find.$(OBJEXT): {$(VPATH)}internal/stdckdint.h dln_find.$(OBJEXT): {$(VPATH)}internal/symbol.h dln_find.$(OBJEXT): {$(VPATH)}internal/value.h dln_find.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -5057,6 +5059,7 @@ dmydln.$(OBJEXT): {$(VPATH)}internal/special_consts.h dmydln.$(OBJEXT): {$(VPATH)}internal/static_assert.h dmydln.$(OBJEXT): {$(VPATH)}internal/stdalign.h dmydln.$(OBJEXT): {$(VPATH)}internal/stdbool.h +dmydln.$(OBJEXT): {$(VPATH)}internal/stdckdint.h dmydln.$(OBJEXT): {$(VPATH)}internal/symbol.h dmydln.$(OBJEXT): {$(VPATH)}internal/value.h dmydln.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -6075,6 +6078,7 @@ encoding.$(OBJEXT): {$(VPATH)}internal/special_consts.h encoding.$(OBJEXT): {$(VPATH)}internal/static_assert.h encoding.$(OBJEXT): {$(VPATH)}internal/stdalign.h encoding.$(OBJEXT): {$(VPATH)}internal/stdbool.h +encoding.$(OBJEXT): {$(VPATH)}internal/stdckdint.h encoding.$(OBJEXT): {$(VPATH)}internal/symbol.h encoding.$(OBJEXT): {$(VPATH)}internal/value.h encoding.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -6284,6 +6288,7 @@ enum.$(OBJEXT): {$(VPATH)}internal/special_consts.h enum.$(OBJEXT): {$(VPATH)}internal/static_assert.h enum.$(OBJEXT): {$(VPATH)}internal/stdalign.h enum.$(OBJEXT): {$(VPATH)}internal/stdbool.h +enum.$(OBJEXT): {$(VPATH)}internal/stdckdint.h enum.$(OBJEXT): {$(VPATH)}internal/symbol.h enum.$(OBJEXT): {$(VPATH)}internal/value.h enum.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -6491,6 +6496,7 @@ enumerator.$(OBJEXT): {$(VPATH)}internal/special_consts.h enumerator.$(OBJEXT): {$(VPATH)}internal/static_assert.h enumerator.$(OBJEXT): {$(VPATH)}internal/stdalign.h enumerator.$(OBJEXT): {$(VPATH)}internal/stdbool.h +enumerator.$(OBJEXT): {$(VPATH)}internal/stdckdint.h enumerator.$(OBJEXT): {$(VPATH)}internal/symbol.h enumerator.$(OBJEXT): {$(VPATH)}internal/value.h enumerator.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -6706,6 +6712,7 @@ error.$(OBJEXT): {$(VPATH)}internal/special_consts.h error.$(OBJEXT): {$(VPATH)}internal/static_assert.h error.$(OBJEXT): {$(VPATH)}internal/stdalign.h error.$(OBJEXT): {$(VPATH)}internal/stdbool.h +error.$(OBJEXT): {$(VPATH)}internal/stdckdint.h error.$(OBJEXT): {$(VPATH)}internal/symbol.h error.$(OBJEXT): {$(VPATH)}internal/value.h error.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -6950,6 +6957,7 @@ eval.$(OBJEXT): {$(VPATH)}internal/special_consts.h eval.$(OBJEXT): {$(VPATH)}internal/static_assert.h eval.$(OBJEXT): {$(VPATH)}internal/stdalign.h eval.$(OBJEXT): {$(VPATH)}internal/stdbool.h +eval.$(OBJEXT): {$(VPATH)}internal/stdckdint.h eval.$(OBJEXT): {$(VPATH)}internal/symbol.h eval.$(OBJEXT): {$(VPATH)}internal/value.h eval.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -7189,6 +7197,7 @@ file.$(OBJEXT): {$(VPATH)}internal/special_consts.h file.$(OBJEXT): {$(VPATH)}internal/static_assert.h file.$(OBJEXT): {$(VPATH)}internal/stdalign.h file.$(OBJEXT): {$(VPATH)}internal/stdbool.h +file.$(OBJEXT): {$(VPATH)}internal/stdckdint.h file.$(OBJEXT): {$(VPATH)}internal/symbol.h file.$(OBJEXT): {$(VPATH)}internal/value.h file.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -7432,6 +7441,7 @@ gc.$(OBJEXT): {$(VPATH)}internal/special_consts.h gc.$(OBJEXT): {$(VPATH)}internal/static_assert.h gc.$(OBJEXT): {$(VPATH)}internal/stdalign.h gc.$(OBJEXT): {$(VPATH)}internal/stdbool.h +gc.$(OBJEXT): {$(VPATH)}internal/stdckdint.h gc.$(OBJEXT): {$(VPATH)}internal/symbol.h gc.$(OBJEXT): {$(VPATH)}internal/value.h gc.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -7490,6 +7500,7 @@ goruby.$(OBJEXT): $(top_srcdir)/internal/fixnum.h goruby.$(OBJEXT): $(top_srcdir)/internal/gc.h goruby.$(OBJEXT): $(top_srcdir)/internal/imemo.h goruby.$(OBJEXT): $(top_srcdir)/internal/numeric.h +goruby.$(OBJEXT): $(top_srcdir)/internal/parse.h goruby.$(OBJEXT): $(top_srcdir)/internal/rational.h goruby.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h goruby.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h @@ -7683,6 +7694,7 @@ goruby.$(OBJEXT): {$(VPATH)}internal/special_consts.h goruby.$(OBJEXT): {$(VPATH)}internal/static_assert.h goruby.$(OBJEXT): {$(VPATH)}internal/stdalign.h goruby.$(OBJEXT): {$(VPATH)}internal/stdbool.h +goruby.$(OBJEXT): {$(VPATH)}internal/stdckdint.h goruby.$(OBJEXT): {$(VPATH)}internal/symbol.h goruby.$(OBJEXT): {$(VPATH)}internal/value.h goruby.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -7926,6 +7938,7 @@ hash.$(OBJEXT): {$(VPATH)}internal/st.h hash.$(OBJEXT): {$(VPATH)}internal/static_assert.h hash.$(OBJEXT): {$(VPATH)}internal/stdalign.h hash.$(OBJEXT): {$(VPATH)}internal/stdbool.h +hash.$(OBJEXT): {$(VPATH)}internal/stdckdint.h hash.$(OBJEXT): {$(VPATH)}internal/symbol.h hash.$(OBJEXT): {$(VPATH)}internal/value.h hash.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -8139,6 +8152,7 @@ imemo.$(OBJEXT): {$(VPATH)}internal/special_consts.h imemo.$(OBJEXT): {$(VPATH)}internal/static_assert.h imemo.$(OBJEXT): {$(VPATH)}internal/stdalign.h imemo.$(OBJEXT): {$(VPATH)}internal/stdbool.h +imemo.$(OBJEXT): {$(VPATH)}internal/stdckdint.h imemo.$(OBJEXT): {$(VPATH)}internal/symbol.h imemo.$(OBJEXT): {$(VPATH)}internal/value.h imemo.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -8316,6 +8330,7 @@ inits.$(OBJEXT): {$(VPATH)}internal/special_consts.h inits.$(OBJEXT): {$(VPATH)}internal/static_assert.h inits.$(OBJEXT): {$(VPATH)}internal/stdalign.h inits.$(OBJEXT): {$(VPATH)}internal/stdbool.h +inits.$(OBJEXT): {$(VPATH)}internal/stdckdint.h inits.$(OBJEXT): {$(VPATH)}internal/symbol.h inits.$(OBJEXT): {$(VPATH)}internal/value.h inits.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -8523,6 +8538,7 @@ io.$(OBJEXT): {$(VPATH)}internal/special_consts.h io.$(OBJEXT): {$(VPATH)}internal/static_assert.h io.$(OBJEXT): {$(VPATH)}internal/stdalign.h io.$(OBJEXT): {$(VPATH)}internal/stdbool.h +io.$(OBJEXT): {$(VPATH)}internal/stdckdint.h io.$(OBJEXT): {$(VPATH)}internal/symbol.h io.$(OBJEXT): {$(VPATH)}internal/value.h io.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -8729,6 +8745,7 @@ io_buffer.$(OBJEXT): {$(VPATH)}internal/special_consts.h io_buffer.$(OBJEXT): {$(VPATH)}internal/static_assert.h io_buffer.$(OBJEXT): {$(VPATH)}internal/stdalign.h io_buffer.$(OBJEXT): {$(VPATH)}internal/stdbool.h +io_buffer.$(OBJEXT): {$(VPATH)}internal/stdckdint.h io_buffer.$(OBJEXT): {$(VPATH)}internal/symbol.h io_buffer.$(OBJEXT): {$(VPATH)}internal/value.h io_buffer.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -8967,6 +8984,7 @@ iseq.$(OBJEXT): {$(VPATH)}internal/special_consts.h iseq.$(OBJEXT): {$(VPATH)}internal/static_assert.h iseq.$(OBJEXT): {$(VPATH)}internal/stdalign.h iseq.$(OBJEXT): {$(VPATH)}internal/stdbool.h +iseq.$(OBJEXT): {$(VPATH)}internal/stdckdint.h iseq.$(OBJEXT): {$(VPATH)}internal/symbol.h iseq.$(OBJEXT): {$(VPATH)}internal/value.h iseq.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -9220,6 +9238,7 @@ load.$(OBJEXT): {$(VPATH)}internal/special_consts.h load.$(OBJEXT): {$(VPATH)}internal/static_assert.h load.$(OBJEXT): {$(VPATH)}internal/stdalign.h load.$(OBJEXT): {$(VPATH)}internal/stdbool.h +load.$(OBJEXT): {$(VPATH)}internal/stdckdint.h load.$(OBJEXT): {$(VPATH)}internal/symbol.h load.$(OBJEXT): {$(VPATH)}internal/value.h load.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -9398,6 +9417,7 @@ loadpath.$(OBJEXT): {$(VPATH)}internal/special_consts.h loadpath.$(OBJEXT): {$(VPATH)}internal/static_assert.h loadpath.$(OBJEXT): {$(VPATH)}internal/stdalign.h loadpath.$(OBJEXT): {$(VPATH)}internal/stdbool.h +loadpath.$(OBJEXT): {$(VPATH)}internal/stdckdint.h loadpath.$(OBJEXT): {$(VPATH)}internal/symbol.h loadpath.$(OBJEXT): {$(VPATH)}internal/value.h loadpath.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -9567,6 +9587,7 @@ localeinit.$(OBJEXT): {$(VPATH)}internal/special_consts.h localeinit.$(OBJEXT): {$(VPATH)}internal/static_assert.h localeinit.$(OBJEXT): {$(VPATH)}internal/stdalign.h localeinit.$(OBJEXT): {$(VPATH)}internal/stdbool.h +localeinit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h localeinit.$(OBJEXT): {$(VPATH)}internal/symbol.h localeinit.$(OBJEXT): {$(VPATH)}internal/value.h localeinit.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -9731,6 +9752,7 @@ main.$(OBJEXT): {$(VPATH)}internal/special_consts.h main.$(OBJEXT): {$(VPATH)}internal/static_assert.h main.$(OBJEXT): {$(VPATH)}internal/stdalign.h main.$(OBJEXT): {$(VPATH)}internal/stdbool.h +main.$(OBJEXT): {$(VPATH)}internal/stdckdint.h main.$(OBJEXT): {$(VPATH)}internal/symbol.h main.$(OBJEXT): {$(VPATH)}internal/value.h main.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -9936,6 +9958,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal/special_consts.h marshal.$(OBJEXT): {$(VPATH)}internal/static_assert.h marshal.$(OBJEXT): {$(VPATH)}internal/stdalign.h marshal.$(OBJEXT): {$(VPATH)}internal/stdbool.h +marshal.$(OBJEXT): {$(VPATH)}internal/stdckdint.h marshal.$(OBJEXT): {$(VPATH)}internal/symbol.h marshal.$(OBJEXT): {$(VPATH)}internal/value.h marshal.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -10125,6 +10148,7 @@ math.$(OBJEXT): {$(VPATH)}internal/special_consts.h math.$(OBJEXT): {$(VPATH)}internal/static_assert.h math.$(OBJEXT): {$(VPATH)}internal/stdalign.h math.$(OBJEXT): {$(VPATH)}internal/stdbool.h +math.$(OBJEXT): {$(VPATH)}internal/stdckdint.h math.$(OBJEXT): {$(VPATH)}internal/symbol.h math.$(OBJEXT): {$(VPATH)}internal/value.h math.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -10315,6 +10339,7 @@ memory_view.$(OBJEXT): {$(VPATH)}internal/special_consts.h memory_view.$(OBJEXT): {$(VPATH)}internal/static_assert.h memory_view.$(OBJEXT): {$(VPATH)}internal/stdalign.h memory_view.$(OBJEXT): {$(VPATH)}internal/stdbool.h +memory_view.$(OBJEXT): {$(VPATH)}internal/stdckdint.h memory_view.$(OBJEXT): {$(VPATH)}internal/symbol.h memory_view.$(OBJEXT): {$(VPATH)}internal/value.h memory_view.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -10357,6 +10382,7 @@ miniinit.$(OBJEXT): $(top_srcdir)/internal/fixnum.h miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h miniinit.$(OBJEXT): $(top_srcdir)/internal/imemo.h miniinit.$(OBJEXT): $(top_srcdir)/internal/numeric.h +miniinit.$(OBJEXT): $(top_srcdir)/internal/parse.h miniinit.$(OBJEXT): $(top_srcdir)/internal/rational.h miniinit.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h miniinit.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h @@ -10553,6 +10579,7 @@ miniinit.$(OBJEXT): {$(VPATH)}internal/special_consts.h miniinit.$(OBJEXT): {$(VPATH)}internal/static_assert.h miniinit.$(OBJEXT): {$(VPATH)}internal/stdalign.h miniinit.$(OBJEXT): {$(VPATH)}internal/stdbool.h +miniinit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h miniinit.$(OBJEXT): {$(VPATH)}internal/symbol.h miniinit.$(OBJEXT): {$(VPATH)}internal/value.h miniinit.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -10776,6 +10803,7 @@ node.$(OBJEXT): {$(VPATH)}internal/special_consts.h node.$(OBJEXT): {$(VPATH)}internal/static_assert.h node.$(OBJEXT): {$(VPATH)}internal/stdalign.h node.$(OBJEXT): {$(VPATH)}internal/stdbool.h +node.$(OBJEXT): {$(VPATH)}internal/stdckdint.h node.$(OBJEXT): {$(VPATH)}internal/symbol.h node.$(OBJEXT): {$(VPATH)}internal/value.h node.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -10816,6 +10844,7 @@ node_dump.$(OBJEXT): $(top_srcdir)/internal/gc.h node_dump.$(OBJEXT): $(top_srcdir)/internal/hash.h node_dump.$(OBJEXT): $(top_srcdir)/internal/imemo.h node_dump.$(OBJEXT): $(top_srcdir)/internal/numeric.h +node_dump.$(OBJEXT): $(top_srcdir)/internal/parse.h node_dump.$(OBJEXT): $(top_srcdir)/internal/rational.h node_dump.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h node_dump.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h @@ -10986,6 +11015,7 @@ node_dump.$(OBJEXT): {$(VPATH)}internal/special_consts.h node_dump.$(OBJEXT): {$(VPATH)}internal/static_assert.h node_dump.$(OBJEXT): {$(VPATH)}internal/stdalign.h node_dump.$(OBJEXT): {$(VPATH)}internal/stdbool.h +node_dump.$(OBJEXT): {$(VPATH)}internal/stdckdint.h node_dump.$(OBJEXT): {$(VPATH)}internal/symbol.h node_dump.$(OBJEXT): {$(VPATH)}internal/value.h node_dump.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -11200,6 +11230,7 @@ numeric.$(OBJEXT): {$(VPATH)}internal/special_consts.h numeric.$(OBJEXT): {$(VPATH)}internal/static_assert.h numeric.$(OBJEXT): {$(VPATH)}internal/stdalign.h numeric.$(OBJEXT): {$(VPATH)}internal/stdbool.h +numeric.$(OBJEXT): {$(VPATH)}internal/stdckdint.h numeric.$(OBJEXT): {$(VPATH)}internal/symbol.h numeric.$(OBJEXT): {$(VPATH)}internal/value.h numeric.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -11418,6 +11449,7 @@ object.$(OBJEXT): {$(VPATH)}internal/st.h object.$(OBJEXT): {$(VPATH)}internal/static_assert.h object.$(OBJEXT): {$(VPATH)}internal/stdalign.h object.$(OBJEXT): {$(VPATH)}internal/stdbool.h +object.$(OBJEXT): {$(VPATH)}internal/stdckdint.h object.$(OBJEXT): {$(VPATH)}internal/symbol.h object.$(OBJEXT): {$(VPATH)}internal/value.h object.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -11630,6 +11662,7 @@ pack.$(OBJEXT): {$(VPATH)}internal/special_consts.h pack.$(OBJEXT): {$(VPATH)}internal/static_assert.h pack.$(OBJEXT): {$(VPATH)}internal/stdalign.h pack.$(OBJEXT): {$(VPATH)}internal/stdbool.h +pack.$(OBJEXT): {$(VPATH)}internal/stdckdint.h pack.$(OBJEXT): {$(VPATH)}internal/symbol.h pack.$(OBJEXT): {$(VPATH)}internal/value.h pack.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -11851,6 +11884,7 @@ parse.$(OBJEXT): {$(VPATH)}internal/special_consts.h parse.$(OBJEXT): {$(VPATH)}internal/static_assert.h parse.$(OBJEXT): {$(VPATH)}internal/stdalign.h parse.$(OBJEXT): {$(VPATH)}internal/stdbool.h +parse.$(OBJEXT): {$(VPATH)}internal/stdckdint.h parse.$(OBJEXT): {$(VPATH)}internal/symbol.h parse.$(OBJEXT): {$(VPATH)}internal/value.h parse.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -12128,6 +12162,7 @@ prism/api_node.$(OBJEXT): {$(VPATH)}internal/special_consts.h prism/api_node.$(OBJEXT): {$(VPATH)}internal/static_assert.h prism/api_node.$(OBJEXT): {$(VPATH)}internal/stdalign.h prism/api_node.$(OBJEXT): {$(VPATH)}internal/stdbool.h +prism/api_node.$(OBJEXT): {$(VPATH)}internal/stdckdint.h prism/api_node.$(OBJEXT): {$(VPATH)}internal/symbol.h prism/api_node.$(OBJEXT): {$(VPATH)}internal/value.h prism/api_node.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -12323,6 +12358,7 @@ prism/api_pack.$(OBJEXT): {$(VPATH)}internal/special_consts.h prism/api_pack.$(OBJEXT): {$(VPATH)}internal/static_assert.h prism/api_pack.$(OBJEXT): {$(VPATH)}internal/stdalign.h prism/api_pack.$(OBJEXT): {$(VPATH)}internal/stdbool.h +prism/api_pack.$(OBJEXT): {$(VPATH)}internal/stdckdint.h prism/api_pack.$(OBJEXT): {$(VPATH)}internal/symbol.h prism/api_pack.$(OBJEXT): {$(VPATH)}internal/value.h prism/api_pack.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -12532,6 +12568,7 @@ prism/extension.$(OBJEXT): {$(VPATH)}internal/special_consts.h prism/extension.$(OBJEXT): {$(VPATH)}internal/static_assert.h prism/extension.$(OBJEXT): {$(VPATH)}internal/stdalign.h prism/extension.$(OBJEXT): {$(VPATH)}internal/stdbool.h +prism/extension.$(OBJEXT): {$(VPATH)}internal/stdckdint.h prism/extension.$(OBJEXT): {$(VPATH)}internal/symbol.h prism/extension.$(OBJEXT): {$(VPATH)}internal/value.h prism/extension.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -12554,6 +12591,7 @@ prism/node.$(OBJEXT): $(top_srcdir)/prism/pack.h prism/node.$(OBJEXT): $(top_srcdir)/prism/parser.h prism/node.$(OBJEXT): $(top_srcdir)/prism/prism.h prism/node.$(OBJEXT): $(top_srcdir)/prism/regexp.h +prism/node.$(OBJEXT): $(top_srcdir)/prism/static_literals.h prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h @@ -12579,6 +12617,7 @@ prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/options.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/parser.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h +prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/static_literals.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h @@ -12621,6 +12660,7 @@ prism/regexp.$(OBJEXT): $(top_srcdir)/prism/options.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/parser.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/regexp.c prism/regexp.$(OBJEXT): $(top_srcdir)/prism/regexp.h +prism/regexp.$(OBJEXT): $(top_srcdir)/prism/static_literals.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h @@ -12731,6 +12771,7 @@ prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/options.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/parser.h +prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/static_literals.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h @@ -12923,6 +12964,7 @@ prism_init.$(OBJEXT): {$(VPATH)}internal/special_consts.h prism_init.$(OBJEXT): {$(VPATH)}internal/static_assert.h prism_init.$(OBJEXT): {$(VPATH)}internal/stdalign.h prism_init.$(OBJEXT): {$(VPATH)}internal/stdbool.h +prism_init.$(OBJEXT): {$(VPATH)}internal/stdckdint.h prism_init.$(OBJEXT): {$(VPATH)}internal/symbol.h prism_init.$(OBJEXT): {$(VPATH)}internal/value.h prism_init.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -13146,6 +13188,7 @@ proc.$(OBJEXT): {$(VPATH)}internal/special_consts.h proc.$(OBJEXT): {$(VPATH)}internal/static_assert.h proc.$(OBJEXT): {$(VPATH)}internal/stdalign.h proc.$(OBJEXT): {$(VPATH)}internal/stdbool.h +proc.$(OBJEXT): {$(VPATH)}internal/stdckdint.h proc.$(OBJEXT): {$(VPATH)}internal/symbol.h proc.$(OBJEXT): {$(VPATH)}internal/value.h proc.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -13374,6 +13417,7 @@ process.$(OBJEXT): {$(VPATH)}internal/special_consts.h process.$(OBJEXT): {$(VPATH)}internal/static_assert.h process.$(OBJEXT): {$(VPATH)}internal/stdalign.h process.$(OBJEXT): {$(VPATH)}internal/stdbool.h +process.$(OBJEXT): {$(VPATH)}internal/stdckdint.h process.$(OBJEXT): {$(VPATH)}internal/symbol.h process.$(OBJEXT): {$(VPATH)}internal/value.h process.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -13596,6 +13640,7 @@ ractor.$(OBJEXT): {$(VPATH)}internal/special_consts.h ractor.$(OBJEXT): {$(VPATH)}internal/static_assert.h ractor.$(OBJEXT): {$(VPATH)}internal/stdalign.h ractor.$(OBJEXT): {$(VPATH)}internal/stdbool.h +ractor.$(OBJEXT): {$(VPATH)}internal/stdckdint.h ractor.$(OBJEXT): {$(VPATH)}internal/symbol.h ractor.$(OBJEXT): {$(VPATH)}internal/value.h ractor.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -13809,6 +13854,7 @@ random.$(OBJEXT): {$(VPATH)}internal/special_consts.h random.$(OBJEXT): {$(VPATH)}internal/static_assert.h random.$(OBJEXT): {$(VPATH)}internal/stdalign.h random.$(OBJEXT): {$(VPATH)}internal/stdbool.h +random.$(OBJEXT): {$(VPATH)}internal/stdckdint.h random.$(OBJEXT): {$(VPATH)}internal/symbol.h random.$(OBJEXT): {$(VPATH)}internal/value.h random.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -14015,6 +14061,7 @@ range.$(OBJEXT): {$(VPATH)}internal/special_consts.h range.$(OBJEXT): {$(VPATH)}internal/static_assert.h range.$(OBJEXT): {$(VPATH)}internal/stdalign.h range.$(OBJEXT): {$(VPATH)}internal/stdbool.h +range.$(OBJEXT): {$(VPATH)}internal/stdckdint.h range.$(OBJEXT): {$(VPATH)}internal/symbol.h range.$(OBJEXT): {$(VPATH)}internal/value.h range.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -14213,6 +14260,7 @@ rational.$(OBJEXT): {$(VPATH)}internal/special_consts.h rational.$(OBJEXT): {$(VPATH)}internal/static_assert.h rational.$(OBJEXT): {$(VPATH)}internal/stdalign.h rational.$(OBJEXT): {$(VPATH)}internal/stdbool.h +rational.$(OBJEXT): {$(VPATH)}internal/stdckdint.h rational.$(OBJEXT): {$(VPATH)}internal/symbol.h rational.$(OBJEXT): {$(VPATH)}internal/value.h rational.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -14425,6 +14473,7 @@ re.$(OBJEXT): {$(VPATH)}internal/special_consts.h re.$(OBJEXT): {$(VPATH)}internal/static_assert.h re.$(OBJEXT): {$(VPATH)}internal/stdalign.h re.$(OBJEXT): {$(VPATH)}internal/stdbool.h +re.$(OBJEXT): {$(VPATH)}internal/stdckdint.h re.$(OBJEXT): {$(VPATH)}internal/symbol.h re.$(OBJEXT): {$(VPATH)}internal/value.h re.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -14599,6 +14648,7 @@ regcomp.$(OBJEXT): {$(VPATH)}internal/special_consts.h regcomp.$(OBJEXT): {$(VPATH)}internal/static_assert.h regcomp.$(OBJEXT): {$(VPATH)}internal/stdalign.h regcomp.$(OBJEXT): {$(VPATH)}internal/stdbool.h +regcomp.$(OBJEXT): {$(VPATH)}internal/stdckdint.h regcomp.$(OBJEXT): {$(VPATH)}internal/symbol.h regcomp.$(OBJEXT): {$(VPATH)}internal/value.h regcomp.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -14760,6 +14810,7 @@ regenc.$(OBJEXT): {$(VPATH)}internal/special_consts.h regenc.$(OBJEXT): {$(VPATH)}internal/static_assert.h regenc.$(OBJEXT): {$(VPATH)}internal/stdalign.h regenc.$(OBJEXT): {$(VPATH)}internal/stdbool.h +regenc.$(OBJEXT): {$(VPATH)}internal/stdckdint.h regenc.$(OBJEXT): {$(VPATH)}internal/symbol.h regenc.$(OBJEXT): {$(VPATH)}internal/value.h regenc.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -14920,6 +14971,7 @@ regerror.$(OBJEXT): {$(VPATH)}internal/special_consts.h regerror.$(OBJEXT): {$(VPATH)}internal/static_assert.h regerror.$(OBJEXT): {$(VPATH)}internal/stdalign.h regerror.$(OBJEXT): {$(VPATH)}internal/stdbool.h +regerror.$(OBJEXT): {$(VPATH)}internal/stdckdint.h regerror.$(OBJEXT): {$(VPATH)}internal/symbol.h regerror.$(OBJEXT): {$(VPATH)}internal/value.h regerror.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -15080,6 +15132,7 @@ regexec.$(OBJEXT): {$(VPATH)}internal/special_consts.h regexec.$(OBJEXT): {$(VPATH)}internal/static_assert.h regexec.$(OBJEXT): {$(VPATH)}internal/stdalign.h regexec.$(OBJEXT): {$(VPATH)}internal/stdbool.h +regexec.$(OBJEXT): {$(VPATH)}internal/stdckdint.h regexec.$(OBJEXT): {$(VPATH)}internal/symbol.h regexec.$(OBJEXT): {$(VPATH)}internal/value.h regexec.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -15244,6 +15297,7 @@ regparse.$(OBJEXT): {$(VPATH)}internal/special_consts.h regparse.$(OBJEXT): {$(VPATH)}internal/static_assert.h regparse.$(OBJEXT): {$(VPATH)}internal/stdalign.h regparse.$(OBJEXT): {$(VPATH)}internal/stdbool.h +regparse.$(OBJEXT): {$(VPATH)}internal/stdckdint.h regparse.$(OBJEXT): {$(VPATH)}internal/symbol.h regparse.$(OBJEXT): {$(VPATH)}internal/value.h regparse.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -15405,6 +15459,7 @@ regsyntax.$(OBJEXT): {$(VPATH)}internal/special_consts.h regsyntax.$(OBJEXT): {$(VPATH)}internal/static_assert.h regsyntax.$(OBJEXT): {$(VPATH)}internal/stdalign.h regsyntax.$(OBJEXT): {$(VPATH)}internal/stdbool.h +regsyntax.$(OBJEXT): {$(VPATH)}internal/stdckdint.h regsyntax.$(OBJEXT): {$(VPATH)}internal/symbol.h regsyntax.$(OBJEXT): {$(VPATH)}internal/value.h regsyntax.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -15634,6 +15689,7 @@ rjit.$(OBJEXT): {$(VPATH)}internal/special_consts.h rjit.$(OBJEXT): {$(VPATH)}internal/static_assert.h rjit.$(OBJEXT): {$(VPATH)}internal/stdalign.h rjit.$(OBJEXT): {$(VPATH)}internal/stdbool.h +rjit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h rjit.$(OBJEXT): {$(VPATH)}internal/symbol.h rjit.$(OBJEXT): {$(VPATH)}internal/value.h rjit.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -15886,6 +15942,7 @@ rjit_c.$(OBJEXT): {$(VPATH)}internal/special_consts.h rjit_c.$(OBJEXT): {$(VPATH)}internal/static_assert.h rjit_c.$(OBJEXT): {$(VPATH)}internal/stdalign.h rjit_c.$(OBJEXT): {$(VPATH)}internal/stdbool.h +rjit_c.$(OBJEXT): {$(VPATH)}internal/stdckdint.h rjit_c.$(OBJEXT): {$(VPATH)}internal/symbol.h rjit_c.$(OBJEXT): {$(VPATH)}internal/value.h rjit_c.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -16162,6 +16219,7 @@ ruby.$(OBJEXT): {$(VPATH)}internal/special_consts.h ruby.$(OBJEXT): {$(VPATH)}internal/static_assert.h ruby.$(OBJEXT): {$(VPATH)}internal/stdalign.h ruby.$(OBJEXT): {$(VPATH)}internal/stdbool.h +ruby.$(OBJEXT): {$(VPATH)}internal/stdckdint.h ruby.$(OBJEXT): {$(VPATH)}internal/symbol.h ruby.$(OBJEXT): {$(VPATH)}internal/value.h ruby.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -16204,6 +16262,7 @@ ruby_parser.$(OBJEXT): $(top_srcdir)/internal/error.h ruby_parser.$(OBJEXT): $(top_srcdir)/internal/fixnum.h ruby_parser.$(OBJEXT): $(top_srcdir)/internal/imemo.h ruby_parser.$(OBJEXT): $(top_srcdir)/internal/numeric.h +ruby_parser.$(OBJEXT): $(top_srcdir)/internal/parse.h ruby_parser.$(OBJEXT): $(top_srcdir)/internal/rational.h ruby_parser.$(OBJEXT): $(top_srcdir)/internal/re.h ruby_parser.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h @@ -16368,6 +16427,7 @@ ruby_parser.$(OBJEXT): {$(VPATH)}internal/special_consts.h ruby_parser.$(OBJEXT): {$(VPATH)}internal/static_assert.h ruby_parser.$(OBJEXT): {$(VPATH)}internal/stdalign.h ruby_parser.$(OBJEXT): {$(VPATH)}internal/stdbool.h +ruby_parser.$(OBJEXT): {$(VPATH)}internal/stdckdint.h ruby_parser.$(OBJEXT): {$(VPATH)}internal/symbol.h ruby_parser.$(OBJEXT): {$(VPATH)}internal/value.h ruby_parser.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -16562,6 +16622,7 @@ scheduler.$(OBJEXT): {$(VPATH)}internal/special_consts.h scheduler.$(OBJEXT): {$(VPATH)}internal/static_assert.h scheduler.$(OBJEXT): {$(VPATH)}internal/stdalign.h scheduler.$(OBJEXT): {$(VPATH)}internal/stdbool.h +scheduler.$(OBJEXT): {$(VPATH)}internal/stdckdint.h scheduler.$(OBJEXT): {$(VPATH)}internal/symbol.h scheduler.$(OBJEXT): {$(VPATH)}internal/value.h scheduler.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -16733,6 +16794,7 @@ setproctitle.$(OBJEXT): {$(VPATH)}internal/special_consts.h setproctitle.$(OBJEXT): {$(VPATH)}internal/static_assert.h setproctitle.$(OBJEXT): {$(VPATH)}internal/stdalign.h setproctitle.$(OBJEXT): {$(VPATH)}internal/stdbool.h +setproctitle.$(OBJEXT): {$(VPATH)}internal/stdckdint.h setproctitle.$(OBJEXT): {$(VPATH)}internal/symbol.h setproctitle.$(OBJEXT): {$(VPATH)}internal/value.h setproctitle.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -16928,6 +16990,7 @@ shape.$(OBJEXT): {$(VPATH)}internal/special_consts.h shape.$(OBJEXT): {$(VPATH)}internal/static_assert.h shape.$(OBJEXT): {$(VPATH)}internal/stdalign.h shape.$(OBJEXT): {$(VPATH)}internal/stdbool.h +shape.$(OBJEXT): {$(VPATH)}internal/stdckdint.h shape.$(OBJEXT): {$(VPATH)}internal/symbol.h shape.$(OBJEXT): {$(VPATH)}internal/value.h shape.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -17139,6 +17202,7 @@ signal.$(OBJEXT): {$(VPATH)}internal/special_consts.h signal.$(OBJEXT): {$(VPATH)}internal/static_assert.h signal.$(OBJEXT): {$(VPATH)}internal/stdalign.h signal.$(OBJEXT): {$(VPATH)}internal/stdbool.h +signal.$(OBJEXT): {$(VPATH)}internal/stdckdint.h signal.$(OBJEXT): {$(VPATH)}internal/symbol.h signal.$(OBJEXT): {$(VPATH)}internal/value.h signal.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -17345,6 +17409,7 @@ sprintf.$(OBJEXT): {$(VPATH)}internal/special_consts.h sprintf.$(OBJEXT): {$(VPATH)}internal/static_assert.h sprintf.$(OBJEXT): {$(VPATH)}internal/stdalign.h sprintf.$(OBJEXT): {$(VPATH)}internal/stdbool.h +sprintf.$(OBJEXT): {$(VPATH)}internal/stdckdint.h sprintf.$(OBJEXT): {$(VPATH)}internal/symbol.h sprintf.$(OBJEXT): {$(VPATH)}internal/value.h sprintf.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -17518,6 +17583,7 @@ st.$(OBJEXT): {$(VPATH)}internal/st.h st.$(OBJEXT): {$(VPATH)}internal/static_assert.h st.$(OBJEXT): {$(VPATH)}internal/stdalign.h st.$(OBJEXT): {$(VPATH)}internal/stdbool.h +st.$(OBJEXT): {$(VPATH)}internal/stdckdint.h st.$(OBJEXT): {$(VPATH)}internal/symbol.h st.$(OBJEXT): {$(VPATH)}internal/value.h st.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -17692,6 +17758,7 @@ strftime.$(OBJEXT): {$(VPATH)}internal/special_consts.h strftime.$(OBJEXT): {$(VPATH)}internal/static_assert.h strftime.$(OBJEXT): {$(VPATH)}internal/stdalign.h strftime.$(OBJEXT): {$(VPATH)}internal/stdbool.h +strftime.$(OBJEXT): {$(VPATH)}internal/stdckdint.h strftime.$(OBJEXT): {$(VPATH)}internal/symbol.h strftime.$(OBJEXT): {$(VPATH)}internal/value.h strftime.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -17900,6 +17967,7 @@ string.$(OBJEXT): {$(VPATH)}internal/special_consts.h string.$(OBJEXT): {$(VPATH)}internal/static_assert.h string.$(OBJEXT): {$(VPATH)}internal/stdalign.h string.$(OBJEXT): {$(VPATH)}internal/stdbool.h +string.$(OBJEXT): {$(VPATH)}internal/stdckdint.h string.$(OBJEXT): {$(VPATH)}internal/symbol.h string.$(OBJEXT): {$(VPATH)}internal/value.h string.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -18147,6 +18215,7 @@ struct.$(OBJEXT): {$(VPATH)}internal/special_consts.h struct.$(OBJEXT): {$(VPATH)}internal/static_assert.h struct.$(OBJEXT): {$(VPATH)}internal/stdalign.h struct.$(OBJEXT): {$(VPATH)}internal/stdbool.h +struct.$(OBJEXT): {$(VPATH)}internal/stdckdint.h struct.$(OBJEXT): {$(VPATH)}internal/symbol.h struct.$(OBJEXT): {$(VPATH)}internal/value.h struct.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -18359,6 +18428,7 @@ symbol.$(OBJEXT): {$(VPATH)}internal/special_consts.h symbol.$(OBJEXT): {$(VPATH)}internal/static_assert.h symbol.$(OBJEXT): {$(VPATH)}internal/stdalign.h symbol.$(OBJEXT): {$(VPATH)}internal/stdbool.h +symbol.$(OBJEXT): {$(VPATH)}internal/stdckdint.h symbol.$(OBJEXT): {$(VPATH)}internal/symbol.h symbol.$(OBJEXT): {$(VPATH)}internal/value.h symbol.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -18607,6 +18677,7 @@ thread.$(OBJEXT): {$(VPATH)}internal/special_consts.h thread.$(OBJEXT): {$(VPATH)}internal/static_assert.h thread.$(OBJEXT): {$(VPATH)}internal/stdalign.h thread.$(OBJEXT): {$(VPATH)}internal/stdbool.h +thread.$(OBJEXT): {$(VPATH)}internal/stdckdint.h thread.$(OBJEXT): {$(VPATH)}internal/symbol.h thread.$(OBJEXT): {$(VPATH)}internal/value.h thread.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -18833,6 +18904,7 @@ time.$(OBJEXT): {$(VPATH)}internal/special_consts.h time.$(OBJEXT): {$(VPATH)}internal/static_assert.h time.$(OBJEXT): {$(VPATH)}internal/stdalign.h time.$(OBJEXT): {$(VPATH)}internal/stdbool.h +time.$(OBJEXT): {$(VPATH)}internal/stdckdint.h time.$(OBJEXT): {$(VPATH)}internal/symbol.h time.$(OBJEXT): {$(VPATH)}internal/value.h time.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -19030,6 +19102,7 @@ transcode.$(OBJEXT): {$(VPATH)}internal/special_consts.h transcode.$(OBJEXT): {$(VPATH)}internal/static_assert.h transcode.$(OBJEXT): {$(VPATH)}internal/stdalign.h transcode.$(OBJEXT): {$(VPATH)}internal/stdbool.h +transcode.$(OBJEXT): {$(VPATH)}internal/stdckdint.h transcode.$(OBJEXT): {$(VPATH)}internal/symbol.h transcode.$(OBJEXT): {$(VPATH)}internal/value.h transcode.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -19198,6 +19271,7 @@ util.$(OBJEXT): {$(VPATH)}internal/special_consts.h util.$(OBJEXT): {$(VPATH)}internal/static_assert.h util.$(OBJEXT): {$(VPATH)}internal/stdalign.h util.$(OBJEXT): {$(VPATH)}internal/stdbool.h +util.$(OBJEXT): {$(VPATH)}internal/stdckdint.h util.$(OBJEXT): {$(VPATH)}internal/symbol.h util.$(OBJEXT): {$(VPATH)}internal/value.h util.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -19398,6 +19472,7 @@ variable.$(OBJEXT): {$(VPATH)}internal/special_consts.h variable.$(OBJEXT): {$(VPATH)}internal/static_assert.h variable.$(OBJEXT): {$(VPATH)}internal/stdalign.h variable.$(OBJEXT): {$(VPATH)}internal/stdbool.h +variable.$(OBJEXT): {$(VPATH)}internal/stdckdint.h variable.$(OBJEXT): {$(VPATH)}internal/symbol.h variable.$(OBJEXT): {$(VPATH)}internal/value.h variable.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -19609,6 +19684,7 @@ version.$(OBJEXT): {$(VPATH)}internal/special_consts.h version.$(OBJEXT): {$(VPATH)}internal/static_assert.h version.$(OBJEXT): {$(VPATH)}internal/stdalign.h version.$(OBJEXT): {$(VPATH)}internal/stdbool.h +version.$(OBJEXT): {$(VPATH)}internal/stdckdint.h version.$(OBJEXT): {$(VPATH)}internal/symbol.h version.$(OBJEXT): {$(VPATH)}internal/value.h version.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -19868,6 +19944,7 @@ vm.$(OBJEXT): {$(VPATH)}internal/special_consts.h vm.$(OBJEXT): {$(VPATH)}internal/static_assert.h vm.$(OBJEXT): {$(VPATH)}internal/stdalign.h vm.$(OBJEXT): {$(VPATH)}internal/stdbool.h +vm.$(OBJEXT): {$(VPATH)}internal/stdckdint.h vm.$(OBJEXT): {$(VPATH)}internal/symbol.h vm.$(OBJEXT): {$(VPATH)}internal/value.h vm.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -20122,6 +20199,7 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}internal/special_consts.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal/static_assert.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal/stdalign.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal/stdbool.h +vm_backtrace.$(OBJEXT): {$(VPATH)}internal/stdckdint.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal/symbol.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal/value.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -20350,6 +20428,7 @@ vm_dump.$(OBJEXT): {$(VPATH)}internal/special_consts.h vm_dump.$(OBJEXT): {$(VPATH)}internal/static_assert.h vm_dump.$(OBJEXT): {$(VPATH)}internal/stdalign.h vm_dump.$(OBJEXT): {$(VPATH)}internal/stdbool.h +vm_dump.$(OBJEXT): {$(VPATH)}internal/stdckdint.h vm_dump.$(OBJEXT): {$(VPATH)}internal/symbol.h vm_dump.$(OBJEXT): {$(VPATH)}internal/value.h vm_dump.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -20560,6 +20639,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}internal/special_consts.h vm_sync.$(OBJEXT): {$(VPATH)}internal/static_assert.h vm_sync.$(OBJEXT): {$(VPATH)}internal/stdalign.h vm_sync.$(OBJEXT): {$(VPATH)}internal/stdbool.h +vm_sync.$(OBJEXT): {$(VPATH)}internal/stdckdint.h vm_sync.$(OBJEXT): {$(VPATH)}internal/symbol.h vm_sync.$(OBJEXT): {$(VPATH)}internal/value.h vm_sync.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -20794,6 +20874,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}internal/special_consts.h vm_trace.$(OBJEXT): {$(VPATH)}internal/static_assert.h vm_trace.$(OBJEXT): {$(VPATH)}internal/stdalign.h vm_trace.$(OBJEXT): {$(VPATH)}internal/stdbool.h +vm_trace.$(OBJEXT): {$(VPATH)}internal/stdckdint.h vm_trace.$(OBJEXT): {$(VPATH)}internal/symbol.h vm_trace.$(OBJEXT): {$(VPATH)}internal/value.h vm_trace.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -21003,6 +21084,7 @@ weakmap.$(OBJEXT): {$(VPATH)}internal/special_consts.h weakmap.$(OBJEXT): {$(VPATH)}internal/static_assert.h weakmap.$(OBJEXT): {$(VPATH)}internal/stdalign.h weakmap.$(OBJEXT): {$(VPATH)}internal/stdbool.h +weakmap.$(OBJEXT): {$(VPATH)}internal/stdckdint.h weakmap.$(OBJEXT): {$(VPATH)}internal/symbol.h weakmap.$(OBJEXT): {$(VPATH)}internal/value.h weakmap.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -21238,6 +21320,7 @@ yjit.$(OBJEXT): {$(VPATH)}internal/special_consts.h yjit.$(OBJEXT): {$(VPATH)}internal/static_assert.h yjit.$(OBJEXT): {$(VPATH)}internal/stdalign.h yjit.$(OBJEXT): {$(VPATH)}internal/stdbool.h +yjit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h yjit.$(OBJEXT): {$(VPATH)}internal/symbol.h yjit.$(OBJEXT): {$(VPATH)}internal/value.h yjit.$(OBJEXT): {$(VPATH)}internal/value_type.h @@ -612,11 +612,13 @@ branch_coverage_valid_p(rb_iseq_t *iseq, int first_line) return 1; } +#define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) + static VALUE -decl_branch_base(rb_iseq_t *iseq, const NODE *node, const char *type) +decl_branch_base(rb_iseq_t *iseq, VALUE key, const rb_code_location_t *loc, const char *type) { - const int first_lineno = nd_first_lineno(node), first_column = nd_first_column(node); - const int last_lineno = nd_last_lineno(node), last_column = nd_last_column(node); + const int first_lineno = loc->beg_pos.lineno, first_column = loc->beg_pos.column; + const int last_lineno = loc->end_pos.lineno, last_column = loc->end_pos.column; if (!branch_coverage_valid_p(iseq, first_lineno)) return Qundef; @@ -629,7 +631,6 @@ decl_branch_base(rb_iseq_t *iseq, const NODE *node, const char *type) */ VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0); - VALUE key = (VALUE)node | 1; // FIXNUM for hash key VALUE branch_base = rb_hash_aref(structure, key); VALUE branches; @@ -662,10 +663,10 @@ generate_dummy_line_node(int lineno, int node_id) } static void -add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *node, int branch_id, const char *type, VALUE branches) +add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const rb_code_location_t *loc, int node_id, int branch_id, const char *type, VALUE branches) { - const int first_lineno = nd_first_lineno(node), first_column = nd_first_column(node); - const int last_lineno = nd_last_lineno(node), last_column = nd_last_column(node); + const int first_lineno = loc->beg_pos.lineno, first_column = loc->beg_pos.column; + const int last_lineno = loc->end_pos.lineno, last_column = loc->end_pos.column; if (!branch_coverage_valid_p(iseq, first_lineno)) return; @@ -699,7 +700,7 @@ add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *n } ADD_TRACE_WITH_DATA(seq, RUBY_EVENT_COVERAGE_BRANCH, counter_idx); - ADD_SYNTHETIC_INSN(seq, last_lineno, nd_node_id(node), nop); + ADD_SYNTHETIC_INSN(seq, last_lineno, node_id, nop); } #define ISEQ_LAST_LINE(iseq) (ISEQ_COMPILE_DATA(iseq)->last_line) @@ -1478,16 +1479,11 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node, VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no) { rb_iseq_t *ret_iseq; - rb_ast_body_t ast; - - ast.root = node; - ast.frozen_string_literal = -1; - ast.coverage_enabled = -1; - ast.script_lines = NULL; + VALUE ast_value = rb_ruby_ast_new(node); debugs("[new_child_iseq]> ---------------------------------------\n"); int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth; - ret_iseq = rb_iseq_new_with_opt(&ast, name, + ret_iseq = rb_iseq_new_with_opt(ast_value, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), line_no, parent, isolated_depth ? isolated_depth + 1 : 0, @@ -2006,8 +2002,11 @@ iseq_set_use_block(rb_iseq_t *iseq) body->param.flags.use_block = 1; rb_vm_t *vm = GET_VM(); - st_data_t key = (st_data_t)rb_intern_str(body->location.label); // String -> ID - st_insert(vm->unused_block_warning_table, key, 1); + + if (!vm->unused_block_warning_strict) { + st_data_t key = (st_data_t)rb_intern_str(body->location.label); // String -> ID + st_insert(vm->unused_block_warning_table, key, 1); + } } } @@ -5381,12 +5380,17 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node)); + bool safenav_call = false; LINK_ELEMENT *insn_element = LAST_ELEMENT(pre); iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */ ASSUME(iobj); - ELEM_REMOVE(LAST_ELEMENT(pre)); - ELEM_REMOVE((LINK_ELEMENT *)iobj); - pre->last = iobj->link.prev; + ELEM_REMOVE(insn_element); + if (!IS_INSN_ID(iobj, send)) { + safenav_call = true; + iobj = (INSN *)get_prev_insn(iobj); + ELEM_INSERT_NEXT(&iobj->link, insn_element); + } + (pre->last = iobj->link.prev)->next = 0; const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0); int argc = vm_ci_argc(ci) + 1; @@ -5405,7 +5409,9 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const return COMPILE_NG; } - ADD_ELEM(lhs, (LINK_ELEMENT *)iobj); + iobj->link.prev = lhs->last; + lhs->last->next = &iobj->link; + for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next); if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) { int argc = vm_ci_argc(ci); bool dupsplat = false; @@ -5438,9 +5444,11 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const } INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1)); } - ADD_INSN(lhs, line_node, pop); - if (argc != 1) { + if (!safenav_call) { ADD_INSN(lhs, line_node, pop); + if (argc != 1) { + ADD_INSN(lhs, line_node, pop); + } } for (int i=0; i < argc; i++) { ADD_INSN(post, line_node, pop); @@ -6443,7 +6451,7 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int ADD_SEQ(ret, cond_seq); if (then_label->refcnt && else_label->refcnt) { - branches = decl_branch_base(iseq, node, type == NODE_IF ? "if" : "unless"); + branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless"); } if (then_label->refcnt) { @@ -6454,10 +6462,12 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int CHECK(COMPILE_(then_seq, "then", node_body, popped)); if (else_label->refcnt) { + const NODE *const coverage_node = node_body ? node_body : node; add_trace_branch_coverage( iseq, ret, - node_body ? node_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), 0, type == NODE_IF ? "then" : "else", branches); @@ -6478,10 +6488,12 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int CHECK(COMPILE_(else_seq, "else", node_else, popped)); if (then_label->refcnt) { + const NODE *const coverage_node = node_else ? node_else : node; add_trace_branch_coverage( iseq, ret, - node_else ? node_else : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), 1, type == NODE_IF ? "else" : "then", branches); @@ -6521,7 +6533,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head)); - branches = decl_branch_base(iseq, node, "case"); + branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case"); node = RNODE_CASE(node)->nd_body; EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG); @@ -6540,13 +6552,17 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod l1 = NEW_LABEL(line); ADD_LABEL(body_seq, l1); ADD_INSN(body_seq, line_node, pop); + + const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node; add_trace_branch_coverage( iseq, body_seq, - RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), branch_id++, "when", branches); + CHECK(COMPILE_(body_seq, "when body", RNODE_WHEN(node)->nd_body, popped)); ADD_INSNL(body_seq, line_node, jump, endlabel); @@ -6583,7 +6599,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod if (node) { ADD_LABEL(cond_seq, elselabel); ADD_INSN(cond_seq, line_node, pop); - add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches); + add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches); CHECK(COMPILE_(cond_seq, "else", node, popped)); ADD_INSNL(cond_seq, line_node, jump, endlabel); } @@ -6591,7 +6607,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod debugs("== else (implicit)\n"); ADD_LABEL(cond_seq, elselabel); ADD_INSN(cond_seq, orig_node, pop); - add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches); + add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches); if (!popped) { ADD_INSN(cond_seq, orig_node, putnil); } @@ -6622,7 +6638,7 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no VALUE branches = Qfalse; int branch_id = 0; - branches = decl_branch_base(iseq, orig_node, "case"); + branches = decl_branch_base(iseq, PTR2NUM(orig_node), nd_code_loc(orig_node), "case"); INIT_ANCHOR(body_seq); endlabel = NEW_LABEL(nd_line(node)); @@ -6631,13 +6647,17 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no const int line = nd_line(node); LABEL *l1 = NEW_LABEL(line); ADD_LABEL(body_seq, l1); + + const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node; add_trace_branch_coverage( iseq, body_seq, - RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), branch_id++, "when", branches); + CHECK(COMPILE_(body_seq, "when", RNODE_WHEN(node)->nd_body, popped)); ADD_INSNL(body_seq, node, jump, endlabel); @@ -6671,10 +6691,12 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no node = RNODE_WHEN(node)->nd_next; } /* else */ + const NODE *const coverage_node = node ? node : orig_node; add_trace_branch_coverage( iseq, ret, - node ? node : orig_node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), branch_id, "else", branches); @@ -7613,7 +7635,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no INIT_ANCHOR(body_seq); INIT_ANCHOR(cond_seq); - branches = decl_branch_base(iseq, node, "case"); + branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case"); node = RNODE_CASE3(node)->nd_body; EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG); @@ -7647,13 +7669,17 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no l1 = NEW_LABEL(line); ADD_LABEL(body_seq, l1); ADD_INSN1(body_seq, line_node, adjuststack, INT2FIX(single_pattern ? 6 : 2)); + + const NODE *const coverage_node = RNODE_IN(node)->nd_body ? RNODE_IN(node)->nd_body : node; add_trace_branch_coverage( iseq, body_seq, - RNODE_IN(node)->nd_body ? RNODE_IN(node)->nd_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), branch_id++, "in", branches); + CHECK(COMPILE_(body_seq, "in body", RNODE_IN(node)->nd_body, popped)); ADD_INSNL(body_seq, line_node, jump, endlabel); @@ -7685,7 +7711,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no ADD_LABEL(cond_seq, elselabel); ADD_INSN(cond_seq, line_node, pop); ADD_INSN(cond_seq, line_node, pop); /* discard cached #deconstruct value */ - add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches); + add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches); CHECK(COMPILE_(cond_seq, "else", node, popped)); ADD_INSNL(cond_seq, line_node, jump, endlabel); ADD_INSN(cond_seq, line_node, putnil); @@ -7696,7 +7722,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no else { debugs("== else (implicit)\n"); ADD_LABEL(cond_seq, elselabel); - add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches); + add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches); ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); if (single_pattern) { @@ -7813,14 +7839,18 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in if (tmp_label) ADD_LABEL(ret, tmp_label); ADD_LABEL(ret, redo_label); - branches = decl_branch_base(iseq, node, type == NODE_WHILE ? "while" : "until"); + branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until"); + + const NODE *const coverage_node = RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node; add_trace_branch_coverage( iseq, ret, - RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), 0, "body", branches); + CHECK(COMPILE_POPPED(ret, "while body", RNODE_WHILE(node)->nd_body)); ADD_LABEL(ret, next_label); /* next */ @@ -8428,11 +8458,11 @@ qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, co LABEL *else_label = NEW_LABEL(nd_line(line_node)); VALUE br = 0; - br = decl_branch_base(iseq, node, "&."); + br = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "&."); *branches = br; ADD_INSN(recv, line_node, dup); ADD_INSNL(recv, line_node, branchnil, else_label); - add_trace_branch_coverage(iseq, recv, node, 0, "then", br); + add_trace_branch_coverage(iseq, recv, nd_code_loc(node), nd_node_id(node), 0, "then", br); return else_label; } @@ -8444,7 +8474,7 @@ qcall_branch_end(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *else_label, VAL end_label = NEW_LABEL(nd_line(line_node)); ADD_INSNL(ret, line_node, jump, end_label); ADD_LABEL(ret, else_label); - add_trace_branch_coverage(iseq, ret, node, 1, "else", branches); + add_trace_branch_coverage(iseq, ret, nd_code_loc(node), nd_node_id(node), 1, "else", branches); ADD_LABEL(ret, end_label); } @@ -8750,15 +8780,10 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N scope_node.nd_body = mandatory_node(iseq, node); scope_node.nd_args = &args_node; - rb_ast_body_t ast = { - .root = RNODE(&scope_node), - .frozen_string_literal = -1, - .coverage_enabled = -1, - .script_lines = NULL - }; + VALUE ast_value = rb_ruby_ast_new(RNODE(&scope_node)); ISEQ_BODY(iseq)->mandatory_only_iseq = - rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq), + rb_iseq_new_with_opt(ast_value, rb_iseq_base_label(iseq), rb_iseq_path(iseq), rb_iseq_realpath(iseq), nd_line(line_node), NULL, 0, ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option, @@ -14211,6 +14236,8 @@ ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, static void ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str) { + StringValue(str); + if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) { rb_raise(rb_eRuntimeError, "broken binary format"); } diff --git a/configure.ac b/configure.ac index 9ea0013850..e9a452ebee 100644 --- a/configure.ac +++ b/configure.ac @@ -427,15 +427,22 @@ AS_CASE(["$build_os"], # default spec. # Xcode linker warns for deprecated architecture and wrongly # installed TBD files. - CC_WRAPPER="" CC_NO_WRAPPER="$CC" + AC_MSG_CHECKING(for $CC linker warning) + suppress_ld_waring=no echo 'int main(void) {return 0;}' > conftest.c - AS_IF([$CC -framework Foundation -o conftest conftest.c 2>&1 | - grep -e '^ld: warning: ignoring duplicate libraries:' \ - -e '^ld: warning: text-based stub file' >/dev/null], [ - CC_WRAPPER=`cd -P "${tooldir}" && pwd`/darwin-cc - CC="$CC_WRAPPER $CC" + AS_IF([$CC -framework Foundation -o conftest -ggdb3 conftest.c 2>&1 | + grep \ + -e '^ld: warning: ignoring duplicate libraries:' \ + -e '^ld: warning: text-based stub file' \ + -e '^ld: warning: -multiply_defined is obsolete' \ + -e "^warning: '\.debug_macinfo'" \ + -e '^note: while processing' \ + >/dev/null], [ + suppress_ld_waring=yes ]) rm -fr conftest* + test $suppress_ld_waring = yes && warnflags="${warnflags:+${warnflags} }-Wl,-w" + AC_MSG_RESULT($suppress_ld_waring) ]) AS_CASE(["$target_os"], [wasi*], [ @@ -1360,6 +1367,7 @@ AC_CHECK_HEADERS(ucontext.h) AC_CHECK_HEADERS(utime.h) AC_CHECK_HEADERS(sys/epoll.h) AC_CHECK_HEADERS(sys/event.h) +AC_CHECK_HEADERS(stdckdint.h) AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [ AC_CHECK_HEADERS(x86intrin.h) @@ -1768,7 +1776,7 @@ AC_CACHE_CHECK(for function name string predefined identifier, [AS_CASE(["$target_os"],[openbsd*],[ rb_cv_function_name_string=__func__ ],[ - rb_cv_function_name_string=no + rb_cv_function_name_string=no RUBY_WERROR_FLAG([ for func in __func__ __FUNCTION__; do AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <stdio.h>]], @@ -1776,7 +1784,8 @@ AC_CACHE_CHECK(for function name string predefined identifier, [rb_cv_function_name_string=$func break]) done - ])])] + ]) + ])] ) AS_IF([test "$rb_cv_function_name_string" != no], [ AC_DEFINE_UNQUOTED(RUBY_FUNCTION_NAME_STRING, [$rb_cv_function_name_string]) @@ -2569,11 +2569,10 @@ rb_fiber_start(rb_fiber_t *fiber) void rb_threadptr_root_fiber_setup(rb_thread_t *th) { - rb_fiber_t *fiber = ruby_mimmalloc(sizeof(rb_fiber_t)); + rb_fiber_t *fiber = ruby_mimcalloc(1, sizeof(rb_fiber_t)); if (!fiber) { rb_bug("%s", strerror(errno)); /* ... is it possible to call rb_bug here? */ } - MEMZERO(fiber, rb_fiber_t, 1); fiber->cont.type = FIBER_CONTEXT; fiber->cont.saved_ec.fiber_ptr = fiber; fiber->cont.saved_ec.thread_ptr = th; diff --git a/defs/gmake.mk b/defs/gmake.mk index e502b9f83e..c914b39690 100644 --- a/defs/gmake.mk +++ b/defs/gmake.mk @@ -513,10 +513,12 @@ ifneq ($(POSTLINK),) endif $(Q) $(RMALL) $@.* -rubyspec-capiext: $(patsubst %.c,$(RUBYSPEC_CAPIEXT)/%.$(DLEXT),$(notdir $(wildcard $(srcdir)/$(RUBYSPEC_CAPIEXT)/*.c))) +RUBYSPEC_CAPIEXT_SO := $(patsubst %.c,$(RUBYSPEC_CAPIEXT)/%.$(DLEXT),$(notdir $(wildcard $(srcdir)/$(RUBYSPEC_CAPIEXT)/*.c))) +rubyspec-capiext: $(RUBYSPEC_CAPIEXT_SO) @ $(NULLCMD) ifeq ($(ENABLE_SHARED),yes) +ruby: $(if $(LIBRUBY_SO_UPDATE),$(RUBYSPEC_CAPIEXT_SO)) exts: rubyspec-capiext endif @@ -76,6 +76,12 @@ void *xrealloc(); # include <unistd.h> #endif +bool +dln_supported_p(void) +{ + return true; +} + #ifndef dln_loaderror static void dln_loaderror(const char *format, ...) @@ -194,7 +200,6 @@ dln_strerror(char *message, size_t size) } return message; } -#define dln_strerror() dln_strerror(message, sizeof message) #elif defined USE_DLN_DLOPEN static const char * dln_strerror(void) @@ -340,15 +345,12 @@ dln_disable_dlclose(void) #if defined(_WIN32) || defined(USE_DLN_DLOPEN) void * -dln_open(const char *file) +dln_open(const char *file, char *error, size_t size) { static const char incompatible[] = "incompatible library version"; - const char *error = NULL; void *handle; #if defined(_WIN32) - char message[1024]; - /* Convert the file path to wide char */ WCHAR *winfile = rb_w32_mbstr_to_wstr(CP_UTF8, file, -1, NULL); if (!winfile) { @@ -360,15 +362,15 @@ dln_open(const char *file) free(winfile); if (!handle) { - error = dln_strerror(); - goto failed; + strlcpy(error, dln_strerror(error, size), size); + return NULL; } # if defined(RUBY_EXPORT) if (!rb_w32_check_imported(handle, rb_libruby_handle())) { FreeLibrary(handle); - error = incompatible; - goto failed; + strlcpy(error, incompatible, size); + return NULL; } # endif @@ -387,8 +389,8 @@ dln_open(const char *file) /* Load file */ handle = dlopen(file, RTLD_LAZY|RTLD_GLOBAL); if (handle == NULL) { - error = dln_strerror(); - goto failed; + strlcpy(error, dln_strerror(), size); + return NULL; } # if defined(RUBY_EXPORT) @@ -410,11 +412,15 @@ dln_open(const char *file) libruby_name = tmp; } dlclose(handle); + if (libruby_name) { - dln_loaderror("linked to incompatible %s - %s", libruby_name, file); + snprintf(error, size, "linked to incompatible %s - %s", libruby_name, file); + } + else { + strlcpy(error, incompatible, size); } - error = incompatible; - goto failed; + + return NULL; } } } @@ -422,9 +428,6 @@ dln_open(const char *file) #endif return handle; - - failed: - dln_loaderror("%s - %s", error, file); } void * @@ -446,7 +449,7 @@ dln_sym_func(void *handle, const char *symbol) const char *error; #if defined(_WIN32) char message[1024]; - error = dln_strerror(); + error = dln_strerror(message, sizeof(message)); #elif defined(USE_DLN_DLOPEN) const size_t errlen = strlen(error = dln_strerror()) + 1; error = memcpy(ALLOCA_N(char, errlen), error, errlen); @@ -501,7 +504,12 @@ void * dln_load(const char *file) { #if defined(_WIN32) || defined(USE_DLN_DLOPEN) - void *handle = dln_open(file); + char error[1024]; + void *handle = dln_open(file, error, sizeof(error)); + + if (handle == NULL) { + dln_loaderror("%s - %s", error, file); + } #ifdef RUBY_DLN_CHECK_ABI typedef unsigned long long abi_version_number; @@ -22,10 +22,11 @@ RUBY_SYMBOL_EXPORT_BEGIN #define DLN_FIND_EXTRA_ARG_DECL #endif +bool dln_supported_p(void); char *dln_find_exe_r(const char*,const char*,char*,size_t DLN_FIND_EXTRA_ARG_DECL); char *dln_find_file_r(const char*,const char*,char*,size_t DLN_FIND_EXTRA_ARG_DECL); void *dln_load(const char*); -void *dln_open(const char *file); +void *dln_open(const char *file, char *error, size_t size); void *dln_symbol(void*,const char*); RUBY_SYMBOL_EXPORT_END @@ -3,6 +3,12 @@ #include "ruby/ruby.h" +bool +dln_supported_p(void) +{ + return false; +} + NORETURN(void *dln_load(const char *)); void* dln_load(const char *file) @@ -21,12 +27,11 @@ dln_symbol(void *handle, const char *symbol) UNREACHABLE_RETURN(NULL); } -NORETURN(void *dln_open(const char*)); void* -dln_open(const char *library) +dln_open(const char *library, char *error, size_t size) { - rb_loaderror("this executable file can't load extension libraries"); - - UNREACHABLE_RETURN(NULL); + static const char *error_str = "this executable file can't load extension libraries"; + strlcpy(error, error_str, size); + return NULL; } diff --git a/doc/contributing/building_ruby.md b/doc/contributing/building_ruby.md index 4dabaca840..38c78e3cca 100644 --- a/doc/contributing/building_ruby.md +++ b/doc/contributing/building_ruby.md @@ -89,8 +89,6 @@ ../configure --prefix="${HOME}/.rubies/ruby-master" ``` - - If you are frequently building Ruby, add the `--disable-install-doc` flag to not build documentation which will speed up the build process. - - Also `-C` (or `--config-cache`) would reduce time to configure from the next time. 5. Build Ruby: @@ -187,7 +185,7 @@ Using the address sanitizer (ASAN) is a great way to detect memory issues. It ca ``` shell ./autogen.sh mkdir build && cd build -../configure CC=clang cflags="-fsanitize=address -fno-omit-frame-pointer -DUSE_MN_THREADS=0" # and any other options you might like +../configure CC=clang-18 cflags="-fsanitize=address -fno-omit-frame-pointer -DUSE_MN_THREADS=0" # and any other options you might like make ``` The compiled Ruby will now automatically crash with a report and a backtrace if ASAN detects a memory safety issue. To run Ruby's test suite under ASAN, issue the following command. Note that this will take quite a long time (over two hours on my laptop); the `RUBY_TEST_TIMEOUT_SCALE` and `SYNTAX_SUGEST_TIMEOUT` variables are required to make sure tests don't spuriously fail with timeouts when in fact they're just slow. @@ -200,7 +198,7 @@ Please note, however, the following caveats! * ASAN will not work properly on any currently released version of Ruby; the necessary support is currently only present on Ruby's master branch (and the whole test suite passes only as of commit [9d0a5148ae062a0481a4a18fbeb9cfd01dc10428](https://bugs.ruby-lang.org/projects/ruby-master/repository/git/revisions/9d0a5148ae062a0481a4a18fbeb9cfd01dc10428)) * Due to [this bug](https://bugs.ruby-lang.org/issues/20243), Clang generates code for threadlocal variables which doesn't work with M:N threading. Thus, it's necessary to disable M:N threading support at build time for now (with the `-DUSE_MN_THREADS=0` configure argument). -* Currently, ASAN will only work correctly when using a recent head build of LLVM/Clang - it requires [this bugfix](https://github.com/llvm/llvm-project/pull/75290) related to multithreaded `fork`, which is not yet in any released version. See [here](https://llvm.org/docs/CMake.html) for instructions on how to build LLVM/Clang from source (note you will need at least the `clang` and `compiler-rt` projects enabled). Then, you will need to replace `CC=clang` in the instructions with an explicit path to your built Clang binary. +* ASAN will only work when using Clang version 18 or later - it requires [this bugfix](https://github.com/llvm/llvm-project/pull/75290) related to multithreaded `fork`. * ASAN has only been tested so far with Clang on Linux. It may or may not work with other compilers or on other platforms - please file an issue on [https://bugs.ruby-lang.org](https://bugs.ruby-lang.org) if you run into problems with such configurations (or, to report that they actually work properly!) * In particular, although I have not yet tried it, I have reason to believe ASAN will _not_ work properly on macOS yet - the fix for the multithreaded fork issue was actually reverted for macOS (see [here](https://github.com/llvm/llvm-project/commit/2a03854e4ce9bb1bcd79a211063bc63c4657f92c)). Please open an issue on [https://bugs.ruby-lang.org](https://bugs.ruby-lang.org) if this is a problem for you. diff --git a/doc/encodings.rdoc b/doc/encodings.rdoc index 97c0d22616..d85099cdbc 100644 --- a/doc/encodings.rdoc +++ b/doc/encodings.rdoc @@ -419,7 +419,7 @@ These keyword-value pairs specify encoding options: hash = {"\u3042" => 'xyzzy'} hash.default = 'XYZZY' - s.encode('ASCII', fallback: h) # => "xyzzyfooXYZZY" + s.encode('ASCII', fallback: hash) # => "xyzzyfooXYZZY" def (fallback = "U+%.4X").escape(x) self % x.unpack("U") diff --git a/doc/extension.ja.rdoc b/doc/extension.ja.rdoc index 48699ac620..2f7856f3d4 100644 --- a/doc/extension.ja.rdoc +++ b/doc/extension.ja.rdoc @@ -784,6 +784,11 @@ RUBY_TYPED_WB_PROTECTED :: GC}[rdoc-ref:@Appendix+D.+-E4-B8-96-E4-BB-A3-E5-88-A5GC] も参照してください. +このマクロは例外を発生させる可能性があることに注意してくださ +い. ラップされる sval が,解放する必要があるリソース (割り +当てられたメモリ,外部ライブラリからのハンドルなど) を保持し +ている場合は,rb_protect を使用する必要があります. + Cの構造体の割当と対応するオブジェクトの生成を同時に行うマク ロとして以下のものが提供されています. diff --git a/doc/extension.rdoc b/doc/extension.rdoc index 01ac140e69..ba59d107ab 100644 --- a/doc/extension.rdoc +++ b/doc/extension.rdoc @@ -759,7 +759,12 @@ RUBY_TYPED_FROZEN_SHAREABLE :: If this flag is not set, the object can not become a shareable object by Ractor.make_shareable() method. -You can allocate and wrap the structure in one step. +Note that this macro can raise an exception. If sval to be wrapped +holds a resource needs to be released (e.g., allocated memory, handle +from an external library, and etc), you will have to use rb_protect. + +You can allocate and wrap the structure in one step, in more +preferable manner. TypedData_Make_Struct(klass, type, data_type, sval) @@ -768,6 +773,10 @@ the structure, which is also allocated. This macro works like: (sval = ZALLOC(type), TypedData_Wrap_Struct(klass, data_type, sval)) +However, you should use this macro instead of "allocation then wrap" +like the above code if it is simply allocated, because the latter can +raise a NoMemoryError and sval will be memory leaked in that case. + Arguments klass and data_type work like their counterparts in TypedData_Wrap_Struct(). A pointer to the allocated structure will be assigned to sval, which should be a pointer of the type specified. diff --git a/doc/globals.rdoc b/doc/globals.rdoc index 1bf78a60ab..1b51bb1b36 100644 --- a/doc/globals.rdoc +++ b/doc/globals.rdoc @@ -1,7 +1,7 @@ = Pre-Defined Global Variables Some of the pre-defined global variables have synonyms -that are available via module Engish. +that are available via module English. For each of those, the \English synonym is given. To use the module: diff --git a/doc/syntax/pattern_matching.rdoc b/doc/syntax/pattern_matching.rdoc index e49c09a1f8..6a30380f46 100644 --- a/doc/syntax/pattern_matching.rdoc +++ b/doc/syntax/pattern_matching.rdoc @@ -422,7 +422,8 @@ These core and library classes implement deconstruction: == Guard clauses -+if+ can be used to attach an additional condition (guard clause) when the pattern matches. This condition may use bound variables: ++if+ can be used to attach an additional condition (guard clause) when the pattern matches in +case+/+in+ expressions. +This condition may use bound variables: case [1, 2] in a, b if b == a*2 @@ -450,6 +451,11 @@ These core and library classes implement deconstruction: end #=> "matched" +Note that <code>=></code> and +in+ operator can not have a guard clause. +The following examples is parsed as a standalone expression with modifier +if+. + + [1, 2] in a, b if b == a*2 + == Appendix A. Pattern syntax Approximate syntax is: diff --git a/enc/depend b/enc/depend index 128203d5ec..2918a90a05 100644 --- a/enc/depend +++ b/enc/depend @@ -336,6 +336,7 @@ enc/ascii.$(OBJEXT): internal/special_consts.h enc/ascii.$(OBJEXT): internal/static_assert.h enc/ascii.$(OBJEXT): internal/stdalign.h enc/ascii.$(OBJEXT): internal/stdbool.h +enc/ascii.$(OBJEXT): internal/stdckdint.h enc/ascii.$(OBJEXT): internal/symbol.h enc/ascii.$(OBJEXT): internal/value.h enc/ascii.$(OBJEXT): internal/value_type.h @@ -497,6 +498,7 @@ enc/big5.$(OBJEXT): internal/special_consts.h enc/big5.$(OBJEXT): internal/static_assert.h enc/big5.$(OBJEXT): internal/stdalign.h enc/big5.$(OBJEXT): internal/stdbool.h +enc/big5.$(OBJEXT): internal/stdckdint.h enc/big5.$(OBJEXT): internal/symbol.h enc/big5.$(OBJEXT): internal/value.h enc/big5.$(OBJEXT): internal/value_type.h @@ -668,6 +670,7 @@ enc/cesu_8.$(OBJEXT): internal/special_consts.h enc/cesu_8.$(OBJEXT): internal/static_assert.h enc/cesu_8.$(OBJEXT): internal/stdalign.h enc/cesu_8.$(OBJEXT): internal/stdbool.h +enc/cesu_8.$(OBJEXT): internal/stdckdint.h enc/cesu_8.$(OBJEXT): internal/symbol.h enc/cesu_8.$(OBJEXT): internal/value.h enc/cesu_8.$(OBJEXT): internal/value_type.h @@ -829,6 +832,7 @@ enc/cp949.$(OBJEXT): internal/special_consts.h enc/cp949.$(OBJEXT): internal/static_assert.h enc/cp949.$(OBJEXT): internal/stdalign.h enc/cp949.$(OBJEXT): internal/stdbool.h +enc/cp949.$(OBJEXT): internal/stdckdint.h enc/cp949.$(OBJEXT): internal/symbol.h enc/cp949.$(OBJEXT): internal/value.h enc/cp949.$(OBJEXT): internal/value_type.h @@ -989,6 +993,7 @@ enc/emacs_mule.$(OBJEXT): internal/special_consts.h enc/emacs_mule.$(OBJEXT): internal/static_assert.h enc/emacs_mule.$(OBJEXT): internal/stdalign.h enc/emacs_mule.$(OBJEXT): internal/stdbool.h +enc/emacs_mule.$(OBJEXT): internal/stdckdint.h enc/emacs_mule.$(OBJEXT): internal/symbol.h enc/emacs_mule.$(OBJEXT): internal/value.h enc/emacs_mule.$(OBJEXT): internal/value_type.h @@ -1159,6 +1164,7 @@ enc/encdb.$(OBJEXT): internal/special_consts.h enc/encdb.$(OBJEXT): internal/static_assert.h enc/encdb.$(OBJEXT): internal/stdalign.h enc/encdb.$(OBJEXT): internal/stdbool.h +enc/encdb.$(OBJEXT): internal/stdckdint.h enc/encdb.$(OBJEXT): internal/symbol.h enc/encdb.$(OBJEXT): internal/value.h enc/encdb.$(OBJEXT): internal/value_type.h @@ -1322,6 +1328,7 @@ enc/euc_jp.$(OBJEXT): internal/special_consts.h enc/euc_jp.$(OBJEXT): internal/static_assert.h enc/euc_jp.$(OBJEXT): internal/stdalign.h enc/euc_jp.$(OBJEXT): internal/stdbool.h +enc/euc_jp.$(OBJEXT): internal/stdckdint.h enc/euc_jp.$(OBJEXT): internal/symbol.h enc/euc_jp.$(OBJEXT): internal/value.h enc/euc_jp.$(OBJEXT): internal/value_type.h @@ -1482,6 +1489,7 @@ enc/euc_kr.$(OBJEXT): internal/special_consts.h enc/euc_kr.$(OBJEXT): internal/static_assert.h enc/euc_kr.$(OBJEXT): internal/stdalign.h enc/euc_kr.$(OBJEXT): internal/stdbool.h +enc/euc_kr.$(OBJEXT): internal/stdckdint.h enc/euc_kr.$(OBJEXT): internal/symbol.h enc/euc_kr.$(OBJEXT): internal/value.h enc/euc_kr.$(OBJEXT): internal/value_type.h @@ -1642,6 +1650,7 @@ enc/euc_tw.$(OBJEXT): internal/special_consts.h enc/euc_tw.$(OBJEXT): internal/static_assert.h enc/euc_tw.$(OBJEXT): internal/stdalign.h enc/euc_tw.$(OBJEXT): internal/stdbool.h +enc/euc_tw.$(OBJEXT): internal/stdckdint.h enc/euc_tw.$(OBJEXT): internal/symbol.h enc/euc_tw.$(OBJEXT): internal/value.h enc/euc_tw.$(OBJEXT): internal/value_type.h @@ -1802,6 +1811,7 @@ enc/gb18030.$(OBJEXT): internal/special_consts.h enc/gb18030.$(OBJEXT): internal/static_assert.h enc/gb18030.$(OBJEXT): internal/stdalign.h enc/gb18030.$(OBJEXT): internal/stdbool.h +enc/gb18030.$(OBJEXT): internal/stdckdint.h enc/gb18030.$(OBJEXT): internal/symbol.h enc/gb18030.$(OBJEXT): internal/value.h enc/gb18030.$(OBJEXT): internal/value_type.h @@ -1962,6 +1972,7 @@ enc/gb2312.$(OBJEXT): internal/special_consts.h enc/gb2312.$(OBJEXT): internal/static_assert.h enc/gb2312.$(OBJEXT): internal/stdalign.h enc/gb2312.$(OBJEXT): internal/stdbool.h +enc/gb2312.$(OBJEXT): internal/stdckdint.h enc/gb2312.$(OBJEXT): internal/symbol.h enc/gb2312.$(OBJEXT): internal/value.h enc/gb2312.$(OBJEXT): internal/value_type.h @@ -2122,6 +2133,7 @@ enc/gbk.$(OBJEXT): internal/special_consts.h enc/gbk.$(OBJEXT): internal/static_assert.h enc/gbk.$(OBJEXT): internal/stdalign.h enc/gbk.$(OBJEXT): internal/stdbool.h +enc/gbk.$(OBJEXT): internal/stdckdint.h enc/gbk.$(OBJEXT): internal/symbol.h enc/gbk.$(OBJEXT): internal/value.h enc/gbk.$(OBJEXT): internal/value_type.h @@ -2283,6 +2295,7 @@ enc/iso_8859_1.$(OBJEXT): internal/special_consts.h enc/iso_8859_1.$(OBJEXT): internal/static_assert.h enc/iso_8859_1.$(OBJEXT): internal/stdalign.h enc/iso_8859_1.$(OBJEXT): internal/stdbool.h +enc/iso_8859_1.$(OBJEXT): internal/stdckdint.h enc/iso_8859_1.$(OBJEXT): internal/symbol.h enc/iso_8859_1.$(OBJEXT): internal/value.h enc/iso_8859_1.$(OBJEXT): internal/value_type.h @@ -2444,6 +2457,7 @@ enc/iso_8859_10.$(OBJEXT): internal/special_consts.h enc/iso_8859_10.$(OBJEXT): internal/static_assert.h enc/iso_8859_10.$(OBJEXT): internal/stdalign.h enc/iso_8859_10.$(OBJEXT): internal/stdbool.h +enc/iso_8859_10.$(OBJEXT): internal/stdckdint.h enc/iso_8859_10.$(OBJEXT): internal/symbol.h enc/iso_8859_10.$(OBJEXT): internal/value.h enc/iso_8859_10.$(OBJEXT): internal/value_type.h @@ -2604,6 +2618,7 @@ enc/iso_8859_11.$(OBJEXT): internal/special_consts.h enc/iso_8859_11.$(OBJEXT): internal/static_assert.h enc/iso_8859_11.$(OBJEXT): internal/stdalign.h enc/iso_8859_11.$(OBJEXT): internal/stdbool.h +enc/iso_8859_11.$(OBJEXT): internal/stdckdint.h enc/iso_8859_11.$(OBJEXT): internal/symbol.h enc/iso_8859_11.$(OBJEXT): internal/value.h enc/iso_8859_11.$(OBJEXT): internal/value_type.h @@ -2765,6 +2780,7 @@ enc/iso_8859_13.$(OBJEXT): internal/special_consts.h enc/iso_8859_13.$(OBJEXT): internal/static_assert.h enc/iso_8859_13.$(OBJEXT): internal/stdalign.h enc/iso_8859_13.$(OBJEXT): internal/stdbool.h +enc/iso_8859_13.$(OBJEXT): internal/stdckdint.h enc/iso_8859_13.$(OBJEXT): internal/symbol.h enc/iso_8859_13.$(OBJEXT): internal/value.h enc/iso_8859_13.$(OBJEXT): internal/value_type.h @@ -2926,6 +2942,7 @@ enc/iso_8859_14.$(OBJEXT): internal/special_consts.h enc/iso_8859_14.$(OBJEXT): internal/static_assert.h enc/iso_8859_14.$(OBJEXT): internal/stdalign.h enc/iso_8859_14.$(OBJEXT): internal/stdbool.h +enc/iso_8859_14.$(OBJEXT): internal/stdckdint.h enc/iso_8859_14.$(OBJEXT): internal/symbol.h enc/iso_8859_14.$(OBJEXT): internal/value.h enc/iso_8859_14.$(OBJEXT): internal/value_type.h @@ -3087,6 +3104,7 @@ enc/iso_8859_15.$(OBJEXT): internal/special_consts.h enc/iso_8859_15.$(OBJEXT): internal/static_assert.h enc/iso_8859_15.$(OBJEXT): internal/stdalign.h enc/iso_8859_15.$(OBJEXT): internal/stdbool.h +enc/iso_8859_15.$(OBJEXT): internal/stdckdint.h enc/iso_8859_15.$(OBJEXT): internal/symbol.h enc/iso_8859_15.$(OBJEXT): internal/value.h enc/iso_8859_15.$(OBJEXT): internal/value_type.h @@ -3248,6 +3266,7 @@ enc/iso_8859_16.$(OBJEXT): internal/special_consts.h enc/iso_8859_16.$(OBJEXT): internal/static_assert.h enc/iso_8859_16.$(OBJEXT): internal/stdalign.h enc/iso_8859_16.$(OBJEXT): internal/stdbool.h +enc/iso_8859_16.$(OBJEXT): internal/stdckdint.h enc/iso_8859_16.$(OBJEXT): internal/symbol.h enc/iso_8859_16.$(OBJEXT): internal/value.h enc/iso_8859_16.$(OBJEXT): internal/value_type.h @@ -3409,6 +3428,7 @@ enc/iso_8859_2.$(OBJEXT): internal/special_consts.h enc/iso_8859_2.$(OBJEXT): internal/static_assert.h enc/iso_8859_2.$(OBJEXT): internal/stdalign.h enc/iso_8859_2.$(OBJEXT): internal/stdbool.h +enc/iso_8859_2.$(OBJEXT): internal/stdckdint.h enc/iso_8859_2.$(OBJEXT): internal/symbol.h enc/iso_8859_2.$(OBJEXT): internal/value.h enc/iso_8859_2.$(OBJEXT): internal/value_type.h @@ -3570,6 +3590,7 @@ enc/iso_8859_3.$(OBJEXT): internal/special_consts.h enc/iso_8859_3.$(OBJEXT): internal/static_assert.h enc/iso_8859_3.$(OBJEXT): internal/stdalign.h enc/iso_8859_3.$(OBJEXT): internal/stdbool.h +enc/iso_8859_3.$(OBJEXT): internal/stdckdint.h enc/iso_8859_3.$(OBJEXT): internal/symbol.h enc/iso_8859_3.$(OBJEXT): internal/value.h enc/iso_8859_3.$(OBJEXT): internal/value_type.h @@ -3731,6 +3752,7 @@ enc/iso_8859_4.$(OBJEXT): internal/special_consts.h enc/iso_8859_4.$(OBJEXT): internal/static_assert.h enc/iso_8859_4.$(OBJEXT): internal/stdalign.h enc/iso_8859_4.$(OBJEXT): internal/stdbool.h +enc/iso_8859_4.$(OBJEXT): internal/stdckdint.h enc/iso_8859_4.$(OBJEXT): internal/symbol.h enc/iso_8859_4.$(OBJEXT): internal/value.h enc/iso_8859_4.$(OBJEXT): internal/value_type.h @@ -3891,6 +3913,7 @@ enc/iso_8859_5.$(OBJEXT): internal/special_consts.h enc/iso_8859_5.$(OBJEXT): internal/static_assert.h enc/iso_8859_5.$(OBJEXT): internal/stdalign.h enc/iso_8859_5.$(OBJEXT): internal/stdbool.h +enc/iso_8859_5.$(OBJEXT): internal/stdckdint.h enc/iso_8859_5.$(OBJEXT): internal/symbol.h enc/iso_8859_5.$(OBJEXT): internal/value.h enc/iso_8859_5.$(OBJEXT): internal/value_type.h @@ -4051,6 +4074,7 @@ enc/iso_8859_6.$(OBJEXT): internal/special_consts.h enc/iso_8859_6.$(OBJEXT): internal/static_assert.h enc/iso_8859_6.$(OBJEXT): internal/stdalign.h enc/iso_8859_6.$(OBJEXT): internal/stdbool.h +enc/iso_8859_6.$(OBJEXT): internal/stdckdint.h enc/iso_8859_6.$(OBJEXT): internal/symbol.h enc/iso_8859_6.$(OBJEXT): internal/value.h enc/iso_8859_6.$(OBJEXT): internal/value_type.h @@ -4211,6 +4235,7 @@ enc/iso_8859_7.$(OBJEXT): internal/special_consts.h enc/iso_8859_7.$(OBJEXT): internal/static_assert.h enc/iso_8859_7.$(OBJEXT): internal/stdalign.h enc/iso_8859_7.$(OBJEXT): internal/stdbool.h +enc/iso_8859_7.$(OBJEXT): internal/stdckdint.h enc/iso_8859_7.$(OBJEXT): internal/symbol.h enc/iso_8859_7.$(OBJEXT): internal/value.h enc/iso_8859_7.$(OBJEXT): internal/value_type.h @@ -4371,6 +4396,7 @@ enc/iso_8859_8.$(OBJEXT): internal/special_consts.h enc/iso_8859_8.$(OBJEXT): internal/static_assert.h enc/iso_8859_8.$(OBJEXT): internal/stdalign.h enc/iso_8859_8.$(OBJEXT): internal/stdbool.h +enc/iso_8859_8.$(OBJEXT): internal/stdckdint.h enc/iso_8859_8.$(OBJEXT): internal/symbol.h enc/iso_8859_8.$(OBJEXT): internal/value.h enc/iso_8859_8.$(OBJEXT): internal/value_type.h @@ -4532,6 +4558,7 @@ enc/iso_8859_9.$(OBJEXT): internal/special_consts.h enc/iso_8859_9.$(OBJEXT): internal/static_assert.h enc/iso_8859_9.$(OBJEXT): internal/stdalign.h enc/iso_8859_9.$(OBJEXT): internal/stdbool.h +enc/iso_8859_9.$(OBJEXT): internal/stdckdint.h enc/iso_8859_9.$(OBJEXT): internal/symbol.h enc/iso_8859_9.$(OBJEXT): internal/value.h enc/iso_8859_9.$(OBJEXT): internal/value_type.h @@ -4692,6 +4719,7 @@ enc/koi8_r.$(OBJEXT): internal/special_consts.h enc/koi8_r.$(OBJEXT): internal/static_assert.h enc/koi8_r.$(OBJEXT): internal/stdalign.h enc/koi8_r.$(OBJEXT): internal/stdbool.h +enc/koi8_r.$(OBJEXT): internal/stdckdint.h enc/koi8_r.$(OBJEXT): internal/symbol.h enc/koi8_r.$(OBJEXT): internal/value.h enc/koi8_r.$(OBJEXT): internal/value_type.h @@ -4852,6 +4880,7 @@ enc/koi8_u.$(OBJEXT): internal/special_consts.h enc/koi8_u.$(OBJEXT): internal/static_assert.h enc/koi8_u.$(OBJEXT): internal/stdalign.h enc/koi8_u.$(OBJEXT): internal/stdbool.h +enc/koi8_u.$(OBJEXT): internal/stdckdint.h enc/koi8_u.$(OBJEXT): internal/symbol.h enc/koi8_u.$(OBJEXT): internal/value.h enc/koi8_u.$(OBJEXT): internal/value_type.h @@ -5015,6 +5044,7 @@ enc/shift_jis.$(OBJEXT): internal/special_consts.h enc/shift_jis.$(OBJEXT): internal/static_assert.h enc/shift_jis.$(OBJEXT): internal/stdalign.h enc/shift_jis.$(OBJEXT): internal/stdbool.h +enc/shift_jis.$(OBJEXT): internal/stdckdint.h enc/shift_jis.$(OBJEXT): internal/symbol.h enc/shift_jis.$(OBJEXT): internal/value.h enc/shift_jis.$(OBJEXT): internal/value_type.h @@ -5174,6 +5204,7 @@ enc/trans/big5.$(OBJEXT): internal/special_consts.h enc/trans/big5.$(OBJEXT): internal/static_assert.h enc/trans/big5.$(OBJEXT): internal/stdalign.h enc/trans/big5.$(OBJEXT): internal/stdbool.h +enc/trans/big5.$(OBJEXT): internal/stdckdint.h enc/trans/big5.$(OBJEXT): internal/symbol.h enc/trans/big5.$(OBJEXT): internal/value.h enc/trans/big5.$(OBJEXT): internal/value_type.h @@ -5332,6 +5363,7 @@ enc/trans/cesu_8.$(OBJEXT): internal/special_consts.h enc/trans/cesu_8.$(OBJEXT): internal/static_assert.h enc/trans/cesu_8.$(OBJEXT): internal/stdalign.h enc/trans/cesu_8.$(OBJEXT): internal/stdbool.h +enc/trans/cesu_8.$(OBJEXT): internal/stdckdint.h enc/trans/cesu_8.$(OBJEXT): internal/symbol.h enc/trans/cesu_8.$(OBJEXT): internal/value.h enc/trans/cesu_8.$(OBJEXT): internal/value_type.h @@ -5490,6 +5522,7 @@ enc/trans/chinese.$(OBJEXT): internal/special_consts.h enc/trans/chinese.$(OBJEXT): internal/static_assert.h enc/trans/chinese.$(OBJEXT): internal/stdalign.h enc/trans/chinese.$(OBJEXT): internal/stdbool.h +enc/trans/chinese.$(OBJEXT): internal/stdckdint.h enc/trans/chinese.$(OBJEXT): internal/symbol.h enc/trans/chinese.$(OBJEXT): internal/value.h enc/trans/chinese.$(OBJEXT): internal/value_type.h @@ -5648,6 +5681,7 @@ enc/trans/ebcdic.$(OBJEXT): internal/special_consts.h enc/trans/ebcdic.$(OBJEXT): internal/static_assert.h enc/trans/ebcdic.$(OBJEXT): internal/stdalign.h enc/trans/ebcdic.$(OBJEXT): internal/stdbool.h +enc/trans/ebcdic.$(OBJEXT): internal/stdckdint.h enc/trans/ebcdic.$(OBJEXT): internal/symbol.h enc/trans/ebcdic.$(OBJEXT): internal/value.h enc/trans/ebcdic.$(OBJEXT): internal/value_type.h @@ -5806,6 +5840,7 @@ enc/trans/emoji.$(OBJEXT): internal/special_consts.h enc/trans/emoji.$(OBJEXT): internal/static_assert.h enc/trans/emoji.$(OBJEXT): internal/stdalign.h enc/trans/emoji.$(OBJEXT): internal/stdbool.h +enc/trans/emoji.$(OBJEXT): internal/stdckdint.h enc/trans/emoji.$(OBJEXT): internal/symbol.h enc/trans/emoji.$(OBJEXT): internal/value.h enc/trans/emoji.$(OBJEXT): internal/value_type.h @@ -5964,6 +5999,7 @@ enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/special_consts.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/static_assert.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/stdalign.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/stdbool.h +enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/stdckdint.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/symbol.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/value.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/value_type.h @@ -6122,6 +6158,7 @@ enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/special_consts.h enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/static_assert.h enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/stdalign.h enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/stdbool.h +enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/stdckdint.h enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/symbol.h enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/value.h enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/value_type.h @@ -6280,6 +6317,7 @@ enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/special_consts.h enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/static_assert.h enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/stdalign.h enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/stdbool.h +enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/stdckdint.h enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/symbol.h enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/value.h enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/value_type.h @@ -6438,6 +6476,7 @@ enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/special_consts.h enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/static_assert.h enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/stdalign.h enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/stdbool.h +enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/stdckdint.h enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/symbol.h enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/value.h enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/value_type.h @@ -6596,6 +6635,7 @@ enc/trans/escape.$(OBJEXT): internal/special_consts.h enc/trans/escape.$(OBJEXT): internal/static_assert.h enc/trans/escape.$(OBJEXT): internal/stdalign.h enc/trans/escape.$(OBJEXT): internal/stdbool.h +enc/trans/escape.$(OBJEXT): internal/stdckdint.h enc/trans/escape.$(OBJEXT): internal/symbol.h enc/trans/escape.$(OBJEXT): internal/value.h enc/trans/escape.$(OBJEXT): internal/value_type.h @@ -6754,6 +6794,7 @@ enc/trans/gb18030.$(OBJEXT): internal/special_consts.h enc/trans/gb18030.$(OBJEXT): internal/static_assert.h enc/trans/gb18030.$(OBJEXT): internal/stdalign.h enc/trans/gb18030.$(OBJEXT): internal/stdbool.h +enc/trans/gb18030.$(OBJEXT): internal/stdckdint.h enc/trans/gb18030.$(OBJEXT): internal/symbol.h enc/trans/gb18030.$(OBJEXT): internal/value.h enc/trans/gb18030.$(OBJEXT): internal/value_type.h @@ -6912,6 +6953,7 @@ enc/trans/gbk.$(OBJEXT): internal/special_consts.h enc/trans/gbk.$(OBJEXT): internal/static_assert.h enc/trans/gbk.$(OBJEXT): internal/stdalign.h enc/trans/gbk.$(OBJEXT): internal/stdbool.h +enc/trans/gbk.$(OBJEXT): internal/stdckdint.h enc/trans/gbk.$(OBJEXT): internal/symbol.h enc/trans/gbk.$(OBJEXT): internal/value.h enc/trans/gbk.$(OBJEXT): internal/value_type.h @@ -7070,6 +7112,7 @@ enc/trans/iso2022.$(OBJEXT): internal/special_consts.h enc/trans/iso2022.$(OBJEXT): internal/static_assert.h enc/trans/iso2022.$(OBJEXT): internal/stdalign.h enc/trans/iso2022.$(OBJEXT): internal/stdbool.h +enc/trans/iso2022.$(OBJEXT): internal/stdckdint.h enc/trans/iso2022.$(OBJEXT): internal/symbol.h enc/trans/iso2022.$(OBJEXT): internal/value.h enc/trans/iso2022.$(OBJEXT): internal/value_type.h @@ -7228,6 +7271,7 @@ enc/trans/japanese.$(OBJEXT): internal/special_consts.h enc/trans/japanese.$(OBJEXT): internal/static_assert.h enc/trans/japanese.$(OBJEXT): internal/stdalign.h enc/trans/japanese.$(OBJEXT): internal/stdbool.h +enc/trans/japanese.$(OBJEXT): internal/stdckdint.h enc/trans/japanese.$(OBJEXT): internal/symbol.h enc/trans/japanese.$(OBJEXT): internal/value.h enc/trans/japanese.$(OBJEXT): internal/value_type.h @@ -7386,6 +7430,7 @@ enc/trans/japanese_euc.$(OBJEXT): internal/special_consts.h enc/trans/japanese_euc.$(OBJEXT): internal/static_assert.h enc/trans/japanese_euc.$(OBJEXT): internal/stdalign.h enc/trans/japanese_euc.$(OBJEXT): internal/stdbool.h +enc/trans/japanese_euc.$(OBJEXT): internal/stdckdint.h enc/trans/japanese_euc.$(OBJEXT): internal/symbol.h enc/trans/japanese_euc.$(OBJEXT): internal/value.h enc/trans/japanese_euc.$(OBJEXT): internal/value_type.h @@ -7544,6 +7589,7 @@ enc/trans/japanese_sjis.$(OBJEXT): internal/special_consts.h enc/trans/japanese_sjis.$(OBJEXT): internal/static_assert.h enc/trans/japanese_sjis.$(OBJEXT): internal/stdalign.h enc/trans/japanese_sjis.$(OBJEXT): internal/stdbool.h +enc/trans/japanese_sjis.$(OBJEXT): internal/stdckdint.h enc/trans/japanese_sjis.$(OBJEXT): internal/symbol.h enc/trans/japanese_sjis.$(OBJEXT): internal/value.h enc/trans/japanese_sjis.$(OBJEXT): internal/value_type.h @@ -7702,6 +7748,7 @@ enc/trans/korean.$(OBJEXT): internal/special_consts.h enc/trans/korean.$(OBJEXT): internal/static_assert.h enc/trans/korean.$(OBJEXT): internal/stdalign.h enc/trans/korean.$(OBJEXT): internal/stdbool.h +enc/trans/korean.$(OBJEXT): internal/stdckdint.h enc/trans/korean.$(OBJEXT): internal/symbol.h enc/trans/korean.$(OBJEXT): internal/value.h enc/trans/korean.$(OBJEXT): internal/value_type.h @@ -7859,6 +7906,7 @@ enc/trans/newline.$(OBJEXT): internal/special_consts.h enc/trans/newline.$(OBJEXT): internal/static_assert.h enc/trans/newline.$(OBJEXT): internal/stdalign.h enc/trans/newline.$(OBJEXT): internal/stdbool.h +enc/trans/newline.$(OBJEXT): internal/stdckdint.h enc/trans/newline.$(OBJEXT): internal/symbol.h enc/trans/newline.$(OBJEXT): internal/value.h enc/trans/newline.$(OBJEXT): internal/value_type.h @@ -8017,6 +8065,7 @@ enc/trans/single_byte.$(OBJEXT): internal/special_consts.h enc/trans/single_byte.$(OBJEXT): internal/static_assert.h enc/trans/single_byte.$(OBJEXT): internal/stdalign.h enc/trans/single_byte.$(OBJEXT): internal/stdbool.h +enc/trans/single_byte.$(OBJEXT): internal/stdckdint.h enc/trans/single_byte.$(OBJEXT): internal/symbol.h enc/trans/single_byte.$(OBJEXT): internal/value.h enc/trans/single_byte.$(OBJEXT): internal/value_type.h @@ -8175,6 +8224,7 @@ enc/trans/transdb.$(OBJEXT): internal/special_consts.h enc/trans/transdb.$(OBJEXT): internal/static_assert.h enc/trans/transdb.$(OBJEXT): internal/stdalign.h enc/trans/transdb.$(OBJEXT): internal/stdbool.h +enc/trans/transdb.$(OBJEXT): internal/stdckdint.h enc/trans/transdb.$(OBJEXT): internal/symbol.h enc/trans/transdb.$(OBJEXT): internal/value.h enc/trans/transdb.$(OBJEXT): internal/value_type.h @@ -8334,6 +8384,7 @@ enc/trans/utf8_mac.$(OBJEXT): internal/special_consts.h enc/trans/utf8_mac.$(OBJEXT): internal/static_assert.h enc/trans/utf8_mac.$(OBJEXT): internal/stdalign.h enc/trans/utf8_mac.$(OBJEXT): internal/stdbool.h +enc/trans/utf8_mac.$(OBJEXT): internal/stdckdint.h enc/trans/utf8_mac.$(OBJEXT): internal/symbol.h enc/trans/utf8_mac.$(OBJEXT): internal/value.h enc/trans/utf8_mac.$(OBJEXT): internal/value_type.h @@ -8492,6 +8543,7 @@ enc/trans/utf_16_32.$(OBJEXT): internal/special_consts.h enc/trans/utf_16_32.$(OBJEXT): internal/static_assert.h enc/trans/utf_16_32.$(OBJEXT): internal/stdalign.h enc/trans/utf_16_32.$(OBJEXT): internal/stdbool.h +enc/trans/utf_16_32.$(OBJEXT): internal/stdckdint.h enc/trans/utf_16_32.$(OBJEXT): internal/symbol.h enc/trans/utf_16_32.$(OBJEXT): internal/value.h enc/trans/utf_16_32.$(OBJEXT): internal/value_type.h @@ -8653,6 +8705,7 @@ enc/unicode.$(OBJEXT): internal/special_consts.h enc/unicode.$(OBJEXT): internal/static_assert.h enc/unicode.$(OBJEXT): internal/stdalign.h enc/unicode.$(OBJEXT): internal/stdbool.h +enc/unicode.$(OBJEXT): internal/stdckdint.h enc/unicode.$(OBJEXT): internal/symbol.h enc/unicode.$(OBJEXT): internal/value.h enc/unicode.$(OBJEXT): internal/value_type.h @@ -8823,6 +8876,7 @@ enc/us_ascii.$(OBJEXT): internal/special_consts.h enc/us_ascii.$(OBJEXT): internal/static_assert.h enc/us_ascii.$(OBJEXT): internal/stdalign.h enc/us_ascii.$(OBJEXT): internal/stdbool.h +enc/us_ascii.$(OBJEXT): internal/stdckdint.h enc/us_ascii.$(OBJEXT): internal/symbol.h enc/us_ascii.$(OBJEXT): internal/value.h enc/us_ascii.$(OBJEXT): internal/value_type.h @@ -8985,6 +9039,7 @@ enc/utf_16be.$(OBJEXT): internal/special_consts.h enc/utf_16be.$(OBJEXT): internal/static_assert.h enc/utf_16be.$(OBJEXT): internal/stdalign.h enc/utf_16be.$(OBJEXT): internal/stdbool.h +enc/utf_16be.$(OBJEXT): internal/stdckdint.h enc/utf_16be.$(OBJEXT): internal/symbol.h enc/utf_16be.$(OBJEXT): internal/value.h enc/utf_16be.$(OBJEXT): internal/value_type.h @@ -9146,6 +9201,7 @@ enc/utf_16le.$(OBJEXT): internal/special_consts.h enc/utf_16le.$(OBJEXT): internal/static_assert.h enc/utf_16le.$(OBJEXT): internal/stdalign.h enc/utf_16le.$(OBJEXT): internal/stdbool.h +enc/utf_16le.$(OBJEXT): internal/stdckdint.h enc/utf_16le.$(OBJEXT): internal/symbol.h enc/utf_16le.$(OBJEXT): internal/value.h enc/utf_16le.$(OBJEXT): internal/value_type.h @@ -9307,6 +9363,7 @@ enc/utf_32be.$(OBJEXT): internal/special_consts.h enc/utf_32be.$(OBJEXT): internal/static_assert.h enc/utf_32be.$(OBJEXT): internal/stdalign.h enc/utf_32be.$(OBJEXT): internal/stdbool.h +enc/utf_32be.$(OBJEXT): internal/stdckdint.h enc/utf_32be.$(OBJEXT): internal/symbol.h enc/utf_32be.$(OBJEXT): internal/value.h enc/utf_32be.$(OBJEXT): internal/value_type.h @@ -9468,6 +9525,7 @@ enc/utf_32le.$(OBJEXT): internal/special_consts.h enc/utf_32le.$(OBJEXT): internal/static_assert.h enc/utf_32le.$(OBJEXT): internal/stdalign.h enc/utf_32le.$(OBJEXT): internal/stdbool.h +enc/utf_32le.$(OBJEXT): internal/stdckdint.h enc/utf_32le.$(OBJEXT): internal/symbol.h enc/utf_32le.$(OBJEXT): internal/value.h enc/utf_32le.$(OBJEXT): internal/value_type.h @@ -9638,6 +9696,7 @@ enc/utf_8.$(OBJEXT): internal/special_consts.h enc/utf_8.$(OBJEXT): internal/static_assert.h enc/utf_8.$(OBJEXT): internal/stdalign.h enc/utf_8.$(OBJEXT): internal/stdbool.h +enc/utf_8.$(OBJEXT): internal/stdckdint.h enc/utf_8.$(OBJEXT): internal/symbol.h enc/utf_8.$(OBJEXT): internal/value.h enc/utf_8.$(OBJEXT): internal/value_type.h @@ -9800,6 +9859,7 @@ enc/windows_1250.$(OBJEXT): internal/special_consts.h enc/windows_1250.$(OBJEXT): internal/static_assert.h enc/windows_1250.$(OBJEXT): internal/stdalign.h enc/windows_1250.$(OBJEXT): internal/stdbool.h +enc/windows_1250.$(OBJEXT): internal/stdckdint.h enc/windows_1250.$(OBJEXT): internal/symbol.h enc/windows_1250.$(OBJEXT): internal/value.h enc/windows_1250.$(OBJEXT): internal/value_type.h @@ -9960,6 +10020,7 @@ enc/windows_1251.$(OBJEXT): internal/special_consts.h enc/windows_1251.$(OBJEXT): internal/static_assert.h enc/windows_1251.$(OBJEXT): internal/stdalign.h enc/windows_1251.$(OBJEXT): internal/stdbool.h +enc/windows_1251.$(OBJEXT): internal/stdckdint.h enc/windows_1251.$(OBJEXT): internal/symbol.h enc/windows_1251.$(OBJEXT): internal/value.h enc/windows_1251.$(OBJEXT): internal/value_type.h @@ -10121,6 +10182,7 @@ enc/windows_1252.$(OBJEXT): internal/special_consts.h enc/windows_1252.$(OBJEXT): internal/static_assert.h enc/windows_1252.$(OBJEXT): internal/stdalign.h enc/windows_1252.$(OBJEXT): internal/stdbool.h +enc/windows_1252.$(OBJEXT): internal/stdckdint.h enc/windows_1252.$(OBJEXT): internal/symbol.h enc/windows_1252.$(OBJEXT): internal/value.h enc/windows_1252.$(OBJEXT): internal/value_type.h @@ -10281,6 +10343,7 @@ enc/windows_1253.$(OBJEXT): internal/special_consts.h enc/windows_1253.$(OBJEXT): internal/static_assert.h enc/windows_1253.$(OBJEXT): internal/stdalign.h enc/windows_1253.$(OBJEXT): internal/stdbool.h +enc/windows_1253.$(OBJEXT): internal/stdckdint.h enc/windows_1253.$(OBJEXT): internal/symbol.h enc/windows_1253.$(OBJEXT): internal/value.h enc/windows_1253.$(OBJEXT): internal/value_type.h @@ -10442,6 +10505,7 @@ enc/windows_1254.$(OBJEXT): internal/special_consts.h enc/windows_1254.$(OBJEXT): internal/static_assert.h enc/windows_1254.$(OBJEXT): internal/stdalign.h enc/windows_1254.$(OBJEXT): internal/stdbool.h +enc/windows_1254.$(OBJEXT): internal/stdckdint.h enc/windows_1254.$(OBJEXT): internal/symbol.h enc/windows_1254.$(OBJEXT): internal/value.h enc/windows_1254.$(OBJEXT): internal/value_type.h @@ -10603,6 +10667,7 @@ enc/windows_1257.$(OBJEXT): internal/special_consts.h enc/windows_1257.$(OBJEXT): internal/static_assert.h enc/windows_1257.$(OBJEXT): internal/stdalign.h enc/windows_1257.$(OBJEXT): internal/stdbool.h +enc/windows_1257.$(OBJEXT): internal/stdckdint.h enc/windows_1257.$(OBJEXT): internal/symbol.h enc/windows_1257.$(OBJEXT): internal/value.h enc/windows_1257.$(OBJEXT): internal/value_type.h @@ -10766,6 +10831,7 @@ enc/windows_31j.$(OBJEXT): internal/special_consts.h enc/windows_31j.$(OBJEXT): internal/static_assert.h enc/windows_31j.$(OBJEXT): internal/stdalign.h enc/windows_31j.$(OBJEXT): internal/stdbool.h +enc/windows_31j.$(OBJEXT): internal/stdckdint.h enc/windows_31j.$(OBJEXT): internal/symbol.h enc/windows_31j.$(OBJEXT): internal/value.h enc/windows_31j.$(OBJEXT): internal/value_type.h diff --git a/enc/encinit.c.erb b/enc/encinit.c.erb index 120408f8e3..3662ba200d 100644 --- a/enc/encinit.c.erb +++ b/enc/encinit.c.erb @@ -1,3 +1,6 @@ +/* Automatically generated from <%= erb.filename %> + * Do not edit<%# directly%>. + */ /* Copyright 2012 Google Inc. Some Rights Reserved. * Author: yugui@google.com (Yugui Sonoda) */ diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb index 9761edd6d9..96d1944bcb 100755 --- a/enc/make_encmake.rb +++ b/enc/make_encmake.rb @@ -147,10 +147,6 @@ if MODULE_TYPE == :static Dir.mkdir 'enc' rescue Errno::EEXIST end - File.open("enc/encinit.c", "w") {|f| - f.puts "/* Automatically generated from enc/encinit.c.erb" - f.puts " * Do not edit." - f.puts " */" - f.puts tmp - } + require 'tool/lib/output' + Output.new(path: "enc/encinit.c", ifchange: true).write(tmp) end diff --git a/encoding.c b/encoding.c index df17f63bb1..480cc8bdc6 100644 --- a/encoding.c +++ b/encoding.c @@ -1015,13 +1015,22 @@ rb_enc_get(VALUE obj) return rb_enc_from_index(rb_enc_get_index(obj)); } +const char * +rb_enc_inspect_name(rb_encoding *enc) +{ + if (enc == global_enc_ascii) { + return "BINARY (ASCII-8BIT)"; + } + return enc->name; +} + static rb_encoding* rb_encoding_check(rb_encoding* enc, VALUE str1, VALUE str2) { if (!enc) rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s", - rb_enc_name(rb_enc_get(str1)), - rb_enc_name(rb_enc_get(str2))); + rb_enc_inspect_name(rb_enc_get(str1)), + rb_enc_inspect_name(rb_enc_get(str2))); return enc; } @@ -1263,9 +1272,10 @@ enc_inspect(VALUE self) if (!(enc = DATA_PTR(self)) || rb_enc_from_index(rb_enc_to_index(enc)) != enc) { rb_raise(rb_eTypeError, "broken Encoding"); } + return rb_enc_sprintf(rb_usascii_encoding(), "#<%"PRIsVALUE":%s%s%s>", rb_obj_class(self), - rb_enc_name(enc), + rb_enc_inspect_name(enc), (ENC_DUMMY_P(enc) ? " (dummy)" : ""), rb_enc_autoload_p(enc) ? " (autoload)" : ""); } @@ -870,134 +870,151 @@ ary_inject_op(VALUE ary, VALUE init, VALUE op) /* * call-seq: - * inject(symbol) -> object - * inject(initial_operand, symbol) -> object - * inject {|memo, operand| ... } -> object - * inject(initial_operand) {|memo, operand| ... } -> object - * - * Returns an object formed from operands via either: + * inject(symbol) -> object + * inject(initial_value, symbol) -> object + * inject {|memo, value| ... } -> object + * inject(initial_value) {|memo, value| ... } -> object * - * - A method named by +symbol+. - * - A block to which each operand is passed. - * - * With method-name argument +symbol+, - * combines operands using the method: - * - * # Sum, without initial_operand. - * (1..4).inject(:+) # => 10 - * # Sum, with initial_operand. - * (1..4).inject(10, :+) # => 20 + * Returns the result of applying a reducer to an initial value and + * the first element of the Enumerable. It then takes the result and applies the + * function to it and the second element of the collection, and so on. The + * return value is the result returned by the final call to the function. * - * With a block, passes each operand to the block: - * - * # Sum of squares, without initial_operand. - * (1..4).inject {|sum, n| sum + n*n } # => 30 - * # Sum of squares, with initial_operand. - * (1..4).inject(2) {|sum, n| sum + n*n } # => 32 + * You can think of * - * <b>Operands</b> + * [ a, b, c, d ].inject(i) { |r, v| fn(r, v) } * - * If argument +initial_operand+ is not given, - * the operands for +inject+ are simply the elements of +self+. - * Example calls and their operands: + * as being * - * - <tt>(1..4).inject(:+)</tt>:: <tt>[1, 2, 3, 4]</tt>. - * - <tt>(1...4).inject(:+)</tt>:: <tt>[1, 2, 3]</tt>. - * - <tt>('a'..'d').inject(:+)</tt>:: <tt>['a', 'b', 'c', 'd']</tt>. - * - <tt>('a'...'d').inject(:+)</tt>:: <tt>['a', 'b', 'c']</tt>. + * fn(fn(fn(fn(i, a), b), c), d) * - * Examples with first operand (which is <tt>self.first</tt>) of various types: + * In a way the +inject+ function _injects_ the function + * between the elements of the enumerable. * - * # Integer. - * (1..4).inject(:+) # => 10 - * # Float. - * [1.0, 2, 3, 4].inject(:+) # => 10.0 - * # Character. - * ('a'..'d').inject(:+) # => "abcd" - * # Complex. - * [Complex(1, 2), 3, 4].inject(:+) # => (8+2i) + * +inject+ is aliased as +reduce+. You use it when you want to + * _reduce_ a collection to a single value. * - * If argument +initial_operand+ is given, - * the operands for +inject+ are that value plus the elements of +self+. - * Example calls their operands: + * <b>The Calling Sequences</b> * - * - <tt>(1..4).inject(10, :+)</tt>:: <tt>[10, 1, 2, 3, 4]</tt>. - * - <tt>(1...4).inject(10, :+)</tt>:: <tt>[10, 1, 2, 3]</tt>. - * - <tt>('a'..'d').inject('e', :+)</tt>:: <tt>['e', 'a', 'b', 'c', 'd']</tt>. - * - <tt>('a'...'d').inject('e', :+)</tt>:: <tt>['e', 'a', 'b', 'c']</tt>. + * Let's start with the most verbose: * - * Examples with +initial_operand+ of various types: + * enum.inject(initial_value) do |result, next_value| + * # do something with +result+ and +next_value+ + * # the value returned by the block becomes the + * # value passed in to the next iteration + * # as +result+ + * end * - * # Integer. - * (1..4).inject(2, :+) # => 12 - * # Float. - * (1..4).inject(2.0, :+) # => 12.0 - * # String. - * ('a'..'d').inject('foo', :+) # => "fooabcd" - * # Array. - * %w[a b c].inject(['x'], :push) # => ["x", "a", "b", "c"] - * # Complex. - * (1..4).inject(Complex(2, 2), :+) # => (12+2i) + * For example: * - * <b>Combination by Given \Method</b> + * product = [ 2, 3, 4 ].inject(1) do |result, next_value| + * result * next_value + * end + * product #=> 24 * - * If the method-name argument +symbol+ is given, - * the operands are combined by that method: + * When this runs, the block is first called with +1+ (the initial value) and + * +2+ (the first element of the array). The block returns <tt>1*2</tt>, so on + * the next iteration the block is called with +2+ (the previous result) and + * +3+. The block returns +6+, and is called one last time with +6+ and +4+. + * The result of the block, +24+ becomes the value returned by +inject+. This + * code returns the product of the elements in the enumerable. * - * - The first and second operands are combined. - * - That result is combined with the third operand. - * - That result is combined with the fourth operand. - * - And so on. + * <b>First Shortcut: Default Initial value</b> * - * The return value from +inject+ is the result of the last combination. + * In the case of the previous example, the initial value, +1+, wasn't really + * necessary: the calculation of the product of a list of numbers is self-contained. * - * This call to +inject+ computes the sum of the operands: + * In these circumstances, you can omit the +initial_value+ parameter. +inject+ + * will then initially call the block with the first element of the collection + * as the +result+ parameter and the second element as the +next_value+. * - * (1..4).inject(:+) # => 10 + * [ 2, 3, 4 ].inject do |result, next_value| + * result * next_value + * end * - * Examples with various methods: + * This shortcut is convenient, but can only be used when the block produces a result + * which can be passed back to it as a first parameter. * - * # Integer addition. - * (1..4).inject(:+) # => 10 - * # Integer multiplication. - * (1..4).inject(:*) # => 24 - * # Character range concatenation. - * ('a'..'d').inject('', :+) # => "abcd" - * # String array concatenation. - * %w[foo bar baz].inject('', :+) # => "foobarbaz" - * # Hash update. - * h = [{foo: 0, bar: 1}, {baz: 2}, {bat: 3}].inject(:update) - * h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3} - * # Hash conversion to nested arrays. - * h = {foo: 0, bar: 1}.inject([], :push) - * h # => [[:foo, 0], [:bar, 1]] + * Here's an example where that's not the case: it returns a hash where the keys are words + * and the values are the number of occurrences of that word in the enumerable. * - * <b>Combination by Given Block</b> + * freqs = File.read("README.md") + * .scan(/\w{2,}/) + * .reduce(Hash.new(0)) do |counts, word| + * counts[word] += 1 + * counts + * end + * freqs #=> {"Actions"=>4, + * "Status"=>5, + * "MinGW"=>3, + * "https"=>27, + * "github"=>10, + * "com"=>15, ... * - * If a block is given, the operands are passed to the block: + * Note that the last line of the block is just the word +counts+. This ensures the + * return value of the block is the result that's being calculated. * - * - The first call passes the first and second operands. - * - The second call passes the result of the first call, - * along with the third operand. - * - The third call passes the result of the second call, - * along with the fourth operand. - * - And so on. + * <b>Second Shortcut: a Reducer function</b> * - * The return value from +inject+ is the return value from the last block call. - * - * This call to +inject+ gives a block - * that writes the memo and element, and also sums the elements: + * A <i>reducer function</i> is a function that takes a partial result and the next value, + * returning the next partial result. The block that is given to +inject+ is a reducer. * - * (1..4).inject do |memo, element| - * p "Memo: #{memo}; element: #{element}" - * memo + element - * end # => 10 + * You can also write a reducer as a function and pass the name of that function + * (as a symbol) to +inject+. However, for this to work, the function * - * Output: + * 1. Must be defined on the type of the result value + * 2. Must accept a single parameter, the next value in the collection, and + * 3. Must return an updated result which will also implement the function. + * + * Here's an example that adds elements to a string. The two calls invoke the functions + * String#concat and String#+ on the result so far, passing it the next value. + * + * s = [ "cat", " ", "dog" ].inject("", :concat) + * s #=> "cat dog" + * s = [ "cat", " ", "dog" ].inject("The result is:", :+) + * s #=> "The result is: cat dog" + * + * Here's a more complex example when the result object maintains + * state of a different type to the enumerable elements. + * + * class Turtle + * + * def initialize + * @x = @y = 0 + * end + * + * def move(dir) + * case dir + * when "n" then @y += 1 + * when "s" then @y -= 1 + * when "e" then @x += 1 + * when "w" then @x -= 1 + * end + * self + * end + * end + * + * position = "nnneesw".chars.reduce(Turtle.new, :move) + * position #=>> #<Turtle:0x00000001052f4698 @y=2, @x=1> + * + * <b>Third Shortcut: Reducer With no Initial Value</b> + * + * If your reducer returns a value that it can accept as a parameter, then you + * don't have to pass in an initial value. Here <tt>:*</tt> is the name of the + * _times_ function: + * + * product = [ 2, 3, 4 ].inject(:*) + * product # => 24 + * + * String concatenation again: + * + * s = [ "cat", " ", "dog" ].inject(:+) + * s #=> "cat dog" + * + * And an example that converts a hash to an array of two-element subarrays. * - * "Memo: 1; element: 2" - * "Memo: 3; element: 3" - * "Memo: 6; element: 4" + * nested = {foo: 0, bar: 1}.inject([], :push) + * nested # => [[:foo, 0], [:bar, 1]] * * */ @@ -386,18 +386,28 @@ warn_vsprintf(rb_encoding *enc, const char *file, int line, const char *fmt, va_ return rb_str_cat2(str, "\n"); } -#define with_warn_vsprintf(file, line, fmt) \ +#define with_warn_vsprintf(enc, file, line, fmt) \ VALUE str; \ va_list args; \ va_start(args, fmt); \ - str = warn_vsprintf(NULL, file, line, fmt, args); \ + str = warn_vsprintf(enc, file, line, fmt, args); \ va_end(args); void rb_compile_warn(const char *file, int line, const char *fmt, ...) { if (!NIL_P(ruby_verbose)) { - with_warn_vsprintf(file, line, fmt) { + with_warn_vsprintf(NULL, file, line, fmt) { + rb_write_warning_str(str); + } + } +} + +void +rb_enc_compile_warn(rb_encoding *enc, const char *file, int line, const char *fmt, ...) +{ + if (!NIL_P(ruby_verbose)) { + with_warn_vsprintf(enc, file, line, fmt) { rb_write_warning_str(str); } } @@ -408,7 +418,18 @@ void rb_compile_warning(const char *file, int line, const char *fmt, ...) { if (RTEST(ruby_verbose)) { - with_warn_vsprintf(file, line, fmt) { + with_warn_vsprintf(NULL, file, line, fmt) { + rb_write_warning_str(str); + } + } +} + +/* rb_enc_compile_warning() reports only in verbose mode */ +void +rb_enc_compile_warning(rb_encoding *enc, const char *file, int line, const char *fmt, ...) +{ + if (RTEST(ruby_verbose)) { + with_warn_vsprintf(enc, file, line, fmt) { rb_write_warning_str(str); } } @@ -418,7 +439,7 @@ void rb_category_compile_warn(rb_warning_category_t category, const char *file, int line, const char *fmt, ...) { if (!NIL_P(ruby_verbose)) { - with_warn_vsprintf(file, line, fmt) { + with_warn_vsprintf(NULL, file, line, fmt) { rb_warn_category(str, rb_warning_category_to_name(category)); } } diff --git a/ext/-test-/RUBY_ALIGNOF/depend b/ext/-test-/RUBY_ALIGNOF/depend index 3011b637e5..25364e55fb 100644 --- a/ext/-test-/RUBY_ALIGNOF/depend +++ b/ext/-test-/RUBY_ALIGNOF/depend @@ -147,6 +147,7 @@ c.o: $(hdrdir)/ruby/internal/special_consts.h c.o: $(hdrdir)/ruby/internal/static_assert.h c.o: $(hdrdir)/ruby/internal/stdalign.h c.o: $(hdrdir)/ruby/internal/stdbool.h +c.o: $(hdrdir)/ruby/internal/stdckdint.h c.o: $(hdrdir)/ruby/internal/symbol.h c.o: $(hdrdir)/ruby/internal/value.h c.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/arith_seq/beg_len_step/depend b/ext/-test-/arith_seq/beg_len_step/depend index dc807eaa99..702a0037a8 100644 --- a/ext/-test-/arith_seq/beg_len_step/depend +++ b/ext/-test-/arith_seq/beg_len_step/depend @@ -146,6 +146,7 @@ beg_len_step.o: $(hdrdir)/ruby/internal/special_consts.h beg_len_step.o: $(hdrdir)/ruby/internal/static_assert.h beg_len_step.o: $(hdrdir)/ruby/internal/stdalign.h beg_len_step.o: $(hdrdir)/ruby/internal/stdbool.h +beg_len_step.o: $(hdrdir)/ruby/internal/stdckdint.h beg_len_step.o: $(hdrdir)/ruby/internal/symbol.h beg_len_step.o: $(hdrdir)/ruby/internal/value.h beg_len_step.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/arith_seq/extract/depend b/ext/-test-/arith_seq/extract/depend index 231736b277..fdbd71dfbc 100644 --- a/ext/-test-/arith_seq/extract/depend +++ b/ext/-test-/arith_seq/extract/depend @@ -146,6 +146,7 @@ extract.o: $(hdrdir)/ruby/internal/special_consts.h extract.o: $(hdrdir)/ruby/internal/static_assert.h extract.o: $(hdrdir)/ruby/internal/stdalign.h extract.o: $(hdrdir)/ruby/internal/stdbool.h +extract.o: $(hdrdir)/ruby/internal/stdckdint.h extract.o: $(hdrdir)/ruby/internal/symbol.h extract.o: $(hdrdir)/ruby/internal/value.h extract.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/array/concat/depend b/ext/-test-/array/concat/depend index d66e7a540f..f2213a42ea 100644 --- a/ext/-test-/array/concat/depend +++ b/ext/-test-/array/concat/depend @@ -147,6 +147,7 @@ to_ary_concat.o: $(hdrdir)/ruby/internal/special_consts.h to_ary_concat.o: $(hdrdir)/ruby/internal/static_assert.h to_ary_concat.o: $(hdrdir)/ruby/internal/stdalign.h to_ary_concat.o: $(hdrdir)/ruby/internal/stdbool.h +to_ary_concat.o: $(hdrdir)/ruby/internal/stdckdint.h to_ary_concat.o: $(hdrdir)/ruby/internal/symbol.h to_ary_concat.o: $(hdrdir)/ruby/internal/value.h to_ary_concat.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/array/resize/depend b/ext/-test-/array/resize/depend index a9c02b3db2..f88a9d03c1 100644 --- a/ext/-test-/array/resize/depend +++ b/ext/-test-/array/resize/depend @@ -146,6 +146,7 @@ resize.o: $(hdrdir)/ruby/internal/special_consts.h resize.o: $(hdrdir)/ruby/internal/static_assert.h resize.o: $(hdrdir)/ruby/internal/stdalign.h resize.o: $(hdrdir)/ruby/internal/stdbool.h +resize.o: $(hdrdir)/ruby/internal/stdckdint.h resize.o: $(hdrdir)/ruby/internal/symbol.h resize.o: $(hdrdir)/ruby/internal/value.h resize.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/bignum/depend b/ext/-test-/bignum/depend index d4392bb6a1..078915ab72 100644 --- a/ext/-test-/bignum/depend +++ b/ext/-test-/bignum/depend @@ -146,6 +146,7 @@ big2str.o: $(hdrdir)/ruby/internal/special_consts.h big2str.o: $(hdrdir)/ruby/internal/static_assert.h big2str.o: $(hdrdir)/ruby/internal/stdalign.h big2str.o: $(hdrdir)/ruby/internal/stdbool.h +big2str.o: $(hdrdir)/ruby/internal/stdckdint.h big2str.o: $(hdrdir)/ruby/internal/symbol.h big2str.o: $(hdrdir)/ruby/internal/value.h big2str.o: $(hdrdir)/ruby/internal/value_type.h @@ -305,6 +306,7 @@ bigzero.o: $(hdrdir)/ruby/internal/special_consts.h bigzero.o: $(hdrdir)/ruby/internal/static_assert.h bigzero.o: $(hdrdir)/ruby/internal/stdalign.h bigzero.o: $(hdrdir)/ruby/internal/stdbool.h +bigzero.o: $(hdrdir)/ruby/internal/stdckdint.h bigzero.o: $(hdrdir)/ruby/internal/symbol.h bigzero.o: $(hdrdir)/ruby/internal/value.h bigzero.o: $(hdrdir)/ruby/internal/value_type.h @@ -464,6 +466,7 @@ div.o: $(hdrdir)/ruby/internal/special_consts.h div.o: $(hdrdir)/ruby/internal/static_assert.h div.o: $(hdrdir)/ruby/internal/stdalign.h div.o: $(hdrdir)/ruby/internal/stdbool.h +div.o: $(hdrdir)/ruby/internal/stdckdint.h div.o: $(hdrdir)/ruby/internal/symbol.h div.o: $(hdrdir)/ruby/internal/value.h div.o: $(hdrdir)/ruby/internal/value_type.h @@ -624,6 +627,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -782,6 +786,7 @@ intpack.o: $(hdrdir)/ruby/internal/special_consts.h intpack.o: $(hdrdir)/ruby/internal/static_assert.h intpack.o: $(hdrdir)/ruby/internal/stdalign.h intpack.o: $(hdrdir)/ruby/internal/stdbool.h +intpack.o: $(hdrdir)/ruby/internal/stdckdint.h intpack.o: $(hdrdir)/ruby/internal/symbol.h intpack.o: $(hdrdir)/ruby/internal/value.h intpack.o: $(hdrdir)/ruby/internal/value_type.h @@ -941,6 +946,7 @@ mul.o: $(hdrdir)/ruby/internal/special_consts.h mul.o: $(hdrdir)/ruby/internal/static_assert.h mul.o: $(hdrdir)/ruby/internal/stdalign.h mul.o: $(hdrdir)/ruby/internal/stdbool.h +mul.o: $(hdrdir)/ruby/internal/stdckdint.h mul.o: $(hdrdir)/ruby/internal/symbol.h mul.o: $(hdrdir)/ruby/internal/value.h mul.o: $(hdrdir)/ruby/internal/value_type.h @@ -1100,6 +1106,7 @@ str2big.o: $(hdrdir)/ruby/internal/special_consts.h str2big.o: $(hdrdir)/ruby/internal/static_assert.h str2big.o: $(hdrdir)/ruby/internal/stdalign.h str2big.o: $(hdrdir)/ruby/internal/stdbool.h +str2big.o: $(hdrdir)/ruby/internal/stdckdint.h str2big.o: $(hdrdir)/ruby/internal/symbol.h str2big.o: $(hdrdir)/ruby/internal/value.h str2big.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/bug-14834/depend b/ext/-test-/bug-14834/depend index bf26a571aa..695094fa7a 100644 --- a/ext/-test-/bug-14834/depend +++ b/ext/-test-/bug-14834/depend @@ -147,6 +147,7 @@ bug-14384.o: $(hdrdir)/ruby/internal/special_consts.h bug-14384.o: $(hdrdir)/ruby/internal/static_assert.h bug-14384.o: $(hdrdir)/ruby/internal/stdalign.h bug-14384.o: $(hdrdir)/ruby/internal/stdbool.h +bug-14384.o: $(hdrdir)/ruby/internal/stdckdint.h bug-14384.o: $(hdrdir)/ruby/internal/symbol.h bug-14384.o: $(hdrdir)/ruby/internal/value.h bug-14384.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/bug-3571/depend b/ext/-test-/bug-3571/depend index 9105093b0d..84517a9c15 100644 --- a/ext/-test-/bug-3571/depend +++ b/ext/-test-/bug-3571/depend @@ -147,6 +147,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h bug.o: $(hdrdir)/ruby/internal/static_assert.h bug.o: $(hdrdir)/ruby/internal/stdalign.h bug.o: $(hdrdir)/ruby/internal/stdbool.h +bug.o: $(hdrdir)/ruby/internal/stdckdint.h bug.o: $(hdrdir)/ruby/internal/symbol.h bug.o: $(hdrdir)/ruby/internal/value.h bug.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/bug-5832/depend b/ext/-test-/bug-5832/depend index 9105093b0d..84517a9c15 100644 --- a/ext/-test-/bug-5832/depend +++ b/ext/-test-/bug-5832/depend @@ -147,6 +147,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h bug.o: $(hdrdir)/ruby/internal/static_assert.h bug.o: $(hdrdir)/ruby/internal/stdalign.h bug.o: $(hdrdir)/ruby/internal/stdbool.h +bug.o: $(hdrdir)/ruby/internal/stdckdint.h bug.o: $(hdrdir)/ruby/internal/symbol.h bug.o: $(hdrdir)/ruby/internal/value.h bug.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/bug_reporter/depend b/ext/-test-/bug_reporter/depend index 20882708d1..1c73234247 100644 --- a/ext/-test-/bug_reporter/depend +++ b/ext/-test-/bug_reporter/depend @@ -147,6 +147,7 @@ bug_reporter.o: $(hdrdir)/ruby/internal/special_consts.h bug_reporter.o: $(hdrdir)/ruby/internal/static_assert.h bug_reporter.o: $(hdrdir)/ruby/internal/stdalign.h bug_reporter.o: $(hdrdir)/ruby/internal/stdbool.h +bug_reporter.o: $(hdrdir)/ruby/internal/stdckdint.h bug_reporter.o: $(hdrdir)/ruby/internal/symbol.h bug_reporter.o: $(hdrdir)/ruby/internal/value.h bug_reporter.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/class/depend b/ext/-test-/class/depend index 0a805f815e..b0595fdc46 100644 --- a/ext/-test-/class/depend +++ b/ext/-test-/class/depend @@ -146,6 +146,7 @@ class2name.o: $(hdrdir)/ruby/internal/special_consts.h class2name.o: $(hdrdir)/ruby/internal/static_assert.h class2name.o: $(hdrdir)/ruby/internal/stdalign.h class2name.o: $(hdrdir)/ruby/internal/stdbool.h +class2name.o: $(hdrdir)/ruby/internal/stdckdint.h class2name.o: $(hdrdir)/ruby/internal/symbol.h class2name.o: $(hdrdir)/ruby/internal/value.h class2name.o: $(hdrdir)/ruby/internal/value_type.h @@ -305,6 +306,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/debug/depend b/ext/-test-/debug/depend index 5feeea6d98..67e32c6aa6 100644 --- a/ext/-test-/debug/depend +++ b/ext/-test-/debug/depend @@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ inspector.o: $(hdrdir)/ruby/internal/special_consts.h inspector.o: $(hdrdir)/ruby/internal/static_assert.h inspector.o: $(hdrdir)/ruby/internal/stdalign.h inspector.o: $(hdrdir)/ruby/internal/stdbool.h +inspector.o: $(hdrdir)/ruby/internal/stdckdint.h inspector.o: $(hdrdir)/ruby/internal/symbol.h inspector.o: $(hdrdir)/ruby/internal/value.h inspector.o: $(hdrdir)/ruby/internal/value_type.h @@ -465,6 +467,7 @@ profile_frames.o: $(hdrdir)/ruby/internal/special_consts.h profile_frames.o: $(hdrdir)/ruby/internal/static_assert.h profile_frames.o: $(hdrdir)/ruby/internal/stdalign.h profile_frames.o: $(hdrdir)/ruby/internal/stdbool.h +profile_frames.o: $(hdrdir)/ruby/internal/stdckdint.h profile_frames.o: $(hdrdir)/ruby/internal/symbol.h profile_frames.o: $(hdrdir)/ruby/internal/value.h profile_frames.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/dln/empty/depend b/ext/-test-/dln/empty/depend index a460159087..d3e606df57 100644 --- a/ext/-test-/dln/empty/depend +++ b/ext/-test-/dln/empty/depend @@ -147,6 +147,7 @@ empty.o: $(hdrdir)/ruby/internal/special_consts.h empty.o: $(hdrdir)/ruby/internal/static_assert.h empty.o: $(hdrdir)/ruby/internal/stdalign.h empty.o: $(hdrdir)/ruby/internal/stdbool.h +empty.o: $(hdrdir)/ruby/internal/stdckdint.h empty.o: $(hdrdir)/ruby/internal/symbol.h empty.o: $(hdrdir)/ruby/internal/value.h empty.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/enumerator_kw/depend b/ext/-test-/enumerator_kw/depend index 49ea495421..85daa55b53 100644 --- a/ext/-test-/enumerator_kw/depend +++ b/ext/-test-/enumerator_kw/depend @@ -147,6 +147,7 @@ enumerator_kw.o: $(hdrdir)/ruby/internal/special_consts.h enumerator_kw.o: $(hdrdir)/ruby/internal/static_assert.h enumerator_kw.o: $(hdrdir)/ruby/internal/stdalign.h enumerator_kw.o: $(hdrdir)/ruby/internal/stdbool.h +enumerator_kw.o: $(hdrdir)/ruby/internal/stdckdint.h enumerator_kw.o: $(hdrdir)/ruby/internal/symbol.h enumerator_kw.o: $(hdrdir)/ruby/internal/value.h enumerator_kw.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/exception/depend b/ext/-test-/exception/depend index 818b4c79df..7a0544b1e1 100644 --- a/ext/-test-/exception/depend +++ b/ext/-test-/exception/depend @@ -146,6 +146,7 @@ dataerror.o: $(hdrdir)/ruby/internal/special_consts.h dataerror.o: $(hdrdir)/ruby/internal/static_assert.h dataerror.o: $(hdrdir)/ruby/internal/stdalign.h dataerror.o: $(hdrdir)/ruby/internal/stdbool.h +dataerror.o: $(hdrdir)/ruby/internal/stdckdint.h dataerror.o: $(hdrdir)/ruby/internal/symbol.h dataerror.o: $(hdrdir)/ruby/internal/value.h dataerror.o: $(hdrdir)/ruby/internal/value_type.h @@ -315,6 +316,7 @@ enc_raise.o: $(hdrdir)/ruby/internal/special_consts.h enc_raise.o: $(hdrdir)/ruby/internal/static_assert.h enc_raise.o: $(hdrdir)/ruby/internal/stdalign.h enc_raise.o: $(hdrdir)/ruby/internal/stdbool.h +enc_raise.o: $(hdrdir)/ruby/internal/stdckdint.h enc_raise.o: $(hdrdir)/ruby/internal/symbol.h enc_raise.o: $(hdrdir)/ruby/internal/value.h enc_raise.o: $(hdrdir)/ruby/internal/value_type.h @@ -476,6 +478,7 @@ ensured.o: $(hdrdir)/ruby/internal/special_consts.h ensured.o: $(hdrdir)/ruby/internal/static_assert.h ensured.o: $(hdrdir)/ruby/internal/stdalign.h ensured.o: $(hdrdir)/ruby/internal/stdbool.h +ensured.o: $(hdrdir)/ruby/internal/stdckdint.h ensured.o: $(hdrdir)/ruby/internal/symbol.h ensured.o: $(hdrdir)/ruby/internal/value.h ensured.o: $(hdrdir)/ruby/internal/value_type.h @@ -635,6 +638,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/fatal/depend b/ext/-test-/fatal/depend index 45a8c659c4..36b0ad4205 100644 --- a/ext/-test-/fatal/depend +++ b/ext/-test-/fatal/depend @@ -1,4 +1,5 @@ # AUTOGENERATED DEPENDENCIES START + init.o: $(RUBY_EXTCONF_H) init.o: $(arch_hdrdir)/ruby/config.h init.o: $(hdrdir)/ruby.h @@ -147,6 +148,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +308,7 @@ invalid.o: $(hdrdir)/ruby/internal/special_consts.h invalid.o: $(hdrdir)/ruby/internal/static_assert.h invalid.o: $(hdrdir)/ruby/internal/stdalign.h invalid.o: $(hdrdir)/ruby/internal/stdbool.h +invalid.o: $(hdrdir)/ruby/internal/stdckdint.h invalid.o: $(hdrdir)/ruby/internal/symbol.h invalid.o: $(hdrdir)/ruby/internal/value.h invalid.o: $(hdrdir)/ruby/internal/value_type.h @@ -465,6 +468,7 @@ rb_fatal.o: $(hdrdir)/ruby/internal/special_consts.h rb_fatal.o: $(hdrdir)/ruby/internal/static_assert.h rb_fatal.o: $(hdrdir)/ruby/internal/stdalign.h rb_fatal.o: $(hdrdir)/ruby/internal/stdbool.h +rb_fatal.o: $(hdrdir)/ruby/internal/stdckdint.h rb_fatal.o: $(hdrdir)/ruby/internal/symbol.h rb_fatal.o: $(hdrdir)/ruby/internal/value.h rb_fatal.o: $(hdrdir)/ruby/internal/value_type.h @@ -476,5 +480,4 @@ rb_fatal.o: $(hdrdir)/ruby/ruby.h rb_fatal.o: $(hdrdir)/ruby/st.h rb_fatal.o: $(hdrdir)/ruby/subst.h rb_fatal.o: rb_fatal.c - # AUTOGENERATED DEPENDENCIES END diff --git a/ext/-test-/file/depend b/ext/-test-/file/depend index 2c8a04e433..e985f914b2 100644 --- a/ext/-test-/file/depend +++ b/ext/-test-/file/depend @@ -156,6 +156,7 @@ fs.o: $(hdrdir)/ruby/internal/special_consts.h fs.o: $(hdrdir)/ruby/internal/static_assert.h fs.o: $(hdrdir)/ruby/internal/stdalign.h fs.o: $(hdrdir)/ruby/internal/stdbool.h +fs.o: $(hdrdir)/ruby/internal/stdckdint.h fs.o: $(hdrdir)/ruby/internal/symbol.h fs.o: $(hdrdir)/ruby/internal/value.h fs.o: $(hdrdir)/ruby/internal/value_type.h @@ -318,6 +319,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -486,6 +488,7 @@ newline_conv.o: $(hdrdir)/ruby/internal/special_consts.h newline_conv.o: $(hdrdir)/ruby/internal/static_assert.h newline_conv.o: $(hdrdir)/ruby/internal/stdalign.h newline_conv.o: $(hdrdir)/ruby/internal/stdbool.h +newline_conv.o: $(hdrdir)/ruby/internal/stdckdint.h newline_conv.o: $(hdrdir)/ruby/internal/symbol.h newline_conv.o: $(hdrdir)/ruby/internal/value.h newline_conv.o: $(hdrdir)/ruby/internal/value_type.h @@ -657,6 +660,7 @@ stat.o: $(hdrdir)/ruby/internal/special_consts.h stat.o: $(hdrdir)/ruby/internal/static_assert.h stat.o: $(hdrdir)/ruby/internal/stdalign.h stat.o: $(hdrdir)/ruby/internal/stdbool.h +stat.o: $(hdrdir)/ruby/internal/stdckdint.h stat.o: $(hdrdir)/ruby/internal/symbol.h stat.o: $(hdrdir)/ruby/internal/value.h stat.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/float/depend b/ext/-test-/float/depend index 9580a0416c..3e34818d5f 100644 --- a/ext/-test-/float/depend +++ b/ext/-test-/float/depend @@ -150,6 +150,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -309,6 +310,7 @@ nextafter.o: $(hdrdir)/ruby/internal/special_consts.h nextafter.o: $(hdrdir)/ruby/internal/static_assert.h nextafter.o: $(hdrdir)/ruby/internal/stdalign.h nextafter.o: $(hdrdir)/ruby/internal/stdbool.h +nextafter.o: $(hdrdir)/ruby/internal/stdckdint.h nextafter.o: $(hdrdir)/ruby/internal/symbol.h nextafter.o: $(hdrdir)/ruby/internal/value.h nextafter.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/funcall/depend b/ext/-test-/funcall/depend index 6719e4e676..a5a30873ac 100644 --- a/ext/-test-/funcall/depend +++ b/ext/-test-/funcall/depend @@ -147,6 +147,7 @@ funcall.o: $(hdrdir)/ruby/internal/special_consts.h funcall.o: $(hdrdir)/ruby/internal/static_assert.h funcall.o: $(hdrdir)/ruby/internal/stdalign.h funcall.o: $(hdrdir)/ruby/internal/stdbool.h +funcall.o: $(hdrdir)/ruby/internal/stdckdint.h funcall.o: $(hdrdir)/ruby/internal/symbol.h funcall.o: $(hdrdir)/ruby/internal/value.h funcall.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/gvl/call_without_gvl/depend b/ext/-test-/gvl/call_without_gvl/depend index a4987a65ca..6463d4527d 100644 --- a/ext/-test-/gvl/call_without_gvl/depend +++ b/ext/-test-/gvl/call_without_gvl/depend @@ -146,6 +146,7 @@ call_without_gvl.o: $(hdrdir)/ruby/internal/special_consts.h call_without_gvl.o: $(hdrdir)/ruby/internal/static_assert.h call_without_gvl.o: $(hdrdir)/ruby/internal/stdalign.h call_without_gvl.o: $(hdrdir)/ruby/internal/stdbool.h +call_without_gvl.o: $(hdrdir)/ruby/internal/stdckdint.h call_without_gvl.o: $(hdrdir)/ruby/internal/symbol.h call_without_gvl.o: $(hdrdir)/ruby/internal/value.h call_without_gvl.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/hash/depend b/ext/-test-/hash/depend index 58d9a6247e..4bc4bfcdd6 100644 --- a/ext/-test-/hash/depend +++ b/ext/-test-/hash/depend @@ -147,6 +147,7 @@ delete.o: $(hdrdir)/ruby/internal/special_consts.h delete.o: $(hdrdir)/ruby/internal/static_assert.h delete.o: $(hdrdir)/ruby/internal/stdalign.h delete.o: $(hdrdir)/ruby/internal/stdbool.h +delete.o: $(hdrdir)/ruby/internal/stdckdint.h delete.o: $(hdrdir)/ruby/internal/symbol.h delete.o: $(hdrdir)/ruby/internal/value.h delete.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/integer/depend b/ext/-test-/integer/depend index b68a68ce73..d89965e3d9 100644 --- a/ext/-test-/integer/depend +++ b/ext/-test-/integer/depend @@ -147,6 +147,7 @@ core_ext.o: $(hdrdir)/ruby/internal/special_consts.h core_ext.o: $(hdrdir)/ruby/internal/static_assert.h core_ext.o: $(hdrdir)/ruby/internal/stdalign.h core_ext.o: $(hdrdir)/ruby/internal/stdbool.h +core_ext.o: $(hdrdir)/ruby/internal/stdckdint.h core_ext.o: $(hdrdir)/ruby/internal/symbol.h core_ext.o: $(hdrdir)/ruby/internal/value.h core_ext.o: $(hdrdir)/ruby/internal/value_type.h @@ -314,6 +315,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -473,6 +475,7 @@ my_integer.o: $(hdrdir)/ruby/internal/special_consts.h my_integer.o: $(hdrdir)/ruby/internal/static_assert.h my_integer.o: $(hdrdir)/ruby/internal/stdalign.h my_integer.o: $(hdrdir)/ruby/internal/stdbool.h +my_integer.o: $(hdrdir)/ruby/internal/stdckdint.h my_integer.o: $(hdrdir)/ruby/internal/symbol.h my_integer.o: $(hdrdir)/ruby/internal/value.h my_integer.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/iseq_load/depend b/ext/-test-/iseq_load/depend index 30deb6e039..fd07b3199c 100644 --- a/ext/-test-/iseq_load/depend +++ b/ext/-test-/iseq_load/depend @@ -147,6 +147,7 @@ iseq_load.o: $(hdrdir)/ruby/internal/special_consts.h iseq_load.o: $(hdrdir)/ruby/internal/static_assert.h iseq_load.o: $(hdrdir)/ruby/internal/stdalign.h iseq_load.o: $(hdrdir)/ruby/internal/stdbool.h +iseq_load.o: $(hdrdir)/ruby/internal/stdckdint.h iseq_load.o: $(hdrdir)/ruby/internal/symbol.h iseq_load.o: $(hdrdir)/ruby/internal/value.h iseq_load.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/iter/depend b/ext/-test-/iter/depend index 077a283532..cff4b1bb43 100644 --- a/ext/-test-/iter/depend +++ b/ext/-test-/iter/depend @@ -147,6 +147,7 @@ break.o: $(hdrdir)/ruby/internal/special_consts.h break.o: $(hdrdir)/ruby/internal/static_assert.h break.o: $(hdrdir)/ruby/internal/stdalign.h break.o: $(hdrdir)/ruby/internal/stdbool.h +break.o: $(hdrdir)/ruby/internal/stdckdint.h break.o: $(hdrdir)/ruby/internal/symbol.h break.o: $(hdrdir)/ruby/internal/value.h break.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -465,6 +467,7 @@ yield.o: $(hdrdir)/ruby/internal/special_consts.h yield.o: $(hdrdir)/ruby/internal/static_assert.h yield.o: $(hdrdir)/ruby/internal/stdalign.h yield.o: $(hdrdir)/ruby/internal/stdbool.h +yield.o: $(hdrdir)/ruby/internal/stdckdint.h yield.o: $(hdrdir)/ruby/internal/symbol.h yield.o: $(hdrdir)/ruby/internal/value.h yield.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/load/dot.dot/depend b/ext/-test-/load/dot.dot/depend index be835d3ea5..f9be79e957 100644 --- a/ext/-test-/load/dot.dot/depend +++ b/ext/-test-/load/dot.dot/depend @@ -147,6 +147,7 @@ dot.dot.o: $(hdrdir)/ruby/internal/special_consts.h dot.dot.o: $(hdrdir)/ruby/internal/static_assert.h dot.dot.o: $(hdrdir)/ruby/internal/stdalign.h dot.dot.o: $(hdrdir)/ruby/internal/stdbool.h +dot.dot.o: $(hdrdir)/ruby/internal/stdckdint.h dot.dot.o: $(hdrdir)/ruby/internal/symbol.h dot.dot.o: $(hdrdir)/ruby/internal/value.h dot.dot.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/load/protect/depend b/ext/-test-/load/protect/depend index 57cf029901..324c17237a 100644 --- a/ext/-test-/load/protect/depend +++ b/ext/-test-/load/protect/depend @@ -147,6 +147,7 @@ protect.o: $(hdrdir)/ruby/internal/special_consts.h protect.o: $(hdrdir)/ruby/internal/static_assert.h protect.o: $(hdrdir)/ruby/internal/stdalign.h protect.o: $(hdrdir)/ruby/internal/stdbool.h +protect.o: $(hdrdir)/ruby/internal/stdckdint.h protect.o: $(hdrdir)/ruby/internal/symbol.h protect.o: $(hdrdir)/ruby/internal/value.h protect.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h index 7d471bf360..847dcb7dd3 100644 --- a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h +++ b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h @@ -1,4 +1,4 @@ #include <ruby.h> #include "ruby/internal/dllexport.h" -RUBY_EXTERN VALUE rst_any_method(VALUE); +RUBY_FUNC_EXPORTED VALUE rst_any_method(VALUE); diff --git a/ext/-test-/load/stringify_target/stringify_target.h b/ext/-test-/load/stringify_target/stringify_target.h index 5081f8cbd6..d95fb65d7c 100644 --- a/ext/-test-/load/stringify_target/stringify_target.h +++ b/ext/-test-/load/stringify_target/stringify_target.h @@ -1,4 +1,4 @@ #include <ruby.h> #include "ruby/internal/dllexport.h" -RUBY_EXTERN VALUE stt_any_method(VALUE); +RUBY_FUNC_EXPORTED VALUE stt_any_method(VALUE); diff --git a/ext/-test-/marshal/compat/depend b/ext/-test-/marshal/compat/depend index ff675ccabb..8bcd9f8b5e 100644 --- a/ext/-test-/marshal/compat/depend +++ b/ext/-test-/marshal/compat/depend @@ -147,6 +147,7 @@ usrcompat.o: $(hdrdir)/ruby/internal/special_consts.h usrcompat.o: $(hdrdir)/ruby/internal/static_assert.h usrcompat.o: $(hdrdir)/ruby/internal/stdalign.h usrcompat.o: $(hdrdir)/ruby/internal/stdbool.h +usrcompat.o: $(hdrdir)/ruby/internal/stdckdint.h usrcompat.o: $(hdrdir)/ruby/internal/symbol.h usrcompat.o: $(hdrdir)/ruby/internal/value.h usrcompat.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/marshal/internal_ivar/depend b/ext/-test-/marshal/internal_ivar/depend index 4fe36834d8..f8be031efc 100644 --- a/ext/-test-/marshal/internal_ivar/depend +++ b/ext/-test-/marshal/internal_ivar/depend @@ -147,6 +147,7 @@ internal_ivar.o: $(hdrdir)/ruby/internal/special_consts.h internal_ivar.o: $(hdrdir)/ruby/internal/static_assert.h internal_ivar.o: $(hdrdir)/ruby/internal/stdalign.h internal_ivar.o: $(hdrdir)/ruby/internal/stdbool.h +internal_ivar.o: $(hdrdir)/ruby/internal/stdckdint.h internal_ivar.o: $(hdrdir)/ruby/internal/symbol.h internal_ivar.o: $(hdrdir)/ruby/internal/value.h internal_ivar.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/marshal/usr/depend b/ext/-test-/marshal/usr/depend index 1d56cc8202..09e8207d3a 100644 --- a/ext/-test-/marshal/usr/depend +++ b/ext/-test-/marshal/usr/depend @@ -147,6 +147,7 @@ usrmarshal.o: $(hdrdir)/ruby/internal/special_consts.h usrmarshal.o: $(hdrdir)/ruby/internal/static_assert.h usrmarshal.o: $(hdrdir)/ruby/internal/stdalign.h usrmarshal.o: $(hdrdir)/ruby/internal/stdbool.h +usrmarshal.o: $(hdrdir)/ruby/internal/stdckdint.h usrmarshal.o: $(hdrdir)/ruby/internal/symbol.h usrmarshal.o: $(hdrdir)/ruby/internal/value.h usrmarshal.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/memory_view/depend b/ext/-test-/memory_view/depend index c9ef06a15c..0c92fc1236 100644 --- a/ext/-test-/memory_view/depend +++ b/ext/-test-/memory_view/depend @@ -147,6 +147,7 @@ memory_view.o: $(hdrdir)/ruby/internal/special_consts.h memory_view.o: $(hdrdir)/ruby/internal/static_assert.h memory_view.o: $(hdrdir)/ruby/internal/stdalign.h memory_view.o: $(hdrdir)/ruby/internal/stdbool.h +memory_view.o: $(hdrdir)/ruby/internal/stdckdint.h memory_view.o: $(hdrdir)/ruby/internal/symbol.h memory_view.o: $(hdrdir)/ruby/internal/value.h memory_view.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/method/depend b/ext/-test-/method/depend index dbf513f48f..dce2a815a4 100644 --- a/ext/-test-/method/depend +++ b/ext/-test-/method/depend @@ -147,6 +147,7 @@ arity.o: $(hdrdir)/ruby/internal/special_consts.h arity.o: $(hdrdir)/ruby/internal/static_assert.h arity.o: $(hdrdir)/ruby/internal/stdalign.h arity.o: $(hdrdir)/ruby/internal/stdbool.h +arity.o: $(hdrdir)/ruby/internal/stdckdint.h arity.o: $(hdrdir)/ruby/internal/symbol.h arity.o: $(hdrdir)/ruby/internal/value.h arity.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/notimplement/depend b/ext/-test-/notimplement/depend index 9105093b0d..84517a9c15 100644 --- a/ext/-test-/notimplement/depend +++ b/ext/-test-/notimplement/depend @@ -147,6 +147,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h bug.o: $(hdrdir)/ruby/internal/static_assert.h bug.o: $(hdrdir)/ruby/internal/stdalign.h bug.o: $(hdrdir)/ruby/internal/stdbool.h +bug.o: $(hdrdir)/ruby/internal/stdckdint.h bug.o: $(hdrdir)/ruby/internal/symbol.h bug.o: $(hdrdir)/ruby/internal/value.h bug.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/num2int/depend b/ext/-test-/num2int/depend index 4e05d1e8c1..5550033be7 100644 --- a/ext/-test-/num2int/depend +++ b/ext/-test-/num2int/depend @@ -147,6 +147,7 @@ num2int.o: $(hdrdir)/ruby/internal/special_consts.h num2int.o: $(hdrdir)/ruby/internal/static_assert.h num2int.o: $(hdrdir)/ruby/internal/stdalign.h num2int.o: $(hdrdir)/ruby/internal/stdbool.h +num2int.o: $(hdrdir)/ruby/internal/stdckdint.h num2int.o: $(hdrdir)/ruby/internal/symbol.h num2int.o: $(hdrdir)/ruby/internal/value.h num2int.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/path_to_class/depend b/ext/-test-/path_to_class/depend index 8fe6ee37c2..a1657c9574 100644 --- a/ext/-test-/path_to_class/depend +++ b/ext/-test-/path_to_class/depend @@ -147,6 +147,7 @@ path_to_class.o: $(hdrdir)/ruby/internal/special_consts.h path_to_class.o: $(hdrdir)/ruby/internal/static_assert.h path_to_class.o: $(hdrdir)/ruby/internal/stdalign.h path_to_class.o: $(hdrdir)/ruby/internal/stdbool.h +path_to_class.o: $(hdrdir)/ruby/internal/stdckdint.h path_to_class.o: $(hdrdir)/ruby/internal/symbol.h path_to_class.o: $(hdrdir)/ruby/internal/value.h path_to_class.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/popen_deadlock/depend b/ext/-test-/popen_deadlock/depend index fb58ca30e9..1904e64e59 100644 --- a/ext/-test-/popen_deadlock/depend +++ b/ext/-test-/popen_deadlock/depend @@ -147,6 +147,7 @@ infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/special_consts.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/static_assert.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdalign.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdbool.h +infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdckdint.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/symbol.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/value.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/postponed_job/depend b/ext/-test-/postponed_job/depend index e44d9d51b7..72250896b0 100644 --- a/ext/-test-/postponed_job/depend +++ b/ext/-test-/postponed_job/depend @@ -148,6 +148,7 @@ postponed_job.o: $(hdrdir)/ruby/internal/special_consts.h postponed_job.o: $(hdrdir)/ruby/internal/static_assert.h postponed_job.o: $(hdrdir)/ruby/internal/stdalign.h postponed_job.o: $(hdrdir)/ruby/internal/stdbool.h +postponed_job.o: $(hdrdir)/ruby/internal/stdckdint.h postponed_job.o: $(hdrdir)/ruby/internal/symbol.h postponed_job.o: $(hdrdir)/ruby/internal/value.h postponed_job.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/printf/depend b/ext/-test-/printf/depend index b397041103..0530df78bf 100644 --- a/ext/-test-/printf/depend +++ b/ext/-test-/printf/depend @@ -157,6 +157,7 @@ printf.o: $(hdrdir)/ruby/internal/special_consts.h printf.o: $(hdrdir)/ruby/internal/static_assert.h printf.o: $(hdrdir)/ruby/internal/stdalign.h printf.o: $(hdrdir)/ruby/internal/stdbool.h +printf.o: $(hdrdir)/ruby/internal/stdckdint.h printf.o: $(hdrdir)/ruby/internal/symbol.h printf.o: $(hdrdir)/ruby/internal/value.h printf.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/proc/depend b/ext/-test-/proc/depend index 7e78aa6f83..45e12bcd09 100644 --- a/ext/-test-/proc/depend +++ b/ext/-test-/proc/depend @@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ receiver.o: $(hdrdir)/ruby/internal/special_consts.h receiver.o: $(hdrdir)/ruby/internal/static_assert.h receiver.o: $(hdrdir)/ruby/internal/stdalign.h receiver.o: $(hdrdir)/ruby/internal/stdbool.h +receiver.o: $(hdrdir)/ruby/internal/stdckdint.h receiver.o: $(hdrdir)/ruby/internal/symbol.h receiver.o: $(hdrdir)/ruby/internal/value.h receiver.o: $(hdrdir)/ruby/internal/value_type.h @@ -465,6 +467,7 @@ super.o: $(hdrdir)/ruby/internal/special_consts.h super.o: $(hdrdir)/ruby/internal/static_assert.h super.o: $(hdrdir)/ruby/internal/stdalign.h super.o: $(hdrdir)/ruby/internal/stdbool.h +super.o: $(hdrdir)/ruby/internal/stdckdint.h super.o: $(hdrdir)/ruby/internal/symbol.h super.o: $(hdrdir)/ruby/internal/value.h super.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/random/depend b/ext/-test-/random/depend index 3f9a52be44..71f5f6e1e6 100644 --- a/ext/-test-/random/depend +++ b/ext/-test-/random/depend @@ -146,6 +146,7 @@ bad_version.o: $(hdrdir)/ruby/internal/special_consts.h bad_version.o: $(hdrdir)/ruby/internal/static_assert.h bad_version.o: $(hdrdir)/ruby/internal/stdalign.h bad_version.o: $(hdrdir)/ruby/internal/stdbool.h +bad_version.o: $(hdrdir)/ruby/internal/stdckdint.h bad_version.o: $(hdrdir)/ruby/internal/symbol.h bad_version.o: $(hdrdir)/ruby/internal/value.h bad_version.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -464,6 +466,7 @@ loop.o: $(hdrdir)/ruby/internal/special_consts.h loop.o: $(hdrdir)/ruby/internal/static_assert.h loop.o: $(hdrdir)/ruby/internal/stdalign.h loop.o: $(hdrdir)/ruby/internal/stdbool.h +loop.o: $(hdrdir)/ruby/internal/stdckdint.h loop.o: $(hdrdir)/ruby/internal/symbol.h loop.o: $(hdrdir)/ruby/internal/value.h loop.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/rational/depend b/ext/-test-/rational/depend index cff2eae38d..363d779302 100644 --- a/ext/-test-/rational/depend +++ b/ext/-test-/rational/depend @@ -151,6 +151,7 @@ rat.o: $(hdrdir)/ruby/internal/special_consts.h rat.o: $(hdrdir)/ruby/internal/static_assert.h rat.o: $(hdrdir)/ruby/internal/stdalign.h rat.o: $(hdrdir)/ruby/internal/stdbool.h +rat.o: $(hdrdir)/ruby/internal/stdckdint.h rat.o: $(hdrdir)/ruby/internal/symbol.h rat.o: $(hdrdir)/ruby/internal/value.h rat.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/rb_call_super_kw/depend b/ext/-test-/rb_call_super_kw/depend index a42ddc85ac..04a0fac12c 100644 --- a/ext/-test-/rb_call_super_kw/depend +++ b/ext/-test-/rb_call_super_kw/depend @@ -147,6 +147,7 @@ rb_call_super_kw.o: $(hdrdir)/ruby/internal/special_consts.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/static_assert.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdalign.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdbool.h +rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdckdint.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/symbol.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/value.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/recursion/depend b/ext/-test-/recursion/depend index 49377250ef..2a65c98b09 100644 --- a/ext/-test-/recursion/depend +++ b/ext/-test-/recursion/depend @@ -147,6 +147,7 @@ recursion.o: $(hdrdir)/ruby/internal/special_consts.h recursion.o: $(hdrdir)/ruby/internal/static_assert.h recursion.o: $(hdrdir)/ruby/internal/stdalign.h recursion.o: $(hdrdir)/ruby/internal/stdbool.h +recursion.o: $(hdrdir)/ruby/internal/stdckdint.h recursion.o: $(hdrdir)/ruby/internal/symbol.h recursion.o: $(hdrdir)/ruby/internal/value.h recursion.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/regexp/depend b/ext/-test-/regexp/depend index 484f0320dd..0127a66a2e 100644 --- a/ext/-test-/regexp/depend +++ b/ext/-test-/regexp/depend @@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ parse_depth_limit.o: $(hdrdir)/ruby/internal/special_consts.h parse_depth_limit.o: $(hdrdir)/ruby/internal/static_assert.h parse_depth_limit.o: $(hdrdir)/ruby/internal/stdalign.h parse_depth_limit.o: $(hdrdir)/ruby/internal/stdbool.h +parse_depth_limit.o: $(hdrdir)/ruby/internal/stdckdint.h parse_depth_limit.o: $(hdrdir)/ruby/internal/symbol.h parse_depth_limit.o: $(hdrdir)/ruby/internal/value.h parse_depth_limit.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/scan_args/depend b/ext/-test-/scan_args/depend index 3bedc6a7cf..922e5bbd5c 100644 --- a/ext/-test-/scan_args/depend +++ b/ext/-test-/scan_args/depend @@ -147,6 +147,7 @@ scan_args.o: $(hdrdir)/ruby/internal/special_consts.h scan_args.o: $(hdrdir)/ruby/internal/static_assert.h scan_args.o: $(hdrdir)/ruby/internal/stdalign.h scan_args.o: $(hdrdir)/ruby/internal/stdbool.h +scan_args.o: $(hdrdir)/ruby/internal/stdckdint.h scan_args.o: $(hdrdir)/ruby/internal/symbol.h scan_args.o: $(hdrdir)/ruby/internal/value.h scan_args.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/st/foreach/depend b/ext/-test-/st/foreach/depend index fdfe356805..36273f8df8 100644 --- a/ext/-test-/st/foreach/depend +++ b/ext/-test-/st/foreach/depend @@ -147,6 +147,7 @@ foreach.o: $(hdrdir)/ruby/internal/special_consts.h foreach.o: $(hdrdir)/ruby/internal/static_assert.h foreach.o: $(hdrdir)/ruby/internal/stdalign.h foreach.o: $(hdrdir)/ruby/internal/stdbool.h +foreach.o: $(hdrdir)/ruby/internal/stdckdint.h foreach.o: $(hdrdir)/ruby/internal/symbol.h foreach.o: $(hdrdir)/ruby/internal/value.h foreach.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/st/numhash/depend b/ext/-test-/st/numhash/depend index ef28c892f3..a0916183b6 100644 --- a/ext/-test-/st/numhash/depend +++ b/ext/-test-/st/numhash/depend @@ -147,6 +147,7 @@ numhash.o: $(hdrdir)/ruby/internal/special_consts.h numhash.o: $(hdrdir)/ruby/internal/static_assert.h numhash.o: $(hdrdir)/ruby/internal/stdalign.h numhash.o: $(hdrdir)/ruby/internal/stdbool.h +numhash.o: $(hdrdir)/ruby/internal/stdckdint.h numhash.o: $(hdrdir)/ruby/internal/symbol.h numhash.o: $(hdrdir)/ruby/internal/value.h numhash.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/st/update/depend b/ext/-test-/st/update/depend index 2d5ff224a2..96ba194df0 100644 --- a/ext/-test-/st/update/depend +++ b/ext/-test-/st/update/depend @@ -147,6 +147,7 @@ update.o: $(hdrdir)/ruby/internal/special_consts.h update.o: $(hdrdir)/ruby/internal/static_assert.h update.o: $(hdrdir)/ruby/internal/stdalign.h update.o: $(hdrdir)/ruby/internal/stdbool.h +update.o: $(hdrdir)/ruby/internal/stdckdint.h update.o: $(hdrdir)/ruby/internal/symbol.h update.o: $(hdrdir)/ruby/internal/value.h update.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/string/depend b/ext/-test-/string/depend index f8f58e7d44..044b6109ff 100644 --- a/ext/-test-/string/depend +++ b/ext/-test-/string/depend @@ -158,6 +158,7 @@ capacity.o: $(hdrdir)/ruby/internal/special_consts.h capacity.o: $(hdrdir)/ruby/internal/static_assert.h capacity.o: $(hdrdir)/ruby/internal/stdalign.h capacity.o: $(hdrdir)/ruby/internal/stdbool.h +capacity.o: $(hdrdir)/ruby/internal/stdckdint.h capacity.o: $(hdrdir)/ruby/internal/symbol.h capacity.o: $(hdrdir)/ruby/internal/value.h capacity.o: $(hdrdir)/ruby/internal/value_type.h @@ -321,6 +322,7 @@ chilled.o: $(hdrdir)/ruby/internal/special_consts.h chilled.o: $(hdrdir)/ruby/internal/static_assert.h chilled.o: $(hdrdir)/ruby/internal/stdalign.h chilled.o: $(hdrdir)/ruby/internal/stdbool.h +chilled.o: $(hdrdir)/ruby/internal/stdckdint.h chilled.o: $(hdrdir)/ruby/internal/symbol.h chilled.o: $(hdrdir)/ruby/internal/value.h chilled.o: $(hdrdir)/ruby/internal/value_type.h @@ -489,6 +491,7 @@ coderange.o: $(hdrdir)/ruby/internal/special_consts.h coderange.o: $(hdrdir)/ruby/internal/static_assert.h coderange.o: $(hdrdir)/ruby/internal/stdalign.h coderange.o: $(hdrdir)/ruby/internal/stdbool.h +coderange.o: $(hdrdir)/ruby/internal/stdckdint.h coderange.o: $(hdrdir)/ruby/internal/symbol.h coderange.o: $(hdrdir)/ruby/internal/value.h coderange.o: $(hdrdir)/ruby/internal/value_type.h @@ -660,6 +663,7 @@ cstr.o: $(hdrdir)/ruby/internal/special_consts.h cstr.o: $(hdrdir)/ruby/internal/static_assert.h cstr.o: $(hdrdir)/ruby/internal/stdalign.h cstr.o: $(hdrdir)/ruby/internal/stdbool.h +cstr.o: $(hdrdir)/ruby/internal/stdckdint.h cstr.o: $(hdrdir)/ruby/internal/symbol.h cstr.o: $(hdrdir)/ruby/internal/value.h cstr.o: $(hdrdir)/ruby/internal/value_type.h @@ -824,6 +828,7 @@ ellipsize.o: $(hdrdir)/ruby/internal/special_consts.h ellipsize.o: $(hdrdir)/ruby/internal/static_assert.h ellipsize.o: $(hdrdir)/ruby/internal/stdalign.h ellipsize.o: $(hdrdir)/ruby/internal/stdbool.h +ellipsize.o: $(hdrdir)/ruby/internal/stdckdint.h ellipsize.o: $(hdrdir)/ruby/internal/symbol.h ellipsize.o: $(hdrdir)/ruby/internal/value.h ellipsize.o: $(hdrdir)/ruby/internal/value_type.h @@ -993,6 +998,7 @@ enc_associate.o: $(hdrdir)/ruby/internal/special_consts.h enc_associate.o: $(hdrdir)/ruby/internal/static_assert.h enc_associate.o: $(hdrdir)/ruby/internal/stdalign.h enc_associate.o: $(hdrdir)/ruby/internal/stdbool.h +enc_associate.o: $(hdrdir)/ruby/internal/stdckdint.h enc_associate.o: $(hdrdir)/ruby/internal/symbol.h enc_associate.o: $(hdrdir)/ruby/internal/value.h enc_associate.o: $(hdrdir)/ruby/internal/value_type.h @@ -1164,6 +1170,7 @@ enc_dummy.o: $(hdrdir)/ruby/internal/special_consts.h enc_dummy.o: $(hdrdir)/ruby/internal/static_assert.h enc_dummy.o: $(hdrdir)/ruby/internal/stdalign.h enc_dummy.o: $(hdrdir)/ruby/internal/stdbool.h +enc_dummy.o: $(hdrdir)/ruby/internal/stdckdint.h enc_dummy.o: $(hdrdir)/ruby/internal/symbol.h enc_dummy.o: $(hdrdir)/ruby/internal/value.h enc_dummy.o: $(hdrdir)/ruby/internal/value_type.h @@ -1334,6 +1341,7 @@ enc_str_buf_cat.o: $(hdrdir)/ruby/internal/special_consts.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/static_assert.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdalign.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdbool.h +enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdckdint.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/symbol.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/value.h enc_str_buf_cat.o: $(hdrdir)/ruby/internal/value_type.h @@ -1506,6 +1514,7 @@ fstring.o: $(hdrdir)/ruby/internal/special_consts.h fstring.o: $(hdrdir)/ruby/internal/static_assert.h fstring.o: $(hdrdir)/ruby/internal/stdalign.h fstring.o: $(hdrdir)/ruby/internal/stdbool.h +fstring.o: $(hdrdir)/ruby/internal/stdckdint.h fstring.o: $(hdrdir)/ruby/internal/symbol.h fstring.o: $(hdrdir)/ruby/internal/value.h fstring.o: $(hdrdir)/ruby/internal/value_type.h @@ -1669,6 +1678,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -1828,6 +1838,7 @@ modify.o: $(hdrdir)/ruby/internal/special_consts.h modify.o: $(hdrdir)/ruby/internal/static_assert.h modify.o: $(hdrdir)/ruby/internal/stdalign.h modify.o: $(hdrdir)/ruby/internal/stdbool.h +modify.o: $(hdrdir)/ruby/internal/stdckdint.h modify.o: $(hdrdir)/ruby/internal/symbol.h modify.o: $(hdrdir)/ruby/internal/value.h modify.o: $(hdrdir)/ruby/internal/value_type.h @@ -1997,6 +2008,7 @@ new.o: $(hdrdir)/ruby/internal/special_consts.h new.o: $(hdrdir)/ruby/internal/static_assert.h new.o: $(hdrdir)/ruby/internal/stdalign.h new.o: $(hdrdir)/ruby/internal/stdbool.h +new.o: $(hdrdir)/ruby/internal/stdckdint.h new.o: $(hdrdir)/ruby/internal/symbol.h new.o: $(hdrdir)/ruby/internal/value.h new.o: $(hdrdir)/ruby/internal/value_type.h @@ -2158,6 +2170,7 @@ nofree.o: $(hdrdir)/ruby/internal/special_consts.h nofree.o: $(hdrdir)/ruby/internal/static_assert.h nofree.o: $(hdrdir)/ruby/internal/stdalign.h nofree.o: $(hdrdir)/ruby/internal/stdbool.h +nofree.o: $(hdrdir)/ruby/internal/stdckdint.h nofree.o: $(hdrdir)/ruby/internal/symbol.h nofree.o: $(hdrdir)/ruby/internal/value.h nofree.o: $(hdrdir)/ruby/internal/value_type.h @@ -2326,6 +2339,7 @@ normalize.o: $(hdrdir)/ruby/internal/special_consts.h normalize.o: $(hdrdir)/ruby/internal/static_assert.h normalize.o: $(hdrdir)/ruby/internal/stdalign.h normalize.o: $(hdrdir)/ruby/internal/stdbool.h +normalize.o: $(hdrdir)/ruby/internal/stdckdint.h normalize.o: $(hdrdir)/ruby/internal/symbol.h normalize.o: $(hdrdir)/ruby/internal/value.h normalize.o: $(hdrdir)/ruby/internal/value_type.h @@ -2498,6 +2512,7 @@ qsort.o: $(hdrdir)/ruby/internal/special_consts.h qsort.o: $(hdrdir)/ruby/internal/static_assert.h qsort.o: $(hdrdir)/ruby/internal/stdalign.h qsort.o: $(hdrdir)/ruby/internal/stdbool.h +qsort.o: $(hdrdir)/ruby/internal/stdckdint.h qsort.o: $(hdrdir)/ruby/internal/symbol.h qsort.o: $(hdrdir)/ruby/internal/value.h qsort.o: $(hdrdir)/ruby/internal/value_type.h @@ -2660,6 +2675,7 @@ rb_interned_str.o: $(hdrdir)/ruby/internal/special_consts.h rb_interned_str.o: $(hdrdir)/ruby/internal/static_assert.h rb_interned_str.o: $(hdrdir)/ruby/internal/stdalign.h rb_interned_str.o: $(hdrdir)/ruby/internal/stdbool.h +rb_interned_str.o: $(hdrdir)/ruby/internal/stdckdint.h rb_interned_str.o: $(hdrdir)/ruby/internal/symbol.h rb_interned_str.o: $(hdrdir)/ruby/internal/value.h rb_interned_str.o: $(hdrdir)/ruby/internal/value_type.h @@ -2819,6 +2835,7 @@ rb_str_dup.o: $(hdrdir)/ruby/internal/special_consts.h rb_str_dup.o: $(hdrdir)/ruby/internal/static_assert.h rb_str_dup.o: $(hdrdir)/ruby/internal/stdalign.h rb_str_dup.o: $(hdrdir)/ruby/internal/stdbool.h +rb_str_dup.o: $(hdrdir)/ruby/internal/stdckdint.h rb_str_dup.o: $(hdrdir)/ruby/internal/symbol.h rb_str_dup.o: $(hdrdir)/ruby/internal/value.h rb_str_dup.o: $(hdrdir)/ruby/internal/value_type.h @@ -2978,6 +2995,7 @@ set_len.o: $(hdrdir)/ruby/internal/special_consts.h set_len.o: $(hdrdir)/ruby/internal/static_assert.h set_len.o: $(hdrdir)/ruby/internal/stdalign.h set_len.o: $(hdrdir)/ruby/internal/stdbool.h +set_len.o: $(hdrdir)/ruby/internal/stdckdint.h set_len.o: $(hdrdir)/ruby/internal/symbol.h set_len.o: $(hdrdir)/ruby/internal/value.h set_len.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/struct/depend b/ext/-test-/struct/depend index 5db943e286..951dddd5dd 100644 --- a/ext/-test-/struct/depend +++ b/ext/-test-/struct/depend @@ -147,6 +147,7 @@ data.o: $(hdrdir)/ruby/internal/special_consts.h data.o: $(hdrdir)/ruby/internal/static_assert.h data.o: $(hdrdir)/ruby/internal/stdalign.h data.o: $(hdrdir)/ruby/internal/stdbool.h +data.o: $(hdrdir)/ruby/internal/stdckdint.h data.o: $(hdrdir)/ruby/internal/symbol.h data.o: $(hdrdir)/ruby/internal/value.h data.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ duplicate.o: $(hdrdir)/ruby/internal/special_consts.h duplicate.o: $(hdrdir)/ruby/internal/static_assert.h duplicate.o: $(hdrdir)/ruby/internal/stdalign.h duplicate.o: $(hdrdir)/ruby/internal/stdbool.h +duplicate.o: $(hdrdir)/ruby/internal/stdckdint.h duplicate.o: $(hdrdir)/ruby/internal/symbol.h duplicate.o: $(hdrdir)/ruby/internal/value.h duplicate.o: $(hdrdir)/ruby/internal/value_type.h @@ -465,6 +467,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -624,6 +627,7 @@ len.o: $(hdrdir)/ruby/internal/special_consts.h len.o: $(hdrdir)/ruby/internal/static_assert.h len.o: $(hdrdir)/ruby/internal/stdalign.h len.o: $(hdrdir)/ruby/internal/stdbool.h +len.o: $(hdrdir)/ruby/internal/stdckdint.h len.o: $(hdrdir)/ruby/internal/symbol.h len.o: $(hdrdir)/ruby/internal/value.h len.o: $(hdrdir)/ruby/internal/value_type.h @@ -783,6 +787,7 @@ member.o: $(hdrdir)/ruby/internal/special_consts.h member.o: $(hdrdir)/ruby/internal/static_assert.h member.o: $(hdrdir)/ruby/internal/stdalign.h member.o: $(hdrdir)/ruby/internal/stdbool.h +member.o: $(hdrdir)/ruby/internal/stdckdint.h member.o: $(hdrdir)/ruby/internal/symbol.h member.o: $(hdrdir)/ruby/internal/value.h member.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/symbol/depend b/ext/-test-/symbol/depend index dd1b5c305f..7c76596fdf 100644 --- a/ext/-test-/symbol/depend +++ b/ext/-test-/symbol/depend @@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ type.o: $(hdrdir)/ruby/internal/special_consts.h type.o: $(hdrdir)/ruby/internal/static_assert.h type.o: $(hdrdir)/ruby/internal/stdalign.h type.o: $(hdrdir)/ruby/internal/stdbool.h +type.o: $(hdrdir)/ruby/internal/stdckdint.h type.o: $(hdrdir)/ruby/internal/symbol.h type.o: $(hdrdir)/ruby/internal/value.h type.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/thread/instrumentation/depend b/ext/-test-/thread/instrumentation/depend index b03f51870f..a37e4d5675 100644 --- a/ext/-test-/thread/instrumentation/depend +++ b/ext/-test-/thread/instrumentation/depend @@ -147,6 +147,7 @@ instrumentation.o: $(hdrdir)/ruby/internal/special_consts.h instrumentation.o: $(hdrdir)/ruby/internal/static_assert.h instrumentation.o: $(hdrdir)/ruby/internal/stdalign.h instrumentation.o: $(hdrdir)/ruby/internal/stdbool.h +instrumentation.o: $(hdrdir)/ruby/internal/stdckdint.h instrumentation.o: $(hdrdir)/ruby/internal/symbol.h instrumentation.o: $(hdrdir)/ruby/internal/value.h instrumentation.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/thread_fd/depend b/ext/-test-/thread_fd/depend index d4cc772526..0fda9f6dbf 100644 --- a/ext/-test-/thread_fd/depend +++ b/ext/-test-/thread_fd/depend @@ -146,6 +146,7 @@ thread_fd.o: $(hdrdir)/ruby/internal/special_consts.h thread_fd.o: $(hdrdir)/ruby/internal/static_assert.h thread_fd.o: $(hdrdir)/ruby/internal/stdalign.h thread_fd.o: $(hdrdir)/ruby/internal/stdbool.h +thread_fd.o: $(hdrdir)/ruby/internal/stdckdint.h thread_fd.o: $(hdrdir)/ruby/internal/symbol.h thread_fd.o: $(hdrdir)/ruby/internal/value.h thread_fd.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/time/depend b/ext/-test-/time/depend index c015588b09..5ed791bcc5 100644 --- a/ext/-test-/time/depend +++ b/ext/-test-/time/depend @@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -307,6 +308,7 @@ leap_second.o: $(hdrdir)/ruby/internal/special_consts.h leap_second.o: $(hdrdir)/ruby/internal/static_assert.h leap_second.o: $(hdrdir)/ruby/internal/stdalign.h leap_second.o: $(hdrdir)/ruby/internal/stdbool.h +leap_second.o: $(hdrdir)/ruby/internal/stdckdint.h leap_second.o: $(hdrdir)/ruby/internal/symbol.h leap_second.o: $(hdrdir)/ruby/internal/value.h leap_second.o: $(hdrdir)/ruby/internal/value_type.h @@ -470,6 +472,7 @@ new.o: $(hdrdir)/ruby/internal/special_consts.h new.o: $(hdrdir)/ruby/internal/static_assert.h new.o: $(hdrdir)/ruby/internal/stdalign.h new.o: $(hdrdir)/ruby/internal/stdbool.h +new.o: $(hdrdir)/ruby/internal/stdckdint.h new.o: $(hdrdir)/ruby/internal/symbol.h new.o: $(hdrdir)/ruby/internal/value.h new.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/tracepoint/depend b/ext/-test-/tracepoint/depend index 396004926e..133663b3bd 100644 --- a/ext/-test-/tracepoint/depend +++ b/ext/-test-/tracepoint/depend @@ -147,6 +147,7 @@ gc_hook.o: $(hdrdir)/ruby/internal/special_consts.h gc_hook.o: $(hdrdir)/ruby/internal/static_assert.h gc_hook.o: $(hdrdir)/ruby/internal/stdalign.h gc_hook.o: $(hdrdir)/ruby/internal/stdbool.h +gc_hook.o: $(hdrdir)/ruby/internal/stdckdint.h gc_hook.o: $(hdrdir)/ruby/internal/symbol.h gc_hook.o: $(hdrdir)/ruby/internal/value.h gc_hook.o: $(hdrdir)/ruby/internal/value_type.h @@ -306,6 +307,7 @@ tracepoint.o: $(hdrdir)/ruby/internal/special_consts.h tracepoint.o: $(hdrdir)/ruby/internal/static_assert.h tracepoint.o: $(hdrdir)/ruby/internal/stdalign.h tracepoint.o: $(hdrdir)/ruby/internal/stdbool.h +tracepoint.o: $(hdrdir)/ruby/internal/stdckdint.h tracepoint.o: $(hdrdir)/ruby/internal/symbol.h tracepoint.o: $(hdrdir)/ruby/internal/value.h tracepoint.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/typeddata/depend b/ext/-test-/typeddata/depend index cbeafa8000..6c0847c82d 100644 --- a/ext/-test-/typeddata/depend +++ b/ext/-test-/typeddata/depend @@ -147,6 +147,7 @@ typeddata.o: $(hdrdir)/ruby/internal/special_consts.h typeddata.o: $(hdrdir)/ruby/internal/static_assert.h typeddata.o: $(hdrdir)/ruby/internal/stdalign.h typeddata.o: $(hdrdir)/ruby/internal/stdbool.h +typeddata.o: $(hdrdir)/ruby/internal/stdckdint.h typeddata.o: $(hdrdir)/ruby/internal/symbol.h typeddata.o: $(hdrdir)/ruby/internal/value.h typeddata.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/vm/depend b/ext/-test-/vm/depend index f0b3f3b1f4..422b40a32d 100644 --- a/ext/-test-/vm/depend +++ b/ext/-test-/vm/depend @@ -146,6 +146,7 @@ at_exit.o: $(hdrdir)/ruby/internal/special_consts.h at_exit.o: $(hdrdir)/ruby/internal/static_assert.h at_exit.o: $(hdrdir)/ruby/internal/stdalign.h at_exit.o: $(hdrdir)/ruby/internal/stdbool.h +at_exit.o: $(hdrdir)/ruby/internal/stdckdint.h at_exit.o: $(hdrdir)/ruby/internal/symbol.h at_exit.o: $(hdrdir)/ruby/internal/value.h at_exit.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/-test-/wait/depend b/ext/-test-/wait/depend index 2e4887559c..7997a16709 100644 --- a/ext/-test-/wait/depend +++ b/ext/-test-/wait/depend @@ -156,6 +156,7 @@ wait.o: $(hdrdir)/ruby/internal/special_consts.h wait.o: $(hdrdir)/ruby/internal/static_assert.h wait.o: $(hdrdir)/ruby/internal/stdalign.h wait.o: $(hdrdir)/ruby/internal/stdbool.h +wait.o: $(hdrdir)/ruby/internal/stdckdint.h wait.o: $(hdrdir)/ruby/internal/symbol.h wait.o: $(hdrdir)/ruby/internal/value.h wait.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/cgi/escape/depend b/ext/cgi/escape/depend index 37304a24f4..746b47246a 100644 --- a/ext/cgi/escape/depend +++ b/ext/cgi/escape/depend @@ -157,6 +157,7 @@ escape.o: $(hdrdir)/ruby/internal/special_consts.h escape.o: $(hdrdir)/ruby/internal/static_assert.h escape.o: $(hdrdir)/ruby/internal/stdalign.h escape.o: $(hdrdir)/ruby/internal/stdbool.h +escape.o: $(hdrdir)/ruby/internal/stdckdint.h escape.o: $(hdrdir)/ruby/internal/symbol.h escape.o: $(hdrdir)/ruby/internal/value.h escape.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/continuation/depend b/ext/continuation/depend index f0333d7fe6..b40e52e29b 100644 --- a/ext/continuation/depend +++ b/ext/continuation/depend @@ -146,6 +146,7 @@ continuation.o: $(hdrdir)/ruby/internal/special_consts.h continuation.o: $(hdrdir)/ruby/internal/static_assert.h continuation.o: $(hdrdir)/ruby/internal/stdalign.h continuation.o: $(hdrdir)/ruby/internal/stdbool.h +continuation.o: $(hdrdir)/ruby/internal/stdckdint.h continuation.o: $(hdrdir)/ruby/internal/symbol.h continuation.o: $(hdrdir)/ruby/internal/value.h continuation.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/coverage/depend b/ext/coverage/depend index 0a6c61d5c6..1be81c5e9a 100644 --- a/ext/coverage/depend +++ b/ext/coverage/depend @@ -159,6 +159,7 @@ coverage.o: $(hdrdir)/ruby/internal/special_consts.h coverage.o: $(hdrdir)/ruby/internal/static_assert.h coverage.o: $(hdrdir)/ruby/internal/stdalign.h coverage.o: $(hdrdir)/ruby/internal/stdbool.h +coverage.o: $(hdrdir)/ruby/internal/stdckdint.h coverage.o: $(hdrdir)/ruby/internal/symbol.h coverage.o: $(hdrdir)/ruby/internal/value.h coverage.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/date/depend b/ext/date/depend index 82f85f7bf3..d07f10a593 100644 --- a/ext/date/depend +++ b/ext/date/depend @@ -157,6 +157,7 @@ date_core.o: $(hdrdir)/ruby/internal/special_consts.h date_core.o: $(hdrdir)/ruby/internal/static_assert.h date_core.o: $(hdrdir)/ruby/internal/stdalign.h date_core.o: $(hdrdir)/ruby/internal/stdbool.h +date_core.o: $(hdrdir)/ruby/internal/stdckdint.h date_core.o: $(hdrdir)/ruby/internal/symbol.h date_core.o: $(hdrdir)/ruby/internal/value.h date_core.o: $(hdrdir)/ruby/internal/value_type.h @@ -331,6 +332,7 @@ date_parse.o: $(hdrdir)/ruby/internal/special_consts.h date_parse.o: $(hdrdir)/ruby/internal/static_assert.h date_parse.o: $(hdrdir)/ruby/internal/stdalign.h date_parse.o: $(hdrdir)/ruby/internal/stdbool.h +date_parse.o: $(hdrdir)/ruby/internal/stdckdint.h date_parse.o: $(hdrdir)/ruby/internal/symbol.h date_parse.o: $(hdrdir)/ruby/internal/value.h date_parse.o: $(hdrdir)/ruby/internal/value_type.h @@ -495,6 +497,7 @@ date_strftime.o: $(hdrdir)/ruby/internal/special_consts.h date_strftime.o: $(hdrdir)/ruby/internal/static_assert.h date_strftime.o: $(hdrdir)/ruby/internal/stdalign.h date_strftime.o: $(hdrdir)/ruby/internal/stdbool.h +date_strftime.o: $(hdrdir)/ruby/internal/stdckdint.h date_strftime.o: $(hdrdir)/ruby/internal/symbol.h date_strftime.o: $(hdrdir)/ruby/internal/value.h date_strftime.o: $(hdrdir)/ruby/internal/value_type.h @@ -666,6 +669,7 @@ date_strptime.o: $(hdrdir)/ruby/internal/special_consts.h date_strptime.o: $(hdrdir)/ruby/internal/static_assert.h date_strptime.o: $(hdrdir)/ruby/internal/stdalign.h date_strptime.o: $(hdrdir)/ruby/internal/stdbool.h +date_strptime.o: $(hdrdir)/ruby/internal/stdckdint.h date_strptime.o: $(hdrdir)/ruby/internal/symbol.h date_strptime.o: $(hdrdir)/ruby/internal/value.h date_strptime.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/bubblebabble/depend b/ext/digest/bubblebabble/depend index 6f0003a66d..0da9c223ee 100644 --- a/ext/digest/bubblebabble/depend +++ b/ext/digest/bubblebabble/depend @@ -147,6 +147,7 @@ bubblebabble.o: $(hdrdir)/ruby/internal/special_consts.h bubblebabble.o: $(hdrdir)/ruby/internal/static_assert.h bubblebabble.o: $(hdrdir)/ruby/internal/stdalign.h bubblebabble.o: $(hdrdir)/ruby/internal/stdbool.h +bubblebabble.o: $(hdrdir)/ruby/internal/stdckdint.h bubblebabble.o: $(hdrdir)/ruby/internal/symbol.h bubblebabble.o: $(hdrdir)/ruby/internal/value.h bubblebabble.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/depend b/ext/digest/depend index 6df940a679..cb9e8d4813 100644 --- a/ext/digest/depend +++ b/ext/digest/depend @@ -147,6 +147,7 @@ digest.o: $(hdrdir)/ruby/internal/special_consts.h digest.o: $(hdrdir)/ruby/internal/static_assert.h digest.o: $(hdrdir)/ruby/internal/stdalign.h digest.o: $(hdrdir)/ruby/internal/stdbool.h +digest.o: $(hdrdir)/ruby/internal/stdckdint.h digest.o: $(hdrdir)/ruby/internal/symbol.h digest.o: $(hdrdir)/ruby/internal/value.h digest.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/md5/depend b/ext/digest/md5/depend index da1b345999..e71915e5b4 100644 --- a/ext/digest/md5/depend +++ b/ext/digest/md5/depend @@ -150,6 +150,7 @@ md5.o: $(hdrdir)/ruby/internal/special_consts.h md5.o: $(hdrdir)/ruby/internal/static_assert.h md5.o: $(hdrdir)/ruby/internal/stdalign.h md5.o: $(hdrdir)/ruby/internal/stdbool.h +md5.o: $(hdrdir)/ruby/internal/stdckdint.h md5.o: $(hdrdir)/ruby/internal/symbol.h md5.o: $(hdrdir)/ruby/internal/value.h md5.o: $(hdrdir)/ruby/internal/value_type.h @@ -311,6 +312,7 @@ md5init.o: $(hdrdir)/ruby/internal/special_consts.h md5init.o: $(hdrdir)/ruby/internal/static_assert.h md5init.o: $(hdrdir)/ruby/internal/stdalign.h md5init.o: $(hdrdir)/ruby/internal/stdbool.h +md5init.o: $(hdrdir)/ruby/internal/stdckdint.h md5init.o: $(hdrdir)/ruby/internal/symbol.h md5init.o: $(hdrdir)/ruby/internal/value.h md5init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/rmd160/depend b/ext/digest/rmd160/depend index abfa08b023..09558ad92b 100644 --- a/ext/digest/rmd160/depend +++ b/ext/digest/rmd160/depend @@ -150,6 +150,7 @@ rmd160.o: $(hdrdir)/ruby/internal/special_consts.h rmd160.o: $(hdrdir)/ruby/internal/static_assert.h rmd160.o: $(hdrdir)/ruby/internal/stdalign.h rmd160.o: $(hdrdir)/ruby/internal/stdbool.h +rmd160.o: $(hdrdir)/ruby/internal/stdckdint.h rmd160.o: $(hdrdir)/ruby/internal/symbol.h rmd160.o: $(hdrdir)/ruby/internal/value.h rmd160.o: $(hdrdir)/ruby/internal/value_type.h @@ -311,6 +312,7 @@ rmd160init.o: $(hdrdir)/ruby/internal/special_consts.h rmd160init.o: $(hdrdir)/ruby/internal/static_assert.h rmd160init.o: $(hdrdir)/ruby/internal/stdalign.h rmd160init.o: $(hdrdir)/ruby/internal/stdbool.h +rmd160init.o: $(hdrdir)/ruby/internal/stdckdint.h rmd160init.o: $(hdrdir)/ruby/internal/symbol.h rmd160init.o: $(hdrdir)/ruby/internal/value.h rmd160init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/sha1/depend b/ext/digest/sha1/depend index d17338e92b..827b8a0852 100644 --- a/ext/digest/sha1/depend +++ b/ext/digest/sha1/depend @@ -150,6 +150,7 @@ sha1.o: $(hdrdir)/ruby/internal/special_consts.h sha1.o: $(hdrdir)/ruby/internal/static_assert.h sha1.o: $(hdrdir)/ruby/internal/stdalign.h sha1.o: $(hdrdir)/ruby/internal/stdbool.h +sha1.o: $(hdrdir)/ruby/internal/stdckdint.h sha1.o: $(hdrdir)/ruby/internal/symbol.h sha1.o: $(hdrdir)/ruby/internal/value.h sha1.o: $(hdrdir)/ruby/internal/value_type.h @@ -311,6 +312,7 @@ sha1init.o: $(hdrdir)/ruby/internal/special_consts.h sha1init.o: $(hdrdir)/ruby/internal/static_assert.h sha1init.o: $(hdrdir)/ruby/internal/stdalign.h sha1init.o: $(hdrdir)/ruby/internal/stdbool.h +sha1init.o: $(hdrdir)/ruby/internal/stdckdint.h sha1init.o: $(hdrdir)/ruby/internal/symbol.h sha1init.o: $(hdrdir)/ruby/internal/value.h sha1init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/digest/sha2/depend b/ext/digest/sha2/depend index 7b88b6411f..af1600d346 100644 --- a/ext/digest/sha2/depend +++ b/ext/digest/sha2/depend @@ -150,6 +150,7 @@ sha2.o: $(hdrdir)/ruby/internal/special_consts.h sha2.o: $(hdrdir)/ruby/internal/static_assert.h sha2.o: $(hdrdir)/ruby/internal/stdalign.h sha2.o: $(hdrdir)/ruby/internal/stdbool.h +sha2.o: $(hdrdir)/ruby/internal/stdckdint.h sha2.o: $(hdrdir)/ruby/internal/symbol.h sha2.o: $(hdrdir)/ruby/internal/value.h sha2.o: $(hdrdir)/ruby/internal/value_type.h @@ -311,6 +312,7 @@ sha2init.o: $(hdrdir)/ruby/internal/special_consts.h sha2init.o: $(hdrdir)/ruby/internal/static_assert.h sha2init.o: $(hdrdir)/ruby/internal/stdalign.h sha2init.o: $(hdrdir)/ruby/internal/stdbool.h +sha2init.o: $(hdrdir)/ruby/internal/stdckdint.h sha2init.o: $(hdrdir)/ruby/internal/symbol.h sha2init.o: $(hdrdir)/ruby/internal/value.h sha2init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/etc/depend b/ext/etc/depend index 00787b6aaf..675699b129 100644 --- a/ext/etc/depend +++ b/ext/etc/depend @@ -162,6 +162,7 @@ etc.o: $(hdrdir)/ruby/internal/special_consts.h etc.o: $(hdrdir)/ruby/internal/static_assert.h etc.o: $(hdrdir)/ruby/internal/stdalign.h etc.o: $(hdrdir)/ruby/internal/stdbool.h +etc.o: $(hdrdir)/ruby/internal/stdckdint.h etc.o: $(hdrdir)/ruby/internal/symbol.h etc.o: $(hdrdir)/ruby/internal/value.h etc.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/extmk.rb b/ext/extmk.rb index 6c7c7aeab7..2f76e174d5 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -773,7 +773,6 @@ begin end submakeopts << 'EXTLDFLAGS="$(EXTLDFLAGS)"' submakeopts << 'EXTINITS="$(EXTINITS)"' - submakeopts << 'UPDATE_LIBRARIES="$(UPDATE_LIBRARIES)"' submakeopts << 'SHOWFLAGS=' mf.macro "SUBMAKEOPTS", submakeopts mf.macro "NOTE_MESG", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb skip] diff --git a/ext/fcntl/depend b/ext/fcntl/depend index 9ce9fa30ef..5ed652563b 100644 --- a/ext/fcntl/depend +++ b/ext/fcntl/depend @@ -147,6 +147,7 @@ fcntl.o: $(hdrdir)/ruby/internal/special_consts.h fcntl.o: $(hdrdir)/ruby/internal/static_assert.h fcntl.o: $(hdrdir)/ruby/internal/stdalign.h fcntl.o: $(hdrdir)/ruby/internal/stdbool.h +fcntl.o: $(hdrdir)/ruby/internal/stdckdint.h fcntl.o: $(hdrdir)/ruby/internal/symbol.h fcntl.o: $(hdrdir)/ruby/internal/value.h fcntl.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/fiddle/depend b/ext/fiddle/depend index 561785275e..43b11ca780 100644 --- a/ext/fiddle/depend +++ b/ext/fiddle/depend @@ -200,6 +200,7 @@ closure.o: $(hdrdir)/ruby/internal/special_consts.h closure.o: $(hdrdir)/ruby/internal/static_assert.h closure.o: $(hdrdir)/ruby/internal/stdalign.h closure.o: $(hdrdir)/ruby/internal/stdbool.h +closure.o: $(hdrdir)/ruby/internal/stdckdint.h closure.o: $(hdrdir)/ruby/internal/symbol.h closure.o: $(hdrdir)/ruby/internal/value.h closure.o: $(hdrdir)/ruby/internal/value_type.h @@ -364,6 +365,7 @@ conversions.o: $(hdrdir)/ruby/internal/special_consts.h conversions.o: $(hdrdir)/ruby/internal/static_assert.h conversions.o: $(hdrdir)/ruby/internal/stdalign.h conversions.o: $(hdrdir)/ruby/internal/stdbool.h +conversions.o: $(hdrdir)/ruby/internal/stdckdint.h conversions.o: $(hdrdir)/ruby/internal/symbol.h conversions.o: $(hdrdir)/ruby/internal/value.h conversions.o: $(hdrdir)/ruby/internal/value_type.h @@ -527,6 +529,7 @@ fiddle.o: $(hdrdir)/ruby/internal/special_consts.h fiddle.o: $(hdrdir)/ruby/internal/static_assert.h fiddle.o: $(hdrdir)/ruby/internal/stdalign.h fiddle.o: $(hdrdir)/ruby/internal/stdbool.h +fiddle.o: $(hdrdir)/ruby/internal/stdckdint.h fiddle.o: $(hdrdir)/ruby/internal/symbol.h fiddle.o: $(hdrdir)/ruby/internal/value.h fiddle.o: $(hdrdir)/ruby/internal/value_type.h @@ -690,6 +693,7 @@ function.o: $(hdrdir)/ruby/internal/special_consts.h function.o: $(hdrdir)/ruby/internal/static_assert.h function.o: $(hdrdir)/ruby/internal/stdalign.h function.o: $(hdrdir)/ruby/internal/stdbool.h +function.o: $(hdrdir)/ruby/internal/stdckdint.h function.o: $(hdrdir)/ruby/internal/symbol.h function.o: $(hdrdir)/ruby/internal/value.h function.o: $(hdrdir)/ruby/internal/value_type.h @@ -854,6 +858,7 @@ handle.o: $(hdrdir)/ruby/internal/special_consts.h handle.o: $(hdrdir)/ruby/internal/static_assert.h handle.o: $(hdrdir)/ruby/internal/stdalign.h handle.o: $(hdrdir)/ruby/internal/stdbool.h +handle.o: $(hdrdir)/ruby/internal/stdckdint.h handle.o: $(hdrdir)/ruby/internal/symbol.h handle.o: $(hdrdir)/ruby/internal/value.h handle.o: $(hdrdir)/ruby/internal/value_type.h @@ -1027,6 +1032,7 @@ memory_view.o: $(hdrdir)/ruby/internal/special_consts.h memory_view.o: $(hdrdir)/ruby/internal/static_assert.h memory_view.o: $(hdrdir)/ruby/internal/stdalign.h memory_view.o: $(hdrdir)/ruby/internal/stdbool.h +memory_view.o: $(hdrdir)/ruby/internal/stdckdint.h memory_view.o: $(hdrdir)/ruby/internal/symbol.h memory_view.o: $(hdrdir)/ruby/internal/value.h memory_view.o: $(hdrdir)/ruby/internal/value_type.h @@ -1193,6 +1199,7 @@ pinned.o: $(hdrdir)/ruby/internal/special_consts.h pinned.o: $(hdrdir)/ruby/internal/static_assert.h pinned.o: $(hdrdir)/ruby/internal/stdalign.h pinned.o: $(hdrdir)/ruby/internal/stdbool.h +pinned.o: $(hdrdir)/ruby/internal/stdckdint.h pinned.o: $(hdrdir)/ruby/internal/symbol.h pinned.o: $(hdrdir)/ruby/internal/value.h pinned.o: $(hdrdir)/ruby/internal/value_type.h @@ -1366,6 +1373,7 @@ pointer.o: $(hdrdir)/ruby/internal/special_consts.h pointer.o: $(hdrdir)/ruby/internal/static_assert.h pointer.o: $(hdrdir)/ruby/internal/stdalign.h pointer.o: $(hdrdir)/ruby/internal/stdbool.h +pointer.o: $(hdrdir)/ruby/internal/stdckdint.h pointer.o: $(hdrdir)/ruby/internal/symbol.h pointer.o: $(hdrdir)/ruby/internal/value.h pointer.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/io/console/depend b/ext/io/console/depend index 59ca3442c2..5b91413f38 100644 --- a/ext/io/console/depend +++ b/ext/io/console/depend @@ -158,6 +158,7 @@ console.o: $(hdrdir)/ruby/internal/special_consts.h console.o: $(hdrdir)/ruby/internal/static_assert.h console.o: $(hdrdir)/ruby/internal/stdalign.h console.o: $(hdrdir)/ruby/internal/stdbool.h +console.o: $(hdrdir)/ruby/internal/stdckdint.h console.o: $(hdrdir)/ruby/internal/symbol.h console.o: $(hdrdir)/ruby/internal/value.h console.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/io/nonblock/depend b/ext/io/nonblock/depend index 48384fca62..20a96f1252 100644 --- a/ext/io/nonblock/depend +++ b/ext/io/nonblock/depend @@ -157,6 +157,7 @@ nonblock.o: $(hdrdir)/ruby/internal/special_consts.h nonblock.o: $(hdrdir)/ruby/internal/static_assert.h nonblock.o: $(hdrdir)/ruby/internal/stdalign.h nonblock.o: $(hdrdir)/ruby/internal/stdbool.h +nonblock.o: $(hdrdir)/ruby/internal/stdckdint.h nonblock.o: $(hdrdir)/ruby/internal/symbol.h nonblock.o: $(hdrdir)/ruby/internal/value.h nonblock.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/io/wait/depend b/ext/io/wait/depend index 83cf8f94c8..70317b1497 100644 --- a/ext/io/wait/depend +++ b/ext/io/wait/depend @@ -158,6 +158,7 @@ wait.o: $(hdrdir)/ruby/internal/special_consts.h wait.o: $(hdrdir)/ruby/internal/static_assert.h wait.o: $(hdrdir)/ruby/internal/stdalign.h wait.o: $(hdrdir)/ruby/internal/stdbool.h +wait.o: $(hdrdir)/ruby/internal/stdckdint.h wait.o: $(hdrdir)/ruby/internal/symbol.h wait.o: $(hdrdir)/ruby/internal/value.h wait.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/json/generator/depend b/ext/json/generator/depend index 42752d1a35..f47e5f3a70 100644 --- a/ext/json/generator/depend +++ b/ext/json/generator/depend @@ -161,6 +161,7 @@ generator.o: $(hdrdir)/ruby/internal/special_consts.h generator.o: $(hdrdir)/ruby/internal/static_assert.h generator.o: $(hdrdir)/ruby/internal/stdalign.h generator.o: $(hdrdir)/ruby/internal/stdbool.h +generator.o: $(hdrdir)/ruby/internal/stdckdint.h generator.o: $(hdrdir)/ruby/internal/symbol.h generator.o: $(hdrdir)/ruby/internal/value.h generator.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/json/parser/depend b/ext/json/parser/depend index cb6e547d29..f3422b4f84 100644 --- a/ext/json/parser/depend +++ b/ext/json/parser/depend @@ -160,6 +160,7 @@ parser.o: $(hdrdir)/ruby/internal/special_consts.h parser.o: $(hdrdir)/ruby/internal/static_assert.h parser.o: $(hdrdir)/ruby/internal/stdalign.h parser.o: $(hdrdir)/ruby/internal/stdbool.h +parser.o: $(hdrdir)/ruby/internal/stdckdint.h parser.o: $(hdrdir)/ruby/internal/symbol.h parser.o: $(hdrdir)/ruby/internal/value.h parser.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/monitor/depend b/ext/monitor/depend index 38e21b3f66..bc7ead0d32 100644 --- a/ext/monitor/depend +++ b/ext/monitor/depend @@ -146,6 +146,7 @@ monitor.o: $(hdrdir)/ruby/internal/special_consts.h monitor.o: $(hdrdir)/ruby/internal/static_assert.h monitor.o: $(hdrdir)/ruby/internal/stdalign.h monitor.o: $(hdrdir)/ruby/internal/stdbool.h +monitor.o: $(hdrdir)/ruby/internal/stdckdint.h monitor.o: $(hdrdir)/ruby/internal/symbol.h monitor.o: $(hdrdir)/ruby/internal/value.h monitor.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/objspace/depend b/ext/objspace/depend index aa0c5c5d7d..a02168b06a 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -159,6 +159,7 @@ object_tracing.o: $(hdrdir)/ruby/internal/special_consts.h object_tracing.o: $(hdrdir)/ruby/internal/static_assert.h object_tracing.o: $(hdrdir)/ruby/internal/stdalign.h object_tracing.o: $(hdrdir)/ruby/internal/stdbool.h +object_tracing.o: $(hdrdir)/ruby/internal/stdckdint.h object_tracing.o: $(hdrdir)/ruby/internal/symbol.h object_tracing.o: $(hdrdir)/ruby/internal/value.h object_tracing.o: $(hdrdir)/ruby/internal/value_type.h @@ -358,6 +359,7 @@ objspace.o: $(hdrdir)/ruby/internal/special_consts.h objspace.o: $(hdrdir)/ruby/internal/static_assert.h objspace.o: $(hdrdir)/ruby/internal/stdalign.h objspace.o: $(hdrdir)/ruby/internal/stdbool.h +objspace.o: $(hdrdir)/ruby/internal/stdckdint.h objspace.o: $(hdrdir)/ruby/internal/symbol.h objspace.o: $(hdrdir)/ruby/internal/value.h objspace.o: $(hdrdir)/ruby/internal/value_type.h @@ -569,6 +571,7 @@ objspace_dump.o: $(hdrdir)/ruby/internal/special_consts.h objspace_dump.o: $(hdrdir)/ruby/internal/static_assert.h objspace_dump.o: $(hdrdir)/ruby/internal/stdalign.h objspace_dump.o: $(hdrdir)/ruby/internal/stdbool.h +objspace_dump.o: $(hdrdir)/ruby/internal/stdckdint.h objspace_dump.o: $(hdrdir)/ruby/internal/symbol.h objspace_dump.o: $(hdrdir)/ruby/internal/value.h objspace_dump.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/openssl/depend b/ext/openssl/depend index 0d03c85b80..12c6793939 100644 --- a/ext/openssl/depend +++ b/ext/openssl/depend @@ -161,6 +161,7 @@ ossl.o: $(hdrdir)/ruby/internal/special_consts.h ossl.o: $(hdrdir)/ruby/internal/static_assert.h ossl.o: $(hdrdir)/ruby/internal/stdalign.h ossl.o: $(hdrdir)/ruby/internal/stdbool.h +ossl.o: $(hdrdir)/ruby/internal/stdckdint.h ossl.o: $(hdrdir)/ruby/internal/symbol.h ossl.o: $(hdrdir)/ruby/internal/value.h ossl.o: $(hdrdir)/ruby/internal/value_type.h @@ -355,6 +356,7 @@ ossl_asn1.o: $(hdrdir)/ruby/internal/special_consts.h ossl_asn1.o: $(hdrdir)/ruby/internal/static_assert.h ossl_asn1.o: $(hdrdir)/ruby/internal/stdalign.h ossl_asn1.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_asn1.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_asn1.o: $(hdrdir)/ruby/internal/symbol.h ossl_asn1.o: $(hdrdir)/ruby/internal/value.h ossl_asn1.o: $(hdrdir)/ruby/internal/value_type.h @@ -549,6 +551,7 @@ ossl_bio.o: $(hdrdir)/ruby/internal/special_consts.h ossl_bio.o: $(hdrdir)/ruby/internal/static_assert.h ossl_bio.o: $(hdrdir)/ruby/internal/stdalign.h ossl_bio.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_bio.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_bio.o: $(hdrdir)/ruby/internal/symbol.h ossl_bio.o: $(hdrdir)/ruby/internal/value.h ossl_bio.o: $(hdrdir)/ruby/internal/value_type.h @@ -743,6 +746,7 @@ ossl_bn.o: $(hdrdir)/ruby/internal/special_consts.h ossl_bn.o: $(hdrdir)/ruby/internal/static_assert.h ossl_bn.o: $(hdrdir)/ruby/internal/stdalign.h ossl_bn.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_bn.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_bn.o: $(hdrdir)/ruby/internal/symbol.h ossl_bn.o: $(hdrdir)/ruby/internal/value.h ossl_bn.o: $(hdrdir)/ruby/internal/value_type.h @@ -938,6 +942,7 @@ ossl_cipher.o: $(hdrdir)/ruby/internal/special_consts.h ossl_cipher.o: $(hdrdir)/ruby/internal/static_assert.h ossl_cipher.o: $(hdrdir)/ruby/internal/stdalign.h ossl_cipher.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_cipher.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_cipher.o: $(hdrdir)/ruby/internal/symbol.h ossl_cipher.o: $(hdrdir)/ruby/internal/value.h ossl_cipher.o: $(hdrdir)/ruby/internal/value_type.h @@ -1132,6 +1137,7 @@ ossl_config.o: $(hdrdir)/ruby/internal/special_consts.h ossl_config.o: $(hdrdir)/ruby/internal/static_assert.h ossl_config.o: $(hdrdir)/ruby/internal/stdalign.h ossl_config.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_config.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_config.o: $(hdrdir)/ruby/internal/symbol.h ossl_config.o: $(hdrdir)/ruby/internal/value.h ossl_config.o: $(hdrdir)/ruby/internal/value_type.h @@ -1326,6 +1332,7 @@ ossl_digest.o: $(hdrdir)/ruby/internal/special_consts.h ossl_digest.o: $(hdrdir)/ruby/internal/static_assert.h ossl_digest.o: $(hdrdir)/ruby/internal/stdalign.h ossl_digest.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_digest.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_digest.o: $(hdrdir)/ruby/internal/symbol.h ossl_digest.o: $(hdrdir)/ruby/internal/value.h ossl_digest.o: $(hdrdir)/ruby/internal/value_type.h @@ -1520,6 +1527,7 @@ ossl_engine.o: $(hdrdir)/ruby/internal/special_consts.h ossl_engine.o: $(hdrdir)/ruby/internal/static_assert.h ossl_engine.o: $(hdrdir)/ruby/internal/stdalign.h ossl_engine.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_engine.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_engine.o: $(hdrdir)/ruby/internal/symbol.h ossl_engine.o: $(hdrdir)/ruby/internal/value.h ossl_engine.o: $(hdrdir)/ruby/internal/value_type.h @@ -1714,6 +1722,7 @@ ossl_hmac.o: $(hdrdir)/ruby/internal/special_consts.h ossl_hmac.o: $(hdrdir)/ruby/internal/static_assert.h ossl_hmac.o: $(hdrdir)/ruby/internal/stdalign.h ossl_hmac.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_hmac.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_hmac.o: $(hdrdir)/ruby/internal/symbol.h ossl_hmac.o: $(hdrdir)/ruby/internal/value.h ossl_hmac.o: $(hdrdir)/ruby/internal/value_type.h @@ -1908,6 +1917,7 @@ ossl_kdf.o: $(hdrdir)/ruby/internal/special_consts.h ossl_kdf.o: $(hdrdir)/ruby/internal/static_assert.h ossl_kdf.o: $(hdrdir)/ruby/internal/stdalign.h ossl_kdf.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_kdf.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_kdf.o: $(hdrdir)/ruby/internal/symbol.h ossl_kdf.o: $(hdrdir)/ruby/internal/value.h ossl_kdf.o: $(hdrdir)/ruby/internal/value_type.h @@ -2102,6 +2112,7 @@ ossl_ns_spki.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/symbol.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/value.h ossl_ns_spki.o: $(hdrdir)/ruby/internal/value_type.h @@ -2296,6 +2307,7 @@ ossl_ocsp.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ocsp.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ocsp.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ocsp.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ocsp.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ocsp.o: $(hdrdir)/ruby/internal/symbol.h ossl_ocsp.o: $(hdrdir)/ruby/internal/value.h ossl_ocsp.o: $(hdrdir)/ruby/internal/value_type.h @@ -2490,6 +2502,7 @@ ossl_pkcs12.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/value.h ossl_pkcs12.o: $(hdrdir)/ruby/internal/value_type.h @@ -2684,6 +2697,7 @@ ossl_pkcs7.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/value.h ossl_pkcs7.o: $(hdrdir)/ruby/internal/value_type.h @@ -2878,6 +2892,7 @@ ossl_pkey.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey.o: $(hdrdir)/ruby/internal/value.h ossl_pkey.o: $(hdrdir)/ruby/internal/value_type.h @@ -3072,6 +3087,7 @@ ossl_pkey_dh.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/value.h ossl_pkey_dh.o: $(hdrdir)/ruby/internal/value_type.h @@ -3266,6 +3282,7 @@ ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/value.h ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/value_type.h @@ -3460,6 +3477,7 @@ ossl_pkey_ec.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/value.h ossl_pkey_ec.o: $(hdrdir)/ruby/internal/value_type.h @@ -3654,6 +3672,7 @@ ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/special_consts.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/static_assert.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdalign.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/symbol.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/value.h ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/value_type.h @@ -3848,6 +3867,7 @@ ossl_provider.o: $(hdrdir)/ruby/internal/special_consts.h ossl_provider.o: $(hdrdir)/ruby/internal/static_assert.h ossl_provider.o: $(hdrdir)/ruby/internal/stdalign.h ossl_provider.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_provider.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_provider.o: $(hdrdir)/ruby/internal/symbol.h ossl_provider.o: $(hdrdir)/ruby/internal/value.h ossl_provider.o: $(hdrdir)/ruby/internal/value_type.h @@ -4042,6 +4062,7 @@ ossl_rand.o: $(hdrdir)/ruby/internal/special_consts.h ossl_rand.o: $(hdrdir)/ruby/internal/static_assert.h ossl_rand.o: $(hdrdir)/ruby/internal/stdalign.h ossl_rand.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_rand.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_rand.o: $(hdrdir)/ruby/internal/symbol.h ossl_rand.o: $(hdrdir)/ruby/internal/value.h ossl_rand.o: $(hdrdir)/ruby/internal/value_type.h @@ -4236,6 +4257,7 @@ ossl_ssl.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ssl.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ssl.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ssl.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ssl.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ssl.o: $(hdrdir)/ruby/internal/symbol.h ossl_ssl.o: $(hdrdir)/ruby/internal/value.h ossl_ssl.o: $(hdrdir)/ruby/internal/value_type.h @@ -4430,6 +4452,7 @@ ossl_ssl_session.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/symbol.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/value.h ossl_ssl_session.o: $(hdrdir)/ruby/internal/value_type.h @@ -4624,6 +4647,7 @@ ossl_ts.o: $(hdrdir)/ruby/internal/special_consts.h ossl_ts.o: $(hdrdir)/ruby/internal/static_assert.h ossl_ts.o: $(hdrdir)/ruby/internal/stdalign.h ossl_ts.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_ts.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_ts.o: $(hdrdir)/ruby/internal/symbol.h ossl_ts.o: $(hdrdir)/ruby/internal/value.h ossl_ts.o: $(hdrdir)/ruby/internal/value_type.h @@ -4818,6 +4842,7 @@ ossl_x509.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509.o: $(hdrdir)/ruby/internal/value.h ossl_x509.o: $(hdrdir)/ruby/internal/value_type.h @@ -5012,6 +5037,7 @@ ossl_x509attr.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509attr.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509attr.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509attr.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509attr.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509attr.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509attr.o: $(hdrdir)/ruby/internal/value.h ossl_x509attr.o: $(hdrdir)/ruby/internal/value_type.h @@ -5206,6 +5232,7 @@ ossl_x509cert.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509cert.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509cert.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509cert.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509cert.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509cert.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509cert.o: $(hdrdir)/ruby/internal/value.h ossl_x509cert.o: $(hdrdir)/ruby/internal/value_type.h @@ -5400,6 +5427,7 @@ ossl_x509crl.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509crl.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509crl.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509crl.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509crl.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509crl.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509crl.o: $(hdrdir)/ruby/internal/value.h ossl_x509crl.o: $(hdrdir)/ruby/internal/value_type.h @@ -5594,6 +5622,7 @@ ossl_x509ext.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509ext.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509ext.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509ext.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509ext.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509ext.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509ext.o: $(hdrdir)/ruby/internal/value.h ossl_x509ext.o: $(hdrdir)/ruby/internal/value_type.h @@ -5788,6 +5817,7 @@ ossl_x509name.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509name.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509name.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509name.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509name.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509name.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509name.o: $(hdrdir)/ruby/internal/value.h ossl_x509name.o: $(hdrdir)/ruby/internal/value_type.h @@ -5982,6 +6012,7 @@ ossl_x509req.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509req.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509req.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509req.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509req.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509req.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509req.o: $(hdrdir)/ruby/internal/value.h ossl_x509req.o: $(hdrdir)/ruby/internal/value_type.h @@ -6176,6 +6207,7 @@ ossl_x509revoked.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/value.h ossl_x509revoked.o: $(hdrdir)/ruby/internal/value_type.h @@ -6370,6 +6402,7 @@ ossl_x509store.o: $(hdrdir)/ruby/internal/special_consts.h ossl_x509store.o: $(hdrdir)/ruby/internal/static_assert.h ossl_x509store.o: $(hdrdir)/ruby/internal/stdalign.h ossl_x509store.o: $(hdrdir)/ruby/internal/stdbool.h +ossl_x509store.o: $(hdrdir)/ruby/internal/stdckdint.h ossl_x509store.o: $(hdrdir)/ruby/internal/symbol.h ossl_x509store.o: $(hdrdir)/ruby/internal/value.h ossl_x509store.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb index 68aa7bc970..216c51e3be 100644 --- a/ext/openssl/lib/openssl/buffering.rb +++ b/ext/openssl/lib/openssl/buffering.rb @@ -349,13 +349,18 @@ module OpenSSL::Buffering @wbuffer << s @wbuffer.force_encoding(Encoding::BINARY) @sync ||= false - if @sync or @wbuffer.size > BLOCK_SIZE - until @wbuffer.empty? - begin - nwrote = syswrite(@wbuffer) - rescue Errno::EAGAIN - retry + buffer_size = @wbuffer.size + if @sync or buffer_size > BLOCK_SIZE + nwrote = 0 + begin + while nwrote < buffer_size do + begin + nwrote += syswrite(@wbuffer[nwrote, buffer_size - nwrote]) + rescue Errno::EAGAIN + retry + end end + ensure @wbuffer[0, nwrote] = "" end end diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb index 75a74a3f51..d28bf1a374 100644 --- a/ext/openssl/lib/openssl/ssl.rb +++ b/ext/openssl/lib/openssl/ssl.rb @@ -459,6 +459,32 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== nil end + # Close the stream for reading. + # This method is ignored by OpenSSL as there is no reasonable way to + # implement it, but exists for compatibility with IO. + def close_read + # Unsupported and ignored. + # Just don't read any more. + end + + # Closes the stream for writing. The behavior of this method depends on + # the version of OpenSSL and the TLS protocol in use. + # + # - Sends a 'close_notify' alert to the peer. + # - Does not wait for the peer's 'close_notify' alert in response. + # + # In TLS 1.2 and earlier: + # - On receipt of a 'close_notify' alert, responds with a 'close_notify' + # alert of its own and close down the connection immediately, + # discarding any pending writes. + # + # Therefore, on TLS 1.2, this method will cause the connection to be + # completely shut down. On TLS 1.3, the connection will remain open for + # reading only. + def close_write + stop + end + private def using_anon_cipher? diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 71c452c88a..0533342077 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -1163,9 +1163,12 @@ ossl_asn1prim_to_der(VALUE self) rb_jump_tag(state); } p0 = p1 = (unsigned char *)RSTRING_PTR(str); - i2d_ASN1_TYPE(asn1, &p0); + if (i2d_ASN1_TYPE(asn1, &p0) < 0) { + ASN1_TYPE_free(asn1); + ossl_raise(eASN1Error, "i2d_ASN1_TYPE"); + } ASN1_TYPE_free(asn1); - assert(p0 - p1 == alllen); + ossl_str_adjust(str, p0); /* Strip header since to_der_internal() wants only the payload */ j = ASN1_get_object((const unsigned char **)&p1, &bodylen, &tag, &tc, alllen); diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 110610e1f9..6f74c925c0 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -386,11 +386,23 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) in = (unsigned char *)RSTRING_PTR(data); in_len = RSTRING_LEN(data); GetCipher(self, ctx); - out_len = in_len+EVP_CIPHER_CTX_block_size(ctx); - if (out_len <= 0) { + + /* + * As of OpenSSL 3.2, there is no reliable way to determine the required + * output buffer size for arbitrary cipher modes. + * https://github.com/openssl/openssl/issues/22628 + * + * in_len+block_size is usually sufficient, but AES key wrap with padding + * ciphers require in_len+15 even though they have a block size of 8 bytes. + * + * Using EVP_MAX_BLOCK_LENGTH (32) as a safe upper bound for ciphers + * currently implemented in OpenSSL, but this can change in the future. + */ + if (in_len > LONG_MAX - EVP_MAX_BLOCK_LENGTH) { ossl_raise(rb_eRangeError, "data too big to make output buffer: %ld bytes", in_len); } + out_len = in_len + EVP_MAX_BLOCK_LENGTH; if (NIL_P(str)) { str = rb_str_new(0, out_len); @@ -401,7 +413,7 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len)) ossl_raise(eCipherError, NULL); - assert(out_len < RSTRING_LEN(str)); + assert(out_len <= RSTRING_LEN(str)); rb_str_set_len(str, out_len); return str; @@ -442,8 +454,8 @@ ossl_cipher_final(VALUE self) * call-seq: * cipher.name -> string * - * Returns the name of the cipher which may differ slightly from the original - * name provided. + * Returns the short name of the cipher which may differ slightly from the + * original name provided. */ static VALUE ossl_cipher_name(VALUE self) diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index 16aeeb8106..1ae26a2355 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -103,7 +103,8 @@ VALUE ossl_digest_update(VALUE, VALUE); * Digest.new(string [, data]) -> Digest * * Creates a Digest instance based on _string_, which is either the ln - * (long name) or sn (short name) of a supported digest algorithm. + * (long name) or sn (short name) of a supported digest algorithm. A list of + * supported algorithms can be obtained by calling OpenSSL::Digest.digests. * * If _data_ (a String) is given, it is used as the initial input to the * Digest instance, i.e. @@ -162,6 +163,32 @@ ossl_digest_copy(VALUE self, VALUE other) return self; } +static void +add_digest_name_to_ary(const OBJ_NAME *name, void *arg) +{ + VALUE ary = (VALUE)arg; + rb_ary_push(ary, rb_str_new2(name->name)); +} + +/* + * call-seq: + * OpenSSL::Digest.digests -> array[string...] + * + * Returns the names of all available digests in an array. + */ +static VALUE +ossl_s_digests(VALUE self) +{ + VALUE ary; + + ary = rb_ary_new(); + OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH, + add_digest_name_to_ary, + (void*)ary); + + return ary; +} + /* * call-seq: * digest.reset -> self @@ -245,7 +272,8 @@ ossl_digest_finish(int argc, VALUE *argv, VALUE self) * call-seq: * digest.name -> string * - * Returns the sn of this Digest algorithm. + * Returns the short name of this Digest algorithm which may differ slightly + * from the original name provided. * * === Example * digest = OpenSSL::Digest.new('SHA512') @@ -412,6 +440,7 @@ Init_ossl_digest(void) rb_define_alloc_func(cDigest, ossl_digest_alloc); + rb_define_module_function(cDigest, "digests", ossl_s_digests, 0); rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1); rb_define_method(cDigest, "initialize_copy", ossl_digest_copy, 1); rb_define_method(cDigest, "reset", ossl_digest_reset, 0); diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 78dcbd667a..446df4c075 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -165,7 +165,11 @@ ossl_pkcs7_s_read_smime(VALUE klass, VALUE arg) out = NULL; pkcs7 = SMIME_read_PKCS7(in, &out); BIO_free(in); - if(!pkcs7) ossl_raise(ePKCS7Error, NULL); + if (!pkcs7) + ossl_raise(ePKCS7Error, "Could not parse the PKCS7"); + if (!pkcs7->d.ptr) + ossl_raise(ePKCS7Error, "No content in PKCS7"); + data = out ? ossl_membio2str(out) : Qnil; SetPKCS7(ret, pkcs7); ossl_pkcs7_set_data(ret, data); @@ -346,6 +350,8 @@ ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self) BIO_free(in); if (!p7) ossl_raise(rb_eArgError, "Could not parse the PKCS7"); + if (!p7->d.ptr) + ossl_raise(rb_eArgError, "No content in PKCS7"); RTYPEDDATA_DATA(self) = p7; PKCS7_free(p7_orig); @@ -842,6 +848,25 @@ ossl_pkcs7_to_der(VALUE self) } static VALUE +ossl_pkcs7_to_text(VALUE self) +{ + PKCS7 *pkcs7; + BIO *out; + VALUE str; + + GetPKCS7(self, pkcs7); + if(!(out = BIO_new(BIO_s_mem()))) + ossl_raise(ePKCS7Error, NULL); + if(!PKCS7_print_ctx(out, pkcs7, 0, NULL)) { + BIO_free(out); + ossl_raise(ePKCS7Error, NULL); + } + str = ossl_membio2str(out); + + return str; +} + +static VALUE ossl_pkcs7_to_pem(VALUE self) { PKCS7 *pkcs7; @@ -1050,6 +1075,7 @@ Init_ossl_pkcs7(void) rb_define_method(cPKCS7, "to_pem", ossl_pkcs7_to_pem, 0); rb_define_alias(cPKCS7, "to_s", "to_pem"); rb_define_method(cPKCS7, "to_der", ossl_pkcs7_to_der, 0); + rb_define_method(cPKCS7, "to_text", ossl_pkcs7_to_text, 0); cPKCS7Signer = rb_define_class_under(cPKCS7, "SignerInfo", rb_cObject); rb_define_const(cPKCS7, "Signer", cPKCS7Signer); diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 9f374b65ff..d68c64d5fb 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1958,9 +1958,11 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) else rb_str_modify_expand(str, ilen - RSTRING_LEN(str)); } - rb_str_set_len(str, 0); - if (ilen == 0) - return str; + + if (ilen == 0) { + rb_str_set_len(str, 0); + return str; + } VALUE io = rb_attr_get(self, id_i_io); diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index f698bdc7ff..b18a86aad9 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -504,6 +504,25 @@ ossl_ts_req_to_der(VALUE self) } static VALUE +ossl_ts_req_to_text(VALUE self) +{ + TS_REQ *req; + BIO *out; + + GetTSRequest(self, req); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eTimestampError, NULL); + + if (!TS_REQ_print_bio(out, req)) { + BIO_free(out); + ossl_raise(eTimestampError, NULL); + } + + return ossl_membio2str(out); +} + +static VALUE ossl_ts_resp_alloc(VALUE klass) { TS_RESP *resp; @@ -757,6 +776,25 @@ ossl_ts_resp_to_der(VALUE self) return asn1_to_der((void *)resp, (int (*)(void *, unsigned char **))i2d_TS_RESP); } +static VALUE +ossl_ts_resp_to_text(VALUE self) +{ + TS_RESP *resp; + BIO *out; + + GetTSResponse(self, resp); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eTimestampError, NULL); + + if (!TS_RESP_print_bio(out, resp)) { + BIO_free(out); + ossl_raise(eTimestampError, NULL); + } + + return ossl_membio2str(out); +} + /* * Verifies a timestamp token by checking the signature, validating the * certificate chain implied by tsa_certificate and by checking conformance to @@ -1073,6 +1111,25 @@ ossl_ts_token_info_to_der(VALUE self) return asn1_to_der((void *)info, (int (*)(void *, unsigned char **))i2d_TS_TST_INFO); } +static VALUE +ossl_ts_token_info_to_text(VALUE self) +{ + TS_TST_INFO *info; + BIO *out; + + GetTSTokenInfo(self, info); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eTimestampError, NULL); + + if (!TS_TST_INFO_print_bio(out, info)) { + BIO_free(out); + ossl_raise(eTimestampError, NULL); + } + + return ossl_membio2str(out); +} + static ASN1_INTEGER * ossl_tsfac_serial_cb(struct TS_resp_ctx *ctx, void *data) { @@ -1356,6 +1413,7 @@ Init_ossl_ts(void) rb_define_method(cTimestampResponse, "token_info", ossl_ts_resp_get_token_info, 0); rb_define_method(cTimestampResponse, "tsa_certificate", ossl_ts_resp_get_tsa_certificate, 0); rb_define_method(cTimestampResponse, "to_der", ossl_ts_resp_to_der, 0); + rb_define_method(cTimestampResponse, "to_text", ossl_ts_resp_to_text, 0); rb_define_method(cTimestampResponse, "verify", ossl_ts_resp_verify, -1); /* Document-class: OpenSSL::Timestamp::TokenInfo @@ -1374,6 +1432,7 @@ Init_ossl_ts(void) rb_define_method(cTimestampTokenInfo, "ordering", ossl_ts_token_info_get_ordering, 0); rb_define_method(cTimestampTokenInfo, "nonce", ossl_ts_token_info_get_nonce, 0); rb_define_method(cTimestampTokenInfo, "to_der", ossl_ts_token_info_to_der, 0); + rb_define_method(cTimestampTokenInfo, "to_text", ossl_ts_token_info_to_text, 0); /* Document-class: OpenSSL::Timestamp::Request * Allows to create timestamp requests or parse existing ones. A Request is @@ -1399,6 +1458,7 @@ Init_ossl_ts(void) rb_define_method(cTimestampRequest, "cert_requested=", ossl_ts_req_set_cert_requested, 1); rb_define_method(cTimestampRequest, "cert_requested?", ossl_ts_req_get_cert_requested, 0); rb_define_method(cTimestampRequest, "to_der", ossl_ts_req_to_der, 0); + rb_define_method(cTimestampRequest, "to_text", ossl_ts_req_to_text, 0); /* * Indicates a successful response. Equal to +0+. diff --git a/ext/pathname/depend b/ext/pathname/depend index 5dd8b042de..cca7877dad 100644 --- a/ext/pathname/depend +++ b/ext/pathname/depend @@ -157,6 +157,7 @@ pathname.o: $(hdrdir)/ruby/internal/special_consts.h pathname.o: $(hdrdir)/ruby/internal/static_assert.h pathname.o: $(hdrdir)/ruby/internal/stdalign.h pathname.o: $(hdrdir)/ruby/internal/stdbool.h +pathname.o: $(hdrdir)/ruby/internal/stdckdint.h pathname.o: $(hdrdir)/ruby/internal/symbol.h pathname.o: $(hdrdir)/ruby/internal/value.h pathname.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/psych/depend b/ext/psych/depend index 13fbe3fb19..a4d9ca9a6a 100644 --- a/ext/psych/depend +++ b/ext/psych/depend @@ -171,6 +171,7 @@ psych.o: $(hdrdir)/ruby/internal/special_consts.h psych.o: $(hdrdir)/ruby/internal/static_assert.h psych.o: $(hdrdir)/ruby/internal/stdalign.h psych.o: $(hdrdir)/ruby/internal/stdbool.h +psych.o: $(hdrdir)/ruby/internal/stdckdint.h psych.o: $(hdrdir)/ruby/internal/symbol.h psych.o: $(hdrdir)/ruby/internal/value.h psych.o: $(hdrdir)/ruby/internal/value_type.h @@ -347,6 +348,7 @@ psych_emitter.o: $(hdrdir)/ruby/internal/special_consts.h psych_emitter.o: $(hdrdir)/ruby/internal/static_assert.h psych_emitter.o: $(hdrdir)/ruby/internal/stdalign.h psych_emitter.o: $(hdrdir)/ruby/internal/stdbool.h +psych_emitter.o: $(hdrdir)/ruby/internal/stdckdint.h psych_emitter.o: $(hdrdir)/ruby/internal/symbol.h psych_emitter.o: $(hdrdir)/ruby/internal/value.h psych_emitter.o: $(hdrdir)/ruby/internal/value_type.h @@ -523,6 +525,7 @@ psych_parser.o: $(hdrdir)/ruby/internal/special_consts.h psych_parser.o: $(hdrdir)/ruby/internal/static_assert.h psych_parser.o: $(hdrdir)/ruby/internal/stdalign.h psych_parser.o: $(hdrdir)/ruby/internal/stdbool.h +psych_parser.o: $(hdrdir)/ruby/internal/stdckdint.h psych_parser.o: $(hdrdir)/ruby/internal/symbol.h psych_parser.o: $(hdrdir)/ruby/internal/value.h psych_parser.o: $(hdrdir)/ruby/internal/value_type.h @@ -699,6 +702,7 @@ psych_to_ruby.o: $(hdrdir)/ruby/internal/special_consts.h psych_to_ruby.o: $(hdrdir)/ruby/internal/static_assert.h psych_to_ruby.o: $(hdrdir)/ruby/internal/stdalign.h psych_to_ruby.o: $(hdrdir)/ruby/internal/stdbool.h +psych_to_ruby.o: $(hdrdir)/ruby/internal/stdckdint.h psych_to_ruby.o: $(hdrdir)/ruby/internal/symbol.h psych_to_ruby.o: $(hdrdir)/ruby/internal/value.h psych_to_ruby.o: $(hdrdir)/ruby/internal/value_type.h @@ -875,6 +879,7 @@ psych_yaml_tree.o: $(hdrdir)/ruby/internal/special_consts.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/static_assert.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdalign.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdbool.h +psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdckdint.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/symbol.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/value.h psych_yaml_tree.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/pty/depend b/ext/pty/depend index d4d0d558ef..adecfff862 100644 --- a/ext/pty/depend +++ b/ext/pty/depend @@ -157,6 +157,7 @@ pty.o: $(hdrdir)/ruby/internal/special_consts.h pty.o: $(hdrdir)/ruby/internal/static_assert.h pty.o: $(hdrdir)/ruby/internal/stdalign.h pty.o: $(hdrdir)/ruby/internal/stdbool.h +pty.o: $(hdrdir)/ruby/internal/stdckdint.h pty.o: $(hdrdir)/ruby/internal/symbol.h pty.o: $(hdrdir)/ruby/internal/value.h pty.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/rbconfig/sizeof/depend b/ext/rbconfig/sizeof/depend index 4e4ebd4ae5..5f75fa8c76 100644 --- a/ext/rbconfig/sizeof/depend +++ b/ext/rbconfig/sizeof/depend @@ -161,6 +161,7 @@ limits.o: $(hdrdir)/ruby/internal/special_consts.h limits.o: $(hdrdir)/ruby/internal/static_assert.h limits.o: $(hdrdir)/ruby/internal/stdalign.h limits.o: $(hdrdir)/ruby/internal/stdbool.h +limits.o: $(hdrdir)/ruby/internal/stdckdint.h limits.o: $(hdrdir)/ruby/internal/symbol.h limits.o: $(hdrdir)/ruby/internal/value.h limits.o: $(hdrdir)/ruby/internal/value_type.h @@ -319,6 +320,7 @@ sizes.o: $(hdrdir)/ruby/internal/special_consts.h sizes.o: $(hdrdir)/ruby/internal/static_assert.h sizes.o: $(hdrdir)/ruby/internal/stdalign.h sizes.o: $(hdrdir)/ruby/internal/stdbool.h +sizes.o: $(hdrdir)/ruby/internal/stdckdint.h sizes.o: $(hdrdir)/ruby/internal/symbol.h sizes.o: $(hdrdir)/ruby/internal/value.h sizes.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/ripper/depend b/ext/ripper/depend index 3b9b890de8..fe6bd872bd 100644 --- a/ext/ripper/depend +++ b/ext/ripper/depend @@ -200,6 +200,7 @@ eventids1.o: $(hdrdir)/ruby/internal/special_consts.h eventids1.o: $(hdrdir)/ruby/internal/static_assert.h eventids1.o: $(hdrdir)/ruby/internal/stdalign.h eventids1.o: $(hdrdir)/ruby/internal/stdbool.h +eventids1.o: $(hdrdir)/ruby/internal/stdckdint.h eventids1.o: $(hdrdir)/ruby/internal/symbol.h eventids1.o: $(hdrdir)/ruby/internal/value.h eventids1.o: $(hdrdir)/ruby/internal/value_type.h @@ -370,6 +371,7 @@ eventids2.o: $(hdrdir)/ruby/internal/special_consts.h eventids2.o: $(hdrdir)/ruby/internal/static_assert.h eventids2.o: $(hdrdir)/ruby/internal/stdalign.h eventids2.o: $(hdrdir)/ruby/internal/stdbool.h +eventids2.o: $(hdrdir)/ruby/internal/stdckdint.h eventids2.o: $(hdrdir)/ruby/internal/symbol.h eventids2.o: $(hdrdir)/ruby/internal/value.h eventids2.o: $(hdrdir)/ruby/internal/value_type.h @@ -549,6 +551,7 @@ ripper.o: $(hdrdir)/ruby/internal/special_consts.h ripper.o: $(hdrdir)/ruby/internal/static_assert.h ripper.o: $(hdrdir)/ruby/internal/stdalign.h ripper.o: $(hdrdir)/ruby/internal/stdbool.h +ripper.o: $(hdrdir)/ruby/internal/stdckdint.h ripper.o: $(hdrdir)/ruby/internal/symbol.h ripper.o: $(hdrdir)/ruby/internal/value.h ripper.o: $(hdrdir)/ruby/internal/value_type.h @@ -786,6 +789,7 @@ ripper_init.o: $(hdrdir)/ruby/internal/special_consts.h ripper_init.o: $(hdrdir)/ruby/internal/static_assert.h ripper_init.o: $(hdrdir)/ruby/internal/stdalign.h ripper_init.o: $(hdrdir)/ruby/internal/stdbool.h +ripper_init.o: $(hdrdir)/ruby/internal/stdckdint.h ripper_init.o: $(hdrdir)/ruby/internal/symbol.h ripper_init.o: $(hdrdir)/ruby/internal/value.h ripper_init.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/ripper/ripper_init.c.tmpl b/ext/ripper/ripper_init.c.tmpl index c9d381da5b..21f29bd79a 100644 --- a/ext/ripper/ripper_init.c.tmpl +++ b/ext/ripper/ripper_init.c.tmpl @@ -17,15 +17,40 @@ ID id_warn, id_warning, id_gets, id_assoc; +enum lex_type { + lex_type_str, + lex_type_io, + lex_type_generic, +}; + struct ripper { rb_parser_t *p; + enum lex_type type; + union { + struct lex_pointer_string ptr_str; + VALUE val; + } data; }; static void ripper_parser_mark2(void *ptr) { struct ripper *r = (struct ripper*)ptr; - if (r->p) ripper_parser_mark(r->p); + if (r->p) { + ripper_parser_mark(r->p); + + switch (r->type) { + case lex_type_str: + rb_gc_mark(r->data.ptr_str.str); + break; + case lex_type_io: + rb_gc_mark(r->data.val); + break; + case lex_type_generic: + rb_gc_mark(r->data.val); + break; + } + } } static void @@ -53,16 +78,18 @@ static const rb_data_type_t parser_data_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; -static VALUE -ripper_lex_get_generic(struct parser_params *p, VALUE src) +static rb_parser_string_t * +ripper_lex_get_generic(struct parser_params *p, rb_parser_input_data input, int line_count) { + VALUE src = (VALUE)input; VALUE line = rb_funcallv_public(src, id_gets, 0, 0); - if (!NIL_P(line) && !RB_TYPE_P(line, T_STRING)) { + if (NIL_P(line)) return 0; + if (!RB_TYPE_P(line, T_STRING)) { rb_raise(rb_eTypeError, "gets returned %"PRIsVALUE" (expected String or nil)", rb_obj_class(line)); } - return line; + return rb_str_to_parser_string(p, line); } void @@ -78,10 +105,19 @@ ripper_compile_error(struct parser_params *p, const char *fmt, ...) ripper_error(p); } -static VALUE -ripper_lex_io_get(struct parser_params *p, VALUE src) +static rb_parser_string_t * +ripper_lex_io_get(struct parser_params *p, rb_parser_input_data input, int line_count) { - return rb_io_gets(src); + VALUE src = (VALUE)input; + VALUE line = rb_io_gets(src); + if (NIL_P(line)) return 0; + return rb_str_to_parser_string(p, line); +} + +static rb_parser_string_t * +ripper_lex_get_str(struct parser_params *p, rb_parser_input_data input, int line_count) +{ + return rb_parser_lex_get_str(p, (struct lex_pointer_string *)input); } static VALUE @@ -155,7 +191,7 @@ ripper_parser_encoding(VALUE vparser) { struct parser_params *p = ripper_parser_params(vparser, false); - return rb_ruby_parser_encoding(p); + return rb_enc_from_encoding(rb_ruby_parser_encoding(p)); } /* @@ -294,26 +330,38 @@ parser_dedent_string(VALUE self, VALUE input, VALUE width) static VALUE ripper_initialize(int argc, VALUE *argv, VALUE self) { + struct ripper *r; struct parser_params *p; VALUE src, fname, lineno; - VALUE (*gets)(struct parser_params*,VALUE); - VALUE input, sourcefile_string; + rb_parser_lex_gets_func *gets; + VALUE sourcefile_string; const char *sourcefile; int sourceline; + rb_parser_input_data input; p = ripper_parser_params(self, false); + TypedData_Get_Struct(self, struct ripper, &parser_data_type, r); rb_scan_args(argc, argv, "12", &src, &fname, &lineno); if (RB_TYPE_P(src, T_FILE)) { gets = ripper_lex_io_get; + r->type = lex_type_io; + r->data.val = src; + input = (rb_parser_input_data)src; } else if (rb_respond_to(src, id_gets)) { gets = ripper_lex_get_generic; + r->type = lex_type_generic; + r->data.val = src; + input = (rb_parser_input_data)src; } else { StringValue(src); - gets = rb_ruby_ripper_lex_get_str; + gets = ripper_lex_get_str; + r->type = lex_type_str; + r->data.ptr_str.str = src; + r->data.ptr_str.ptr = 0; + input = (rb_parser_input_data)&r->data.ptr_str; } - input = src; if (NIL_P(fname)) { fname = STR_NEW2("(ripper)"); OBJ_FREEZE(fname); diff --git a/ext/ripper/tools/dsl.rb b/ext/ripper/tools/dsl.rb index 3e368813e5..d0002d1ec3 100644 --- a/ext/ripper/tools/dsl.rb +++ b/ext/ripper/tools/dsl.rb @@ -20,6 +20,12 @@ class DSL NAME_PATTERN = /(?>\$|\d+|[a-zA-Z_][a-zA-Z0-9_]*|\[[a-zA-Z_.][-a-zA-Z0-9_.]*\])(?>(?:\.|->)[a-zA-Z_][a-zA-Z0-9_]*)*/.source NOT_REF_PATTERN = /(?>\#.*|[^\"$@]*|"(?>\\.|[^\"])*")/.source + def self.line?(line, lineno = nil) + if %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/> =~ line + new($2, $1&.split(",") || [], lineno) + end + end + def initialize(code, options, lineno = nil) @lineno = lineno @events = {} diff --git a/ext/ripper/tools/generate.rb b/ext/ripper/tools/generate.rb index 27aa53bce0..92ced37f04 100644 --- a/ext/ripper/tools/generate.rb +++ b/ext/ripper/tools/generate.rb @@ -167,15 +167,13 @@ require_relative "dsl" def read_ids1_with_locations(path) h = {} File.open(path) {|f| - f.each.with_index(1) do |line, i| + f.each do |line| next if /\A\#\s*define\s+dispatch/ =~ line next if /ripper_dispatch/ =~ line line.scan(/\bdispatch(\d)\((\w+)/) do |arity, event| (h[event] ||= []).push [f.lineno, arity.to_i] end - if line =~ %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/> - gen = DSL.new($2, ($1 || "").split(","), i) - gen.generate + if gen = DSL.line?(line, f.lineno) gen.events.each do |event, arity| (h[event] ||= []).push [f.lineno, arity.to_i] end diff --git a/ext/ripper/tools/preproc.rb b/ext/ripper/tools/preproc.rb index a54302fb91..a92be93d5b 100644 --- a/ext/ripper/tools/preproc.rb +++ b/ext/ripper/tools/preproc.rb @@ -51,47 +51,50 @@ def process(f, out, path, template) usercode f, out, path, template end -def prelude(f, out) - @exprs = {} +require_relative 'dsl' + +def generate_line(f, out) while line = f.gets - case line - when /\A%%/ + case + when gen = DSL.line?(line) + out << gen.generate << "\n" + when line.start_with?("%%") out << "%%\n" - return + break else - if (/^enum lex_state_(?:bits|e) \{/ =~ line)..(/^\}/ =~ line) - case line - when /^\s*(EXPR_\w+),\s+\/\*(.+)\*\// - @exprs[$1.chomp("_bit")] = $2.strip - when /^\s*(EXPR_\w+)\s+=\s+(.+)$/ - name = $1 - val = $2.chomp(",") - @exprs[name] = "equals to " + (val.start_with?("(") ? "<tt>#{val}</tt>" : "+#{val}+") - end - end + out << yield(line) end - out << line end end -require_relative "dsl" +def prelude(f, out) + @exprs = {} + generate_line(f, out) do |line| + if (/^enum lex_state_(?:bits|e) \{/ =~ line)..(/^\}/ =~ line) + case line + when /^\s*(EXPR_\w+),\s+\/\*(.+)\*\// + @exprs[$1.chomp("_bit")] = $2.strip + when /^\s*(EXPR_\w+)\s+=\s+(.+)$/ + name = $1 + val = $2.chomp(",") + @exprs[name] = "equals to " + (val.start_with?("(") ? "<tt>#{val}</tt>" : "+#{val}+") + end + end + line + end +end def grammar(f, out) - while line = f.gets + generate_line(f, out) do |line| case line - when %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/> - out << DSL.new($2, ($1 || "").split(",")).generate << "\n" when %r</\*%%%\*/> - out << "#if 0\n" + "#if 0\n" when %r</\*%> - out << "#endif\n" + "#endif\n" when %r<%\*/> - out << "\n" - when /\A%%/ - out << "%%\n" - return + "\n" else - out << line + line end end end diff --git a/ext/socket/depend b/ext/socket/depend index e95555ea92..750bb0734f 100644 --- a/ext/socket/depend +++ b/ext/socket/depend @@ -170,6 +170,7 @@ ancdata.o: $(hdrdir)/ruby/internal/special_consts.h ancdata.o: $(hdrdir)/ruby/internal/static_assert.h ancdata.o: $(hdrdir)/ruby/internal/stdalign.h ancdata.o: $(hdrdir)/ruby/internal/stdbool.h +ancdata.o: $(hdrdir)/ruby/internal/stdckdint.h ancdata.o: $(hdrdir)/ruby/internal/symbol.h ancdata.o: $(hdrdir)/ruby/internal/value.h ancdata.o: $(hdrdir)/ruby/internal/value_type.h @@ -380,6 +381,7 @@ basicsocket.o: $(hdrdir)/ruby/internal/special_consts.h basicsocket.o: $(hdrdir)/ruby/internal/static_assert.h basicsocket.o: $(hdrdir)/ruby/internal/stdalign.h basicsocket.o: $(hdrdir)/ruby/internal/stdbool.h +basicsocket.o: $(hdrdir)/ruby/internal/stdckdint.h basicsocket.o: $(hdrdir)/ruby/internal/symbol.h basicsocket.o: $(hdrdir)/ruby/internal/value.h basicsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -590,6 +592,7 @@ constants.o: $(hdrdir)/ruby/internal/special_consts.h constants.o: $(hdrdir)/ruby/internal/static_assert.h constants.o: $(hdrdir)/ruby/internal/stdalign.h constants.o: $(hdrdir)/ruby/internal/stdbool.h +constants.o: $(hdrdir)/ruby/internal/stdckdint.h constants.o: $(hdrdir)/ruby/internal/symbol.h constants.o: $(hdrdir)/ruby/internal/value.h constants.o: $(hdrdir)/ruby/internal/value_type.h @@ -801,6 +804,7 @@ ifaddr.o: $(hdrdir)/ruby/internal/special_consts.h ifaddr.o: $(hdrdir)/ruby/internal/static_assert.h ifaddr.o: $(hdrdir)/ruby/internal/stdalign.h ifaddr.o: $(hdrdir)/ruby/internal/stdbool.h +ifaddr.o: $(hdrdir)/ruby/internal/stdckdint.h ifaddr.o: $(hdrdir)/ruby/internal/symbol.h ifaddr.o: $(hdrdir)/ruby/internal/value.h ifaddr.o: $(hdrdir)/ruby/internal/value_type.h @@ -1011,6 +1015,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h init.o: $(hdrdir)/ruby/internal/static_assert.h init.o: $(hdrdir)/ruby/internal/stdalign.h init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h init.o: $(hdrdir)/ruby/internal/symbol.h init.o: $(hdrdir)/ruby/internal/value.h init.o: $(hdrdir)/ruby/internal/value_type.h @@ -1221,6 +1226,7 @@ ipsocket.o: $(hdrdir)/ruby/internal/special_consts.h ipsocket.o: $(hdrdir)/ruby/internal/static_assert.h ipsocket.o: $(hdrdir)/ruby/internal/stdalign.h ipsocket.o: $(hdrdir)/ruby/internal/stdbool.h +ipsocket.o: $(hdrdir)/ruby/internal/stdckdint.h ipsocket.o: $(hdrdir)/ruby/internal/symbol.h ipsocket.o: $(hdrdir)/ruby/internal/value.h ipsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -1431,6 +1437,7 @@ option.o: $(hdrdir)/ruby/internal/special_consts.h option.o: $(hdrdir)/ruby/internal/static_assert.h option.o: $(hdrdir)/ruby/internal/stdalign.h option.o: $(hdrdir)/ruby/internal/stdbool.h +option.o: $(hdrdir)/ruby/internal/stdckdint.h option.o: $(hdrdir)/ruby/internal/symbol.h option.o: $(hdrdir)/ruby/internal/value.h option.o: $(hdrdir)/ruby/internal/value_type.h @@ -1641,6 +1648,7 @@ raddrinfo.o: $(hdrdir)/ruby/internal/special_consts.h raddrinfo.o: $(hdrdir)/ruby/internal/static_assert.h raddrinfo.o: $(hdrdir)/ruby/internal/stdalign.h raddrinfo.o: $(hdrdir)/ruby/internal/stdbool.h +raddrinfo.o: $(hdrdir)/ruby/internal/stdckdint.h raddrinfo.o: $(hdrdir)/ruby/internal/symbol.h raddrinfo.o: $(hdrdir)/ruby/internal/value.h raddrinfo.o: $(hdrdir)/ruby/internal/value_type.h @@ -1851,6 +1859,7 @@ socket.o: $(hdrdir)/ruby/internal/special_consts.h socket.o: $(hdrdir)/ruby/internal/static_assert.h socket.o: $(hdrdir)/ruby/internal/stdalign.h socket.o: $(hdrdir)/ruby/internal/stdbool.h +socket.o: $(hdrdir)/ruby/internal/stdckdint.h socket.o: $(hdrdir)/ruby/internal/symbol.h socket.o: $(hdrdir)/ruby/internal/value.h socket.o: $(hdrdir)/ruby/internal/value_type.h @@ -2061,6 +2070,7 @@ sockssocket.o: $(hdrdir)/ruby/internal/special_consts.h sockssocket.o: $(hdrdir)/ruby/internal/static_assert.h sockssocket.o: $(hdrdir)/ruby/internal/stdalign.h sockssocket.o: $(hdrdir)/ruby/internal/stdbool.h +sockssocket.o: $(hdrdir)/ruby/internal/stdckdint.h sockssocket.o: $(hdrdir)/ruby/internal/symbol.h sockssocket.o: $(hdrdir)/ruby/internal/value.h sockssocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -2271,6 +2281,7 @@ tcpserver.o: $(hdrdir)/ruby/internal/special_consts.h tcpserver.o: $(hdrdir)/ruby/internal/static_assert.h tcpserver.o: $(hdrdir)/ruby/internal/stdalign.h tcpserver.o: $(hdrdir)/ruby/internal/stdbool.h +tcpserver.o: $(hdrdir)/ruby/internal/stdckdint.h tcpserver.o: $(hdrdir)/ruby/internal/symbol.h tcpserver.o: $(hdrdir)/ruby/internal/value.h tcpserver.o: $(hdrdir)/ruby/internal/value_type.h @@ -2481,6 +2492,7 @@ tcpsocket.o: $(hdrdir)/ruby/internal/special_consts.h tcpsocket.o: $(hdrdir)/ruby/internal/static_assert.h tcpsocket.o: $(hdrdir)/ruby/internal/stdalign.h tcpsocket.o: $(hdrdir)/ruby/internal/stdbool.h +tcpsocket.o: $(hdrdir)/ruby/internal/stdckdint.h tcpsocket.o: $(hdrdir)/ruby/internal/symbol.h tcpsocket.o: $(hdrdir)/ruby/internal/value.h tcpsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -2691,6 +2703,7 @@ udpsocket.o: $(hdrdir)/ruby/internal/special_consts.h udpsocket.o: $(hdrdir)/ruby/internal/static_assert.h udpsocket.o: $(hdrdir)/ruby/internal/stdalign.h udpsocket.o: $(hdrdir)/ruby/internal/stdbool.h +udpsocket.o: $(hdrdir)/ruby/internal/stdckdint.h udpsocket.o: $(hdrdir)/ruby/internal/symbol.h udpsocket.o: $(hdrdir)/ruby/internal/value.h udpsocket.o: $(hdrdir)/ruby/internal/value_type.h @@ -2901,6 +2914,7 @@ unixserver.o: $(hdrdir)/ruby/internal/special_consts.h unixserver.o: $(hdrdir)/ruby/internal/static_assert.h unixserver.o: $(hdrdir)/ruby/internal/stdalign.h unixserver.o: $(hdrdir)/ruby/internal/stdbool.h +unixserver.o: $(hdrdir)/ruby/internal/stdckdint.h unixserver.o: $(hdrdir)/ruby/internal/symbol.h unixserver.o: $(hdrdir)/ruby/internal/value.h unixserver.o: $(hdrdir)/ruby/internal/value_type.h @@ -3111,6 +3125,7 @@ unixsocket.o: $(hdrdir)/ruby/internal/special_consts.h unixsocket.o: $(hdrdir)/ruby/internal/static_assert.h unixsocket.o: $(hdrdir)/ruby/internal/stdalign.h unixsocket.o: $(hdrdir)/ruby/internal/stdbool.h +unixsocket.o: $(hdrdir)/ruby/internal/stdckdint.h unixsocket.o: $(hdrdir)/ruby/internal/symbol.h unixsocket.o: $(hdrdir)/ruby/internal/value.h unixsocket.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/stringio/depend b/ext/stringio/depend index ba2b812041..b9fba7e9fa 100644 --- a/ext/stringio/depend +++ b/ext/stringio/depend @@ -157,6 +157,7 @@ stringio.o: $(hdrdir)/ruby/internal/special_consts.h stringio.o: $(hdrdir)/ruby/internal/static_assert.h stringio.o: $(hdrdir)/ruby/internal/stdalign.h stringio.o: $(hdrdir)/ruby/internal/stdbool.h +stringio.o: $(hdrdir)/ruby/internal/stdckdint.h stringio.o: $(hdrdir)/ruby/internal/symbol.h stringio.o: $(hdrdir)/ruby/internal/value.h stringio.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/strscan/depend b/ext/strscan/depend index 8d985b59e8..8dbae206d4 100644 --- a/ext/strscan/depend +++ b/ext/strscan/depend @@ -157,6 +157,7 @@ strscan.o: $(hdrdir)/ruby/internal/special_consts.h strscan.o: $(hdrdir)/ruby/internal/static_assert.h strscan.o: $(hdrdir)/ruby/internal/stdalign.h strscan.o: $(hdrdir)/ruby/internal/stdbool.h +strscan.o: $(hdrdir)/ruby/internal/stdckdint.h strscan.o: $(hdrdir)/ruby/internal/symbol.h strscan.o: $(hdrdir)/ruby/internal/value.h strscan.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/win32/lib/win32/registry.rb b/ext/win32/lib/win32/registry.rb index 16a08310ad..92b95d1bf7 100644 --- a/ext/win32/lib/win32/registry.rb +++ b/ext/win32/lib/win32/registry.rb @@ -1,4 +1,4 @@ -# frozen_string_literal: false +# frozen_string_literal: true require 'fiddle/import' module Win32 @@ -254,30 +254,34 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr /^(?:x64|x86_64)/ =~ RUBY_PLATFORM end + TEMPLATE_HANDLE = 'J<' + def packhandle(h) - win64? ? packqw(h) : packdw(h) + [h].pack(TEMPLATE_HANDLE) end def unpackhandle(h) - win64? ? unpackqw(h) : unpackdw(h) + (h + [0].pack(TEMPLATE_HANDLE)).unpack1(TEMPLATE_HANDLE) end + TEMPLATE_DWORD = 'V' + def packdw(dw) - [dw].pack('V') + [dw].pack(TEMPLATE_DWORD) end def unpackdw(dw) - dw += [0].pack('V') - dw.unpack('V')[0] + (dw + [0].pack(TEMPLATE_DWORD)).unpack1(TEMPLATE_DWORD) end + TEMPLATE_QWORD = 'Q<' + def packqw(qw) - [ qw & 0xFFFFFFFF, qw >> 32 ].pack('VV') + [qw].pack(TEMPLATE_QWORD) end def unpackqw(qw) - qw = qw.unpack('VV') - (qw[1] << 32) | qw[0] + (qw + [0].pack(TEMPLATE_QWORD)).unpack1(TEMPLATE_QWORD) end def make_wstr(str) @@ -318,7 +322,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr size = packdw(0) name = make_wstr(name) check RegQueryValueExW.call(hkey, name, 0, type, 0, size) - data = "\0".force_encoding('ASCII-8BIT') * unpackdw(size) + data = "\0".b * unpackdw(size) check RegQueryValueExW.call(hkey, name, 0, type, data, size) [ unpackdw(type), data[0, unpackdw(size)] ] end @@ -373,8 +377,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr def self.expand_environ(str) str.gsub(Regexp.compile("%([^%]+)%".encode(str.encoding))) { v = $1.encode(LOCALE) - (e = ENV[v] || ENV[v.upcase]; e.encode(str.encoding) if e) || - $& + (ENV[v] || ENV[v.upcase])&.encode(str.encoding) || $& } end @@ -384,7 +387,6 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr REG_RESOURCE_LIST REG_FULL_RESOURCE_DESCRIPTOR REG_RESOURCE_REQUIREMENTS_LIST REG_QWORD ].inject([]) do |ary, type| - type.freeze ary[Constants.const_get(type)] = type ary end.freeze @@ -657,7 +659,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr when REG_DWORD [ type, API.unpackdw(data) ] when REG_DWORD_BIG_ENDIAN - [ type, data.unpack('N')[0] ] + [ type, data.unpack1('N') ] when REG_QWORD [ type, API.unpackqw(data) ] else diff --git a/ext/zlib/depend b/ext/zlib/depend index bdcf6a93e8..bdce420264 100644 --- a/ext/zlib/depend +++ b/ext/zlib/depend @@ -157,6 +157,7 @@ zlib.o: $(hdrdir)/ruby/internal/special_consts.h zlib.o: $(hdrdir)/ruby/internal/static_assert.h zlib.o: $(hdrdir)/ruby/internal/stdalign.h zlib.o: $(hdrdir)/ruby/internal/stdbool.h +zlib.o: $(hdrdir)/ruby/internal/stdckdint.h zlib.o: $(hdrdir)/ruby/internal/symbol.h zlib.o: $(hdrdir)/ruby/internal/value.h zlib.o: $(hdrdir)/ruby/internal/value_type.h diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index fe03072576..aad9f8d28a 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -90,7 +90,7 @@ static void zstream_expand_buffer_into(struct zstream*, unsigned long); static int zstream_expand_buffer_non_stream(struct zstream *z); static void zstream_append_buffer(struct zstream*, const Bytef*, long); static VALUE zstream_detach_buffer(struct zstream*); -static VALUE zstream_shift_buffer(struct zstream*, long); +static VALUE zstream_shift_buffer(struct zstream*, long, VALUE); static void zstream_buffer_ungets(struct zstream*, const Bytef*, unsigned long); static void zstream_buffer_ungetbyte(struct zstream*, int); static void zstream_append_input(struct zstream*, const Bytef*, long); @@ -170,8 +170,8 @@ static void gzfile_check_footer(struct gzfile*, VALUE outbuf); static void gzfile_write(struct gzfile*, Bytef*, long); static long gzfile_read_more(struct gzfile*, VALUE outbuf); static void gzfile_calc_crc(struct gzfile*, VALUE); -static VALUE gzfile_read(struct gzfile*, long); -static VALUE gzfile_read_all(struct gzfile*); +static VALUE gzfile_read(struct gzfile*, long, VALUE); +static VALUE gzfile_read_all(struct gzfile*, VALUE); static void gzfile_ungets(struct gzfile*, const Bytef*, long); static void gzfile_ungetbyte(struct gzfile*, int); static VALUE gzfile_writer_end_run(VALUE); @@ -820,19 +820,31 @@ zstream_detach_buffer(struct zstream *z) } static VALUE -zstream_shift_buffer(struct zstream *z, long len) +zstream_shift_buffer(struct zstream *z, long len, VALUE dst) { - VALUE dst; char *bufptr; long buflen = ZSTREAM_BUF_FILLED(z); if (buflen <= len) { - return zstream_detach_buffer(z); + if (NIL_P(dst) || (!ZSTREAM_IS_FINISHED(z) && !ZSTREAM_IS_GZFILE(z) && + rb_block_given_p())) { + return zstream_detach_buffer(z); + } else { + bufptr = RSTRING_PTR(z->buf); + rb_str_resize(dst, buflen); + memcpy(RSTRING_PTR(dst), bufptr, buflen); + } + buflen = 0; + } else { + bufptr = RSTRING_PTR(z->buf); + if (NIL_P(dst)) { + dst = rb_str_new(bufptr, len); + } else { + rb_str_resize(dst, len); + memcpy(RSTRING_PTR(dst), bufptr, len); + } + buflen -= len; } - - bufptr = RSTRING_PTR(z->buf); - dst = rb_str_new(bufptr, len); - buflen -= len; memmove(bufptr, bufptr + len, buflen); rb_str_set_len(z->buf, buflen); z->stream.next_out = (Bytef*)RSTRING_END(z->buf); @@ -2874,18 +2886,18 @@ gzfile_newstr(struct gzfile *gz, VALUE str) } static long -gzfile_fill(struct gzfile *gz, long len) +gzfile_fill(struct gzfile *gz, long len, VALUE outbuf) { if (len < 0) rb_raise(rb_eArgError, "negative length %ld given", len); if (len == 0) return 0; while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) < len) { - gzfile_read_more(gz, Qnil); + gzfile_read_more(gz, outbuf); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz, Qnil); + gzfile_check_footer(gz, outbuf); } return -1; } @@ -2893,14 +2905,27 @@ gzfile_fill(struct gzfile *gz, long len) } static VALUE -gzfile_read(struct gzfile *gz, long len) +gzfile_read(struct gzfile *gz, long len, VALUE outbuf) { VALUE dst; - len = gzfile_fill(gz, len); - if (len == 0) return rb_str_new(0, 0); - if (len < 0) return Qnil; - dst = zstream_shift_buffer(&gz->z, len); + len = gzfile_fill(gz, len, outbuf); + + if (len < 0) { + if (!NIL_P(outbuf)) + rb_str_resize(outbuf, 0); + return Qnil; + } + if (len == 0) { + if (NIL_P(outbuf)) + return rb_str_new(0, 0); + else { + rb_str_resize(outbuf, 0); + return outbuf; + } + } + + dst = zstream_shift_buffer(&gz->z, len, outbuf); if (!NIL_P(dst)) gzfile_calc_crc(gz, dst); return dst; } @@ -2933,29 +2958,26 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf) rb_raise(rb_eEOFError, "end of file reached"); } - dst = zstream_shift_buffer(&gz->z, len); + dst = zstream_shift_buffer(&gz->z, len, outbuf); gzfile_calc_crc(gz, dst); - if (!NIL_P(outbuf)) { - rb_str_resize(outbuf, RSTRING_LEN(dst)); - memcpy(RSTRING_PTR(outbuf), RSTRING_PTR(dst), RSTRING_LEN(dst)); - dst = outbuf; - } return dst; } static VALUE -gzfile_read_all(struct gzfile *gz) +gzfile_read_all(struct gzfile *gz, VALUE dst) { - VALUE dst; - while (!ZSTREAM_IS_FINISHED(&gz->z)) { - gzfile_read_more(gz, Qnil); + gzfile_read_more(gz, dst); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz, Qnil); + gzfile_check_footer(gz, dst); } + if (!NIL_P(dst)) { + rb_str_resize(dst, 0); + return dst; + } return rb_str_new(0, 0); } @@ -2993,7 +3015,7 @@ gzfile_getc(struct gzfile *gz) de = (unsigned char *)ds + GZFILE_CBUF_CAPA; (void)rb_econv_convert(gz->ec, &sp, se, &dp, de, ECONV_PARTIAL_INPUT|ECONV_AFTER_OUTPUT); rb_econv_check_error(gz->ec); - dst = zstream_shift_buffer(&gz->z, sp - ss); + dst = zstream_shift_buffer(&gz->z, sp - ss, Qnil); gzfile_calc_crc(gz, dst); rb_str_resize(cbuf, dp - ds); return cbuf; @@ -3001,7 +3023,7 @@ gzfile_getc(struct gzfile *gz) else { buf = gz->z.buf; len = rb_enc_mbclen(RSTRING_PTR(buf), RSTRING_END(buf), gz->enc); - dst = gzfile_read(gz, len); + dst = gzfile_read(gz, len, Qnil); if (NIL_P(dst)) return dst; return gzfile_newstr(gz, dst); } @@ -3909,7 +3931,7 @@ rb_gzreader_s_zcat(int argc, VALUE *argv, VALUE klass) if (!buf) { buf = rb_str_new(0, 0); } - tmpbuf = gzfile_read_all(get_gzfile(obj)); + tmpbuf = gzfile_read_all(get_gzfile(obj), Qnil); rb_str_cat(buf, RSTRING_PTR(tmpbuf), RSTRING_LEN(tmpbuf)); } @@ -4011,19 +4033,19 @@ static VALUE rb_gzreader_read(int argc, VALUE *argv, VALUE obj) { struct gzfile *gz = get_gzfile(obj); - VALUE vlen; + VALUE vlen, outbuf; long len; - rb_scan_args(argc, argv, "01", &vlen); + rb_scan_args(argc, argv, "02", &vlen, &outbuf); if (NIL_P(vlen)) { - return gzfile_read_all(gz); + return gzfile_read_all(gz, outbuf); } len = NUM2INT(vlen); if (len < 0) { rb_raise(rb_eArgError, "negative length %ld given", len); } - return gzfile_read(gz, len); + return gzfile_read(gz, len, outbuf); } /* @@ -4096,7 +4118,7 @@ rb_gzreader_getbyte(VALUE obj) struct gzfile *gz = get_gzfile(obj); VALUE dst; - dst = gzfile_read(gz, 1); + dst = gzfile_read(gz, 1, Qnil); if (!NIL_P(dst)) { dst = INT2FIX((unsigned int)(RSTRING_PTR(dst)[0]) & 0xff); } @@ -4217,7 +4239,7 @@ gzreader_skip_linebreaks(struct gzfile *gz) } } - str = zstream_shift_buffer(&gz->z, n - 1); + str = zstream_shift_buffer(&gz->z, n - 1, Qnil); gzfile_calc_crc(gz, str); } @@ -4238,7 +4260,7 @@ gzreader_charboundary(struct gzfile *gz, long n) if (l < n) { int n_bytes = rb_enc_precise_mbclen(p, e, gz->enc); if (MBCLEN_NEEDMORE_P(n_bytes)) { - if ((l = gzfile_fill(gz, n + MBCLEN_NEEDMORE_LEN(n_bytes))) > 0) { + if ((l = gzfile_fill(gz, n + MBCLEN_NEEDMORE_LEN(n_bytes), Qnil)) > 0) { return l; } } @@ -4290,10 +4312,10 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) if (NIL_P(rs)) { if (limit < 0) { - dst = gzfile_read_all(gz); + dst = gzfile_read_all(gz, Qnil); if (RSTRING_LEN(dst) == 0) return Qnil; } - else if ((n = gzfile_fill(gz, limit)) <= 0) { + else if ((n = gzfile_fill(gz, limit, Qnil)) <= 0) { return Qnil; } else { @@ -4303,7 +4325,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) else { n = limit; } - dst = zstream_shift_buffer(&gz->z, n); + dst = zstream_shift_buffer(&gz->z, n, Qnil); if (NIL_P(dst)) return dst; gzfile_calc_crc(gz, dst); dst = gzfile_newstr(gz, dst); @@ -4330,7 +4352,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) while (ZSTREAM_BUF_FILLED(&gz->z) < rslen) { if (ZSTREAM_IS_FINISHED(&gz->z)) { if (ZSTREAM_BUF_FILLED(&gz->z) > 0) gz->lineno++; - return gzfile_read(gz, rslen); + return gzfile_read(gz, rslen, Qnil); } gzfile_read_more(gz, Qnil); } @@ -4367,7 +4389,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) } gz->lineno++; - dst = gzfile_read(gz, n); + dst = gzfile_read(gz, n, Qnil); if (NIL_P(dst)) return dst; if (rspara) { gzreader_skip_linebreaks(gz); @@ -224,6 +224,9 @@ size_add_overflow(size_t x, size_t y) bool p; #if 0 +#elif defined(ckd_add) + p = ckd_add(&z, x, y); + #elif __has_builtin(__builtin_add_overflow) p = __builtin_add_overflow(x, y, &z); @@ -416,7 +419,6 @@ rb_gc_guarded_ptr_val(volatile VALUE *ptr, VALUE val) #endif #define USE_TICK_T (PRINT_ENTER_EXIT_TICK || PRINT_MEASURE_LINE || PRINT_ROOT_TICKS) -#define TICK_TYPE 1 typedef struct { size_t size_pool_init_slots[SIZE_POOL_COUNT]; @@ -687,29 +689,33 @@ typedef struct RVALUE { VALUE v3; } values; } as; +} RVALUE; - /* Start of RVALUE_OVERHEAD. - * Do not directly read these members from the RVALUE as they're located - * at the end of the slot (which may differ in size depending on the size - * pool). */ -#if RACTOR_CHECK_MODE +/* These members ae located at the end of the slot that the object is in. */ +#if RACTOR_CHECK_MODE || GC_DEBUG +struct rvalue_overhead { +# if RACTOR_CHECK_MODE uint32_t _ractor_belonging_id; -#endif -#if GC_DEBUG +# endif +# if GC_DEBUG const char *file; int line; -#endif -} RVALUE; +# endif +}; -#if RACTOR_CHECK_MODE -# define RVALUE_OVERHEAD (sizeof(RVALUE) - offsetof(RVALUE, _ractor_belonging_id)) -#elif GC_DEBUG -# define RVALUE_OVERHEAD (sizeof(RVALUE) - offsetof(RVALUE, file)) +// Make sure that RVALUE_OVERHEAD aligns to sizeof(VALUE) +# define RVALUE_OVERHEAD (sizeof(struct { \ + union { \ + struct rvalue_overhead overhead; \ + VALUE value; \ + }; \ +})) +# define GET_RVALUE_OVERHEAD(obj) ((struct rvalue_overhead *)((uintptr_t)obj + rb_gc_obj_slot_size(obj))) #else # define RVALUE_OVERHEAD 0 #endif -STATIC_ASSERT(sizeof_rvalue, sizeof(RVALUE) == (SIZEOF_VALUE * 5) + RVALUE_OVERHEAD); +STATIC_ASSERT(sizeof_rvalue, sizeof(RVALUE) == (SIZEOF_VALUE * 5)); STATIC_ASSERT(alignof_rvalue, RUBY_ALIGNOF(RVALUE) == SIZEOF_VALUE); typedef uintptr_t bits_t; @@ -717,7 +723,6 @@ enum { BITS_SIZE = sizeof(bits_t), BITS_BITLENGTH = ( BITS_SIZE * CHAR_BIT ) }; -#define popcount_bits rb_popcount_intptr struct heap_page_header { struct heap_page *page; @@ -954,7 +959,7 @@ typedef struct rb_objspace { #define HEAP_PAGE_ALIGN_LOG 16 #endif -#define BASE_SLOT_SIZE sizeof(RVALUE) +#define BASE_SLOT_SIZE (sizeof(RVALUE) + RVALUE_OVERHEAD) #define CEILDIV(i, mod) roomof(i, mod) enum { @@ -1292,6 +1297,7 @@ total_freed_objects(rb_objspace_t *objspace) #define gc_mode(objspace) gc_mode_verify((enum gc_mode)(objspace)->flags.mode) #define gc_mode_set(objspace, m) ((objspace)->flags.mode = (unsigned int)gc_mode_verify(m)) +#define gc_needs_major_flags objspace->rgengc.need_major_gc #define is_marking(objspace) (gc_mode(objspace) == gc_mode_marking) #define is_sweeping(objspace) (gc_mode(objspace) == gc_mode_sweeping) @@ -1416,17 +1422,7 @@ static const char *obj_type_name(VALUE obj); static void gc_finalize_deferred(void *dmy); -/* - * 1 - TSC (H/W Time Stamp Counter) - * 2 - getrusage - */ -#ifndef TICK_TYPE -#define TICK_TYPE 1 -#endif - #if USE_TICK_T - -#if TICK_TYPE == 1 /* the following code is only for internal tuning. */ /* Source code to use RDTSC is quoted and modified from @@ -1523,28 +1519,6 @@ tick(void) return clock(); } #endif /* TSC */ - -#elif TICK_TYPE == 2 -typedef double tick_t; -#define PRItick "4.9f" - -static inline tick_t -tick(void) -{ - return getrusage_time(); -} -#else /* TICK_TYPE */ -#error "choose tick type" -#endif /* TICK_TYPE */ - -#define MEASURE_LINE(expr) do { \ - volatile tick_t start_time = tick(); \ - volatile tick_t end_time; \ - expr; \ - end_time = tick(); \ - fprintf(stderr, "0\t%"PRItick"\t%s\n", end_time - start_time, #expr); \ -} while (0) - #else /* USE_TICK_T */ #define MEASURE_LINE(expr) expr #endif /* USE_TICK_T */ @@ -1882,44 +1856,45 @@ rb_gc_initial_stress_set(VALUE flag) initial_stress = flag; } -static void * Alloc_GC_impl(void); +static void *rb_gc_impl_objspace_alloc(void); #if USE_SHARED_GC # include "dln.h" -# define Alloc_GC rb_gc_functions->init + +# define RUBY_GC_LIBRARY_PATH "RUBY_GC_LIBRARY_PATH" void -ruby_external_gc_init() +ruby_external_gc_init(void) { - rb_gc_function_map_t *map = malloc(sizeof(rb_gc_function_map_t)); - rb_gc_functions = map; - - char *gc_so_path = getenv("RUBY_GC_LIBRARY_PATH"); - if (!gc_so_path) { - map->init = Alloc_GC_impl; - return; + char *gc_so_path = getenv(RUBY_GC_LIBRARY_PATH); + void *handle = NULL; + if (gc_so_path && dln_supported_p()) { + char error[1024]; + handle = dln_open(gc_so_path, error, sizeof(error)); + if (!handle) { + fprintf(stderr, "%s", error); + rb_bug("ruby_external_gc_init: Shared library %s cannot be opened", gc_so_path); + } } - void *h = dln_open(gc_so_path); - if (!h) { - rb_bug( - "ruby_external_gc_init: Shared library %s cannot be opened.", - gc_so_path - ); - } +# define load_external_gc_func(name) do { \ + if (handle) { \ + rb_gc_functions->name = dln_symbol(handle, "rb_gc_impl_" #name); \ + if (!rb_gc_functions->name) { \ + rb_bug("ruby_external_gc_init: " #name " func not exported by library %s", gc_so_path); \ + } \ + } \ + else { \ + rb_gc_functions->name = rb_gc_impl_##name; \ + } \ +} while (0) - void *gc_init_func = dln_symbol(h, "Init_GC"); - if (!gc_init_func) { - rb_bug( - "ruby_external_gc_init: Init_GC func not exported by library %s", - gc_so_path - ); - } + load_external_gc_func(objspace_alloc); - map->init = gc_init_func; +# undef load_external_gc_func } -#else -# define Alloc_GC Alloc_GC_impl + +# define rb_gc_impl_objspace_alloc rb_gc_functions->objspace_alloc #endif rb_objspace_t * @@ -1928,9 +1903,13 @@ rb_objspace_alloc(void) #if USE_SHARED_GC ruby_external_gc_init(); #endif - return (rb_objspace_t *)Alloc_GC(); + return (rb_objspace_t *)rb_gc_impl_objspace_alloc(); } +#if USE_SHARED_GC +# undef rb_gc_impl_objspace_alloc +#endif + static void free_stack_chunks(mark_stack_t *); static void mark_stack_free_cache(mark_stack_t *); static void heap_page_free(rb_objspace_t *objspace, struct heap_page *page); @@ -2550,7 +2529,7 @@ heap_prepare(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap * sweeping and still don't have a free page, then * gc_sweep_finish_size_pool should allow us to create a new page. */ if (heap->free_pages == NULL && !heap_increment(objspace, size_pool, heap)) { - if (objspace->rgengc.need_major_gc == GPR_FLAG_NONE) { + if (gc_needs_major_flags == GPR_FLAG_NONE) { rb_bug("cannot create a new page after GC"); } else { // Major GC is required, which will allow us to create new page @@ -2659,7 +2638,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, #endif #if GC_DEBUG - RANY(obj)->file = rb_source_location_cstr(&RANY(obj)->line); + GET_RVALUE_OVERHEAD(obj)->file = rb_source_location_cstr(&GET_RVALUE_OVERHEAD(obj)->line); GC_ASSERT(!SPECIAL_CONST_P(obj)); /* check alignment */ #endif @@ -2692,12 +2671,6 @@ size_pool_slot_size(unsigned char pool_id) return slot_size; } -size_t -rb_size_pool_slot_size(unsigned char pool_id) -{ - return size_pool_slot_size(pool_id); -} - bool rb_gc_size_allocatable_p(size_t size) { @@ -2711,7 +2684,7 @@ rb_gc_size_pool_sizes(void) { if (size_pool_sizes[0] == 0) { for (unsigned char i = 0; i < SIZE_POOL_COUNT; i++) { - size_pool_sizes[i] = rb_size_pool_slot_size(i); + size_pool_sizes[i] = size_pool_slot_size(i); } } @@ -3507,7 +3480,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj) } -#define OBJ_ID_INCREMENT (sizeof(RVALUE)) +#define OBJ_ID_INCREMENT (BASE_SLOT_SIZE) #define OBJ_ID_INITIAL (OBJ_ID_INCREMENT) static int @@ -3537,7 +3510,7 @@ static const struct st_hash_type object_id_hash_type = { }; static void * -Alloc_GC_impl(void) +rb_gc_impl_objspace_alloc(void) { rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t)); ruby_current_vm_ptr->objspace = objspace; @@ -5632,7 +5605,7 @@ gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool) grow_heap = TRUE; } else if (is_growth_heap) { /* Only growth heaps are allowed to start a major GC. */ - objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_NOFREE; + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE; size_pool->force_major_gc_count++; } } @@ -8071,7 +8044,7 @@ gc_marks_finish(rb_objspace_t *objspace) } else { gc_report(1, objspace, "gc_marks_finish: next is full GC!!)\n"); - objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_NOFREE; + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE; } } } @@ -8087,20 +8060,20 @@ gc_marks_finish(rb_objspace_t *objspace) } if (objspace->rgengc.uncollectible_wb_unprotected_objects > objspace->rgengc.uncollectible_wb_unprotected_objects_limit) { - objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_SHADY; + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_SHADY; } if (objspace->rgengc.old_objects > objspace->rgengc.old_objects_limit) { - objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDGEN; + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_OLDGEN; } if (RGENGC_FORCE_MAJOR_GC) { - objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_FORCE; + gc_needs_major_flags = GPR_FLAG_MAJOR_BY_FORCE; } gc_report(1, objspace, "gc_marks_finish (marks %"PRIdSIZE" objects, " "old %"PRIdSIZE" objects, total %"PRIdSIZE" slots, " "sweep %"PRIdSIZE" slots, increment: %"PRIdSIZE", next GC: %s)\n", objspace->marked_slots, objspace->rgengc.old_objects, heap_eden_total_slots(objspace), sweep_slots, heap_allocatable_pages(objspace), - objspace->rgengc.need_major_gc ? "major" : "minor"); + gc_needs_major_flags ? "major" : "minor"); } rb_ractor_finish_marking(); @@ -8225,7 +8198,7 @@ gc_compact_plane(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t * do { VALUE vp = (VALUE)p; - GC_ASSERT(vp % sizeof(RVALUE) == 0); + GC_ASSERT(vp % BASE_SLOT_SIZE == 0); if (bitset & 1) { objspace->rcompactor.considered_count_table[BUILTIN_TYPE(vp)]++; @@ -8942,7 +8915,7 @@ gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark) #if RGENGC_ESTIMATE_OLDMALLOC if (!full_mark) { if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) { - objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDMALLOC; + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_OLDMALLOC; objspace->rgengc.oldmalloc_increase_limit = (size_t)(objspace->rgengc.oldmalloc_increase_limit * gc_params.oldmalloc_limit_growth_factor); @@ -8953,7 +8926,7 @@ gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark) if (0) fprintf(stderr, "%"PRIdSIZE"\t%d\t%"PRIuSIZE"\t%"PRIuSIZE"\t%"PRIdSIZE"\n", rb_gc_count(), - objspace->rgengc.need_major_gc, + gc_needs_major_flags, objspace->rgengc.oldmalloc_increase, objspace->rgengc.oldmalloc_increase_limit, gc_params.oldmalloc_limit_max); @@ -9029,8 +9002,8 @@ gc_start(rb_objspace_t *objspace, unsigned int reason) objspace->flags.immediate_sweep = !(flag & (1<<gc_stress_no_immediate_sweep)); } - if (objspace->rgengc.need_major_gc) { - reason |= objspace->rgengc.need_major_gc; + if (gc_needs_major_flags) { + reason |= gc_needs_major_flags; do_full_mark = TRUE; } else if (RGENGC_FORCE_MAJOR_GC) { @@ -9038,7 +9011,7 @@ gc_start(rb_objspace_t *objspace, unsigned int reason) do_full_mark = TRUE; } - objspace->rgengc.need_major_gc = GPR_FLAG_NONE; + gc_needs_major_flags = GPR_FLAG_NONE; if (do_full_mark && (reason & GPR_FLAG_MAJOR_MASK) == 0) { reason |= GPR_FLAG_MAJOR_BY_FORCE; /* GC by CAPI, METHOD, and so on. */ @@ -9296,7 +9269,7 @@ gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_ gc_enter_count(event); if (UNLIKELY(during_gc != 0)) rb_bug("during_gc != 0"); - if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(objspace); + if (RGENGC_CHECK_MODE >= 3 && (dont_gc_val() == 0)) gc_verify_internal_consistency(objspace); during_gc = TRUE; RUBY_DEBUG_LOG("%s (%s)",gc_enter_event_cstr(event), gc_current_status(objspace)); @@ -9983,9 +9956,6 @@ update_cc_tbl_i(VALUE ccs_ptr, void *data) } for (int i=0; i<ccs->len; i++) { - if (gc_object_moved_p(objspace, (VALUE)ccs->entries[i].ci)) { - ccs->entries[i].ci = (struct rb_callinfo *)rb_gc_location((VALUE)ccs->entries[i].ci); - } if (gc_object_moved_p(objspace, (VALUE)ccs->entries[i].cc)) { ccs->entries[i].cc = (struct rb_callcache *)rb_gc_location((VALUE)ccs->entries[i].cc); } @@ -10766,7 +10736,7 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned SET(major_by, major_by); if (orig_flags == 0) { /* set need_major_by only if flags not set explicitly */ - unsigned int need_major_flags = objspace->rgengc.need_major_gc; + unsigned int need_major_flags = gc_needs_major_flags; need_major_by = (need_major_flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree : (need_major_flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen : @@ -11895,7 +11865,7 @@ static inline void * objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size) { size = objspace_malloc_size(objspace, mem, size); - objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC); + objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC) {} #if CALC_EXACT_MALLOC_SIZE { @@ -12369,6 +12339,38 @@ ruby_mimmalloc(size_t size) return mem; } +void * +ruby_mimcalloc(size_t num, size_t size) +{ + void *mem; +#if CALC_EXACT_MALLOC_SIZE + struct rbimpl_size_mul_overflow_tag t = rbimpl_size_mul_overflow(num, size); + if (UNLIKELY(t.left)) { + return NULL; + } + size = t.right + sizeof(struct malloc_obj_info); + mem = calloc1(size); + if (!mem) { + return NULL; + } + else + /* set 0 for consistency of allocated_size/allocations */ + { + struct malloc_obj_info *info = mem; + info->size = 0; +#if USE_GC_MALLOC_OBJ_INFO_DETAILS + info->gen = 0; + info->file = NULL; + info->line = 0; +#endif + mem = info + 1; + } +#else + mem = calloc(num, size); +#endif + return mem; +} + void ruby_mimfree(void *ptr) { @@ -13182,7 +13184,7 @@ rb_raw_obj_info_common(char *const buff, const size_t buff_size, const VALUE obj } #if GC_DEBUG - APPEND_F("@%s:%d", RANY(obj)->file, RANY(obj)->line); + APPEND_F("@%s:%d", GET_RVALUE_OVERHEAD(obj)->file, GET_RVALUE_OVERHEAD(obj)->line); #endif } end: @@ -13491,7 +13493,7 @@ rb_gcdebug_print_obj_condition(VALUE obj) { rb_objspace_t *objspace = &rb_objspace; - fprintf(stderr, "created at: %s:%d\n", RANY(obj)->file, RANY(obj)->line); + fprintf(stderr, "created at: %s:%d\n", GET_RVALUE_OVERHEAD(obj)->file, GET_RVALUE_OVERHEAD(obj)->line); if (BUILTIN_TYPE(obj) == T_MOVED) { fprintf(stderr, "moved?: true\n"); @@ -13516,7 +13518,7 @@ rb_gcdebug_print_obj_condition(VALUE obj) if (is_lazy_sweeping(objspace)) { fprintf(stderr, "lazy sweeping?: true\n"); - fprintf(stderr, "page swept?: %s\n", GET_HEAP_PAGE(ptr)->flags.before_sweep ? "false" : "true"); + fprintf(stderr, "page swept?: %s\n", GET_HEAP_PAGE(obj)->flags.before_sweep ? "false" : "true"); } else { fprintf(stderr, "lazy sweeping?: false\n"); @@ -13592,10 +13594,9 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self) * traverse all living objects with an iterator. * * ObjectSpace also provides support for object finalizers, procs that will be - * called when a specific object is about to be destroyed by garbage - * collection. See the documentation for - * <code>ObjectSpace.define_finalizer</code> for important information on - * how to use this method correctly. + * called after a specific object was destroyed by garbage collection. See + * the documentation for +ObjectSpace.define_finalizer+ for important + * information on how to use this method correctly. * * a = "A" * b = "B" @@ -13635,6 +13636,12 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self) void Init_GC(void) { +#if USE_SHARED_GC + if (getenv(RUBY_GC_LIBRARY_PATH) != NULL && !dln_supported_p()) { + rb_warn(RUBY_GC_LIBRARY_PATH " is ignored because this executable file can't load extension libraries"); + } +#endif + #undef rb_intern malloc_offset = gc_compute_malloc_offset(); @@ -13648,7 +13655,7 @@ Init_GC(void) rb_hash_aset(gc_constants, ID2SYM(rb_intern("DEBUG")), RBOOL(GC_DEBUG)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(BASE_SLOT_SIZE - RVALUE_OVERHEAD)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), SIZET2NUM(RVALUE_OVERHEAD)); - rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_SIZE")), SIZET2NUM(sizeof(RVALUE))); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_SIZE")), SIZET2NUM(BASE_SLOT_SIZE)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_OBJ_LIMIT")), SIZET2NUM(HEAP_PAGE_OBJ_LIMIT)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_BITMAP_SIZE")), SIZET2NUM(HEAP_PAGE_BITMAP_SIZE)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_SIZE")), SIZET2NUM(HEAP_PAGE_SIZE)); diff --git a/gems/bundled_gems b/gems/bundled_gems index ad979c1c5c..bb6dcb5447 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -6,14 +6,14 @@ # - revision: revision in repository-url to test # if `revision` is not given, "v"+`version` or `version` will be used. -minitest 5.22.3 https://github.com/minitest/minitest ea9caafc0754b1d6236a490d59e624b53209734a +minitest 5.23.0 https://github.com/minitest/minitest power_assert 2.0.3 https://github.com/ruby/power_assert 84e85124c5014a139af39161d484156cfe87a9ed rake 13.2.1 https://github.com/ruby/rake test-unit 3.6.2 https://github.com/test-unit/test-unit rexml 3.2.6 https://github.com/ruby/rexml rss 0.3.0 https://github.com/ruby/rss net-ftp 0.3.4 https://github.com/ruby/net-ftp -net-imap 0.4.10 https://github.com/ruby/net-imap +net-imap 0.4.11 https://github.com/ruby/net-imap net-pop 0.1.2 https://github.com/ruby/net-pop net-smtp 0.5.0 https://github.com/ruby/net-smtp matrix 0.4.2 https://github.com/ruby/matrix @@ -25,7 +25,7 @@ racc 1.7.3 https://github.com/ruby/racc mutex_m 0.2.0 https://github.com/ruby/mutex_m getoptlong 0.2.1 https://github.com/ruby/getoptlong base64 0.2.0 https://github.com/ruby/base64 -bigdecimal 3.1.7 https://github.com/ruby/bigdecimal +bigdecimal 3.1.8 https://github.com/ruby/bigdecimal observer 0.1.2 https://github.com/ruby/observer abbrev 0.1.2 https://github.com/ruby/abbrev resolv-replace 0.1.1 https://github.com/ruby/resolv-replace @@ -6,6 +6,8 @@ # include <sys/time.h> #endif +#include "internal/compilers.h" + /* * Hi-res monotonic clock. It is currently nsec resolution, which has over * 500 years of range (with an unsigned 64-bit integer). Developers @@ -61,7 +63,11 @@ rb_hrtime_mul(rb_hrtime_t a, rb_hrtime_t b) { rb_hrtime_t c; -#ifdef HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW +#ifdef ckd_mul + if (ckd_mul(&c, a, b)) + return RB_HRTIME_MAX; + +#elif __has_builtin(__builtin_mul_overflow) if (__builtin_mul_overflow(a, b, &c)) return RB_HRTIME_MAX; #else @@ -81,7 +87,11 @@ rb_hrtime_add(rb_hrtime_t a, rb_hrtime_t b) { rb_hrtime_t c; -#ifdef HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW +#ifdef ckd_add + if (ckd_add(&c, a, b)) + return RB_HRTIME_MAX; + +#elif __has_builtin(__builtin_add_overflow) if (__builtin_add_overflow(a, b, &c)) return RB_HRTIME_MAX; #else @@ -130,7 +130,7 @@ rb_imemo_memsize(VALUE obj) size_t size = 0; switch (imemo_type(obj)) { case imemo_ast: - size += rb_ast_memsize((rb_ast_t *)obj); + rb_bug("imemo_ast is obsolete"); break; case imemo_callcache: @@ -196,7 +196,6 @@ cc_table_mark_i(ID id, VALUE ccs_ptr, void *data) VM_ASSERT((VALUE)data == ccs->entries[i].cc->klass); VM_ASSERT(vm_cc_check_cme(ccs->entries[i].cc, ccs->cme)); - rb_gc_mark_movable((VALUE)ccs->entries[i].ci); rb_gc_mark_movable((VALUE)ccs->entries[i].cc); } return ID_TABLE_CONTINUE; @@ -274,7 +273,7 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating) { switch (imemo_type(obj)) { case imemo_ast: - // TODO: Make AST decoupled from IMEMO + rb_bug("imemo_ast is obsolete"); break; case imemo_callcache: { @@ -514,8 +513,7 @@ rb_imemo_free(VALUE obj) { switch (imemo_type(obj)) { case imemo_ast: - rb_ast_free((rb_ast_t *)obj); - RB_DEBUG_COUNTER_INC(obj_imemo_ast); + rb_bug("imemo_ast is obsolete"); break; case imemo_callcache: diff --git a/include/ruby/internal/encoding/encoding.h b/include/ruby/internal/encoding/encoding.h index dc3e0151f0..a680651a81 100644 --- a/include/ruby/internal/encoding/encoding.h +++ b/include/ruby/internal/encoding/encoding.h @@ -28,6 +28,7 @@ #include "ruby/internal/attr/pure.h" #include "ruby/internal/attr/returns_nonnull.h" #include "ruby/internal/dllexport.h" +#include "ruby/internal/encoding/coderange.h" #include "ruby/internal/value.h" #include "ruby/internal/core/rbasic.h" #include "ruby/internal/fl_type.h" diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h index 3bd54cd6c7..270cc1ac8b 100644 --- a/include/ruby/internal/memory.h +++ b/include/ruby/internal/memory.h @@ -56,6 +56,7 @@ #include "ruby/internal/has/builtin.h" #include "ruby/internal/stdalign.h" #include "ruby/internal/stdbool.h" +#include "ruby/internal/stdckdint.h" #include "ruby/internal/xmalloc.h" #include "ruby/backward/2/limits.h" #include "ruby/backward/2/long_long.h" @@ -567,7 +568,10 @@ rbimpl_size_mul_overflow(size_t x, size_t y) { struct rbimpl_size_mul_overflow_tag ret = { false, 0, }; -#if RBIMPL_HAS_BUILTIN(__builtin_mul_overflow) +#if defined(ckd_mul) + ret.left = ckd_mul(&ret.right, x, y); + +#elif RBIMPL_HAS_BUILTIN(__builtin_mul_overflow) ret.left = __builtin_mul_overflow(x, y, &ret.right); #elif defined(DSIZE_T) diff --git a/include/ruby/internal/stdckdint.h b/include/ruby/internal/stdckdint.h new file mode 100644 index 0000000000..e5b5b8b751 --- /dev/null +++ b/include/ruby/internal/stdckdint.h @@ -0,0 +1,68 @@ +#ifndef RBIMPL_STDCKDINT_H /*-*-C++-*-vi:se ft=cpp:*/ +#define RBIMPL_STDCKDINT_H +/** + * @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 C23 shim for <stdckdint.h> + */ +#include "ruby/internal/config.h" +#include "ruby/internal/cast.h" +#include "ruby/internal/has/builtin.h" +#include "ruby/internal/stdbool.h" + +#ifdef __has_include +# if __has_include(<stdckdint.h>) +# /* Conforming C23 situation; e.g. recent clang */ +# define RBIMPL_HAVE_STDCKDINT_H +# endif +#endif + +#ifdef HAVE_STDCKDINT_H +# /* Some OSes (most notably FreeBSD) have this file. */ +# define RBIMPL_HAVE_STDCKDINT_H +#endif + +#ifdef __cplusplus +# /* It seems OS/Compiler provided stdckdint.h tend not support C++ yet. +# * Situations could improve someday but in a meantime let us work around. +# */ +# undef RBIMPL_HAVE_STDCKDINT_H +#endif + +#ifdef RBIMPL_HAVE_STDCKDINT_H +# /* Take that. */ +# include <stdckdint.h> + +#elif RBIMPL_HAS_BUILTIN(__builtin_add_overflow) +# define ckd_add(x, y, z) RBIMPL_CAST((bool)__builtin_add_overflow((y), (z), (x))) +# define ckd_sub(x, y, z) RBIMPL_CAST((bool)__builtin_sub_overflow((y), (z), (x))) +# define ckd_mul(x, y, z) RBIMPL_CAST((bool)__builtin_mul_overflow((y), (z), (x))) +# define __STDC_VERSION_STDCKDINT_H__ 202311L + +#/* elif defined(__cplusplus) */ +#/* :TODO: if we assume C++11 we can use `<type_traits>` to implement them. */ + +#else +# /* intentionally leave them undefined */ +# /* to make `#ifdef ckd_add` etc. work as intended. */ +# undef ckd_add +# undef ckd_sub +# undef ckd_mul +# undef __STDC_VERSION_STDCKDINT_H__ +#endif + +#endif /* RBIMPL_STDCKDINT_H */ diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h index b044db0539..e4d98bf051 100644 --- a/include/ruby/io/buffer.h +++ b/include/ruby/io/buffer.h @@ -69,14 +69,10 @@ enum rb_io_buffer_endian { RB_IO_BUFFER_LITTLE_ENDIAN = 4, RB_IO_BUFFER_BIG_ENDIAN = 8, -#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN, -#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#if defined(WORDS_BIGENDIAN) RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN, -#elif defined(REG_DWORD) && REG_DWORD == REG_DWORD_LITTLE_ENDIAN +#else RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN, -#elif defined(REG_DWORD) && REG_DWORD == REG_DWORD_BIG_ENDIAN - RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN, #endif RB_IO_BUFFER_NETWORK_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN diff --git a/internal/encoding.h b/internal/encoding.h index 11ffa6d83d..fe9ea10ec4 100644 --- a/internal/encoding.h +++ b/internal/encoding.h @@ -18,6 +18,7 @@ /* encoding.c */ ID rb_id_encoding(void); +const char * rb_enc_inspect_name(rb_encoding *enc); rb_encoding *rb_enc_get_from_index(int index); rb_encoding *rb_enc_check_str(VALUE str1, VALUE str2); int rb_encdb_replicate(const char *alias, const char *orig); diff --git a/internal/gc.h b/internal/gc.h index 91dbc0ccf3..ecc3f11b2c 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -119,8 +119,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); -size_t rb_size_pool_slot_size(unsigned char pool_id); - struct rb_execution_context_struct; /* in vm_core.h */ struct rb_objspace; /* in vm_core.h */ @@ -195,6 +193,7 @@ typedef struct ractor_newobj_cache { /* gc.c */ extern int ruby_disable_gc; RUBY_ATTR_MALLOC void *ruby_mimmalloc(size_t size); +RUBY_ATTR_MALLOC void *ruby_mimcalloc(size_t num, size_t size); void ruby_mimfree(void *ptr); void rb_gc_prepare_heap(void); void rb_objspace_set_event_hook(const rb_event_flag_t event); diff --git a/internal/imemo.h b/internal/imemo.h index 673e7e668a..36c0776987 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -39,7 +39,7 @@ enum imemo_type { imemo_ment = 6, imemo_iseq = 7, imemo_tmpbuf = 8, - imemo_ast = 9, + imemo_ast = 9, // Obsolete due to the universal parser imemo_parser_strterm = 10, imemo_callinfo = 11, imemo_callcache = 12, diff --git a/internal/parse.h b/internal/parse.h index 80328686c1..5f52e8a8e3 100644 --- a/internal/parse.h +++ b/internal/parse.h @@ -13,7 +13,7 @@ #include "internal/static_assert.h" #ifdef UNIVERSAL_PARSER -#define rb_encoding void +#define rb_encoding const void #endif struct rb_iseq_struct; /* in vm_core.h */ @@ -53,14 +53,13 @@ void rb_ruby_parser_set_options(rb_parser_t *p, int print, int loop, int chomp, rb_parser_t *rb_ruby_parser_set_context(rb_parser_t *p, const struct rb_iseq_struct *base, int main); void rb_ruby_parser_set_script_lines(rb_parser_t *p); void rb_ruby_parser_error_tolerant(rb_parser_t *p); -rb_ast_t* rb_ruby_parser_compile_file_path(rb_parser_t *p, VALUE fname, VALUE file, int start); void rb_ruby_parser_keep_tokens(rb_parser_t *p); -rb_ast_t* rb_ruby_parser_compile_generic(rb_parser_t *p, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int start); -rb_ast_t* rb_ruby_parser_compile_string_path(rb_parser_t *p, VALUE f, VALUE s, int line); +typedef rb_parser_string_t*(rb_parser_lex_gets_func)(struct parser_params*, rb_parser_input_data, int); +rb_ast_t *rb_parser_compile(rb_parser_t *p, rb_parser_lex_gets_func *gets, const char *fname_ptr, long fname_len, rb_encoding *fname_enc, rb_parser_input_data input, int line); RUBY_SYMBOL_EXPORT_BEGIN -VALUE rb_ruby_parser_encoding(rb_parser_t *p); +rb_encoding *rb_ruby_parser_encoding(rb_parser_t *p); int rb_ruby_parser_end_seen_p(rb_parser_t *p); int rb_ruby_parser_set_yydebug(rb_parser_t *p, int flag); rb_parser_string_t *rb_str_to_parser_string(rb_parser_t *p, VALUE str); @@ -73,6 +72,11 @@ int rb_parser_local_defined(struct parser_params *p, ID id, const struct rb_iseq RUBY_SYMBOL_EXPORT_END +#ifndef UNIVERSAL_PARSER +rb_parser_t *rb_ruby_parser_allocate(void); +rb_parser_t *rb_ruby_parser_new(void); +#endif + #ifdef RIPPER void ripper_parser_mark(void *ptr); void ripper_parser_free(void *ptr); @@ -86,7 +90,7 @@ VALUE rb_ruby_parser_debug_output(rb_parser_t *p); void rb_ruby_parser_set_debug_output(rb_parser_t *p, VALUE output); VALUE rb_ruby_parser_parsing_thread(rb_parser_t *p); void rb_ruby_parser_set_parsing_thread(rb_parser_t *p, VALUE parsing_thread); -void rb_ruby_parser_ripper_initialize(rb_parser_t *p, VALUE (*gets)(struct parser_params*,VALUE), VALUE input, VALUE sourcefile_string, const char *sourcefile, int sourceline); +void rb_ruby_parser_ripper_initialize(rb_parser_t *p, rb_parser_lex_gets_func *gets, rb_parser_input_data input, VALUE sourcefile_string, const char *sourcefile, int sourceline); VALUE rb_ruby_parser_result(rb_parser_t *p); rb_encoding *rb_ruby_parser_enc(rb_parser_t *p); VALUE rb_ruby_parser_ruby_sourcefile_string(rb_parser_t *p); @@ -94,7 +98,6 @@ int rb_ruby_parser_ruby_sourceline(rb_parser_t *p); int rb_ruby_parser_lex_state(rb_parser_t *p); void rb_ruby_ripper_parse0(rb_parser_t *p); int rb_ruby_ripper_dedent_string(rb_parser_t *p, VALUE string, int width); -VALUE rb_ruby_ripper_lex_get_str(rb_parser_t *p, VALUE s); int rb_ruby_ripper_initialized_p(rb_parser_t *p); void rb_ruby_ripper_parser_initialize(rb_parser_t *p); long rb_ruby_ripper_column(rb_parser_t *p); diff --git a/internal/ruby_parser.h b/internal/ruby_parser.h index f0cec86668..8e306d18de 100644 --- a/internal/ruby_parser.h +++ b/internal/ruby_parser.h @@ -5,21 +5,27 @@ #include "internal/bignum.h" #include "internal/compilers.h" #include "internal/complex.h" +#include "internal/parse.h" #include "internal/rational.h" #include "rubyparser.h" #include "vm.h" +struct lex_pointer_string { + VALUE str; + long ptr; +}; + RUBY_SYMBOL_EXPORT_BEGIN #ifdef UNIVERSAL_PARSER const rb_parser_config_t *rb_ruby_parser_config(void); -rb_parser_t *rb_parser_params_allocate(void); rb_parser_t *rb_parser_params_new(void); #endif VALUE rb_parser_set_context(VALUE, const struct rb_iseq_struct *, int); VALUE rb_parser_new(void); -rb_ast_t *rb_parser_compile_string_path(VALUE vparser, VALUE fname, VALUE src, int line); +VALUE rb_parser_compile_string_path(VALUE vparser, VALUE fname, VALUE src, int line); VALUE rb_str_new_parser_string(rb_parser_string_t *str); VALUE rb_str_new_mutable_parser_string(rb_parser_string_t *str); +rb_parser_string_t *rb_parser_lex_get_str(struct parser_params *p, struct lex_pointer_string *ptr_str); VALUE rb_node_str_string_val(const NODE *); VALUE rb_node_sym_string_val(const NODE *); @@ -40,16 +46,16 @@ VALUE rb_parser_end_seen_p(VALUE); VALUE rb_parser_encoding(VALUE); VALUE rb_parser_set_yydebug(VALUE, VALUE); VALUE rb_parser_build_script_lines_from(rb_parser_ary_t *script_lines); -void rb_parser_aset_script_lines_for(VALUE path, rb_parser_ary_t *script_lines); void rb_parser_set_options(VALUE, int, int, int, int); -void *rb_parser_load_file(VALUE parser, VALUE name); +VALUE rb_parser_load_file(VALUE parser, VALUE name); void rb_parser_set_script_lines(VALUE vparser); void rb_parser_error_tolerant(VALUE vparser); void rb_parser_keep_tokens(VALUE vparser); -rb_ast_t *rb_parser_compile_string(VALUE, const char*, VALUE, int); -rb_ast_t *rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE input, int line); -rb_ast_t *rb_parser_compile_generic(VALUE vparser, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int line); +VALUE rb_parser_compile_string(VALUE, const char*, VALUE, int); +VALUE rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE input, int line); +VALUE rb_parser_compile_generic(VALUE vparser, rb_parser_lex_gets_func *lex_gets, VALUE fname, VALUE input, int line); +VALUE rb_parser_compile_array(VALUE vparser, VALUE fname, VALUE array, int start); enum lex_state_bits { EXPR_BEG_bit, /* ignore newline, +/- is a sign. */ @@ -90,4 +96,7 @@ enum lex_state_e { EXPR_NONE = 0 }; +VALUE rb_ruby_ast_new(const NODE *const root); +rb_ast_t *rb_ruby_ast_data_get(VALUE ast_value); + #endif /* INTERNAL_RUBY_PARSE_H */ diff --git a/io_buffer.c b/io_buffer.c index 7715aa0d37..af884f1473 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -260,7 +260,7 @@ io_buffer_free(struct rb_io_buffer *buffer) if (buffer->mapping) { if (RB_IO_BUFFER_DEBUG) fprintf(stderr, "io_buffer_free:CloseHandle -> %p\n", buffer->mapping); if (!CloseHandle(buffer->mapping)) { - fprintf(stderr, "io_buffer_free:GetLastError -> %d\n", GetLastError()); + fprintf(stderr, "io_buffer_free:GetLastError -> %lu\n", GetLastError()); } buffer->mapping = NULL; } @@ -167,7 +167,7 @@ rb_iseq_free(const rb_iseq_t *iseq) struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); rb_rjit_free_iseq(iseq); /* Notify RJIT */ #if USE_YJIT - rb_yjit_iseq_free(body->yjit_payload); + rb_yjit_iseq_free(iseq); if (FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)) { RUBY_ASSERT(rb_yjit_live_iseq_count > 0); rb_yjit_live_iseq_count--; @@ -292,6 +292,10 @@ static bool cc_is_active(const struct rb_callcache *cc, bool reference_updating) { if (cc) { + if (cc == rb_vm_empty_cc() || rb_vm_empty_cc_for_super()) { + return false; + } + if (reference_updating) { cc = (const struct rb_callcache *)rb_gc_location((VALUE)cc); } @@ -373,7 +377,7 @@ rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating) rb_rjit_iseq_update_references(body); #endif #if USE_YJIT - rb_yjit_iseq_update_references(body->yjit_payload); + rb_yjit_iseq_update_references(iseq); #endif } else { @@ -835,32 +839,24 @@ make_compile_option_value(rb_compile_option_t *option) } rb_iseq_t * -rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, +rb_iseq_new(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type type) { - return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, + return rb_iseq_new_with_opt(ast_value, name, path, realpath, 0, parent, 0, type, &COMPILE_OPTION_DEFAULT, Qnil); } static int -ast_line_count(const rb_ast_body_t *ast) +ast_line_count(const VALUE ast_value) { - if (ast->script_lines == NULL) { - // this occurs when failed to parse the source code with a syntax error - return 0; - } - if (!FIXNUM_P((VALUE)ast->script_lines)) { - return (int)ast->script_lines->len; - } - return FIX2INT((VALUE)ast->script_lines); + rb_ast_t *ast = rb_ruby_ast_data_get(ast_value); + return ast->body.line_count; } static VALUE -iseq_setup_coverage(VALUE coverages, VALUE path, const rb_ast_body_t *ast, int line_offset) +iseq_setup_coverage(VALUE coverages, VALUE path, int line_count) { - int line_count = line_offset + ast_line_count(ast); - if (line_count >= 0) { int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count; @@ -874,21 +870,21 @@ iseq_setup_coverage(VALUE coverages, VALUE path, const rb_ast_body_t *ast, int l } static inline void -iseq_new_setup_coverage(VALUE path, const rb_ast_body_t *ast, int line_offset) +iseq_new_setup_coverage(VALUE path, int line_count) { VALUE coverages = rb_get_coverages(); if (RTEST(coverages)) { - iseq_setup_coverage(coverages, path, ast, line_offset); + iseq_setup_coverage(coverages, path, line_count); } } rb_iseq_t * -rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent) +rb_iseq_new_top(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent) { - iseq_new_setup_coverage(path, ast, 0); + iseq_new_setup_coverage(path, ast_line_count(ast_value)); - return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, 0, + return rb_iseq_new_with_opt(ast_value, name, path, realpath, 0, parent, 0, ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT, Qnil); } @@ -899,18 +895,18 @@ rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath rb_iseq_t * pm_iseq_new_top(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent) { - // iseq_new_setup_coverage(path, ast, 0); + iseq_new_setup_coverage(path, (int) (node->parser->newline_list.size - 1)); return pm_iseq_new_with_opt(node, name, path, realpath, 0, parent, 0, ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT); } rb_iseq_t * -rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt) +rb_iseq_new_main(const VALUE ast_value, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt) { - iseq_new_setup_coverage(path, ast, 0); + iseq_new_setup_coverage(path, ast_line_count(ast_value)); - return rb_iseq_new_with_opt(ast, rb_fstring_lit("<main>"), + return rb_iseq_new_with_opt(ast_value, rb_fstring_lit("<main>"), path, realpath, 0, parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE, Qnil); @@ -923,7 +919,7 @@ rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_ rb_iseq_t * pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt) { - // iseq_new_setup_coverage(path, ast, 0); + iseq_new_setup_coverage(path, (int) (node->parser->newline_list.size - 1)); return pm_iseq_new_with_opt(node, rb_fstring_lit("<main>"), path, realpath, 0, @@ -931,16 +927,16 @@ pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_ise } rb_iseq_t * -rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth) +rb_iseq_new_eval(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth) { if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) { VALUE coverages = rb_get_coverages(); if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) { - iseq_setup_coverage(coverages, path, ast, first_lineno - 1); + iseq_setup_coverage(coverages, path, ast_line_count(ast_value) + first_lineno - 1); } } - return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno, + return rb_iseq_new_with_opt(ast_value, name, path, realpath, first_lineno, parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT, Qnil); } @@ -949,8 +945,15 @@ rb_iseq_t * pm_iseq_new_eval(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth) { - return pm_iseq_new_with_opt(node, name, path, realpath, first_lineno, - parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT); + if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) { + VALUE coverages = rb_get_coverages(); + if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) { + iseq_setup_coverage(coverages, path, ((int) (node->parser->newline_list.size - 1)) + first_lineno - 1); + } + } + + return pm_iseq_new_with_opt(node, name, path, realpath, first_lineno, + parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT); } static inline rb_iseq_t * @@ -968,27 +971,29 @@ iseq_translate(rb_iseq_t *iseq) } rb_iseq_t * -rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, +rb_iseq_new_with_opt(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type, const rb_compile_option_t *option, VALUE script_lines) { - const NODE *node = ast ? ast->root : 0; + rb_ast_t *ast = rb_ruby_ast_data_get(ast_value); + rb_ast_body_t *body = ast ? &ast->body : NULL; + const NODE *node = body ? body->root : 0; /* TODO: argument check */ rb_iseq_t *iseq = iseq_alloc(); rb_compile_option_t new_opt; if (!option) option = &COMPILE_OPTION_DEFAULT; - if (ast) { + if (body) { new_opt = *option; - option = set_compile_option_from_ast(&new_opt, ast); + option = set_compile_option_from_ast(&new_opt, body); } if (!NIL_P(script_lines)) { // noop } - else if (ast && !FIXNUM_P((VALUE)ast->script_lines) && ast->script_lines) { - script_lines = rb_parser_build_script_lines_from(ast->script_lines); + else if (body && body->script_lines) { + script_lines = rb_parser_build_script_lines_from(body->script_lines); } else if (parent) { script_lines = ISEQ_BODY(parent)->variable.script_lines; @@ -1210,9 +1215,10 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V #else # define INITIALIZED /* volatile */ #endif - rb_ast_t *(*parse)(VALUE vparser, VALUE fname, VALUE file, int start); + VALUE (*parse)(VALUE vparser, VALUE fname, VALUE file, int start); int ln; - rb_ast_t *INITIALIZED ast; + VALUE INITIALIZED ast_value; + rb_ast_t *ast; VALUE name = rb_fstring_lit("<compiled>"); /* safe results first */ @@ -1228,20 +1234,22 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V } { const VALUE parser = rb_parser_new(); - const rb_iseq_t *outer_scope = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP); + const rb_iseq_t *outer_scope = rb_iseq_new(Qnil, name, name, Qnil, 0, ISEQ_TYPE_TOP); VALUE outer_scope_v = (VALUE)outer_scope; rb_parser_set_context(parser, outer_scope, FALSE); if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser); RB_GC_GUARD(outer_scope_v); - ast = (*parse)(parser, file, src, ln); + ast_value = (*parse)(parser, file, src, ln); } - if (!ast->body.root) { + ast = rb_ruby_ast_data_get(ast_value); + + if (!ast || !ast->body.root) { rb_ast_dispose(ast); rb_exc_raise(GET_EC()->errinfo); } else { - iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, ln, + iseq = rb_iseq_new_with_opt(ast_value, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option, Qnil); rb_ast_dispose(ast); @@ -1266,6 +1274,20 @@ pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V pm_parse_result_t result = { 0 }; pm_options_line_set(&result.options, NUM2INT(line)); + switch (option.frozen_string_literal) { + case ISEQ_FROZEN_STRING_LITERAL_UNSET: + break; + case ISEQ_FROZEN_STRING_LITERAL_DISABLED: + pm_options_frozen_string_literal_set(&result.options, false); + break; + case ISEQ_FROZEN_STRING_LITERAL_ENABLED: + pm_options_frozen_string_literal_set(&result.options, true); + break; + default: + rb_bug("pm_iseq_compile_with_option: invalid frozen_string_literal=%d", option.frozen_string_literal); + break; + } + VALUE error; if (RB_TYPE_P(src, T_FILE)) { VALUE filepath = rb_io_path(src); @@ -1602,6 +1624,7 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) VALUE file, opt = Qnil; VALUE parser, f, exc = Qnil, ret; rb_ast_t *ast; + VALUE ast_value; rb_compile_option_t option; int i; @@ -1620,7 +1643,8 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) parser = rb_parser_new(); rb_parser_set_context(parser, NULL, FALSE); - ast = (rb_ast_t *)rb_parser_load_file(parser, file); + ast_value = rb_parser_load_file(parser, file); + ast = rb_ruby_ast_data_get(ast_value); if (!ast->body.root) exc = GET_EC()->errinfo; rb_io_close(f); @@ -1631,7 +1655,7 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) make_compile_option(&option, opt); - ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("<main>"), + ret = iseqw_new(rb_iseq_new_with_opt(ast_value, rb_fstring_lit("<main>"), file, rb_realpath_internal(Qnil, file, 1), 1, NULL, 0, ISEQ_TYPE_TOP, &option, diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb index a340463c98..c933ad0471 100644 --- a/lib/bundled_gems.rb +++ b/lib/bundled_gems.rb @@ -28,6 +28,7 @@ module Gem::BUNDLED_GEMS "syslog" => "3.4.0", "ostruct" => "3.5.0", "pstore" => "3.5.0", + "rdoc" => "3.5.0", }.freeze EXACT = { @@ -45,6 +46,7 @@ module Gem::BUNDLED_GEMS "syslog" => true, "ostruct" => true, "pstore" => true, + "rdoc" => true, }.freeze PREFIXED = { @@ -101,8 +103,11 @@ module Gem::BUNDLED_GEMS def self.warning?(name, specs: nil) # name can be a feature name or a file path with String or Pathname feature = File.path(name) - # bootsnap expand `require "csv"` to `require "#{LIBDIR}/csv.rb"` - name = feature.delete_prefix(LIBDIR).chomp(".rb").tr("/", "-") + # bootsnap expands `require "csv"` to `require "#{LIBDIR}/csv.rb"`, + # and `require "syslog"` to `require "#{ARCHDIR}/syslog.so"`. + name = feature.delete_prefix(ARCHDIR) + name.delete_prefix!(LIBDIR) + name.tr!("/", "-") name.sub!(LIBEXT, "") return if specs.include?(name) _t, path = $:.resolve_feature_path(feature) diff --git a/lib/bundler.rb b/lib/bundler.rb index 59a1107bb7..5033109db6 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -40,6 +40,7 @@ module Bundler SUDO_MUTEX = Thread::Mutex.new autoload :Checksum, File.expand_path("bundler/checksum", __dir__) + autoload :CLI, File.expand_path("bundler/cli", __dir__) autoload :CIDetector, File.expand_path("bundler/ci_detector", __dir__) autoload :Definition, File.expand_path("bundler/definition", __dir__) autoload :Dependency, File.expand_path("bundler/dependency", __dir__) @@ -165,6 +166,25 @@ module Bundler end end + # Automatically install dependencies if Bundler.settings[:auto_install] exists. + # This is set through config cmd `bundle config set --global auto_install 1`. + # + # Note that this method `nil`s out the global Definition object, so it + # should be called first, before you instantiate anything like an + # `Installer` that'll keep a reference to the old one instead. + def auto_install + return unless settings[:auto_install] + + begin + definition.specs + rescue GemNotFound, GitError + ui.info "Automatically installing missing gems." + reset! + CLI::Install.new({}).run + reset! + end + end + # Setups Bundler environment (see Bundler.setup) if it is not already set, # and loads all gems from groups specified. Unlike ::setup, can be called # multiple times with different groups (if they were allowed by setup). @@ -184,6 +204,7 @@ module Bundler # Bundler.require(:test) # requires second_gem # def require(*groups) + load_plugins setup(*groups).require(*groups) end @@ -560,6 +581,23 @@ module Bundler @feature_flag ||= FeatureFlag.new(VERSION) end + def load_plugins(definition = Bundler.definition) + return if defined?(@load_plugins_ran) + + Bundler.rubygems.load_plugins + + requested_path_gems = definition.requested_specs.select {|s| s.source.is_a?(Source::Path) } + path_plugin_files = requested_path_gems.map do |spec| + Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}") + rescue TypeError + error_message = "#{spec.name} #{spec.version} has an invalid gemspec" + raise Gem::InvalidSpecificationException, error_message + end.flatten + Bundler.rubygems.load_plugin_files(path_plugin_files) + Bundler.rubygems.load_env_plugins + @load_plugins_ran = true + end + def reset! reset_paths! Plugin.reset! diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 8db725bbde..40f19c7fed 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -5,6 +5,7 @@ require_relative "vendored_thor" module Bundler class CLI < Thor require_relative "cli/common" + require_relative "cli/install" package_name "Bundler" @@ -69,7 +70,7 @@ module Bundler Bundler.settings.set_command_option_if_given :retry, options[:retry] current_cmd = args.last[:current_command].name - auto_install if AUTO_INSTALL_CMDS.include?(current_cmd) + Bundler.auto_install if AUTO_INSTALL_CMDS.include?(current_cmd) rescue UnknownArgumentError => e raise InvalidOption, e.message ensure @@ -114,6 +115,8 @@ module Bundler class_option "verbose", type: :boolean, desc: "Enable verbose output mode", aliases: "-V" def help(cli = nil) + cli = self.class.all_aliases[cli] if self.class.all_aliases[cli] + case cli when "gemfile" then command = "gemfile" when nil then command = "bundle" @@ -683,7 +686,6 @@ module Bundler exec_used = args.index {|a| exec_commands.include? a } command = args.find {|a| bundler_commands.include? a } - command = all_aliases[command] if all_aliases[command] if exec_used && help_used if exec_used + help_used == 1 @@ -736,26 +738,6 @@ module Bundler private - # Automatically invoke `bundle install` and resume if - # Bundler.settings[:auto_install] exists. This is set through config cmd - # `bundle config set --global auto_install 1`. - # - # Note that this method `nil`s out the global Definition object, so it - # should be called first, before you instantiate anything like an - # `Installer` that'll keep a reference to the old one instead. - def auto_install - return unless Bundler.settings[:auto_install] - - begin - Bundler.definition.specs - rescue GemNotFound, GitError - Bundler.ui.info "Automatically installing missing gems." - Bundler.reset! - invoke :install, [] - Bundler.reset! - end - end - def current_command _, _, config = @_initializer config[:current_command] diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index 6c102d537d..a233d5d2e5 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -14,7 +14,7 @@ module Bundler Bundler.self_manager.install_locked_bundler_and_restart_with_it_if_needed - Bundler::SharedHelpers.set_env "RB_USER_INSTALL", "1" if Bundler::FREEBSD + Bundler::SharedHelpers.set_env "RB_USER_INSTALL", "1" if Gem.freebsd_platform? # Disable color in deployment mode Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment] diff --git a/lib/bundler/constants.rb b/lib/bundler/constants.rb index de9698b577..9564771e78 100644 --- a/lib/bundler/constants.rb +++ b/lib/bundler/constants.rb @@ -1,7 +1,14 @@ # frozen_string_literal: true +require "rbconfig" + module Bundler WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/ + deprecate_constant :WINDOWS + FREEBSD = RbConfig::CONFIG["host_os"].to_s.include?("bsd") - NULL = File::NULL + deprecate_constant :FREEBSD + + NULL = File::NULL + deprecate_constant :NULL end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index c8faf77b3b..22070b6b17 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -92,11 +92,12 @@ module Bundler @platforms = @locked_platforms.dup @locked_bundler_version = @locked_gems.bundler_version @locked_ruby_version = @locked_gems.ruby_version + @originally_locked_deps = @locked_gems.dependencies @originally_locked_specs = SpecSet.new(@locked_gems.specs) @locked_checksums = @locked_gems.checksums if unlock != true - @locked_deps = @locked_gems.dependencies + @locked_deps = @originally_locked_deps @locked_specs = @originally_locked_specs @locked_sources = @locked_gems.sources else @@ -111,6 +112,7 @@ module Bundler @locked_gems = nil @locked_deps = {} @locked_specs = SpecSet.new([]) + @originally_locked_deps = {} @originally_locked_specs = @locked_specs @locked_sources = [] @locked_platforms = [] @@ -130,7 +132,7 @@ module Bundler @sources.merged_gem_lockfile_sections!(locked_gem_sources.first) end - @unlock[:sources] ||= [] + @sources_to_unlock = @unlock.delete(:sources) || [] @unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object @ruby_version.diff(locked_ruby_version_object) end @@ -142,11 +144,13 @@ module Bundler @path_changes = converge_paths @source_changes = converge_sources + @explicit_unlocks = @unlock.delete(:gems) || [] + if @unlock[:conservative] - @unlock[:gems] ||= @dependencies.map(&:name) + @gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name) else - eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") } - @unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq + eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") } + @gems_to_unlock = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq end @dependency_changes = converge_dependencies @@ -225,7 +229,6 @@ module Bundler @resolver = nil @resolution_packages = nil @specs = nil - @gem_version_promoter = nil Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})" true @@ -566,8 +569,10 @@ module Bundler @resolution_packages ||= begin last_resolve = converge_locked_specs remove_invalid_platforms!(current_dependencies) - packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlock[:gems], prerelease: gem_version_promoter.pre?) - additional_base_requirements_for_resolve(packages, last_resolve) + packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?) + packages = additional_base_requirements_to_prevent_downgrades(packages, last_resolve) + packages = additional_base_requirements_to_force_updates(packages) + packages end end @@ -671,14 +676,18 @@ module Bundler def change_reason if unlocking? - unlock_reason = @unlock.reject {|_k, v| Array(v).empty? }.map do |k, v| - if v == true - k.to_s - else - v = Array(v) - "#{k}: (#{v.join(", ")})" - end - end.join(", ") + unlock_targets = if @gems_to_unlock.any? + ["gems", @gems_to_unlock] + elsif @sources_to_unlock.any? + ["sources", @sources_to_unlock] + end + + unlock_reason = if unlock_targets + "#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})" + else + @unlock[:ruby] ? "ruby" : "" + end + return "bundler is unlocking #{unlock_reason}" end [ @@ -733,7 +742,7 @@ module Bundler spec = @dependencies.find {|s| s.name == k } source = spec&.source if source&.respond_to?(:local_override!) - source.unlock! if @unlock[:gems].include?(spec.name) + source.unlock! if @gems_to_unlock.include?(spec.name) locals << [source, source.local_override!(v)] end end @@ -741,7 +750,7 @@ module Bundler sources_with_changes = locals.select do |source, changed| changed || specs_changed?(source) end.map(&:first) - !sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty? + !sources_with_changes.each {|source| @sources_to_unlock << source.name }.empty? end def check_lockfile @@ -818,7 +827,7 @@ module Bundler # gem), unlock it. For git sources, this means to unlock the revision, which # will cause the `ref` used to be the most recent for the branch (or master) if # an explicit `ref` is not used. - if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name) + if source.respond_to?(:unlock!) && @sources_to_unlock.include?(source.name) source.unlock! changes = true end @@ -835,9 +844,7 @@ module Bundler dep.source = sources.get(dep.source) end - next if unlocking? - - unless locked_dep = @locked_deps[dep.name] + unless locked_dep = @originally_locked_deps[dep.name] changes = true next end @@ -864,7 +871,7 @@ module Bundler def converge_locked_specs converged = converge_specs(@locked_specs) - resolve = SpecSet.new(converged.reject {|s| @unlock[:gems].include?(s.name) }) + resolve = SpecSet.new(converged.reject {|s| @gems_to_unlock.include?(s.name) }) diff = nil @@ -897,7 +904,7 @@ module Bundler @specs_that_changed_sources << s if gemfile_source != lockfile_source deps << dep if !dep.source || lockfile_source.include?(dep.source) - @unlock[:gems] << name if lockfile_source.include?(dep.source) && lockfile_source != gemfile_source + @gems_to_unlock << name if lockfile_source.include?(dep.source) && lockfile_source != gemfile_source # Replace the locked dependency's source with the equivalent source from the Gemfile s.source = gemfile_source @@ -906,7 +913,7 @@ module Bundler s.source = default_source unless sources.get(lockfile_source) end - next if @unlock[:sources].include?(s.source.name) + next if @sources_to_unlock.include?(s.source.name) # Path sources have special logic if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec) @@ -928,12 +935,12 @@ module Bundler else # If the spec is no longer in the path source, unlock it. This # commonly happens if the version changed in the gemspec - @unlock[:gems] << name + @gems_to_unlock << name end end if dep.nil? && requested_dependencies.find {|d| name == d.name } - @unlock[:gems] << s.name + @gems_to_unlock << s.name else converged << s end @@ -1010,7 +1017,7 @@ module Bundler current == proposed end - def additional_base_requirements_for_resolve(resolution_packages, last_resolve) + def additional_base_requirements_to_prevent_downgrades(resolution_packages, last_resolve) return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources) converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec| next if locked_spec.source.is_a?(Source::Path) @@ -1019,6 +1026,28 @@ module Bundler resolution_packages end + def additional_base_requirements_to_force_updates(resolution_packages) + return resolution_packages if @explicit_unlocks.empty? + full_update = dup_for_full_unlock.resolve + @explicit_unlocks.each do |name| + version = full_update[name].first&.version + resolution_packages.base_requirements[name] = Gem::Requirement.new("= #{version}") if version + end + resolution_packages + end + + def dup_for_full_unlock + unlocked_definition = self.class.new(@lockfile, @dependencies, @sources, true, @ruby_version, @optional_groups, @gemfiles) + unlocked_definition.resolution_mode = { "local" => !@remote } + unlocked_definition.setup_sources_for_resolve + unlocked_definition.gem_version_promoter.tap do |gvp| + gvp.level = gem_version_promoter.level + gvp.strict = gem_version_promoter.strict + gvp.pre = gem_version_promoter.pre + end + unlocked_definition + end + def remove_invalid_platforms!(dependencies) return if Bundler.frozen_bundle? diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb index c4c1b53fa4..444ab6fd37 100644 --- a/lib/bundler/environment_preserver.rb +++ b/lib/bundler/environment_preserver.rb @@ -19,14 +19,7 @@ module Bundler BUNDLER_PREFIX = "BUNDLER_ORIG_" def self.from_env - new(env_to_hash(ENV), BUNDLER_KEYS) - end - - def self.env_to_hash(env) - to_hash = env.to_hash - return to_hash unless Gem.win_platform? - - to_hash.each_with_object({}) {|(k,v), a| a[k.upcase] = v } + new(ENV.to_hash, BUNDLER_KEYS) end # @param env [Hash] @@ -39,18 +32,7 @@ module Bundler # Replaces `ENV` with the bundler environment variables backed up def replace_with_backup - unless Gem.win_platform? - ENV.replace(backup) - return - end - - # Fallback logic for Windows below to workaround - # https://bugs.ruby-lang.org/issues/16798. Can be dropped once all - # supported rubies include the fix for that. - - ENV.clear - - backup.each {|k, v| ENV[k] = v } + ENV.replace(backup) end # @return [Hash] diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb index b6a11cc721..c29b1bfed8 100644 --- a/lib/bundler/errors.rb +++ b/lib/bundler/errors.rb @@ -230,4 +230,18 @@ module Bundler status_code(38) end + + class CorruptBundlerInstallError < BundlerError + def initialize(loaded_spec) + @loaded_spec = loaded_spec + end + + def message + "The running version of Bundler (#{Bundler::VERSION}) does not match the version of the specification installed for it (#{@loaded_spec.version}). " \ + "This can be caused by reinstalling Ruby without removing previous installation, leaving around an upgraded default version of Bundler. " \ + "Reinstalling Ruby from scratch should fix the problem." + end + + status_code(39) + end end diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 018324f840..72e5602cc3 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -81,7 +81,7 @@ module Bundler if resolve_if_needed(options) ensure_specs_are_compatible! - load_plugins + Bundler.load_plugins(@definition) options.delete(:jobs) else options[:jobs] = 1 # to avoid the overhead of Bundler::Worker @@ -213,20 +213,6 @@ module Bundler Bundler.settings.processor_count end - def load_plugins - Gem.load_plugins - - requested_path_gems = @definition.requested_specs.select {|s| s.source.is_a?(Source::Path) } - path_plugin_files = requested_path_gems.map do |spec| - Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}") - rescue TypeError - error_message = "#{spec.name} #{spec.version} has an invalid gemspec" - raise Gem::InvalidSpecificationException, error_message - end.flatten - Gem.load_plugin_files(path_plugin_files) - Gem.load_env_plugins - end - def ensure_specs_are_compatible! @definition.specs.each do |spec| unless spec.matches_current_ruby? diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1 index a6cbc88f34..56a3b6f85c 100644 --- a/lib/bundler/man/bundle-add.1 +++ b/lib/bundler/man/bundle-add.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-ADD" "1" "March 2024" "" +.TH "BUNDLE\-ADD" "1" "May 2024" "" .SH "NAME" \fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1 index 2b35bc956a..4ec301951f 100644 --- a/lib/bundler/man/bundle-binstubs.1 +++ b/lib/bundler/man/bundle-binstubs.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-BINSTUBS" "1" "March 2024" "" +.TH "BUNDLE\-BINSTUBS" "1" "May 2024" "" .SH "NAME" \fBbundle\-binstubs\fR \- Install the binstubs of the listed gems .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1 index 3b86b995a6..e2da1269e6 100644 --- a/lib/bundler/man/bundle-cache.1 +++ b/lib/bundler/man/bundle-cache.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-CACHE" "1" "March 2024" "" +.TH "BUNDLE\-CACHE" "1" "May 2024" "" .SH "NAME" \fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1 index 7f18e26537..dee1af1326 100644 --- a/lib/bundler/man/bundle-check.1 +++ b/lib/bundler/man/bundle-check.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-CHECK" "1" "March 2024" "" +.TH "BUNDLE\-CHECK" "1" "May 2024" "" .SH "NAME" \fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems .SH "SYNOPSIS" @@ -9,6 +9,8 @@ \fBcheck\fR searches the local machine for each of the gems requested in the Gemfile\. If all gems are found, Bundler prints a success message and exits with a status of 0\. .P If not, the first missing gem is listed and Bundler exits status 1\. +.P +If the lockfile needs to be updated then it will be resolved using the gems installed on the local machine, if they satisfy the requirements\. .SH "OPTIONS" .TP \fB\-\-dry\-run\fR diff --git a/lib/bundler/man/bundle-check.1.ronn b/lib/bundler/man/bundle-check.1.ronn index f2846b8ff2..eb3ff1daf9 100644 --- a/lib/bundler/man/bundle-check.1.ronn +++ b/lib/bundler/man/bundle-check.1.ronn @@ -15,6 +15,9 @@ a status of 0. If not, the first missing gem is listed and Bundler exits status 1. +If the lockfile needs to be updated then it will be resolved using the gems +installed on the local machine, if they satisfy the requirements. + ## OPTIONS * `--dry-run`: diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1 index 0180eb38a2..7c7f9b5c77 100644 --- a/lib/bundler/man/bundle-clean.1 +++ b/lib/bundler/man/bundle-clean.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-CLEAN" "1" "March 2024" "" +.TH "BUNDLE\-CLEAN" "1" "May 2024" "" .SH "NAME" \fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1 index b768f1e3d2..a207dbbcaa 100644 --- a/lib/bundler/man/bundle-config.1 +++ b/lib/bundler/man/bundle-config.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-CONFIG" "1" "March 2024" "" +.TH "BUNDLE\-CONFIG" "1" "May 2024" "" .SH "NAME" \fBbundle\-config\fR \- Set bundler configuration options .SH "SYNOPSIS" @@ -95,8 +95,6 @@ Any periods in the configuration keys must be replaced with two underscores when .SH "LIST OF AVAILABLE KEYS" The following is a list of all configuration keys and their purpose\. You can learn more about their operation in bundle install(1) \fIbundle\-install\.1\.html\fR\. .IP "\(bu" 4 -\fBallow_deployment_source_credential_changes\fR (\fBBUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES\fR): When in deployment mode, allow changing the credentials to a gem's source\. Ex: \fBhttps://some\.host\.com/gems/path/\fR \-> \fBhttps://user_name:password@some\.host\.com/gems/path\fR -.IP "\(bu" 4 \fBallow_offline_install\fR (\fBBUNDLE_ALLOW_OFFLINE_INSTALL\fR): Allow Bundler to use cached data when installing without network access\. .IP "\(bu" 4 \fBauto_clean_without_path\fR (\fBBUNDLE_AUTO_CLEAN_WITHOUT_PATH\fR): Automatically run \fBbundle clean\fR after installing when an explicit \fBpath\fR has not been set and Bundler is not installing into the system gems\. diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn index 587d31dbad..7e5f458fb2 100644 --- a/lib/bundler/man/bundle-config.1.ronn +++ b/lib/bundler/man/bundle-config.1.ronn @@ -137,9 +137,6 @@ the environment variable `BUNDLE_LOCAL__RACK`. The following is a list of all configuration keys and their purpose. You can learn more about their operation in [bundle install(1)](bundle-install.1.html). -* `allow_deployment_source_credential_changes` (`BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES`): - When in deployment mode, allow changing the credentials to a gem's source. - Ex: `https://some.host.com/gems/path/` -> `https://user_name:password@some.host.com/gems/path` * `allow_offline_install` (`BUNDLE_ALLOW_OFFLINE_INSTALL`): Allow Bundler to use cached data when installing without network access. * `auto_clean_without_path` (`BUNDLE_AUTO_CLEAN_WITHOUT_PATH`): diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1 index 1368a50eb1..dca18ec43d 100644 --- a/lib/bundler/man/bundle-console.1 +++ b/lib/bundler/man/bundle-console.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-CONSOLE" "1" "March 2024" "" +.TH "BUNDLE\-CONSOLE" "1" "May 2024" "" .SH "NAME" \fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1 index 80eaf2a888..6489cc07f7 100644 --- a/lib/bundler/man/bundle-doctor.1 +++ b/lib/bundler/man/bundle-doctor.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-DOCTOR" "1" "March 2024" "" +.TH "BUNDLE\-DOCTOR" "1" "May 2024" "" .SH "NAME" \fBbundle\-doctor\fR \- Checks the bundle for common problems .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1 index 191863c045..1548d29670 100644 --- a/lib/bundler/man/bundle-exec.1 +++ b/lib/bundler/man/bundle-exec.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-EXEC" "1" "March 2024" "" +.TH "BUNDLE\-EXEC" "1" "May 2024" "" .SH "NAME" \fBbundle\-exec\fR \- Execute a command in the context of the bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1 index 464d8d1126..5df7b0ef2f 100644 --- a/lib/bundler/man/bundle-gem.1 +++ b/lib/bundler/man/bundle-gem.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-GEM" "1" "March 2024" "" +.TH "BUNDLE\-GEM" "1" "May 2024" "" .SH "NAME" \fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1 index 3604ad6127..a3e7c7770d 100644 --- a/lib/bundler/man/bundle-help.1 +++ b/lib/bundler/man/bundle-help.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-HELP" "1" "March 2024" "" +.TH "BUNDLE\-HELP" "1" "May 2024" "" .SH "NAME" \fBbundle\-help\fR \- Displays detailed help for each subcommand .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1 index 647f5987be..a3d7ff0988 100644 --- a/lib/bundler/man/bundle-info.1 +++ b/lib/bundler/man/bundle-info.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-INFO" "1" "March 2024" "" +.TH "BUNDLE\-INFO" "1" "May 2024" "" .SH "NAME" \fBbundle\-info\fR \- Show information for the given gem in your bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1 index 2c41a3c7de..a0edaaa18f 100644 --- a/lib/bundler/man/bundle-init.1 +++ b/lib/bundler/man/bundle-init.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-INIT" "1" "March 2024" "" +.TH "BUNDLE\-INIT" "1" "May 2024" "" .SH "NAME" \fBbundle\-init\fR \- Generates a Gemfile into the current working directory .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1 index c7269db34d..7a1038206e 100644 --- a/lib/bundler/man/bundle-inject.1 +++ b/lib/bundler/man/bundle-inject.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-INJECT" "1" "March 2024" "" +.TH "BUNDLE\-INJECT" "1" "May 2024" "" .SH "NAME" \fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1 index 3fa1a467e2..cc46a03b7f 100644 --- a/lib/bundler/man/bundle-install.1 +++ b/lib/bundler/man/bundle-install.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-INSTALL" "1" "March 2024" "" +.TH "BUNDLE\-INSTALL" "1" "May 2024" "" .SH "NAME" \fBbundle\-install\fR \- Install the dependencies specified in your Gemfile .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1 index f91fd95739..21723608cc 100644 --- a/lib/bundler/man/bundle-list.1 +++ b/lib/bundler/man/bundle-list.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-LIST" "1" "March 2024" "" +.TH "BUNDLE\-LIST" "1" "May 2024" "" .SH "NAME" \fBbundle\-list\fR \- List all the gems in the bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1 index f992f5ee5f..8b81b7732f 100644 --- a/lib/bundler/man/bundle-lock.1 +++ b/lib/bundler/man/bundle-lock.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-LOCK" "1" "March 2024" "" +.TH "BUNDLE\-LOCK" "1" "May 2024" "" .SH "NAME" \fBbundle\-lock\fR \- Creates / Updates a lockfile without installing .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1 index 53d3541555..41a8804a09 100644 --- a/lib/bundler/man/bundle-open.1 +++ b/lib/bundler/man/bundle-open.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-OPEN" "1" "March 2024" "" +.TH "BUNDLE\-OPEN" "1" "May 2024" "" .SH "NAME" \fBbundle\-open\fR \- Opens the source directory for a gem in your bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1 index f79eff5ae9..838fce45cd 100644 --- a/lib/bundler/man/bundle-outdated.1 +++ b/lib/bundler/man/bundle-outdated.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-OUTDATED" "1" "March 2024" "" +.TH "BUNDLE\-OUTDATED" "1" "May 2024" "" .SH "NAME" \fBbundle\-outdated\fR \- List installed gems with newer versions available .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1 index d2133ec4d3..0dbce7a7a4 100644 --- a/lib/bundler/man/bundle-platform.1 +++ b/lib/bundler/man/bundle-platform.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-PLATFORM" "1" "March 2024" "" +.TH "BUNDLE\-PLATFORM" "1" "May 2024" "" .SH "NAME" \fBbundle\-platform\fR \- Displays platform compatibility information .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1 index cbdfac11b6..1290abb3ba 100644 --- a/lib/bundler/man/bundle-plugin.1 +++ b/lib/bundler/man/bundle-plugin.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-PLUGIN" "1" "March 2024" "" +.TH "BUNDLE\-PLUGIN" "1" "May 2024" "" .SH "NAME" \fBbundle\-plugin\fR \- Manage Bundler plugins .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1 index faa04d7676..bf4a7cd323 100644 --- a/lib/bundler/man/bundle-pristine.1 +++ b/lib/bundler/man/bundle-pristine.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-PRISTINE" "1" "March 2024" "" +.TH "BUNDLE\-PRISTINE" "1" "May 2024" "" .SH "NAME" \fBbundle\-pristine\fR \- Restores installed gems to their pristine condition .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1 index 3f8cbbd9b6..c3c96b416d 100644 --- a/lib/bundler/man/bundle-remove.1 +++ b/lib/bundler/man/bundle-remove.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-REMOVE" "1" "March 2024" "" +.TH "BUNDLE\-REMOVE" "1" "May 2024" "" .SH "NAME" \fBbundle\-remove\fR \- Removes gems from the Gemfile .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1 index bc72c6e3b6..c054875efd 100644 --- a/lib/bundler/man/bundle-show.1 +++ b/lib/bundler/man/bundle-show.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-SHOW" "1" "March 2024" "" +.TH "BUNDLE\-SHOW" "1" "May 2024" "" .SH "NAME" \fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1 index d1284c2e72..acd80ec75c 100644 --- a/lib/bundler/man/bundle-update.1 +++ b/lib/bundler/man/bundle-update.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-UPDATE" "1" "March 2024" "" +.TH "BUNDLE\-UPDATE" "1" "May 2024" "" .SH "NAME" \fBbundle\-update\fR \- Update your gems to the latest available versions .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1 index 05905e1347..44eaf92224 100644 --- a/lib/bundler/man/bundle-version.1 +++ b/lib/bundler/man/bundle-version.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-VERSION" "1" "March 2024" "" +.TH "BUNDLE\-VERSION" "1" "May 2024" "" .SH "NAME" \fBbundle\-version\fR \- Prints Bundler version information .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1 index 681563cd4c..77b902214c 100644 --- a/lib/bundler/man/bundle-viz.1 +++ b/lib/bundler/man/bundle-viz.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE\-VIZ" "1" "March 2024" "" +.TH "BUNDLE\-VIZ" "1" "May 2024" "" .SH "NAME" \fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1 index 1d2c780060..199d1ce9fd 100644 --- a/lib/bundler/man/bundle.1 +++ b/lib/bundler/man/bundle.1 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "BUNDLE" "1" "March 2024" "" +.TH "BUNDLE" "1" "May 2024" "" .SH "NAME" \fBbundle\fR \- Ruby Dependency Management .SH "SYNOPSIS" diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index 39503f22a6..fa9e31f615 100644 --- a/lib/bundler/man/gemfile.5 +++ b/lib/bundler/man/gemfile.5 @@ -1,6 +1,6 @@ .\" generated with nRonn/v0.11.1 .\" https://github.com/n-ronn/nronn/tree/0.11.1 -.TH "GEMFILE" "5" "March 2024" "" +.TH "GEMFILE" "5" "May 2024" "" .SH "NAME" \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs .SH "SYNOPSIS" diff --git a/lib/bundler/plugin/events.rb b/lib/bundler/plugin/events.rb index bc037d1af5..29c05098ae 100644 --- a/lib/bundler/plugin/events.rb +++ b/lib/bundler/plugin/events.rb @@ -56,6 +56,30 @@ module Bundler # Includes an Array of Bundler::Dependency objects # GEM_AFTER_INSTALL_ALL = "after-install-all" define :GEM_AFTER_INSTALL_ALL, "after-install-all" + + # @!parse + # A hook called before each individual gem is required + # Includes a Bundler::Dependency. + # GEM_BEFORE_REQUIRE = "before-require" + define :GEM_BEFORE_REQUIRE, "before-require" + + # @!parse + # A hook called after each individual gem is required + # Includes a Bundler::Dependency. + # GEM_AFTER_REQUIRE = "after-require" + define :GEM_AFTER_REQUIRE, "after-require" + + # @!parse + # A hook called before any gems require + # Includes an Array of Bundler::Dependency objects. + # GEM_BEFORE_REQUIRE_ALL = "before-require-all" + define :GEM_BEFORE_REQUIRE_ALL, "before-require-all" + + # @!parse + # A hook called after all gems required + # Includes an Array of Bundler::Dependency objects. + # GEM_AFTER_REQUIRE_ALL = "after-require-all" + define :GEM_AFTER_REQUIRE_ALL, "after-require-all" end end end diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index e0582beba2..18180a81a1 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -1,11 +1,7 @@ # frozen_string_literal: true -require "pathname" - require "rubygems" unless defined?(Gem) -require "rubygems/specification" - # We can't let `Gem::Source` be autoloaded in the `Gem::Specification#source` # redefinition below, so we need to load it upfront. The reason is that if # Bundler monkeypatches are loaded before RubyGems activates an executable (for @@ -17,10 +13,6 @@ require "rubygems/specification" # `Gem::Source` from the redefined `Gem::Specification#source`. require "rubygems/source" -require_relative "match_metadata" -require_relative "force_platform" -require_relative "match_platform" - # Cherry-pick fixes to `Gem.ruby_version` to be useful for modern Bundler # versions and ignore patchlevels # (https://github.com/rubygems/rubygems/pull/5472, @@ -31,7 +23,19 @@ unless Gem.ruby_version.to_s == RUBY_VERSION || RUBY_PATCHLEVEL == -1 end module Gem + # Can be removed once RubyGems 3.5.11 support is dropped + unless Gem.respond_to?(:freebsd_platform?) + def self.freebsd_platform? + RbConfig::CONFIG["host_os"].to_s.include?("bsd") + end + end + + require "rubygems/specification" + class Specification + require_relative "match_metadata" + require_relative "match_platform" + include ::Bundler::MatchMetadata include ::Bundler::MatchPlatform @@ -48,7 +52,7 @@ module Gem def full_gem_path if source.respond_to?(:root) - Pathname.new(loaded_from).dirname.expand_path(source.root).to_s + File.expand_path(File.dirname(loaded_from), source.root) else rg_full_gem_path end @@ -146,7 +150,23 @@ module Gem end end + module BetterPermissionError + def data + super + rescue Errno::EACCES + raise Bundler::PermissionError.new(loaded_from, :read) + end + end + + require "rubygems/stub_specification" + + class StubSpecification + prepend BetterPermissionError + end + class Dependency + require_relative "force_platform" + include ::Bundler::ForcePlatform attr_accessor :source, :groups diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index da555681f9..494030eab2 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -156,6 +156,18 @@ module Bundler loaded_gem_paths.flatten end + def load_plugins + Gem.load_plugins + end + + def load_plugin_files(plugin_files) + Gem.load_plugin_files(plugin_files) + end + + def load_env_plugins + Gem.load_env_plugins + end + def ui=(obj) Gem::DefaultUserInteraction.ui = obj end diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb index ec772cfe7b..54aa30ce0b 100644 --- a/lib/bundler/runtime.rb +++ b/lib/bundler/runtime.rb @@ -41,12 +41,17 @@ module Bundler groups.map!(&:to_sym) groups = [:default] if groups.empty? - @definition.dependencies.each do |dep| - # Skip the dependency if it is not in any of the requested groups, or - # not for the current platform, or doesn't match the gem constraints. - next unless (dep.groups & groups).any? && dep.should_include? + dependencies = @definition.dependencies.select do |dep| + # Select the dependency if it is in any of the requested groups, and + # for the current platform, and matches the gem constraints. + (dep.groups & groups).any? && dep.should_include? + end + + Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE_ALL, dependencies) + dependencies.each do |dep| required_file = nil + Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE, dep) begin # Loop through all the specified autorequires for the @@ -76,7 +81,13 @@ module Bundler end end end + + Plugin.hook(Plugin::Events::GEM_AFTER_REQUIRE, dep) end + + Plugin.hook(Plugin::Events::GEM_AFTER_REQUIRE_ALL, dependencies) + + dependencies end def self.definition_method(meth) diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 379abfb24a..abbaec9783 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -7,7 +7,6 @@ module Bundler autoload :Validator, File.expand_path("settings/validator", __dir__) BOOL_KEYS = %w[ - allow_deployment_source_credential_changes allow_offline_install auto_clean_without_path auto_install diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb index 7131d15055..6010d66742 100644 --- a/lib/bundler/setup.rb +++ b/lib/bundler/setup.rb @@ -5,6 +5,9 @@ require_relative "shared_helpers" if Bundler::SharedHelpers.in_bundle? require_relative "../bundler" + # try to auto_install first before we get to the `Bundler.ui.silence`, so user knows what is happening + Bundler.auto_install + if STDOUT.tty? || ENV["BUNDLER_FORCE_TTY"] begin Bundler.ui.silence { Bundler.setup } diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index 78760e6fa4..28f0cdff19 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -1,15 +1,17 @@ # frozen_string_literal: true -require "pathname" -require "rbconfig" - require_relative "version" -require_relative "constants" require_relative "rubygems_integration" require_relative "current_ruby" module Bundler + autoload :WINDOWS, File.expand_path("constants", __dir__) + autoload :FREEBSD, File.expand_path("constants", __dir__) + autoload :NULL, File.expand_path("constants", __dir__) + module SharedHelpers + autoload :Pathname, "pathname" + def root gemfile = find_gemfile raise GemfileNotFound, "Could not locate Gemfile" unless gemfile diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index 4d27761365..6b05e17727 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -11,6 +11,8 @@ module Bundler end if local_spec = Gem.loaded_specs["bundler"] + raise CorruptBundlerInstallError.new(local_spec) if local_spec.version.to_s != Bundler::VERSION + idx << local_spec else idx << Gem::Specification.new do |s| diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 04cfc0a850..2e76becb84 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -10,7 +10,7 @@ module Bundler # Ask for X gems per API request API_REQUEST_SIZE = 50 - attr_reader :remotes + attr_accessor :remotes def initialize(options = {}) @options = options @@ -96,7 +96,7 @@ module Bundler def to_lock out = String.new("GEM\n") remotes.reverse_each do |remote| - out << " remote: #{suppress_configured_credentials remote}\n" + out << " remote: #{remove_auth remote}\n" end out << " specs:\n" end @@ -312,11 +312,7 @@ module Bundler end def credless_remotes - if Bundler.settings[:allow_deployment_source_credential_changes] - remotes.map(&method(:remove_auth)) - else - remotes.map(&method(:suppress_configured_credentials)) - end + remotes.map(&method(:remove_auth)) end def remotes_for_spec(spec) @@ -355,15 +351,6 @@ module Bundler uri end - def suppress_configured_credentials(remote) - remote_nouser = remove_auth(remote) - if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser] - remote_nouser - else - remote - end - end - def remove_auth(remote) if remote.user || remote.password remote.dup.tap {|uri| uri.user = uri.password = nil }.to_s diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index d85e1c1c01..bbaac33a95 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -157,7 +157,11 @@ module Bundler end def map_sources(replacement_sources) - rubygems, git, plugin = [@rubygems_sources, @git_sources, @plugin_sources].map do |sources| + rubygems = @rubygems_sources.map do |source| + replace_rubygems_source(replacement_sources, source) || source + end + + git, plugin = [@git_sources, @plugin_sources].map do |sources| sources.map do |source| replacement_sources.find {|s| s == source } || source end @@ -171,13 +175,22 @@ module Bundler end def global_replacement_source(replacement_sources) - replacement_source = replacement_sources.find {|s| s == global_rubygems_source } + replacement_source = replace_rubygems_source(replacement_sources, global_rubygems_source) return global_rubygems_source unless replacement_source replacement_source.cached! replacement_source end + def replace_rubygems_source(replacement_sources, gemfile_source) + replacement_source = replacement_sources.find {|s| s == gemfile_source } + return unless replacement_source + + # locked sources never include credentials so always prefer remotes from the gemfile + replacement_source.remotes = gemfile_source.remotes + replacement_source + end + def different_sources?(lock_sources, replacement_sources) !equivalent_sources?(lock_sources, replacement_sources) end diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index 2933d28450..8e1130e40e 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -129,6 +129,7 @@ module Bundler def materialized_for_all_platforms @specs.map do |s| next s unless s.is_a?(LazySpecification) + s.source.cached! s.source.remote! spec = s.materialize_for_installation raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec diff --git a/lib/did_you_mean/did_you_mean.gemspec b/lib/did_you_mean/did_you_mean.gemspec index 8fe5723129..be4ac76b4b 100644 --- a/lib/did_you_mean/did_you_mean.gemspec +++ b/lib/did_you_mean/did_you_mean.gemspec @@ -22,6 +22,4 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.required_ruby_version = '>= 2.5.0' - - spec.add_development_dependency "rake" end diff --git a/lib/find.gemspec b/lib/find.gemspec index cb845e9409..aef24a5028 100644 --- a/lib/find.gemspec +++ b/lib/find.gemspec @@ -25,7 +25,5 @@ Gem::Specification.new do |spec| spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] end diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index c1073ecd2b..dbb213c90a 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -227,6 +227,12 @@ class IPAddr return str end + # Returns a string containing the IP address representation in + # cidr notation + def cidr + format("%s/%s", to_s, prefix) + end + # Returns a network byte ordered string form of the IP address. def hton case @family diff --git a/lib/irb.rb b/lib/irb.rb index ab50c797c7..b3435c257e 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -311,7 +311,9 @@ require_relative "irb/pager" # ### Input Method # # The IRB input method determines how command input is to be read; by default, -# the input method for a session is IRB::RelineInputMethod. +# the input method for a session is IRB::RelineInputMethod. Unless the +# value of the TERM environment variable is 'dumb', in which case the +# most simplistic input method is used. # # You can set the input method by: # @@ -329,7 +331,8 @@ require_relative "irb/pager" # IRB::ReadlineInputMethod. # * `--nosingleline` or `--multiline` sets the input method to # IRB::RelineInputMethod. -# +# * `--nosingleline` together with `--nomultiline` sets the +# input to IRB::StdioInputMethod. # # # Method `conf.use_multiline?` and its synonym `conf.use_reline` return: @@ -656,8 +659,10 @@ require_relative "irb/pager" # * `%m`: the value of `self.to_s`. # * `%M`: the value of `self.inspect`. # * `%l`: an indication of the type of string; one of `"`, `'`, `/`, `]`. -# * `*NN*i`: Indentation level. -# * `*NN*n`: Line number. +# * `%NNi`: Indentation level. NN is a 2-digit number that specifies the number +# of digits of the indentation level (03 will result in 001). +# * `%NNn`: Line number. NN is a 2-digit number that specifies the number +# of digits of the line number (03 will result in 001). # * `%%`: Literal `%`. # # @@ -926,8 +931,11 @@ module IRB # The lexer used by this irb session attr_accessor :scanner + attr_reader :from_binding + # Creates a new irb session - def initialize(workspace = nil, input_method = nil) + def initialize(workspace = nil, input_method = nil, from_binding: false) + @from_binding = from_binding @context = Context.new(self, workspace, input_method) @context.workspace.load_helper_methods_to_main @signal_status = :IN_IRB @@ -960,20 +968,26 @@ module IRB # # Irb#eval_input will simply return the input, and we need to pass it to the # debugger. - input = if IRB.conf[:SAVE_HISTORY] && context.io.support_history_saving? - # Previous IRB session's history has been saved when `Irb#run` is exited We need - # to make sure the saved history is not saved again by resetting the counter - context.io.reset_history_counter + input = nil + forced_exit = catch(:IRB_EXIT) do + if IRB.conf[:SAVE_HISTORY] && context.io.support_history_saving? + # Previous IRB session's history has been saved when `Irb#run` is exited We need + # to make sure the saved history is not saved again by resetting the counter + context.io.reset_history_counter - begin - eval_input - ensure - context.io.save_history + begin + input = eval_input + ensure + context.io.save_history + end + else + input = eval_input end - else - eval_input + false end + Kernel.exit if forced_exit + if input&.include?("\n") @line_no += input.count("\n") - 1 end @@ -984,6 +998,7 @@ module IRB def run(conf = IRB.conf) in_nested_session = !!conf[:MAIN_CONTEXT] conf[:IRB_RC].call(context) if conf[:IRB_RC] + prev_context = conf[:MAIN_CONTEXT] conf[:MAIN_CONTEXT] = context save_history = !in_nested_session && conf[:SAVE_HISTORY] && context.io.support_history_saving? @@ -1006,6 +1021,9 @@ module IRB eval_input end ensure + # Do not restore to nil. It will cause IRB crash when used with threads. + IRB.conf[:MAIN_CONTEXT] = prev_context if prev_context + RubyVM.keep_script_lines = keep_script_lines_backup if defined?(RubyVM.keep_script_lines) trap("SIGINT", prev_trap) conf[:AT_EXIT].each{|hook| hook.call} @@ -1112,7 +1130,7 @@ module IRB code.force_encoding(@context.io.encoding) if (command, arg = parse_command(code)) - command_class = ExtendCommandBundle.load_command(command) + command_class = Command.load_command(command) Statement::Command.new(code, command_class, arg) else is_assignment_expression = @scanner.assignment_expression?(code, local_variables: @context.local_variables) @@ -1134,7 +1152,7 @@ module IRB # Check visibility public_method = !!Kernel.instance_method(:public_method).bind_call(@context.main, command) rescue false private_method = !public_method && !!Kernel.instance_method(:method).bind_call(@context.main, command) rescue false - if ExtendCommandBundle.execute_as_command?(command, public_method: public_method, private_method: private_method) + if Command.execute_as_command?(command, public_method: public_method, private_method: private_method) [command, arg] end end @@ -1230,27 +1248,33 @@ module IRB irb_bug = true else irb_bug = false - # This is mostly to make IRB work nicely with Rails console's backtrace filtering, which patches WorkSpace#filter_backtrace - # In such use case, we want to filter the exception's backtrace before its displayed through Exception#full_message - # And we clone the exception object in order to avoid mutating the original exception - # TODO: introduce better API to expose exception backtrace externally - backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact + # To support backtrace filtering while utilizing Exception#full_message, we need to clone + # the exception to avoid modifying the original exception's backtrace. exc = exc.clone - exc.set_backtrace(backtrace) - end + filtered_backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact + backtrace_filter = IRB.conf[:BACKTRACE_FILTER] - if RUBY_VERSION < '3.0.0' - if STDOUT.tty? - message = exc.full_message(order: :bottom) - order = :bottom - else - message = exc.full_message(order: :top) - order = :top + if backtrace_filter + if backtrace_filter.respond_to?(:call) + filtered_backtrace = backtrace_filter.call(filtered_backtrace) + else + warn "IRB.conf[:BACKTRACE_FILTER] #{backtrace_filter} should respond to `call` method" + end end - else # '3.0.0' <= RUBY_VERSION - message = exc.full_message(order: :top) - order = :top + + exc.set_backtrace(filtered_backtrace) end + + highlight = Color.colorable? + + order = + if RUBY_VERSION < '3.0.0' + STDOUT.tty? ? :bottom : :top + else # '3.0.0' <= RUBY_VERSION + :top + end + + message = exc.full_message(order: order, highlight: highlight) message = convert_invalid_byte_sequence(message, exc.message.encoding) message = encode_with_invalid_byte_sequence(message, IRB.conf[:LC_MESSAGES].encoding) unless message.encoding.to_s.casecmp?(IRB.conf[:LC_MESSAGES].encoding.to_s) message = message.gsub(/((?:^\t.+$\n)+)/) { |m| @@ -1455,7 +1479,7 @@ module IRB end def format_prompt(format, ltype, indent, line_no) # :nodoc: - format.gsub(/%([0-9]+)?([a-zA-Z])/) do + format.gsub(/%([0-9]+)?([a-zA-Z%])/) do case $2 when "N" @context.irb_name @@ -1488,7 +1512,7 @@ module IRB line_no.to_s end when "%" - "%" + "%" unless $1 end end end @@ -1575,7 +1599,7 @@ class Binding else # If we're not in a debugger session, create a new IRB instance with the current # workspace - binding_irb = IRB::Irb.new(workspace) + binding_irb = IRB::Irb.new(workspace, from_binding: true) binding_irb.context.irb_path = irb_path binding_irb.run(IRB.conf) binding_irb.debug_break diff --git a/lib/irb/color.rb b/lib/irb/color.rb index ad8670160c..fca942b28b 100644 --- a/lib/irb/color.rb +++ b/lib/irb/color.rb @@ -79,12 +79,12 @@ module IRB # :nodoc: class << self def colorable? - supported = $stdout.tty? && (/mswin|mingw/ =~ RUBY_PLATFORM || (ENV.key?('TERM') && ENV['TERM'] != 'dumb')) + supported = $stdout.tty? && (/mswin|mingw/.match?(RUBY_PLATFORM) || (ENV.key?('TERM') && ENV['TERM'] != 'dumb')) # because ruby/debug also uses irb's color module selectively, # irb won't be activated in that case. if IRB.respond_to?(:conf) - supported && IRB.conf.fetch(:USE_COLORIZE, true) + supported && !!IRB.conf.fetch(:USE_COLORIZE, true) else supported end diff --git a/lib/irb/command.rb b/lib/irb/command.rb index 19fde56356..68a4b52727 100644 --- a/lib/irb/command.rb +++ b/lib/irb/command.rb @@ -16,13 +16,7 @@ module IRB # :nodoc: # Registers a command with the given name. # Aliasing is intentionally not supported at the moment. def register(name, command_class) - @commands[name] = [command_class, []] - end - - # This API is for IRB's internal use only and may change at any time. - # Please do NOT use it. - def _register_with_aliases(name, command_class, *aliases) - @commands[name] = [command_class, aliases] + @commands[name.to_sym] = [command_class, []] end end end diff --git a/lib/irb/command/base.rb b/lib/irb/command/base.rb index b078b48237..1d406630a2 100644 --- a/lib/irb/command/base.rb +++ b/lib/irb/command/base.rb @@ -18,12 +18,12 @@ module IRB class << self def category(category = nil) @category = category if category - @category + @category || "No category" end def description(description = nil) @description = description if description - @description + @description || "No description provided." end def help_message(help_message = nil) diff --git a/lib/irb/command/chws.rb b/lib/irb/command/chws.rb index e0a406885f..ef456d0961 100644 --- a/lib/irb/command/chws.rb +++ b/lib/irb/command/chws.rb @@ -15,7 +15,7 @@ module IRB description "Show the current workspace." def execute(_arg) - irb_context.main + puts "Current workspace: #{irb_context.main}" end end @@ -30,7 +30,8 @@ module IRB obj = eval(arg, irb_context.workspace.binding) irb_context.change_workspace(obj) end - irb_context.main + + puts "Current workspace: #{irb_context.main}" end end end diff --git a/lib/irb/command/debug.rb b/lib/irb/command/debug.rb index f9aca0a672..8a091a49ed 100644 --- a/lib/irb/command/debug.rb +++ b/lib/irb/command/debug.rb @@ -8,11 +8,6 @@ module IRB category "Debugging" description "Start the debugger of debug.gem." - BINDING_IRB_FRAME_REGEXPS = [ - '<internal:prelude>', - binding.method(:irb).source_location.first, - ].map { |file| /\A#{Regexp.escape(file)}:\d+:in (`|'Binding#)irb'\z/ } - def execute(_arg) execute_debug_command end @@ -36,7 +31,7 @@ module IRB # 3. Insert a debug breakpoint at `Irb#debug_break` with the intended command. # 4. Exit the current Irb#run call via `throw :IRB_EXIT`. # 5. `Irb#debug_break` will be called and trigger the breakpoint, which will run the intended command. - unless binding_irb? + unless irb_context.from_binding? puts "Debugging commands are only available when IRB is started with binding.irb" return end @@ -60,16 +55,6 @@ module IRB throw :IRB_EXIT end end - - private - - def binding_irb? - caller.any? do |frame| - BINDING_IRB_FRAME_REGEXPS.any? do |regexp| - frame.match?(regexp) - end - end - end end class DebugCommand < Debug diff --git a/lib/irb/command/exit.rb b/lib/irb/command/exit.rb index 3109ec16e3..b4436f0343 100644 --- a/lib/irb/command/exit.rb +++ b/lib/irb/command/exit.rb @@ -8,10 +8,8 @@ module IRB category "IRB" description "Exit the current irb session." - def execute(*) + def execute(_arg) IRB.irb_exit - rescue UncaughtThrowError - Kernel.exit end end end diff --git a/lib/irb/command/force_exit.rb b/lib/irb/command/force_exit.rb index c2c5542e24..14086aa849 100644 --- a/lib/irb/command/force_exit.rb +++ b/lib/irb/command/force_exit.rb @@ -8,10 +8,8 @@ module IRB category "IRB" description "Exit the current process." - def execute(*) + def execute(_arg) throw :IRB_EXIT, true - rescue UncaughtThrowError - Kernel.exit! end end end diff --git a/lib/irb/command/help.rb b/lib/irb/command/help.rb index c9f16e05bc..c2018f9b30 100644 --- a/lib/irb/command/help.rb +++ b/lib/irb/command/help.rb @@ -11,7 +11,7 @@ module IRB if command_name.empty? help_message else - if command_class = ExtendCommandBundle.load_command(command_name) + if command_class = Command.load_command(command_name) command_class.help_message || command_class.description else "Can't find command `#{command_name}`. Please check the command name and try again.\n\n" @@ -23,20 +23,14 @@ module IRB private def help_message - commands_info = IRB::ExtendCommandBundle.all_commands_info + commands_info = IRB::Command.all_commands_info + helper_methods_info = IRB::HelperMethod.all_helper_methods_info commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] } - - user_aliases = irb_context.instance_variable_get(:@user_aliases) - - commands_grouped_by_categories["Aliases"] = user_aliases.map do |alias_name, target| - { display_name: alias_name, description: "Alias for `#{target}`" } - end + commands_grouped_by_categories["Helper methods"] = helper_methods_info if irb_context.with_debugger # Remove the original "Debugging" category commands_grouped_by_categories.delete("Debugging") - # Add an empty "Debugging (from debug.gem)" category at the end - commands_grouped_by_categories["Debugging (from debug.gem)"] = [] end longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max @@ -44,15 +38,31 @@ module IRB output = StringIO.new help_cmds = commands_grouped_by_categories.delete("Help") + no_category_cmds = commands_grouped_by_categories.delete("No category") + aliases = irb_context.instance_variable_get(:@user_aliases).map do |alias_name, target| + { display_name: alias_name, description: "Alias for `#{target}`" } + end + # Display help commands first add_category_to_output("Help", help_cmds, output, longest_cmd_name_length) + # Display the rest of the commands grouped by categories commands_grouped_by_categories.each do |category, cmds| add_category_to_output(category, cmds, output, longest_cmd_name_length) end + # Display commands without a category + if no_category_cmds + add_category_to_output("No category", no_category_cmds, output, longest_cmd_name_length) + end + + # Display aliases + add_category_to_output("Aliases", aliases, output, longest_cmd_name_length) + # Append the debugger help at the end if irb_context.with_debugger + # Add "Debugging (from debug.gem)" category as title + add_category_to_output("Debugging (from debug.gem)", [], output, longest_cmd_name_length) output.puts DEBUGGER__.help end diff --git a/lib/irb/command/subirb.rb b/lib/irb/command/subirb.rb index 138d61c930..85af28c1a5 100644 --- a/lib/irb/command/subirb.rb +++ b/lib/irb/command/subirb.rb @@ -49,6 +49,7 @@ module IRB extend_irb_context IRB.irb(nil, *obj) + puts IRB.JobManager.inspect end end @@ -65,7 +66,7 @@ module IRB end extend_irb_context - IRB.JobManager + puts IRB.JobManager.inspect end end @@ -90,6 +91,7 @@ module IRB raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key IRB.JobManager.switch(key) + puts IRB.JobManager.inspect end end @@ -112,6 +114,7 @@ module IRB extend_irb_context IRB.JobManager.kill(*keys) + puts IRB.JobManager.inspect end end end diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index 8a1df11561..a3d89373c3 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -88,7 +88,7 @@ module IRB def command_completions(preposing, target) if preposing.empty? && !target.empty? - IRB::ExtendCommandBundle.command_names.select { _1.start_with?(target) } + IRB::Command.command_names.select { _1.start_with?(target) } else [] end diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 836b8d2625..aafce7aade 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -73,11 +73,12 @@ module IRB self.prompt_mode = IRB.conf[:PROMPT_MODE] - if IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager) - @irb_name = IRB.conf[:IRB_NAME] - else - @irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s + @irb_name = IRB.conf[:IRB_NAME] + + unless IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager) + @irb_name = @irb_name + "#" + IRB.JobManager.n_jobs.to_s end + self.irb_path = "(" + @irb_name + ")" case input_method @@ -85,7 +86,7 @@ module IRB @io = nil case use_multiline? when nil - if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline? + if term_interactive? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline? # Both of multiline mode and singleline mode aren't specified. @io = RelineInputMethod.new(build_completor) else @@ -99,7 +100,7 @@ module IRB unless @io case use_singleline? when nil - if (defined?(ReadlineInputMethod) && STDIN.tty? && + if (defined?(ReadlineInputMethod) && term_interactive? && IRB.conf[:PROMPT_MODE] != :INF_RUBY) @io = ReadlineInputMethod.new else @@ -151,6 +152,11 @@ module IRB @command_aliases = @user_aliases.merge(KEYWORD_ALIASES) end + private def term_interactive? + return true if ENV['TEST_IRB_FORCE_INTERACTIVE'] + STDIN.tty? && ENV['TERM'] != 'dumb' + end + # because all input will eventually be evaluated as Ruby code, # command names that conflict with Ruby keywords need special workaround # we can remove them once we implemented a better command system for IRB @@ -587,18 +593,23 @@ module IRB def evaluate(statement, line_no) # :nodoc: @line_no = line_no - result = nil case statement when Statement::EmptyInput return when Statement::Expression result = evaluate_expression(statement.code, line_no) + set_last_value(result) when Statement::Command - result = statement.command_class.execute(self, statement.arg) + statement.command_class.execute(self, statement.arg) + set_last_value(nil) end - set_last_value(result) + nil + end + + def from_binding? + @irb.from_binding end def evaluate_expression(code, line_no) # :nodoc: diff --git a/lib/irb/default_commands.rb b/lib/irb/default_commands.rb index d680655fee..1bbc68efa7 100644 --- a/lib/irb/default_commands.rb +++ b/lib/irb/default_commands.rb @@ -30,35 +30,88 @@ require_relative "command/whereami" require_relative "command/history" module IRB - ExtendCommand = Command - - # Installs the default irb extensions command bundle. - module ExtendCommandBundle - # See #install_alias_method. + module Command NO_OVERRIDE = 0 - # See #install_alias_method. OVERRIDE_PRIVATE_ONLY = 0x01 - # See #install_alias_method. OVERRIDE_ALL = 0x02 - Command._register_with_aliases(:irb_context, Command::Context, - [ - [:context, NO_OVERRIDE], - [:conf, NO_OVERRIDE], - ], + class << self + # This API is for IRB's internal use only and may change at any time. + # Please do NOT use it. + def _register_with_aliases(name, command_class, *aliases) + @commands[name.to_sym] = [command_class, aliases] + end + + def all_commands_info + user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result| + result[target] ||= [] + result[target] << alias_name + end + + commands.map do |command_name, (command_class, aliases)| + aliases = aliases.map { |a| a.first } + + if additional_aliases = user_aliases[command_name] + aliases += additional_aliases + end + + display_name = aliases.shift || command_name + { + display_name: display_name, + description: command_class.description, + category: command_class.category + } + end + end + + def command_override_policies + @@command_override_policies ||= commands.flat_map do |cmd_name, (cmd_class, aliases)| + [[cmd_name, OVERRIDE_ALL]] + aliases + end.to_h + end + + def execute_as_command?(name, public_method:, private_method:) + case command_override_policies[name] + when OVERRIDE_ALL + true + when OVERRIDE_PRIVATE_ONLY + !public_method + when NO_OVERRIDE + !public_method && !private_method + end + end + + def command_names + command_override_policies.keys.map(&:to_s) + end + + # Convert a command name to its implementation class if such command exists + def load_command(command) + command = command.to_sym + commands.each do |command_name, (command_class, aliases)| + if command_name == command || aliases.any? { |alias_name, _| alias_name == command } + return command_class + end + end + nil + end + end + + _register_with_aliases(:irb_context, Command::Context, + [:context, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_exit, Command::Exit, + _register_with_aliases(:irb_exit, Command::Exit, [:exit, OVERRIDE_PRIVATE_ONLY], [:quit, OVERRIDE_PRIVATE_ONLY], [:irb_quit, OVERRIDE_PRIVATE_ONLY] ) - Command._register_with_aliases(:irb_exit!, Command::ForceExit, + _register_with_aliases(:irb_exit!, Command::ForceExit, [:exit!, OVERRIDE_PRIVATE_ONLY] ) - Command._register_with_aliases(:irb_current_working_workspace, Command::CurrentWorkingWorkspace, + _register_with_aliases(:irb_current_working_workspace, Command::CurrentWorkingWorkspace, [:cwws, NO_OVERRIDE], [:pwws, NO_OVERRIDE], [:irb_print_working_workspace, OVERRIDE_ALL], @@ -70,7 +123,7 @@ module IRB [:irb_pwb, OVERRIDE_ALL], ) - Command._register_with_aliases(:irb_change_workspace, Command::ChangeWorkspace, + _register_with_aliases(:irb_change_workspace, Command::ChangeWorkspace, [:chws, NO_OVERRIDE], [:cws, NO_OVERRIDE], [:irb_chws, OVERRIDE_ALL], @@ -80,13 +133,13 @@ module IRB [:cb, NO_OVERRIDE], ) - Command._register_with_aliases(:irb_workspaces, Command::Workspaces, + _register_with_aliases(:irb_workspaces, Command::Workspaces, [:workspaces, NO_OVERRIDE], [:irb_bindings, OVERRIDE_ALL], [:bindings, NO_OVERRIDE], ) - Command._register_with_aliases(:irb_push_workspace, Command::PushWorkspace, + _register_with_aliases(:irb_push_workspace, Command::PushWorkspace, [:pushws, NO_OVERRIDE], [:irb_pushws, OVERRIDE_ALL], [:irb_push_binding, OVERRIDE_ALL], @@ -94,7 +147,7 @@ module IRB [:pushb, NO_OVERRIDE], ) - Command._register_with_aliases(:irb_pop_workspace, Command::PopWorkspace, + _register_with_aliases(:irb_pop_workspace, Command::PopWorkspace, [:popws, NO_OVERRIDE], [:irb_popws, OVERRIDE_ALL], [:irb_pop_binding, OVERRIDE_ALL], @@ -102,140 +155,98 @@ module IRB [:popb, NO_OVERRIDE], ) - Command._register_with_aliases(:irb_load, Command::Load) - Command._register_with_aliases(:irb_require, Command::Require) - Command._register_with_aliases(:irb_source, Command::Source, + _register_with_aliases(:irb_load, Command::Load) + _register_with_aliases(:irb_require, Command::Require) + _register_with_aliases(:irb_source, Command::Source, [:source, NO_OVERRIDE] ) - Command._register_with_aliases(:irb, Command::IrbCommand) - Command._register_with_aliases(:irb_jobs, Command::Jobs, + _register_with_aliases(:irb, Command::IrbCommand) + _register_with_aliases(:irb_jobs, Command::Jobs, [:jobs, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_fg, Command::Foreground, + _register_with_aliases(:irb_fg, Command::Foreground, [:fg, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_kill, Command::Kill, + _register_with_aliases(:irb_kill, Command::Kill, [:kill, OVERRIDE_PRIVATE_ONLY] ) - Command._register_with_aliases(:irb_debug, Command::Debug, + _register_with_aliases(:irb_debug, Command::Debug, [:debug, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_edit, Command::Edit, + _register_with_aliases(:irb_edit, Command::Edit, [:edit, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_break, Command::Break) - Command._register_with_aliases(:irb_catch, Command::Catch) - Command._register_with_aliases(:irb_next, Command::Next) - Command._register_with_aliases(:irb_delete, Command::Delete, + _register_with_aliases(:irb_break, Command::Break) + _register_with_aliases(:irb_catch, Command::Catch) + _register_with_aliases(:irb_next, Command::Next) + _register_with_aliases(:irb_delete, Command::Delete, [:delete, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_step, Command::Step, + _register_with_aliases(:irb_step, Command::Step, [:step, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_continue, Command::Continue, + _register_with_aliases(:irb_continue, Command::Continue, [:continue, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_finish, Command::Finish, + _register_with_aliases(:irb_finish, Command::Finish, [:finish, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_backtrace, Command::Backtrace, + _register_with_aliases(:irb_backtrace, Command::Backtrace, [:backtrace, NO_OVERRIDE], [:bt, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_debug_info, Command::Info, + _register_with_aliases(:irb_debug_info, Command::Info, [:info, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_help, Command::Help, + _register_with_aliases(:irb_help, Command::Help, [:help, NO_OVERRIDE], [:show_cmds, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_show_doc, Command::ShowDoc, + _register_with_aliases(:irb_show_doc, Command::ShowDoc, [:show_doc, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_info, Command::IrbInfo) + _register_with_aliases(:irb_info, Command::IrbInfo) - Command._register_with_aliases(:irb_ls, Command::Ls, + _register_with_aliases(:irb_ls, Command::Ls, [:ls, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_measure, Command::Measure, + _register_with_aliases(:irb_measure, Command::Measure, [:measure, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_show_source, Command::ShowSource, + _register_with_aliases(:irb_show_source, Command::ShowSource, [:show_source, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_whereami, Command::Whereami, + _register_with_aliases(:irb_whereami, Command::Whereami, [:whereami, NO_OVERRIDE] ) - Command._register_with_aliases(:irb_history, Command::History, + _register_with_aliases(:irb_history, Command::History, [:history, NO_OVERRIDE], [:hist, NO_OVERRIDE] ) + end - def self.all_commands_info - user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result| - result[target] ||= [] - result[target] << alias_name - end - - Command.commands.map do |command_name, (command_class, aliases)| - aliases = aliases.map { |a| a.first } - - if additional_aliases = user_aliases[command_name] - aliases += additional_aliases - end - - display_name = aliases.shift || command_name - { - display_name: display_name, - description: command_class.description, - category: command_class.category - } - end - end - - def self.command_override_policies - @@command_override_policies ||= Command.commands.flat_map do |cmd_name, (cmd_class, aliases)| - [[cmd_name, OVERRIDE_ALL]] + aliases - end.to_h - end - - def self.execute_as_command?(name, public_method:, private_method:) - case command_override_policies[name] - when OVERRIDE_ALL - true - when OVERRIDE_PRIVATE_ONLY - !public_method - when NO_OVERRIDE - !public_method && !private_method - end - end - - def self.command_names - command_override_policies.keys.map(&:to_s) - end + ExtendCommand = Command - # Convert a command name to its implementation class if such command exists - def self.load_command(command) - command = command.to_sym - Command.commands.each do |command_name, (command_class, aliases)| - if command_name == command || aliases.any? { |alias_name, _| alias_name == command } - return command_class - end - end - nil - end + # For backward compatibility, we need to keep this module: + # - As a container of helper methods + # - As a place to register commands with the deprecated def_extend_command method + module ExtendCommandBundle + # For backward compatibility + NO_OVERRIDE = Command::NO_OVERRIDE + OVERRIDE_PRIVATE_ONLY = Command::OVERRIDE_PRIVATE_ONLY + OVERRIDE_ALL = Command::OVERRIDE_ALL # Deprecated. Doesn't have any effect. @EXTEND_COMMANDS = [] @@ -243,7 +254,7 @@ module IRB # Drepcated. Use Command.regiser instead. def self.def_extend_command(cmd_name, cmd_class, _, *aliases) Command._register_with_aliases(cmd_name, cmd_class, *aliases) - @@command_override_policies = nil + Command.class_variable_set(:@@command_override_policies, nil) end end end diff --git a/lib/irb/helper_method.rb b/lib/irb/helper_method.rb new file mode 100644 index 0000000000..f1f6fff915 --- /dev/null +++ b/lib/irb/helper_method.rb @@ -0,0 +1,29 @@ +require_relative "helper_method/base" + +module IRB + module HelperMethod + @helper_methods = {} + + class << self + attr_reader :helper_methods + + def register(name, helper_class) + @helper_methods[name] = helper_class + + if defined?(HelpersContainer) + HelpersContainer.install_helper_methods + end + end + + def all_helper_methods_info + @helper_methods.map do |name, helper_class| + { display_name: name, description: helper_class.description } + end + end + end + + # Default helper_methods + require_relative "helper_method/conf" + register(:conf, HelperMethod::Conf) + end +end diff --git a/lib/irb/helper_method/base.rb b/lib/irb/helper_method/base.rb new file mode 100644 index 0000000000..a68001ed28 --- /dev/null +++ b/lib/irb/helper_method/base.rb @@ -0,0 +1,16 @@ +require "singleton" + +module IRB + module HelperMethod + class Base + include Singleton + + class << self + def description(description = nil) + @description = description if description + @description + end + end + end + end +end diff --git a/lib/irb/helper_method/conf.rb b/lib/irb/helper_method/conf.rb new file mode 100644 index 0000000000..718ed279c0 --- /dev/null +++ b/lib/irb/helper_method/conf.rb @@ -0,0 +1,11 @@ +module IRB + module HelperMethod + class Conf < Base + description "Returns the current IRB context." + + def execute + IRB.CurrentContext + end + end + end +end diff --git a/lib/irb/init.rb b/lib/irb/init.rb index 355047519c..7dc08912ef 100644 --- a/lib/irb/init.rb +++ b/lib/irb/init.rb @@ -52,6 +52,7 @@ module IRB # :nodoc: IRB.init_error IRB.parse_opts(argv: argv) IRB.run_config + IRB.validate_config IRB.load_modules unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]] @@ -427,6 +428,40 @@ module IRB # :nodoc: @irbrc_files end + def IRB.validate_config + conf[:IRB_NAME] = conf[:IRB_NAME].to_s + + irb_rc = conf[:IRB_RC] + unless irb_rc.nil? || irb_rc.respond_to?(:call) + raise_validation_error "IRB.conf[:IRB_RC] should be a callable object. Got #{irb_rc.inspect}." + end + + back_trace_limit = conf[:BACK_TRACE_LIMIT] + unless back_trace_limit.is_a?(Integer) + raise_validation_error "IRB.conf[:BACK_TRACE_LIMIT] should be an integer. Got #{back_trace_limit.inspect}." + end + + prompt = conf[:PROMPT] + unless prompt.is_a?(Hash) + msg = "IRB.conf[:PROMPT] should be a Hash. Got #{prompt.inspect}." + + if prompt.is_a?(Symbol) + msg += " Did you mean to set `IRB.conf[:PROMPT_MODE]`?" + end + + raise_validation_error msg + end + + eval_history = conf[:EVAL_HISTORY] + unless eval_history.nil? || eval_history.is_a?(Integer) + raise_validation_error "IRB.conf[:EVAL_HISTORY] should be an integer. Got #{eval_history.inspect}." + end + end + + def IRB.raise_validation_error(msg) + raise TypeError, msg, @irbrc_files + end + # loading modules def IRB.load_modules for m in @CONF[:LOAD_MODULES] diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index e5adb350e8..684527edc4 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -67,6 +67,7 @@ module IRB # # See IO#gets for more information. def gets + puts if @stdout.tty? # workaround for debug compatibility test print @prompt line = @stdin.gets @line[@line_no += 1] = line diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb index cfe36be83f..f6ac7f0f5f 100644 --- a/lib/irb/ruby-lex.rb +++ b/lib/irb/ruby-lex.rb @@ -219,28 +219,7 @@ module IRB :unrecoverable_error rescue SyntaxError => e case e.message - when /unterminated (?:string|regexp) meets end of file/ - # "unterminated regexp meets end of file" - # - # example: - # / - # - # "unterminated string meets end of file" - # - # example: - # ' - return :recoverable_error - when /syntax error, unexpected end-of-input/ - # "syntax error, unexpected end-of-input, expecting keyword_end" - # - # example: - # if true - # hoge - # if false - # fuga - # end - return :recoverable_error - when /syntax error, unexpected keyword_end/ + when /unexpected keyword_end/ # "syntax error, unexpected keyword_end" # # example: @@ -250,7 +229,7 @@ module IRB # example: # end return :unrecoverable_error - when /syntax error, unexpected '\.'/ + when /unexpected '\.'/ # "syntax error, unexpected '.'" # # example: @@ -262,6 +241,27 @@ module IRB # example: # method / f / return :unrecoverable_error + when /unterminated (?:string|regexp) meets end of file/ + # "unterminated regexp meets end of file" + # + # example: + # / + # + # "unterminated string meets end of file" + # + # example: + # ' + return :recoverable_error + when /unexpected end-of-input/ + # "syntax error, unexpected end-of-input, expecting keyword_end" + # + # example: + # if true + # hoge + # if false + # fuga + # end + return :recoverable_error else return :other_error end diff --git a/lib/irb/version.rb b/lib/irb/version.rb index 9a7b12766b..c41917329c 100644 --- a/lib/irb/version.rb +++ b/lib/irb/version.rb @@ -5,7 +5,7 @@ # module IRB # :nodoc: - VERSION = "1.12.0" + VERSION = "1.13.1" @RELEASE_VERSION = VERSION - @LAST_UPDATE_DATE = "2024-03-06" + @LAST_UPDATE_DATE = "2024-05-05" end diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb index 1490f7b478..d24d1cc38d 100644 --- a/lib/irb/workspace.rb +++ b/lib/irb/workspace.rb @@ -6,6 +6,8 @@ require "delegate" +require_relative "helper_method" + IRB::TOPLEVEL_BINDING = binding module IRB # :nodoc: class WorkSpace @@ -109,9 +111,9 @@ EOF attr_reader :main def load_helper_methods_to_main - if !(class<<main;ancestors;end).include?(ExtendCommandBundle) - main.extend ExtendCommandBundle - end + ancestors = class<<main;ancestors;end + main.extend ExtendCommandBundle if !ancestors.include?(ExtendCommandBundle) + main.extend HelpersContainer if !ancestors.include?(HelpersContainer) end # Evaluate the given +statements+ within the context of this workspace. @@ -172,4 +174,16 @@ EOF "\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}#{Color.clear}\n" end end + + module HelpersContainer + def self.install_helper_methods + HelperMethod.helper_methods.each do |name, helper_method_class| + define_method name do |*args, **opts, &block| + helper_method_class.instance.execute(*args, **opts, &block) + end unless method_defined?(name) + end + end + + install_helper_methods + end end diff --git a/lib/optparse/optparse.gemspec b/lib/optparse/optparse.gemspec index a4287ddeee..1aa54aa781 100644 --- a/lib/optparse/optparse.gemspec +++ b/lib/optparse/optparse.gemspec @@ -22,7 +22,8 @@ Gem::Specification.new do |spec| spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage - spec.files = Dir["{doc,lib,misc}/**/*"] + %w[README.md ChangeLog COPYING] + spec.files = Dir["{doc,lib,misc}/**/{*,.document}"] + + %w[README.md ChangeLog COPYING .document .rdoc_options] spec.rdoc_options = ["--main=README.md", "--op=rdoc", "--page-dir=doc"] spec.bindir = "exe" spec.executables = [] diff --git a/lib/prism.rb b/lib/prism.rb index c512cb4015..2bb7f79bf6 100644 --- a/lib/prism.rb +++ b/lib/prism.rb @@ -18,10 +18,10 @@ module Prism autoload :Dispatcher, "prism/dispatcher" autoload :DotVisitor, "prism/dot_visitor" autoload :DSL, "prism/dsl" + autoload :InspectVisitor, "prism/inspect_visitor" autoload :LexCompat, "prism/lex_compat" autoload :LexRipper, "prism/lex_compat" autoload :MutationCompiler, "prism/mutation_compiler" - autoload :NodeInspector, "prism/node_inspector" autoload :Pack, "prism/pack" autoload :Pattern, "prism/pattern" autoload :Reflection, "prism/reflection" @@ -37,7 +37,7 @@ module Prism private_constant :LexRipper # :call-seq: - # Prism::lex_compat(source, **options) -> ParseResult + # Prism::lex_compat(source, **options) -> LexCompat::Result # # Returns a parse result whose value is an array of tokens that closely # resembles the return value of Ripper::lex. The main difference is that the @@ -67,11 +67,10 @@ module Prism end end +require_relative "prism/polyfill/byteindex" require_relative "prism/node" require_relative "prism/node_ext" require_relative "prism/parse_result" -require_relative "prism/parse_result/comments" -require_relative "prism/parse_result/newlines" # This is a Ruby implementation of the prism parser. If we're running on CRuby # and we haven't explicitly set the PRISM_FFI_BACKEND environment variable, then diff --git a/lib/prism/desugar_compiler.rb b/lib/prism/desugar_compiler.rb index 9b62c00df3..de02445149 100644 --- a/lib/prism/desugar_compiler.rb +++ b/lib/prism/desugar_compiler.rb @@ -73,7 +73,7 @@ module Prism # Desugar `x += y` to `x = x + y` def compile - operator_loc = node.operator_loc.chop + binary_operator_loc = node.binary_operator_loc.chop write_class.new( source, @@ -84,15 +84,15 @@ module Prism 0, read_class.new(source, *arguments, node.name_loc), nil, - operator_loc.slice.to_sym, - operator_loc, + binary_operator_loc.slice.to_sym, + binary_operator_loc, nil, ArgumentsNode.new(source, 0, [node.value], node.value.location), nil, nil, node.location ), - node.operator_loc.copy(start_offset: node.operator_loc.end_offset - 1, length: 1), + node.binary_operator_loc.copy(start_offset: node.binary_operator_loc.end_offset - 1, length: 1), node.location ) end diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index 1fd053f902..b62a59d037 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -317,7 +317,7 @@ module Prism buffer.read end - Serialize.load_tokens(Source.new(code), serialized) + Serialize.load_tokens(Source.for(code), serialized) end def parse_common(string, code, options) # :nodoc: @@ -329,7 +329,7 @@ module Prism LibRubyParser::PrismBuffer.with do |buffer| LibRubyParser.pm_serialize_parse_comments(buffer.pointer, string.pointer, string.length, dump_options(options)) - source = Source.new(code) + source = Source.for(code) loader = Serialize::Loader.new(source, buffer.read) loader.load_header @@ -343,14 +343,14 @@ module Prism LibRubyParser::PrismBuffer.with do |buffer| LibRubyParser.pm_serialize_parse_lex(buffer.pointer, string.pointer, string.length, dump_options(options)) - source = Source.new(code) + source = Source.for(code) loader = Serialize::Loader.new(source, buffer.read) tokens = loader.load_tokens node, comments, magic_comments, data_loc, errors, warnings = loader.load_nodes tokens.each { |token,| token.value.force_encoding(loader.encoding) } - ParseResult.new([node, tokens], comments, magic_comments, data_loc, errors, warnings, source) + ParseLexResult.new([node, tokens], comments, magic_comments, data_loc, errors, warnings, source) end end @@ -408,7 +408,7 @@ module Prism values << dump_options_command_line(options) template << "C" - values << { nil => 0, "3.3.0" => 1, "3.4.0" => 0, "latest" => 0 }.fetch(options[:version]) + values << { nil => 0, "3.3.0" => 1, "3.3.1" => 1, "3.4.0" => 0, "latest" => 0 }.fetch(options[:version]) template << "L" if (scopes = options[:scopes]) diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb index 70cb065201..4f8e443a3b 100644 --- a/lib/prism/lex_compat.rb +++ b/lib/prism/lex_compat.rb @@ -10,6 +10,23 @@ module Prism # generally lines up. However, there are a few cases that require special # handling. class LexCompat # :nodoc: + # A result class specialized for holding tokens produced by the lexer. + class Result < Prism::Result + # The list of tokens that were produced by the lexer. + attr_reader :value + + # Create a new lex compat result object with the given values. + def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) + @value = value + super(comments, magic_comments, data_loc, errors, warnings, source) + end + + # Implement the hash pattern matching interface for Result. + def deconstruct_keys(keys) + super.merge!(value: value) + end + end + # This is a mapping of prism token types to Ripper token types. This is a # many-to-one mapping because we split up our token types, whereas Ripper # tends to group them. @@ -844,7 +861,7 @@ module Prism # We sort by location to compare against Ripper's output tokens.sort_by!(&:location) - ParseResult.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, Source.new(source)) + Result.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, Source.for(source)) end end diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb index 8674544065..ceec76b8d6 100644 --- a/lib/prism/node_ext.rb +++ b/lib/prism/node_ext.rb @@ -3,6 +3,17 @@ # Here we are reopening the prism module to provide methods on nodes that aren't # templated and are meant as convenience methods. module Prism + class Node + def deprecated(*replacements) # :nodoc: + suggest = replacements.map { |replacement| "#{self.class}##{replacement}" } + warn(<<~MSG, category: :deprecated) + [deprecation]: #{self.class}##{caller_locations(1, 1)[0].label} is deprecated \ + and will be removed in the next major version. Use #{suggest.join("/")} instead. + #{(caller(1, 3) || []).join("\n")} + MSG + end + end + module RegularExpressionOptions # :nodoc: # Returns a numeric value that represents the flags that were used to create # the regular expression. @@ -143,11 +154,12 @@ module Prism current = self #: node? while current.is_a?(ConstantPathNode) - child = current.child - if child.is_a?(MissingNode) + name = current.name + if name.nil? raise MissingNodesInConstantPathError, "Constant path contains missing nodes. Cannot compute full name" end - parts.unshift(child.name) + + parts.unshift(name) current = current.parent end @@ -162,6 +174,14 @@ module Prism def full_name full_name_parts.join("::") end + + # Previously, we had a child node on this class that contained either a + # constant read or a missing node. To not cause a breaking change, we + # continue to supply that API. + def child + deprecated("name", "name_loc") + name ? ConstantReadNode.new(source, name, name_loc) : MissingNode.new(source, location) + end end class ConstantPathTargetNode < Node @@ -179,17 +199,25 @@ module Prism raise ConstantPathNode::DynamicPartsInConstantPathError, "Constant target path contains dynamic parts. Cannot compute full name" end - if child.is_a?(MissingNode) + if name.nil? raise ConstantPathNode::MissingNodesInConstantPathError, "Constant target path contains missing nodes. Cannot compute full name" end - parts.push(child.name) + parts.push(name) end # Returns the full name of this constant path. For example: "Foo::Bar" def full_name full_name_parts.join("::") end + + # Previously, we had a child node on this class that contained either a + # constant read or a missing node. To not cause a breaking change, we + # continue to supply that API. + def child + deprecated("name", "name_loc") + name ? ConstantReadNode.new(source, name, name_loc) : MissingNode.new(source, location) + end end class ConstantTargetNode < Node @@ -257,4 +285,147 @@ module Prism names end end + + class CallNode < Node + # When a call node has the attribute_write flag set, it means that the call + # is using the attribute write syntax. This is either a method call to []= + # or a method call to a method that ends with =. Either way, the = sign is + # present in the source. + # + # Prism returns the message_loc _without_ the = sign attached, because there + # can be any amount of space between the message and the = sign. However, + # sometimes you want the location of the full message including the inner + # space and the = sign. This method provides that. + def full_message_loc + attribute_write? ? message_loc&.adjoin("=") : message_loc + end + end + + class CallOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class ClassVariableOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class ConstantOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class ConstantPathOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class GlobalVariableOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class IndexOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class InstanceVariableOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class LocalVariableOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end end diff --git a/lib/prism/node_inspector.rb b/lib/prism/node_inspector.rb deleted file mode 100644 index d77af33c3a..0000000000 --- a/lib/prism/node_inspector.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -module Prism - # This object is responsible for generating the output for the inspect method - # implementations of child nodes. - class NodeInspector # :nodoc: - attr_reader :prefix, :output - - def initialize(prefix = "") - @prefix = prefix - @output = +"" - end - - # Appends a line to the output with the current prefix. - def <<(line) - output << "#{prefix}#{line}" - end - - # This generates a string that is used as the header of the inspect output - # for any given node. - def header(node) - output = +"@ #{node.class.name.split("::").last} (" - output << "location: (#{node.location.start_line},#{node.location.start_column})-(#{node.location.end_line},#{node.location.end_column})" - output << ", newline: true" if node.newline? - output << ")\n" - output - end - - # Generates a string that represents a list of nodes. It handles properly - # using the box drawing characters to make the output look nice. - def list(prefix, nodes) - output = +"(length: #{nodes.length})\n" - last_index = nodes.length - 1 - - nodes.each_with_index do |node, index| - pointer, preadd = (index == last_index) ? ["└── ", " "] : ["├── ", "│ "] - node_prefix = "#{prefix}#{preadd}" - output << node.inspect(NodeInspector.new(node_prefix)).sub(node_prefix, "#{prefix}#{pointer}") - end - - output - end - - # Generates a string that represents a location field on a node. - def location(value) - if value - "(#{value.start_line},#{value.start_column})-(#{value.end_line},#{value.end_column}) = #{value.slice.inspect}" - else - "∅" - end - end - - # Generates a string that represents a child node. - def child_node(node, append) - node.inspect(child_inspector(append)).delete_prefix(prefix) - end - - # Returns a new inspector that can be used to inspect a child node. - def child_inspector(append) - NodeInspector.new("#{prefix}#{append}") - end - - # Returns the output as a string. - def to_str - output - end - end -end diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb index 39e15f6027..798fde09e5 100644 --- a/lib/prism/parse_result.rb +++ b/lib/prism/parse_result.rb @@ -5,6 +5,14 @@ module Prism # conjunction with locations to allow them to resolve line numbers and source # ranges. class Source + # Create a new source object with the given source code. This method should + # be used instead of `new` and it will return either a `Source` or a + # specialized and more performant `ASCIISource` if no multibyte characters + # are present in the source code. + def self.for(source, start_line = 1, offsets = []) + source.ascii_only? ? ASCIISource.new(source, start_line, offsets): new(source, start_line, offsets) + end + # The source code that this source object represents. attr_reader :source @@ -27,6 +35,11 @@ module Prism source.encoding end + # Returns the lines of the source code as an array of strings. + def lines + source.lines + end + # Perform a byteslice on the source code using the given byte offset and # byte length. def slice(byte_offset, length) @@ -45,6 +58,12 @@ module Prism offsets[find_line(byte_offset)] end + # Returns the byte offset of the end of the line corresponding to the given + # byte offset. + def line_end(byte_offset) + offsets[find_line(byte_offset) + 1] || source.bytesize + end + # Return the column number for the given byte offset. def column(byte_offset) byte_offset - line_start(byte_offset) @@ -100,6 +119,39 @@ module Prism end end + # Specialized version of Prism::Source for source code that includes ASCII + # characters only. This class is used to apply performance optimizations that + # cannot be applied to sources that include multibyte characters. Sources that + # include multibyte characters are represented by the Prism::Source class. + class ASCIISource < Source + # Return the character offset for the given byte offset. + def character_offset(byte_offset) + byte_offset + end + + # Return the column number in characters for the given byte offset. + def character_column(byte_offset) + byte_offset - line_start(byte_offset) + end + + # Returns the offset from the start of the file for the given byte offset + # counting in code units for the given encoding. + # + # This method is tested with UTF-8, UTF-16, and UTF-32. If there is the + # concept of code units that differs from the number of characters in other + # encodings, it is not captured here. + def code_units_offset(byte_offset, encoding) + byte_offset + end + + # Specialized version of `code_units_column` that does not depend on + # `code_units_offset`, which is a more expensive operation. This is + # essentialy the same as `Prism::Source#column`. + def code_units_column(byte_offset, encoding) + byte_offset - line_start(byte_offset) + end + end + # This represents a location in the source. class Location # A Source object that is used to determine more information from the given @@ -171,11 +223,25 @@ module Prism "#<Prism::Location @start_offset=#{@start_offset} @length=#{@length} start_line=#{start_line}>" end + # Returns all of the lines of the source code associated with this location. + def source_lines + source.lines + end + # The source code that this location represents. def slice source.slice(start_offset, length) end + # The source code that this location represents starting from the beginning + # of the line that this location starts on to the end of the line that this + # location ends on. + def slice_lines + line_start = source.line_start(start_offset) + line_end = source.line_end(end_offset) + source.slice(line_start, line_end - line_start) + end + # The character offset from the beginning of the source where this location # starts. def start_character_offset @@ -281,6 +347,18 @@ module Prism Location.new(source, start_offset, other.end_offset - start_offset) end + + # Join this location with the first occurrence of the string in the source + # that occurs after this location on the same line, and return the new + # location. This will raise an error if the string does not exist. + def adjoin(string) + line_suffix = source.slice(end_offset, source.line_end(end_offset) - end_offset) + + line_suffix_index = line_suffix.byteindex(string) + raise "Could not find #{string}" if line_suffix_index.nil? + + Location.new(source, start_offset, length + line_suffix_index + string.bytesize) + end end # This represents a comment that was encountered during parsing. It is the @@ -438,14 +516,9 @@ module Prism end # This represents the result of a call to ::parse or ::parse_file. It contains - # the AST, any comments that were encounters, and any errors that were - # encountered. - class ParseResult - # The value that was generated by parsing. Normally this holds the AST, but - # it can sometimes how a list of tokens or other results passed back from - # the parser. - attr_reader :value - + # the requested structure, any comments that were encounters, and any errors + # that were encountered. + class Result # The list of comments that were encountered during parsing. attr_reader :comments @@ -466,9 +539,8 @@ module Prism # A Source instance that represents the source code that was parsed. attr_reader :source - # Create a new parse result object with the given values. - def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) - @value = value + # Create a new result object with the given values. + def initialize(comments, magic_comments, data_loc, errors, warnings, source) @comments = comments @magic_comments = magic_comments @data_loc = data_loc @@ -477,9 +549,9 @@ module Prism @source = source end - # Implement the hash pattern matching interface for ParseResult. + # Implement the hash pattern matching interface for Result. def deconstruct_keys(keys) - { value: value, comments: comments, magic_comments: magic_comments, data_loc: data_loc, errors: errors, warnings: warnings } + { comments: comments, magic_comments: magic_comments, data_loc: data_loc, errors: errors, warnings: warnings } end # Returns the encoding of the source code that was parsed. @@ -500,6 +572,75 @@ module Prism end end + # This is a result specific to the `parse` and `parse_file` methods. + class ParseResult < Result + autoload :Comments, "prism/parse_result/comments" + autoload :Newlines, "prism/parse_result/newlines" + + private_constant :Comments + private_constant :Newlines + + # The syntax tree that was parsed from the source code. + attr_reader :value + + # Create a new parse result object with the given values. + def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) + @value = value + super(comments, magic_comments, data_loc, errors, warnings, source) + end + + # Implement the hash pattern matching interface for ParseResult. + def deconstruct_keys(keys) + super.merge!(value: value) + end + + # Attach the list of comments to their respective locations in the tree. + def attach_comments! + Comments.new(self).attach! # steep:ignore + end + + # Walk the tree and mark nodes that are on a new line, loosely emulating + # the behavior of CRuby's `:line` tracepoint event. + def mark_newlines! + value.accept(Newlines.new(source.offsets.size)) # steep:ignore + end + end + + # This is a result specific to the `lex` and `lex_file` methods. + class LexResult < Result + # The list of tokens that were parsed from the source code. + attr_reader :value + + # Create a new lex result object with the given values. + def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) + @value = value + super(comments, magic_comments, data_loc, errors, warnings, source) + end + + # Implement the hash pattern matching interface for LexResult. + def deconstruct_keys(keys) + super.merge!(value: value) + end + end + + # This is a result specific to the `parse_lex` and `parse_lex_file` methods. + class ParseLexResult < Result + # A tuple of the syntax tree and the list of tokens that were parsed from + # the source code. + attr_reader :value + + # Create a new parse lex result object with the given values. + def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) + @value = value + super(comments, magic_comments, data_loc, errors, warnings, source) + end + + # Implement the hash pattern matching interface for ParseLexResult. + def deconstruct_keys(keys) + super.merge!(value: value) + end + end + # This represents a token from the Ruby source. class Token # The Source object that represents the source this token came from. diff --git a/lib/prism/parse_result/comments.rb b/lib/prism/parse_result/comments.rb index f8f74d2503..22c4148b2c 100644 --- a/lib/prism/parse_result/comments.rb +++ b/lib/prism/parse_result/comments.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Prism - class ParseResult + class ParseResult < Result # When we've parsed the source, we have both the syntax tree and the list of # comments that we found in the source. This class is responsible for # walking the tree and finding the nearest location to attach each comment. @@ -183,12 +183,5 @@ module Prism [preceding, NodeTarget.new(node), following] end end - - private_constant :Comments - - # Attach the list of comments to their respective locations in the tree. - def attach_comments! - Comments.new(self).attach! # steep:ignore - end end end diff --git a/lib/prism/parse_result/newlines.rb b/lib/prism/parse_result/newlines.rb index 03acb0b862..cc1343dfda 100644 --- a/lib/prism/parse_result/newlines.rb +++ b/lib/prism/parse_result/newlines.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Prism - class ParseResult + class ParseResult < Result # The :line tracepoint event gets fired whenever the Ruby VM encounters an # expression on a new line. The types of expressions that can trigger this # event are: @@ -17,21 +17,26 @@ module Prism # Note that the logic in this file should be kept in sync with the Java # MarkNewlinesVisitor, since that visitor is responsible for marking the # newlines for JRuby/TruffleRuby. + # + # This file is autoloaded only when `mark_newlines!` is called, so the + # re-opening of the various nodes in this file will only be performed in + # that case. We do that to avoid storing the extra `@newline` instance + # variable on every node if we don't need it. class Newlines < Visitor # Create a new Newlines visitor with the given newline offsets. - def initialize(newline_marked) - @newline_marked = newline_marked + def initialize(lines) + @lines = Array.new(1 + lines, false) end # Permit block/lambda nodes to mark newlines within themselves. def visit_block_node(node) - old_newline_marked = @newline_marked - @newline_marked = Array.new(old_newline_marked.size, false) + old_lines = @lines + @lines = Array.new(old_lines.size, false) begin super(node) ensure - @newline_marked = old_newline_marked + @lines = old_lines end end @@ -39,7 +44,7 @@ module Prism # Mark if/unless nodes as newlines. def visit_if_node(node) - node.set_newline_flag(@newline_marked) + node.newline!(@lines) super(node) end @@ -48,19 +53,101 @@ module Prism # Permit statements lists to mark newlines within themselves. def visit_statements_node(node) node.body.each do |child| - child.set_newline_flag(@newline_marked) + child.newline!(@lines) end super(node) end end + end + + class Node + def newline? # :nodoc: + @newline ? true : false + end + + def newline!(lines) # :nodoc: + line = location.start_line + unless lines[line] + lines[line] = true + @newline = true + end + end + end + + class BeginNode < Node + def newline!(lines) # :nodoc: + # Never mark BeginNode with a newline flag, mark children instead. + end + end + + class ParenthesesNode < Node + def newline!(lines) # :nodoc: + # Never mark ParenthesesNode with a newline flag, mark children instead. + end + end + + class IfNode < Node + def newline!(lines) # :nodoc: + predicate.newline!(lines) + end + end + + class UnlessNode < Node + def newline!(lines) # :nodoc: + predicate.newline!(lines) + end + end + + class UntilNode < Node + def newline!(lines) # :nodoc: + predicate.newline!(lines) + end + end + + class WhileNode < Node + def newline!(lines) # :nodoc: + predicate.newline!(lines) + end + end + + class RescueModifierNode < Node + def newline!(lines) # :nodoc: + expression.newline!(lines) + end + end + + class InterpolatedMatchLastLineNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first + end + end + + class InterpolatedRegularExpressionNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first + end + end + + class InterpolatedStringNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first + end + end - private_constant :Newlines + class InterpolatedSymbolNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first + end + end - # Walk the tree and mark nodes that are on a new line. - def mark_newlines! - value = self.value - raise "This method should only be called on a parse result that contains a node" unless Node === value - value.accept(Newlines.new(Array.new(1 + source.offsets.size, false))) # steep:ignore + class InterpolatedXStringNode < Node + def newline!(lines) # :nodoc: + first = parts.first + first.newline!(lines) if first end end end diff --git a/lib/prism/pattern.rb b/lib/prism/pattern.rb index e12cfd597f..03fec26789 100644 --- a/lib/prism/pattern.rb +++ b/lib/prism/pattern.rb @@ -149,7 +149,10 @@ module Prism parent = node.parent if parent.is_a?(ConstantReadNode) && parent.slice == "Prism" - compile_node(node.child) + name = node.name + raise CompilationError, node.inspect if name.nil? + + compile_constant_name(node, name) else compile_error(node) end @@ -158,14 +161,17 @@ module Prism # in ConstantReadNode # in String def compile_constant_read_node(node) - value = node.slice + compile_constant_name(node, node.name) + end - if Prism.const_defined?(value, false) - clazz = Prism.const_get(value) + # Compile a name associated with a constant. + def compile_constant_name(node, name) + if Prism.const_defined?(name, false) + clazz = Prism.const_get(name) ->(other) { clazz === other } - elsif Object.const_defined?(value, false) - clazz = Object.const_get(value) + elsif Object.const_defined?(name, false) + clazz = Object.const_get(name) ->(other) { clazz === other } else diff --git a/lib/prism/polyfill/byteindex.rb b/lib/prism/polyfill/byteindex.rb new file mode 100644 index 0000000000..98c6089f14 --- /dev/null +++ b/lib/prism/polyfill/byteindex.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Polyfill for String#byteindex, which didn't exist until Ruby 3.2. +if !("".respond_to?(:byteindex)) + String.include( + Module.new { + def byteindex(needle, offset = 0) + charindex = index(needle, offset) + slice(0...charindex).bytesize if charindex + end + } + ) +end diff --git a/lib/prism/polyfill/string.rb b/lib/prism/polyfill/string.rb deleted file mode 100644 index 582266d956..0000000000 --- a/lib/prism/polyfill/string.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -# Polyfill for String#unpack1 with the offset parameter. -if String.instance_method(:unpack1).parameters.none? { |_, name| name == :offset } - String.prepend( - Module.new { - def unpack1(format, offset: 0) # :nodoc: - offset == 0 ? super(format) : self[offset..].unpack1(format) # steep:ignore - end - } - ) -end diff --git a/lib/prism/polyfill/unpack1.rb b/lib/prism/polyfill/unpack1.rb new file mode 100644 index 0000000000..3fa9b5a0c5 --- /dev/null +++ b/lib/prism/polyfill/unpack1.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Polyfill for String#unpack1 with the offset parameter. Not all Ruby engines +# have Method#parameters implemented, so we check the arity instead if +# necessary. +if (unpack1 = String.instance_method(:unpack1)).respond_to?(:parameters) ? unpack1.parameters.none? { |_, name| name == :offset } : (unpack1.arity == 1) + String.prepend( + Module.new { + def unpack1(format, offset: 0) # :nodoc: + offset == 0 ? super(format) : self[offset..].unpack1(format) # steep:ignore + end + } + ) +end diff --git a/lib/prism/prism.gemspec b/lib/prism/prism.gemspec index c0f3db5594..374591bb70 100644 --- a/lib/prism/prism.gemspec +++ b/lib/prism/prism.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "prism" - spec.version = "0.25.0" + spec.version = "0.29.0" spec.authors = ["Shopify"] spec.email = ["ruby@shopify.com"] @@ -76,17 +76,18 @@ Gem::Specification.new do |spec| "lib/prism/dot_visitor.rb", "lib/prism/dsl.rb", "lib/prism/ffi.rb", + "lib/prism/inspect_visitor.rb", "lib/prism/lex_compat.rb", "lib/prism/mutation_compiler.rb", "lib/prism/node_ext.rb", - "lib/prism/node_inspector.rb", "lib/prism/node.rb", "lib/prism/pack.rb", "lib/prism/parse_result.rb", "lib/prism/parse_result/comments.rb", "lib/prism/parse_result/newlines.rb", "lib/prism/pattern.rb", - "lib/prism/polyfill/string.rb", + "lib/prism/polyfill/byteindex.rb", + "lib/prism/polyfill/unpack1.rb", "lib/prism/reflection.rb", "lib/prism/serialize.rb", "lib/prism/translation.rb", @@ -101,11 +102,42 @@ Gem::Specification.new do |spec| "lib/prism/translation/ripper/shim.rb", "lib/prism/translation/ruby_parser.rb", "lib/prism/visitor.rb", + "prism.gemspec", + "rbi/prism.rbi", + "rbi/prism/compiler.rbi", + "rbi/prism/inspect_visitor.rbi", + "rbi/prism/node_ext.rbi", + "rbi/prism/node.rbi", + "rbi/prism/parse_result.rbi", + "rbi/prism/reflection.rbi", + "rbi/prism/translation/parser.rbi", + "rbi/prism/translation/parser33.rbi", + "rbi/prism/translation/parser34.rbi", + "rbi/prism/translation/ripper.rbi", + "rbi/prism/visitor.rbi", + "sig/prism.rbs", + "sig/prism/compiler.rbs", + "sig/prism/dispatcher.rbs", + "sig/prism/dot_visitor.rbs", + "sig/prism/dsl.rbs", + "sig/prism/inspect_visitor.rbs", + "sig/prism/lex_compat.rbs", + "sig/prism/mutation_compiler.rbs", + "sig/prism/node_ext.rbs", + "sig/prism/node.rbs", + "sig/prism/pack.rbs", + "sig/prism/parse_result.rbs", + "sig/prism/pattern.rbs", + "sig/prism/reflection.rbs", + "sig/prism/serialize.rbs", + "sig/prism/visitor.rbs", "src/diagnostic.c", "src/encoding.c", "src/node.c", + "src/options.c", "src/pack.c", "src/prettyprint.c", + "src/prism.c", "src/regexp.c", "src/serialize.c", "src/static_literals.c", @@ -117,40 +149,10 @@ Gem::Specification.new do |spec| "src/util/pm_list.c", "src/util/pm_memchr.c", "src/util/pm_newline_list.c", - "src/util/pm_string.c", "src/util/pm_string_list.c", + "src/util/pm_string.c", "src/util/pm_strncasecmp.c", - "src/util/pm_strpbrk.c", - "src/options.c", - "src/prism.c", - "prism.gemspec", - "sig/prism.rbs", - "sig/prism/compiler.rbs", - "sig/prism/dispatcher.rbs", - "sig/prism/dot_visitor.rbs", - "sig/prism/dsl.rbs", - "sig/prism/mutation_compiler.rbs", - "sig/prism/node.rbs", - "sig/prism/node_ext.rbs", - "sig/prism/pack.rbs", - "sig/prism/parse_result.rbs", - "sig/prism/pattern.rbs", - "sig/prism/reflection.rbs", - "sig/prism/serialize.rbs", - "sig/prism/visitor.rbs", - "rbi/prism.rbi", - "rbi/prism/compiler.rbi", - "rbi/prism/desugar_compiler.rbi", - "rbi/prism/mutation_compiler.rbi", - "rbi/prism/node_ext.rbi", - "rbi/prism/node.rbi", - "rbi/prism/parse_result.rbi", - "rbi/prism/reflection.rbi", - "rbi/prism/translation/parser/compiler.rbi", - "rbi/prism/translation/ripper.rbi", - "rbi/prism/translation/ripper/ripper_compiler.rbi", - "rbi/prism/translation/ruby_parser.rbi", - "rbi/prism/visitor.rbi" + "src/util/pm_strpbrk.c" ] spec.extensions = ["ext/prism/extconf.rb"] diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb index 0d11b8f566..3748fc896e 100644 --- a/lib/prism/translation/parser.rb +++ b/lib/prism/translation/parser.rb @@ -1,6 +1,11 @@ # frozen_string_literal: true -require "parser" +begin + require "parser" +rescue LoadError + warn(%q{Error: Unable to load parser. Add `gem "parser"` to your Gemfile.}) + exit(1) +end module Prism module Translation @@ -46,7 +51,7 @@ module Prism source = source_buffer.source offset_cache = build_offset_cache(source) - result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version)), offset_cache) + result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version), scopes: [[]]), offset_cache) build_ast(result.value, offset_cache) ensure @@ -59,7 +64,7 @@ module Prism source = source_buffer.source offset_cache = build_offset_cache(source) - result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version)), offset_cache) + result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version), scopes: [[]]), offset_cache) [ build_ast(result.value, offset_cache), @@ -78,7 +83,7 @@ module Prism offset_cache = build_offset_cache(source) result = begin - unwrap(Prism.parse_lex(source, filepath: source_buffer.name, version: convert_for_prism(version)), offset_cache) + unwrap(Prism.parse_lex(source, filepath: source_buffer.name, version: convert_for_prism(version), scopes: [[]]), offset_cache) rescue ::Parser::SyntaxError raise if !recover end @@ -149,17 +154,17 @@ module Prism Diagnostic.new(:error, :endless_setter, {}, diagnostic_location, []) when :embdoc_term Diagnostic.new(:error, :embedded_document, {}, diagnostic_location, []) - when :incomplete_variable_class, :incomplete_variable_class_3_3_0 + when :incomplete_variable_class, :incomplete_variable_class_3_3 location = location.copy(length: location.length + 1) diagnostic_location = build_range(location, offset_cache) Diagnostic.new(:error, :cvar_name, { name: location.slice }, diagnostic_location, []) - when :incomplete_variable_instance, :incomplete_variable_instance_3_3_0 + when :incomplete_variable_instance, :incomplete_variable_instance_3_3 location = location.copy(length: location.length + 1) diagnostic_location = build_range(location, offset_cache) Diagnostic.new(:error, :ivar_name, { name: location.slice }, diagnostic_location, []) - when :invalid_variable_global, :invalid_variable_global_3_3_0 + when :invalid_variable_global, :invalid_variable_global_3_3 Diagnostic.new(:error, :gvar_name, { name: location.slice }, diagnostic_location, []) when :module_in_method Diagnostic.new(:error, :module_in_def, {}, diagnostic_location, []) @@ -284,7 +289,7 @@ module Prism def convert_for_prism(version) case version when 33 - "3.3.0" + "3.3.1" when 34 "3.4.0" else diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb index 9437589623..a6c3118efd 100644 --- a/lib/prism/translation/parser/compiler.rb +++ b/lib/prism/translation/parser/compiler.rb @@ -328,18 +328,48 @@ module Prism [], nil ), - [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], visit(node.value) ) end # foo.bar &&= baz # ^^^^^^^^^^^^^^^ - alias visit_call_and_write_node visit_call_operator_write_node + def visit_call_and_write_node(node) + call_operator_loc = node.call_operator_loc + + builder.op_assign( + builder.call_method( + visit(node.receiver), + call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)], + node.message_loc ? [node.read_name, srange(node.message_loc)] : nil, + nil, + [], + nil + ), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # foo.bar ||= baz # ^^^^^^^^^^^^^^^ - alias visit_call_or_write_node visit_call_operator_write_node + def visit_call_or_write_node(node) + call_operator_loc = node.call_operator_loc + + builder.op_assign( + builder.call_method( + visit(node.receiver), + call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)], + node.message_loc ? [node.read_name, srange(node.message_loc)] : nil, + nil, + [], + nil + ), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # foo.bar, = 1 # ^^^^^^^ @@ -419,18 +449,30 @@ module Prism def visit_class_variable_operator_write_node(node) builder.op_assign( builder.assignable(builder.cvar(token(node.name_loc))), - [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], visit(node.value) ) end # @@foo &&= bar # ^^^^^^^^^^^^^ - alias visit_class_variable_and_write_node visit_class_variable_operator_write_node + def visit_class_variable_and_write_node(node) + builder.op_assign( + builder.assignable(builder.cvar(token(node.name_loc))), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # @@foo ||= bar # ^^^^^^^^^^^^^ - alias visit_class_variable_or_write_node visit_class_variable_operator_write_node + def visit_class_variable_or_write_node(node) + builder.op_assign( + builder.assignable(builder.cvar(token(node.name_loc))), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # @@foo, = bar # ^^^^^ @@ -458,18 +500,30 @@ module Prism def visit_constant_operator_write_node(node) builder.op_assign( builder.assignable(builder.const([node.name, srange(node.name_loc)])), - [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], visit(node.value) ) end # Foo &&= bar # ^^^^^^^^^^^^ - alias visit_constant_and_write_node visit_constant_operator_write_node + def visit_constant_and_write_node(node) + builder.op_assign( + builder.assignable(builder.const([node.name, srange(node.name_loc)])), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # Foo ||= bar # ^^^^^^^^^^^^ - alias visit_constant_or_write_node visit_constant_operator_write_node + def visit_constant_or_write_node(node) + builder.op_assign( + builder.assignable(builder.const([node.name, srange(node.name_loc)])), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # Foo, = bar # ^^^ @@ -483,13 +537,13 @@ module Prism if node.parent.nil? builder.const_global( token(node.delimiter_loc), - [node.child.name, srange(node.child.location)] + [node.name, srange(node.name_loc)] ) else builder.const_fetch( visit(node.parent), token(node.delimiter_loc), - [node.child.name, srange(node.child.location)] + [node.name, srange(node.name_loc)] ) end end @@ -512,18 +566,30 @@ module Prism def visit_constant_path_operator_write_node(node) builder.op_assign( builder.assignable(visit(node.target)), - [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], visit(node.value) ) end # Foo::Bar &&= baz # ^^^^^^^^^^^^^^^^ - alias visit_constant_path_and_write_node visit_constant_path_operator_write_node + def visit_constant_path_and_write_node(node) + builder.op_assign( + builder.assignable(visit(node.target)), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # Foo::Bar ||= baz # ^^^^^^^^^^^^^^^^ - alias visit_constant_path_or_write_node visit_constant_path_operator_write_node + def visit_constant_path_or_write_node(node) + builder.op_assign( + builder.assignable(visit(node.target)), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # Foo::Bar, = baz # ^^^^^^^^ @@ -711,18 +777,30 @@ module Prism def visit_global_variable_operator_write_node(node) builder.op_assign( builder.assignable(builder.gvar(token(node.name_loc))), - [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], visit(node.value) ) end # $foo &&= bar # ^^^^^^^^^^^^ - alias visit_global_variable_and_write_node visit_global_variable_operator_write_node + def visit_global_variable_and_write_node(node) + builder.op_assign( + builder.assignable(builder.gvar(token(node.name_loc))), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # $foo ||= bar # ^^^^^^^^^^^^ - alias visit_global_variable_or_write_node visit_global_variable_operator_write_node + def visit_global_variable_or_write_node(node) + builder.op_assign( + builder.assignable(builder.gvar(token(node.name_loc))), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # $foo, = bar # ^^^^ @@ -839,7 +917,7 @@ module Prism token(node.in_loc), pattern, guard, - srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset || node.location.end_offset, [";", "then"]), + srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset, [";", "then"]), visit(node.statements) ) end @@ -857,18 +935,46 @@ module Prism visit_all(arguments), token(node.closing_loc) ), - [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], visit(node.value) ) end # foo[bar] &&= baz # ^^^^^^^^^^^^^^^^ - alias visit_index_and_write_node visit_index_operator_write_node + def visit_index_and_write_node(node) + arguments = node.arguments&.arguments || [] + arguments << node.block if node.block + + builder.op_assign( + builder.index( + visit(node.receiver), + token(node.opening_loc), + visit_all(arguments), + token(node.closing_loc) + ), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # foo[bar] ||= baz # ^^^^^^^^^^^^^^^^ - alias visit_index_or_write_node visit_index_operator_write_node + def visit_index_or_write_node(node) + arguments = node.arguments&.arguments || [] + arguments << node.block if node.block + + builder.op_assign( + builder.index( + visit(node.receiver), + token(node.opening_loc), + visit_all(arguments), + token(node.closing_loc) + ), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # foo[bar], = 1 # ^^^^^^^^ @@ -902,18 +1008,30 @@ module Prism def visit_instance_variable_operator_write_node(node) builder.op_assign( builder.assignable(builder.ivar(token(node.name_loc))), - [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], visit(node.value) ) end # @foo &&= bar # ^^^^^^^^^^^^ - alias visit_instance_variable_and_write_node visit_instance_variable_operator_write_node + def visit_instance_variable_and_write_node(node) + builder.op_assign( + builder.assignable(builder.ivar(token(node.name_loc))), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # @foo ||= bar # ^^^^^^^^^^^^ - alias visit_instance_variable_or_write_node visit_instance_variable_operator_write_node + def visit_instance_variable_or_write_node(node) + builder.op_assign( + builder.assignable(builder.ivar(token(node.name_loc))), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # @foo, = bar # ^^^^ @@ -1030,6 +1148,12 @@ module Prism end end + # -> { it } + # ^^^^^^^^^ + def visit_it_parameters_node(node) + builder.args(nil, [], nil, false) + end + # foo(bar: baz) # ^^^^^^^^ def visit_keyword_hash_node(node) @@ -1052,13 +1176,14 @@ module Prism # ^^^^^ def visit_lambda_node(node) parameters = node.parameters + implicit_parameters = parameters.is_a?(NumberedParametersNode) || parameters.is_a?(ItParametersNode) builder.block( builder.call_lambda(token(node.operator_loc)), [node.opening, srange(node.opening_loc)], if parameters.nil? builder.args(nil, [], nil, false) - elsif node.parameters.is_a?(NumberedParametersNode) + elsif implicit_parameters visit(node.parameters) else builder.args( @@ -1068,7 +1193,7 @@ module Prism false ) end, - node.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))), + node.body&.accept(copy_compiler(forwarding: implicit_parameters ? [] : find_forwarding(parameters&.parameters))), [node.closing, srange(node.closing_loc)] ) end @@ -1076,7 +1201,14 @@ module Prism # foo # ^^^ def visit_local_variable_read_node(node) - builder.ident([node.name, srange(node.location)]).updated(:lvar) + name = node.name + + # This is just a guess. parser doesn't have support for the implicit + # `it` variable yet, so we'll probably have to visit this once it + # does. + name = :it if name == :"0it" + + builder.ident([name, srange(node.location)]).updated(:lvar) end # foo = 1 @@ -1094,18 +1226,30 @@ module Prism def visit_local_variable_operator_write_node(node) builder.op_assign( builder.assignable(builder.ident(token(node.name_loc))), - [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], visit(node.value) ) end # foo &&= bar # ^^^^^^^^^^^ - alias visit_local_variable_and_write_node visit_local_variable_operator_write_node + def visit_local_variable_and_write_node(node) + builder.op_assign( + builder.assignable(builder.ident(token(node.name_loc))), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # foo ||= bar # ^^^^^^^^^^^ - alias visit_local_variable_or_write_node visit_local_variable_operator_write_node + def visit_local_variable_or_write_node(node) + builder.op_assign( + builder.assignable(builder.ident(token(node.name_loc))), + [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], + visit(node.value) + ) + end # foo, = bar # ^^^ @@ -1535,19 +1679,29 @@ module Prism elsif node.opening == "?" builder.character([node.unescaped, srange(node.location)]) else - parts = if node.content.lines.count <= 1 || node.unescaped.lines.count <= 1 - [builder.string_internal([node.unescaped, srange(node.content_loc)])] - else - start_offset = node.content_loc.start_offset + content_lines = node.content.lines + unescaped_lines = node.unescaped.lines - [node.content.lines, node.unescaped.lines].transpose.map do |content_line, unescaped_line| - end_offset = start_offset + content_line.length - offsets = srange_offsets(start_offset, end_offset) - start_offset = end_offset + parts = + if content_lines.length <= 1 || unescaped_lines.length <= 1 + [builder.string_internal([node.unescaped, srange(node.content_loc)])] + elsif content_lines.length != unescaped_lines.length + # This occurs when we have line continuations in the string. We + # need to come back and fix this, but for now this stops the + # code from breaking when we encounter it because of trying to + # transpose arrays of different lengths. + [builder.string_internal([node.unescaped, srange(node.content_loc)])] + else + start_offset = node.content_loc.start_offset - builder.string_internal([unescaped_line, offsets]) + [content_lines, unescaped_lines].transpose.map do |content_line, unescaped_line| + end_offset = start_offset + content_line.length + offsets = srange_offsets(start_offset, end_offset) + start_offset = end_offset + + builder.string_internal([unescaped_line, offsets]) + end end - end builder.string_compose( token(node.opening_loc), @@ -1655,7 +1809,7 @@ module Prism end # until foo; bar end - # ^^^^^^^^^^^^^^^^^ + # ^^^^^^^^^^^^^^^^^^ # # bar until foo # ^^^^^^^^^^^^^ @@ -1688,7 +1842,7 @@ module Prism if node.then_keyword_loc token(node.then_keyword_loc) else - srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset || (node.conditions.last.location.end_offset + 1), [";"]) + srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset, [";"]) end, visit(node.statements) ) @@ -1847,12 +2001,16 @@ module Prism # Constructs a new source range by finding the given tokens between the # given start offset and end offset. If the needle is not found, it - # returns nil. + # returns nil. Importantly it does not search past newlines or comments. + # + # Note that end_offset is allowed to be nil, in which case this will + # search until the end of the string. def srange_find(start_offset, end_offset, tokens) - tokens.find do |token| - next unless (index = source_buffer.source.byteslice(start_offset...end_offset).index(token)) - offset = start_offset + index - return [token, Range.new(source_buffer, offset_cache[offset], offset_cache[offset + token.length])] + if (match = source_buffer.source.byteslice(start_offset...end_offset).match(/(\s*)(#{tokens.join("|")})/)) + _, whitespace, token = *match + token_offset = start_offset + whitespace.bytesize + + [token, Range.new(source_buffer, offset_cache[token_offset], offset_cache[token_offset + token.bytesize])] end end @@ -1865,13 +2023,14 @@ module Prism def visit_block(call, block) if block parameters = block.parameters + implicit_parameters = parameters.is_a?(NumberedParametersNode) || parameters.is_a?(ItParametersNode) builder.block( call, token(block.opening_loc), if parameters.nil? builder.args(nil, [], nil, false) - elsif parameters.is_a?(NumberedParametersNode) + elsif implicit_parameters visit(parameters) else builder.args( @@ -1886,7 +2045,7 @@ module Prism false ) end, - block.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))), + block.body&.accept(copy_compiler(forwarding: implicit_parameters ? [] : find_forwarding(parameters&.parameters))), token(block.closing_loc) ) else diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb index 3c06f6a40d..68f658565d 100644 --- a/lib/prism/translation/ripper.rb +++ b/lib/prism/translation/ripper.rb @@ -19,31 +19,31 @@ module Prism # The main known difference is that we may omit dispatching some events in # some cases. This impacts the following events: # - # * on_assign_error - # * on_comma - # * on_ignored_nl - # * on_ignored_sp - # * on_kw - # * on_label_end - # * on_lbrace - # * on_lbracket - # * on_lparen - # * on_nl - # * on_op - # * on_operator_ambiguous - # * on_rbrace - # * on_rbracket - # * on_rparen - # * on_semicolon - # * on_sp - # * on_symbeg - # * on_tstring_beg - # * on_tstring_end + # - on_assign_error + # - on_comma + # - on_ignored_nl + # - on_ignored_sp + # - on_kw + # - on_label_end + # - on_lbrace + # - on_lbracket + # - on_lparen + # - on_nl + # - on_op + # - on_operator_ambiguous + # - on_rbrace + # - on_rbracket + # - on_rparen + # - on_semicolon + # - on_sp + # - on_symbeg + # - on_tstring_beg + # - on_tstring_end # class Ripper < Compiler # Parses the given Ruby program read from +src+. # +src+ must be a String or an IO or a object with a #gets method. - def Ripper.parse(src, filename = "(ripper)", lineno = 1) + def self.parse(src, filename = "(ripper)", lineno = 1) new(src, filename, lineno).parse end @@ -54,22 +54,22 @@ module Prism # By default, this method does not handle syntax errors in +src+, # use the +raise_errors+ keyword to raise a SyntaxError for an error in +src+. # - # require 'ripper' - # require 'pp' + # require "ripper" + # require "pp" # - # pp Ripper.lex("def m(a) nil end") - # #=> [[[1, 0], :on_kw, "def", FNAME ], - # [[1, 3], :on_sp, " ", FNAME ], - # [[1, 4], :on_ident, "m", ENDFN ], - # [[1, 5], :on_lparen, "(", BEG|LABEL], - # [[1, 6], :on_ident, "a", ARG ], - # [[1, 7], :on_rparen, ")", ENDFN ], - # [[1, 8], :on_sp, " ", BEG ], - # [[1, 9], :on_kw, "nil", END ], - # [[1, 12], :on_sp, " ", END ], - # [[1, 13], :on_kw, "end", END ]] + # pp Ripper.lex("def m(a) nil end") + # #=> [[[1, 0], :on_kw, "def", FNAME ], + # [[1, 3], :on_sp, " ", FNAME ], + # [[1, 4], :on_ident, "m", ENDFN ], + # [[1, 5], :on_lparen, "(", BEG|LABEL], + # [[1, 6], :on_ident, "a", ARG ], + # [[1, 7], :on_rparen, ")", ENDFN ], + # [[1, 8], :on_sp, " ", BEG ], + # [[1, 9], :on_kw, "nil", END ], + # [[1, 12], :on_sp, " ", END ], + # [[1, 13], :on_kw, "end", END ]] # - def Ripper.lex(src, filename = "-", lineno = 1, raise_errors: false) + def self.lex(src, filename = "-", lineno = 1, raise_errors: false) result = Prism.lex_compat(src, filepath: filename, line: lineno) if result.failure? && raise_errors @@ -368,17 +368,17 @@ module Prism # returning +nil+ in such cases. Use the +raise_errors+ keyword # to raise a SyntaxError for an error in +src+. # - # require "ripper" - # require "pp" + # require "ripper" + # require "pp" # - # pp Ripper.sexp("def m(a) nil end") - # #=> [:program, - # [[:def, - # [:@ident, "m", [1, 4]], - # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil, nil, nil, nil]], - # [:bodystmt, [[:var_ref, [:@kw, "nil", [1, 9]]]], nil, nil, nil]]]] + # pp Ripper.sexp("def m(a) nil end") + # #=> [:program, + # [[:def, + # [:@ident, "m", [1, 4]], + # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil, nil, nil, nil]], + # [:bodystmt, [[:var_ref, [:@kw, "nil", [1, 9]]]], nil, nil, nil]]]] # - def Ripper.sexp(src, filename = "-", lineno = 1, raise_errors: false) + def self.sexp(src, filename = "-", lineno = 1, raise_errors: false) builder = SexpBuilderPP.new(src, filename, lineno) sexp = builder.parse if builder.error? @@ -397,23 +397,23 @@ module Prism # returning +nil+ in such cases. Use the +raise_errors+ keyword # to raise a SyntaxError for an error in +src+. # - # require 'ripper' - # require 'pp' + # require "ripper" + # require "pp" # - # pp Ripper.sexp_raw("def m(a) nil end") - # #=> [:program, - # [:stmts_add, - # [:stmts_new], - # [:def, - # [:@ident, "m", [1, 4]], - # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil]], - # [:bodystmt, - # [:stmts_add, [:stmts_new], [:var_ref, [:@kw, "nil", [1, 9]]]], - # nil, - # nil, - # nil]]]] + # pp Ripper.sexp_raw("def m(a) nil end") + # #=> [:program, + # [:stmts_add, + # [:stmts_new], + # [:def, + # [:@ident, "m", [1, 4]], + # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil]], + # [:bodystmt, + # [:stmts_add, [:stmts_new], [:var_ref, [:@kw, "nil", [1, 9]]]], + # nil, + # nil, + # nil]]]] # - def Ripper.sexp_raw(src, filename = "-", lineno = 1, raise_errors: false) + def self.sexp_raw(src, filename = "-", lineno = 1, raise_errors: false) builder = SexpBuilder.new(src, filename, lineno) sexp = builder.parse if builder.error? @@ -1181,8 +1181,8 @@ module Prism bounds(node.location) target = on_field(receiver, call_operator, message) - bounds(node.operator_loc) - operator = on_op("#{node.operator}=") + bounds(node.binary_operator_loc) + operator = on_op("#{node.binary_operator}=") value = visit_write_value(node.value) bounds(node.location) @@ -1339,8 +1339,8 @@ module Prism bounds(node.name_loc) target = on_var_field(on_cvar(node.name.to_s)) - bounds(node.operator_loc) - operator = on_op("#{node.operator}=") + bounds(node.binary_operator_loc) + operator = on_op("#{node.binary_operator}=") value = visit_write_value(node.value) bounds(node.location) @@ -1409,8 +1409,8 @@ module Prism bounds(node.name_loc) target = on_var_field(on_const(node.name.to_s)) - bounds(node.operator_loc) - operator = on_op("#{node.operator}=") + bounds(node.binary_operator_loc) + operator = on_op("#{node.binary_operator}=") value = visit_write_value(node.value) bounds(node.location) @@ -1456,16 +1456,16 @@ module Prism # ^^^^^^^^ def visit_constant_path_node(node) if node.parent.nil? - bounds(node.child.location) - child = on_const(node.child.name.to_s) + bounds(node.name_loc) + child = on_const(node.name.to_s) bounds(node.location) on_top_const_ref(child) else parent = visit(node.parent) - bounds(node.child.location) - child = on_const(node.child.name.to_s) + bounds(node.name_loc) + child = on_const(node.name.to_s) bounds(node.location) on_const_path_ref(parent, child) @@ -1488,16 +1488,16 @@ module Prism # Visit a constant path that is part of a write node. private def visit_constant_path_write_node_target(node) if node.parent.nil? - bounds(node.child.location) - child = on_const(node.child.name.to_s) + bounds(node.name_loc) + child = on_const(node.name.to_s) bounds(node.location) on_top_const_field(child) else parent = visit(node.parent) - bounds(node.child.location) - child = on_const(node.child.name.to_s) + bounds(node.name_loc) + child = on_const(node.name.to_s) bounds(node.location) on_const_path_field(parent, child) @@ -1510,8 +1510,8 @@ module Prism target = visit_constant_path_write_node_target(node.target) value = visit(node.value) - bounds(node.operator_loc) - operator = on_op("#{node.operator}=") + bounds(node.binary_operator_loc) + operator = on_op("#{node.binary_operator}=") value = visit_write_value(node.value) bounds(node.location) @@ -1802,8 +1802,8 @@ module Prism bounds(node.name_loc) target = on_var_field(on_gvar(node.name.to_s)) - bounds(node.operator_loc) - operator = on_op("#{node.operator}=") + bounds(node.binary_operator_loc) + operator = on_op("#{node.binary_operator}=") value = visit_write_value(node.value) bounds(node.location) @@ -1983,8 +1983,8 @@ module Prism bounds(node.location) target = on_aref_field(receiver, arguments) - bounds(node.operator_loc) - operator = on_op("#{node.operator}=") + bounds(node.binary_operator_loc) + operator = on_op("#{node.binary_operator}=") value = visit_write_value(node.value) bounds(node.location) @@ -2059,8 +2059,8 @@ module Prism bounds(node.name_loc) target = on_var_field(on_ivar(node.name.to_s)) - bounds(node.operator_loc) - operator = on_op("#{node.operator}=") + bounds(node.binary_operator_loc) + operator = on_op("#{node.binary_operator}=") value = visit_write_value(node.value) bounds(node.location) @@ -2337,8 +2337,8 @@ module Prism bounds(node.name_loc) target = on_var_field(on_ident(node.name_loc.slice)) - bounds(node.operator_loc) - operator = on_op("#{node.operator}=") + bounds(node.binary_operator_loc) + operator = on_op("#{node.binary_operator}=") value = visit_write_value(node.value) bounds(node.location) @@ -3267,7 +3267,11 @@ module Prism # Lazily initialize the parse result. def result - @result ||= Prism.parse(source) + @result ||= + begin + scopes = RUBY_VERSION >= "3.3.0" ? [] : [[]] + Prism.parse(source, scopes: scopes) + end end ########################################################################## diff --git a/lib/prism/translation/ruby_parser.rb b/lib/prism/translation/ruby_parser.rb index 5c59fe3181..43aac23be7 100644 --- a/lib/prism/translation/ruby_parser.rb +++ b/lib/prism/translation/ruby_parser.rb @@ -1,6 +1,11 @@ # frozen_string_literal: true -require "ruby_parser" +begin + require "ruby_parser" +rescue LoadError + warn(%q{Error: Unable to load ruby_parser. Add `gem "ruby_parser"` to your Gemfile.}) + exit(1) +end module Prism module Translation @@ -271,9 +276,9 @@ module Prism # ^^^^^^^^^^^^^^^ def visit_call_operator_write_node(node) if op_asgn?(node) - s(node, op_asgn_type(node, :op_asgn), visit(node.receiver), visit_write_value(node.value), node.read_name, node.operator) + s(node, op_asgn_type(node, :op_asgn), visit(node.receiver), visit_write_value(node.value), node.read_name, node.binary_operator) else - s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, node.operator, visit_write_value(node.value)) + s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, node.binary_operator, visit_write_value(node.value)) end end @@ -372,7 +377,7 @@ module Prism # @@foo += bar # ^^^^^^^^^^^^ def visit_class_variable_operator_write_node(node) - s(node, class_variable_write_type, node.name, s(node, :call, s(node, :cvar, node.name), node.operator, visit_write_value(node.value))) + s(node, class_variable_write_type, node.name, s(node, :call, s(node, :cvar, node.name), node.binary_operator, visit_write_value(node.value))) end # @@foo &&= bar @@ -417,7 +422,7 @@ module Prism # Foo += bar # ^^^^^^^^^^^ def visit_constant_operator_write_node(node) - s(node, :cdecl, node.name, s(node, :call, s(node, :const, node.name), node.operator, visit_write_value(node.value))) + s(node, :cdecl, node.name, s(node, :call, s(node, :const, node.name), node.binary_operator, visit_write_value(node.value))) end # Foo &&= bar @@ -442,9 +447,9 @@ module Prism # ^^^^^^^^ def visit_constant_path_node(node) if node.parent.nil? - s(node, :colon3, node.child.name) + s(node, :colon3, node.name) else - s(node, :colon2, visit(node.parent), node.child.name) + s(node, :colon2, visit(node.parent), node.name) end end @@ -460,7 +465,7 @@ module Prism # Foo::Bar += baz # ^^^^^^^^^^^^^^^ def visit_constant_path_operator_write_node(node) - s(node, :op_asgn, visit(node.target), node.operator, visit_write_value(node.value)) + s(node, :op_asgn, visit(node.target), node.binary_operator, visit_write_value(node.value)) end # Foo::Bar &&= baz @@ -627,7 +632,7 @@ module Prism # $foo += bar # ^^^^^^^^^^^ def visit_global_variable_operator_write_node(node) - s(node, :gasgn, node.name, s(node, :call, s(node, :gvar, node.name), node.operator, visit(node.value))) + s(node, :gasgn, node.name, s(node, :call, s(node, :gvar, node.name), node.binary_operator, visit(node.value))) end # $foo &&= bar @@ -719,7 +724,7 @@ module Prism arglist << visit(node.block) if !node.block.nil? end - s(node, :op_asgn1, visit(node.receiver), arglist, node.operator, visit_write_value(node.value)) + s(node, :op_asgn1, visit(node.receiver), arglist, node.binary_operator, visit_write_value(node.value)) end # foo[bar] &&= baz @@ -775,7 +780,7 @@ module Prism # @foo += bar # ^^^^^^^^^^^ def visit_instance_variable_operator_write_node(node) - s(node, :iasgn, node.name, s(node, :call, s(node, :ivar, node.name), node.operator, visit_write_value(node.value))) + s(node, :iasgn, node.name, s(node, :call, s(node, :ivar, node.name), node.binary_operator, visit_write_value(node.value))) end # @foo &&= bar @@ -805,17 +810,29 @@ module Prism # if /foo #{bar}/ then end # ^^^^^^^^^^^^ def visit_interpolated_match_last_line_node(node) - s(node, :match, s(node, :dregx).concat(visit_interpolated_parts(node.parts))) + parts = visit_interpolated_parts(node.parts) + regexp = + if parts.length == 1 + s(node, :lit, Regexp.new(parts.first, node.options)) + else + s(node, :dregx).concat(parts).tap do |result| + options = node.options + result << options if options != 0 + end + end + + s(node, :match, regexp) end # /foo #{bar}/ # ^^^^^^^^^^^^ def visit_interpolated_regular_expression_node(node) - if node.parts.all? { |part| part.is_a?(StringNode) || (part.is_a?(EmbeddedStatementsNode) && part.statements&.body&.length == 1 && part.statements.body.first.is_a?(StringNode)) } - unescaped = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : part.statements.body.first.unescaped }.join - s(node, :lit, Regexp.new(unescaped, node.options)) + parts = visit_interpolated_parts(node.parts) + + if parts.length == 1 + s(node, :lit, Regexp.new(parts.first, node.options)) else - s(node, :dregx).concat(visit_interpolated_parts(node.parts)).tap do |result| + s(node, :dregx).concat(parts).tap do |result| options = node.options result << options if options != 0 end @@ -825,45 +842,71 @@ module Prism # "foo #{bar}" # ^^^^^^^^^^^^ def visit_interpolated_string_node(node) - if (node.parts.all? { |part| part.is_a?(StringNode) || (part.is_a?(EmbeddedStatementsNode) && part.statements&.body&.length == 1 && part.statements.body.first.is_a?(StringNode)) }) || - (node.opening.nil? && node.parts.all? { |part| part.is_a?(StringNode) && !part.opening_loc.nil? }) - unescaped = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : part.statements.body.first.unescaped }.join - s(node, :str, unescaped) - else - s(node, :dstr).concat(visit_interpolated_parts(node.parts)) - end + parts = visit_interpolated_parts(node.parts) + parts.length == 1 ? s(node, :str, parts.first) : s(node, :dstr).concat(parts) end # :"foo #{bar}" # ^^^^^^^^^^^^^ def visit_interpolated_symbol_node(node) - if node.parts.all? { |part| part.is_a?(StringNode) || (part.is_a?(EmbeddedStatementsNode) && part.statements&.body&.length == 1 && part.statements.body.first.is_a?(StringNode)) } - unescaped = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : part.statements.body.first.unescaped }.join - s(node, :lit, unescaped.to_sym) - else - s(node, :dsym).concat(visit_interpolated_parts(node.parts)) - end + parts = visit_interpolated_parts(node.parts) + parts.length == 1 ? s(node, :lit, parts.first.to_sym) : s(node, :dsym).concat(parts) end # `foo #{bar}` # ^^^^^^^^^^^^ def visit_interpolated_x_string_node(node) - children = visit_interpolated_parts(node.parts) - s(node.heredoc? ? node.parts.first : node, :dxstr).concat(children) + source = node.heredoc? ? node.parts.first : node + parts = visit_interpolated_parts(node.parts) + parts.length == 1 ? s(source, :xstr, parts.first) : s(source, :dxstr).concat(parts) end # Visit the interpolated content of the string-like node. private def visit_interpolated_parts(parts) - parts.each_with_object([]).with_index do |(part, results), index| - if index == 0 - if part.is_a?(StringNode) - results << part.unescaped + visited = [] + parts.each do |part| + result = visit(part) + + if result[0] == :evstr && result[1] + if result[1][0] == :str + visited << result[1] + elsif result[1][0] == :dstr + visited.concat(result[1][1..-1]) + else + visited << result + end + else + visited << result + end + end + + state = :beginning #: :beginning | :string_content | :interpolated_content + + visited.each_with_object([]) do |result, results| + case state + when :beginning + if result.is_a?(String) + results << result + state = :string_content + elsif result.is_a?(Array) && result[0] == :str + results << result[1] + state = :string_content else results << "" - results << visit(part) + results << result + state = :interpolated_content + end + when :string_content + if result.is_a?(String) + results[0] << result + elsif result.is_a?(Array) && result[0] == :str + results[0] << result[1] + else + results << result + state = :interpolated_content end else - results << visit(part) + results << result end end end @@ -922,7 +965,7 @@ module Prism # foo += bar # ^^^^^^^^^^ def visit_local_variable_operator_write_node(node) - s(node, :lasgn, node.name, s(node, :call, s(node, :lvar, node.name), node.operator, visit_write_value(node.value))) + s(node, :lasgn, node.name, s(node, :call, s(node, :lvar, node.name), node.binary_operator, visit_write_value(node.value))) end # foo &&= bar @@ -1297,7 +1340,7 @@ module Prism # __FILE__ # ^^^^^^^^ def visit_source_file_node(node) - s(node, :str, file) + s(node, :str, node.filepath) end # __LINE__ @@ -1498,13 +1541,13 @@ module Prism # Parse the given source and translate it into the seattlerb/ruby_parser # gem's Sexp format. def parse(source, filepath = "(string)") - translate(Prism.parse(source), filepath) + translate(Prism.parse(source, filepath: filepath, scopes: [[]]), filepath) end # Parse the given file and translate it into the seattlerb/ruby_parser # gem's Sexp format. def parse_file(filepath) - translate(Prism.parse_file(filepath), filepath) + translate(Prism.parse_file(filepath, scopes: [[]]), filepath) end class << self diff --git a/lib/reline.rb b/lib/reline.rb index f0060f5c9c..fb00b96531 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -225,17 +225,20 @@ module Reline journey_data = completion_journey_data return unless journey_data - target = journey_data.list[journey_data.pointer] + target = journey_data.list.first + completed = journey_data.list[journey_data.pointer] result = journey_data.list.drop(1) pointer = journey_data.pointer - 1 - return if target.empty? || (result == [target] && pointer < 0) + return if completed.empty? || (result == [completed] && pointer < 0) target_width = Reline::Unicode.calculate_width(target) - x = cursor_pos.x - target_width - if x < 0 - x = screen_width + x + completed_width = Reline::Unicode.calculate_width(completed) + if cursor_pos.x <= completed_width - target_width + # When target is rendered on the line above cursor position + x = screen_width - completed_width y = -1 else + x = [cursor_pos.x - completed_width, 0].max y = 0 end cursor_pos_to_render = Reline::CursorPos.new(x, y) @@ -309,6 +312,10 @@ module Reline $stderr.sync = true $stderr.puts "Reline is used by #{Process.pid}" end + unless config.test_mode or config.loaded? + config.read + io_gate.set_default_key_bindings(config) + end otio = io_gate.prep may_req_ambiguous_char_width @@ -335,12 +342,6 @@ module Reline end end - unless config.test_mode - config.read - config.reset_default_key_bindings - io_gate.set_default_key_bindings(config) - end - line_editor.print_nomultiline_prompt(prompt) line_editor.update_dialogs line_editor.rerender @@ -350,7 +351,15 @@ module Reline loop do read_io(config.keyseq_timeout) { |inputs| line_editor.set_pasting_state(io_gate.in_pasting?) - inputs.each { |key| line_editor.update(key) } + inputs.each do |key| + if key.char == :bracketed_paste_start + text = io_gate.read_bracketed_paste + line_editor.insert_pasted_text(text) + line_editor.scroll_into_view + else + line_editor.update(key) + end + end } if line_editor.finished? line_editor.render_finished diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb index a3719f502c..45a475a787 100644 --- a/lib/reline/ansi.rb +++ b/lib/reline/ansi.rb @@ -45,6 +45,7 @@ class Reline::ANSI end def self.set_default_key_bindings(config, allow_terminfo: true) + set_bracketed_paste_key_bindings(config) set_default_key_bindings_ansi_cursor(config) if allow_terminfo && Reline::Terminfo.enabled? set_default_key_bindings_terminfo(config) @@ -66,6 +67,12 @@ class Reline::ANSI end end + def self.set_bracketed_paste_key_bindings(config) + [:emacs, :vi_insert, :vi_command].each do |keymap| + config.add_default_key_binding_by_keymap(keymap, START_BRACKETED_PASTE.bytes, :bracketed_paste_start) + end + end + def self.set_default_key_bindings_ansi_cursor(config) ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)| bindings = [["\e[#{char}", default_func]] # CSI + char @@ -178,46 +185,26 @@ class Reline::ANSI nil end - @@in_bracketed_paste_mode = false - START_BRACKETED_PASTE = String.new("\e[200~,", encoding: Encoding::ASCII_8BIT) - END_BRACKETED_PASTE = String.new("\e[200~.", encoding: Encoding::ASCII_8BIT) - def self.getc_with_bracketed_paste(timeout_second) + START_BRACKETED_PASTE = String.new("\e[200~", encoding: Encoding::ASCII_8BIT) + END_BRACKETED_PASTE = String.new("\e[201~", encoding: Encoding::ASCII_8BIT) + def self.read_bracketed_paste buffer = String.new(encoding: Encoding::ASCII_8BIT) - buffer << inner_getc(timeout_second) - while START_BRACKETED_PASTE.start_with?(buffer) or END_BRACKETED_PASTE.start_with?(buffer) do - if START_BRACKETED_PASTE == buffer - @@in_bracketed_paste_mode = true - return inner_getc(timeout_second) - elsif END_BRACKETED_PASTE == buffer - @@in_bracketed_paste_mode = false - ungetc(-1) - return inner_getc(timeout_second) - end - succ_c = inner_getc(Reline.core.config.keyseq_timeout) - - if succ_c - buffer << succ_c - else - break - end + until buffer.end_with?(END_BRACKETED_PASTE) + c = inner_getc(Float::INFINITY) + break unless c + buffer << c end - buffer.bytes.reverse_each do |ch| - ungetc ch - end - inner_getc(timeout_second) + string = buffer.delete_suffix(END_BRACKETED_PASTE).force_encoding(encoding) + string.valid_encoding? ? string : '' end # if the usage expects to wait indefinitely, use Float::INFINITY for timeout_second def self.getc(timeout_second) - if Reline.core.config.enable_bracketed_paste - getc_with_bracketed_paste(timeout_second) - else - inner_getc(timeout_second) - end + inner_getc(timeout_second) end def self.in_pasting? - @@in_bracketed_paste_mode or (not empty_buffer?) + not empty_buffer? end def self.empty_buffer? @@ -284,7 +271,7 @@ class Reline::ANSI buf = @@output.pread(@@output.pos, 0) row = buf.count("\n") column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0 - rescue Errno::ESPIPE + rescue Errno::ESPIPE, IOError # Just returns column 1 for ambiguous width because this I/O is not # tty and can't seek. row = 0 @@ -361,11 +348,15 @@ class Reline::ANSI end def self.prep + # Enable bracketed paste + @@output.write "\e[?2004h" if Reline.core.config.enable_bracketed_paste retrieve_keybuffer nil end def self.deprep(otio) + # Disable bracketed paste + @@output.write "\e[?2004l" if Reline.core.config.enable_bracketed_paste Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler end end diff --git a/lib/reline/config.rb b/lib/reline/config.rb index 4c07a73701..d44c2675ab 100644 --- a/lib/reline/config.rb +++ b/lib/reline/config.rb @@ -8,31 +8,12 @@ class Reline::Config end VARIABLE_NAMES = %w{ - bind-tty-special-chars - blink-matching-paren - byte-oriented completion-ignore-case convert-meta disable-completion - enable-keypad - expand-tilde - history-preserve-point history-size - horizontal-scroll-mode - input-meta keyseq-timeout - mark-directories - mark-modified-lines - mark-symlinked-directories - match-hidden-files - meta-flag - output-meta - page-completions - prefer-visible-bell - print-completions-horizontally show-all-if-ambiguous - show-all-if-unmodified - visible-stats show-mode-in-prompt vi-cmd-mode-string vi-ins-mode-string @@ -53,8 +34,6 @@ class Reline::Config @additional_key_bindings[:vi_insert] = {} @additional_key_bindings[:vi_command] = {} @oneshot_key_bindings = {} - @skip_section = nil - @if_stack = nil @editing_mode_label = :emacs @keymap_label = :emacs @keymap_prefix = [] @@ -71,17 +50,15 @@ class Reline::Config @test_mode = false @autocompletion = false @convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding) + @loaded = false + @enable_bracketed_paste = true end def reset if editing_mode_is?(:vi_command) @editing_mode_label = :vi_insert end - @additional_key_bindings.keys.each do |key| - @additional_key_bindings[key].clear - end @oneshot_key_bindings.clear - reset_default_key_bindings end def editing_mode @@ -100,6 +77,10 @@ class Reline::Config @key_actors[@keymap_label] end + def loaded? + @loaded + end + def inputrc_path case ENV['INPUTRC'] when nil, '' @@ -131,6 +112,7 @@ class Reline::Config end def read(file = nil) + @loaded = true file ||= default_inputrc_path begin if file.respond_to?(:readlines) @@ -173,12 +155,6 @@ class Reline::Config @key_actors[@keymap_label].default_key_bindings[keystroke] = target end - def reset_default_key_bindings - @key_actors.values.each do |ka| - ka.reset_default_key_bindings - end - end - def read_lines(lines, file = nil) if not lines.empty? and lines.first.encoding != Reline.encoding_system_needs begin @@ -190,9 +166,7 @@ class Reline::Config end end end - conditions = [@skip_section, @if_stack] - @skip_section = nil - @if_stack = [] + if_stack = [] lines.each_with_index do |line, no| next if line.match(/\A\s*#/) @@ -201,11 +175,11 @@ class Reline::Config line = line.chomp.lstrip if line.start_with?('$') - handle_directive(line[1..-1], file, no) + handle_directive(line[1..-1], file, no, if_stack) next end - next if @skip_section + next if if_stack.any? { |_no, skip| skip } case line when /^set +([^ ]+) +([^ ]+)/i @@ -214,43 +188,47 @@ class Reline::Config next when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o key, func_name = $1, $2 + func_name = func_name.split.first keystroke, func = bind_key(key, func_name) next unless keystroke @additional_key_bindings[@keymap_label][@keymap_prefix + keystroke] = func end end - unless @if_stack.empty? - raise InvalidInputrc, "#{file}:#{@if_stack.last[1]}: unclosed if" + unless if_stack.empty? + raise InvalidInputrc, "#{file}:#{if_stack.last[0]}: unclosed if" end - ensure - @skip_section, @if_stack = conditions end - def handle_directive(directive, file, no) + def handle_directive(directive, file, no, if_stack) directive, args = directive.split(' ') case directive when 'if' condition = false case args - when 'mode' + when /^mode=(vi|emacs)$/i + mode = $1.downcase + # NOTE: mode=vi means vi-insert mode + mode = 'vi_insert' if mode == 'vi' + if @editing_mode_label == mode.to_sym + condition = true + end when 'term' when 'version' else # application name condition = true if args == 'Ruby' condition = true if args == 'Reline' end - @if_stack << [file, no, @skip_section] - @skip_section = !condition + if_stack << [no, !condition] when 'else' - if @if_stack.empty? + if if_stack.empty? raise InvalidInputrc, "#{file}:#{no}: unmatched else" end - @skip_section = !@skip_section + if_stack.last[1] = !if_stack.last[1] when 'endif' - if @if_stack.empty? + if if_stack.empty? raise InvalidInputrc, "#{file}:#{no}: unmatched endif" end - @skip_section = @if_stack.pop + if_stack.pop when 'include' read(File.expand_path(args)) end diff --git a/lib/reline/key_actor/base.rb b/lib/reline/key_actor/base.rb index a1cd7fb2a1..194e98938c 100644 --- a/lib/reline/key_actor/base.rb +++ b/lib/reline/key_actor/base.rb @@ -12,8 +12,4 @@ class Reline::KeyActor::Base def default_key_bindings @default_key_bindings end - - def reset_default_key_bindings - @default_key_bindings.clear - end end diff --git a/lib/reline/key_actor/emacs.rb b/lib/reline/key_actor/emacs.rb index 5d0a7fb63d..edd88289a3 100644 --- a/lib/reline/key_actor/emacs.rb +++ b/lib/reline/key_actor/emacs.rb @@ -19,7 +19,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base # 8 ^H :em_delete_prev_char, # 9 ^I - :ed_unassigned, + :complete, # 10 ^J :ed_newline, # 11 ^K @@ -63,7 +63,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base # 30 ^^ :ed_unassigned, # 31 ^_ - :ed_unassigned, + :undo, # 32 SPACE :ed_insert, # 33 ! diff --git a/lib/reline/key_actor/vi_insert.rb b/lib/reline/key_actor/vi_insert.rb index c3d7f9c12d..f8ccf468c6 100644 --- a/lib/reline/key_actor/vi_insert.rb +++ b/lib/reline/key_actor/vi_insert.rb @@ -19,7 +19,7 @@ class Reline::KeyActor::ViInsert < Reline::KeyActor::Base # 8 ^H :vi_delete_prev_char, # 9 ^I - :ed_insert, + :complete, # 10 ^J :ed_newline, # 11 ^K @@ -29,11 +29,11 @@ class Reline::KeyActor::ViInsert < Reline::KeyActor::Base # 13 ^M :ed_newline, # 14 ^N - :ed_insert, + :menu_complete, # 15 ^O :ed_insert, # 16 ^P - :ed_insert, + :menu_complete_backward, # 17 ^Q :ed_ignore, # 18 ^R diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 6e5ef11bc0..23ece60220 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -4,7 +4,6 @@ require 'reline/unicode' require 'tempfile' class Reline::LineEditor - # TODO: undo # TODO: Use "private alias_method" idiom after drop Ruby 2.5. attr_reader :byte_pointer attr_accessor :confirm_multiline_termination_proc @@ -75,7 +74,7 @@ class Reline::LineEditor def initialize(config, encoding) @config = config @completion_append_character = '' - @screen_size = Reline::IOGate.get_screen_size + @screen_size = [0, 0] # Should be initialized with actual winsize in LineEditor#reset reset_variables(encoding: encoding) end @@ -235,7 +234,6 @@ class Reline::LineEditor @vi_waiting_operator_arg = nil @completion_journey_state = nil @completion_state = CompletionState::NORMAL - @completion_occurs = false @perfect_matched = nil @menu_info = nil @searching_prompt = nil @@ -252,6 +250,8 @@ class Reline::LineEditor @resized = false @cache = {} @rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0) + @past_lines = [] + @undoing = false reset_line end @@ -284,7 +284,7 @@ class Reline::LineEditor indent1 = @auto_indent_proc.(@buffer_of_lines.take(@line_index - 1).push(''), @line_index - 1, 0, true) indent2 = @auto_indent_proc.(@buffer_of_lines.take(@line_index), @line_index - 1, @buffer_of_lines[@line_index - 1].bytesize, false) indent = indent2 || indent1 - @buffer_of_lines[@line_index - 1] = ' ' * indent + @buffer_of_lines[@line_index - 1].gsub(/\A */, '') + @buffer_of_lines[@line_index - 1] = ' ' * indent + @buffer_of_lines[@line_index - 1].gsub(/\A\s*/, '') ) process_auto_indent @line_index, add_newline: true else @@ -295,8 +295,8 @@ class Reline::LineEditor end end - private def split_by_width(str, max_width) - Reline::Unicode.split_by_width(str, max_width, @encoding) + private def split_by_width(str, max_width, offset: 0) + Reline::Unicode.split_by_width(str, max_width, @encoding, offset: offset) end def current_byte_pointer_cursor @@ -370,7 +370,7 @@ class Reline::LineEditor @scroll_partial_screen end - def wrapped_lines + def wrapped_prompt_and_input_lines with_cache(__method__, @buffer_of_lines.size, modified_lines, prompt_list, screen_width) do |n, lines, prompts, width, prev_cache_key, cached_value| prev_n, prev_lines, prev_prompts, prev_width = prev_cache_key cached_wraps = {} @@ -381,9 +381,14 @@ class Reline::LineEditor end n.times.map do |i| - prompt = prompts[i] - line = lines[i] - cached_wraps[[prompt, line]] || split_by_width("#{prompt}#{line}", width).first.compact + prompt = prompts[i] || '' + line = lines[i] || '' + if (cached = cached_wraps[[prompt, line]]) + next cached + end + *wrapped_prompts, code_line_prompt = split_by_width(prompt, width).first.compact + wrapped_lines = split_by_width(line, width, offset: calculate_width(code_line_prompt, true)).first.compact + wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] } end end end @@ -409,8 +414,13 @@ class Reline::LineEditor @output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}" else x, w, content = new_items[level] - content = Reline::Unicode.take_range(content, base_x - x, width) unless x == base_x && w == width - Reline::IOGate.move_cursor_column base_x + cover_begin = base_x != 0 && new_levels[base_x - 1] == level + cover_end = new_levels[base_x + width] == level + pos = 0 + unless x == base_x && w == width + content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true) + end + Reline::IOGate.move_cursor_column x + pos @output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}" end base_x += width @@ -426,7 +436,7 @@ class Reline::LineEditor prompt_width = calculate_width(prompt_list[@line_index], true) line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer) wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact - wrapped_cursor_y = wrapped_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1 + wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1 wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last) [wrapped_cursor_x, wrapped_cursor_y] end @@ -490,8 +500,9 @@ class Reline::LineEditor wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position rendered_lines = @rendered_screen.lines - new_lines = wrapped_lines.flatten[screen_scroll_top, screen_height].map do |l| - [[0, Reline::Unicode.calculate_width(l, true), l]] + new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line| + prompt_width = Reline::Unicode.calculate_width(prompt, true) + [[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]] end if @menu_info @menu_info.lines(screen_width).each do |item| @@ -507,7 +518,8 @@ class Reline::LineEditor y_range.each do |row| next if row < 0 || row >= screen_height dialog_rows = new_lines[row] ||= [] - dialog_rows[index + 1] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]] + # index 0 is for prompt, index 1 is for line, index 2.. is for dialog + dialog_rows[index + 2] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]] end end @@ -692,13 +704,6 @@ class Reline::LineEditor DIALOG_DEFAULT_HEIGHT = 20 - private def padding_space_with_escape_sequences(str, width) - padding_width = width - calculate_width(str, true) - # padding_width should be only positive value. But macOS and Alacritty returns negative value. - padding_width = 0 if padding_width < 0 - str + (' ' * padding_width) - end - private def dialog_range(dialog, dialog_y) x_range = dialog.column...dialog.column + dialog.width y_range = dialog_y + dialog.vertical_offset...dialog_y + dialog.vertical_offset + dialog.contents.size @@ -771,7 +776,7 @@ class Reline::LineEditor dialog.contents = contents.map.with_index do |item, i| line_sgr = i == pointer ? enhanced_sgr : default_sgr str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width) - str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width) + str, = Reline::Unicode.take_mbchar_range(item, 0, str_width, padding: true) colored_content = "#{line_sgr}#{str}" if scrollbar_pos if scrollbar_pos <= (i * 2) and (i * 2 + 1) < (scrollbar_pos + bar_height) @@ -851,7 +856,7 @@ class Reline::LineEditor [target, preposing, completed, postposing] end - private def complete(list, just_show_list) + private def perform_completion(list, just_show_list) case @completion_state when CompletionState::NORMAL @completion_state = CompletionState::COMPLETION @@ -880,10 +885,12 @@ class Reline::LineEditor @completion_state = CompletionState::PERFECT_MATCH else @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH + perform_completion(list, true) if @config.show_all_if_ambiguous end @perfect_matched = completed else @completion_state = CompletionState::MENU + perform_completion(list, true) if @config.show_all_if_ambiguous end if not just_show_list and target < completed @buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding) @@ -942,7 +949,8 @@ class Reline::LineEditor unless @waiting_proc byte_pointer_diff = @byte_pointer - old_byte_pointer @byte_pointer = old_byte_pointer - send(@vi_waiting_operator, byte_pointer_diff) + method_obj = method(@vi_waiting_operator) + wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff) cleanup_waiting end else @@ -1003,7 +1011,8 @@ class Reline::LineEditor if @vi_waiting_operator byte_pointer_diff = @byte_pointer - old_byte_pointer @byte_pointer = old_byte_pointer - send(@vi_waiting_operator, byte_pointer_diff) + method_obj = method(@vi_waiting_operator) + wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff) cleanup_waiting end @kill_ring.process @@ -1058,10 +1067,6 @@ class Reline::LineEditor end private def normal_char(key) - if key.combined_char.is_a?(Symbol) - process_key(key.combined_char, key.combined_char) - return - end @multibyte_buffer << key.combined_char if @multibyte_buffer.size > 1 if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding? @@ -1104,6 +1109,7 @@ class Reline::LineEditor end def input_key(key) + save_old_buffer @config.reset_oneshot_key_bindings @dialogs.each do |dialog| if key.char.instance_of?(Symbol) and key.char == dialog.name @@ -1111,38 +1117,17 @@ class Reline::LineEditor end end if key.char.nil? + process_insert(force: true) if @first_char @eof = true end finish return end - old_lines = @buffer_of_lines.dup @first_char = false @completion_occurs = false - if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord - if !@config.disable_completion - process_insert(force: true) - if @config.autocompletion - @completion_state = CompletionState::NORMAL - @completion_occurs = move_completed_list(:down) - else - @completion_journey_state = nil - result = call_completion_proc - if result.is_a?(Array) - @completion_occurs = true - complete(result, false) - end - end - end - elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char) - # In vi mode, move completed list even if autocompletion is off - if not @config.disable_completion - process_insert(force: true) - @completion_state = CompletionState::NORMAL - @completion_occurs = move_completed_list("\C-p".ord == key.char ? :up : :down) - end - elsif Symbol === key.char and respond_to?(key.char, true) + + if key.char.is_a?(Symbol) process_key(key.char, key.char) else normal_char(key) @@ -1152,12 +1137,15 @@ class Reline::LineEditor @completion_journey_state = nil end + push_past_lines unless @undoing + @undoing = false + if @in_pasting clear_dialogs return end - modified = old_lines != @buffer_of_lines + modified = @old_buffer_of_lines != @buffer_of_lines if !@completion_occurs && modified && !@config.disable_completion && @config.autocompletion # Auto complete starts only when edited process_insert(force: true) @@ -1166,6 +1154,26 @@ class Reline::LineEditor modified end + def save_old_buffer + @old_buffer_of_lines = @buffer_of_lines.dup + @old_byte_pointer = @byte_pointer.dup + @old_line_index = @line_index.dup + end + + def push_past_lines + if @old_buffer_of_lines != @buffer_of_lines + @past_lines.push([@old_buffer_of_lines, @old_byte_pointer, @old_line_index]) + end + trim_past_lines + end + + MAX_PAST_LINES = 100 + def trim_past_lines + if @past_lines.size > MAX_PAST_LINES + @past_lines.shift + end + end + def scroll_into_view _wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position if wrapped_cursor_y < screen_scroll_top @@ -1242,6 +1250,18 @@ class Reline::LineEditor process_auto_indent end + def set_current_lines(lines, byte_pointer = nil, line_index = 0) + cursor = current_byte_pointer_cursor + @buffer_of_lines = lines + @line_index = line_index + if byte_pointer + @byte_pointer = byte_pointer + else + calculate_nearest_cursor(cursor) + end + process_auto_indent + end + def retrieve_completion_block(set_completion_quote_character = false) if Reline.completer_word_break_characters.empty? word_break_regexp = nil @@ -1323,6 +1343,18 @@ class Reline::LineEditor @confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n") end + def insert_pasted_text(text) + save_old_buffer + pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer) + post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..) + lines = (pre + text.gsub(/\r\n?/, "\n") + post).split("\n", -1) + lines << '' if lines.empty? + @buffer_of_lines[@line_index, 1] = lines + @line_index += lines.size - 1 + @byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize + push_past_lines + end + def insert_text(text) if @buffer_of_lines[@line_index].bytesize == @byte_pointer @buffer_of_lines[@line_index] += text @@ -1421,13 +1453,42 @@ class Reline::LineEditor end end - private def completion_journey_up(key) - if not @config.disable_completion and @config.autocompletion + private def complete(_key) + return if @config.disable_completion + + process_insert(force: true) + if @config.autocompletion @completion_state = CompletionState::NORMAL - @completion_occurs = move_completed_list(:up) + @completion_occurs = move_completed_list(:down) + else + @completion_journey_state = nil + result = call_completion_proc + if result.is_a?(Array) + @completion_occurs = true + perform_completion(result, false) + end end end - alias_method :menu_complete_backward, :completion_journey_up + + private def completion_journey_move(direction) + return if @config.disable_completion + + process_insert(force: true) + @completion_state = CompletionState::NORMAL + @completion_occurs = move_completed_list(direction) + end + + private def menu_complete(_key) + completion_journey_move(:down) + end + + private def menu_complete_backward(_key) + completion_journey_move(:up) + end + + private def completion_journey_up(_key) + completion_journey_move(:up) if @config.autocompletion + end # Editline:: +ed-unassigned+ This editor command always results in an error. # GNU Readline:: There is no corresponding macro. @@ -1533,11 +1594,7 @@ class Reline::LineEditor alias_method :vi_zero, :ed_move_to_beg private def ed_move_to_end(key) - @byte_pointer = 0 - while @byte_pointer < current_line.bytesize - byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer) - @byte_pointer += byte_size - end + @byte_pointer = current_line.bytesize end alias_method :end_of_line, :ed_move_to_end @@ -1720,7 +1777,6 @@ class Reline::LineEditor return if @history_pointer.nil? history_range = @history_pointer + 1...Reline::HISTORY.size - history = Reline::HISTORY.slice((@history_pointer + 1)..-1) h_pointer, line_index = search_history(substr, history_range) return if h_pointer.nil? and not substr.empty? @@ -1901,7 +1957,7 @@ class Reline::LineEditor elsif !@config.autocompletion # show completed list result = call_completion_proc if result.is_a?(Array) - complete(result, true) + perform_completion(result, true) end end end @@ -2471,4 +2527,15 @@ class Reline::LineEditor private def vi_editing_mode(key) @config.editing_mode = :vi_insert end + + private def undo(_key) + return if @past_lines.empty? + + @undoing = true + + target_lines, target_cursor_x, target_cursor_y = @past_lines.last + set_current_lines(target_lines, target_cursor_x, target_cursor_y) + + @past_lines.pop + end end diff --git a/lib/reline/unicode.rb b/lib/reline/unicode.rb index 26ef207ba6..d7460d6d4a 100644 --- a/lib/reline/unicode.rb +++ b/lib/reline/unicode.rb @@ -43,11 +43,13 @@ class Reline::Unicode def self.escape_for_print(str) str.chars.map! { |gr| - escaped = EscapedPairs[gr.ord] - if escaped && gr != -"\n" && gr != -"\t" - escaped - else + case gr + when -"\n" gr + when -"\t" + -' ' + else + EscapedPairs[gr.ord] || gr end }.join end @@ -128,10 +130,10 @@ class Reline::Unicode end end - def self.split_by_width(str, max_width, encoding = str.encoding) + def self.split_by_width(str, max_width, encoding = str.encoding, offset: 0) lines = [String.new(encoding: encoding)] height = 1 - width = 0 + width = offset rest = str.encode(Encoding::UTF_8) in_zero_width = false seq = String.new(encoding: encoding) @@ -145,7 +147,13 @@ class Reline::Unicode lines.last << NON_PRINTING_END when csi lines.last << csi - seq << csi + unless in_zero_width + if csi == -"\e[m" || csi == -"\e[0m" + seq.clear + else + seq << csi + end + end when osc lines.last << osc seq << osc @@ -173,32 +181,78 @@ class Reline::Unicode # Take a chunk of a String cut by width with escape sequences. def self.take_range(str, start_col, max_width) + take_mbchar_range(str, start_col, max_width).first + end + + def self.take_mbchar_range(str, start_col, width, cover_begin: false, cover_end: false, padding: false) chunk = String.new(encoding: str.encoding) + + end_col = start_col + width total_width = 0 rest = str.encode(Encoding::UTF_8) in_zero_width = false + chunk_start_col = nil + chunk_end_col = nil + has_csi = false rest.scan(WIDTH_SCANNER) do |non_printing_start, non_printing_end, csi, osc, gc| case when non_printing_start in_zero_width = true + chunk << NON_PRINTING_START when non_printing_end in_zero_width = false + chunk << NON_PRINTING_END when csi + has_csi = true chunk << csi when osc chunk << osc when gc if in_zero_width chunk << gc + next + end + + mbchar_width = get_mbchar_width(gc) + prev_width = total_width + total_width += mbchar_width + + if (cover_begin || padding ? total_width <= start_col : prev_width < start_col) + # Current character haven't reached start_col yet + next + elsif padding && !cover_begin && prev_width < start_col && start_col < total_width + # Add preceding padding. This padding might have background color. + chunk << ' ' + chunk_start_col ||= start_col + chunk_end_col = total_width + next + elsif (cover_end ? prev_width < end_col : total_width <= end_col) + # Current character is in the range + chunk << gc + chunk_start_col ||= prev_width + chunk_end_col = total_width + break if total_width >= end_col else - mbchar_width = get_mbchar_width(gc) - total_width += mbchar_width - break if (start_col + max_width) < total_width - chunk << gc if start_col < total_width + # Current character exceeds end_col + if padding && end_col < total_width + # Add succeeding padding. This padding might have background color. + chunk << ' ' + chunk_start_col ||= prev_width + chunk_end_col = end_col + end + break end end end - chunk + chunk_start_col ||= start_col + chunk_end_col ||= start_col + if padding && chunk_end_col < end_col + # Append padding. This padding should not include background color. + chunk << "\e[0m" if has_csi + chunk << ' ' * (end_col - chunk_end_col) + chunk_end_col = end_col + end + [chunk, chunk_start_col, chunk_end_col - chunk_start_col] end def self.get_next_mbchar_size(line, byte_pointer) diff --git a/lib/reline/version.rb b/lib/reline/version.rb index e6f370c6ec..46613a5952 100644 --- a/lib/reline/version.rb +++ b/lib/reline/version.rb @@ -1,3 +1,3 @@ module Reline - VERSION = '0.5.2' + VERSION = '0.5.7' end diff --git a/lib/ruby_vm/rjit/insn_compiler.rb b/lib/ruby_vm/rjit/insn_compiler.rb index 2346c92bd1..f9450241c9 100644 --- a/lib/ruby_vm/rjit/insn_compiler.rb +++ b/lib/ruby_vm/rjit/insn_compiler.rb @@ -5461,6 +5461,12 @@ module RubyVM::RJIT return CantCompile end + if c_method_tracing_currently_enabled? + # Don't JIT if tracing c_call or c_return + asm.incr_counter(:send_cfunc_tracing) + return CantCompile + end + off = cme.def.body.optimized.index recv_idx = argc # blockarg is not supported diff --git a/lib/rubygems.rb b/lib/rubygems.rb index ad7ab10756..ac225ca70a 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -1013,6 +1013,13 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} end ## + # Is this platform FreeBSD + + def self.freebsd_platform? + RbConfig::CONFIG["host_os"].to_s.include?("bsd") + end + + ## # Load +plugins+ as Ruby files def self.load_plugin_files(plugins) # :nodoc: diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb index 456d897df2..b272a15b6c 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -57,7 +57,7 @@ class Gem::Commands::PristineCommand < Gem::Command end add_option("-i", "--install-dir DIR", - "Gem repository to get binstubs and plugins installed") do |value, options| + "Gem repository to get gems restored") do |value, options| options[:install_dir] = File.expand_path(value) end @@ -103,21 +103,25 @@ extensions will be restored. end def execute + install_dir = options[:install_dir] + + specification_record = install_dir ? Gem::SpecificationRecord.from_path(install_dir) : Gem::Specification.specification_record + specs = if options[:all] - Gem::Specification.map + specification_record.map # `--extensions` must be explicitly given to pristine only gems # with extensions. elsif options[:extensions_set] && options[:extensions] && options[:args].empty? - Gem::Specification.select do |spec| + specification_record.select do |spec| spec.extensions && !spec.extensions.empty? end elsif options[:only_missing_extensions] - Gem::Specification.select(&:missing_extensions?) + specification_record.select(&:missing_extensions?) else get_all_gem_names.sort.map do |gem_name| - Gem::Specification.find_all_by_name(gem_name, options[:version]).reverse + specification_record.find_all_by_name(gem_name, options[:version]).reverse end.flatten end @@ -176,7 +180,6 @@ extensions will be restored. end bin_dir = options[:bin_dir] if options[:bin_dir] - install_dir = options[:install_dir] if options[:install_dir] installer_options = { wrappers: true, diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index 3f38074280..9c633d6ef7 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -585,6 +585,8 @@ abort "#{deprecation_message}" args = %w[--all --only-executables --silent] args << "--bindir=#{bindir}" + args << "--install-dir=#{default_dir}" + if options[:env_shebang] args << "--env-shebang" end diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb index 2a77ec72cf..283bc96ce3 100644 --- a/lib/rubygems/commands/uninstall_command.rb +++ b/lib/rubygems/commands/uninstall_command.rb @@ -184,7 +184,7 @@ that is a dependency of an existing gem. You can use the rescue Gem::GemNotInHomeException => e spec = e.spec alert("In order to remove #{spec.name}, please execute:\n" \ - "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}") + "\tgem uninstall #{spec.name} --install-dir=#{spec.base_dir}") rescue Gem::UninstallError => e spec = e.spec alert_error("Error: unable to successfully uninstall '#{spec.name}' which is " \ diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb index 3d6fecaa40..8e80d46856 100644 --- a/lib/rubygems/commands/update_command.rb +++ b/lib/rubygems/commands/update_command.rb @@ -197,18 +197,17 @@ command to remove old versions. yield else require "tmpdir" - tmpdir = Dir.mktmpdir - FileUtils.mv Gem.plugindir, tmpdir + Dir.mktmpdir("gem_update") do |tmpdir| + FileUtils.mv Gem.plugindir, tmpdir - status = yield + status = yield - if status - FileUtils.rm_rf tmpdir - else - FileUtils.mv File.join(tmpdir, "plugins"), Gem.plugindir - end + unless status + FileUtils.mv File.join(tmpdir, "plugins"), Gem.plugindir + end - status + status + end end end diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index d1bf074441..5ce9c5e840 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -271,15 +271,7 @@ class Gem::Dependency end def matching_specs(platform_only = false) - env_req = Gem.env_requirement(name) - matches = Gem::Specification.stubs_for(name).find_all do |spec| - requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) - end.map(&:to_spec) - - if prioritizes_bundler? - require_relative "bundler_version_finder" - Gem::BundlerVersionFinder.prioritize!(matches) - end + matches = Gem::Specification.find_all_by_name(name, requirement) if platform_only matches.reject! do |spec| @@ -297,10 +289,6 @@ class Gem::Dependency @requirement.specific? end - def prioritizes_bundler? - name == "bundler" && !specific? - end - def to_specs matches = matching_specs true diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb index 58a6c5b7dc..7d24f9cbfc 100644 --- a/lib/rubygems/deprecate.rb +++ b/lib/rubygems/deprecate.rb @@ -69,99 +69,101 @@ # end # end -module Gem::Deprecate - def self.skip # :nodoc: - @skip ||= false - end +module Gem + module Deprecate + def self.skip # :nodoc: + @skip ||= false + end - def self.skip=(v) # :nodoc: - @skip = v - end + def self.skip=(v) # :nodoc: + @skip = v + end - ## - # Temporarily turn off warnings. Intended for tests only. + ## + # Temporarily turn off warnings. Intended for tests only. - def skip_during - original = Gem::Deprecate.skip - Gem::Deprecate.skip = true - yield - ensure - Gem::Deprecate.skip = original - end + def skip_during + original = Gem::Deprecate.skip + Gem::Deprecate.skip = true + yield + ensure + Gem::Deprecate.skip = original + end - def self.next_rubygems_major_version # :nodoc: - Gem::Version.new(Gem.rubygems_version.segments.first).bump - end + def self.next_rubygems_major_version # :nodoc: + Gem::Version.new(Gem.rubygems_version.segments.first).bump + end - ## - # Simple deprecation method that deprecates +name+ by wrapping it up - # in a dummy method. It warns on each call to the dummy method - # telling the user of +repl+ (unless +repl+ is :none) and the - # year/month that it is planned to go away. + ## + # Simple deprecation method that deprecates +name+ by wrapping it up + # in a dummy method. It warns on each call to the dummy method + # telling the user of +repl+ (unless +repl+ is :none) and the + # year/month that it is planned to go away. - def deprecate(name, repl, year, month) - class_eval do - old = "_deprecated_#{name}" - alias_method old, name - define_method name do |*args, &block| - klass = is_a? Module - target = klass ? "#{self}." : "#{self.class}#" - msg = [ - "NOTE: #{target}#{name} is deprecated", - repl == :none ? " with no replacement" : "; use #{repl} instead", - format(". It will be removed on or after %4d-%02d.", year, month), - "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}", - ] - warn "#{msg.join}." unless Gem::Deprecate.skip - send old, *args, &block + def deprecate(name, repl, year, month) + class_eval do + old = "_deprecated_#{name}" + alias_method old, name + define_method name do |*args, &block| + klass = is_a? Module + target = klass ? "#{self}." : "#{self.class}#" + msg = [ + "NOTE: #{target}#{name} is deprecated", + repl == :none ? " with no replacement" : "; use #{repl} instead", + format(". It will be removed on or after %4d-%02d.", year, month), + "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}", + ] + warn "#{msg.join}." unless Gem::Deprecate.skip + send old, *args, &block + end + ruby2_keywords name if respond_to?(:ruby2_keywords, true) end - ruby2_keywords name if respond_to?(:ruby2_keywords, true) end - end - ## - # Simple deprecation method that deprecates +name+ by wrapping it up - # in a dummy method. It warns on each call to the dummy method - # telling the user of +repl+ (unless +repl+ is :none) and the - # Rubygems version that it is planned to go away. + ## + # Simple deprecation method that deprecates +name+ by wrapping it up + # in a dummy method. It warns on each call to the dummy method + # telling the user of +repl+ (unless +repl+ is :none) and the + # Rubygems version that it is planned to go away. - def rubygems_deprecate(name, replacement=:none) - class_eval do - old = "_deprecated_#{name}" - alias_method old, name - define_method name do |*args, &block| - klass = is_a? Module - target = klass ? "#{self}." : "#{self.class}#" - msg = [ - "NOTE: #{target}#{name} is deprecated", - replacement == :none ? " with no replacement" : "; use #{replacement} instead", - ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}", - "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}", - ] - warn "#{msg.join}." unless Gem::Deprecate.skip - send old, *args, &block + def rubygems_deprecate(name, replacement=:none) + class_eval do + old = "_deprecated_#{name}" + alias_method old, name + define_method name do |*args, &block| + klass = is_a? Module + target = klass ? "#{self}." : "#{self.class}#" + msg = [ + "NOTE: #{target}#{name} is deprecated", + replacement == :none ? " with no replacement" : "; use #{replacement} instead", + ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}", + "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}", + ] + warn "#{msg.join}." unless Gem::Deprecate.skip + send old, *args, &block + end + ruby2_keywords name if respond_to?(:ruby2_keywords, true) end - ruby2_keywords name if respond_to?(:ruby2_keywords, true) end - end - # Deprecation method to deprecate Rubygems commands - def rubygems_deprecate_command(version = Gem::Deprecate.next_rubygems_major_version) - class_eval do - define_method "deprecated?" do - true - end + # Deprecation method to deprecate Rubygems commands + def rubygems_deprecate_command(version = Gem::Deprecate.next_rubygems_major_version) + class_eval do + define_method "deprecated?" do + true + end - define_method "deprecation_warning" do - msg = [ - "#{command} command is deprecated", - ". It will be removed in Rubygems #{version}.\n", - ] + define_method "deprecation_warning" do + msg = [ + "#{command} command is deprecated", + ". It will be removed in Rubygems #{version}.\n", + ] - alert_warning msg.join.to_s unless Gem::Deprecate.skip + alert_warning msg.join.to_s unless Gem::Deprecate.skip + end end end - end - module_function :rubygems_deprecate, :rubygems_deprecate_command, :skip_during + module_function :rubygems_deprecate, :rubygems_deprecate_command, :skip_during + end end diff --git a/lib/rubygems/ext/cargo_builder.rb b/lib/rubygems/ext/cargo_builder.rb index 86a0e73f28..09ad1407c2 100644 --- a/lib/rubygems/ext/cargo_builder.rb +++ b/lib/rubygems/ext/cargo_builder.rb @@ -184,6 +184,7 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder end def cargo_dylib_path(dest_path, crate_name) + so_ext = RbConfig::CONFIG["SOEXT"] prefix = so_ext == "dll" ? "" : "lib" path_parts = [dest_path] path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"] @@ -312,22 +313,6 @@ EOF deffile_path end - # We have to basically reimplement <code>RbConfig::CONFIG['SOEXT']</code> here to support - # Ruby < 2.5 - # - # @see https://github.com/ruby/ruby/blob/c87c027f18c005460746a74c07cd80ee355b16e4/configure.ac#L3185 - def so_ext - return RbConfig::CONFIG["SOEXT"] if RbConfig::CONFIG.key?("SOEXT") - - if win_target? - "dll" - elsif darwin_target? - "dylib" - else - "so" - end - end - # Corresponds to $(LIBPATH) in mkmf def mkmf_libpath ["-L", "native=#{makefile_config("libdir")}"] diff --git a/lib/rubygems/gemcutter_utilities/webauthn_poller.rb b/lib/rubygems/gemcutter_utilities/webauthn_poller.rb index 0fdd1d5bf4..fe3f163a88 100644 --- a/lib/rubygems/gemcutter_utilities/webauthn_poller.rb +++ b/lib/rubygems/gemcutter_utilities/webauthn_poller.rb @@ -69,8 +69,10 @@ module Gem::GemcutterUtilities rubygems_api_request(:get, "api/v1/webauthn_verification/#{webauthn_token}/status.json") do |request| if credentials.empty? request.add_field "Authorization", api_key + elsif credentials[:identifier] && credentials[:password] + request.basic_auth credentials[:identifier], credentials[:password] else - request.basic_auth credentials[:email], credentials[:password] + raise Gem::WebauthnVerificationError, "Provided missing credentials" end end end diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 8f6f9a5aa8..844f292ba2 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -344,7 +344,7 @@ class Gem::Installer say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil? - Gem::Specification.add_spec(spec) + Gem::Specification.add_spec(spec) unless @install_dir load_plugin diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb index 1d5d764237..c855423ed7 100644 --- a/lib/rubygems/package.rb +++ b/lib/rubygems/package.rb @@ -7,7 +7,6 @@ # rubocop:enable Style/AsciiComments -require_relative "../rubygems" require_relative "security" require_relative "user_interaction" @@ -295,7 +294,6 @@ class Gem::Package Gem.load_yaml - @spec.mark_version @spec.validate true, strict_validation unless skip_validation setup_signer( @@ -528,12 +526,13 @@ EOM # Loads a Gem::Specification from the TarEntry +entry+ def load_spec(entry) # :nodoc: + limit = 10 * 1024 * 1024 case entry.full_name when "metadata" then - @spec = Gem::Specification.from_yaml entry.read + @spec = Gem::Specification.from_yaml limit_read(entry, "metadata", limit) when "metadata.gz" then Zlib::GzipReader.wrap(entry, external_encoding: Encoding::UTF_8) do |gzio| - @spec = Gem::Specification.from_yaml gzio.read + @spec = Gem::Specification.from_yaml limit_read(gzio, "metadata.gz", limit) end end end @@ -557,7 +556,7 @@ EOM @checksums = gem.seek "checksums.yaml.gz" do |entry| Zlib::GzipReader.wrap entry do |gz_io| - Gem::SafeYAML.safe_load gz_io.read + Gem::SafeYAML.safe_load limit_read(gz_io, "checksums.yaml.gz", 10 * 1024 * 1024) end end end @@ -664,7 +663,7 @@ EOM case file_name when /\.sig$/ then - @signatures[$`] = entry.read if @security_policy + @signatures[$`] = limit_read(entry, file_name, 1024 * 1024) if @security_policy return else digest entry @@ -724,6 +723,12 @@ EOM IO.copy_stream(src, dst) end end + + def limit_read(io, name, limit) + bytes = io.read(limit + 1) + raise Gem::Package::FormatError, "#{name} is too big (over #{limit} bytes)" if bytes.size > limit + bytes + end end require_relative "package/digest_io" diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb index 087f13f6c9..dd5e835a1e 100644 --- a/lib/rubygems/package/tar_header.rb +++ b/lib/rubygems/package/tar_header.rb @@ -95,14 +95,14 @@ class Gem::Package::TarHeader attr_reader(*FIELDS) - EMPTY_HEADER = ("\0" * 512).freeze # :nodoc: + EMPTY_HEADER = ("\0" * 512).b.freeze # :nodoc: ## # Creates a tar header from IO +stream+ def self.from(stream) header = stream.read 512 - empty = (header == EMPTY_HEADER) + return EMPTY if header == EMPTY_HEADER fields = header.unpack UNPACK_FORMAT @@ -123,7 +123,7 @@ class Gem::Package::TarHeader devminor: strict_oct(fields.shift), prefix: fields.shift, - empty: empty + empty: false end def self.strict_oct(str) @@ -172,6 +172,22 @@ class Gem::Package::TarHeader @empty = vals[:empty] end + EMPTY = new({ # :nodoc: + checksum: 0, + gname: "", + linkname: "", + magic: "", + mode: 0, + name: "", + prefix: "", + size: 0, + uname: "", + version: 0, + + empty: true, + }).freeze + private_constant :EMPTY + ## # Is the tar entry empty? @@ -241,7 +257,7 @@ class Gem::Package::TarHeader header = header.pack PACK_FORMAT - header << ("\0" * ((512 - header.size) % 512)) + header.ljust 512, "\0" end def oct(num, len) diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index 48b7344aee..d54ad12880 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -134,6 +134,7 @@ class Gem::Platform when /netbsdelf/ then ["netbsdelf", nil] when /openbsd(\d+\.\d+)?/ then ["openbsd", $1] when /solaris(\d+\.\d+)?/ then ["solaris", $1] + when /wasi/ then ["wasi", nil] # test when /^(\w+_platform)(\d+)?/ then [$1, $2] else ["unknown", nil] diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 29139cf725..57f9b45cf7 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -11,6 +11,7 @@ require_relative "deprecate" require_relative "basic_specification" require_relative "stub_specification" require_relative "platform" +require_relative "specification_record" require_relative "util/list" require "rbconfig" @@ -179,22 +180,12 @@ class Gem::Specification < Gem::BasicSpecification @@default_value[k].nil? end - def self.clear_specs # :nodoc: - @@all = nil - @@stubs = nil - @@stubs_by_name = {} - @@spec_with_requirable_file = {} - @@active_stub_with_requirable_file = {} - end - private_class_method :clear_specs - - clear_specs - # Sentinel object to represent "not found" stubs NOT_FOUND = Struct.new(:to_spec, :this).new # :nodoc: + deprecate_constant :NOT_FOUND # Tracking removed method calls to warn users during build time. - REMOVED_METHODS = [:rubyforge_project=].freeze # :nodoc: + REMOVED_METHODS = [:rubyforge_project=, :mark_version].freeze # :nodoc: def removed_method_calls @removed_method_calls ||= [] end @@ -770,7 +761,7 @@ class Gem::Specification < Gem::BasicSpecification attr_accessor :specification_version def self._all # :nodoc: - @@all ||= Gem.loaded_specs.values | stubs.map(&:to_spec) + specification_record.all end def self.clear_load_cache # :nodoc: @@ -788,26 +779,9 @@ class Gem::Specification < Gem::BasicSpecification end end - def self.gemspec_stubs_in(dir, pattern) + def self.gemspec_stubs_in(dir, pattern) # :nodoc: Gem::Util.glob_files_in_dir(pattern, dir).map {|path| yield path }.select(&:valid?) end - private_class_method :gemspec_stubs_in - - def self.installed_stubs(dirs, pattern) - map_stubs(dirs, pattern) do |path, base_dir, gems_dir| - Gem::StubSpecification.gemspec_stub(path, base_dir, gems_dir) - end - end - private_class_method :installed_stubs - - def self.map_stubs(dirs, pattern) # :nodoc: - dirs.flat_map do |dir| - base_dir = File.dirname dir - gems_dir = File.join base_dir, "gems" - gemspec_stubs_in(dir, pattern) {|path| yield path, base_dir, gems_dir } - end - end - private_class_method :map_stubs def self.each_spec(dirs) # :nodoc: each_gemspec(dirs) do |path| @@ -820,13 +794,7 @@ class Gem::Specification < Gem::BasicSpecification # Returns a Gem::StubSpecification for every installed gem def self.stubs - @@stubs ||= begin - pattern = "*.gemspec" - stubs = stubs_for_pattern(pattern, false) - - @@stubs_by_name = stubs.select {|s| Gem::Platform.match_spec? s }.group_by(&:name) - stubs - end + specification_record.stubs end ## @@ -845,13 +813,7 @@ class Gem::Specification < Gem::BasicSpecification # only returns stubs that match Gem.platforms def self.stubs_for(name) - if @@stubs - @@stubs_by_name[name] || [] - else - @@stubs_by_name[name] ||= stubs_for_pattern("#{name}-*.gemspec").select do |s| - s.name == name - end - end + specification_record.stubs_for(name) end ## @@ -859,12 +821,7 @@ class Gem::Specification < Gem::BasicSpecification # optionally filtering out specs not matching the current platform # def self.stubs_for_pattern(pattern, match_platform = true) # :nodoc: - installed_stubs = installed_stubs(Gem::Specification.dirs, pattern) - installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform - stubs = installed_stubs + default_stubs(pattern) - stubs = stubs.uniq(&:full_name) - _resort!(stubs) - stubs + specification_record.stubs_for_pattern(pattern, match_platform) end def self._resort!(specs) # :nodoc: @@ -893,23 +850,14 @@ class Gem::Specification < Gem::BasicSpecification # properly sorted. def self.add_spec(spec) - return if _all.include? spec - - _all << spec - stubs << spec - (@@stubs_by_name[spec.name] ||= []) << spec - - _resort!(@@stubs_by_name[spec.name]) - _resort!(stubs) + specification_record.add_spec(spec) end ## # Removes +spec+ from the known specs. def self.remove_spec(spec) - _all.delete spec.to_spec - stubs.delete spec - (@@stubs_by_name[spec.name] || []).delete spec + specification_record.remove_spec(spec) end ## @@ -923,27 +871,17 @@ class Gem::Specification < Gem::BasicSpecification end ## - # Sets the known specs to +specs+. Not guaranteed to work for you in - # the future. Use at your own risk. Caveat emptor. Doomy doom doom. - # Etc etc. - # - #-- - # Makes +specs+ the known specs - # Listen, time is a river - # Winter comes, code breaks - # - # -- wilsonb + # Sets the known specs to +specs+. def self.all=(specs) - @@stubs_by_name = specs.group_by(&:name) - @@all = @@stubs = specs + specification_record.all = specs end ## # Return full names of all specs in sorted order. def self.all_names - _all.map(&:full_name) + specification_record.all_names end ## @@ -968,9 +906,7 @@ class Gem::Specification < Gem::BasicSpecification # Return the directories that Specification uses to find specs. def self.dirs - @@dirs ||= Gem.path.collect do |dir| - File.join dir, "specifications" - end + @@dirs ||= Gem::SpecificationRecord.dirs_from(Gem.path) end ## @@ -980,7 +916,7 @@ class Gem::Specification < Gem::BasicSpecification def self.dirs=(dirs) reset - @@dirs = Array(dirs).map {|dir| File.join dir, "specifications" } + @@dirs = Gem::SpecificationRecord.dirs_from(Array(dirs)) end extend Enumerable @@ -989,21 +925,15 @@ class Gem::Specification < Gem::BasicSpecification # Enumerate every known spec. See ::dirs= and ::add_spec to set the list of # specs. - def self.each - return enum_for(:each) unless block_given? - - _all.each do |x| - yield x - end + def self.each(&block) + specification_record.each(&block) end ## # Returns every spec that matches +name+ and optional +requirements+. def self.find_all_by_name(name, *requirements) - requirements = Gem::Requirement.default if requirements.empty? - - Gem::Dependency.new(name, *requirements).matching_specs + specification_record.find_all_by_name(name, *requirements) end ## @@ -1033,12 +963,7 @@ class Gem::Specification < Gem::BasicSpecification # Return the best specification that contains the file matching +path+. def self.find_by_path(path) - path = path.dup.freeze - spec = @@spec_with_requirable_file[path] ||= stubs.find do |s| - s.contains_requirable_file? path - end || NOT_FOUND - - spec.to_spec + specification_record.find_by_path(path) end ## @@ -1046,19 +971,15 @@ class Gem::Specification < Gem::BasicSpecification # amongst the specs that are not activated. def self.find_inactive_by_path(path) - stub = stubs.find do |s| - next if s.activated? - s.contains_requirable_file? path - end - stub&.to_spec + specification_record.find_inactive_by_path(path) end - def self.find_active_stub_by_path(path) - stub = @@active_stub_with_requirable_file[path] ||= stubs.find do |s| - s.activated? && s.contains_requirable_file?(path) - end || NOT_FOUND + ## + # Return the best specification that contains the file matching +path+, among + # those already activated. - stub.this + def self.find_active_stub_by_path(path) + specification_record.find_active_stub_by_path(path) end ## @@ -1125,14 +1046,14 @@ class Gem::Specification < Gem::BasicSpecification # +prerelease+ is true. def self.latest_specs(prerelease = false) - _latest_specs Gem::Specification.stubs, prerelease + specification_record.latest_specs(prerelease) end ## # Return the latest installed spec for gem +name+. def self.latest_spec_for(name) - latest_specs(true).find {|installed_spec| installed_spec.name == name } + specification_record.latest_spec_for(name) end def self._latest_specs(specs, prerelease = false) # :nodoc: @@ -1270,7 +1191,7 @@ class Gem::Specification < Gem::BasicSpecification def self.reset @@dirs = nil Gem.pre_reset_hooks.each(&:call) - clear_specs + @specification_record = nil clear_load_cache unresolved = unresolved_deps unless unresolved.empty? @@ -1291,6 +1212,13 @@ class Gem::Specification < Gem::BasicSpecification Gem.post_reset_hooks.each(&:call) end + ## + # Keeps track of all currently known specifications + + def self.specification_record + @specification_record ||= Gem::SpecificationRecord.new(dirs) + end + # DOC: This method needs documented or nodoc'd def self.unresolved_deps @unresolved_deps ||= Hash.new {|h, n| h[n] = Gem::Dependency.new n } @@ -1874,8 +1802,6 @@ class Gem::Specification < Gem::BasicSpecification end def encode_with(coder) # :nodoc: - mark_version - coder.add "name", @name coder.add "version", @version platform = case @original_platform @@ -2171,13 +2097,6 @@ class Gem::Specification < Gem::BasicSpecification end ## - # Sets the rubygems_version to the current RubyGems version. - - def mark_version - @rubygems_version = Gem::VERSION - end - - ## # Track removed method calls to warn about during build time. # Warn about unknown attributes while loading a spec. @@ -2494,7 +2413,6 @@ class Gem::Specification < Gem::BasicSpecification # still have their default values are omitted. def to_ruby - mark_version result = [] result << "# -*- encoding: utf-8 -*-" result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{raw_require_paths.join("\0")}" diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb index 516c26f53c..812b0f889e 100644 --- a/lib/rubygems/specification_policy.rb +++ b/lib/rubygems/specification_policy.rb @@ -274,7 +274,9 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use: return if rubygems_version == Gem::VERSION - error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}" + warning "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}" + + @specification.rubygems_version = Gem::VERSION end def validate_required_attributes diff --git a/lib/rubygems/specification_record.rb b/lib/rubygems/specification_record.rb new file mode 100644 index 0000000000..dd6aa7eafa --- /dev/null +++ b/lib/rubygems/specification_record.rb @@ -0,0 +1,213 @@ +# frozen_string_literal: true + +module Gem + class SpecificationRecord + def self.dirs_from(paths) + paths.map do |path| + File.join(path, "specifications") + end + end + + def self.from_path(path) + new(dirs_from([path])) + end + + def initialize(dirs) + @all = nil + @stubs = nil + @stubs_by_name = {} + @spec_with_requirable_file = {} + @active_stub_with_requirable_file = {} + + @dirs = dirs + end + + # Sentinel object to represent "not found" stubs + NOT_FOUND = Struct.new(:to_spec, :this).new + private_constant :NOT_FOUND + + ## + # Returns the list of all specifications in the record + + def all + @all ||= Gem.loaded_specs.values | stubs.map(&:to_spec) + end + + ## + # Returns a Gem::StubSpecification for every specification in the record + + def stubs + @stubs ||= begin + pattern = "*.gemspec" + stubs = stubs_for_pattern(pattern, false) + + @stubs_by_name = stubs.select {|s| Gem::Platform.match_spec? s }.group_by(&:name) + stubs + end + end + + ## + # Returns a Gem::StubSpecification for every specification in the record + # named +name+ only returns stubs that match Gem.platforms + + def stubs_for(name) + if @stubs + @stubs_by_name[name] || [] + else + @stubs_by_name[name] ||= stubs_for_pattern("#{name}-*.gemspec").select do |s| + s.name == name + end + end + end + + ## + # Finds stub specifications matching a pattern in the record, optionally + # filtering out specs not matching the current platform + + def stubs_for_pattern(pattern, match_platform = true) + installed_stubs = installed_stubs(pattern) + installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform + stubs = installed_stubs + Gem::Specification.default_stubs(pattern) + stubs = stubs.uniq(&:full_name) + Gem::Specification._resort!(stubs) + stubs + end + + ## + # Adds +spec+ to the the record, keeping the collection properly sorted. + + def add_spec(spec) + return if all.include? spec + + all << spec + stubs << spec + (@stubs_by_name[spec.name] ||= []) << spec + + Gem::Specification._resort!(@stubs_by_name[spec.name]) + Gem::Specification._resort!(stubs) + end + + ## + # Removes +spec+ from the record. + + def remove_spec(spec) + all.delete spec.to_spec + stubs.delete spec + (@stubs_by_name[spec.name] || []).delete spec + end + + ## + # Sets the specs known by the record to +specs+. + + def all=(specs) + @stubs_by_name = specs.group_by(&:name) + @all = @stubs = specs + end + + ## + # Return full names of all specs in the record in sorted order. + + def all_names + all.map(&:full_name) + end + + include Enumerable + + ## + # Enumerate every known spec. + + def each + return enum_for(:each) unless block_given? + + all.each do |x| + yield x + end + end + + ## + # Returns every spec in the record that matches +name+ and optional +requirements+. + + def find_all_by_name(name, *requirements) + req = Gem::Requirement.create(*requirements) + env_req = Gem.env_requirement(name) + + matches = stubs_for(name).find_all do |spec| + req.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) + end.map(&:to_spec) + + if name == "bundler" && !req.specific? + require_relative "bundler_version_finder" + Gem::BundlerVersionFinder.prioritize!(matches) + end + + matches + end + + ## + # Return the best specification in the record that contains the file matching +path+. + + def find_by_path(path) + path = path.dup.freeze + spec = @spec_with_requirable_file[path] ||= stubs.find do |s| + s.contains_requirable_file? path + end || NOT_FOUND + + spec.to_spec + end + + ## + # Return the best specification in the record that contains the file + # matching +path+ amongst the specs that are not activated. + + def find_inactive_by_path(path) + stub = stubs.find do |s| + next if s.activated? + s.contains_requirable_file? path + end + stub&.to_spec + end + + ## + # Return the best specification in the record that contains the file + # matching +path+, among those already activated. + + def find_active_stub_by_path(path) + stub = @active_stub_with_requirable_file[path] ||= stubs.find do |s| + s.activated? && s.contains_requirable_file?(path) + end || NOT_FOUND + + stub.this + end + + ## + # Return the latest specs in the record, optionally including prerelease + # specs if +prerelease+ is true. + + def latest_specs(prerelease) + Gem::Specification._latest_specs stubs, prerelease + end + + ## + # Return the latest installed spec in the record for gem +name+. + + def latest_spec_for(name) + latest_specs(true).find {|installed_spec| installed_spec.name == name } + end + + private + + def installed_stubs(pattern) + map_stubs(pattern) do |path, base_dir, gems_dir| + Gem::StubSpecification.gemspec_stub(path, base_dir, gems_dir) + end + end + + def map_stubs(pattern) + @dirs.flat_map do |dir| + base_dir = File.dirname dir + gems_dir = File.join base_dir, "gems" + Gem::Specification.gemspec_stubs_in(dir, pattern) {|path| yield path, base_dir, gems_dir } + end + end + end +end diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb index c96df2a085..4d72f6fd0a 100644 --- a/lib/rubygems/uninstaller.rb +++ b/lib/rubygems/uninstaller.rb @@ -32,7 +32,7 @@ class Gem::Uninstaller attr_reader :bin_dir ## - # The gem repository the gem will be installed into + # The gem repository the gem will be uninstalled from attr_reader :gem_home @@ -49,8 +49,9 @@ class Gem::Uninstaller # TODO: document the valid options @gem = gem @version = options[:version] || Gem::Requirement.default - @gem_home = File.realpath(options[:install_dir] || Gem.dir) - @plugins_dir = Gem.plugindir(@gem_home) + @install_dir = options[:install_dir] + @gem_home = File.realpath(@install_dir || Gem.dir) + @user_dir = File.exist?(Gem.user_dir) ? File.realpath(Gem.user_dir) : Gem.user_dir @force_executables = options[:executables] @force_all = options[:all] @force_ignore = options[:ignore] @@ -70,7 +71,7 @@ class Gem::Uninstaller # only add user directory if install_dir is not set @user_install = false - @user_install = options[:user_install] unless options[:install_dir] + @user_install = options[:user_install] unless @install_dir # Optimization: populated during #uninstall @default_specs_matching_uninstall_params = [] @@ -105,7 +106,7 @@ class Gem::Uninstaller list, other_repo_specs = list.partition do |spec| @gem_home == spec.base_dir || - (@user_install && spec.base_dir == Gem.user_dir) + (@user_install && spec.base_dir == @user_dir) end list.sort! @@ -239,7 +240,7 @@ class Gem::Uninstaller def remove(spec) unless path_ok?(@gem_home, spec) || - (@user_install && path_ok?(Gem.user_dir, spec)) + (@user_install && path_ok?(@user_dir, spec)) e = Gem::GemNotInHomeException.new \ "Gem '#{spec.full_name}' is not installed in directory #{@gem_home}" e.spec = spec @@ -284,17 +285,18 @@ class Gem::Uninstaller def remove_plugins(spec) # :nodoc: return if spec.plugins.empty? - remove_plugins_for(spec, @plugins_dir) + remove_plugins_for(spec, plugin_dir_for(spec)) end ## # Regenerates plugin wrappers after removal. def regenerate_plugins - latest = Gem::Specification.latest_spec_for(@spec.name) + specification_record = @install_dir ? Gem::SpecificationRecord.from_path(@install_dir) : Gem::Specification.specification_record + latest = specification_record.latest_spec_for(@spec.name) return if latest.nil? - regenerate_plugins_for(latest, @plugins_dir) + regenerate_plugins_for(latest, plugin_dir_for(@spec)) end ## @@ -406,4 +408,8 @@ class Gem::Uninstaller say "Gem #{spec.full_name} cannot be uninstalled because it is a default gem" end end + + def plugin_dir_for(spec) + Gem.plugindir(spec.base_dir) + end end diff --git a/lib/syntax_suggest/clean_document.rb b/lib/syntax_suggest/clean_document.rb index 0847a62e27..2790ccae86 100644 --- a/lib/syntax_suggest/clean_document.rb +++ b/lib/syntax_suggest/clean_document.rb @@ -267,7 +267,7 @@ module SyntaxSuggest groups.each do |lines| line = lines.first - # Handle the case of multiple groups in a a row + # Handle the case of multiple groups in a row # if one is already replaced, move on next if @document[line.index].empty? diff --git a/lib/time.rb b/lib/time.rb index 970932a200..b565130b50 100644 --- a/lib/time.rb +++ b/lib/time.rb @@ -391,6 +391,8 @@ class Time # heuristic to detect the format of the input string, you provide # a second argument that describes the format of the string. # + # Raises `ArgumentError` if the date or format is invalid. + # # If a block is given, the year described in +date+ is converted by the # block. For example: # @@ -762,11 +762,13 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname) } else { rb_ast_t *ast; + VALUE ast_value; VALUE parser = rb_parser_new(); rb_parser_set_context(parser, NULL, FALSE); - ast = (rb_ast_t *)rb_parser_load_file(parser, fname); + ast_value = rb_parser_load_file(parser, fname); + ast = rb_ruby_ast_data_get(ast_value); - iseq = rb_iseq_new_top(&ast->body, rb_fstring_lit("<top (required)>"), + iseq = rb_iseq_new_top(ast_value, rb_fstring_lit("<top (required)>"), fname, realpath_internal_cached(realpath_map, fname), NULL); rb_ast_dispose(ast); } diff --git a/man/ruby.1 b/man/ruby.1 index a0cda14641..bbad8ae203 100644 --- a/man/ruby.1 +++ b/man/ruby.1 @@ -448,18 +448,20 @@ Check syntax (same as .El .Pp Or one of the following, which are intended for debugging the interpreter: -.Bl -hang -offset indent -tag -width "parsetree_with_comment" +.Bl -hang -offset indent -tag -width "+error-tolerant" .It Sy yydebug Enable compiler debug mode (same as .Fl -yydebug). .It Sy parsetree Print a textual representation of the Ruby AST for the program. -.It Sy parsetree_with_comment -Print a textual representation of the Ruby AST for the program, but with each node annotated with the associated Ruby source code. .It Sy insns Print a list of disassembled bytecode instructions. -.It Sy insns_without_opt -Print the list of disassembled bytecode instructions before various optimizations have been applied. +.It Sy -optimize +Disable various optimizations to print a list disassembled bytecode instructions. +.It Sy +error-tolerant +Enable error-tolerant parsing, when yydebug or parsetree. +.It Sy +comment +Annotate a textual representation of the Ruby AST for the program with the associated Ruby source code. .El .Pp .It Fl -verbose @@ -537,7 +539,7 @@ malloc family of C standard library calls ( .Xr calloc 3 , and .Xr realloc 3 ) . -In this documentatation, the "heap" refers to the Ruby object heap +In this documentation, the "heap" refers to the Ruby object heap of fixed-sized slots, while "malloc" refers to auxiliary allocations commonly referred to as the "process heap". Thus there are at least two possible ways to trigger GC: @@ -1696,6 +1696,11 @@ r_copy_ivar(VALUE v, VALUE data) return v; } +#define override_ivar_error(type, str) \ + rb_raise(rb_eTypeError, \ + "can't override instance variable of "type" '%"PRIsVALUE"'", \ + (str)) + static void r_ivar(VALUE obj, int *has_encoding, struct load_arg *arg) { @@ -1703,6 +1708,12 @@ r_ivar(VALUE obj, int *has_encoding, struct load_arg *arg) len = r_long(arg); if (len > 0) { + if (RB_TYPE_P(obj, T_MODULE)) { + override_ivar_error("module", rb_mod_name(obj)); + } + else if (RB_TYPE_P(obj, T_CLASS)) { + override_ivar_error("class", rb_class_name(obj)); + } do { VALUE sym = r_symbol(arg); VALUE val = r_object(arg); @@ -1795,9 +1806,7 @@ append_extmod(VALUE obj, VALUE extmod) #define prohibit_ivar(type, str) do { \ if (!ivp || !*ivp) break; \ - rb_raise(rb_eTypeError, \ - "can't override instance variable of "type" '%"PRIsVALUE"'", \ - (str)); \ + override_ivar_error(type, str); \ } while (0) static VALUE r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int type); diff --git a/mini_builtin.c b/mini_builtin.c index 38b0ca8d81..810125fa2e 100644 --- a/mini_builtin.c +++ b/mini_builtin.c @@ -12,16 +12,17 @@ static struct st_table *loaded_builtin_table; #endif -rb_ast_t *rb_builtin_ast(const char *feature_name, VALUE *name_str); +VALUE rb_builtin_ast_value(const char *feature_name, VALUE *name_str); static const rb_iseq_t * builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *table) { VALUE name_str = 0; - rb_ast_t *ast = rb_builtin_ast(feature_name, &name_str); + rb_ast_t *ast; + VALUE ast_value = rb_builtin_ast_value(feature_name, &name_str); rb_vm_t *vm = GET_VM(); - if (!ast) { + if (NIL_P(ast_value)) { rb_fatal("builtin_iseq_load: can not find %s; " "probably miniprelude.c is out of date", feature_name); @@ -39,7 +40,8 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta .coverage_enabled = FALSE, .debug_level = 0, }; - const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization, Qnil); + ast = rb_ruby_ast_data_get(ast_value); + const rb_iseq_t *iseq = rb_iseq_new_with_opt(ast_value, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization, Qnil); GET_VM()->builtin_function_table = NULL; rb_ast_dispose(ast); diff --git a/misc/lldb_rb/utils.py b/misc/lldb_rb/utils.py index 86b5bdda2d..1b0e4f9f2a 100644 --- a/misc/lldb_rb/utils.py +++ b/misc/lldb_rb/utils.py @@ -60,6 +60,9 @@ class RbInspector(LLDBInterface): rbUndef = self.ruby_globals["RUBY_Qundef"] rbImmediateMask = self.ruby_globals["RUBY_IMMEDIATE_MASK"] + if self.inspect_node(val): + return + num = val.GetValueAsSigned() if num == rbFalse: print('false', file=self.result) @@ -245,227 +248,6 @@ class RbInspector(LLDBInterface): print("T_DATA:", file=self.result) self._append_expression("*(struct RData *) %0#x" % val.GetValueAsUnsigned()) - elif rval.is_type("RUBY_T_NODE"): - tRNode = self.target.FindFirstType("struct RNode").GetPointerType() - rbNodeTypeMask = self.ruby_globals["RUBY_NODE_TYPEMASK"] - rbNodeTypeShift = self.ruby_globals["RUBY_NODE_TYPESHIFT"] - - nd_type = (rval.flags & rbNodeTypeMask) >> rbNodeTypeShift - val = val.Cast(tRNode) - - self._append_expression("(node_type) %d" % nd_type) - - if nd_type == self.ruby_globals["NODE_SCOPE"]: - self._append_expression("*(struct RNode_SCOPE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_BLOCK"]: - self._append_expression("*(struct RNode_BLOCK *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_IF"]: - self._append_expression("*(struct RNode_IF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_UNLESS"]: - self._append_expression("*(struct RNode_UNLESS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CASE"]: - self._append_expression("*(struct RNode_CASE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CASE2"]: - self._append_expression("*(struct RNode_CASE2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CASE3"]: - self._append_expression("*(struct RNode_CASE3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_WHEN"]: - self._append_expression("*(struct RNode_WHEN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_IN"]: - self._append_expression("*(struct RNode_IN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_WHILE"]: - self._append_expression("*(struct RNode_WHILE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_UNTIL"]: - self._append_expression("*(struct RNode_UNTIL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ITER"]: - self._append_expression("*(struct RNode_ITER *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FOR"]: - self._append_expression("*(struct RNode_FOR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FOR_MASGN"]: - self._append_expression("*(struct RNode_FOR_MASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_BREAK"]: - self._append_expression("*(struct RNode_BREAK *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_NEXT"]: - self._append_expression("*(struct RNode_NEXT *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_REDO"]: - self._append_expression("*(struct RNode_REDO *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_RETRY"]: - self._append_expression("*(struct RNode_RETRY *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_BEGIN"]: - self._append_expression("*(struct RNode_BEGIN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_RESCUE"]: - self._append_expression("*(struct RNode_RESCUE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_RESBODY"]: - self._append_expression("*(struct RNode_RESBODY *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ENSURE"]: - self._append_expression("*(struct RNode_ENSURE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_AND"]: - self._append_expression("*(struct RNode_AND *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OR"]: - self._append_expression("*(struct RNode_OR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MASGN"]: - self._append_expression("*(struct RNode_MASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LASGN"]: - self._append_expression("*(struct RNode_LASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DASGN"]: - self._append_expression("*(struct RNode_DASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_GASGN"]: - self._append_expression("*(struct RNode_GASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_IASGN"]: - self._append_expression("*(struct RNode_IASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CDECL"]: - self._append_expression("*(struct RNode_CDECL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CVASGN"]: - self._append_expression("*(struct RNode_CVASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_ASGN1"]: - self._append_expression("*(struct RNode_OP_ASGN1 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_ASGN2"]: - self._append_expression("*(struct RNode_OP_ASGN2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_ASGN_AND"]: - self._append_expression("*(struct RNode_OP_ASGN_AND *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_ASGN_OR"]: - self._append_expression("*(struct RNode_OP_ASGN_OR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OP_CDECL"]: - self._append_expression("*(struct RNode_OP_CDECL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CALL"]: - self._append_expression("*(struct RNode_CALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OPCALL"]: - self._append_expression("*(struct RNode_OPCALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FCALL"]: - self._append_expression("*(struct RNode_FCALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_VCALL"]: - self._append_expression("*(struct RNode_VCALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_QCALL"]: - self._append_expression("*(struct RNode_QCALL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_SUPER"]: - self._append_expression("*(struct RNode_SUPER *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ZSUPER"]: - self._append_expression("*(struct RNode_ZSUPER *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LIST"]: - self._append_expression("*(struct RNode_LIST *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ZLIST"]: - self._append_expression("*(struct RNode_ZLIST *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_HASH"]: - self._append_expression("*(struct RNode_HASH *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_RETURN"]: - self._append_expression("*(struct RNode_RETURN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_YIELD"]: - self._append_expression("*(struct RNode_YIELD *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LVAR"]: - self._append_expression("*(struct RNode_LVAR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DVAR"]: - self._append_expression("*(struct RNode_DVAR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_GVAR"]: - self._append_expression("*(struct RNode_GVAR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CONST"]: - self._append_expression("*(struct RNode_CONST *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CVAR"]: - self._append_expression("*(struct RNode_CVAR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_NTH_REF"]: - self._append_expression("*(struct RNode_NTH_REF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_BACK_REF"]: - self._append_expression("*(struct RNode_BACK_REF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MATCH"]: - self._append_expression("*(struct RNode_MATCH *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MATCH2"]: - self._append_expression("*(struct RNode_MATCH2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MATCH3"]: - self._append_expression("*(struct RNode_MATCH3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_STR"]: - self._append_expression("*(struct RNode_STR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DSTR"]: - self._append_expression("*(struct RNode_DSTR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_XSTR"]: - self._append_expression("*(struct RNode_XSTR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DXSTR"]: - self._append_expression("*(struct RNode_DXSTR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_EVSTR"]: - self._append_expression("*(struct RNode_EVSTR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_REGX"]: - self._append_expression("*(struct RNode_REGX *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DREGX"]: - self._append_expression("*(struct RNode_DREGX *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ONCE"]: - self._append_expression("*(struct RNode_ONCE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARGS"]: - self._append_expression("*(struct RNode_ARGS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARGS_AUX"]: - self._append_expression("*(struct RNode_ARGS_AUX *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_OPT_ARG"]: - self._append_expression("*(struct RNode_OPT_ARG *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_KW_ARG"]: - self._append_expression("*(struct RNode_KW_ARG *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_POSTARG"]: - self._append_expression("*(struct RNode_POSTARG *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARGSCAT"]: - self._append_expression("*(struct RNode_ARGSCAT *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARGSPUSH"]: - self._append_expression("*(struct RNode_ARGSPUSH *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_SPLAT"]: - self._append_expression("*(struct RNode_SPLAT *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DEFN"]: - self._append_expression("*(struct RNode_DEFN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DEFS"]: - self._append_expression("*(struct RNode_DEFS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ALIAS"]: - self._append_expression("*(struct RNode_ALIAS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_VALIAS"]: - self._append_expression("*(struct RNode_VALIAS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_UNDEF"]: - self._append_expression("*(struct RNode_UNDEF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_CLASS"]: - self._append_expression("*(struct RNode_CLASS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_MODULE"]: - self._append_expression("*(struct RNode_MODULE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_SCLASS"]: - self._append_expression("*(struct RNode_SCLASS *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_COLON2"]: - self._append_expression("*(struct RNode_COLON2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_COLON3"]: - self._append_expression("*(struct RNode_COLON3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DOT2"]: - self._append_expression("*(struct RNode_DOT2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DOT3"]: - self._append_expression("*(struct RNode_DOT3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FLIP2"]: - self._append_expression("*(struct RNode_FLIP2 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FLIP3"]: - self._append_expression("*(struct RNode_FLIP3 *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_SELF"]: - self._append_expression("*(struct RNode_SELF *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_NIL"]: - self._append_expression("*(struct RNode_NIL *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_TRUE"]: - self._append_expression("*(struct RNode_TRUE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FALSE"]: - self._append_expression("*(struct RNode_FALSE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ERRINFO"]: - self._append_expression("*(struct RNode_ERRINFO *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DEFINED"]: - self._append_expression("*(struct RNode_DEFINED *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_POSTEXE"]: - self._append_expression("*(struct RNode_POSTEXE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_DSYM"]: - self._append_expression("*(struct RNode_DSYM *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ATTRASGN"]: - self._append_expression("*(struct RNode_ATTRASGN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LAMBDA"]: - self._append_expression("*(struct RNode_LAMBDA *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ARYPTN"]: - self._append_expression("*(struct RNode_ARYPTN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_HSHPTN"]: - self._append_expression("*(struct RNode_HSHPTN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FNDPTN"]: - self._append_expression("*(struct RNode_FNDPTN *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_ERROR"]: - self._append_expression("*(struct RNode_ERROR *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_LINE"]: - self._append_expression("*(struct RNode_LINE *) %0#x" % val.GetValueAsUnsigned()) - elif nd_type == self.ruby_globals["NODE_FILE"]: - self._append_expression("*(struct RNode_FILE *) %0#x" % val.GetValueAsUnsigned()) - else: - self._append_expression("*(struct RNode *) %0#x" % val.GetValueAsUnsigned()) - elif rval.is_type("RUBY_T_IMEMO"): imemo_type = ((rval.flags >> self.ruby_globals["RUBY_FL_USHIFT"]) & IMEMO_MASK) @@ -492,3 +274,230 @@ class RbInspector(LLDBInterface): else: print("Not-handled type %0#x" % rval.type, file=self.result) print(val, file=self.result) + + def inspect_node(self, val): + tRNode = self.target.FindFirstType("struct RNode").GetPointerType() + + # if val.GetType() != tRNode: does not work for unknown reason + + if val.GetType().GetPointeeType().name != "NODE": + return False + + rbNodeTypeMask = self.ruby_globals["RUBY_NODE_TYPEMASK"] + rbNodeTypeShift = self.ruby_globals["RUBY_NODE_TYPESHIFT"] + flags = val.Cast(tRNode).GetChildMemberWithName("flags").GetValueAsUnsigned() + nd_type = (flags & rbNodeTypeMask) >> rbNodeTypeShift + + self._append_expression("(node_type) %d" % nd_type) + + if nd_type == self.ruby_globals["NODE_SCOPE"]: + self._append_expression("*(struct RNode_SCOPE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_BLOCK"]: + self._append_expression("*(struct RNode_BLOCK *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_IF"]: + self._append_expression("*(struct RNode_IF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_UNLESS"]: + self._append_expression("*(struct RNode_UNLESS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CASE"]: + self._append_expression("*(struct RNode_CASE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CASE2"]: + self._append_expression("*(struct RNode_CASE2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CASE3"]: + self._append_expression("*(struct RNode_CASE3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_WHEN"]: + self._append_expression("*(struct RNode_WHEN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_IN"]: + self._append_expression("*(struct RNode_IN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_WHILE"]: + self._append_expression("*(struct RNode_WHILE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_UNTIL"]: + self._append_expression("*(struct RNode_UNTIL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ITER"]: + self._append_expression("*(struct RNode_ITER *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FOR"]: + self._append_expression("*(struct RNode_FOR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FOR_MASGN"]: + self._append_expression("*(struct RNode_FOR_MASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_BREAK"]: + self._append_expression("*(struct RNode_BREAK *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_NEXT"]: + self._append_expression("*(struct RNode_NEXT *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_REDO"]: + self._append_expression("*(struct RNode_REDO *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_RETRY"]: + self._append_expression("*(struct RNode_RETRY *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_BEGIN"]: + self._append_expression("*(struct RNode_BEGIN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_RESCUE"]: + self._append_expression("*(struct RNode_RESCUE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_RESBODY"]: + self._append_expression("*(struct RNode_RESBODY *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ENSURE"]: + self._append_expression("*(struct RNode_ENSURE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_AND"]: + self._append_expression("*(struct RNode_AND *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OR"]: + self._append_expression("*(struct RNode_OR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MASGN"]: + self._append_expression("*(struct RNode_MASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LASGN"]: + self._append_expression("*(struct RNode_LASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DASGN"]: + self._append_expression("*(struct RNode_DASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_GASGN"]: + self._append_expression("*(struct RNode_GASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_IASGN"]: + self._append_expression("*(struct RNode_IASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CDECL"]: + self._append_expression("*(struct RNode_CDECL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CVASGN"]: + self._append_expression("*(struct RNode_CVASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_ASGN1"]: + self._append_expression("*(struct RNode_OP_ASGN1 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_ASGN2"]: + self._append_expression("*(struct RNode_OP_ASGN2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_ASGN_AND"]: + self._append_expression("*(struct RNode_OP_ASGN_AND *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_ASGN_OR"]: + self._append_expression("*(struct RNode_OP_ASGN_OR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OP_CDECL"]: + self._append_expression("*(struct RNode_OP_CDECL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CALL"]: + self._append_expression("*(struct RNode_CALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OPCALL"]: + self._append_expression("*(struct RNode_OPCALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FCALL"]: + self._append_expression("*(struct RNode_FCALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_VCALL"]: + self._append_expression("*(struct RNode_VCALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_QCALL"]: + self._append_expression("*(struct RNode_QCALL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_SUPER"]: + self._append_expression("*(struct RNode_SUPER *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ZSUPER"]: + self._append_expression("*(struct RNode_ZSUPER *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LIST"]: + self._append_expression("*(struct RNode_LIST *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ZLIST"]: + self._append_expression("*(struct RNode_ZLIST *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_HASH"]: + self._append_expression("*(struct RNode_HASH *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_RETURN"]: + self._append_expression("*(struct RNode_RETURN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_YIELD"]: + self._append_expression("*(struct RNode_YIELD *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LVAR"]: + self._append_expression("*(struct RNode_LVAR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DVAR"]: + self._append_expression("*(struct RNode_DVAR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_GVAR"]: + self._append_expression("*(struct RNode_GVAR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CONST"]: + self._append_expression("*(struct RNode_CONST *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CVAR"]: + self._append_expression("*(struct RNode_CVAR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_NTH_REF"]: + self._append_expression("*(struct RNode_NTH_REF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_BACK_REF"]: + self._append_expression("*(struct RNode_BACK_REF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MATCH"]: + self._append_expression("*(struct RNode_MATCH *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MATCH2"]: + self._append_expression("*(struct RNode_MATCH2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MATCH3"]: + self._append_expression("*(struct RNode_MATCH3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_STR"]: + self._append_expression("*(struct RNode_STR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DSTR"]: + self._append_expression("*(struct RNode_DSTR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_XSTR"]: + self._append_expression("*(struct RNode_XSTR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DXSTR"]: + self._append_expression("*(struct RNode_DXSTR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_EVSTR"]: + self._append_expression("*(struct RNode_EVSTR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_REGX"]: + self._append_expression("*(struct RNode_REGX *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DREGX"]: + self._append_expression("*(struct RNode_DREGX *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ONCE"]: + self._append_expression("*(struct RNode_ONCE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARGS"]: + self._append_expression("*(struct RNode_ARGS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARGS_AUX"]: + self._append_expression("*(struct RNode_ARGS_AUX *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_OPT_ARG"]: + self._append_expression("*(struct RNode_OPT_ARG *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_KW_ARG"]: + self._append_expression("*(struct RNode_KW_ARG *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_POSTARG"]: + self._append_expression("*(struct RNode_POSTARG *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARGSCAT"]: + self._append_expression("*(struct RNode_ARGSCAT *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARGSPUSH"]: + self._append_expression("*(struct RNode_ARGSPUSH *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_SPLAT"]: + self._append_expression("*(struct RNode_SPLAT *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DEFN"]: + self._append_expression("*(struct RNode_DEFN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DEFS"]: + self._append_expression("*(struct RNode_DEFS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ALIAS"]: + self._append_expression("*(struct RNode_ALIAS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_VALIAS"]: + self._append_expression("*(struct RNode_VALIAS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_UNDEF"]: + self._append_expression("*(struct RNode_UNDEF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_CLASS"]: + self._append_expression("*(struct RNode_CLASS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_MODULE"]: + self._append_expression("*(struct RNode_MODULE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_SCLASS"]: + self._append_expression("*(struct RNode_SCLASS *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_COLON2"]: + self._append_expression("*(struct RNode_COLON2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_COLON3"]: + self._append_expression("*(struct RNode_COLON3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DOT2"]: + self._append_expression("*(struct RNode_DOT2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DOT3"]: + self._append_expression("*(struct RNode_DOT3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FLIP2"]: + self._append_expression("*(struct RNode_FLIP2 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FLIP3"]: + self._append_expression("*(struct RNode_FLIP3 *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_SELF"]: + self._append_expression("*(struct RNode_SELF *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_NIL"]: + self._append_expression("*(struct RNode_NIL *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_TRUE"]: + self._append_expression("*(struct RNode_TRUE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FALSE"]: + self._append_expression("*(struct RNode_FALSE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ERRINFO"]: + self._append_expression("*(struct RNode_ERRINFO *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DEFINED"]: + self._append_expression("*(struct RNode_DEFINED *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_POSTEXE"]: + self._append_expression("*(struct RNode_POSTEXE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_DSYM"]: + self._append_expression("*(struct RNode_DSYM *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ATTRASGN"]: + self._append_expression("*(struct RNode_ATTRASGN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LAMBDA"]: + self._append_expression("*(struct RNode_LAMBDA *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ARYPTN"]: + self._append_expression("*(struct RNode_ARYPTN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_HSHPTN"]: + self._append_expression("*(struct RNode_HSHPTN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FNDPTN"]: + self._append_expression("*(struct RNode_FNDPTN *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_ERROR"]: + self._append_expression("*(struct RNode_ERROR *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_LINE"]: + self._append_expression("*(struct RNode_LINE *) %0#x" % val.GetValueAsUnsigned()) + elif nd_type == self.ruby_globals["NODE_FILE"]: + self._append_expression("*(struct RNode_FILE *) %0#x" % val.GetValueAsUnsigned()) + else: + self._append_expression("*(struct RNode *) %0#x" % val.GetValueAsUnsigned()) + return True diff --git a/missing/setproctitle.c b/missing/setproctitle.c index f90886671c..5b2dfa65ce 100644 --- a/missing/setproctitle.c +++ b/missing/setproctitle.c @@ -87,7 +87,30 @@ static char **argv1_addr = NULL; #endif #if ALLOCATE_ENVIRON +/* system_environ is the value of environ before we allocate a custom buffer. + * + * We use this to restore environ in ruby_free_proctitle. + */ +static char **system_environ = NULL; +/* orig_environ is the buffer we allocate for environ. + * + * We use this to free this buffer in ruby_free_proctitle. When we add new + * environment variables using setenv, the system may change environ to a + * different buffer and will not free the original buffer, so we need to hold + * onto this so we can free it in ruby_free_proctitle. + * + * We must not free any of the contents because it may change if the system + * updates existing environment variables. + */ static char **orig_environ = NULL; +/* alloc_environ is a copy of orig_environ. + * + * We use this to free all the original string copies that were in orig_environ. + * Since environ could be changed to point to strings allocated by the system + * if environment variables are updated, so we need this to point to the + * original strings. + */ +static char **alloc_environ = NULL; #endif void @@ -112,6 +135,9 @@ compat_init_setproctitle(int argc, char *argv[]) /* Fail if we can't allocate room for the new environment */ for (i = 0; envp[i] != NULL; i++); + system_environ = environ; + + alloc_environ = xcalloc(i + 1, sizeof(*environ)); orig_environ = environ = xcalloc(i + 1, sizeof(*environ)); if (environ == NULL) { environ = envp; /* put it back */ @@ -140,8 +166,8 @@ compat_init_setproctitle(int argc, char *argv[]) argv_env_len = lastenvp - argv[0]; for (i = 0; envp[i] != NULL; i++) - environ[i] = ruby_strdup(envp[i]); - environ[i] = NULL; + alloc_environ[i] = environ[i] = ruby_strdup(envp[i]); + alloc_environ[i] = environ[i] = NULL; #endif /* SPT_REUSEARGV */ } @@ -153,16 +179,13 @@ ruby_free_proctitle(void) if (!orig_environ) return; /* environ is allocated by OS */ - /* ruby_setenv could allocate a new environ, so we need to free orig_environ - * in that case. */ - if (environ != orig_environ) { - for (int i = 0; orig_environ[i] != NULL; i++) { - xfree(orig_environ[i]); - } - - xfree(orig_environ); - orig_environ = NULL; + for (int i = 0; alloc_environ[i] != NULL; i++) { + xfree(alloc_environ[i]); } + xfree(alloc_environ); + xfree(orig_environ); + + environ = system_environ; #endif } @@ -10,19 +10,9 @@ **********************************************************************/ #ifdef UNIVERSAL_PARSER - #include <stddef.h> #include "node.h" #include "rubyparser.h" -#include "internal/parse.h" - -#else - -#include "internal.h" -#include "internal/hash.h" -#include "ruby/ruby.h" -#include "vm_core.h" - #endif #include "internal/variable.h" @@ -68,19 +58,16 @@ rb_node_buffer_new(void) init_node_buffer_list(&nb->buffer_list, (node_buffer_elem_t*)&nb[1], ruby_xmalloc); nb->local_tables = 0; nb->tokens = 0; -#ifdef UNIVERSAL_PARSER - nb->config = config; -#endif return nb; } #ifdef UNIVERSAL_PARSER #undef ruby_xmalloc -#define ruby_xmalloc ast->node_buffer->config->malloc +#define ruby_xmalloc ast->config->malloc #undef xfree -#define xfree ast->node_buffer->config->free -#define rb_xmalloc_mul_add ast->node_buffer->config->xmalloc_mul_add -#define ruby_xrealloc(var,size) (ast->node_buffer->config->realloc_n((void *)var, 1, size)) +#define xfree ast->config->free +#define rb_xmalloc_mul_add ast->config->xmalloc_mul_add +#define ruby_xrealloc(var,size) (ast->config->realloc_n((void *)var, 1, size)) #endif typedef void node_itr_t(rb_ast_t *ast, void *ctx, NODE *node); @@ -227,8 +214,8 @@ free_ast_value(rb_ast_t *ast, void *ctx, NODE *node) static void rb_node_buffer_free(rb_ast_t *ast, node_buffer_t *nb) { - if (ast->node_buffer && ast->node_buffer->tokens) { - parser_tokens_free(ast, ast->node_buffer->tokens); + if (nb && nb->tokens) { + parser_tokens_free(ast, nb->tokens); } iterate_node_values(ast, &nb->buffer_list, free_ast_value, NULL); node_buffer_list_free(ast, &nb->buffer_list); @@ -313,14 +300,19 @@ rb_ast_t * rb_ast_new(const rb_parser_config_t *config) { node_buffer_t *nb = rb_node_buffer_new(config); - return config->ast_new((VALUE)nb); + rb_ast_t *ast = (rb_ast_t *)config->calloc(1, sizeof(rb_ast_t)); + ast->config = config; + ast->node_buffer = nb; + return ast; } #else rb_ast_t * rb_ast_new(void) { node_buffer_t *nb = rb_node_buffer_new(); - return IMEMO_NEW(rb_ast_t, imemo_ast, (VALUE)nb); + rb_ast_t *ast = ruby_xcalloc(1, sizeof(rb_ast_t)); + ast->node_buffer = nb; + return ast; } #endif @@ -347,6 +339,7 @@ iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, vo static void script_lines_free(rb_ast_t *ast, rb_parser_ary_t *script_lines) { + if (!script_lines) return; for (long i = 0; i < script_lines->len; i++) { parser_string_free(ast, (rb_parser_string_t *)script_lines->data[i]); } @@ -357,14 +350,8 @@ script_lines_free(rb_ast_t *ast, rb_parser_ary_t *script_lines) void rb_ast_free(rb_ast_t *ast) { - if (ast->node_buffer) { - if (ast->body.script_lines && !FIXNUM_P((VALUE)ast->body.script_lines)) { - script_lines_free(ast, ast->body.script_lines); - ast->body.script_lines = NULL; - } - rb_node_buffer_free(ast, ast->node_buffer); - ast->node_buffer = 0; - } + rb_ast_dispose(ast); + xfree(ast); } static size_t @@ -382,20 +369,57 @@ buffer_list_size(node_buffer_list_t *nb) size_t rb_ast_memsize(const rb_ast_t *ast) { - size_t size = 0; + size_t size = sizeof(rb_ast_t); node_buffer_t *nb = ast->node_buffer; + rb_parser_ary_t *tokens = NULL; + struct rb_ast_local_table_link *link = NULL; + rb_parser_ary_t *script_lines = ast->body.script_lines; + + long i; if (nb) { size += sizeof(node_buffer_t); size += buffer_list_size(&nb->buffer_list); + link = nb->local_tables; + tokens = nb->tokens; } + + while (link) { + size += sizeof(struct rb_ast_local_table_link); + size += link->size * sizeof(ID); + link = link->next; + } + + if (tokens) { + size += sizeof(rb_parser_ary_t); + for (i = 0; i < tokens->len; i++) { + size += sizeof(rb_parser_ast_token_t); + rb_parser_ast_token_t *token = tokens->data[i]; + size += sizeof(rb_parser_string_t); + size += token->str->len + 1; + } + } + + if (script_lines) { + size += sizeof(rb_parser_ary_t); + for (i = 0; i < script_lines->len; i++) { + size += sizeof(rb_parser_string_t); + size += ((rb_parser_string_t *)script_lines->data[i])->len + 1; + } + } + return size; } void rb_ast_dispose(rb_ast_t *ast) { - rb_ast_free(ast); + if (ast && ast->node_buffer) { + script_lines_free(ast, ast->body.script_lines); + ast->body.script_lines = NULL; + rb_node_buffer_free(ast, ast->node_buffer); + ast->node_buffer = 0; + } } VALUE @@ -39,9 +39,6 @@ struct node_buffer_struct { // - location info // Array, whose entry is array rb_parser_ary_t *tokens; -#ifdef UNIVERSAL_PARSER - const rb_parser_config_t *config; -#endif }; RUBY_SYMBOL_EXPORT_BEGIN @@ -88,6 +85,7 @@ RUBY_SYMBOL_EXPORT_END #define NODE_SPECIAL_EXCESSIVE_COMMA ((ID)1) #define NODE_SPECIAL_NO_REST_KEYWORD ((NODE *)-1) +#define nd_code_loc(n) (&RNODE(n)->nd_loc) #define nd_first_column(n) ((int)(RNODE(n)->nd_loc.beg_pos.column)) #define nd_set_first_column(n, v) (RNODE(n)->nd_loc.beg_pos.column = (v)) #define nd_first_lineno(n) ((int)(RNODE(n)->nd_loc.beg_pos.lineno)) @@ -862,6 +862,8 @@ rb_int_zero_p(VALUE num) * Of the Core and Standard Library classes, * Integer, Float, Rational, and Complex use this implementation. * + * Related: #zero? + * */ static VALUE @@ -3236,15 +3236,22 @@ ALWAYS_INLINE(static VALUE rb_to_integer_with_id_exception(VALUE val, const char static inline VALUE rb_to_integer_with_id_exception(VALUE val, const char *method, ID mid, int raise) { + // We need to pop the lazily pushed frame when not raising an exception. + rb_control_frame_t *current_cfp; VALUE v; if (RB_INTEGER_TYPE_P(val)) return val; + current_cfp = GET_EC()->cfp; rb_yjit_lazy_push_frame(GET_EC()->cfp->pc); v = try_to_int(val, mid, raise); - if (!raise && NIL_P(v)) return Qnil; + if (!raise && NIL_P(v)) { + GET_EC()->cfp = current_cfp; + return Qnil; + } if (!RB_INTEGER_TYPE_P(v)) { conversion_mismatch(val, "Integer", method, v); } + GET_EC()->cfp = current_cfp; return v; } #define rb_to_integer(val, method, mid) \ @@ -81,14 +81,10 @@ syntax_error_new(void) static NODE *reg_named_capture_assign(struct parser_params* p, VALUE regexp, const YYLTYPE *loc); #define compile_callback rb_suppress_tracing -VALUE rb_io_gets_internal(VALUE io); #endif /* !UNIVERSAL_PARSER */ static int rb_parser_string_hash_cmp(rb_parser_string_t *str1, rb_parser_string_t *str2); - -#ifndef RIPPER static rb_parser_string_t *rb_parser_string_deep_copy(struct parser_params *p, const rb_parser_string_t *original); -#endif static int node_integer_cmp(rb_node_integer_t *n1, rb_node_integer_t *n2) @@ -490,8 +486,8 @@ struct parser_params { struct { rb_strterm_t *strterm; - VALUE (*gets)(struct parser_params*,VALUE); - VALUE input; + rb_parser_lex_gets_func *gets; + rb_parser_input_data input; parser_string_buffer_t string_buffer; rb_parser_string_t *lastline; rb_parser_string_t *nextline; @@ -499,10 +495,6 @@ struct parser_params { const char *pcur; const char *pend; const char *ptok; - union { - long ptr; - VALUE (*call)(VALUE, int); - } gets_; enum lex_state_e state; /* track the nest level of any parens "()[]{}" */ int paren_nest; @@ -525,7 +517,7 @@ struct parser_params { int line_count; int ruby_sourceline; /* current line no. */ const char *ruby_sourcefile; /* current source file */ - VALUE ruby_sourcefile_string; + rb_parser_string_t *ruby_sourcefile_string; rb_encoding *enc; token_info *token_info; st_table *case_labels; @@ -1166,7 +1158,7 @@ static rb_node_aryptn_t *rb_node_aryptn_new(struct parser_params *p, NODE *pre_a static rb_node_hshptn_t *rb_node_hshptn_new(struct parser_params *p, NODE *nd_pconst, NODE *nd_pkwargs, NODE *nd_pkwrestarg, const YYLTYPE *loc); static rb_node_fndptn_t *rb_node_fndptn_new(struct parser_params *p, NODE *pre_rest_arg, NODE *args, NODE *post_rest_arg, const YYLTYPE *loc); static rb_node_line_t *rb_node_line_new(struct parser_params *p, const YYLTYPE *loc); -static rb_node_file_t *rb_node_file_new(struct parser_params *p, VALUE str, const YYLTYPE *loc); +static rb_node_file_t *rb_node_file_new(struct parser_params *p, rb_parser_string_t *str, const YYLTYPE *loc); static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE *loc); #define NEW_SCOPE(a,b,loc) (NODE *)rb_node_scope_new(p,a,b,loc) @@ -1775,9 +1767,6 @@ endless_method_name(struct parser_params *p, ID mid, const YYLTYPE *loc) local_push(p, 0); \ } while (0) -#define Qnone 0 -#define Qnull 0 - #ifndef RIPPER # define ifndef_ripper(x) (x) #else @@ -2111,6 +2100,41 @@ rb_parser_encoding_string_new(rb_parser_t *p, const char *ptr, long len, rb_enco } #ifndef RIPPER +static bool +zero_filled(const char *s, int n) +{ + for (; n > 0; --n) { + if (*s++) return false; + } + return true; +} + +static bool +str_null_char(rb_parser_t *p, const char *s, long len, const int minlen, rb_encoding *enc) +{ + const char *e = s + len; + + for (; s + minlen <= e; s += rb_enc_mbclen(s, e, enc)) { + if (zero_filled(s, minlen)) return true; + } + return false; +} + +static bool +cstr_null_check(rb_parser_t *p, const char *s, long len, rb_encoding *enc, int *w) +{ + const int minlen = rb_enc_mbminlen(enc); + + if (minlen > 1) { + *w = 1; + return str_null_char(p, s, len, minlen, enc); + } + else { + *w = 0; + return (!s || memchr(s, 0, len)); + } +} + rb_parser_string_t * rb_str_to_parser_string(rb_parser_t *p, VALUE str) { @@ -2830,22 +2854,19 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) %type <node> if_tail opt_else case_body case_args cases opt_rescue exc_list exc_var opt_ensure %type <node> args arg_splat call_args opt_call_args %type <node> paren_args opt_paren_args -%type <node_args> args_tail opt_args_tail block_args_tail opt_block_args_tail +%type <node_args> args_tail block_args_tail %type <node> command_args aref_args %type <node_block_pass> opt_block_arg block_arg %type <node> var_ref var_lhs %type <node> command_rhs arg_rhs %type <node> command_asgn mrhs mrhs_arg superclass block_call block_command -%type <node_opt_arg> f_block_optarg f_block_opt %type <node_args> f_arglist f_opt_paren_args f_paren_args f_args %type <node_args_aux> f_arg f_arg_item -%type <node_opt_arg> f_optarg %type <node> f_marg f_marg_list f_rest_marg %type <node_masgn> f_margs %type <node> assoc_list assocs assoc undef_list backref string_dvar for_var %type <node_args> block_param opt_block_param block_param_def -%type <node_opt_arg> f_opt -%type <node_kw_arg> f_kwarg f_kw f_block_kwarg f_block_kw +%type <node_kw_arg> f_kw f_block_kw %type <id> bv_decls opt_bv_decl bvar %type <node> lambda lambda_body brace_body do_body %type <node_args> f_larglist @@ -2959,6 +2980,61 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) %token tLAST_TOKEN +/* + * parameterizing rules + */ +%rule f_opt(value) <node_opt_arg>: f_arg_asgn f_eq value + { + p->cur_arg = 0; + p->ctxt.in_argdef = 1; + $$ = NEW_OPT_ARG(assignable(p, $1, $3, &@$), &@$); + /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:3)) %*/ + } + ; + +%rule f_optarg(value) <node_opt_arg>: f_opt(value) + { + $$ = $1; + /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ + } + | f_optarg(value) ',' f_opt(value) + { + $$ = opt_arg_append($1, $3); + /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ + } + ; + +%rule f_kwarg(kw) <node_kw_arg>: kw + { + $$ = $1; + /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ + } + | f_kwarg(kw) ',' kw + { + $$ = kwd_append($1, $3); + /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ + } + ; + +%rule opt_args_tail(tail) <node_args>: ',' tail + { + $$ = $2; + /*% ripper: get_value($:2); %*/ + } + | /* none */ + { + $$ = new_args_tail(p, 0, 0, 0, &@0); + /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, Qnil); %*/ + } + ; + +%rule words(begin, word_list): begin ' '+ word_list tSTRING_END + { + $$ = make_list($3, &@$); + /*% ripper: array!($:3) %*/ + } + ; + %% program : { SET_LEX_STATE(EXPR_BEG); @@ -3306,7 +3382,7 @@ command_asgn : lhs '=' lex_ctxt command_rhs rb_backref_error(p, $1); /*% %*/ $$ = NEW_ERROR(&@$); - /*% ripper[error]: backref_error(p, RNODE($:1), assign!(var_field(p, get_value($:1)), $:4)) %*/ + /*% ripper[error]: backref_error(p, $1, opassign!(var_field(p, get_value($:1)), $:2, $:4)) %*/ } ; @@ -3492,7 +3568,7 @@ command : fcall command_args %prec tLOWEST } | primary_value call_op operation2 command_args %prec tLOWEST { - $$ = new_command_qcall(p, $2, $1, $3, $4, Qnull, &@3, &@$); + $$ = new_command_qcall(p, $2, $1, $3, $4, 0, &@3, &@$); /*% ripper: command_call!($:1, $:2, $:3, $:4) %*/ } | primary_value call_op operation2 command_args cmd_brace_block @@ -3502,7 +3578,7 @@ command : fcall command_args %prec tLOWEST } | primary_value tCOLON2 operation2 command_args %prec tLOWEST { - $$ = new_command_qcall(p, idCOLON2, $1, $3, $4, Qnull, &@3, &@$); + $$ = new_command_qcall(p, idCOLON2, $1, $3, $4, 0, &@3, &@$); /*% ripper: command_call!($:1, $:2, $:3, $:4) %*/ } | primary_value tCOLON2 operation2 command_args cmd_brace_block @@ -3513,7 +3589,7 @@ command : fcall command_args %prec tLOWEST | primary_value tCOLON2 tCONSTANT '{' brace_body '}' { set_embraced_location($5, &@4, &@6); - $$ = new_command_qcall(p, idCOLON2, $1, $3, Qnull, $5, &@3, &@$); + $$ = new_command_qcall(p, idCOLON2, $1, $3, 0, $5, &@3, &@$); /*% ripper: method_add_block!(command_call!($:1, $:2, $:3, Qnil), $:5) %*/ } | keyword_super command_args @@ -3905,7 +3981,7 @@ arg : lhs '=' lex_ctxt arg_rhs /*%%%*/ $$ = NEW_ERROR(&@$); /*% %*/ - /*% ripper[error]: backref_error(p, RNODE($:1), opassign!(var_field(p, get_value($:1)), $:2, $:4)) %*/ + /*% ripper[error]: backref_error(p, $1, opassign!(var_field(p, get_value($:1)), $:2, $:4)) %*/ } | arg tDOT2 arg { @@ -4219,7 +4295,7 @@ paren_args : '(' opt_call_args rparen | '(' args ',' args_forward rparen { if (!check_forwarding_args(p)) { - $$ = Qnone; + $$ = 0; } else { $$ = new_args_forward_call(p, $2, &@4, &@$); @@ -4229,7 +4305,7 @@ paren_args : '(' opt_call_args rparen | '(' args_forward rparen { if (!check_forwarding_args(p)) { - $$ = Qnone; + $$ = 0; } else { $$ = new_args_forward_call(p, 0, &@2, &@$); @@ -5027,40 +5103,28 @@ f_any_kwrest : f_kwrest f_eq : {p->ctxt.in_argdef = 0;} '='; -block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg +block_args_tail : f_kwarg(f_block_kw) ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4, &@3); /*% ripper: rb_ary_new_from_args(3, get_value($:1), get_value($:3), get_value($:4)); %*/ } - | f_block_kwarg opt_f_block_arg + | f_kwarg(f_block_kw) opt_f_block_arg { - $$ = new_args_tail(p, $1, Qnone, $2, &@1); + $$ = new_args_tail(p, $1, 0, $2, &@1); /*% ripper: rb_ary_new_from_args(3, get_value($:1), Qnil, get_value($:2)); %*/ } | f_any_kwrest opt_f_block_arg { - $$ = new_args_tail(p, Qnone, $1, $2, &@1); + $$ = new_args_tail(p, 0, $1, $2, &@1); /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), get_value($:2)); %*/ } | f_block_arg { - $$ = new_args_tail(p, Qnone, Qnone, $1, &@1); + $$ = new_args_tail(p, 0, 0, $1, &@1); /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, get_value($:1)); %*/ } ; -opt_block_args_tail : ',' block_args_tail - { - $$ = $2; - /*% ripper: get_value($:2); %*/ - } - | /* none */ - { - $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0); - /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, Qnil); %*/ - } - ; - excessed_comma : ',' { /* magic number for rest_id in iseq_set_arguments() */ @@ -5069,80 +5133,80 @@ excessed_comma : ',' } ; -block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail +block_param : f_arg ',' f_optarg(primary_value) ',' f_rest_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, $1, $3, $5, Qnone, $6, &@$); + $$ = new_args(p, $1, $3, $5, 0, $6, &@$); /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), Qnil, get_value($:6)) %*/ } - | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail + | f_arg ',' f_optarg(primary_value) ',' f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, $1, $3, $5, $7, $8, &@$); /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), get_value($:7), get_value($:8)) %*/ } - | f_arg ',' f_block_optarg opt_block_args_tail + | f_arg ',' f_optarg(primary_value) opt_args_tail(block_args_tail) { - $$ = new_args(p, $1, $3, Qnone, Qnone, $4, &@$); + $$ = new_args(p, $1, $3, 0, 0, $4, &@$); /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, Qnil, get_value($:4)) %*/ } - | f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail + | f_arg ',' f_optarg(primary_value) ',' f_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, $1, $3, Qnone, $5, $6, &@$); + $$ = new_args(p, $1, $3, 0, $5, $6, &@$); /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, get_value($:5), get_value($:6)) %*/ } - | f_arg ',' f_rest_arg opt_block_args_tail + | f_arg ',' f_rest_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, $1, Qnone, $3, Qnone, $4, &@$); + $$ = new_args(p, $1, 0, $3, 0, $4, &@$); /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), Qnil, get_value($:4)) %*/ } | f_arg excessed_comma { - $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@2); - $$ = new_args(p, $1, Qnone, $2, Qnone, $$, &@$); + $$ = new_args_tail(p, 0, 0, 0, &@2); + $$ = new_args(p, $1, 0, $2, 0, $$, &@$); /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:2), Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/ } - | f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail + | f_arg ',' f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, $1, Qnone, $3, $5, $6, &@$); + $$ = new_args(p, $1, 0, $3, $5, $6, &@$); /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), get_value($:5), get_value($:6)) %*/ } - | f_arg opt_block_args_tail + | f_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, $1, Qnone, Qnone, Qnone, $2, &@$); + $$ = new_args(p, $1, 0, 0, 0, $2, &@$); /*% ripper: ripper_new_args(p, get_value($:1), Qnil, Qnil, Qnil, get_value($:2)) %*/ } - | f_block_optarg ',' f_rest_arg opt_block_args_tail + | f_optarg(primary_value) ',' f_rest_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, Qnone, $1, $3, Qnone, $4, &@$); + $$ = new_args(p, 0, $1, $3, 0, $4, &@$); /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), Qnil, get_value($:4)) %*/ } - | f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail + | f_optarg(primary_value) ',' f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, Qnone, $1, $3, $5, $6, &@$); + $$ = new_args(p, 0, $1, $3, $5, $6, &@$); /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), get_value($:5), get_value($:6)) %*/ } - | f_block_optarg opt_block_args_tail + | f_optarg(primary_value) opt_args_tail(block_args_tail) { - $$ = new_args(p, Qnone, $1, Qnone, Qnone, $2, &@$); + $$ = new_args(p, 0, $1, 0, 0, $2, &@$); /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, Qnil, get_value($:2)) %*/ } - | f_block_optarg ',' f_arg opt_block_args_tail + | f_optarg(primary_value) ',' f_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, Qnone, $1, Qnone, $3, $4, &@$); + $$ = new_args(p, 0, $1, 0, $3, $4, &@$); /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, get_value($:3), get_value($:4)) %*/ } - | f_rest_arg opt_block_args_tail + | f_rest_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, Qnone, Qnone, $1, Qnone, $2, &@$); + $$ = new_args(p, 0, 0, $1, 0, $2, &@$); /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), Qnil, get_value($:2)) %*/ } - | f_rest_arg ',' f_arg opt_block_args_tail + | f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { - $$ = new_args(p, Qnone, Qnone, $1, $3, $4, &@$); + $$ = new_args(p, 0, 0, $1, $3, $4, &@$); /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), get_value($:3), get_value($:4)) %*/ } | block_args_tail { - $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $1, &@$); + $$ = new_args(p, 0, 0, 0, 0, $1, &@$); /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, get_value($:1)) %*/ } ; @@ -5350,7 +5414,7 @@ method_call : fcall paren_args } | primary_value tCOLON2 operation3 { - $$ = new_qcall(p, idCOLON2, $1, $3, Qnull, &@3, &@$); + $$ = new_qcall(p, idCOLON2, $1, $3, 0, &@3, &@$); /*% ripper: call!($:1, $:2, $:3) %*/ } | primary_value call_op paren_args @@ -5522,29 +5586,29 @@ p_top_expr : p_top_expr_body p_top_expr_body : p_expr | p_expr ',' { - $$ = new_array_pattern_tail(p, Qnone, 1, Qnone, Qnone, &@$); - $$ = new_array_pattern(p, Qnone, $1, $$, &@$); + $$ = new_array_pattern_tail(p, 0, 1, 0, 0, &@$); + $$ = new_array_pattern(p, 0, $1, $$, &@$); /*% ripper: ripper_new_array_pattern(p, Qnil, get_value($:1), rb_ary_new()); %*/ } | p_expr ',' p_args { - $$ = new_array_pattern(p, Qnone, $1, $3, &@$); + $$ = new_array_pattern(p, 0, $1, $3, &@$); nd_set_first_loc($$, @1.beg_pos); /*% ripper: ripper_new_array_pattern(p, Qnil, get_value($:1), get_value($:3)); %*/ } | p_find { - $$ = new_find_pattern(p, Qnone, $1, &@$); + $$ = new_find_pattern(p, 0, $1, &@$); /*% ripper: ripper_new_find_pattern(p, Qnil, get_value($:1)); %*/ } | p_args_tail { - $$ = new_array_pattern(p, Qnone, Qnone, $1, &@$); + $$ = new_array_pattern(p, 0, 0, $1, &@$); /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, get_value($:1)); %*/ } | p_kwargs { - $$ = new_hash_pattern(p, Qnone, $1, &@$); + $$ = new_hash_pattern(p, 0, $1, &@$); /*% ripper: ripper_new_hash_pattern(p, Qnil, get_value($:1)); %*/ } ; @@ -5589,7 +5653,7 @@ p_expr_basic : p_value | p_const p_lparen[p_pktbl] p_args rparen { pop_pktbl(p, $p_pktbl); - $$ = new_array_pattern(p, $p_const, Qnone, $p_args, &@$); + $$ = new_array_pattern(p, $p_const, 0, $p_args, &@$); nd_set_first_loc($$, @p_const.beg_pos); /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, get_value($:p_args)); %*/ } @@ -5609,14 +5673,14 @@ p_expr_basic : p_value } | p_const '(' rparen { - $$ = new_array_pattern_tail(p, Qnone, 0, Qnone, Qnone, &@$); - $$ = new_array_pattern(p, $p_const, Qnone, $$, &@$); + $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$); + $$ = new_array_pattern(p, $p_const, 0, $$, &@$); /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, rb_ary_new()); %*/ } | p_const p_lbracket[p_pktbl] p_args rbracket { pop_pktbl(p, $p_pktbl); - $$ = new_array_pattern(p, $p_const, Qnone, $p_args, &@$); + $$ = new_array_pattern(p, $p_const, 0, $p_args, &@$); nd_set_first_loc($$, @p_const.beg_pos); /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, get_value($:p_args)); %*/ } @@ -5636,24 +5700,24 @@ p_expr_basic : p_value } | p_const '[' rbracket { - $$ = new_array_pattern_tail(p, Qnone, 0, Qnone, Qnone, &@$); - $$ = new_array_pattern(p, $1, Qnone, $$, &@$); + $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$); + $$ = new_array_pattern(p, $1, 0, $$, &@$); /*% ripper: ripper_new_array_pattern(p, get_value($:1), Qnil, rb_ary_new()); %*/ } | tLBRACK p_args rbracket { - $$ = new_array_pattern(p, Qnone, Qnone, $p_args, &@$); + $$ = new_array_pattern(p, 0, 0, $p_args, &@$); /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, get_value($:p_args)); %*/ } | tLBRACK p_find rbracket { - $$ = new_find_pattern(p, Qnone, $p_find, &@$); + $$ = new_find_pattern(p, 0, $p_find, &@$); /*% ripper: ripper_new_find_pattern(p, Qnil, get_value($:p_find)); %*/ } | tLBRACK rbracket { - $$ = new_array_pattern_tail(p, Qnone, 0, Qnone, Qnone, &@$); - $$ = new_array_pattern(p, Qnone, Qnone, $$, &@$); + $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$); + $$ = new_array_pattern(p, 0, 0, $$, &@$); /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, rb_ary_new()); %*/ } | tLBRACE p_pktbl lex_ctxt[ctxt] @@ -5664,13 +5728,13 @@ p_expr_basic : p_value { pop_pktbl(p, $p_pktbl); p->ctxt.in_kwarg = $ctxt.in_kwarg; - $$ = new_hash_pattern(p, Qnone, $p_kwargs, &@$); + $$ = new_hash_pattern(p, 0, $p_kwargs, &@$); /*% ripper: ripper_new_hash_pattern(p, Qnil, get_value($:p_kwargs)); %*/ } | tLBRACE rbrace { - $$ = new_hash_pattern_tail(p, Qnone, 0, &@$); - $$ = new_hash_pattern(p, Qnone, $$, &@$); + $$ = new_hash_pattern_tail(p, 0, 0, &@$); + $$ = new_hash_pattern(p, 0, $$, &@$); /*%%%*/ /*% VALUE val = ripper_new_hash_pattern_tail(p, Qnil, 0); @@ -5689,7 +5753,7 @@ p_expr_basic : p_value p_args : p_expr { NODE *pre_args = NEW_LIST($1, &@$); - $$ = new_array_pattern_tail(p, pre_args, 0, Qnone, Qnone, &@$); + $$ = new_array_pattern_tail(p, pre_args, 0, 0, 0, &@$); /*%%%*/ /*% VALUE ary = rb_ary_new_from_args(1, get_value($:1)); @@ -5698,7 +5762,7 @@ p_args : p_expr } | p_args_head { - $$ = new_array_pattern_tail(p, $1, 1, Qnone, Qnone, &@$); + $$ = new_array_pattern_tail(p, $1, 1, 0, 0, &@$); /*%%%*/ /*% set_value(rb_ary_new_from_args(3, get_value($:1), Qnil, Qnil)); @@ -5706,7 +5770,7 @@ p_args : p_expr } | p_args_head p_arg { - $$ = new_array_pattern_tail(p, list_concat($1, $2), 0, Qnone, Qnone, &@$); + $$ = new_array_pattern_tail(p, list_concat($1, $2), 0, 0, 0, &@$); /*%%%*/ /*% VALUE pre_args = rb_ary_concat(get_value($:1), get_value($:2)); @@ -5715,7 +5779,7 @@ p_args : p_expr } | p_args_head p_rest { - $$ = new_array_pattern_tail(p, $1, 1, $2, Qnone, &@$); + $$ = new_array_pattern_tail(p, $1, 1, $2, 0, &@$); /*%%%*/ /*% set_value(rb_ary_new_from_args(3, get_value($:1), get_value($:2), Qnil)); @@ -5745,12 +5809,12 @@ p_args_head : p_arg ',' p_args_tail : p_rest { - $$ = new_array_pattern_tail(p, Qnone, 1, $1, Qnone, &@$); + $$ = new_array_pattern_tail(p, 0, 1, $1, 0, &@$); /*% ripper: ripper_new_array_pattern_tail(p, Qnil, get_value($:1), Qnil); %*/ } | p_rest ',' p_args_post { - $$ = new_array_pattern_tail(p, Qnone, 1, $1, $3, &@$); + $$ = new_array_pattern_tail(p, 0, 1, $1, $3, &@$); /*% ripper: ripper_new_array_pattern_tail(p, Qnil, get_value($:1), get_value($:3)); %*/ } ; @@ -5808,7 +5872,7 @@ p_kwargs : p_kwarg ',' p_any_kwrest } | p_any_kwrest { - $$ = new_hash_pattern_tail(p, new_hash(p, Qnone, &@$), $1, &@$); + $$ = new_hash_pattern_tail(p, new_hash(p, 0, &@$), $1, &@$); /*% ripper: ripper_new_hash_pattern_tail(p, rb_ary_new(), get_value($:1)) %*/ } ; @@ -6112,15 +6176,7 @@ regexp : tREGEXP_BEG regexp_contents tREGEXP_END } ; -words_sep : ' ' {} - | words_sep ' ' - ; - -words : tWORDS_BEG words_sep word_list tSTRING_END - { - $$ = make_list($3, &@$); - /*% ripper: array!($:3) %*/ - } +words : words(tWORDS_BEG, word_list) <node> ; word_list : /* none */ @@ -6128,7 +6184,7 @@ word_list : /* none */ $$ = 0; /*% ripper: words_new! %*/ } - | word_list word words_sep + | word_list word ' '+ { $$ = list_append(p, $1, evstr2dstr(p, $2)); /*% ripper: words_add!($:1, $:2) %*/ @@ -6144,11 +6200,7 @@ word : string_content } ; -symbols : tSYMBOLS_BEG words_sep symbol_list tSTRING_END - { - $$ = make_list($3, &@$); - /*% ripper: array!($:3) %*/ - } +symbols : words(tSYMBOLS_BEG, symbol_list) <node> ; symbol_list : /* none */ @@ -6156,25 +6208,17 @@ symbol_list : /* none */ $$ = 0; /*% ripper: symbols_new! %*/ } - | symbol_list word words_sep + | symbol_list word ' '+ { $$ = symbol_append(p, $1, evstr2dstr(p, $2)); /*% ripper: symbols_add!($:1, $:2) %*/ } ; -qwords : tQWORDS_BEG words_sep qword_list tSTRING_END - { - $$ = make_list($3, &@$); - /*% ripper: array!($:3) %*/ - } +qwords : words(tQWORDS_BEG, qword_list) <node> ; -qsymbols : tQSYMBOLS_BEG words_sep qsym_list tSTRING_END - { - $$ = make_list($3, &@$); - /*% ripper: array!($:3) %*/ - } +qsymbols : words(tQSYMBOLS_BEG, qsym_list) <node> ; qword_list : /* none */ @@ -6182,7 +6226,7 @@ qword_list : /* none */ $$ = 0; /*% ripper: qwords_new! %*/ } - | qword_list tSTRING_CONTENT words_sep + | qword_list tSTRING_CONTENT ' '+ { $$ = list_append(p, $1, $2); /*% ripper: qwords_add!($:1, $:2) %*/ @@ -6194,7 +6238,7 @@ qsym_list : /* none */ $$ = 0; /*% ripper: qsymbols_new! %*/ } - | qsym_list tSTRING_CONTENT words_sep + | qsym_list tSTRING_CONTENT ' '+ { $$ = symbol_append(p, $1, $2); /*% ripper: qsymbols_add!($:1, $:2) %*/ @@ -6444,8 +6488,8 @@ f_opt_paren_args: f_paren_args | none { p->ctxt.in_argdef = 0; - $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0); - $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $$, &@0); + $$ = new_args_tail(p, 0, 0, 0, &@0); + $$ = new_args(p, 0, 0, 0, 0, $$, &@0); /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/ } ; @@ -6478,121 +6522,109 @@ f_arglist : f_paren_args } ; -args_tail : f_kwarg ',' f_kwrest opt_f_block_arg +args_tail : f_kwarg(f_kw) ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4, &@3); /*% ripper: rb_ary_new_from_args(3, get_value($:1), get_value($:3), get_value($:4)); %*/ } - | f_kwarg opt_f_block_arg + | f_kwarg(f_kw) opt_f_block_arg { - $$ = new_args_tail(p, $1, Qnone, $2, &@1); + $$ = new_args_tail(p, $1, 0, $2, &@1); /*% ripper: rb_ary_new_from_args(3, get_value($:1), Qnil, get_value($:2)); %*/ } | f_any_kwrest opt_f_block_arg { - $$ = new_args_tail(p, Qnone, $1, $2, &@1); + $$ = new_args_tail(p, 0, $1, $2, &@1); /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), get_value($:2)); %*/ } | f_block_arg { - $$ = new_args_tail(p, Qnone, Qnone, $1, &@1); + $$ = new_args_tail(p, 0, 0, $1, &@1); /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, get_value($:1)); %*/ } | args_forward { add_forwarding_args(p); - $$ = new_args_tail(p, Qnone, $1, arg_FWD_BLOCK, &@1); + $$ = new_args_tail(p, 0, $1, arg_FWD_BLOCK, &@1); $$->nd_ainfo.forwarding = 1; /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), Qnil); %*/ } ; -opt_args_tail : ',' args_tail +f_args : f_arg ',' f_optarg(arg_value) ',' f_rest_arg opt_args_tail(args_tail) { - $$ = $2; - /*% ripper: get_value($:2); %*/ - } - | /* none */ - { - $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0); - /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, Qnil); %*/ - } - ; - -f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail - { - $$ = new_args(p, $1, $3, $5, Qnone, $6, &@$); + $$ = new_args(p, $1, $3, $5, 0, $6, &@$); /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), Qnil, get_value($:6)) %*/ } - | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail + | f_arg ',' f_optarg(arg_value) ',' f_rest_arg ',' f_arg opt_args_tail(args_tail) { $$ = new_args(p, $1, $3, $5, $7, $8, &@$); /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), get_value($:7), get_value($:8)) %*/ } - | f_arg ',' f_optarg opt_args_tail + | f_arg ',' f_optarg(arg_value) opt_args_tail(args_tail) { - $$ = new_args(p, $1, $3, Qnone, Qnone, $4, &@$); + $$ = new_args(p, $1, $3, 0, 0, $4, &@$); /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, Qnil, get_value($:4)) %*/ } - | f_arg ',' f_optarg ',' f_arg opt_args_tail + | f_arg ',' f_optarg(arg_value) ',' f_arg opt_args_tail(args_tail) { - $$ = new_args(p, $1, $3, Qnone, $5, $6, &@$); + $$ = new_args(p, $1, $3, 0, $5, $6, &@$); /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, get_value($:5), get_value($:6)) %*/ } - | f_arg ',' f_rest_arg opt_args_tail + | f_arg ',' f_rest_arg opt_args_tail(args_tail) { - $$ = new_args(p, $1, Qnone, $3, Qnone, $4, &@$); + $$ = new_args(p, $1, 0, $3, 0, $4, &@$); /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), Qnil, get_value($:4)) %*/ } - | f_arg ',' f_rest_arg ',' f_arg opt_args_tail + | f_arg ',' f_rest_arg ',' f_arg opt_args_tail(args_tail) { - $$ = new_args(p, $1, Qnone, $3, $5, $6, &@$); + $$ = new_args(p, $1, 0, $3, $5, $6, &@$); /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), get_value($:5), get_value($:6)) %*/ } - | f_arg opt_args_tail + | f_arg opt_args_tail(args_tail) { - $$ = new_args(p, $1, Qnone, Qnone, Qnone, $2, &@$); + $$ = new_args(p, $1, 0, 0, 0, $2, &@$); /*% ripper: ripper_new_args(p, get_value($:1), Qnil, Qnil, Qnil, get_value($:2)) %*/ } - | f_optarg ',' f_rest_arg opt_args_tail + | f_optarg(arg_value) ',' f_rest_arg opt_args_tail(args_tail) { - $$ = new_args(p, Qnone, $1, $3, Qnone, $4, &@$); + $$ = new_args(p, 0, $1, $3, 0, $4, &@$); /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), Qnil, get_value($:4)) %*/ } - | f_optarg ',' f_rest_arg ',' f_arg opt_args_tail + | f_optarg(arg_value) ',' f_rest_arg ',' f_arg opt_args_tail(args_tail) { - $$ = new_args(p, Qnone, $1, $3, $5, $6, &@$); + $$ = new_args(p, 0, $1, $3, $5, $6, &@$); /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), get_value($:5), get_value($:6)) %*/ } - | f_optarg opt_args_tail + | f_optarg(arg_value) opt_args_tail(args_tail) { - $$ = new_args(p, Qnone, $1, Qnone, Qnone, $2, &@$); + $$ = new_args(p, 0, $1, 0, 0, $2, &@$); /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, Qnil, get_value($:2)) %*/ } - | f_optarg ',' f_arg opt_args_tail + | f_optarg(arg_value) ',' f_arg opt_args_tail(args_tail) { - $$ = new_args(p, Qnone, $1, Qnone, $3, $4, &@$); + $$ = new_args(p, 0, $1, 0, $3, $4, &@$); /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, get_value($:3), get_value($:4)) %*/ } - | f_rest_arg opt_args_tail + | f_rest_arg opt_args_tail(args_tail) { - $$ = new_args(p, Qnone, Qnone, $1, Qnone, $2, &@$); + $$ = new_args(p, 0, 0, $1, 0, $2, &@$); /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), Qnil, get_value($:2)) %*/ } - | f_rest_arg ',' f_arg opt_args_tail + | f_rest_arg ',' f_arg opt_args_tail(args_tail) { - $$ = new_args(p, Qnone, Qnone, $1, $3, $4, &@$); + $$ = new_args(p, 0, 0, $1, $3, $4, &@$); /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), get_value($:3), get_value($:4)) %*/ } | args_tail { - $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $1, &@$); + $$ = new_args(p, 0, 0, 0, 0, $1, &@$); /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, get_value($:1)) %*/ } | /* none */ { - $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0); - $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $$, &@0); + $$ = new_args_tail(p, 0, 0, 0, &@0); + $$ = new_args(p, 0, 0, 0, 0, $$, &@0); /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/ } ; @@ -6750,31 +6782,6 @@ f_block_kw : f_label primary_value } ; -f_block_kwarg : f_block_kw - { - $$ = $1; - /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ - } - | f_block_kwarg ',' f_block_kw - { - $$ = kwd_append($1, $3); - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ - } - ; - - -f_kwarg : f_kw - { - $$ = $1; - /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ - } - | f_kwarg ',' f_kw - { - $$ = kwd_append($1, $3); - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ - } - ; - kwrest_mark : tPOW | tDSTAR ; @@ -6799,48 +6806,6 @@ f_kwrest : kwrest_mark tIDENTIFIER } ; -f_opt : f_arg_asgn f_eq arg_value - { - p->cur_arg = 0; - p->ctxt.in_argdef = 1; - $$ = NEW_OPT_ARG(assignable(p, $1, $3, &@$), &@$); - /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:3)) %*/ - } - ; - -f_block_opt : f_arg_asgn f_eq primary_value - { - p->cur_arg = 0; - p->ctxt.in_argdef = 1; - $$ = NEW_OPT_ARG(assignable(p, $1, $3, &@$), &@$); - /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:3)) %*/ - } - ; - -f_block_optarg : f_block_opt - { - $$ = $1; - /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ - } - | f_block_optarg ',' f_block_opt - { - $$ = opt_arg_append($1, $3); - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ - } - ; - -f_optarg : f_opt - { - $$ = $1; - /*% ripper: rb_ary_new3(1, get_value($:1)) %*/ - } - | f_optarg ',' f_opt - { - $$ = opt_arg_append($1, $3); - /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/ - } - ; - restarg_mark : '*' | tSTAR ; @@ -6884,7 +6849,7 @@ opt_f_block_arg : ',' f_block_arg } | none { - $$ = Qnull; + $$ = 0; /*% ripper: Qnil; %*/ } ; @@ -7051,7 +7016,7 @@ terms : term none : /* none */ { - $$ = Qnull; + $$ = 0; /*%%%*/ /*% set_value(rb_ripper_none); @@ -7713,7 +7678,7 @@ yycompile0(VALUE arg) struct parser_params *p = (struct parser_params *)arg; int cov = FALSE; - if (!compile_for_eval && !NIL_P(p->ruby_sourcefile_string) && !e_option_supplied(p)) { + if (!compile_for_eval && p->ruby_sourcefile_string && !e_option_supplied(p)) { cov = TRUE; } @@ -7730,7 +7695,6 @@ yycompile0(VALUE arg) n = yyparse(p); RUBY_DTRACE_PARSE_HOOK(END); - rb_parser_aset_script_lines_for(p->ruby_sourcefile_string, p->debug_lines); p->debug_lines = 0; xfree(p->lex.strterm); @@ -7764,21 +7728,43 @@ yycompile0(VALUE arg) } } p->ast->body.root = tree; - if (!p->ast->body.script_lines) p->ast->body.script_lines = (rb_parser_ary_t *)INT2FIX(p->line_count); + p->ast->body.line_count = p->line_count; return TRUE; } +static void +set_arg_error(struct parser_params *p, const char *err) +{ + VALUE excargs[3]; + + excargs[0] = rb_eArgError; + excargs[1] = rb_str_new_cstr(err); + excargs[2] = rb_make_backtrace(); + rb_set_errinfo(rb_make_exception(3, excargs)); +} + static rb_ast_t * -yycompile(struct parser_params *p, VALUE fname, int line) +yycompile(struct parser_params *p, const char *fname_ptr, long fname_len, rb_encoding *fname_enc, int line) { rb_ast_t *ast; - if (NIL_P(fname)) { - p->ruby_sourcefile_string = Qnil; + if (!fname_ptr) { + p->ruby_sourcefile_string = NULL; p->ruby_sourcefile = "(none)"; } else { - p->ruby_sourcefile_string = rb_str_to_interned_str(fname); - p->ruby_sourcefile = StringValueCStr(fname); + int w; + if (cstr_null_check(p, fname_ptr, fname_len, fname_enc, &w)) { + if (w) { + set_arg_error(p, "string contains null char"); + } + else { + set_arg_error(p, "string contains null byte"); + } + return rb_ast_new(); + } + + p->ruby_sourcefile_string = rb_parser_encoding_string_new(p, fname_ptr, fname_len, fname_enc); + p->ruby_sourcefile = fname_ptr; } p->ruby_sourceline = line - 1; @@ -7797,104 +7783,36 @@ yycompile(struct parser_params *p, VALUE fname, int line) #endif /* !RIPPER */ static rb_encoding * -must_be_ascii_compatible(struct parser_params *p, VALUE s) +must_be_ascii_compatible(struct parser_params *p, rb_parser_string_t *s) { - rb_encoding *enc = rb_enc_get(s); + rb_encoding *enc = rb_parser_str_get_encoding(s); if (!rb_enc_asciicompat(enc)) { rb_raise(rb_eArgError, "invalid source encoding"); } return enc; } -static VALUE -lex_get_str(struct parser_params *p, VALUE s) -{ - char *beg, *end, *start; - long len; - - beg = RSTRING_PTR(s); - len = RSTRING_LEN(s); - start = beg; - if (p->lex.gets_.ptr) { - if (len == p->lex.gets_.ptr) return Qnil; - beg += p->lex.gets_.ptr; - len -= p->lex.gets_.ptr; - } - end = memchr(beg, '\n', len); - if (end) len = ++end - beg; - p->lex.gets_.ptr += len; - return rb_str_subseq(s, beg - start, len); -} - static rb_parser_string_t * lex_getline(struct parser_params *p) { - rb_parser_string_t *str; - VALUE line = (*p->lex.gets)(p, p->lex.input); - if (NIL_P(line)) return 0; - must_be_ascii_compatible(p, line); + rb_parser_string_t *line = (*p->lex.gets)(p, p->lex.input, p->line_count); + if (!line) return 0; p->line_count++; - str = rb_str_to_parser_string(p, line); - string_buffer_append(p, str); - return str; + string_buffer_append(p, line); + must_be_ascii_compatible(p, line); + return line; } #ifndef RIPPER -static rb_ast_t* -parser_compile_string(rb_parser_t *p, VALUE fname, VALUE s, int line) -{ - p->lex.gets = lex_get_str; - p->lex.gets_.ptr = 0; - p->lex.input = rb_str_new_frozen(s); - p->lex.pbeg = p->lex.pcur = p->lex.pend = 0; - - return yycompile(p, fname, line); -} - -rb_ast_t* -rb_ruby_parser_compile_string_path(rb_parser_t *p, VALUE f, VALUE s, int line) -{ - must_be_ascii_compatible(p, s); - return parser_compile_string(p, f, s, line); -} - -rb_ast_t* -rb_ruby_parser_compile_string(rb_parser_t *p, const char *f, VALUE s, int line) -{ - return rb_ruby_parser_compile_string_path(p, rb_filesystem_str_new_cstr(f), s, line); -} - -static VALUE -lex_io_gets(struct parser_params *p, VALUE io) -{ - return rb_io_gets_internal(io); -} - -rb_ast_t* -rb_ruby_parser_compile_file_path(rb_parser_t *p, VALUE fname, VALUE file, int start) -{ - p->lex.gets = lex_io_gets; - p->lex.input = file; - p->lex.pbeg = p->lex.pcur = p->lex.pend = 0; - - return yycompile(p, fname, start); -} - -static VALUE -lex_generic_gets(struct parser_params *p, VALUE input) -{ - return (*p->lex.gets_.call)(input, p->line_count); -} - rb_ast_t* -rb_ruby_parser_compile_generic(rb_parser_t *p, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int start) +rb_parser_compile(rb_parser_t *p, rb_parser_lex_gets_func *gets, + const char *fname_ptr, long fname_len, rb_encoding *fname_enc, rb_parser_input_data input, int line) { - p->lex.gets = lex_generic_gets; - p->lex.gets_.call = lex_gets; + p->lex.gets = gets; p->lex.input = input; p->lex.pbeg = p->lex.pcur = p->lex.pend = 0; - return yycompile(p, fname, start); + return yycompile(p, fname_ptr, fname_len, fname_enc, line); } #endif /* !RIPPER */ @@ -8660,6 +8578,10 @@ parser_update_heredoc_indent(struct parser_params *p, int c) } p->heredoc_line_indent = -1; } + else { + /* Whitespace only line has no indentation */ + p->heredoc_line_indent = 0; + } } return FALSE; } @@ -9689,7 +9611,7 @@ parser_set_encode(struct parser_params *p, const char *name) error: excargs[0] = rb_eArgError; excargs[2] = rb_make_backtrace(); - rb_ary_unshift(excargs[2], rb_sprintf("%"PRIsVALUE":%d", p->ruby_sourcefile_string, p->ruby_sourceline)); + rb_ary_unshift(excargs[2], rb_sprintf("%"PRIsVALUE":%d", rb_str_new_mutable_parser_string(p->ruby_sourcefile_string), p->ruby_sourceline)); VALUE exc = rb_make_exception(3, excargs); ruby_show_error_line(p, exc, &(YYLTYPE)RUBY_INIT_YYLLOC(), p->ruby_sourceline, p->lex.lastline); rb_exc_raise(exc); @@ -12666,10 +12588,10 @@ rb_node_line_new(struct parser_params *p, const YYLTYPE *loc) } static rb_node_file_t * -rb_node_file_new(struct parser_params *p, VALUE str, const YYLTYPE *loc) +rb_node_file_new(struct parser_params *p, rb_parser_string_t *str, const YYLTYPE *loc) { rb_node_file_t *n = NODE_NEWNODE(NODE_FILE, rb_node_file_t, loc); - n->path = rb_str_to_parser_string(p, str); + n->path = str; return n; } @@ -12908,17 +12830,16 @@ literal_concat0(struct parser_params *p, rb_parser_string_t *head, rb_parser_str static rb_parser_string_t * string_literal_head(struct parser_params *p, enum node_type htype, NODE *head) { - if (htype != NODE_DSTR) return false; + if (htype != NODE_DSTR) return NULL; if (RNODE_DSTR(head)->nd_next) { head = RNODE_LIST(RNODE_LIST(RNODE_DSTR(head)->nd_next)->as.nd_end)->nd_head; - if (!head || !nd_type_p(head, NODE_STR)) return false; + if (!head || !nd_type_p(head, NODE_STR)) return NULL; } rb_parser_string_t *lit = RNODE_DSTR(head)->string; - ASSUME(lit != false); + ASSUME(lit); return lit; } -#ifndef RIPPER static rb_parser_string_t * rb_parser_string_deep_copy(struct parser_params *p, const rb_parser_string_t *orig) { @@ -12929,7 +12850,6 @@ rb_parser_string_deep_copy(struct parser_params *p, const rb_parser_string_t *or copy->enc = orig->enc; return copy; } -#endif /* concat two string literals */ static NODE * @@ -13263,9 +13183,13 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc) return NEW_FALSE(loc); case keyword__FILE__: { - VALUE file = p->ruby_sourcefile_string; - if (NIL_P(file)) - file = rb_str_new(0, 0); + rb_parser_string_t *file; + if (p->ruby_sourcefile_string) { + file = rb_parser_string_deep_copy(p, p->ruby_sourcefile_string); + } + else { + file = STRING_NEW0(); + } node = NEW_FILE(file, loc); } return node; @@ -15848,7 +15772,7 @@ parser_initialize(struct parser_params *p) { /* note: we rely on TypedData_Make_Struct to set most fields to 0 */ p->command_start = TRUE; - p->ruby_sourcefile_string = Qnil; + p->ruby_sourcefile_string = NULL; p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE at first */ string_buffer_init(p); p->node_id = 0; @@ -15883,9 +15807,6 @@ rb_ruby_parser_mark(void *ptr) { struct parser_params *p = (struct parser_params*)ptr; - rb_gc_mark(p->lex.input); - rb_gc_mark(p->ruby_sourcefile_string); - rb_gc_mark((VALUE)p->ast); #ifndef RIPPER rb_gc_mark(p->error_buffer); #else @@ -15916,6 +15837,10 @@ rb_ruby_parser_free(void *ptr) ruby_sized_xfree(p->tokenbuf, p->toksiz); } + if (p->ruby_sourcefile_string) { + rb_parser_string_free(p, p->ruby_sourcefile_string); + } + for (local = p->lvtbl; local; local = prev) { prev = local->prev; local_free(p, local); @@ -15956,20 +15881,6 @@ rb_ruby_parser_memsize(const void *ptr) return size; } -#ifndef UNIVERSAL_PARSER -#ifndef RIPPER -static const rb_data_type_t parser_data_type = { - "parser", - { - rb_ruby_parser_mark, - rb_ruby_parser_free, - rb_ruby_parser_memsize, - }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; -#endif -#endif - #ifndef RIPPER #undef rb_reserved_word @@ -15997,6 +15908,23 @@ rb_ruby_parser_new(const rb_parser_config_t *config) parser_initialize(p); return p; } +#else +rb_parser_t * +rb_ruby_parser_allocate(void) +{ + /* parser_initialize expects fields to be set to 0 */ + rb_parser_t *p = (rb_parser_t *)ruby_xcalloc(1, sizeof(rb_parser_t)); + return p; +} + +rb_parser_t * +rb_ruby_parser_new(void) +{ + /* parser_initialize expects fields to be set to 0 */ + rb_parser_t *p = rb_ruby_parser_allocate(); + parser_initialize(p); + return p; +} #endif rb_parser_t * @@ -16026,152 +15954,10 @@ rb_ruby_parser_keep_tokens(rb_parser_t *p) p->tokens = rb_parser_ary_new_capa_for_ast_token(p, 10); } -#ifndef UNIVERSAL_PARSER -rb_ast_t* -rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE file, int start) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - RB_GC_GUARD(vparser); /* prohibit tail call optimization */ - return rb_ruby_parser_compile_file_path(p, fname, file, start); -} - -rb_ast_t* -rb_parser_compile_generic(VALUE vparser, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int start) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - RB_GC_GUARD(vparser); /* prohibit tail call optimization */ - return rb_ruby_parser_compile_generic(p, lex_gets, fname, input, start); -} - -rb_ast_t* -rb_parser_compile_string(VALUE vparser, const char *f, VALUE s, int line) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - RB_GC_GUARD(vparser); /* prohibit tail call optimization */ - return rb_ruby_parser_compile_string(p, f, s, line); -} - -rb_ast_t* -rb_parser_compile_string_path(VALUE vparser, VALUE f, VALUE s, int line) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - RB_GC_GUARD(vparser); /* prohibit tail call optimization */ - return rb_ruby_parser_compile_string_path(p, f, s, line); -} - -VALUE -rb_parser_encoding(VALUE vparser) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - return rb_ruby_parser_encoding(p); -} - -VALUE -rb_parser_end_seen_p(VALUE vparser) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - return RBOOL(rb_ruby_parser_end_seen_p(p)); -} - -void -rb_parser_error_tolerant(VALUE vparser) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - rb_ruby_parser_error_tolerant(p); -} - -void -rb_parser_set_script_lines(VALUE vparser) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - rb_ruby_parser_set_script_lines(p); -} - -void -rb_parser_keep_tokens(VALUE vparser) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - rb_ruby_parser_keep_tokens(p); -} - -VALUE -rb_parser_new(void) -{ - struct parser_params *p; - VALUE parser = TypedData_Make_Struct(0, struct parser_params, - &parser_data_type, p); - parser_initialize(p); - return parser; -} - -VALUE -rb_parser_set_context(VALUE vparser, const struct rb_iseq_struct *base, int main) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - rb_ruby_parser_set_context(p, base, main); - return vparser; -} - -void -rb_parser_set_options(VALUE vparser, int print, int loop, int chomp, int split) -{ - struct parser_params *p; - - TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p); - rb_ruby_parser_set_options(p, print, loop, chomp, split); -} - -VALUE -rb_parser_set_yydebug(VALUE self, VALUE flag) -{ - struct parser_params *p; - - TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p); - rb_ruby_parser_set_yydebug(p, RTEST(flag)); - return flag; -} - -void -rb_set_script_lines_for(VALUE self, VALUE path) -{ - struct parser_params *p; - VALUE hash; - ID script_lines; - CONST_ID(script_lines, "SCRIPT_LINES__"); - if (!rb_const_defined_at(rb_cObject, script_lines)) return; - hash = rb_const_get_at(rb_cObject, script_lines); - if (RB_TYPE_P(hash, T_HASH)) { - rb_hash_aset(hash, path, Qtrue); - TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p); - rb_ruby_parser_set_script_lines(p); - } -} -#endif /* !UNIVERSAL_PARSER */ - -VALUE +rb_encoding * rb_ruby_parser_encoding(rb_parser_t *p) { - return rb_enc_from_encoding(p->enc); + return p->enc; } int @@ -16232,12 +16018,12 @@ rb_ruby_parser_set_parsing_thread(rb_parser_t *p, VALUE parsing_thread) } void -rb_ruby_parser_ripper_initialize(rb_parser_t *p, VALUE (*gets)(struct parser_params*,VALUE), VALUE input, VALUE sourcefile_string, const char *sourcefile, int sourceline) +rb_ruby_parser_ripper_initialize(rb_parser_t *p, rb_parser_lex_gets_func *gets, rb_parser_input_data input, VALUE sourcefile_string, const char *sourcefile, int sourceline) { p->lex.gets = gets; p->lex.input = input; p->eofp = 0; - p->ruby_sourcefile_string = sourcefile_string; + p->ruby_sourcefile_string = rb_str_to_parser_string(p, sourcefile_string); p->ruby_sourcefile = sourcefile; p->ruby_sourceline = sourceline; } @@ -16257,7 +16043,7 @@ rb_ruby_parser_enc(rb_parser_t *p) VALUE rb_ruby_parser_ruby_sourcefile_string(rb_parser_t *p) { - return p->ruby_sourcefile_string; + return rb_str_new_parser_string(p->ruby_sourcefile_string); } int @@ -16278,7 +16064,7 @@ rb_ruby_ripper_parse0(rb_parser_t *p) parser_prepare(p); p->ast = rb_ast_new(); ripper_yyparse((void*)p); - rb_ast_dispose(p->ast); + rb_ast_free(p->ast); p->ast = 0; p->eval_tree = 0; p->eval_tree_begin = 0; @@ -16304,12 +16090,6 @@ rb_ruby_ripper_dedent_string(rb_parser_t *p, VALUE string, int width) return i; } -VALUE -rb_ruby_ripper_lex_get_str(rb_parser_t *p, VALUE s) -{ - return lex_get_str(p, s); -} - int rb_ruby_ripper_initialized_p(rb_parser_t *p) { @@ -16400,7 +16180,7 @@ parser_compile_error(struct parser_params *p, const rb_code_location_t *loc, con va_start(ap, fmt); p->error_buffer = rb_syntax_error_append(p->error_buffer, - p->ruby_sourcefile_string, + p->ruby_sourcefile_string ? rb_str_new_parser_string(p->ruby_sourcefile_string) : Qnil, lineno, column, p->enc, fmt, ap); va_end(ap); diff --git a/prism/config.yml b/prism/config.yml index 8551c95b1f..0652cc4a9f 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -7,13 +7,16 @@ errors: - ARGUMENT_BARE_HASH - ARGUMENT_BLOCK_FORWARDING - ARGUMENT_BLOCK_MULTI + - ARGUMENT_CONFLICT_AMPERSAND + - ARGUMENT_CONFLICT_STAR + - ARGUMENT_CONFLICT_STAR_STAR - ARGUMENT_FORMAL_CLASS - ARGUMENT_FORMAL_CONSTANT - ARGUMENT_FORMAL_GLOBAL - ARGUMENT_FORMAL_IVAR - ARGUMENT_FORWARDING_UNBOUND - ARGUMENT_IN - - ARGUMENT_NO_FORWARDING_AMP + - ARGUMENT_NO_FORWARDING_AMPERSAND - ARGUMENT_NO_FORWARDING_ELLIPSES - ARGUMENT_NO_FORWARDING_STAR - ARGUMENT_NO_FORWARDING_STAR_STAR @@ -95,6 +98,7 @@ errors: - EXPECT_EXPRESSION_AFTER_SPLAT_HASH - EXPECT_EXPRESSION_AFTER_STAR - EXPECT_IDENT_REQ_PARAMETER + - EXPECT_IN_DELIMITER - EXPECT_LPAREN_REQ_PARAMETER - EXPECT_MESSAGE - EXPECT_RBRACKET @@ -123,32 +127,40 @@ errors: - HASH_ROCKET - HASH_TERM - HASH_VALUE + - HEREDOC_IDENTIFIER - HEREDOC_TERM - INCOMPLETE_QUESTION_MARK - INCOMPLETE_VARIABLE_CLASS - - INCOMPLETE_VARIABLE_CLASS_3_3_0 + - INCOMPLETE_VARIABLE_CLASS_3_3 - INCOMPLETE_VARIABLE_INSTANCE - - INCOMPLETE_VARIABLE_INSTANCE_3_3_0 + - INCOMPLETE_VARIABLE_INSTANCE_3_3 - INSTANCE_VARIABLE_BARE - INVALID_BLOCK_EXIT - INVALID_CHARACTER - INVALID_ENCODING_MAGIC_COMMENT + - INVALID_ESCAPE_CHARACTER - INVALID_FLOAT_EXPONENT + - INVALID_LOCAL_VARIABLE_READ + - INVALID_LOCAL_VARIABLE_WRITE - INVALID_MULTIBYTE_CHAR - INVALID_MULTIBYTE_CHARACTER - INVALID_MULTIBYTE_ESCAPE - INVALID_NUMBER_BINARY - INVALID_NUMBER_DECIMAL + - INVALID_NUMBER_FRACTION - INVALID_NUMBER_HEXADECIMAL - INVALID_NUMBER_OCTAL - - INVALID_NUMBER_UNDERSCORE + - INVALID_NUMBER_UNDERSCORE_INNER + - INVALID_NUMBER_UNDERSCORE_TRAILING - INVALID_PERCENT + - INVALID_PERCENT_EOF - INVALID_PRINTABLE_CHARACTER - INVALID_RETRY_AFTER_ELSE - INVALID_RETRY_AFTER_ENSURE - INVALID_RETRY_WITHOUT_RESCUE + - INVALID_SYMBOL - INVALID_VARIABLE_GLOBAL - - INVALID_VARIABLE_GLOBAL_3_3_0 + - INVALID_VARIABLE_GLOBAL_3_3 - INVALID_YIELD - IT_NOT_ALLOWED_NUMBERED - IT_NOT_ALLOWED_ORDINARY @@ -182,6 +194,7 @@ errors: - PARAMETER_ASSOC_SPLAT_MULTI - PARAMETER_BLOCK_MULTI - PARAMETER_CIRCULAR + - PARAMETER_FORWARDING_AFTER_REST - PARAMETER_METHOD_NAME - PARAMETER_NAME_DUPLICATED - PARAMETER_NO_DEFAULT @@ -192,6 +205,7 @@ errors: - PARAMETER_STAR - PARAMETER_UNEXPECTED_FWD - PARAMETER_WILD_LOOSE_COMMA + - PARAMETER_UNEXPECTED_NO_KW - PATTERN_CAPTURE_DUPLICATE - PATTERN_EXPRESSION_AFTER_BRACKET - PATTERN_EXPRESSION_AFTER_COMMA @@ -203,9 +217,12 @@ errors: - PATTERN_EXPRESSION_AFTER_PIPE - PATTERN_EXPRESSION_AFTER_RANGE - PATTERN_EXPRESSION_AFTER_REST + - PATTERN_HASH_IMPLICIT - PATTERN_HASH_KEY - PATTERN_HASH_KEY_DUPLICATE + - PATTERN_HASH_KEY_INTERPOLATED - PATTERN_HASH_KEY_LABEL + - PATTERN_HASH_KEY_LOCALS - PATTERN_IDENT_AFTER_HROCKET - PATTERN_LABEL_AFTER_COMMA - PATTERN_REST @@ -244,6 +261,9 @@ errors: - UNARY_RECEIVER - UNDEF_ARGUMENT - UNEXPECTED_BLOCK_ARGUMENT + - UNEXPECTED_INDEX_BLOCK + - UNEXPECTED_INDEX_KEYWORDS + - UNEXPECTED_SAFE_NAVIGATION - UNEXPECTED_TOKEN_CLOSE_CONTEXT - UNEXPECTED_TOKEN_IGNORE - UNTIL_TERM @@ -263,7 +283,7 @@ warnings: - COMPARISON_AFTER_COMPARISON - DOT_DOT_DOT_EOL - EQUAL_IN_CONDITIONAL - - EQUAL_IN_CONDITIONAL_3_3_0 + - EQUAL_IN_CONDITIONAL_3_3 - END_IN_METHOD - DUPLICATED_HASH_KEY - DUPLICATED_WHEN_CLAUSE @@ -276,6 +296,7 @@ warnings: - KEYWORD_EOL - LITERAL_IN_CONDITION_DEFAULT - LITERAL_IN_CONDITION_VERBOSE + - SHAREABLE_CONSTANT_VALUE_LINE - SHEBANG_CARRIAGE_RETURN - UNEXPECTED_CARRIAGE_RETURN - UNREACHABLE_STATEMENT @@ -614,6 +635,8 @@ tokens: flags: - name: ArgumentsNodeFlags values: + - name: CONTAINS_KEYWORDS + comment: "if arguments contain keywords" - name: CONTAINS_KEYWORD_SPLAT comment: "if arguments contain keyword splat" comment: Flags for arguments nodes. @@ -703,6 +726,11 @@ flags: - name: FORCED_US_ASCII_ENCODING comment: "internal bytes forced the encoding to US-ASCII" comment: Flags for regular expression and match last line nodes. + - name: ReturnNodeFlags + values: + - name: REDUNDANT + comment: "a return statement that is redundant because it is the last statement in a method" + comment: Flags for return nodes. - name: ShareableConstantNodeFlags values: - name: LITERAL @@ -737,10 +765,25 @@ nodes: fields: - name: new_name type: node + comment: | + Represents the new name of the global variable that can be used after aliasing. This can be either a global variable, a back reference, or a numbered reference. + + alias $foo $bar + ^^^^ - name: old_name type: node + comment: | + Represents the old name of the global variable that could be used before aliasing. This can be either a global variable, a back reference, or a numbered reference. + + alias $foo $bar + ^^^^ - name: keyword_loc type: location + comment: | + The location of the `alias` keyword. + + alias $foo $bar + ^^^^^ comment: | Represents the use of the `alias` keyword to alias a global variable. @@ -825,10 +868,25 @@ nodes: kind: ArrayNodeFlags - name: elements type: node[] + comment: Represent the list of zero or more [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression) within the array. - name: opening_loc type: location? + comment: | + Represents the optional source location for the opening token. + + [1,2,3] # "[" + %w[foo bar baz] # "%w[" + %I(apple orange banana) # "%I(" + foo = 1, 2, 3 # nil - name: closing_loc type: location? + comment: | + Represents the optional source location for the closing token. + + [1,2,3] # "]" + %w[foo bar baz] # "]" + %I(apple orange banana) # ")" + foo = 1, 2, 3 # nil comment: | Represents an array literal. This can be a regular array using brackets or a special array using % like %w or %i. @@ -1047,8 +1105,18 @@ nodes: - name: arguments type: node? kind: ArgumentsNode + comment: | + The arguments to the break statement, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + break foo + ^^^ - name: keyword_loc type: location + comment: | + The location of the `break` keyword. + + break foo + ^^^^^ comment: | Represents the use of the `break` keyword. @@ -1146,9 +1214,9 @@ nodes: type: constant - name: write_name type: constant - - name: operator + - name: binary_operator type: constant - - name: operator_loc + - name: binary_operator_loc type: location - name: value type: node @@ -1304,11 +1372,11 @@ nodes: type: constant - name: name_loc type: location - - name: operator_loc + - name: binary_operator_loc type: location - name: value type: node - - name: operator + - name: binary_operator type: constant comment: | Represents assigning to a class variable using an operator that isn't `=`. @@ -1374,8 +1442,7 @@ nodes: - name: value type: node comment: | - The value to assign to the class variable. Can be any node that - represents a non-void expression. + The value to write to the class variable. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). @@foo = :bar ^^^^ @@ -1415,11 +1482,11 @@ nodes: type: constant - name: name_loc type: location - - name: operator_loc + - name: binary_operator_loc type: location - name: value type: node - - name: operator + - name: binary_operator type: constant comment: | Represents assigning to a constant using an operator that isn't `=`. @@ -1459,13 +1526,40 @@ nodes: fields: - name: parent type: node? - - name: child - type: node - kind: - - ConstantReadNode - - MissingNode + comment: | + The left-hand node of the path, if present. It can be `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). It will be `nil` when the constant lookup is at the root of the module tree. + + Foo::Bar + ^^^ + + self::Test + ^^^^ + + a.b::C + ^^^ + - name: name + type: constant? + comment: The name of the constant being accessed. This could be `nil` in the event of a syntax error. - name: delimiter_loc type: location + comment: | + The location of the `::` delimiter. + + ::Foo + ^^ + + One::Two + ^^ + - name: name_loc + type: location + comment: | + The location of the name of the constant. + + ::Foo + ^^^ + + One::Two + ^^^ comment: | Represents accessing a constant through a path of `::` operators. @@ -1476,11 +1570,11 @@ nodes: - name: target type: node kind: ConstantPathNode - - name: operator_loc + - name: binary_operator_loc type: location - name: value type: node - - name: operator + - name: binary_operator type: constant comment: | Represents assigning to a constant path using an operator that isn't `=`. @@ -1505,13 +1599,12 @@ nodes: fields: - name: parent type: node? - - name: child - type: node - kind: - - ConstantReadNode - - MissingNode + - name: name + type: constant? - name: delimiter_loc type: location + - name: name_loc + type: location comment: | Represents writing to a constant path in a context that doesn't have an explicit value. @@ -1522,10 +1615,28 @@ nodes: - name: target type: node kind: ConstantPathNode + comment: | + A node representing the constant path being written to. + + Foo::Bar = 1 + ^^^^^^^^ + + ::Foo = :abc + ^^^^^ - name: operator_loc type: location + comment: | + The location of the `=` operator. + + ::ABC = 123 + ^ - name: value type: node + comment: | + The value to write to the constant path. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + FOO::BAR = :abc + ^^^^ comment: | Represents writing to a constant path. @@ -1565,12 +1676,36 @@ nodes: fields: - name: name type: constant + comment: | + The name of the [constant](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#constants). + + Foo = :bar # name `:Foo` + + XYZ = 1 # name `:XYZ` - name: name_loc type: location + comment: | + The location of the constant name. + + FOO = 1 + ^^^ - name: value type: node + comment: | + The value to write to the constant. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + FOO = :bar + ^^^^ + + MyClass = Class.new + ^^^^^^^^^ - name: operator_loc type: location + comment: | + The location of the `=` operator. + + FOO = :bar + ^ comment: | Represents writing to a constant. @@ -1806,11 +1941,11 @@ nodes: type: constant - name: name_loc type: location - - name: operator_loc + - name: binary_operator_loc type: location - name: value type: node - - name: operator + - name: binary_operator type: constant comment: | Represents assigning to a global variable using an operator that isn't `=`. @@ -1860,12 +1995,36 @@ nodes: fields: - name: name type: constant + comment: | + The name of the global variable, which is a `$` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifier). Alternatively, it can be one of the special global variables designated by a symbol. + + $foo = :bar # name `:$foo` + + $_Test = 123 # name `:$_Test` - name: name_loc type: location + comment: | + The location of the global variable's name. + + $foo = :bar + ^^^^ - name: value type: node + comment: | + The value to write to the global variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + $foo = :bar + ^^^^ + + $-xyz = 123 + ^^^ - name: operator_loc type: location + comment: | + The location of the `=` operator. + + $foo = :bar + ^ comment: | Represents writing to a global variable. @@ -1933,26 +2092,87 @@ nodes: fields: - name: if_keyword_loc type: location? + comment: | + The location of the `if` keyword if present. + + bar if foo + ^^ + + The `if_keyword_loc` field will be `nil` when the `IfNode` represents a ternary expression. - name: predicate type: node + comment: | + The node for the condition the `IfNode` is testing. + + if foo + ^^^ + bar + end + + bar if foo + ^^^ + + foo ? bar : baz + ^^^ - name: then_keyword_loc type: location? + comment: | + The location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. + + if foo then bar end + ^^^^ + + a ? b : c + ^ - name: statements type: node? kind: StatementsNode + comment: | + Represents the body of statements that will be executed when the predicate is evaluated as truthy. Will be `nil` when no body is provided. + + if foo + bar + ^^^ + baz + ^^^ + end - name: consequent type: node? + comment: | + Represents an `ElseNode` or an `IfNode` when there is an `else` or an `elsif` in the `if` statement. + + if foo + bar + elsif baz + ^^^^^^^^^ + qux + ^^^ + end + ^^^ + + if foo then bar else baz end + ^^^^^^^^^^^^ - name: end_keyword_loc type: location? + comment: | + The location of the `end` keyword if present, `nil` otherwise. + + if foo + bar + end + ^^^ newline: predicate comment: | - Represents the use of the `if` keyword, either in the block form or the modifier form. + Represents the use of the `if` keyword, either in the block form or the modifier form, or a ternary expression. bar if foo ^^^^^^^^^^ if foo then bar end ^^^^^^^^^^^^^^^^^^^ + + foo ? bar : baz + ^^^^^^^^^^^^^^^ - name: ImaginaryNode fields: - name: numeric @@ -2057,9 +2277,9 @@ nodes: type: location - name: block type: node? - - name: operator + - name: binary_operator type: constant - - name: operator_loc + - name: binary_operator_loc type: location - name: value type: node @@ -2145,11 +2365,11 @@ nodes: type: constant - name: name_loc type: location - - name: operator_loc + - name: binary_operator_loc type: location - name: value type: node - - name: operator + - name: binary_operator type: constant comment: | Represents assigning to an instance variable using an operator that isn't `=`. @@ -2215,8 +2435,7 @@ nodes: - name: value type: node comment: | - The value to assign to the instance variable. Can be any node that - represents a non-void expression. + The value to write to the instance variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). @foo = :bar ^^^^ @@ -2426,13 +2645,13 @@ nodes: fields: - name: name_loc type: location - - name: operator_loc + - name: binary_operator_loc type: location - name: value type: node - name: name type: constant - - name: operator + - name: binary_operator type: constant - name: depth type: uint32 @@ -2507,14 +2726,50 @@ nodes: fields: - name: name type: constant + comment: | + The name of the local variable, which is an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + + foo = :bar # name `:foo` + + abc = 123 # name `:abc` - name: depth type: uint32 + comment: | + The number of semantic scopes we have to traverse to find the declaration of this variable. + + foo = 1 # depth 0 + + tap { foo = 1 } # depth 1 + + The specific rules for calculating the depth may differ from individual Ruby implementations, as they are not specified by the language. For more information, see [the Prism documentation](https://github.com/ruby/prism/blob/main/docs/local_variable_depth.md). - name: name_loc type: location + comment: | + The location of the variable name. + + foo = :bar + ^^^ - name: value type: node + comment: | + The value to write to the local variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + foo = :bar + ^^^^ + + abc = 1234 + ^^^^ + + Note that since the name of a local variable is known before the value is parsed, it is valid for a local variable to appear within the value of its own write. + + foo = foo - name: operator_loc type: location + comment: | + The location of the `=` operator. + + x = :y + ^ comment: | Represents writing to a local variable. @@ -2831,6 +3086,8 @@ nodes: # On parsing error of `f(**kwargs, ...)` or `f(**nil, ...)`, the keyword_rest value is moved here: - KeywordRestParameterNode - NoKeywordsParameterNode + # On parsing error of `f(..., ...)`, the first forwarding parameter is moved here: + - ForwardingParameterNode - name: keywords type: node[] kind: @@ -3095,6 +3352,9 @@ nodes: ^^^^^ - name: ReturnNode fields: + - name: flags + type: flags + kind: ReturnNodeFlags - name: keyword_loc type: location - name: arguments @@ -3166,6 +3426,7 @@ nodes: kind: StringFlags - name: filepath type: string + comment: Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism::parse*` APIs. comment: | Represents the use of the `__FILE__` keyword. @@ -3287,18 +3548,55 @@ nodes: fields: - name: keyword_loc type: location + comment: | + The location of the `unless` keyword. + + unless cond then bar end + ^^^^^^ + + bar unless cond + ^^^^^^ - name: predicate type: node + comment: | + The condition to be evaluated for the unless expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + unless cond then bar end + ^^^^ + + bar unless cond + ^^^^ - name: then_keyword_loc type: location? + comment: + The location of the `then` keyword, if present. + + unless cond then bar end + ^^^^ - name: statements type: node? kind: StatementsNode + comment: | + The body of statements that will executed if the unless condition is + falsey. Will be `nil` if no body is provided. + + unless cond then bar end + ^^^ - name: consequent type: node? kind: ElseNode + comment: | + The else clause of the unless expression, if present. + + unless cond then bar else baz end + ^^^^^^^^ - name: end_keyword_loc type: location? + comment: | + The location of the `end` keyword, if present. + + unless cond then bar end + ^^^ newline: predicate comment: | Represents the use of the `unless` keyword, either in the block form or the modifier form. diff --git a/prism/extension.c b/prism/extension.c index 807c8f69dc..84872914c4 100644 --- a/prism/extension.c +++ b/prism/extension.c @@ -19,7 +19,9 @@ VALUE rb_cPrismEmbDocComment; VALUE rb_cPrismMagicComment; VALUE rb_cPrismParseError; VALUE rb_cPrismParseWarning; +VALUE rb_cPrismResult; VALUE rb_cPrismParseResult; +VALUE rb_cPrismParseLexResult; VALUE rb_cPrismDebugEncoding; @@ -30,6 +32,7 @@ ID rb_option_id_frozen_string_literal; ID rb_option_id_line; ID rb_option_id_scopes; ID rb_option_id_version; +ID rb_prism_source_id_for; /******************************************************************************/ /* IO of Ruby code */ @@ -515,7 +518,7 @@ parser_warnings(pm_parser_t *parser, rb_encoding *encoding, VALUE source) { * Create a new parse result from the given parser, value, encoding, and source. */ static VALUE -parse_result_create(pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source) { +parse_result_create(VALUE class, pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source) { VALUE result_argv[] = { value, parser_comments(parser, source), @@ -526,7 +529,7 @@ parse_result_create(pm_parser_t *parser, VALUE value, rb_encoding *encoding, VAL source }; - return rb_class_new_instance(7, result_argv, rb_cPrismParseResult); + return rb_class_new_instance(7, result_argv, class); } /******************************************************************************/ @@ -597,8 +600,7 @@ parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nod VALUE source_string = rb_str_new((const char *) pm_string_source(input), pm_string_length(input)); VALUE offsets = rb_ary_new(); - VALUE source_argv[] = { source_string, LONG2NUM(parser.start_line), offsets }; - VALUE source = rb_class_new_instance(3, source_argv, rb_cPrismSource); + VALUE source = rb_funcall(rb_cPrismSource, rb_prism_source_id_for, 3, source_string, LONG2NUM(parser.start_line), offsets); parse_lex_data_t parse_lex_data = { .source = source, @@ -635,7 +637,7 @@ parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nod value = parse_lex_data.tokens; } - VALUE result = parse_result_create(&parser, value, parse_lex_data.encoding, source); + VALUE result = parse_result_create(rb_cPrismParseLexResult, &parser, value, parse_lex_data.encoding, source); pm_node_destroy(&parser, node); pm_parser_free(&parser); @@ -700,7 +702,7 @@ parse_input(pm_string_t *input, const pm_options_t *options) { VALUE source = pm_source_new(&parser, encoding); VALUE value = pm_ast_new(&parser, node, encoding, source); - VALUE result = parse_result_create(&parser, value, encoding, source) ; + VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source) ; pm_node_destroy(&parser, node); pm_parser_free(&parser); @@ -804,7 +806,7 @@ parse_stream(int argc, VALUE *argv, VALUE self) { VALUE source = pm_source_new(&parser, encoding); VALUE value = pm_ast_new(&parser, node, encoding, source); - VALUE result = parse_result_create(&parser, value, encoding, source); + VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source); pm_node_destroy(&parser, node); pm_buffer_free(&buffer); @@ -1241,7 +1243,7 @@ static_inspect(int argc, VALUE *argv, VALUE self) { pm_node_t *node = ((pm_program_node_t *) program)->statements->body.nodes[0]; pm_buffer_t buffer = { 0 }; - pm_static_literal_inspect(&buffer, &parser, node); + pm_static_literal_inspect(&buffer, &parser.newline_list, parser.start_line, parser.encoding->name, node); rb_encoding *encoding = rb_enc_find(parser.encoding->name); VALUE result = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), encoding); @@ -1362,7 +1364,10 @@ Init_prism(void) { rb_cPrismMagicComment = rb_define_class_under(rb_cPrism, "MagicComment", rb_cObject); rb_cPrismParseError = rb_define_class_under(rb_cPrism, "ParseError", rb_cObject); rb_cPrismParseWarning = rb_define_class_under(rb_cPrism, "ParseWarning", rb_cObject); - rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cObject); + + rb_cPrismResult = rb_define_class_under(rb_cPrism, "Result", rb_cObject); + rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cPrismResult); + rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult); // Intern all of the options that we support so that we don't have to do it // every time we parse. @@ -1374,6 +1379,8 @@ Init_prism(void) { rb_option_id_scopes = rb_intern_const("scopes"); rb_option_id_version = rb_intern_const("version"); + rb_prism_source_id_for = rb_intern("for"); + /** * The version of the prism library. */ diff --git a/prism/extension.h b/prism/extension.h index 59a3f0232a..b15a2c5e61 100644 --- a/prism/extension.h +++ b/prism/extension.h @@ -1,7 +1,7 @@ #ifndef PRISM_EXT_NODE_H #define PRISM_EXT_NODE_H -#define EXPECTED_PRISM_VERSION "0.25.0" +#define EXPECTED_PRISM_VERSION "0.29.0" #include <ruby.h> #include <ruby/encoding.h> diff --git a/prism/options.c b/prism/options.c index 4d0d6dbc49..664db4f061 100644 --- a/prism/options.c +++ b/prism/options.c @@ -58,8 +58,8 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length case 5: assert(version != NULL); - if (strncmp(version, "3.3.0", length) == 0) { - options->version = PM_OPTIONS_VERSION_CRUBY_3_3_0; + if ((strncmp(version, "3.3.0", length) == 0) || (strncmp(version, "3.3.1", length) == 0)) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3; return true; } diff --git a/prism/options.h b/prism/options.h index d0b46a0864..a623ae0b83 100644 --- a/prism/options.h +++ b/prism/options.h @@ -49,8 +49,8 @@ typedef enum { /** The current version of prism. */ PM_OPTIONS_VERSION_LATEST = 0, - /** The vendored version of prism in CRuby 3.3.0. */ - PM_OPTIONS_VERSION_CRUBY_3_3_0 = 1 + /** The vendored version of prism in CRuby 3.3.x. */ + PM_OPTIONS_VERSION_CRUBY_3_3 = 1 } pm_options_version_t; /** diff --git a/prism/parser.h b/prism/parser.h index b35026e6a2..8054e332f7 100644 --- a/prism/parser.h +++ b/prism/parser.h @@ -10,6 +10,7 @@ #include "prism/ast.h" #include "prism/encoding.h" #include "prism/options.h" +#include "prism/static_literals.h" #include "prism/util/pm_constant_pool.h" #include "prism/util/pm_list.h" #include "prism/util/pm_newline_list.h" @@ -718,6 +719,15 @@ struct pm_parser { pm_context_node_t *current_context; /** + * The hash keys for the hash that is currently being parsed. This is not + * usually necessary because it can pass it down the various call chains, + * but in the event that you're parsing a hash that is being directly + * pushed into another hash with **, we need to share the hash keys so that + * we can warn for the nested hash as well. + */ + pm_static_literals_t *current_hash_keys; + + /** * The encoding functions for the current file is attached to the parser as * it's parsing so that it can change with a magic comment. */ diff --git a/prism/prism.c b/prism/prism.c index 18675b994a..02c2088089 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -672,6 +672,26 @@ pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id #define PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, ...) \ PM_PARSER_WARN_FORMAT(parser, (node)->location.start, (node)->location.end, diag_id, __VA_ARGS__) +/** + * Add an error for an expected heredoc terminator. This is a special function + * only because it grabs its location off of a lex mode instead of a node or a + * token. + */ +static void +pm_parser_err_heredoc_term(pm_parser_t *parser, pm_lex_mode_t *lex_mode) { + const uint8_t *ident_start = lex_mode->as.heredoc.ident_start; + size_t ident_length = lex_mode->as.heredoc.ident_length; + + PM_PARSER_ERR_FORMAT( + parser, + ident_start, + ident_start + ident_length, + PM_ERR_HEREDOC_TERM, + (int) ident_length, + (const char *) ident_start + ); +} + /******************************************************************************/ /* Scope-related functions */ /******************************************************************************/ @@ -729,42 +749,97 @@ pm_parser_scope_find(pm_parser_t *parser, uint32_t depth) { return scope; } -static void -pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const pm_token_t * token, const uint8_t mask, pm_diagnostic_id_t diag) { +typedef enum { + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS, + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT, + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL +} pm_scope_forwarding_param_check_result_t; + +static pm_scope_forwarding_param_check_result_t +pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const uint8_t mask) { pm_scope_t *scope = parser->current_scope; - while (scope) { + bool conflict = false; + + while (scope != NULL) { if (scope->parameters & mask) { - if (!scope->closed) { - pm_parser_err_token(parser, token, diag); - return; + if (scope->closed) { + if (conflict) { + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT; + } else { + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS; + } } - return; + + conflict = true; } + if (scope->closed) break; scope = scope->previous; } - pm_parser_err_token(parser, token, diag); + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL; } -static inline void +static void pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) { - pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK, PM_ERR_ARGUMENT_NO_FORWARDING_AMP); + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_AMPERSAND); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND); + break; + } } -static inline void +static void pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) { - pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); + break; + } } -static inline void -pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t * token) { - pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_ALL, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); +static void +pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t *token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_ALL)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + // This shouldn't happen, because ... is not allowed in the + // declaration of blocks. If we get here, we assume we already have + // an error for this. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); + break; + } } -static inline void +static void pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) { - pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR); + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR_STAR); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR); + break; + } } /** @@ -1405,7 +1480,7 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) { static inline void pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) { if (pm_conditional_predicate_warn_write_literal_p(node)) { - pm_parser_warn_node(parser, node, parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3_0 : PM_WARN_EQUAL_IN_CONDITIONAL); + pm_parser_warn_node(parser, node, parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL); } } @@ -1555,7 +1630,7 @@ not_provided(pm_parser_t *parser) { return (pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }; } -#define PM_LOCATION_NULL_VALUE(parser) ((pm_location_t) { .start = parser->start, .end = parser->start }) +#define PM_LOCATION_NULL_VALUE(parser) ((pm_location_t) { .start = (parser)->start, .end = (parser)->start }) #define PM_LOCATION_TOKEN_VALUE(token) ((pm_location_t) { .start = (token)->start, .end = (token)->end }) #define PM_LOCATION_NODE_VALUE(node) ((pm_location_t) { .start = (node)->location.start, .end = (node)->location.end }) #define PM_LOCATION_NODE_BASE_VALUE(node) ((pm_location_t) { .start = (node)->base.location.start, .end = (node)->base.location.end }) @@ -1683,7 +1758,7 @@ char_is_identifier_utf8(const uint8_t *b, const uint8_t *end) { * it's important that it be as fast as possible. */ static inline size_t -char_is_identifier(pm_parser_t *parser, const uint8_t *b) { +char_is_identifier(const pm_parser_t *parser, const uint8_t *b) { if (parser->encoding_changed) { size_t width; if ((width = parser->encoding->alnum_char(b, parser->end - b)) != 0) { @@ -2752,8 +2827,7 @@ static pm_call_node_t * pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) { pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY); - node->base.location.start = parser->start; - node->base.location.end = parser->start; + node->base.location = PM_LOCATION_NULL_VALUE(parser); node->arguments = arguments; node->name = name; @@ -2924,6 +2998,29 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const } /** + * Validate that index expressions do not have keywords or blocks if we are + * parsing as Ruby 3.4+. + */ +static void +pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) { + if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) { + if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) { + pm_node_t *node; + PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) { + if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) { + pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS); + break; + } + } + } + + if (block != NULL) { + pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK); + } + } +} + +/** * Allocate and initialize a new IndexAndWriteNode node. */ static pm_index_and_write_node_t * @@ -2931,6 +3028,8 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); pm_index_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_and_write_node_t); + pm_index_arguments_check(parser, target->arguments, target->block); + *node = (pm_index_and_write_node_t) { { .type = PM_INDEX_AND_WRITE_NODE, @@ -2980,8 +3079,8 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, .message_loc = target->message_loc, .read_name = 0, .write_name = target->name, - .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), - .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value }; @@ -3002,6 +3101,8 @@ static pm_index_operator_write_node_t * pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { pm_index_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_operator_write_node_t); + pm_index_arguments_check(parser, target->arguments, target->block); + *node = (pm_index_operator_write_node_t) { { .type = PM_INDEX_OPERATOR_WRITE_NODE, @@ -3017,8 +3118,8 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, .arguments = target->arguments, .closing_loc = target->closing_loc, .block = target->block, - .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), - .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value }; @@ -3075,6 +3176,8 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); pm_index_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_or_write_node_t); + pm_index_arguments_check(parser, target->arguments, target->block); + *node = (pm_index_or_write_node_t) { { .type = PM_INDEX_OR_WRITE_NODE, @@ -3139,6 +3242,8 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { pm_index_target_node_t *node = PM_ALLOC_NODE(parser, pm_index_target_node_t); pm_node_flags_t flags = target->base.flags; + pm_index_arguments_check(parser, target->arguments, target->block); + *node = (pm_index_target_node_t) { { .type = PM_INDEX_TARGET_NODE, @@ -3358,9 +3463,9 @@ pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_varia }, .name = target->name, .name_loc = target->base.location, - .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, - .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) }; return node; @@ -3474,9 +3579,9 @@ pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_pat } }, .target = target, - .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, - .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) }; return node; @@ -3510,22 +3615,27 @@ pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_node * Allocate and initialize a new ConstantPathNode node. */ static pm_constant_path_node_t * -pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, pm_node_t *child) { +pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, const pm_token_t *name_token) { pm_assert_value_expression(parser, parent); - pm_constant_path_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_node_t); + pm_constant_id_t name = PM_CONSTANT_ID_UNSET; + if (name_token->type == PM_TOKEN_CONSTANT) { + name = pm_parser_constant_id_token(parser, name_token); + } + *node = (pm_constant_path_node_t) { { .type = PM_CONSTANT_PATH_NODE, .location = { .start = parent == NULL ? delimiter->start : parent->location.start, - .end = child->location.end + .end = name_token->end }, }, .parent = parent, - .child = child, - .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter) + .name = name, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), + .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) }; return node; @@ -3596,9 +3706,9 @@ pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_nod }, .name = target->name, .name_loc = target->base.location, - .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, - .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) }; return node; @@ -3717,6 +3827,113 @@ pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) { } /** + * When a method body is created, we want to check if the last statement is a + * return or a statement that houses a return. If it is, then we want to mark + * that return as being redundant so that we can compile it differently but also + * so that we can indicate that to the user. + */ +static void +pm_def_node_body_redundant_return(pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_RETURN_NODE: + node->flags |= PM_RETURN_NODE_FLAGS_REDUNDANT; + break; + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + + if (cast->statements != NULL && cast->else_clause == NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->statements); + } + break; + } + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + + if (cast->body.size > 0) { + pm_def_node_body_redundant_return(cast->body.nodes[cast->body.size - 1]); + } + break; + } + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + + if (cast->statements != NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->statements); + } + + if (cast->consequent != NULL) { + pm_def_node_body_redundant_return(cast->consequent); + } + break; + } + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + + if (cast->statements != NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->statements); + } + + if (cast->consequent != NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->consequent); + } + break; + } + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + + if (cast->statements != NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->statements); + } + break; + } + case PM_CASE_NODE: { + pm_case_node_t *cast = (pm_case_node_t *) node; + pm_node_t *condition; + + PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) { + pm_def_node_body_redundant_return(condition); + } + + if (cast->consequent != NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->consequent); + } + break; + } + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + + if (cast->statements != NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->statements); + } + break; + } + case PM_CASE_MATCH_NODE: { + pm_case_match_node_t *cast = (pm_case_match_node_t *) node; + pm_node_t *condition; + + PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) { + pm_def_node_body_redundant_return(condition); + } + + if (cast->consequent != NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->consequent); + } + break; + } + case PM_IN_NODE: { + pm_in_node_t *cast = (pm_in_node_t *) node; + + if (cast->statements != NULL) { + pm_def_node_body_redundant_return((pm_node_t *) cast->statements); + } + break; + } + default: + break; + } +} + +/** * Allocate and initialize a new DefNode node. */ static pm_def_node_t * @@ -3748,6 +3965,10 @@ pm_def_node_create( pm_def_node_receiver_check(parser, receiver); } + if (body != NULL) { + pm_def_node_body_redundant_return(body); + } + *node = (pm_def_node_t) { { .type = PM_DEF_NODE, @@ -4338,9 +4559,9 @@ pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *ta }, .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, - .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, - .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) }; return node; @@ -4399,7 +4620,7 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant *node = (pm_global_variable_read_node_t) { { .type = PM_GLOBAL_VARIABLE_READ_NODE, - .location = { .start = parser->start, .end = parser->start } + .location = PM_LOCATION_NULL_VALUE(parser) }, .name = name }; @@ -4441,11 +4662,11 @@ pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constan *node = (pm_global_variable_write_node_t) { { .type = PM_GLOBAL_VARIABLE_WRITE_NODE, - .location = { .start = parser->start, .end = parser->start } + .location = PM_LOCATION_NULL_VALUE(parser) }, .name = name, - .name_loc = { .start = parser->start, .end = parser->start }, - .operator_loc = { .start = parser->start, .end = parser->start }, + .name_loc = PM_LOCATION_NULL_VALUE(parser), + .operator_loc = PM_LOCATION_NULL_VALUE(parser), .value = value }; @@ -4846,9 +5067,9 @@ pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance }, .name = target->name, .name_loc = target->base.location, - .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, - .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1) }; return node; @@ -4923,6 +5144,50 @@ pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable } /** + * Append a part into a list of string parts. Importantly this handles nested + * interpolated strings by not necessarily removing the marker for static + * literals. + */ +static void +pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) { + switch (PM_NODE_TYPE(part)) { + case PM_STRING_NODE: + pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); + break; + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part; + pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL; + + if (embedded == NULL) { + // If there are no statements or more than one statement, then + // we lose the static literal flag. + pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL); + } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) { + // If the embedded statement is a string, then we can keep the + // static literal flag and mark the string as frozen. + pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); + } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the embedded statement is an interpolated string and it's + // a static literal, then we can keep the static literal flag. + } else { + // Otherwise we lose the static literal flag. + pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: + pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL); + break; + default: + assert(false && "unexpected node type"); + break; + } + + pm_node_list_append(parts, part); +} + +/** * Allocate a new InterpolatedRegularExpressionNode node. */ static pm_interpolated_regular_expression_node_t * @@ -4955,54 +5220,113 @@ pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expressio node->base.location.end = part->location.end; } - if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { - pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); - } - - if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) { - pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL); - } - - pm_node_list_append(&node->parts, part); + pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); } static inline void pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) { node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing); node->base.location.end = closing->end; - pm_node_flag_set((pm_node_t *)node, pm_regular_expression_flags_create(parser, closing)); + pm_node_flag_set((pm_node_t *) node, pm_regular_expression_flags_create(parser, closing)); } /** * Append a part to an InterpolatedStringNode node. + * + * This has some somewhat complicated semantics, because we need to update + * multiple flags that have somewhat confusing interactions. + * + * PM_NODE_FLAG_STATIC_LITERAL indicates that the node should be treated as a + * single static literal string that can be pushed onto the stack on its own. + * Note that this doesn't necessarily mean that the string will be frozen or + * not; the instructions in CRuby will be either putobject or putstring, + * depending on the combination of `--enable-frozen-string-literal`, + * `# frozen_string_literal: true`, and whether or not there is interpolation. + * + * PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN indicates that the string should be + * explicitly frozen. This will only happen if the string is comprised entirely + * of string parts that are themselves static literals and frozen. + * + * PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE indicates that the string should + * be explicitly marked as mutable. This will happen from + * `--disable-frozen-string-literal` or `# frozen_string_literal: false`. This + * is necessary to indicate that the string should be left up to the runtime, + * which could potentially use a chilled string otherwise. */ static inline void -pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_node_t *node, pm_node_t *part) { +pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_t *part) { +#define CLEAR_FLAGS(node) \ + node->base.flags = (pm_node_flags_t) (node->base.flags & ~(PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) + +#define MUTABLE_FLAGS(node) \ + node->base.flags = (pm_node_flags_t) ((node->base.flags | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN); + if (node->parts.size == 0 && node->opening_loc.start == NULL) { node->base.location.start = part->location.start; } - if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { - pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); - } + node->base.location.end = MAX(node->base.location.end, part->location.end); - if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) { - pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE); + switch (PM_NODE_TYPE(part)) { + case PM_STRING_NODE: + pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); + break; + case PM_INTERPOLATED_STRING_NODE: + if (PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the string that we're concatenating is a static literal, + // then we can keep the static literal flag for this string. + } else { + // Otherwise, we lose the static literal flag here and we should + // also clear the mutability flags. + CLEAR_FLAGS(node); + } + break; + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part; + pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL; + + if (embedded == NULL) { + // If we're embedding multiple statements or no statements, then + // the string is not longer a static literal. + CLEAR_FLAGS(node); + } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) { + // If the embedded statement is a string, then we can make that + // string as frozen and static literal, and not touch the static + // literal status of this string. + pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); + + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) { + MUTABLE_FLAGS(node); + } + } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the embedded statement is an interpolated string, but that + // string is marked as static literal, then we can keep our + // static literal status for this string. + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) { + MUTABLE_FLAGS(node); + } + } else { + // In all other cases, we lose the static literal flag here and + // become mutable. + CLEAR_FLAGS(node); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: + // Embedded variables clear static literal, which means we also + // should clear the mutability flags. + CLEAR_FLAGS(node); + break; + default: + assert(false && "unexpected node type"); + break; } pm_node_list_append(&node->parts, part); - node->base.location.end = MAX(node->base.location.end, part->location.end); - if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) { - switch (parser->frozen_string_literal) { - case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED: - pm_node_flag_set((pm_node_t *) node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE); - break; - case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED: - pm_node_flag_set((pm_node_t *) node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN); - break; - } - } +#undef CLEAR_FLAGS +#undef MUTABLE_FLAGS } /** @@ -5011,11 +5335,21 @@ pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_n static pm_interpolated_string_node_t * pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) { pm_interpolated_string_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_string_node_t); + pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL; + + switch (parser->frozen_string_literal) { + case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED: + flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE; + break; + case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED: + flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN; + break; + } *node = (pm_interpolated_string_node_t) { { .type = PM_INTERPOLATED_STRING_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, + .flags = flags, .location = { .start = opening->start, .end = closing->end, @@ -5029,7 +5363,7 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin if (parts != NULL) { pm_node_t *part; PM_NODE_LIST_FOREACH(parts, index, part) { - pm_interpolated_string_node_append(parser, node, part); + pm_interpolated_string_node_append(node, part); } } @@ -5051,15 +5385,7 @@ pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_ node->base.location.start = part->location.start; } - if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { - pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); - } - - if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) { - pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL); - } - - pm_node_list_append(&node->parts, part); + pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); node->base.location.end = MAX(node->base.location.end, part->location.end); } @@ -5125,11 +5451,7 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi static inline void pm_interpolated_xstring_node_append(pm_interpolated_x_string_node_t *node, pm_node_t *part) { - if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { - pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); - } - - pm_node_list_append(&node->parts, part); + pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); node->base.location.end = part->location.end; } @@ -5341,10 +5663,10 @@ pm_local_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *tar } }, .name_loc = target->location, - .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, .name = name, - .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), .depth = depth }; @@ -6397,6 +6719,7 @@ pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argumen *node = (pm_return_node_t) { { .type = PM_RETURN_NODE, + .flags = 0, .location = { .start = keyword->start, .end = (arguments == NULL ? keyword->end : arguments->base.location.end) @@ -6622,7 +6945,7 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, case PM_REDO_NODE: case PM_RETRY_NODE: case PM_RETURN_NODE: - pm_parser_warn_node(parser, previous, PM_WARN_UNREACHABLE_STATEMENT); + pm_parser_warn_node(parser, statement, PM_WARN_UNREACHABLE_STATEMENT); break; default: break; @@ -6729,7 +7052,8 @@ pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument } /** - * Read through the contents of a string and check if it consists solely of US ASCII code points. + * Read through the contents of a string and check if it consists solely of + * US-ASCII code points. */ static bool pm_ascii_only_p(const pm_string_t *contents) { @@ -6744,26 +7068,71 @@ pm_ascii_only_p(const pm_string_t *contents) { } /** + * Validate that the contents of the given symbol are all valid UTF-8. + */ +static void +parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) { + for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) { + size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor); + + if (width == 0) { + pm_parser_err(parser, location->start, location->end, PM_ERR_INVALID_SYMBOL); + break; + } + + cursor += width; + } +} + +/** + * Validate that the contents of the given symbol are all valid in the encoding + * of the parser. + */ +static void +parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) { + const pm_encoding_t *encoding = parser->encoding; + + for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) { + size_t width = encoding->char_width(cursor, end - cursor); + + if (width == 0) { + pm_parser_err(parser, location->start, location->end, PM_ERR_INVALID_SYMBOL); + break; + } + + cursor += width; + } +} + +/** * Ruby "downgrades" the encoding of Symbols to US-ASCII if the associated * encoding is ASCII-compatible and the Symbol consists only of US-ASCII code * points. Otherwise, the encoding may be explicitly set with an escape * sequence. + * + * If the validate flag is set, then it will check the contents of the symbol + * to ensure that all characters are valid in the encoding. */ static inline pm_node_flags_t -parse_symbol_encoding(const pm_parser_t *parser, const pm_string_t *contents) { +parse_symbol_encoding(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents, bool validate) { if (parser->explicit_encoding != NULL) { // A Symbol may optionally have its encoding explicitly set. This will // happen if an escape sequence results in a non-ASCII code point. if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents); return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING; } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING; + } else if (validate) { + parse_symbol_encoding_validate_other(parser, location, contents); } } else if (pm_ascii_only_p(contents)) { // Ruby stipulates that all source files must use an ASCII-compatible // encoding. Thus, all symbols appearing in source are eligible for // "downgrading" to US-ASCII. return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING; + } else if (validate) { + parse_symbol_encoding_validate_other(parser, location, contents); } return 0; @@ -6931,7 +7300,7 @@ pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_t */ static pm_symbol_node_t * pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) { - pm_symbol_node_t *node = pm_symbol_node_create_unescaped(parser, opening, value, closing, &parser->current_string, parse_symbol_encoding(parser, &parser->current_string)); + pm_symbol_node_t *node = pm_symbol_node_create_unescaped(parser, opening, value, closing, &parser->current_string, parse_symbol_encoding(parser, value, &parser->current_string, false)); parser->current_string = PM_STRING_EMPTY; return node; } @@ -6953,7 +7322,7 @@ pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) { assert((label.end - label.start) >= 0); pm_string_shared_init(&node->unescaped, label.start, label.end); - pm_node_flag_set((pm_node_t *) node, parse_symbol_encoding(parser, &node->unescaped)); + pm_node_flag_set((pm_node_t *) node, parse_symbol_encoding(parser, &label, &node->unescaped, false)); break; } @@ -6985,9 +7354,9 @@ pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) { { .type = PM_SYMBOL_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, - .location = { .start = parser->start, .end = parser->start } + .location = PM_LOCATION_NULL_VALUE(parser) }, - .value_loc = { .start = parser->start, .end = parser->start }, + .value_loc = PM_LOCATION_NULL_VALUE(parser), .unescaped = { 0 } }; @@ -7038,7 +7407,8 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const .unescaped = node->unescaped }; - pm_node_flag_set((pm_node_t *)new_node, parse_symbol_encoding(parser, &node->unescaped)); + pm_token_t content = { .type = PM_TOKEN_IDENTIFIER, .start = node->content_loc.start, .end = node->content_loc.end }; + pm_node_flag_set((pm_node_t *) new_node, parse_symbol_encoding(parser, &content, &node->unescaped, true)); // We are explicitly _not_ using pm_node_destroy here because we don't want // to trash the unescaped string. We could instead copy the string if we @@ -7387,10 +7757,10 @@ pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_s *node = (pm_while_node_t) { { .type = PM_WHILE_NODE, - .location = { .start = parser->start, .end = parser->start } + .location = PM_LOCATION_NULL_VALUE(parser) }, - .keyword_loc = { .start = parser->start, .end = parser->start }, - .closing_loc = { .start = parser->start, .end = parser->start }, + .keyword_loc = PM_LOCATION_NULL_VALUE(parser), + .closing_loc = PM_LOCATION_NULL_VALUE(parser), .predicate = predicate, .statements = statements }; @@ -7574,7 +7944,7 @@ pm_local_variable_read_node_create_it(pm_parser_t *parser, const pm_token_t *nam static pm_node_t * pm_node_check_it(pm_parser_t *parser, pm_node_t *node) { if ( - (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3_0) && + (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && !parser->current_scope->closed && (parser->current_scope->numbered_parameters != PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED) && pm_node_is_it(parser, node) @@ -8023,7 +8393,12 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) { // If we have hit a ractor pragma, attempt to lex that. uint32_t value_length = (uint32_t) (value_end - value_start); if (key_length == 24 && pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) { - if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) { + const uint8_t *cursor = parser->current.start; + while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--; + + if (!((cursor == parser->start) || (cursor[-1] == '\n'))) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE); + } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) { pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE); } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) { pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL); @@ -8298,10 +8673,11 @@ context_human(pm_context_t context) { /* Specific token lexers */ /******************************************************************************/ -static void -pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *invalid) { +static inline void +pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) { if (invalid != NULL) { - pm_parser_err(parser, invalid, invalid + 1, PM_ERR_INVALID_NUMBER_UNDERSCORE); + pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER; + pm_parser_err(parser, invalid, invalid + 1, diag_id); } } @@ -8309,7 +8685,7 @@ static size_t pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) { const uint8_t *invalid = NULL; size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid); - pm_strspn_number_validate(parser, invalid); + pm_strspn_number_validate(parser, string, length, invalid); return length; } @@ -8317,7 +8693,7 @@ static size_t pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) { const uint8_t *invalid = NULL; size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid); - pm_strspn_number_validate(parser, invalid); + pm_strspn_number_validate(parser, string, length, invalid); return length; } @@ -8325,7 +8701,7 @@ static size_t pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) { const uint8_t *invalid = NULL; size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid); - pm_strspn_number_validate(parser, invalid); + pm_strspn_number_validate(parser, string, length, invalid); return length; } @@ -8333,7 +8709,7 @@ static size_t pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) { const uint8_t *invalid = NULL; size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid); - pm_strspn_number_validate(parser, invalid); + pm_strspn_number_validate(parser, string, length, invalid); return length; } @@ -8395,6 +8771,7 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { if (pm_char_is_decimal_digit(peek(parser))) { parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end); } else { + match(parser, '_'); pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL); } @@ -8407,6 +8784,7 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { if (pm_char_is_binary_digit(peek(parser))) { parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end); } else { + match(parser, '_'); pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY); } @@ -8420,6 +8798,7 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { if (pm_char_is_octal_digit(peek(parser))) { parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end); } else { + match(parser, '_'); pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL); } @@ -8447,6 +8826,7 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { if (pm_char_is_hexadecimal_digit(peek(parser))) { parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end); } else { + match(parser, '_'); pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL); } @@ -8475,6 +8855,16 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { type = lex_optional_float_suffix(parser, seen_e); } + // At this point we have a completed number, but we want to provide the user + // with a good experience if they put an additional .xxx fractional + // component on the end, so we'll check for that here. + if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) { + const uint8_t *fraction_start = parser->current.end; + const uint8_t *fraction_end = parser->current.end + 2; + fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end); + pm_parser_err(parser, fraction_start, fraction_end, PM_ERR_INVALID_NUMBER_FRACTION); + } + return type; } @@ -8567,7 +8957,7 @@ lex_global_variable(pm_parser_t *parser) { } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0); // $0 isn't allowed to be followed by anything. - pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0 : PM_ERR_INVALID_VARIABLE_GLOBAL; + pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id); } @@ -8603,9 +8993,9 @@ lex_global_variable(pm_parser_t *parser) { } else { // If we get here, then we have a $ followed by something that // isn't recognized as a global variable. - pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0 : PM_ERR_INVALID_VARIABLE_GLOBAL; - size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); - PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start); + pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start); } return PM_TOKEN_GLOBAL_VARIABLE; @@ -8976,12 +9366,20 @@ escape_hexadecimal_digit(const uint8_t value) { * validated. */ static inline uint32_t -escape_unicode(const uint8_t *string, size_t length) { +escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length) { uint32_t value = 0; for (size_t index = 0; index < length; index++) { if (index != 0) value <<= 4; value |= escape_hexadecimal_digit(string[index]); } + + // Here we're going to verify that the value is actually a valid Unicode + // codepoint and not a surrogate pair. + if (value >= 0xD800 && value <= 0xDFFF) { + pm_parser_err(parser, string, string + length, PM_ERR_ESCAPE_INVALID_UNICODE); + return 0xFFFD; + } + return value; } @@ -9230,7 +9628,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); } - escape_write_byte_encoded(parser, buffer, value); + escape_write_byte_encoded(parser, buffer, escape_byte(value, flags)); } else { pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL); } @@ -9241,22 +9639,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre const uint8_t *start = parser->current.end - 1; parser->current.end++; - if ( - (parser->current.end + 4 <= parser->end) && - pm_char_is_hexadecimal_digit(parser->current.end[0]) && - pm_char_is_hexadecimal_digit(parser->current.end[1]) && - pm_char_is_hexadecimal_digit(parser->current.end[2]) && - pm_char_is_hexadecimal_digit(parser->current.end[3]) - ) { - uint32_t value = escape_unicode(parser->current.end, 4); - - if (flags & PM_ESCAPE_FLAG_REGEXP) { - pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start)); - } - escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value); - - parser->current.end += 4; - } else if (peek(parser) == '{') { + if (peek(parser) == '{') { const uint8_t *unicode_codepoints_start = parser->current.end - 2; parser->current.end++; @@ -9274,7 +9657,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE_LONG); } else if (hexadecimal_length == 0) { // there are not hexadecimal characters - pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE); + pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE); + pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_TERM); return; } @@ -9284,7 +9668,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre extra_codepoints_start = unicode_start; } - uint32_t value = escape_unicode(unicode_start, hexadecimal_length); + uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length); escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value); parser->current.end += pm_strspn_whitespace(parser->current.end, parser->end - parser->current.end); @@ -9306,7 +9690,21 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre pm_buffer_append_bytes(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start)); } } else { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE); + size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4)); + + if (length == 4) { + uint32_t value = escape_unicode(parser, parser->current.end, 4); + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start)); + } + + escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value); + parser->current.end += 4; + } else { + parser->current.end += length; + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE); + } } return; @@ -9331,6 +9729,12 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL); return; case ' ': @@ -9358,7 +9762,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre case 'C': { parser->current.end++; if (peek(parser) != '-') { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL); return; } @@ -9381,6 +9786,12 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL); return; case ' ': @@ -9395,7 +9806,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; default: { if (!char_is_ascii_printable(peeked)) { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL); return; } @@ -9408,7 +9820,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre case 'M': { parser->current.end++; if (peek(parser) != '-') { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META); + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META); return; } @@ -9426,6 +9839,12 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META); return; case ' ': @@ -9440,7 +9859,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; default: if (!char_is_ascii_printable(peeked)) { - pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META); + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META); return; } @@ -9560,8 +9980,8 @@ lex_at_variable(pm_parser_t *parser) { } } else if (parser->current.end < parser->end && pm_char_is_decimal_digit(*parser->current.end)) { pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE; - if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0) { - diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3_0 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0; + if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) { + diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3; } size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); @@ -10545,8 +10965,11 @@ parser_lex(pm_parser_t *parser) { } size_t ident_length = (size_t) (parser->current.end - ident_start); + bool ident_error = false; + if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) { - // TODO: handle unterminated heredoc + pm_parser_err(parser, ident_start, ident_start + ident_length, PM_ERR_HEREDOC_IDENTIFIER); + ident_error = true; } parser->explicit_encoding = NULL; @@ -10571,7 +10994,7 @@ parser_lex(pm_parser_t *parser) { // this is not a valid heredoc declaration. In this case we // will add an error, but we will still return a heredoc // start. - pm_parser_err_current(parser, PM_ERR_HEREDOC_TERM); + if (!ident_error) pm_parser_err_heredoc_term(parser, parser->lex_modes.current); body_start = parser->end; } else { // Otherwise, we want to indicate that the body of the @@ -10951,7 +11374,7 @@ parser_lex(pm_parser_t *parser) { // operator because we don't want to move into the string // lex mode unnecessarily. if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) { - pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT); + pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT_EOF); LEX(PM_TOKEN_PERCENT); } @@ -10970,10 +11393,7 @@ parser_lex(pm_parser_t *parser) { const uint8_t delimiter = pm_lex_percent_delimiter(parser); lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter)); - - if (parser->current.end < parser->end) { - LEX(PM_TOKEN_STRING_BEGIN); - } + LEX(PM_TOKEN_STRING_BEGIN); } // Delimiters for %-literals cannot be alphanumeric. We @@ -11898,7 +12318,7 @@ parser_lex(pm_parser_t *parser) { // terminator) but still continue parsing so that content after the // declaration of the heredoc can be parsed. if (parser->current.end >= parser->end) { - pm_parser_err_current(parser, PM_ERR_HEREDOC_TERM); + pm_parser_err_heredoc_term(parser, lex_mode); parser->next_start = lex_mode->as.heredoc.next_start; parser->heredoc_end = parser->current.end; lex_state_set(parser, PM_LEX_STATE_END); @@ -12537,6 +12957,23 @@ expect3(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_to parser->previous.type = PM_TOKEN_MISSING; } +/** + * A special expect1 that expects a heredoc terminator and handles popping the + * lex mode accordingly. + */ +static void +expect1_heredoc_term(pm_parser_t *parser, pm_lex_mode_t *lex_mode) { + if (match1(parser, PM_TOKEN_HEREDOC_END)) { + lex_mode_pop(parser); + parser_lex(parser); + } else { + pm_parser_err_heredoc_term(parser, lex_mode); + lex_mode_pop(parser); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } +} + static pm_node_t * parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id); @@ -12665,24 +13102,71 @@ parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) { } /** + * Certain expressions are not targetable, but in order to provide a better + * experience we give a specific error message. In order to maintain as much + * information in the tree as possible, we replace them with local variable + * writes. + */ +static pm_node_t * +parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) { + switch (PM_NODE_TYPE(target)) { + case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break; + case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break; + case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break; + case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break; + case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break; + case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break; + case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break; + default: break; + } + + pm_constant_id_t name = pm_parser_constant_id_location(parser, target->location.start, target->location.end); + pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0); + + pm_node_destroy(parser, target); + return (pm_node_t *) result; +} + +/** * Convert the given node into a valid target node. */ static pm_node_t * -parse_target(pm_parser_t *parser, pm_node_t *target) { +parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple) { switch (PM_NODE_TYPE(target)) { case PM_MISSING_NODE: return target; + case PM_SOURCE_ENCODING_NODE: + case PM_FALSE_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_NIL_NODE: + case PM_SELF_NODE: + case PM_TRUE_NODE: { + // In these special cases, we have specific error messages and we + // will replace them with local variable writes. + return parse_unwriteable_target(parser, target); + } case PM_CLASS_VARIABLE_READ_NODE: assert(sizeof(pm_class_variable_target_node_t) == sizeof(pm_class_variable_read_node_t)); target->type = PM_CLASS_VARIABLE_TARGET_NODE; return target; case PM_CONSTANT_PATH_NODE: + if (context_def_p(parser)) { + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD); + } + assert(sizeof(pm_constant_path_target_node_t) == sizeof(pm_constant_path_node_t)); target->type = PM_CONSTANT_PATH_TARGET_NODE; + return target; case PM_CONSTANT_READ_NODE: + if (context_def_p(parser)) { + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD); + } + assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t)); target->type = PM_CONSTANT_TARGET_NODE; + return target; case PM_BACK_REFERENCE_READ_NODE: case PM_NUMBERED_REFERENCE_READ_NODE: @@ -12715,7 +13199,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target) { pm_splat_node_t *splat = (pm_splat_node_t *) target; if (splat->expression != NULL) { - splat->expression = parse_target(parser, splat->expression); + splat->expression = parse_target(parser, splat->expression, multiple); } return (pm_node_t *) splat; @@ -12753,6 +13237,10 @@ parse_target(pm_parser_t *parser, pm_node_t *target) { } if (*call->message_loc.start == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { + if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION); + } + parse_write_name(parser, &call->name); return (pm_node_t *) pm_call_target_node_create(parser, call); } @@ -12780,8 +13268,8 @@ parse_target(pm_parser_t *parser, pm_node_t *target) { * assignment. */ static pm_node_t * -parse_target_validate(pm_parser_t *parser, pm_node_t *target) { - pm_node_t *result = parse_target(parser, target); +parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) { + pm_node_t *result = parse_target(parser, target, multiple); // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in parens after the targets. if ( @@ -12826,13 +13314,20 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod } case PM_CONSTANT_PATH_NODE: { pm_node_t *node = (pm_node_t *) pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value); + + if (context_def_p(parser)) { + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD); + } + return parse_shareable_constant_write(parser, node); } case PM_CONSTANT_READ_NODE: { pm_node_t *node = (pm_node_t *) pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value); + if (context_def_p(parser)) { pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD); } + pm_node_destroy(parser, target); return parse_shareable_constant_write(parser, node); } @@ -13011,7 +13506,7 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE); pm_multi_target_node_t *result = pm_multi_target_node_create(parser); - pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target)); + pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true)); while (accept1(parser, PM_TOKEN_COMMA)) { if (accept1(parser, PM_TOKEN_USTAR)) { @@ -13027,7 +13522,7 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b if (token_begins_expression_p(parser->current.type)) { name = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR); - name = parse_target(parser, name); + name = parse_target(parser, name, true); } pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name); @@ -13035,7 +13530,7 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b has_rest = true; } else if (token_begins_expression_p(parser->current.type)) { pm_node_t *target = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA); - target = parse_target(parser, target); + target = parse_target(parser, target, true); pm_multi_target_node_targets_append(parser, result, target); } else if (!match1(parser, PM_TOKEN_EOF)) { @@ -13072,8 +13567,8 @@ parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_ */ static pm_statements_node_t * parse_statements(pm_parser_t *parser, pm_context_t context) { - // First, skip past any optional terminators that might be at the beginning of - // the statements. + // First, skip past any optional terminators that might be at the beginning + // of the statements. while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)); // If we have a terminator, then we can just return NULL. @@ -13089,20 +13584,20 @@ parse_statements(pm_parser_t *parser, pm_context_t context) { pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION); pm_statements_node_body_append(parser, statements, node); - // If we're recovering from a syntax error, then we need to stop parsing the - // statements now. + // If we're recovering from a syntax error, then we need to stop parsing + // the statements now. if (parser->recovering) { - // If this is the level of context where the recovery has happened, then - // we can mark the parser as done recovering. + // If this is the level of context where the recovery has happened, + // then we can mark the parser as done recovering. if (context_terminator(context, &parser->current)) parser->recovering = false; break; } - // If we have a terminator, then we will parse all consecutive terminators - // and then continue parsing the statements list. + // If we have a terminator, then we will parse all consecutive + // terminators and then continue parsing the statements list. if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { - // If we have a terminator, then we will continue parsing the statements - // list. + // If we have a terminator, then we will continue parsing the + // statements list. while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); if (context_terminator(context, &parser->current)) break; @@ -13110,27 +13605,28 @@ parse_statements(pm_parser_t *parser, pm_context_t context) { continue; } - // At this point we have a list of statements that are not terminated by a - // newline or semicolon. At this point we need to check if we're at the end - // of the statements list. If we are, then we should break out of the loop. + // At this point we have a list of statements that are not terminated by + // a newline or semicolon. At this point we need to check if we're at + // the end of the statements list. If we are, then we should break out + // of the loop. if (context_terminator(context, &parser->current)) break; // At this point, we have a syntax error, because the statement was not // terminated by a newline or semicolon, and we're not at the end of the - // statements list. Ideally we should scan forward to determine if we should - // insert a missing terminator or break out of parsing the statements list - // at this point. + // statements list. Ideally we should scan forward to determine if we + // should insert a missing terminator or break out of parsing the + // statements list at this point. // - // We don't have that yet, so instead we'll do a more naive approach. If we - // were unable to parse an expression, then we will skip past this token and - // continue parsing the statements list. Otherwise we'll add an error and - // continue parsing the statements list. + // We don't have that yet, so instead we'll do a more naive approach. If + // we were unable to parse an expression, then we will skip past this + // token and continue parsing the statements list. Otherwise we'll add + // an error and continue parsing the statements list. if (PM_NODE_TYPE_P(node, PM_MISSING_NODE)) { parser_lex(parser); while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); if (context_terminator(context, &parser->current)) break; - } else if (!accept1(parser, PM_TOKEN_NEWLINE)) { + } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) { // This is an inlined version of accept1 because the error that we // want to add has varargs. If this happens again, we should // probably extract a helper function. @@ -13152,11 +13648,11 @@ parse_statements(pm_parser_t *parser, pm_context_t context) { */ static void pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) { - const pm_node_t *duplicated = pm_static_literals_add(parser, literals, node); + const pm_node_t *duplicated = pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node); if (duplicated != NULL) { pm_buffer_t buffer = { 0 }; - pm_static_literal_inspect(&buffer, parser, duplicated); + pm_static_literal_inspect(&buffer, &parser->newline_list, parser->start_line, parser->encoding->name, duplicated); pm_diagnostic_list_append_format( &parser->warning_list, @@ -13178,7 +13674,7 @@ pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *liter */ static void pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) { - if (pm_static_literals_add(parser, literals, node) != NULL) { + if (pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node) != NULL) { pm_diagnostic_list_append_format( &parser->warning_list, node->location.start, @@ -13206,10 +13702,16 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod pm_token_t operator = parser->previous; pm_node_t *value = NULL; - if (token_begins_expression_p(parser->current.type)) { + if (match1(parser, PM_TOKEN_BRACE_LEFT)) { + // If we're about to parse a nested hash that is being + // pushed into this hash directly with **, then we want the + // inner hash to share the static literals with the outer + // hash. + parser->current_hash_keys = literals; value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH); - } - else { + } else if (token_begins_expression_p(parser->current.type)) { + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH); + } else { pm_parser_scope_forwarding_keywords_check(parser, &operator); } @@ -13234,9 +13736,15 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 }; value = (pm_node_t *) pm_constant_read_node_create(parser, &constant); } else { - int depth = pm_parser_local_depth(parser, &((pm_token_t) { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 })); + int depth = -1; pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 }; + if (identifier.end[-1] == '!' || identifier.end[-1] == '?') { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ); + } else { + depth = pm_parser_local_depth(parser, &identifier); + } + if (depth == -1) { value = (pm_node_t *) pm_call_node_variable_call_create(parser, &identifier); } else { @@ -13354,15 +13862,16 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser); argument = (pm_node_t *) hash; - pm_static_literals_t literals = { 0 }; - bool contains_keyword_splat = parse_assocs(parser, &literals, (pm_node_t *) hash); + pm_static_literals_t hash_keys = { 0 }; + bool contains_keyword_splat = parse_assocs(parser, &hash_keys, (pm_node_t *) hash); parse_arguments_append(parser, arguments, argument); - if (contains_keyword_splat) { - pm_node_flag_set((pm_node_t *)arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT); - } - pm_static_literals_free(&literals); + pm_node_flags_t flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; + if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + pm_node_flag_set((pm_node_t *) arguments->arguments, flags); + + pm_static_literals_free(&hash_keys); parsed_bare_hash = true; break; @@ -13438,7 +13947,9 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, !parsed_first_argument, PM_ERR_EXPECT_ARGUMENT); } + bool contains_keywords = false; bool contains_keyword_splat = false; + if (pm_symbol_node_label_p(argument) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) { if (parsed_bare_hash) { pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH); @@ -13452,10 +13963,11 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for } pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser); + contains_keywords = true; // Create the set of static literals for this hash. - pm_static_literals_t literals = { 0 }; - pm_hash_key_static_literals_add(parser, &literals, argument); + pm_static_literals_t hash_keys = { 0 }; + pm_hash_key_static_literals_add(parser, &hash_keys, argument); // Finish parsing the one we are part way through. pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE); @@ -13469,10 +13981,10 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for token_begins_expression_p(parser->current.type) || match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL) )) { - contains_keyword_splat = parse_assocs(parser, &literals, (pm_node_t *) bare_hash); + contains_keyword_splat = parse_assocs(parser, &hash_keys, (pm_node_t *) bare_hash); } - pm_static_literals_free(&literals); + pm_static_literals_free(&hash_keys); parsed_bare_hash = true; } else if (accept1(parser, PM_TOKEN_KEYWORD_IN)) { // TODO: Could we solve this with binding powers instead? @@ -13480,9 +13992,12 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for } parse_arguments_append(parser, arguments, argument); - if (contains_keyword_splat) { - pm_node_flag_set((pm_node_t *)arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT); - } + + pm_node_flags_t flags = 0; + if (contains_keywords) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; + if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + pm_node_flag_set((pm_node_t *) arguments->arguments, flags); + break; } } @@ -13595,7 +14110,6 @@ typedef enum { PM_PARAMETERS_ORDER_OPTIONAL, PM_PARAMETERS_ORDER_NAMED, PM_PARAMETERS_ORDER_NONE, - } pm_parameters_order_t; /** @@ -13620,31 +14134,37 @@ static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = { * Check if current parameter follows valid parameters ordering. If not it adds * an error to the list without stopping the parsing, otherwise sets the * parameters state to the one corresponding to the current parameter. + * + * It returns true if it was successful, and false otherwise. */ -static void +static bool update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) { pm_parameters_order_t state = parameters_ordering[token->type]; - if (state == PM_PARAMETERS_NO_CHANGE) return; + if (state == PM_PARAMETERS_NO_CHANGE) return true; // If we see another ordered argument after a optional argument // we only continue parsing ordered arguments until we stop seeing ordered arguments. if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) { *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL; - return; + return true; } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) { - return; + return true; } if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR); - } - - if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) { + return false; + } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) { + pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER); + return false; + } else if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) { // We know what transition we failed on, so we can provide a better error here. pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER); - } else if (state < *current) { - *current = state; + return false; } + + if (state < *current) *current = state; + return true; } /** @@ -13713,27 +14233,22 @@ parse_parameters( pm_parser_err_current(parser, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); } - if (order > PM_PARAMETERS_ORDER_NOTHING_AFTER) { - update_parameter_state(parser, &parser->current, &order); - parser_lex(parser); + bool succeeded = update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); - parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL; + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL; + pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous); - pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous); - if (params->keyword_rest != NULL) { - // If we already have a keyword rest parameter, then we replace it with the - // forwarding parameter and move the keyword rest parameter to the posts list. - pm_node_t *keyword_rest = params->keyword_rest; - pm_parameters_node_posts_append(params, keyword_rest); - pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD); - params->keyword_rest = NULL; - } - pm_parameters_node_keyword_rest_set(params, (pm_node_t *)param); - } else { - update_parameter_state(parser, &parser->current, &order); - parser_lex(parser); + if (params->keyword_rest != NULL) { + // If we already have a keyword rest parameter, then we replace it with the + // forwarding parameter and move the keyword rest parameter to the posts list. + pm_node_t *keyword_rest = params->keyword_rest; + pm_parameters_node_posts_append(params, keyword_rest); + if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD); + params->keyword_rest = NULL; } + pm_parameters_node_keyword_rest_set(params, (pm_node_t *) param); break; } case PM_TOKEN_CLASS_VARIABLE: @@ -13828,6 +14343,12 @@ parse_parameters( pm_token_t local = name; local.end -= 1; + if (parser->encoding_changed ? parser->encoding->isupper_char(local.start, local.end - local.start) : pm_encoding_utf_8_isupper_char(local.start, local.end - local.start)) { + pm_parser_err(parser, local.start, local.end, PM_ERR_ARGUMENT_FORMAL_CONSTANT); + } else if (local.end[-1] == '!' || local.end[-1] == '?') { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE); + } + bool repeated = pm_parser_parameter_name_check(parser, &local); pm_parser_local_add_token(parser, &local, 1); @@ -13903,6 +14424,7 @@ parse_parameters( pm_token_t operator = parser->previous; pm_token_t name; bool repeated = false; + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { name = parser->previous; repeated = pm_parser_parameter_name_check(parser, &name); @@ -13916,6 +14438,7 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter(param); } + if (params->rest == NULL) { pm_parameters_node_rest_set(params, param); } else { @@ -13927,6 +14450,7 @@ parse_parameters( } case PM_TOKEN_STAR_STAR: case PM_TOKEN_USTAR_STAR: { + pm_parameters_order_t previous_order = order; update_parameter_state(parser, &parser->current, &order); parser_lex(parser); @@ -13934,6 +14458,10 @@ parse_parameters( pm_node_t *param; if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) { + if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW); + } + param = (pm_node_t *) pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous); } else { pm_token_t name; @@ -14031,7 +14559,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type pm_rescue_node_operator_set(rescue, &parser->previous); pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_RESCUE_VARIABLE); - reference = parse_target(parser, reference); + reference = parse_target(parser, reference, false); pm_rescue_node_reference_set(rescue, reference); break; @@ -14061,7 +14589,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type pm_rescue_node_operator_set(rescue, &parser->previous); pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_RESCUE_VARIABLE); - reference = parse_target(parser, reference); + reference = parse_target(parser, reference, false); pm_rescue_node_reference_set(rescue, reference); break; @@ -14385,7 +14913,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); } else { pm_accepts_block_stack_push(parser, true); - parse_arguments(parser, arguments, true, PM_TOKEN_PARENTHESIS_RIGHT); + parse_arguments(parser, arguments, accepts_block, PM_TOKEN_PARENTHESIS_RIGHT); if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_type_human(parser->current.type)); @@ -14403,7 +14931,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept // If we get here, then the subsequent token cannot be used as an infix // operator. In this case we assume the subsequent token is part of an // argument to this method call. - parse_arguments(parser, arguments, true, PM_TOKEN_EOF); + parse_arguments(parser, arguments, accepts_block, PM_TOKEN_EOF); // If we have done with the arguments and still not consumed the comma, // then we have a trailing comma where we need to check whether it is @@ -14434,11 +14962,8 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept if (arguments->block == NULL && !arguments->has_forwarding) { arguments->block = (pm_node_t *) block; } else { - if (arguments->has_forwarding) { - pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_FORWARDING); - } else { - pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); - } + pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); + if (arguments->block != NULL) { if (arguments->arguments == NULL) { arguments->arguments = pm_arguments_node_create(parser); @@ -14875,6 +15400,10 @@ parse_string_part(pm_parser_t *parser) { // "aaa #{bbb} #@ccc ddd" // ^^^^^^ case PM_TOKEN_EMBEXPR_BEGIN: { + // Ruby disallows seeing encoding around interpolation in strings, + // even though it is known at parse time. + parser->explicit_encoding = NULL; + pm_lex_state_t state = parser->lex_state; int brace_nesting = parser->brace_nesting; @@ -14907,6 +15436,10 @@ parse_string_part(pm_parser_t *parser) { // "aaa #{bbb} #@ccc ddd" // ^^^^^ case PM_TOKEN_EMBVAR: { + // Ruby disallows seeing encoding around interpolation in strings, + // even though it is known at parse time. + parser->explicit_encoding = NULL; + lex_state_set(parser, PM_LEX_STATE_BEG); parser_lex(parser); @@ -15030,7 +15563,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); - pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &symbol->unescaped)); + pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); return (pm_node_t *) symbol; } @@ -15130,7 +15663,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC); } - return (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped)); + return (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, false)); } /** @@ -15155,7 +15688,7 @@ parse_undef_argument(pm_parser_t *parser) { pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); - pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &symbol->unescaped)); + pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); return (pm_node_t *) symbol; } @@ -15196,7 +15729,7 @@ parse_alias_argument(pm_parser_t *parser, bool first) { pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); - pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &symbol->unescaped)); + pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); return (pm_node_t *) symbol; } @@ -15423,8 +15956,12 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_w nodes->size = write_index; } +#define PM_PARSE_PATTERN_SINGLE 0 +#define PM_PARSE_PATTERN_TOP 1 +#define PM_PARSE_PATTERN_MULTI 2 + static pm_node_t * -parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, bool top_pattern, pm_diagnostic_id_t diag_id); +parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id); /** * Add the newly created local to the list of captures for this pattern matching @@ -15453,9 +15990,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures while (accept1(parser, PM_TOKEN_COLON_COLON)) { pm_token_t delimiter = parser->previous; expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - - pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); - node = (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, child); + node = (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); } // If there is a [ or ( that follows, then this is part of a larger pattern @@ -15474,7 +16009,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures accept1(parser, PM_TOKEN_NEWLINE); if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) { - inner = parse_pattern(parser, captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET); + inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET); accept1(parser, PM_TOKEN_NEWLINE); expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET); } @@ -15486,7 +16021,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures accept1(parser, PM_TOKEN_NEWLINE); if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { - inner = parse_pattern(parser, captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN); + inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN); accept1(parser, PM_TOKEN_NEWLINE); expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); } @@ -15635,16 +16170,54 @@ parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) } /** + * Check that the slice of the source given by the bounds parameters constitutes + * a valid local variable name. + */ +static bool +pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + ptrdiff_t length = end - start; + if (length == 0) return false; + + // First ensure that it starts with a valid identifier starting character. + size_t width = char_is_identifier_start(parser, start); + if (width == 0) return false; + + // Next, ensure that it's not an uppercase character. + if (parser->encoding_changed) { + if (parser->encoding->isupper_char(start, length)) return false; + } else { + if (pm_encoding_utf_8_isupper_char(start, length)) return false; + } + + // Next, iterate through all of the bytes of the string to ensure that they + // are all valid identifier characters. + const uint8_t *cursor = start + width; + while ((cursor < end) && (width = char_is_identifier(parser, cursor))) cursor += width; + return cursor == end; +} + +/** * Create an implicit node for the value of a hash pattern that has omitted the * value. This will use an implicit local variable target. */ static pm_node_t * parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key) { const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc; + pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, value_loc->start, value_loc->end); + int depth = -1; - int depth; - if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { + if (pm_slice_is_valid_local(parser, value_loc->start, value_loc->end)) { + depth = pm_parser_local_depth_constant_id(parser, constant_id); + } else { + pm_parser_err(parser, key->base.location.start, key->base.location.end, PM_ERR_PATTERN_HASH_KEY_LOCALS); + + if ((value_loc->end > value_loc->start) && ((value_loc->end[-1] == '!') || (value_loc->end[-1] == '?'))) { + PM_PARSER_ERR_LOCATION_FORMAT(parser, value_loc, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (value_loc->end - value_loc->start), (const char *) value_loc->start); + } + } + + if (depth == -1) { pm_parser_local_add(parser, constant_id, value_loc->start, value_loc->end, 0); } @@ -15665,7 +16238,7 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *ca */ static void parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) { - if (pm_static_literals_add(parser, keys, node) != NULL) { + if (pm_static_literals_add(&parser->newline_list, parser->start_line, keys, node) != NULL) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE); } } @@ -15696,7 +16269,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } else { // Here we have a value for the first assoc in the list, so // we will parse it now. - value = parse_pattern(parser, captures, false, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY); + value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY); } pm_token_t operator = not_provided(parser); @@ -15711,7 +16284,8 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node // If we get anything else, then this is an error. For this we'll // create a missing node for the value and create an assoc node for // the first node in the list. - pm_parser_err_node(parser, first_node, PM_ERR_PATTERN_HASH_KEY_LABEL); + pm_diagnostic_id_t diag_id = PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE) ? PM_ERR_PATTERN_HASH_KEY_INTERPOLATED : PM_ERR_PATTERN_HASH_KEY_LABEL; + pm_parser_err_node(parser, first_node, diag_id); pm_token_t operator = not_provided(parser); pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, first_node->location.start, first_node->location.end); @@ -15748,7 +16322,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node if (match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key); } else { - value = parse_pattern(parser, captures, false, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY); + value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY); } pm_token_t operator = not_provided(parser); @@ -15805,7 +16379,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm // Otherwise, we'll parse the inner pattern, then deal with it depending // on the type it returns. - pm_node_t *inner = parse_pattern(parser, captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET); + pm_node_t *inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET); accept1(parser, PM_TOKEN_NEWLINE); expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET); @@ -15872,11 +16446,11 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm first_node = parse_pattern_keyword_rest(parser, captures); break; case PM_TOKEN_STRING_BEGIN: - first_node = parse_expression(parser, PM_BINDING_POWER_MAX, false, PM_ERR_PATTERN_HASH_KEY); + first_node = parse_expression(parser, PM_BINDING_POWER_MAX, false, PM_ERR_PATTERN_HASH_KEY_LABEL); break; default: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_type_human(parser->current.type)); parser_lex(parser); - pm_parser_err_previous(parser, PM_ERR_PATTERN_HASH_KEY); first_node = (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); break; @@ -15953,7 +16527,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm if (variable == NULL) { if ( - (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3_0) && + (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && !parser->current_scope->closed && (parser->current_scope->numbered_parameters != PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED) && pm_token_is_it(parser->previous.start, parser->previous.end) @@ -16027,8 +16601,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm parser_lex(parser); expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); - pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, child); + pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous); return parse_pattern_constant_path(parser, captures, (pm_node_t *) node); } @@ -16079,7 +16652,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p pm_token_t opening = parser->current; parser_lex(parser); - pm_node_t *body = parse_pattern(parser, captures, false, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN); + pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN); accept1(parser, PM_TOKEN_NEWLINE); expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); pm_node_t *right = (pm_node_t *) pm_parentheses_node_create(parser, &opening, body, &parser->previous); @@ -16138,7 +16711,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p * Parse a pattern matching expression. */ static pm_node_t * -parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, bool top_pattern, pm_diagnostic_id_t diag_id) { +parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id) { pm_node_t *node = NULL; bool leading_rest = false; @@ -16148,14 +16721,26 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, bool top_pat case PM_TOKEN_LABEL: { parser_lex(parser); pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); - return (pm_node_t *) parse_pattern_hash(parser, captures, key); + node = (pm_node_t *) parse_pattern_hash(parser, captures, key); + + if (!(flags & PM_PARSE_PATTERN_TOP)) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); + } + + return node; } case PM_TOKEN_USTAR_STAR: { node = parse_pattern_keyword_rest(parser, captures); - return (pm_node_t *) parse_pattern_hash(parser, captures, node); + node = (pm_node_t *) parse_pattern_hash(parser, captures, node); + + if (!(flags & PM_PARSE_PATTERN_TOP)) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); + } + + return node; } case PM_TOKEN_USTAR: { - if (top_pattern) { + if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) { parser_lex(parser); node = (pm_node_t *) parse_pattern_rest(parser, captures); leading_rest = true; @@ -16174,7 +16759,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, bool top_pat return (pm_node_t *) parse_pattern_hash(parser, captures, node); } - if (top_pattern && match1(parser, PM_TOKEN_COMMA)) { + if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) { // If we have a comma, then we are now parsing either an array pattern or a // find pattern. We need to parse all of the patterns, put them into a big // list, and then determine which type of node we have. @@ -16316,6 +16901,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { // start with a single string content node. pm_string_t unescaped; pm_token_t content; + if (match1(parser, PM_TOKEN_EOF)) { unescaped = PM_STRING_EMPTY; content = not_provided(parser); @@ -16354,7 +16940,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { pm_node_list_free(&parts); } else if (accept1(parser, PM_TOKEN_LABEL_END) && !state_is_arg_labeled) { - node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped)); + node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)); } else if (match1(parser, PM_TOKEN_EOF)) { pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF); node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); @@ -16378,9 +16964,21 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); pm_node_flag_set(node, parse_unescaped_encoding(parser)); - expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF); + + // Kind of odd behavior, but basically if we have an + // unterminated string and it ends in a newline, we back up one + // character so that the error message is on the last line of + // content in the string. + if (!accept1(parser, PM_TOKEN_STRING_END)) { + const uint8_t *location = parser->previous.end; + if (location > parser->start && location[-1] == '\n') location--; + pm_parser_err(parser, location, location, PM_ERR_STRING_LITERAL_EOF); + + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } } else if (accept1(parser, PM_TOKEN_LABEL_END)) { - node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped)); + node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)); } else { // If we get here, then we have interpolation so we'll need // to create a string or symbol node with interpolation. @@ -16462,11 +17060,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { pm_token_t bounds = not_provided(parser); pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, &bounds, NULL, &bounds); - pm_interpolated_string_node_append(parser, container, current); + pm_interpolated_string_node_append(container, current); current = (pm_node_t *) container; } - pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, node); + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, node); } } @@ -16485,6 +17083,11 @@ pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, pm_token_type_human(parser->previous.type)); break; } + case PM_ERR_HASH_VALUE: + case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, pm_token_type_human(parser->current.type)); + break; + } case PM_ERR_UNARY_RECEIVER: { const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_type_human(parser->current.type)); PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, human, parser->previous.start[0]); @@ -16711,13 +17314,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } element = (pm_node_t *) pm_keyword_hash_node_create(parser); - pm_static_literals_t literals = { 0 }; + pm_static_literals_t hash_keys = { 0 }; if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) { - parse_assocs(parser, &literals, element); + parse_assocs(parser, &hash_keys, element); } - pm_static_literals_free(&literals); + pm_static_literals_free(&hash_keys); parsed_bare_hash = true; } else { element = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_ARRAY_EXPRESSION); @@ -16728,8 +17331,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser); - pm_static_literals_t literals = { 0 }; - pm_hash_key_static_literals_add(parser, &literals, element); + pm_static_literals_t hash_keys = { 0 }; + pm_hash_key_static_literals_add(parser, &hash_keys, element); pm_token_t operator; if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) { @@ -16744,10 +17347,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b element = (pm_node_t *) hash; if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) { - parse_assocs(parser, &literals, element); + parse_assocs(parser, &hash_keys, element); } - pm_static_literals_free(&literals); + pm_static_literals_free(&hash_keys); parsed_bare_hash = true; } } @@ -16841,7 +17444,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b return (pm_node_t *) multi_target; } - return parse_target_validate(parser, (pm_node_t *) multi_target); + return parse_target_validate(parser, (pm_node_t *) multi_target, false); } // If we have a single statement and are ending on a right parenthesis @@ -16862,7 +17465,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we didn't find a terminator and we didn't find a right // parenthesis, then this is a syntax error. - if (!terminator_found) { + if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) { PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type)); } @@ -16891,7 +17494,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break; } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { break; - } else { + } else if (!match1(parser, PM_TOKEN_EOF)) { + // If we're at the end of the file, then we're going to add + // an error after this for the ) anyway. PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type)); } } @@ -16907,14 +17512,30 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous); } case PM_TOKEN_BRACE_LEFT: { + // If we were passed a current_hash_keys via the parser, then that + // means we're already parsing a hash and we want to share the set + // of hash keys with this inner hash we're about to parse for the + // sake of warnings. We'll set it to NULL after we grab it to make + // sure subsequent expressions don't use it. Effectively this is a + // way of getting around passing it to every call to + // parse_expression. + pm_static_literals_t *current_hash_keys = parser->current_hash_keys; + parser->current_hash_keys = NULL; + pm_accepts_block_stack_push(parser, true); parser_lex(parser); pm_hash_node_t *node = pm_hash_node_create(parser, &parser->previous); - pm_static_literals_t literals = { 0 }; if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) { - parse_assocs(parser, &literals, (pm_node_t *) node); + if (current_hash_keys != NULL) { + parse_assocs(parser, current_hash_keys, (pm_node_t *) node); + } else { + pm_static_literals_t hash_keys = { 0 }; + parse_assocs(parser, &hash_keys, (pm_node_t *) node); + pm_static_literals_free(&hash_keys); + } + accept1(parser, PM_TOKEN_NEWLINE); } @@ -16922,7 +17543,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM); pm_hash_node_closing_loc_set(node, &parser->previous); - pm_static_literals_free(&literals); return (pm_node_t *) node; } case PM_TOKEN_CHARACTER_LITERAL: { @@ -16987,12 +17607,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_UCOLON_COLON: { parser_lex(parser); - pm_token_t delimiter = parser->previous; - expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - pm_node_t *constant = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); - pm_node_t *node = (pm_node_t *)pm_constant_path_node_create(parser, NULL, &delimiter, constant); + expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + pm_node_t *node = (pm_node_t *) pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous); if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX); @@ -17152,8 +17770,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) { // If we get here, then we have an empty heredoc. We'll create // an empty content token and return an empty string node. - lex_mode_pop(parser); - expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM); + expect1_heredoc_term(parser, lex_mode); pm_token_t content = parse_strings_empty_content(parser->previous.start); if (quote == PM_HEREDOC_QUOTE_BACKTICK) { @@ -17194,8 +17811,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } node = (pm_node_t *) cast; - lex_mode_pop(parser); - expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM); + expect1_heredoc_term(parser, lex_mode); } else { // If we get here, then we have multiple parts in the heredoc, // so we'll need to create an interpolated string node to hold @@ -17217,20 +17833,18 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening); cast->parts = parts; - lex_mode_pop(parser); - expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM); - + expect1_heredoc_term(parser, lex_mode); pm_interpolated_xstring_node_closing_set(cast, &parser->previous); + cast->base.location = cast->opening_loc; node = (pm_node_t *) cast; } else { pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening); pm_node_list_free(&parts); - lex_mode_pop(parser); - expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM); - + expect1_heredoc_term(parser, lex_mode); pm_interpolated_string_node_closing_set(cast, &parser->previous); + cast->base.location = cast->opening_loc; node = (pm_node_t *) cast; } @@ -17451,7 +18065,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t in_keyword = parser->previous; pm_constant_id_list_t captures = { 0 }; - pm_node_t *pattern = parse_pattern(parser, &captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_IN); + pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_IN); parser->pattern_matching_newlines = previous_pattern_matching_newlines; pm_constant_id_list_free(&captures); @@ -17480,7 +18094,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b then_keyword = not_provided(parser); } } else { - expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER); + expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_IN_DELIMITER); then_keyword = parser->previous; } @@ -17934,7 +18548,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b lex_state_set(parser, PM_LEX_STATE_BEG); parser->command_start = true; - expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_DEF_PARAMS_TERM_PAREN); + if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_type_human(parser->current.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + rparen = parser->previous; break; } @@ -18132,7 +18751,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match1(parser, PM_TOKEN_COMMA)) { index = parse_targets(parser, index, PM_BINDING_POWER_INDEX); } else { - index = parse_target(parser, index); + index = parse_target(parser, index, false); } context_pop(parser); @@ -18254,9 +18873,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t double_colon = parser->previous; expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - pm_node_t *constant = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); - - constant_path = (pm_node_t *) pm_constant_path_node_create(parser, constant_path, &double_colon, constant); + constant_path = (pm_node_t *) pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous); } // Here we retrieve the name of the module. If it wasn't a constant, @@ -18636,15 +19253,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we hit string content and the current node is // an interpolated string, then we need to append // the string content to the list of child nodes. - pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string); + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, string); } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { // If we hit string content and the current node is // a string node, then we need to convert the // current node into an interpolated string and add // the string content to the list of child nodes. pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); - pm_interpolated_string_node_append(parser, interpolated, current); - pm_interpolated_string_node_append(parser, interpolated, string); + pm_interpolated_string_node_append(interpolated, current); + pm_interpolated_string_node_append(interpolated, string); current = (pm_node_t *) interpolated; } else { assert(false && "unreachable"); @@ -18669,7 +19286,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); - pm_interpolated_string_node_append(parser, interpolated, current); + pm_interpolated_string_node_append(interpolated, current); current = (pm_node_t *) interpolated; } else { // If we hit an embedded variable and the current @@ -18678,7 +19295,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser); - pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part); + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part); break; } case PM_TOKEN_EMBEXPR_BEGIN: { @@ -18698,7 +19315,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); - pm_interpolated_string_node_append(parser, interpolated, current); + pm_interpolated_string_node_append(interpolated, current); current = (pm_node_t *) interpolated; } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { // If we hit an embedded expression and the current @@ -18709,7 +19326,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser); - pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part); + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part); break; } default: @@ -18785,6 +19402,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped); + + if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + // This is extremely strange, but the first string part of a + // regular expression will always be tagged as binary if we + // are in a US-ASCII file, no matter its contents. + pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING); + } + pm_interpolated_regular_expression_node_append(interpolated, part); } else { // If the first part of the body of the regular expression is not a @@ -18913,7 +19538,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match1(parser, PM_TOKEN_COMMA)) { return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX); } else { - return parse_target_validate(parser, splat); + return parse_target_validate(parser, splat, true); } } case PM_TOKEN_BANG: { @@ -19259,39 +19884,6 @@ parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const } /** - * Returns true if the name of the capture group is a valid local variable that - * can be written to. - */ -static bool -parse_regular_expression_named_capture(pm_parser_t *parser, const uint8_t *source, size_t length) { - if (length == 0) { - return false; - } - - // First ensure that it starts with a valid identifier starting character. - size_t width = char_is_identifier_start(parser, source); - if (!width) { - return false; - } - - // Next, ensure that it's not an uppercase character. - if (parser->encoding_changed) { - if (parser->encoding->isupper_char(source, (ptrdiff_t) length)) return false; - } else { - if (pm_encoding_utf_8_isupper_char(source, (ptrdiff_t) length)) return false; - } - - // Next, iterate through all of the bytes of the string to ensure that they - // are all valid identifier characters. - const uint8_t *cursor = source + width; - while (cursor < source + length && (width = char_is_identifier(parser, cursor))) { - cursor += width; - } - - return cursor == source + length; -} - -/** * Potentially change a =~ with a regular expression with named captures into a * match write node. */ @@ -19317,7 +19909,7 @@ parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t * // If the name of the capture group isn't a valid identifier, we do // not add it to the local table. - if (!parse_regular_expression_named_capture(parser, source, length)) continue; + if (!pm_slice_is_valid_local(parser, source, source + length)) continue; if (content->type == PM_STRING_SHARED) { // If the unescaped string is a slice of the source, then we can @@ -19775,7 +20367,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // In this case we have an operator but we don't know what it's for. // We need to treat it as an error. For now, we'll mark it as an error // and just skip right past it. - pm_parser_err_previous(parser, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_type_human(parser->current.type)); return node; } } @@ -20046,8 +20638,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t path = (pm_node_t *) pm_call_node_call_create(parser, node, &delimiter, &message, &arguments); } else { // Otherwise, this is a constant path. That would look like Foo::Bar. - pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); - path = (pm_node_t *)pm_constant_path_node_create(parser, node, &delimiter, child); + path = (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); } // If this is followed by a comma then it is a multiple assignment. @@ -20086,9 +20677,8 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &delimiter, &arguments); } default: { - pm_parser_err_token(parser, &delimiter, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - pm_node_t *child = (pm_node_t *) pm_missing_node_create(parser, delimiter.start, delimiter.end); - return (pm_node_t *)pm_constant_path_node_create(parser, node, &delimiter, child); + expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + return (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); } } } @@ -20159,7 +20749,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_constant_id_list_t captures = { 0 }; - pm_node_t *pattern = parse_pattern(parser, &captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_IN); + pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_IN); parser->pattern_matching_newlines = previous_pattern_matching_newlines; pm_constant_id_list_free(&captures); @@ -20176,7 +20766,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_constant_id_list_t captures = { 0 }; - pm_node_t *pattern = parse_pattern(parser, &captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET); + pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET); parser->pattern_matching_newlines = previous_pattern_matching_newlines; pm_constant_id_list_free(&captures); @@ -20189,6 +20779,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } } +#undef PM_PARSE_PATTERN_SINGLE +#undef PM_PARSE_PATTERN_TOP +#undef PM_PARSE_PATTERN_MULTI + /** * Parse an expression at the given point of the parser using the given binding * power to parse subsequent chains. If this function finds a syntax error, it @@ -20997,6 +21591,7 @@ typedef struct { #define PM_COLOR_GRAY "\033[38;5;102m" #define PM_COLOR_RED "\033[1;31m" #define PM_COLOR_RESET "\033[m" +#define PM_ERROR_TRUNCATE 30 static inline pm_error_t * pm_parser_errors_format_sort(const pm_parser_t *parser, const pm_list_t *error_list, const pm_newline_list_t *newline_list) { @@ -21051,7 +21646,7 @@ pm_parser_errors_format_sort(const pm_parser_t *parser, const pm_list_t *error_l } static inline void -pm_parser_errors_format_line(const pm_parser_t *parser, const pm_newline_list_t *newline_list, const char *number_prefix, int32_t line, pm_buffer_t *buffer) { +pm_parser_errors_format_line(const pm_parser_t *parser, const pm_newline_list_t *newline_list, const char *number_prefix, int32_t line, uint32_t column_start, uint32_t column_end, pm_buffer_t *buffer) { int32_t line_delta = line - parser->start_line; assert(line_delta >= 0); @@ -21068,9 +21663,25 @@ pm_parser_errors_format_line(const pm_parser_t *parser, const pm_newline_list_t } pm_buffer_append_format(buffer, number_prefix, line); + + // Here we determine if we should truncate the end of the line. + bool truncate_end = false; + if ((column_end != 0) && ((end - (start + column_end)) >= PM_ERROR_TRUNCATE)) { + end = start + column_end + PM_ERROR_TRUNCATE; + truncate_end = true; + } + + // Here we determine if we should truncate the start of the line. + if (column_start >= PM_ERROR_TRUNCATE) { + pm_buffer_append_string(buffer, "... ", 4); + start += column_start; + } + pm_buffer_append_string(buffer, (const char *) start, (size_t) (end - start)); - if (end == parser->end && end[-1] != '\n') { + if (truncate_end) { + pm_buffer_append_string(buffer, " ...\n", 5); + } else if (end == parser->end && end[-1] != '\n') { pm_buffer_append_string(buffer, "\n", 1); } } @@ -21185,6 +21796,7 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, // display the same line twice in case the errors are close enough in the // source. int32_t last_line = parser->start_line - 1; + uint32_t last_column_start = 0; const pm_encoding_t *encoding = parser->encoding; for (size_t index = 0; index < error_list->size; index++) { @@ -21199,11 +21811,11 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, } pm_buffer_append_string(buffer, " ", 2); - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 2, buffer); + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 2, 0, 0, buffer); } pm_buffer_append_string(buffer, " ", 2); - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 1, buffer); + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 1, 0, 0, buffer); } // If this is the first error or we're on a new line, then we'll display @@ -21214,7 +21826,17 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, } else { pm_buffer_append_string(buffer, "> ", 2); } - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line, buffer); + + last_column_start = error->column_start; + + // Find the maximum column end of all the errors on this line. + uint32_t column_end = error->column_end; + for (size_t next_index = index + 1; next_index < error_list->size; next_index++) { + if (errors[next_index].line != error->line) break; + if (errors[next_index].column_end > column_end) column_end = errors[next_index].column_end; + } + + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line, error->column_start, column_end, buffer); } const uint8_t *start = &parser->start[newline_list->offsets[error->line - start_line]]; @@ -21233,25 +21855,33 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, pm_buffer_append_string(buffer, error_format.blank_prefix, error_format.blank_prefix_length); size_t column = 0; - while (column < error->column_end) { - if (column < error->column_start) { - pm_buffer_append_byte(buffer, ' '); - } else { - const uint8_t caret = column == error->column_start ? '^' : '~'; + if (last_column_start >= PM_ERROR_TRUNCATE) { + pm_buffer_append_string(buffer, " ", 4); + column = last_column_start; + } - if (colorize) { - pm_buffer_append_string(buffer, PM_COLOR_RED, 7); - pm_buffer_append_byte(buffer, caret); - pm_buffer_append_string(buffer, PM_COLOR_RESET, 3); - } else { - pm_buffer_append_byte(buffer, caret); - } - } + while (column < error->column_start) { + pm_buffer_append_byte(buffer, ' '); size_t char_width = encoding->char_width(start + column, parser->end - (start + column)); column += (char_width == 0 ? 1 : char_width); } + if (colorize) pm_buffer_append_string(buffer, PM_COLOR_RED, 7); + pm_buffer_append_byte(buffer, '^'); + + size_t char_width = encoding->char_width(start + column, parser->end - (start + column)); + column += (char_width == 0 ? 1 : char_width); + + while (column < error->column_end) { + pm_buffer_append_byte(buffer, '~'); + + size_t char_width = encoding->char_width(start + column, parser->end - (start + column)); + column += (char_width == 0 ? 1 : char_width); + } + + if (colorize) pm_buffer_append_string(buffer, PM_COLOR_RESET, 3); + if (inline_messages) { pm_buffer_append_byte(buffer, ' '); assert(error->error != NULL); @@ -21269,12 +21899,12 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, if (next_line - last_line > 1) { pm_buffer_append_string(buffer, " ", 2); - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, buffer); + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer); } if (next_line - last_line > 1) { pm_buffer_append_string(buffer, " ", 2); - pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, buffer); + pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer); } } @@ -21282,6 +21912,7 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, xfree(errors); } +#undef PM_ERROR_TRUNCATE #undef PM_COLOR_GRAY #undef PM_COLOR_RED #undef PM_COLOR_RESET diff --git a/prism/static_literals.c b/prism/static_literals.c index 469bdfd5ea..08f61c81fa 100644 --- a/prism/static_literals.c +++ b/prism/static_literals.c @@ -1,5 +1,21 @@ #include "prism/static_literals.h" +/** + * A small struct used for passing around a subset of the information that is + * stored on the parser. We use this to avoid having static literals explicitly + * depend on the parser struct. + */ +typedef struct { + /** The list of newline offsets to use to calculate line numbers. */ + const pm_newline_list_t *newline_list; + + /** The line number that the parser starts on. */ + int32_t start_line; + + /** The name of the encoding that the parser is using. */ + const char *encoding_name; +} pm_static_literals_metadata_t; + static inline uint32_t murmur_scramble(uint32_t value) { value *= 0xcc9e2d51; @@ -48,7 +64,7 @@ murmur_hash(const uint8_t *key, size_t length) { * these hashes to look for duplicates. */ static uint32_t -node_hash(const pm_parser_t *parser, const pm_node_t *node) { +node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) { switch (PM_NODE_TYPE(node)) { case PM_INTEGER_NODE: { // Integers hash their value. @@ -68,7 +84,7 @@ node_hash(const pm_parser_t *parser, const pm_node_t *node) { } case PM_SOURCE_LINE_NODE: { // Source lines hash their line number. - const pm_line_column_t line_column = pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line); + const pm_line_column_t line_column = pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line); const int32_t *value = &line_column.line; return murmur_hash((const uint8_t *) value, sizeof(int32_t)); } @@ -82,14 +98,14 @@ node_hash(const pm_parser_t *parser, const pm_node_t *node) { // is stored as a subnode, we hash that node and then mix in the // fact that this is a rational node. const pm_node_t *numeric = ((const pm_rational_node_t *) node)->numeric; - return node_hash(parser, numeric) ^ murmur_scramble((uint32_t) node->type); + return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type); } case PM_IMAGINARY_NODE: { // Imaginaries hash their numeric value. Because their numeric value // is stored as a subnode, we hash that node and then mix in the // fact that this is an imaginary node. const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric; - return node_hash(parser, numeric) ^ murmur_scramble((uint32_t) node->type); + return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type); } case PM_STRING_NODE: { // Strings hash their value and mix in their flags so that different @@ -132,7 +148,7 @@ node_hash(const pm_parser_t *parser, const pm_node_t *node) { * and must be able to compare all node types that will be stored in this hash. */ static pm_node_t * -pm_node_hash_insert(pm_node_hash_t *hash, const pm_parser_t *parser, pm_node_t *node, int (*compare)(const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right)) { +pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *metadata, pm_node_t *node, int (*compare)(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right)) { // If we are out of space, we need to resize the hash. This will cause all // of the nodes to be rehashed and reinserted into the new hash. if (hash->size * 2 >= hash->capacity) { @@ -152,7 +168,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_parser_t *parser, pm_node_t * pm_node_t *node = hash->nodes[index]; if (node != NULL) { - uint32_t index = node_hash(parser, node) & mask; + uint32_t index = node_hash(metadata, node) & mask; new_nodes[index] = node; } } @@ -165,14 +181,14 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_parser_t *parser, pm_node_t * // Now, insert the node into the hash. uint32_t mask = hash->capacity - 1; - uint32_t index = node_hash(parser, node) & mask; + uint32_t index = node_hash(metadata, node) & mask; // We use linear probing to resolve collisions. This means that if the // current index is occupied, we will move to the next index and try again. // We are guaranteed that this will eventually find an empty slot because we // resize the hash when it gets too full. while (hash->nodes[index] != NULL) { - if (compare(parser, hash->nodes[index], node) == 0) break; + if (compare(metadata, hash->nodes[index], node) == 0) break; index = (index + 1) & mask; } @@ -203,7 +219,7 @@ pm_node_hash_free(pm_node_hash_t *hash) { * Return the integer value of the given node as an int64_t. */ static int64_t -pm_int64_value(const pm_parser_t *parser, const pm_node_t *node) { +pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) { switch (PM_NODE_TYPE(node)) { case PM_INTEGER_NODE: { const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value; @@ -213,7 +229,7 @@ pm_int64_value(const pm_parser_t *parser, const pm_node_t *node) { return integer->negative ? -value : value; } case PM_SOURCE_LINE_NODE: - return (int64_t) pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line; + return (int64_t) pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line; default: assert(false && "unreachable"); return 0; @@ -225,10 +241,10 @@ pm_int64_value(const pm_parser_t *parser, const pm_node_t *node) { * instances. */ static int -pm_compare_integer_nodes(const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) { +pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { if (PM_NODE_TYPE_P(left, PM_SOURCE_LINE_NODE) || PM_NODE_TYPE_P(right, PM_SOURCE_LINE_NODE)) { - int64_t left_value = pm_int64_value(parser, left); - int64_t right_value = pm_int64_value(parser, right); + int64_t left_value = pm_int64_value(metadata, left); + int64_t right_value = pm_int64_value(metadata, right); return PM_NUMERIC_COMPARISON(left_value, right_value); } @@ -241,7 +257,7 @@ pm_compare_integer_nodes(const pm_parser_t *parser, const pm_node_t *left, const * A comparison function for comparing two FloatNode instances. */ static int -pm_compare_float_nodes(PRISM_ATTRIBUTE_UNUSED const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) { +pm_compare_float_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { const double left_value = ((const pm_float_node_t *) left)->value; const double right_value = ((const pm_float_node_t *) right)->value; return PM_NUMERIC_COMPARISON(left_value, right_value); @@ -251,20 +267,20 @@ pm_compare_float_nodes(PRISM_ATTRIBUTE_UNUSED const pm_parser_t *parser, const p * A comparison function for comparing two nodes that have attached numbers. */ static int -pm_compare_number_nodes(const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) { +pm_compare_number_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { if (PM_NODE_TYPE(left) != PM_NODE_TYPE(right)) { return PM_NUMERIC_COMPARISON(PM_NODE_TYPE(left), PM_NODE_TYPE(right)); } switch (PM_NODE_TYPE(left)) { case PM_IMAGINARY_NODE: - return pm_compare_number_nodes(parser, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric); + return pm_compare_number_nodes(metadata, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric); case PM_RATIONAL_NODE: - return pm_compare_number_nodes(parser, ((const pm_rational_node_t *) left)->numeric, ((const pm_rational_node_t *) right)->numeric); + return pm_compare_number_nodes(metadata, ((const pm_rational_node_t *) left)->numeric, ((const pm_rational_node_t *) right)->numeric); case PM_INTEGER_NODE: - return pm_compare_integer_nodes(parser, left, right); + return pm_compare_integer_nodes(metadata, left, right); case PM_FLOAT_NODE: - return pm_compare_float_nodes(parser, left, right); + return pm_compare_float_nodes(metadata, left, right); default: assert(false && "unreachable"); return 0; @@ -293,7 +309,7 @@ pm_string_value(const pm_node_t *node) { * A comparison function for comparing two nodes that have attached strings. */ static int -pm_compare_string_nodes(PRISM_ATTRIBUTE_UNUSED const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) { +pm_compare_string_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { const pm_string_t *left_string = pm_string_value(left); const pm_string_t *right_string = pm_string_value(right); return pm_string_compare(left_string, right_string); @@ -303,7 +319,7 @@ pm_compare_string_nodes(PRISM_ATTRIBUTE_UNUSED const pm_parser_t *parser, const * A comparison function for comparing two RegularExpressionNode instances. */ static int -pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) { +pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) { const pm_regular_expression_node_t *left_regexp = (const pm_regular_expression_node_t *) left; const pm_regular_expression_node_t *right_regexp = (const pm_regular_expression_node_t *) right; @@ -319,23 +335,77 @@ pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_parser_t *pa * Add a node to the set of static literals. */ pm_node_t * -pm_static_literals_add(const pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) { +pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node) { switch (PM_NODE_TYPE(node)) { case PM_INTEGER_NODE: case PM_SOURCE_LINE_NODE: - return pm_node_hash_insert(&literals->integer_nodes, parser, node, pm_compare_integer_nodes); + return pm_node_hash_insert( + &literals->integer_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + pm_compare_integer_nodes + ); case PM_FLOAT_NODE: - return pm_node_hash_insert(&literals->float_nodes, parser, node, pm_compare_float_nodes); + return pm_node_hash_insert( + &literals->float_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + pm_compare_float_nodes + ); case PM_RATIONAL_NODE: case PM_IMAGINARY_NODE: - return pm_node_hash_insert(&literals->number_nodes, parser, node, pm_compare_number_nodes); + return pm_node_hash_insert( + &literals->number_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + pm_compare_number_nodes + ); case PM_STRING_NODE: case PM_SOURCE_FILE_NODE: - return pm_node_hash_insert(&literals->string_nodes, parser, node, pm_compare_string_nodes); + return pm_node_hash_insert( + &literals->string_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + pm_compare_string_nodes + ); case PM_REGULAR_EXPRESSION_NODE: - return pm_node_hash_insert(&literals->regexp_nodes, parser, node, pm_compare_regular_expression_nodes); + return pm_node_hash_insert( + &literals->regexp_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + pm_compare_regular_expression_nodes + ); case PM_SYMBOL_NODE: - return pm_node_hash_insert(&literals->symbol_nodes, parser, node, pm_compare_string_nodes); + return pm_node_hash_insert( + &literals->symbol_nodes, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = NULL + }, + node, + pm_compare_string_nodes + ); case PM_TRUE_NODE: { pm_node_t *duplicated = literals->true_node; literals->true_node = node; @@ -435,8 +505,8 @@ pm_rational_inspect(pm_buffer_t *buffer, pm_rational_node_t *node) { /** * Create a string-based representation of the given static literal. */ -PRISM_EXPORTED_FUNCTION void -pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node) { +static inline void +pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_metadata_t *metadata, const pm_node_t *node) { switch (PM_NODE_TYPE(node)) { case PM_FALSE_NODE: pm_buffer_append_string(buffer, "false", 5); @@ -473,7 +543,7 @@ pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric; pm_buffer_append_string(buffer, "(0", 2); if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+'); - pm_static_literal_inspect(buffer, parser, numeric); + pm_static_literal_inspect_node(buffer, metadata, numeric); if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) pm_buffer_append_byte(buffer, '*'); pm_buffer_append_string(buffer, "i)", 2); break; @@ -490,7 +560,7 @@ pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const switch (PM_NODE_TYPE(numeric)) { case PM_INTEGER_NODE: pm_buffer_append_byte(buffer, '('); - pm_static_literal_inspect(buffer, parser, numeric); + pm_static_literal_inspect_node(buffer, metadata, numeric); pm_buffer_append_string(buffer, "/1)", 3); break; case PM_FLOAT_NODE: @@ -517,7 +587,7 @@ pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const break; } case PM_SOURCE_ENCODING_NODE: - pm_buffer_append_format(buffer, "#<Encoding:%s>", parser->encoding->name); + pm_buffer_append_format(buffer, "#<Encoding:%s>", metadata->encoding_name); break; case PM_SOURCE_FILE_NODE: { const pm_string_t *filepath = &((const pm_source_file_node_t *) node)->filepath; @@ -527,7 +597,7 @@ pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const break; } case PM_SOURCE_LINE_NODE: - pm_buffer_append_format(buffer, "%d", pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line); + pm_buffer_append_format(buffer, "%d", pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line); break; case PM_STRING_NODE: { const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped; @@ -550,3 +620,19 @@ pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const break; } } + +/** + * Create a string-based representation of the given static literal. + */ +PRISM_EXPORTED_FUNCTION void +pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node) { + pm_static_literal_inspect_node( + buffer, + &(pm_static_literals_metadata_t) { + .newline_list = newline_list, + .start_line = start_line, + .encoding_name = encoding_name + }, + node + ); +} diff --git a/prism/static_literals.h b/prism/static_literals.h index dd1f2e7f84..72706054c1 100644 --- a/prism/static_literals.h +++ b/prism/static_literals.h @@ -8,8 +8,7 @@ #include "prism/defines.h" #include "prism/ast.h" -#include "prism/node.h" -#include "prism/parser.h" +#include "prism/util/pm_newline_list.h" #include <assert.h> #include <stdbool.h> @@ -92,12 +91,13 @@ typedef struct { /** * Add a node to the set of static literals. * - * @param parser The parser that created the node. + * @param newline_list The list of newline offsets to use to calculate lines. + * @param start_line The line number that the parser starts on. * @param literals The set of static literals to add the node to. * @param node The node to add to the set. * @return A pointer to the node that is being overwritten, if there is one. */ -pm_node_t * pm_static_literals_add(const pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node); +pm_node_t * pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node); /** * Free the internal memory associated with the given static literals set. @@ -110,9 +110,11 @@ void pm_static_literals_free(pm_static_literals_t *literals); * Create a string-based representation of the given static literal. * * @param buffer The buffer to write the string to. - * @param parser The parser that created the node. + * @param newline_list The list of newline offsets to use to calculate lines. + * @param start_line The line number that the parser starts on. + * @param encoding_name The name of the encoding of the source being parsed. * @param node The node to create a string representation of. */ -PRISM_EXPORTED_FUNCTION void pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node); +PRISM_EXPORTED_FUNCTION void pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node); #endif diff --git a/prism/templates/ext/prism/api_node.c.erb b/prism/templates/ext/prism/api_node.c.erb index 0e8aaae322..0e3e4d63cc 100644 --- a/prism/templates/ext/prism/api_node.c.erb +++ b/prism/templates/ext/prism/api_node.c.erb @@ -76,8 +76,7 @@ pm_source_new(const pm_parser_t *parser, rb_encoding *encoding) { rb_ary_push(offsets, ULONG2NUM(parser->newline_list.offsets[index])); } - VALUE source_argv[] = { source_string, LONG2NUM(parser->start_line), offsets }; - return rb_class_new_instance(3, source_argv, rb_cPrismSource); + return rb_funcall(rb_cPrismSource, rb_intern("for"), 3, source_string, LONG2NUM(parser->start_line), offsets); } typedef struct pm_node_stack_node { @@ -108,21 +107,21 @@ pm_node_stack_pop(pm_node_stack_node_t **stack) { VALUE pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encoding, VALUE source) { - ID *constants = xcalloc(parser->constant_pool.size, sizeof(ID)); + VALUE constants = rb_ary_new_capa(parser->constant_pool.size); for (uint32_t index = 0; index < parser->constant_pool.size; index++) { pm_constant_t *constant = &parser->constant_pool.constants[index]; int state = 0; VALUE string = rb_enc_str_new((const char *) constant->start, constant->length, encoding); - ID value = rb_protect(rb_intern_str, string, &state); + VALUE value = rb_protect(rb_str_intern, string, &state); if (state != 0) { - value = rb_intern_const("?"); + value = ID2SYM(rb_intern_const("?")); rb_set_errinfo(Qnil); } - constants[index] = value; + rb_ary_push(constants, value); } pm_node_stack_node_t *node_stack = NULL; @@ -197,15 +196,15 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi <%- when Prism::Template::ConstantField -%> #line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>" assert(cast-><%= field.name %> != 0); - argv[<%= index %>] = rb_id2sym(constants[cast-><%= field.name %> - 1]); + argv[<%= index %>] = RARRAY_AREF(constants, cast-><%= field.name %> - 1); <%- when Prism::Template::OptionalConstantField -%> - argv[<%= index %>] = cast-><%= field.name %> == 0 ? Qnil : rb_id2sym(constants[cast-><%= field.name %> - 1]); + argv[<%= index %>] = cast-><%= field.name %> == 0 ? Qnil : RARRAY_AREF(constants, cast-><%= field.name %> - 1); <%- when Prism::Template::ConstantListField -%> #line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>" argv[<%= index %>] = rb_ary_new_capa(cast-><%= field.name %>.size); for (size_t index = 0; index < cast-><%= field.name %>.size; index++) { assert(cast-><%= field.name %>.ids[index] != 0); - rb_ary_push(argv[<%= index %>], rb_id2sym(constants[cast-><%= field.name %>.ids[index] - 1])); + rb_ary_push(argv[<%= index %>], RARRAY_AREF(constants, cast-><%= field.name %>.ids[index] - 1)); } <%- when Prism::Template::LocationField -%> #line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>" @@ -246,9 +245,7 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi } } - VALUE result = rb_ary_pop(value_stack); - xfree(constants); - return result; + return rb_ary_pop(value_stack); } void diff --git a/prism/templates/lib/prism/dsl.rb.erb b/prism/templates/lib/prism/dsl.rb.erb index 8dbb540952..eff0d1c4fc 100644 --- a/prism/templates/lib/prism/dsl.rb.erb +++ b/prism/templates/lib/prism/dsl.rb.erb @@ -2,7 +2,7 @@ module Prism # The DSL module provides a set of methods that can be used to create prism # nodes in a more concise manner. For example, instead of writing: # - # source = Prism::Source.new("[1]") + # source = Prism::Source.for("[1]") # # Prism::ArrayNode.new( # [ @@ -20,7 +20,7 @@ module Prism # # you could instead write: # - # source = Prism::Source.new("[1]") + # source = Prism::Source.for("[1]") # # ArrayNode( # IntegerNode(Prism::IntegerBaseFlags::DECIMAL, 1, Location(source, 1, 1)), source), diff --git a/prism/templates/lib/prism/inspect_visitor.rb.erb b/prism/templates/lib/prism/inspect_visitor.rb.erb new file mode 100644 index 0000000000..9328da636b --- /dev/null +++ b/prism/templates/lib/prism/inspect_visitor.rb.erb @@ -0,0 +1,132 @@ +module Prism + # This visitor is responsible for composing the strings that get returned by + # the various #inspect methods defined on each of the nodes. + class InspectVisitor < Visitor + # Most of the time, we can simply pass down the indent to the next node. + # However, when we are inside a list we want some extra special formatting + # when we hit an element in that list. In this case, we have a special + # command that replaces the subsequent indent with the given value. + class Replace # :nodoc: + attr_reader :value + + def initialize(value) + @value = value + end + end + + private_constant :Replace + + # The current prefix string. + attr_reader :indent + + # The list of commands that we need to execute in order to compose the + # final string. + attr_reader :commands + + # Initializes a new instance of the InspectVisitor. + def initialize(indent = +"") + @indent = indent + @commands = [] + end + + # Compose an inspect string for the given node. + def self.compose(node) + visitor = new + node.accept(visitor) + visitor.compose + end + + # Compose the final string. + def compose + buffer = +"" + replace = nil + + until commands.empty? + # @type var command: String | node | Replace + # @type var indent: String + command, indent = *commands.shift + + case command + when String + buffer << (replace || indent) + buffer << command + replace = nil + when Node + visitor = InspectVisitor.new(indent) + command.accept(visitor) + @commands = [*visitor.commands, *@commands] + when Replace + replace = command.value + else + raise "Unknown command: #{command.inspect}" + end + end + + buffer + end + <%- nodes.each do |node| -%> + + # Inspect a <%= node.name %> node. + def visit_<%= node.human %>(node) + commands << [inspect_node(<%= node.name.inspect %>, node), indent] + <%- node.fields.each_with_index do |field, index| -%> + <%- pointer = index == node.fields.length - 1 ? "└── " : "├── " -%> + <%- preadd = index == node.fields.length - 1 ? " " : "│ " -%> + <%- case field -%> + <%- when Prism::Template::NodeListField -%> + commands << ["<%= pointer %><%= field.name %>: (length: #{(<%= field.name %> = node.<%= field.name %>).length})\n", indent] + if <%= field.name %>.any? + <%= field.name %>[0...-1].each do |child| + commands << [Replace.new("#{indent}<%= preadd %>├── "), indent] + commands << [child, "#{indent}<%= preadd %>│ "] + end + commands << [Replace.new("#{indent}<%= preadd %>└── "), indent] + commands << [<%= field.name %>[-1], "#{indent}<%= preadd %> "] + end + <%- when Prism::Template::NodeField -%> + commands << ["<%= pointer %><%= field.name %>:\n", indent] + commands << [node.<%= field.name %>, "#{indent}<%= preadd %>"] + <%- when Prism::Template::OptionalNodeField -%> + if (<%= field.name %> = node.<%= field.name %>).nil? + commands << ["<%= pointer %><%= field.name %>: ∅\n", indent] + else + commands << ["<%= pointer %><%= field.name %>:\n", indent] + commands << [<%= field.name %>, "#{indent}<%= preadd %>"] + end + <%- when Prism::Template::ConstantField, Prism::Template::ConstantListField, Prism::Template::StringField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField, Prism::Template::DoubleField -%> + commands << ["<%= pointer %><%= field.name %>: #{node.<%= field.name %>.inspect}\n", indent] + <%- when Prism::Template::OptionalConstantField -%> + if (<%= field.name %> = node.<%= field.name %>).nil? + commands << ["<%= pointer %><%= field.name %>: ∅\n", indent] + else + commands << ["<%= pointer %><%= field.name %>: #{<%= field.name %>.inspect}\n", indent] + end + <%- when Prism::Template::FlagsField -%> + <%- flag = flags.find { |flag| flag.name == field.kind }.tap { |flag| raise unless flag } -%> + flags = [<%= flag.values.map { |value| "(\"#{value.name.downcase}\" if node.#{value.name.downcase}?)" }.join(", ") %>].compact + commands << ["<%= pointer %><%= field.name %>: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField -%> + commands << ["<%= pointer %><%= field.name %>: #{inspect_location(node.<%= field.name %>)}\n", indent] + <%- end -%> + <%- end -%> + end + <%- end -%> + + private + + # Compose a header for the given node. + def inspect_node(name, node) + location = node.location + "@ #{name} (location: (#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}))\n" + end + + # Compose a string representing the given inner location field. + def inspect_location(location) + if location + "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}) = #{location.slice.inspect}" + else + "∅" + end + end + end +end diff --git a/prism/templates/lib/prism/node.rb.erb b/prism/templates/lib/prism/node.rb.erb index 6b5a285315..f0ce226def 100644 --- a/prism/templates/lib/prism/node.rb.erb +++ b/prism/templates/lib/prism/node.rb.erb @@ -28,23 +28,27 @@ module Prism location.is_a?(Location) ? location.end_offset : ((location >> 32) + (location & 0xFFFFFFFF)) end - def newline? # :nodoc: - @newline ? true : false + # Returns all of the lines of the source code associated with this node. + def source_lines + location.source_lines end - def set_newline_flag(newline_marked) # :nodoc: - line = location.start_line - unless newline_marked[line] - newline_marked[line] = true - @newline = true - end - end + # An alias for source_lines, used to mimic the API from + # RubyVM::AbstractSyntaxTree to make it easier to migrate. + alias script_lines source_lines # Slice the location of the node from the source. def slice location.slice end + # Slice the location of the node from the source, starting at the beginning + # of the line that the location starts on, ending at the end of the line + # that the location ends on. + def slice_lines + location.slice_lines + end + # Similar to inspect, but respects the current level of indentation given by # the pretty print object. def pretty_print(q) @@ -60,6 +64,43 @@ module Prism DotVisitor.new.tap { |visitor| accept(visitor) }.to_dot end + # Returns a list of nodes that are descendants of this node that contain the + # given line and column. This is useful for locating a node that is selected + # based on the line and column of the source code. + # + # Important to note is that the column given to this method should be in + # bytes, as opposed to characters or code units. + def tunnel(line, column) + queue = [self] #: Array[Prism::node] + result = [] + + while (node = queue.shift) + result << node + + node.compact_child_nodes.each do |child_node| + child_location = child_node.location + + start_line = child_location.start_line + end_line = child_location.end_line + + if start_line == end_line + if line == start_line && column >= child_location.start_column && column < child_location.end_column + queue << child_node + break + end + elsif (line == start_line && column >= child_location.start_column) || (line == end_line && column < child_location.end_column) + queue << child_node + break + elsif line > start_line && line < end_line + queue << child_node + break + end + end + end + + result + end + # Returns a list of the fields that exist for this node class. Fields # describe the structure of the node. This kind of reflection is useful for # things like recursively visiting each node _and_ field in the tree. @@ -110,7 +151,7 @@ module Prism end # Returns a string representation of the node. - def inspect(inspector = NodeInspector.new) + def inspect raise NoMethodError, "undefined method `inspect' for #{inspect}" end @@ -128,7 +169,6 @@ module Prism # def initialize: (<%= (node.fields.map { |field| "#{field.rbs_class} #{field.name}" } + ["Location location"]).join(", ") %>) -> void def initialize(source, <%= (node.fields.map(&:name) + ["location"]).join(", ") %>) @source = source - @newline = false @location = location <%- node.fields.each do |field| -%> <%- if Prism::Template::CHECK_FIELD_KIND && field.respond_to?(:check_field_kind) -%> @@ -142,25 +182,6 @@ module Prism def accept(visitor) visitor.visit_<%= node.human %>(self) end - <%- if node.newline == false -%> - - def set_newline_flag(newline_marked) # :nodoc: - # Never mark <%= node.name %> with a newline flag, mark children instead - end - <%- elsif node.newline.is_a?(String) -%> - - def set_newline_flag(newline_marked) # :nodoc: - <%- field = node.fields.find { |f| f.name == node.newline } or raise node.newline -%> - <%- case field -%> - <%- when Prism::Template::NodeField -%> - <%= field.name %>.set_newline_flag(newline_marked) - <%- when Prism::Template::NodeListField -%> - first = <%= field.name %>.first - first.set_newline_flag(newline_marked) if first - <%- else raise field.class.name -%> - <%- end -%> - end - <%- end -%> # def child_nodes: () -> Array[nil | Node] def child_nodes @@ -219,10 +240,10 @@ module Prism def deconstruct_keys(keys) { <%= (node.fields.map { |field| "#{field.name}: #{field.name}" } + ["location: location"]).join(", ") %> } end - <%- node.fields.each do |field| -%> + <%- if field.comment.nil? -%> - # <%= "private " if field.is_a?(Prism::Template::FlagsField) %>attr_reader <%= field.name %>: <%= field.rbs_class %> + # <%= "protected " if field.is_a?(Prism::Template::FlagsField) %>attr_reader <%= field.name %>: <%= field.rbs_class %> <%- else -%> <%- field.each_comment_line do |line| -%> #<%= line %> @@ -248,9 +269,8 @@ module Prism end end <%- else -%> - attr_reader :<%= field.name -%><%= "\n private :#{field.name}" if field.is_a?(Prism::Template::FlagsField) %> + attr_reader :<%= field.name -%><%= "\n protected :#{field.name}" if field.is_a?(Prism::Template::FlagsField) %> <%- end -%> - <%- end -%> <%- node.fields.each do |field| -%> <%- case field -%> @@ -281,45 +301,9 @@ module Prism <%- end -%> <%- end -%> - # def inspect(NodeInspector inspector) -> String - def inspect(inspector = NodeInspector.new) - inspector << inspector.header(self) - <%- node.fields.each_with_index do |field, index| -%> - <%- pointer, preadd = index == node.fields.length - 1 ? ["└── ", " "] : ["├── ", "│ "] -%> - <%- case field -%> - <%- when Prism::Template::NodeListField -%> - inspector << "<%= pointer %><%= field.name %>: #{inspector.list("#{inspector.prefix}<%= preadd %>", <%= field.name %>)}" - <%- when Prism::Template::ConstantListField -%> - inspector << "<%= pointer %><%= field.name %>: #{<%= field.name %>.inspect}\n" - <%- when Prism::Template::NodeField -%> - inspector << "<%= pointer %><%= field.name %>:\n" - inspector << inspector.child_node(<%= field.name %>, "<%= preadd %>") - <%- when Prism::Template::OptionalNodeField -%> - if (<%= field.name %> = self.<%= field.name %>).nil? - inspector << "<%= pointer %><%= field.name %>: ∅\n" - else - inspector << "<%= pointer %><%= field.name %>:\n" - inspector << <%= field.name %>.inspect(inspector.child_inspector("<%= preadd %>")).delete_prefix(inspector.prefix) - end - <%- when Prism::Template::ConstantField, Prism::Template::StringField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField, Prism::Template::DoubleField -%> - inspector << "<%= pointer %><%= field.name %>: #{<%= field.name %>.inspect}\n" - <%- when Prism::Template::OptionalConstantField -%> - if (<%= field.name %> = self.<%= field.name %>).nil? - inspector << "<%= pointer %><%= field.name %>: ∅\n" - else - inspector << "<%= pointer %><%= field.name %>: #{<%= field.name %>.inspect}\n" - end - <%- when Prism::Template::FlagsField -%> - <%- flag = flags.find { |flag| flag.name == field.kind }.tap { |flag| raise unless flag } -%> - flags = [<%= flag.values.map { |value| "(\"#{value.name.downcase}\" if #{value.name.downcase}?)" }.join(", ") %>].compact - inspector << "<%= pointer %><%= field.name %>: #{flags.empty? ? "∅" : flags.join(", ")}\n" - <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField -%> - inspector << "<%= pointer %><%= field.name %>: #{inspector.location(<%= field.name %>)}\n" - <%- else -%> - <%- raise -%> - <%- end -%> - <%- end -%> - inspector.to_str + # def inspect -> String + def inspect + InspectVisitor.compose(self) end # Sometimes you want to check an instance of a node against a list of @@ -349,6 +333,22 @@ module Prism def self.type :<%= node.human %> end + + # Implements case-equality for the node. This is effectively == but without + # comparing the value of locations. Locations are checked only for presence. + def ===(other) + other.is_a?(<%= node.name %>)<%= " &&" if node.fields.any? %> + <%- node.fields.each_with_index do |field, index| -%> + <%- if field.is_a?(Prism::Template::LocationField) || field.is_a?(Prism::Template::OptionalLocationField) -%> + (<%= field.name %>.nil? == other.<%= field.name %>.nil?)<%= " &&" if index != node.fields.length - 1 %> + <%- elsif field.is_a?(Prism::Template::NodeListField) || field.is_a?(Prism::Template::ConstantListField) -%> + (<%= field.name %>.length == other.<%= field.name %>.length) && + <%= field.name %>.zip(other.<%= field.name %>).all? { |left, right| left === right }<%= " &&" if index != node.fields.length - 1 %> + <%- else -%> + (<%= field.name %> === other.<%= field.name %>)<%= " &&" if index != node.fields.length - 1 %> + <%- end -%> + <%- end -%> + end end <%- end -%> <%- flags.each_with_index do |flag, flag_index| -%> diff --git a/prism/templates/lib/prism/reflection.rb.erb b/prism/templates/lib/prism/reflection.rb.erb index 13d1da33e8..3c1d61c6c1 100644 --- a/prism/templates/lib/prism/reflection.rb.erb +++ b/prism/templates/lib/prism/reflection.rb.erb @@ -65,14 +65,16 @@ module Prism class OptionalLocationField < Field end - # A uint8 field represents an unsigned 8-bit integer value on a node. It - # resolves to an Integer in Ruby. - class UInt8Field < Field + # An integer field represents an integer value. It is used to represent the + # value of an integer literal, the depth of local variables, and the number + # of a numbered reference. It resolves to an Integer in Ruby. + class IntegerField < Field end - # A uint32 field represents an unsigned 32-bit integer value on a node. It - # resolves to an Integer in Ruby. - class UInt32Field < Field + # A float field represents a double-precision floating point value. It is + # used exclusively to represent the value of a floating point literal. It + # resolves to a Float in Ruby. + class FloatField < Field end # A flags field represents a bitset of flags on a node. It resolves to an @@ -90,18 +92,6 @@ module Prism end end - # An integer field represents an arbitrarily-sized integer value. It is used - # exclusively to represent the value of an integer literal. It resolves to - # an Integer in Ruby. - class IntegerField < Field - end - - # A double field represents a double-precision floating point value. It is - # used exclusively to represent the value of a floating point literal. It - # resolves to a Float in Ruby. - class DoubleField < Field - end - # Returns the fields for the given node. def self.fields_for(node) case node.type @@ -127,17 +117,13 @@ module Prism "LocationField.new(:#{field.name})" when Prism::Template::OptionalLocationField "OptionalLocationField.new(:#{field.name})" - when Prism::Template::UInt8Field - "UInt8Field.new(:#{field.name})" - when Prism::Template::UInt32Field - "UInt32Field.new(:#{field.name})" + when Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField + "IntegerField.new(:#{field.name})" + when Prism::Template::DoubleField + "FloatField.new(:#{field.name})" when Prism::Template::FlagsField found = flags.find { |flag| flag.name == field.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } "FlagsField.new(:#{field.name}, [#{found.values.map { |value| ":#{value.name.downcase}?" }.join(", ")}])" - when Prism::Template::IntegerField - "IntegerField.new(:#{field.name})" - when Prism::Template::DoubleField - "DoubleField.new(:#{field.name})" else raise field.class.name end diff --git a/prism/templates/lib/prism/serialize.rb.erb b/prism/templates/lib/prism/serialize.rb.erb index 36d5d5432d..756821cf7d 100644 --- a/prism/templates/lib/prism/serialize.rb.erb +++ b/prism/templates/lib/prism/serialize.rb.erb @@ -1,5 +1,5 @@ require "stringio" -require_relative "polyfill/string" +require_relative "polyfill/unpack1" module Prism # A module responsible for deserializing parse results. @@ -10,7 +10,7 @@ module Prism # The minor version of prism that we are expecting to find in the serialized # strings. - MINOR_VERSION = 25 + MINOR_VERSION = 29 # The patch version of prism that we are expecting to find in the serialized # strings. @@ -19,7 +19,7 @@ module Prism # Deserialize the AST represented by the given string into a parse result. def self.load(input, serialized) input = input.dup - source = Source.new(input) + source = Source.for(input) loader = Loader.new(source, serialized) result = loader.load_result @@ -143,7 +143,7 @@ module Prism length = load_varuint lex_state = load_varuint location = Location.new(@source, start, length) - tokens << [Prism::Token.new(source, type, location.slice, location), lex_state] + tokens << [Token.new(source, type, location.slice, location), lex_state] end tokens @@ -158,7 +158,7 @@ module Prism tokens.each { |token,| token.value.force_encoding(encoding) } raise "Expected to consume all bytes while deserializing" unless @io.eof? - Prism::ParseResult.new(tokens, comments, magic_comments, data_loc, errors, warnings, @source) + LexResult.new(tokens, comments, magic_comments, data_loc, errors, warnings, @source) end def load_nodes @@ -177,7 +177,7 @@ module Prism def load_result node, comments, magic_comments, data_loc, errors, warnings = load_nodes - Prism::ParseResult.new(node, comments, magic_comments, data_loc, errors, warnings, @source) + ParseResult.new(node, comments, magic_comments, data_loc, errors, warnings, @source) end private diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index 42f8024551..d9e195e08f 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -91,22 +91,24 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_ARGUMENT_AFTER_BLOCK] = { "unexpected argument after a block argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES] = { "unexpected argument after `...`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_BARE_HASH] = { "unexpected bare hash argument", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_BLOCK_FORWARDING] = { "both a block argument and a forwarding argument; only one block is allowed", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_BLOCK_MULTI] = { "both block arg and actual block given; only one block is allowed", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_AMPERSAND] = { "unexpected `&`; anonymous block parameter is also used within block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_STAR] = { "unexpected `*`; anonymous rest parameter is also used within block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_STAR_STAR] = { "unexpected `**`; anonymous keyword rest parameter is also used within block", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_FORMAL_CLASS] = { "invalid formal argument; formal argument cannot be a class variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_FORMAL_CONSTANT] = { "invalid formal argument; formal argument cannot be a constant", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_FORMAL_GLOBAL] = { "invalid formal argument; formal argument cannot be a global variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_FORMAL_IVAR] = { "invalid formal argument; formal argument cannot be an instance variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_FORWARDING_UNBOUND] = { "unexpected `...` in an non-parenthesized call", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_IN] = { "unexpected `in` keyword in arguments", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_NO_FORWARDING_AMP] = { "unexpected `&`; no anonymous block parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND] = { "unexpected `&`; no anonymous block parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = { "unexpected ... when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = { "unexpected `*`; no anonymous rest parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR] = { "unexpected `**`; no anonymous keyword rest parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT] = { "unexpected `*` splat argument after a `**` keyword splat argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT] = { "unexpected `*` splat argument after a `*` splat argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_TERM_PAREN] = { "unexpected %s; expected a `)` to close the arguments", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_UNEXPECTED_BLOCK] = { "unexpected `{` after a method call without parenthesis", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_UNEXPECTED_BLOCK] = { "unexpected '{' after a method call without parenthesis", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARRAY_ELEMENT] = { "expected an element for the array", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARRAY_EXPRESSION] = { "expected an expression for the array element", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARRAY_EXPRESSION_AFTER_STAR] = { "expected an expression after `*` in the array", PM_ERROR_LEVEL_SYNTAX }, @@ -146,7 +148,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_DEF_ENDLESS_SETTER] = { "invalid method name; a setter method cannot be defined in an endless method definition", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_NAME] = { "unexpected %s; expected a method name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_PARAMS_TERM] = { "expected a delimiter to close the parameters", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_DEF_PARAMS_TERM_PAREN] = { "expected a `)` to close the parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_PARAMS_TERM_PAREN] = { "unexpected %s; expected a `)` to close the parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_RECEIVER] = { "expected a receiver for the method definition", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_RECEIVER_TERM] = { "expected a `.` or `::` after the receiver in a method definition", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_TERM] = { "expected an `end` to close the `def` statement", PM_ERROR_LEVEL_SYNTAX }, @@ -156,16 +158,16 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_EMBVAR_INVALID] = { "invalid embedded variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_END_UPCASE_BRACE] = { "expected a `{` after `END`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_END_UPCASE_TERM] = { "expected a `}` to close the `END` statement", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ESCAPE_INVALID_CONTROL] = { "invalid control escape sequence", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_CONTROL] = { "Invalid escape character syntax", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT] = { "invalid control escape sequence; control cannot be repeated", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ESCAPE_INVALID_HEXADECIMAL] = { "invalid hexadecimal escape sequence", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ESCAPE_INVALID_META] = { "invalid meta escape sequence", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_HEXADECIMAL] = { "invalid hex escape sequence", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_META] = { "Invalid escape character syntax", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_META_REPEAT] = { "invalid meta escape sequence; meta cannot be repeated", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_UNICODE] = { "invalid Unicode escape sequence", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS] = { "invalid Unicode escape sequence; Unicode cannot be combined with control or meta flags", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL] = { "invalid Unicode escape sequence; multiple codepoints are not allowed in a character literal", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL] = { "invalid Unicode escape sequence; Multiple codepoints at single character literal are disallowed", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ESCAPE_INVALID_UNICODE_LONG] = { "invalid Unicode escape sequence; maximum length is 6 digits", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = { "invalid Unicode escape sequence; needs closing `}`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = { "unterminated Unicode escape", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_ARGUMENT] = { "expected an argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EOL_AFTER_STATEMENT] = { "unexpected %s, expecting end-of-input", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ] = { "expected an expression after `&&=`", PM_ERROR_LEVEL_SYNTAX }, @@ -174,11 +176,12 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL] = { "expected an expression after `=`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS] = { "expected an expression after `<<`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN] = { "expected an expression after `(`", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR] = { "expected an expression after the operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR] = { "unexpected %s; expected an expression after the operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT] = { "expected an expression after `*` splat in an argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH] = { "expected an expression after `**` in a hash", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_EXPRESSION_AFTER_STAR] = { "expected an expression after `*`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_IDENT_REQ_PARAMETER] = { "expected an identifier for the required parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_IN_DELIMITER] = { "expected a delimiter after the patterns of an `in` clause", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_LPAREN_REQ_PARAMETER] = { "expected a `(` to start a required parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_MESSAGE] = { "unexpected %s; expecting a message to send to the receiver", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_RBRACKET] = { "expected a matching `]`", PM_ERROR_LEVEL_SYNTAX }, @@ -206,31 +209,39 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_HASH_KEY] = { "unexpected %s, expecting '}' or a key in the hash literal", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_HASH_ROCKET] = { "expected a `=>` between the hash key and value", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_HASH_TERM] = { "expected a `}` to close the hash literal", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_HASH_VALUE] = { "expected a value in the hash literal", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_HEREDOC_TERM] = { "could not find a terminator for the heredoc", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HASH_VALUE] = { "unexpected %s; expected a value in the hash literal", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HEREDOC_IDENTIFIER] = { "unterminated here document identifier", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HEREDOC_TERM] = { "unterminated heredoc; can't find string \"%.*s\" anywhere before EOF", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INCOMPLETE_QUESTION_MARK] = { "incomplete expression at `?`", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3_0] = { "`%.*s' is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3] = { "`%.*s' is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INCOMPLETE_VARIABLE_CLASS] = { "'%.*s' is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "'%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INSTANCE_VARIABLE_BARE] = { "'@' without identifiers is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_BLOCK_EXIT] = { "Invalid %s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_ESCAPE_CHARACTER] = { "Invalid escape character syntax", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_NUMBER_HEXADECIMAL] = { "invalid hexadecimal number", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_NUMBER_OCTAL] = { "invalid octal number", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_NUMBER_UNDERSCORE] = { "invalid underscore placement in number", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_CHARACTER] = { "invalid character 0x%X", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_LOCAL_VARIABLE_READ] = { "identifier %.*s is not valid to get", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_LOCAL_VARIABLE_WRITE] = { "identifier %.*s is not valid to set", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_FRACTION] = { "unexpected fraction part after numeric literal", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_HEXADECIMAL] = { "invalid hexadecimal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_OCTAL] = { "invalid octal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER] = { "invalid underscore placement in number", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING] = { "trailing '_' in number", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_CHARACTER] = { "Invalid char '\\x%02X' in expression", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_MULTIBYTE_CHAR] = { "invalid multibyte char (%s)", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_MULTIBYTE_CHARACTER] = { "invalid multibyte character 0x%X", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_MULTIBYTE_ESCAPE] = { "invalid multibyte escape: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_SYNTAX }, // TODO WHAT? + [PM_ERR_INVALID_PERCENT] = { "unknown type of %string", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_PERCENT_EOF] = { "unterminated quoted string meets end of file", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_AFTER_ELSE] = { "Invalid retry after else", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_AFTER_ENSURE] = { "Invalid retry after ensure", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_RETRY_WITHOUT_RESCUE] = { "Invalid retry without rescue", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0] = { "`%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_SYMBOL] = { "invalid symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_VARIABLE_GLOBAL_3_3] = { "`%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_VARIABLE_GLOBAL] = { "'%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_YIELD] = { "Invalid yield", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_IT_NOT_ALLOWED_NUMBERED] = { "`it` is not allowed when an numbered parameter is defined", PM_ERROR_LEVEL_SYNTAX }, @@ -239,13 +250,13 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_LAMBDA_TERM_BRACE] = { "expected a lambda block beginning with `{` to end with `}`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_LAMBDA_TERM_END] = { "expected a lambda block beginning with `do` to end with `end`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_LIST_I_LOWER_ELEMENT] = { "expected a symbol in a `%i` list", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_LIST_I_LOWER_TERM] = { "expected a closing delimiter for the `%i` list", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_I_LOWER_TERM] = { "unterminated list; expected a closing delimiter for the `%i`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_LIST_I_UPPER_ELEMENT] = { "expected a symbol in a `%I` list", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_LIST_I_UPPER_TERM] = { "expected a closing delimiter for the `%I` list", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_I_UPPER_TERM] = { "unterminated list; expected a closing delimiter for the `%I`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_LIST_W_LOWER_ELEMENT] = { "expected a string in a `%w` list", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_LIST_W_LOWER_TERM] = { "expected a closing delimiter for the `%w` list", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_W_LOWER_TERM] = { "unterminated list; expected a closing delimiter for the `%w`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_LIST_W_UPPER_ELEMENT] = { "expected a string in a `%W` list", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_LIST_W_UPPER_TERM] = { "expected a closing delimiter for the `%W` list", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_LIST_W_UPPER_TERM] = { "unterminated list; expected a closing delimiter for the `%W`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_MALLOC_FAILED] = { "failed to allocate memory", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_MIXED_ENCODING] = { "UTF-8 mixed within %s source", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_MODULE_IN_METHOD] = { "unexpected module definition in method body", PM_ERROR_LEVEL_SYNTAX }, @@ -265,6 +276,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI] = { "unexpected multiple `**` splat parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_BLOCK_MULTI] = { "multiple block parameters; only one block is allowed", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_CIRCULAR] = { "circular argument reference - %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_FORWARDING_AFTER_REST] = { "... after rest argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_METHOD_NAME] = { "unexpected name for a parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_NAME_DUPLICATED] = { "duplicated argument name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_NO_DEFAULT] = { "expected a default value for the parameter", PM_ERROR_LEVEL_SYNTAX }, @@ -275,6 +287,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_PARAMETER_STAR] = { "unexpected parameter `*`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_UNEXPECTED_FWD] = { "unexpected `...` in parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_WILD_LOOSE_COMMA] = { "unexpected `,` in parameters", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PARAMETER_UNEXPECTED_NO_KW] = { "unexpected **nil; no keywords marker disallowed after keywords", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_CAPTURE_DUPLICATE] = { "duplicated variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET] = { "expected a pattern expression after the `[` operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA] = { "expected a pattern expression after `,`", PM_ERROR_LEVEL_SYNTAX }, @@ -286,9 +299,12 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE] = { "expected a pattern expression after the `|` operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE] = { "expected a pattern expression after the range operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_EXPRESSION_AFTER_REST] = { "unexpected pattern expression after the `**` expression", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_PATTERN_HASH_KEY] = { "expected a key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_IMPLICIT] = { "unexpected implicit hash in pattern; use '{' to delineate", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY] = { "unexpected %s; expected a key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_HASH_KEY_DUPLICATE] = { "duplicated key name", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_PATTERN_HASH_KEY_LABEL] = { "expected a label as the key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, // TODO // THIS // AND // ABOVE // IS WEIRD + [PM_ERR_PATTERN_HASH_KEY_INTERPOLATED] = { "symbol literal with interpolation is not allowed", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY_LABEL] = { "expected a label as the key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY_LOCALS] = { "key must be valid as local variables", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_IDENT_AFTER_HROCKET] = { "expected an identifier after the `=>` operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_LABEL_AFTER_COMMA] = { "expected a label after the `,` in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_REST] = { "unexpected rest pattern", PM_ERROR_LEVEL_SYNTAX }, @@ -301,7 +317,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_REGEXP_NON_ESCAPED_MBC] = { "/.../n has a non escaped non ASCII character in non ASCII-8BIT script: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_REGEXP_INVALID_UNICODE_RANGE] = { "invalid Unicode range: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_REGEXP_UNKNOWN_OPTIONS] = { "unknown regexp %s: %.*s", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_REGEXP_TERM] = { "expected a closing delimiter for the regular expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_TERM] = { "unterminated regexp meets end of file; expected a closing delimiter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP] = { "UTF-8 character in non UTF-8 regexp: /%s/", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_RESCUE_EXPRESSION] = { "expected a rescued expression", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_RESCUE_MODIFIER_VALUE] = { "expected a value after the `rescue` modifier", PM_ERROR_LEVEL_SYNTAX }, @@ -314,18 +330,21 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_STATEMENT_PREEXE_BEGIN] = { "unexpected a `BEGIN` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STATEMENT_UNDEF] = { "unexpected an `undef` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STRING_CONCATENATION] = { "expected a string for concatenation", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_STRING_INTERPOLATED_TERM] = { "expected a closing delimiter for the interpolated string", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_STRING_INTERPOLATED_TERM] = { "unterminated string; expected a closing delimiter for the interpolated string", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STRING_LITERAL_EOF] = { "unterminated string meets end of file", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STRING_LITERAL_TERM] = { "unexpected %s, expected a string literal terminator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_SYMBOL_INVALID] = { "invalid symbol", PM_ERROR_LEVEL_SYNTAX }, // TODO expected symbol? prism.c ~9719 - [PM_ERR_SYMBOL_TERM_DYNAMIC] = { "expected a closing delimiter for the dynamic symbol", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_SYMBOL_TERM_INTERPOLATED] = { "expected a closing delimiter for the interpolated symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_SYMBOL_TERM_DYNAMIC] = { "unterminated quoted string; expected a closing delimiter for the dynamic symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_SYMBOL_TERM_INTERPOLATED] = { "unterminated symbol; expected a closing delimiter for the interpolated symbol", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_TERNARY_COLON] = { "expected a `:` after the true expression of a ternary operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_TERNARY_EXPRESSION_FALSE] = { "expected an expression after `:` in the ternary operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_TERNARY_EXPRESSION_TRUE] = { "expected an expression after `?` in the ternary operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNDEF_ARGUMENT] = { "invalid argument being passed to `undef`; expected a bare word, constant, or symbol argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNARY_RECEIVER] = { "unexpected %s, expected a receiver for unary `%c`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNEXPECTED_BLOCK_ARGUMENT] = { "block argument should not be given", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_INDEX_BLOCK] = { "unexpected block arg given in index; blocks are not allowed in index expressions", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_INDEX_KEYWORDS] = { "unexpected keyword arg given in index; keywords are not allowed in index expressions", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_SAFE_NAVIGATION] = { "&. inside multiple assignment destination", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT] = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNEXPECTED_TOKEN_IGNORE] = { "unexpected %s, ignoring it", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNTIL_TERM] = { "expected an `end` to close the `until` statement", PM_ERROR_LEVEL_SYNTAX }, @@ -347,7 +366,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_WARN_DOT_DOT_DOT_EOL] = { "... at EOL, should be parenthesized?", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_DUPLICATED_HASH_KEY] = { "key %.*s is duplicated and overwritten on line %" PRIi32, PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_DUPLICATED_WHEN_CLAUSE] = { "duplicated 'when' clause with line %" PRIi32 " is ignored", PM_WARNING_LEVEL_VERBOSE }, - [PM_WARN_EQUAL_IN_CONDITIONAL_3_3_0] = { "found `= literal' in conditional, should be ==", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_EQUAL_IN_CONDITIONAL_3_3] = { "found `= literal' in conditional, should be ==", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_EQUAL_IN_CONDITIONAL] = { "found '= literal' in conditional, should be ==", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_END_IN_METHOD] = { "END in method; use at_exit", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_FLOAT_OUT_OF_RANGE] = { "Float %.*s%s out of range", PM_WARNING_LEVEL_VERBOSE }, @@ -359,6 +378,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_WARN_KEYWORD_EOL] = { "`%.*s` at the end of line without an expression", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_LITERAL_IN_CONDITION_DEFAULT] = { "%sliteral in %s", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_LITERAL_IN_CONDITION_VERBOSE] = { "%sliteral in %s", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE] = { "'shareable_constant_value' is ignored unless in comment-only line", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_SHEBANG_CARRIAGE_RETURN] = { "shebang line ending with \\r may cause problems", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_UNEXPECTED_CARRIAGE_RETURN] = { "encountered \\r in middle of line, treated as a mere space", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_UNREACHABLE_STATEMENT] = { "statement not reached", PM_WARNING_LEVEL_VERBOSE }, diff --git a/prism/templates/src/token_type.c.erb b/prism/templates/src/token_type.c.erb index 1aeecd72b2..af6a2ad6fe 100644 --- a/prism/templates/src/token_type.c.erb +++ b/prism/templates/src/token_type.c.erb @@ -30,7 +30,7 @@ const char * pm_token_type_human(pm_token_type_t token_type) { switch (token_type) { case PM_TOKEN_EOF: - return "end of file"; + return "end-of-input"; case PM_TOKEN_MISSING: return "missing token"; case PM_TOKEN_NOT_PROVIDED: @@ -90,9 +90,9 @@ pm_token_type_human(pm_token_type_t token_type) { case PM_TOKEN_DOT: return "'.'"; case PM_TOKEN_DOT_DOT: - return "'..'"; + return ".."; case PM_TOKEN_DOT_DOT_DOT: - return "'...'"; + return "..."; case PM_TOKEN_EMBDOC_BEGIN: return "'=begin'"; case PM_TOKEN_EMBDOC_END: @@ -130,9 +130,9 @@ pm_token_type_human(pm_token_type_t token_type) { case PM_TOKEN_GREATER_EQUAL: return "'>='"; case PM_TOKEN_GREATER_GREATER: - return "'>>'"; + return ">>"; case PM_TOKEN_GREATER_GREATER_EQUAL: - return "'>>='"; + return ">>="; case PM_TOKEN_HEREDOC_END: return "heredoc ending"; case PM_TOKEN_HEREDOC_START: @@ -258,9 +258,9 @@ pm_token_type_human(pm_token_type_t token_type) { case PM_TOKEN_LESS_EQUAL_GREATER: return "'<=>'"; case PM_TOKEN_LESS_LESS: - return "'<<'"; + return "<<"; case PM_TOKEN_LESS_LESS_EQUAL: - return "'<<='"; + return "<<="; case PM_TOKEN_METHOD_NAME: return "method name"; case PM_TOKEN_MINUS: diff --git a/prism/templates/template.rb b/prism/templates/template.rb index d0ce6c6643..6f79a85371 100755 --- a/prism/templates/template.rb +++ b/prism/templates/template.rb @@ -629,6 +629,7 @@ module Prism "lib/prism/dispatcher.rb", "lib/prism/dot_visitor.rb", "lib/prism/dsl.rb", + "lib/prism/inspect_visitor.rb", "lib/prism/mutation_compiler.rb", "lib/prism/node.rb", "lib/prism/reflection.rb", diff --git a/prism/util/pm_integer.c b/prism/util/pm_integer.c index 0739662e98..e523bae90b 100644 --- a/prism/util/pm_integer.c +++ b/prism/util/pm_integer.c @@ -135,12 +135,19 @@ karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t * } if (left_length * 2 <= right_length) { - uint32_t *values = (uint32_t*) xcalloc(left_length + right_length, sizeof(uint32_t)); + uint32_t *values = (uint32_t *) xcalloc(left_length + right_length, sizeof(uint32_t)); for (size_t start_offset = 0; start_offset < right_length; start_offset += left_length) { size_t end_offset = start_offset + left_length; if (end_offset > right_length) end_offset = right_length; + pm_integer_t sliced_left = { + .value = 0, + .length = left_length, + .values = left_values, + .negative = false + }; + pm_integer_t sliced_right = { .value = 0, .length = end_offset - start_offset, @@ -149,7 +156,7 @@ karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t * }; pm_integer_t product; - karatsuba_multiply(&product, left, &sliced_right, base); + karatsuba_multiply(&product, &sliced_left, &sliced_right, base); uint32_t carry = 0; for (size_t index = 0; index < product.length; index++) { diff --git a/prism/util/pm_strpbrk.c b/prism/util/pm_strpbrk.c index 6c8dea1836..916a4cc3fd 100644 --- a/prism/util/pm_strpbrk.c +++ b/prism/util/pm_strpbrk.c @@ -9,6 +9,27 @@ pm_strpbrk_invalid_multibyte_character(pm_parser_t *parser, const uint8_t *start } /** + * Set the explicit encoding for the parser to the current encoding. + */ +static inline void +pm_strpbrk_explicit_encoding_set(pm_parser_t *parser, const uint8_t *source, size_t width) { + if (parser->explicit_encoding != NULL) { + if (parser->explicit_encoding == parser->encoding) { + // Okay, we already locked to this encoding. + } else if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + // Not okay, we already found a Unicode escape sequence and this + // conflicts. + pm_diagnostic_list_append_format(&parser->error_list, source, source + width, PM_ERR_MIXED_ENCODING, parser->encoding->name); + } else { + // Should not be anything else. + assert(false && "unreachable"); + } + } + + parser->explicit_encoding = parser->encoding; +} + +/** * This is the default path. */ static inline const uint8_t * @@ -52,7 +73,7 @@ pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *chars * This is the path when the encoding is ASCII-8BIT. */ static inline const uint8_t * -pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maximum) { +pm_strpbrk_ascii_8bit(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { size_t index = 0; while (index < maximum) { @@ -60,6 +81,7 @@ pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maxi return source + index; } + if (validate && source[index] >= 0x80) pm_strpbrk_explicit_encoding_set(parser, source, 1); index++; } @@ -72,6 +94,7 @@ pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maxi static inline const uint8_t * pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { size_t index = 0; + const pm_encoding_t *encoding = parser->encoding; while (index < maximum) { if (strchr((const char *) charset, source[index]) != NULL) { @@ -81,7 +104,8 @@ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t if (source[index] < 0x80) { index++; } else { - size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + if (validate) pm_strpbrk_explicit_encoding_set(parser, source, width); if (width > 0) { index += width; @@ -96,7 +120,7 @@ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t do { index++; - } while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); + } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index); } @@ -113,6 +137,7 @@ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t static inline const uint8_t * pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) { size_t index = 0; + const pm_encoding_t *encoding = parser->encoding; while (index < maximum) { if (strchr((const char *) charset, source[index]) != NULL) { @@ -122,7 +147,8 @@ pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t if (source[index] < 0x80 || !validate) { index++; } else { - size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + pm_strpbrk_explicit_encoding_set(parser, source, width); if (width > 0) { index += width; @@ -135,7 +161,7 @@ pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t do { index++; - } while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); + } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0); pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index); } @@ -171,7 +197,7 @@ pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, p } else if (!parser->encoding_changed) { return pm_strpbrk_utf8(parser, source, charset, (size_t) length, validate); } else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) { - return pm_strpbrk_ascii_8bit(source, charset, (size_t) length); + return pm_strpbrk_ascii_8bit(parser, source, charset, (size_t) length, validate); } else if (parser->encoding->multibyte) { return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length, validate); } else { diff --git a/prism/version.h b/prism/version.h index c96fe6882f..154e967944 100644 --- a/prism/version.h +++ b/prism/version.h @@ -14,7 +14,7 @@ /** * The minor version of the Prism library as an int. */ -#define PRISM_VERSION_MINOR 25 +#define PRISM_VERSION_MINOR 29 /** * The patch version of the Prism library as an int. @@ -24,6 +24,6 @@ /** * The version of the Prism library as a constant string. */ -#define PRISM_VERSION "0.25.0" +#define PRISM_VERSION "0.29.0" #endif diff --git a/prism_compile.c b/prism_compile.c index 26d94e979d..9207f7a5f5 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -55,6 +55,13 @@ #define PUSH_SEQ(seq1, seq2) \ APPEND_LIST((seq1), (seq2)) +#define PUSH_SYNTHETIC_PUTNIL(seq, iseq) \ + do { \ + int lineno = ISEQ_COMPILE_DATA(iseq)->last_line; \ + if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq)); \ + ADD_SYNTHETIC_INSN(seq, lineno, -1, putnil); \ + } while (0) + /******************************************************************************/ /* These functions compile getlocal/setlocal instructions but operate on */ /* prism locations instead of NODEs. */ @@ -130,7 +137,7 @@ pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int c #define PM_NODE_END_LINE_COLUMN(parser, node) \ pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.end, (parser)->start_line) -#define PM_LOCATION_LINE_COLUMN(parser, location) \ +#define PM_LOCATION_START_LINE_COLUMN(parser, location) \ pm_newline_list_line_column(&(parser)->newline_list, (location)->start, (parser)->start_line) static int @@ -141,7 +148,7 @@ pm_node_line_number(const pm_parser_t *parser, const pm_node_t *node) static int pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) { - return (int) PM_LOCATION_LINE_COLUMN(parser, location).line; + return (int) PM_LOCATION_START_LINE_COLUMN(parser, location).line; } /** @@ -272,7 +279,7 @@ parse_string(const pm_scope_node_t *scope_node, const pm_string_t *string) * creating those strings based on the flags set on the owning node. */ static inline VALUE -parse_string_encoded(const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string) +parse_string_encoded(const pm_node_t *node, const pm_string_t *string, rb_encoding *default_encoding) { rb_encoding *encoding; @@ -283,7 +290,7 @@ parse_string_encoded(const pm_scope_node_t *scope_node, const pm_node_t *node, c encoding = rb_utf8_encoding(); } else { - encoding = scope_node->encoding; + encoding = default_encoding; } return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding); @@ -294,10 +301,10 @@ parse_static_literal_string(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, { rb_encoding *encoding; - if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) { + if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) { encoding = rb_ascii8bit_encoding(); } - else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) { + else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) { encoding = rb_utf8_encoding(); } else { @@ -344,91 +351,46 @@ pm_optimizable_range_item_p(const pm_node_t *node) return (!node || PM_NODE_TYPE_P(node, PM_INTEGER_NODE) || PM_NODE_TYPE_P(node, PM_NIL_NODE)); } -static void pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node); - -static int -pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +/** Raise an error corresponding to the invalid regular expression. */ +static VALUE +parse_regexp_error(rb_iseq_t *iseq, int32_t line_number, const char *fmt, ...) { - int stack_size = 0; - size_t parts_size = parts->size; - bool interpolated = false; - - if (parts_size > 0) { - VALUE current_string = Qnil; - - for (size_t index = 0; index < parts_size; index++) { - const pm_node_t *part = parts->nodes[index]; - - if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { - const pm_string_node_t *string_node = (const pm_string_node_t *) part; - VALUE string_value = parse_string_encoded(scope_node, (const pm_node_t *) string_node, &string_node->unescaped); - - if (RTEST(current_string)) { - current_string = rb_str_concat(current_string, string_value); - } - else { - current_string = string_value; - } - } - else { - interpolated = true; - - if ( - PM_NODE_TYPE_P(part, PM_EMBEDDED_STATEMENTS_NODE) && - ((const pm_embedded_statements_node_t *) part)->statements != NULL && - ((const pm_embedded_statements_node_t *) part)->statements->body.size == 1 && - PM_NODE_TYPE_P(((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0], PM_STRING_NODE) - ) { - const pm_string_node_t *string_node = (const pm_string_node_t *) ((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0]; - VALUE string_value = parse_string_encoded(scope_node, (const pm_node_t *) string_node, &string_node->unescaped); - - if (RTEST(current_string)) { - current_string = rb_str_concat(current_string, string_value); - } - else { - current_string = string_value; - } - } - else { - if (!RTEST(current_string)) { - current_string = rb_enc_str_new(NULL, 0, scope_node->encoding); - } - - PUSH_INSN1(ret, *node_location, putobject, rb_fstring(current_string)); - PM_COMPILE_NOT_POPPED(part); - PUSH_INSN(ret, *node_location, dup); - PUSH_INSN1(ret, *node_location, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE , NULL, FALSE)); - PUSH_INSN(ret, *node_location, anytostring); - - current_string = Qnil; - stack_size += 2; - } - } - } - - if (RTEST(current_string)) { - current_string = rb_fstring(current_string); + va_list args; + va_start(args, fmt); + VALUE error = rb_syntax_error_append(Qnil, rb_iseq_path(iseq), line_number, -1, NULL, "%" PRIsVALUE, args); + va_end(args); + rb_exc_raise(error); +} - if (stack_size == 0 && interpolated) { - PUSH_INSN1(ret, *node_location, putstring, current_string); - } - else { - PUSH_INSN1(ret, *node_location, putobject, current_string); - } +static VALUE +parse_regexp_string_part(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding) +{ + // If we were passed an explicit regexp encoding, then we need to double + // check that it's okay here for this fragment of the string. + rb_encoding *encoding; - current_string = Qnil; - stack_size++; - } + if (explicit_regexp_encoding != NULL) { + encoding = explicit_regexp_encoding; + } + else if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) { + encoding = rb_ascii8bit_encoding(); + } + else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) { + encoding = rb_utf8_encoding(); } else { - PUSH_INSN(ret, *node_location, putnil); + encoding = implicit_regexp_encoding; } - return stack_size; + VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), encoding); + VALUE error = rb_reg_check_preprocess(string); + + if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number(scope_node->parser, node), "%" PRIsVALUE, rb_obj_as_string(error)); + return string; } static VALUE -pm_static_literal_concat(const pm_node_list_t *nodes, const pm_scope_node_t *scope_node, bool top) +pm_static_literal_concat(rb_iseq_t *iseq, const pm_node_list_t *nodes, const pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding, bool top) { VALUE current = Qnil; @@ -438,11 +400,28 @@ pm_static_literal_concat(const pm_node_list_t *nodes, const pm_scope_node_t *sco switch (PM_NODE_TYPE(part)) { case PM_STRING_NODE: - string = parse_string_encoded(scope_node, part, &((const pm_string_node_t *) part)->unescaped); + if (implicit_regexp_encoding != NULL) { + if (top) { + string = parse_regexp_string_part(iseq, scope_node, part, &((const pm_string_node_t *) part)->unescaped, implicit_regexp_encoding, explicit_regexp_encoding); + } + else { + string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding); + VALUE error = rb_reg_check_preprocess(string); + if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number(scope_node->parser, part), "%" PRIsVALUE, rb_obj_as_string(error)); + } + } + else { + string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding); + } break; case PM_INTERPOLATED_STRING_NODE: - string = pm_static_literal_concat(&((const pm_interpolated_string_node_t *) part)->parts, scope_node, false); + string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) part)->parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false); break; + case PM_EMBEDDED_STATEMENTS_NODE: { + const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) part; + string = pm_static_literal_concat(iseq, &cast->statements->body, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false); + break; + } default: RUBY_ASSERT(false && "unexpected node type in pm_static_literal_concat"); return Qnil; @@ -536,21 +515,10 @@ parse_regexp_encoding(const pm_scope_node_t *scope_node, const pm_node_t *node) return rb_enc_get_from_index(ENCINDEX_Windows_31J); } else { - return scope_node->encoding; + return NULL; } } -/** Raise an error corresponding to the invalid regular expression. */ -static VALUE -parse_regexp_error(rb_iseq_t *iseq, int32_t line_number, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - VALUE error = rb_syntax_error_append(Qnil, rb_iseq_path(iseq), line_number, -1, NULL, "%" PRIsVALUE, args); - va_end(args); - rb_exc_raise(error); -} - static VALUE parse_regexp(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, VALUE string) { @@ -574,22 +542,144 @@ parse_regexp(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t static inline VALUE parse_regexp_literal(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped) { - VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), parse_regexp_encoding(scope_node, node)); + rb_encoding *regexp_encoding = parse_regexp_encoding(scope_node, node); + if (regexp_encoding == NULL) regexp_encoding = scope_node->encoding; + + VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), regexp_encoding); return parse_regexp(iseq, scope_node, node, string); } static inline VALUE parse_regexp_concat(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_node_list_t *parts) { - VALUE string = pm_static_literal_concat(parts, scope_node, false); - rb_enc_associate(string, parse_regexp_encoding(scope_node, node)); + rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node); + rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding; + + VALUE string = pm_static_literal_concat(iseq, parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false); return parse_regexp(iseq, scope_node, node, string); } +static void pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node); + +static int +pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding) +{ + int stack_size = 0; + size_t parts_size = parts->size; + bool interpolated = false; + + if (parts_size > 0) { + VALUE current_string = Qnil; + + for (size_t index = 0; index < parts_size; index++) { + const pm_node_t *part = parts->nodes[index]; + + if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { + const pm_string_node_t *string_node = (const pm_string_node_t *) part; + VALUE string_value; + + if (implicit_regexp_encoding == NULL) { + string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding); + } + else { + string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding); + } + + if (RTEST(current_string)) { + current_string = rb_str_concat(current_string, string_value); + } + else { + current_string = string_value; + } + } + else { + interpolated = true; + + if ( + PM_NODE_TYPE_P(part, PM_EMBEDDED_STATEMENTS_NODE) && + ((const pm_embedded_statements_node_t *) part)->statements != NULL && + ((const pm_embedded_statements_node_t *) part)->statements->body.size == 1 && + PM_NODE_TYPE_P(((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0], PM_STRING_NODE) + ) { + const pm_string_node_t *string_node = (const pm_string_node_t *) ((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0]; + VALUE string_value; + + if (implicit_regexp_encoding == NULL) { + string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding); + } + else { + string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding); + } + + if (RTEST(current_string)) { + current_string = rb_str_concat(current_string, string_value); + } + else { + current_string = string_value; + } + } + else { + if (!RTEST(current_string)) { + rb_encoding *encoding; + + if (implicit_regexp_encoding != NULL) { + if (explicit_regexp_encoding != NULL) { + encoding = explicit_regexp_encoding; + } + else if (scope_node->parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + encoding = rb_ascii8bit_encoding(); + } + else { + encoding = implicit_regexp_encoding; + } + } + else { + encoding = scope_node->encoding; + } + + current_string = rb_enc_str_new(NULL, 0, encoding); + } + + PUSH_INSN1(ret, *node_location, putobject, rb_fstring(current_string)); + PM_COMPILE_NOT_POPPED(part); + PUSH_INSN(ret, *node_location, dup); + PUSH_INSN1(ret, *node_location, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE , NULL, FALSE)); + PUSH_INSN(ret, *node_location, anytostring); + + current_string = Qnil; + stack_size += 2; + } + } + } + + if (RTEST(current_string)) { + current_string = rb_fstring(current_string); + + if (stack_size == 0 && interpolated) { + PUSH_INSN1(ret, *node_location, putstring, current_string); + } + else { + PUSH_INSN1(ret, *node_location, putobject, current_string); + } + + current_string = Qnil; + stack_size++; + } + } + else { + PUSH_INSN(ret, *node_location, putnil); + } + + return stack_size; +} + static void pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *parts, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) { - int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node); + rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node); + rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding; + + int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding); PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length)); } @@ -686,13 +776,13 @@ pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_n return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts); } case PM_INTERPOLATED_STRING_NODE: { - VALUE string = pm_static_literal_concat(&((const pm_interpolated_string_node_t *) node)->parts, scope_node, false); + VALUE string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) node)->parts, scope_node, NULL, NULL, false); int line_number = pm_node_line_number(scope_node->parser, node); return pm_static_literal_string(iseq, string, line_number); } case PM_INTERPOLATED_SYMBOL_NODE: { const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node; - VALUE string = pm_static_literal_concat(&cast->parts, scope_node, true); + VALUE string = pm_static_literal_concat(iseq, &cast->parts, scope_node, NULL, NULL, true); return ID2SYM(rb_intern_str(string)); } @@ -730,6 +820,28 @@ pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_n } } +/** + * A helper for converting a pm_location_t into a rb_code_location_t. + */ +static rb_code_location_t +pm_code_location(const pm_scope_node_t *scope_node, const pm_node_t *node) +{ + const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, node); + + return (rb_code_location_t) { + .beg_pos = { .lineno = start_location.line, .column = start_location.column }, + .end_pos = { .lineno = end_location.line, .column = end_location.column } + }; +} + +/** + * A macro for determining if we should go through the work of adding branch + * coverage to the current iseq. We check this manually each time because we + * want to avoid the overhead of creating rb_code_location_t objects. + */ +#define PM_BRANCH_COVERAGE_P(iseq) (ISEQ_COVERAGE(iseq) && ISEQ_BRANCH_COVERAGE(iseq)) + static void pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node); @@ -882,7 +994,7 @@ again: * Compile an if or unless node. */ static void -pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, const pm_statements_node_t *statements, const pm_node_t *consequent, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_type_t type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *consequent, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) { const pm_line_column_t location = *line_column; LABEL *then_label = NEW_LABEL(location.line); @@ -891,20 +1003,47 @@ pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, con pm_compile_branch_condition(iseq, ret, predicate, then_label, else_label, false, scope_node); + rb_code_location_t conditional_location; + VALUE branches = Qfalse; + + if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) { + conditional_location = pm_code_location(scope_node, node); + branches = decl_branch_base(iseq, PTR2NUM(node), &conditional_location, type == PM_IF_NODE ? "if" : "unless"); + } + if (then_label->refcnt) { PUSH_LABEL(ret, then_label); DECL_ANCHOR(then_seq); INIT_ANCHOR(then_seq); - if (statements) { + if (statements != NULL) { pm_compile_node(iseq, (const pm_node_t *) statements, then_seq, popped, scope_node); } else if (!popped) { - PUSH_INSN(then_seq, location, putnil); + PUSH_SYNTHETIC_PUTNIL(then_seq, iseq); } if (else_label->refcnt) { + // Establish branch coverage for the then block. + if (PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t branch_location; + + if (statements != NULL) { + branch_location = pm_code_location(scope_node, (const pm_node_t *) statements); + } else if (type == PM_IF_NODE) { + pm_line_column_t predicate_end = PM_NODE_END_LINE_COLUMN(scope_node->parser, predicate); + branch_location = (rb_code_location_t) { + .beg_pos = { .lineno = predicate_end.line, .column = predicate_end.column }, + .end_pos = { .lineno = predicate_end.line, .column = predicate_end.column } + }; + } else { + branch_location = conditional_location; + } + + add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, type == PM_IF_NODE ? "then" : "else", branches); + } + end_label = NEW_LABEL(location.line); PUSH_INSNL(then_seq, location, jump, end_label); if (!popped) PUSH_INSN(then_seq, location, pop); @@ -919,11 +1058,27 @@ pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, con DECL_ANCHOR(else_seq); INIT_ANCHOR(else_seq); - if (consequent) { - pm_compile_node(iseq, (const pm_node_t *) consequent, else_seq, popped, scope_node); + if (consequent != NULL) { + pm_compile_node(iseq, consequent, else_seq, popped, scope_node); } else if (!popped) { - PUSH_INSN(else_seq, location, putnil); + PUSH_SYNTHETIC_PUTNIL(else_seq, iseq); + } + + // Establish branch coverage for the else block. + if (then_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t branch_location; + + if (consequent == NULL) { + branch_location = conditional_location; + } else if (PM_NODE_TYPE_P(consequent, PM_ELSE_NODE)) { + const pm_else_node_t *else_node = (const pm_else_node_t *) consequent; + branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : (const pm_node_t *) else_node); + } else { + branch_location = pm_code_location(scope_node, (const pm_node_t *) consequent); + } + + add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 1, type == PM_IF_NODE ? "else" : "then", branches); } PUSH_SEQ(ret, else_seq); @@ -940,7 +1095,7 @@ pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, con * Compile a while or until loop. */ static void -pm_compile_loop(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_flags_t flags, enum pm_node_type type, const pm_statements_node_t *statements, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +pm_compile_loop(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_flags_t flags, enum pm_node_type type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) { const pm_line_column_t location = *line_column; @@ -976,9 +1131,19 @@ pm_compile_loop(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_fl if (tmp_label) PUSH_LABEL(ret, tmp_label); PUSH_LABEL(ret, redo_label); - if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements); + // Establish branch coverage for the loop. + if (PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t loop_location = pm_code_location(scope_node, node); + VALUE branches = decl_branch_base(iseq, PTR2NUM(node), &loop_location, type == PM_WHILE_NODE ? "while" : "until"); + + rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location; + add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches); + } + + if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements); PUSH_LABEL(ret, next_label); + if (type == PM_WHILE_NODE) { pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, popped, scope_node); } @@ -1572,7 +1737,7 @@ pm_compile_index_operator_write_node(rb_iseq_t *iseq, const pm_index_operator_wr PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords); PM_COMPILE_NOT_POPPED(node->value); - ID id_operator = pm_constant_id_lookup(scope_node, node->operator); + ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator); PUSH_SEND(ret, location, id_operator, INT2FIX(1)); if (!popped) { @@ -2808,19 +2973,81 @@ pm_scope_node_destroy(pm_scope_node_t *scope_node) } } +/** + * We need to put the label "retry_end_l" immediately after the last "send" + * instruction. This because vm_throw checks if the break cont is equal to the + * index of next insn of the "send". (Otherwise, it is considered + * "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".) + * + * Normally, "send" instruction is at the last. However, qcall under branch + * coverage measurement adds some instructions after the "send". + * + * Note that "invokesuper" appears instead of "send". + */ +static void +pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry_end_l) +{ + INSN *iobj; + LINK_ELEMENT *last_elem = LAST_ELEMENT(ret); + iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem); + while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) { + iobj = (INSN*) get_prev_insn(iobj); + } + ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l); + + // LINK_ANCHOR has a pointer to the last element, but + // ELEM_INSERT_NEXT does not update it even if we add an insn to the + // last of LINK_ANCHOR. So this updates it manually. + if (&iobj->link == LAST_ELEMENT(ret)) { + ret->last = (LINK_ELEMENT*) retry_end_l; + } +} + +/** + * Compile a call node into the given iseq. + */ static void pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, ID method_id, LABEL *start) { const pm_location_t *message_loc = &call_node->message_loc; if (message_loc->start == NULL) message_loc = &call_node->base.location; - const pm_line_column_t location = PM_LOCATION_LINE_COLUMN(scope_node->parser, message_loc); + const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, message_loc); LABEL *else_label = NEW_LABEL(location.line); LABEL *end_label = NEW_LABEL(location.line); + LABEL *retry_end_l = NEW_LABEL(location.line); + + VALUE branches = Qfalse; + rb_code_location_t code_location = { 0 }; + int node_id = location.column; if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (PM_BRANCH_COVERAGE_P(iseq)) { + const uint8_t *cursors[3] = { + call_node->closing_loc.end, + call_node->arguments == NULL ? NULL : call_node->arguments->base.location.end, + call_node->message_loc.end + }; + + const uint8_t *end_cursor = cursors[0]; + end_cursor = (end_cursor == NULL || cursors[1] == NULL) ? cursors[1] : (end_cursor > cursors[1] ? end_cursor : cursors[1]); + end_cursor = (end_cursor == NULL || cursors[2] == NULL) ? cursors[2] : (end_cursor > cursors[2] ? end_cursor : cursors[2]); + + const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, call_node); + const pm_line_column_t end_location = pm_newline_list_line_column(&scope_node->parser->newline_list, end_cursor, scope_node->parser->start_line); + + code_location = (rb_code_location_t) { + .beg_pos = { .lineno = start_location.line, .column = start_location.column }, + .end_pos = { .lineno = end_location.line, .column = end_location.column } + }; + + branches = decl_branch_base(iseq, PTR2NUM(call_node), &code_location, "&."); + } + PUSH_INSN(ret, location, dup); PUSH_INSNL(ret, location, branchnil, else_label); + + add_trace_branch_coverage(iseq, ret, &code_location, node_id, 0, "then", branches); } int flags = 0; @@ -2836,10 +3063,6 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, pm_node_line_number(scope_node->parser, call_node->block)); pm_scope_node_destroy(&next_scope_node); - - if (ISEQ_BODY(block_iseq)->catch_table) { - PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, end_label, block_iseq, end_label); - } ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq; } else { @@ -2884,12 +3107,15 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg); + if (block_iseq && ISEQ_BODY(block_iseq)->catch_table) { + pm_compile_retry_end_label(iseq, ret, retry_end_l); + PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, retry_end_l, block_iseq, retry_end_l); + } + if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { PUSH_INSNL(ret, location, jump, end_label); PUSH_LABEL(ret, else_label); - } - - if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) || (block_iseq && ISEQ_BODY(block_iseq)->catch_table)) { + add_trace_branch_coverage(iseq, ret, &code_location, node_id, 1, "else", branches); PUSH_LABEL(ret, end_label); } @@ -3075,7 +3301,7 @@ pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_c } case PM_CONSTANT_PATH_NODE: { const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) cast->child)->name)); + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name)); if (cast->parent != NULL) { if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line); @@ -3371,8 +3597,11 @@ pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, st_tabl if (rest->expression != NULL) { RUBY_ASSERT(PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE)); - pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node); - local_index++; + + if (!PM_NODE_FLAG_P(rest->expression, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node); + local_index++; + } } } @@ -3380,8 +3609,10 @@ pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, st_tabl const pm_node_t *right = node->rights.nodes[index]; if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) { - pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node); - local_index++; + if (!PM_NODE_FLAG_P(right, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node); + local_index++; + } } else { RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE)); @@ -3574,7 +3805,7 @@ pm_multi_target_state_update(pm_multi_target_state_t *state) previous = current; current = current->next; - free(previous); + xfree(previous); } } @@ -3686,7 +3917,7 @@ pm_compile_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *cons // for I::J in []; end // const pm_constant_path_target_node_t *cast = (const pm_constant_path_target_node_t *) node; - ID name = pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) cast->child)->name); + ID name = pm_constant_id_lookup(scope_node, cast->name); if (cast->parent != NULL) { pm_compile_node(iseq, cast->parent, parents, false, scope_node); @@ -4182,7 +4413,7 @@ pm_opt_aset_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node) static void pm_compile_constant_read(rb_iseq_t *iseq, VALUE name, const pm_location_t *name_loc, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node) { - const pm_line_column_t location = PM_LOCATION_LINE_COLUMN(scope_node->parser, name_loc); + const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, name_loc); if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { ISEQ_BODY(iseq)->ic_size++; @@ -4216,7 +4447,7 @@ pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node) } case PM_CONSTANT_PATH_NODE: { const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) cast->child)->name)); + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name)); rb_ary_unshift(parts, name); if (cast->parent == NULL) { @@ -4254,7 +4485,7 @@ pm_compile_constant_path(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *co } case PM_CONSTANT_PATH_NODE: { const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) cast->child)->name)); + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name)); if (cast->parent == NULL) { PUSH_INSN(body, location, pop); @@ -4332,7 +4563,496 @@ pm_compile_case_node_dispatch(rb_iseq_t *iseq, VALUE dispatch, const pm_node_t * return dispatch; } -/* +/** + * Return the object that will be pushed onto the stack for the given node. + */ +static VALUE +pm_compile_shareable_constant_literal(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_node_t *scope_node) +{ + switch (PM_NODE_TYPE(node)) { + case PM_TRUE_NODE: + case PM_FALSE_NODE: + case PM_NIL_NODE: + case PM_SYMBOL_NODE: + case PM_REGULAR_EXPRESSION_NODE: + case PM_SOURCE_LINE_NODE: + case PM_INTEGER_NODE: + case PM_FLOAT_NODE: + case PM_RATIONAL_NODE: + case PM_IMAGINARY_NODE: + case PM_SOURCE_ENCODING_NODE: + return pm_static_literal_value(iseq, node, scope_node); + case PM_STRING_NODE: + return parse_static_literal_string(iseq, scope_node, node, &((const pm_string_node_t *) node)->unescaped); + case PM_SOURCE_FILE_NODE: + return pm_source_file_value((const pm_source_file_node_t *) node, scope_node); + case PM_ARRAY_NODE: { + const pm_array_node_t *cast = (const pm_array_node_t *) node; + VALUE result = rb_ary_new_capa(cast->elements.size); + + for (size_t index = 0; index < cast->elements.size; index++) { + VALUE element = pm_compile_shareable_constant_literal(iseq, cast->elements.nodes[index], scope_node); + if (element == Qundef) return Qundef; + + rb_ary_push(result, element); + } + + return rb_ractor_make_shareable(result); + } + case PM_HASH_NODE: { + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + VALUE result = rb_hash_new_capa(cast->elements.size); + + for (size_t index = 0; index < cast->elements.size; index++) { + const pm_node_t *element = cast->elements.nodes[index]; + if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return Qundef; + + const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element; + + VALUE key = pm_compile_shareable_constant_literal(iseq, assoc->key, scope_node); + if (key == Qundef) return Qundef; + + VALUE value = pm_compile_shareable_constant_literal(iseq, assoc->value, scope_node); + if (value == Qundef) return Qundef; + + rb_hash_aset(result, key, value); + } + + return rb_ractor_make_shareable(result); + } + default: + return Qundef; + } +} + +/** + * Compile the instructions for pushing the value that will be written to a + * shared constant. + */ +static void +pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_flags_t shareability, VALUE path, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, bool top) +{ + VALUE literal = pm_compile_shareable_constant_literal(iseq, node, scope_node); + if (literal != Qundef) { + const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + PUSH_INSN1(ret, location, putobject, literal); + return; + } + + const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + switch (PM_NODE_TYPE(node)) { + case PM_ARRAY_NODE: { + const pm_array_node_t *cast = (const pm_array_node_t *) node; + + if (top) { + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + } + + for (size_t index = 0; index < cast->elements.size; index++) { + pm_compile_shareable_constant_value(iseq, cast->elements.nodes[index], shareability, path, ret, scope_node, false); + } + + PUSH_INSN1(ret, location, newarray, INT2FIX(cast->elements.size)); + + if (top) { + ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable"); + PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + + return; + } + case PM_HASH_NODE: { + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + + if (top) { + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + } + + for (size_t index = 0; index < cast->elements.size; index++) { + const pm_node_t *element = cast->elements.nodes[index]; + + if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) { + COMPILE_ERROR(ERROR_ARGS "Ractor constant writes do not support **"); + } + + const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element; + pm_compile_shareable_constant_value(iseq, assoc->key, shareability, path, ret, scope_node, false); + pm_compile_shareable_constant_value(iseq, assoc->value, shareability, path, ret, scope_node, false); + } + + PUSH_INSN1(ret, location, newhash, INT2FIX(cast->elements.size * 2)); + + if (top) { + ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable"); + PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + + return; + } + default: { + DECL_ANCHOR(value_seq); + INIT_ANCHOR(value_seq); + + pm_compile_node(iseq, node, value_seq, false, scope_node); + if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) { + PUSH_SEND_WITH_FLAG(value_seq, location, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + + if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) { + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + PUSH_SEQ(ret, value_seq); + PUSH_INSN1(ret, location, putobject, path); + PUSH_SEND_WITH_FLAG(ret, location, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) { + if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + PUSH_SEQ(ret, value_seq); + if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) { + if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + PUSH_SEQ(ret, value_seq); + if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + + break; + } + } +} + +/** + * Compile a constant write node, either in the context of a ractor pragma or + * not. + */ +static void +pm_compile_constant_write_node(rb_iseq_t *iseq, const pm_constant_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +{ + const pm_line_column_t location = *node_location; + ID name_id = pm_constant_id_lookup(scope_node, node->name); + + if (shareability != 0) { + pm_compile_shareable_constant_value(iseq, node->value, shareability, rb_id2str(name_id), ret, scope_node, true); + } + else { + PM_COMPILE_NOT_POPPED(node->value); + } + + if (!popped) PUSH_INSN(ret, location, dup); + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); + PUSH_INSN1(ret, location, setconstant, ID2SYM(name_id)); +} + +/** + * Compile a constant and write node, either in the context of a ractor pragma + * or not. + */ +static void +pm_compile_constant_and_write_node(rb_iseq_t *iseq, const pm_constant_and_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +{ + const pm_line_column_t location = *node_location; + + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name)); + LABEL *end_label = NEW_LABEL(location.line); + + pm_compile_constant_read(iseq, name, &node->name_loc, ret, scope_node); + if (!popped) PUSH_INSN(ret, location, dup); + + PUSH_INSNL(ret, location, branchunless, end_label); + if (!popped) PUSH_INSN(ret, location, pop); + + if (shareability != 0) { + pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true); + } + else { + PM_COMPILE_NOT_POPPED(node->value); + } + + if (!popped) PUSH_INSN(ret, location, dup); + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); + PUSH_INSN1(ret, location, setconstant, name); + PUSH_LABEL(ret, end_label); +} + +/** + * Compile a constant or write node, either in the context of a ractor pragma or + * not. + */ +static void +pm_compile_constant_or_write_node(rb_iseq_t *iseq, const pm_constant_or_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +{ + const pm_line_column_t location = *node_location; + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name)); + + LABEL *set_label = NEW_LABEL(location.line); + LABEL *end_label = NEW_LABEL(location.line); + + PUSH_INSN(ret, location, putnil); + PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue); + PUSH_INSNL(ret, location, branchunless, set_label); + + pm_compile_constant_read(iseq, name, &node->name_loc, ret, scope_node); + if (!popped) PUSH_INSN(ret, location, dup); + + PUSH_INSNL(ret, location, branchif, end_label); + if (!popped) PUSH_INSN(ret, location, pop); + PUSH_LABEL(ret, set_label); + + if (shareability != 0) { + pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true); + } + else { + PM_COMPILE_NOT_POPPED(node->value); + } + + if (!popped) PUSH_INSN(ret, location, dup); + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); + PUSH_INSN1(ret, location, setconstant, name); + PUSH_LABEL(ret, end_label); +} + +/** + * Compile a constant operator write node, either in the context of a ractor + * pragma or not. + */ +static void +pm_compile_constant_operator_write_node(rb_iseq_t *iseq, const pm_constant_operator_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +{ + const pm_line_column_t location = *node_location; + + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name)); + ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator); + + pm_compile_constant_read(iseq, name, &node->name_loc, ret, scope_node); + + if (shareability != 0) { + pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true); + } + else { + PM_COMPILE_NOT_POPPED(node->value); + } + + PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + if (!popped) PUSH_INSN(ret, location, dup); + + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); + PUSH_INSN1(ret, location, setconstant, name); +} + +/** + * Creates a string that is used in ractor error messages to describe the + * constant path being written. + */ +static VALUE +pm_constant_path_path(const pm_constant_path_node_t *node, const pm_scope_node_t *scope_node) +{ + VALUE parts = rb_ary_new(); + rb_ary_push(parts, rb_id2str(pm_constant_id_lookup(scope_node, node->name))); + + const pm_node_t *current = node->parent; + while (current != NULL && PM_NODE_TYPE_P(current, PM_CONSTANT_PATH_NODE)) { + const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) current; + rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, cast->name))); + current = cast->parent; + } + + if (current == NULL) { + rb_ary_unshift(parts, rb_id2str(idNULL)); + } + else if (PM_NODE_TYPE_P(current, PM_CONSTANT_READ_NODE)) { + rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) current)->name))); + } + else { + rb_ary_unshift(parts, rb_str_new_cstr("...")); + } + + return rb_ary_join(parts, rb_str_new_cstr("::")); +} + +/** + * Compile a constant path write node, either in the context of a ractor pragma + * or not. + */ +static void +pm_compile_constant_path_write_node(rb_iseq_t *iseq, const pm_constant_path_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +{ + const pm_line_column_t location = *node_location; + const pm_constant_path_node_t *target = node->target; + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name)); + + if (target->parent) { + PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent); + } + else { + PUSH_INSN1(ret, location, putobject, rb_cObject); + } + + if (shareability != 0) { + pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true); + } + else { + PM_COMPILE_NOT_POPPED(node->value); + } + + if (!popped) { + PUSH_INSN(ret, location, swap); + PUSH_INSN1(ret, location, topn, INT2FIX(1)); + } + + PUSH_INSN(ret, location, swap); + PUSH_INSN1(ret, location, setconstant, name); +} + +/** + * Compile a constant path and write node, either in the context of a ractor + * pragma or not. + */ +static void +pm_compile_constant_path_and_write_node(rb_iseq_t *iseq, const pm_constant_path_and_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +{ + const pm_line_column_t location = *node_location; + const pm_constant_path_node_t *target = node->target; + + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name)); + LABEL *lfin = NEW_LABEL(location.line); + + if (target->parent) { + PM_COMPILE_NOT_POPPED(target->parent); + } + else { + PUSH_INSN1(ret, location, putobject, rb_cObject); + } + + PUSH_INSN(ret, location, dup); + PUSH_INSN1(ret, location, putobject, Qtrue); + PUSH_INSN1(ret, location, getconstant, name); + + if (!popped) PUSH_INSN(ret, location, dup); + PUSH_INSNL(ret, location, branchunless, lfin); + + if (!popped) PUSH_INSN(ret, location, pop); + + if (shareability != 0) { + pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true); + } + else { + PM_COMPILE_NOT_POPPED(node->value); + } + + if (popped) { + PUSH_INSN1(ret, location, topn, INT2FIX(1)); + } + else { + PUSH_INSN1(ret, location, dupn, INT2FIX(2)); + PUSH_INSN(ret, location, swap); + } + + PUSH_INSN1(ret, location, setconstant, name); + PUSH_LABEL(ret, lfin); + + if (!popped) PUSH_INSN(ret, location, swap); + PUSH_INSN(ret, location, pop); +} + +/** + * Compile a constant path or write node, either in the context of a ractor + * pragma or not. + */ +static void +pm_compile_constant_path_or_write_node(rb_iseq_t *iseq, const pm_constant_path_or_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +{ + const pm_line_column_t location = *node_location; + const pm_constant_path_node_t *target = node->target; + + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name)); + LABEL *lassign = NEW_LABEL(location.line); + LABEL *lfin = NEW_LABEL(location.line); + + if (target->parent) { + PM_COMPILE_NOT_POPPED(target->parent); + } + else { + PUSH_INSN1(ret, location, putobject, rb_cObject); + } + + PUSH_INSN(ret, location, dup); + PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue); + PUSH_INSNL(ret, location, branchunless, lassign); + + PUSH_INSN(ret, location, dup); + PUSH_INSN1(ret, location, putobject, Qtrue); + PUSH_INSN1(ret, location, getconstant, name); + + if (!popped) PUSH_INSN(ret, location, dup); + PUSH_INSNL(ret, location, branchif, lfin); + + if (!popped) PUSH_INSN(ret, location, pop); + PUSH_LABEL(ret, lassign); + + if (shareability != 0) { + pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true); + } + else { + PM_COMPILE_NOT_POPPED(node->value); + } + + if (popped) { + PUSH_INSN1(ret, location, topn, INT2FIX(1)); + } + else { + PUSH_INSN1(ret, location, dupn, INT2FIX(2)); + PUSH_INSN(ret, location, swap); + } + + PUSH_INSN1(ret, location, setconstant, name); + PUSH_LABEL(ret, lfin); + + if (!popped) PUSH_INSN(ret, location, swap); + PUSH_INSN(ret, location, pop); +} + +/** + * Compile a constant path operator write node, either in the context of a + * ractor pragma or not. + */ +static void +pm_compile_constant_path_operator_write_node(rb_iseq_t *iseq, const pm_constant_path_operator_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +{ + const pm_line_column_t location = *node_location; + const pm_constant_path_node_t *target = node->target; + + ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator); + VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name)); + + if (target->parent) { + PM_COMPILE_NOT_POPPED(target->parent); + } + else { + PUSH_INSN1(ret, location, putobject, rb_cObject); + } + + PUSH_INSN(ret, location, dup); + PUSH_INSN1(ret, location, putobject, Qtrue); + PUSH_INSN1(ret, location, getconstant, name); + + if (shareability != 0) { + pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true); + } + else { + PM_COMPILE_NOT_POPPED(node->value); + } + + PUSH_CALL(ret, location, method_id, INT2FIX(1)); + PUSH_INSN(ret, location, swap); + + if (!popped) { + PUSH_INSN1(ret, location, topn, INT2FIX(1)); + PUSH_INSN(ret, location, swap); + } + + PUSH_INSN1(ret, location, setconstant, name); +} + +/** * Compiles a prism node into instruction sequences. * * iseq - The current instruction sequence object (used for locals) @@ -4349,14 +5069,16 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(parser, node); int lineno = (int) location.line; - if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) { - int event = RUBY_EVENT_LINE; + if (!PM_NODE_TYPE_P(node, PM_RETURN_NODE) || !PM_NODE_FLAG_P(node, PM_RETURN_NODE_FLAGS_REDUNDANT) || ((const pm_return_node_t *) node)->arguments != NULL) { + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) { + int event = RUBY_EVENT_LINE; - ISEQ_COMPILE_DATA(iseq)->last_line = lineno; - if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) { - event |= RUBY_EVENT_COVERAGE_LINE; + ISEQ_COMPILE_DATA(iseq)->last_line = lineno; + if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) { + event |= RUBY_EVENT_COVERAGE_LINE; + } + PUSH_TRACE(ret, event); } - PUSH_TRACE(ret, event); } switch (PM_NODE_TYPE(node)) { @@ -4820,7 +5542,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag)); PM_COMPILE_NOT_POPPED(cast->value); - ID id_operator = pm_constant_id_lookup(scope_node, cast->operator); + ID id_operator = pm_constant_id_lookup(scope_node, cast->binary_operator); PUSH_SEND(ret, location, id_operator, INT2FIX(1)); if (!popped) { @@ -4863,6 +5585,16 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // compare all of the various when clauses to the predicate. If we // don't, then it's basically an if-elsif-else chain. if (cast->predicate == NULL) { + // Establish branch coverage for the case node. + VALUE branches = Qfalse; + rb_code_location_t case_location = { 0 }; + int branch_id = 0; + + if (PM_BRANCH_COVERAGE_P(iseq)) { + case_location = pm_code_location(scope_node, (const pm_node_t *) cast); + branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case"); + } + // Loop through each clauses in the case node and compile each of // the conditions within them into cond_seq. If they match, they // should jump into their respective bodies in body_seq. @@ -4872,8 +5604,14 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, int clause_lineno = pm_node_line_number(parser, (const pm_node_t *) clause); LABEL *label = NEW_LABEL(clause_lineno); - PUSH_LABEL(body_seq, label); + + // Establish branch coverage for the when clause. + if (PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t branch_location = pm_code_location(scope_node, clause->statements != NULL ? ((const pm_node_t *) clause->statements) : ((const pm_node_t *) clause)); + add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches); + } + if (clause->statements != NULL) { pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node); } @@ -4890,10 +5628,11 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, const pm_node_t *condition = conditions->nodes[condition_index]; if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) { - PUSH_INSN(cond_seq, location, putnil); + pm_line_column_t cond_location = PM_NODE_START_LINE_COLUMN(parser, condition); + PUSH_INSN(cond_seq, cond_location, putnil); pm_compile_node(iseq, condition, cond_seq, false, scope_node); - PUSH_INSN1(cond_seq, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY)); - PUSH_INSNL(cond_seq, location, branchif, label); + PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY)); + PUSH_INSNL(cond_seq, cond_location, branchif, label); } else { LABEL *next_label = NEW_LABEL(pm_node_line_number(parser, condition)); @@ -4903,12 +5642,28 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } } + // Establish branch coverage for the else clause (implicit or + // explicit). + if (PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t branch_location; + + if (cast->consequent == NULL) { + branch_location = case_location; + } else if (cast->consequent->statements == NULL) { + branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->consequent); + } else { + branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->consequent->statements); + } + + add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches); + } + // Compile the consequent else clause if there is one. - if (cast->consequent) { + if (cast->consequent != NULL) { pm_compile_node(iseq, (const pm_node_t *) cast->consequent, cond_seq, popped, scope_node); } else if (!popped) { - PUSH_INSN(cond_seq, location, putnil); + PUSH_SYNTHETIC_PUTNIL(cond_seq, iseq); } // Finally, jump to the end label if none of the other conditions @@ -4917,6 +5672,16 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_SEQ(ret, cond_seq); } else { + // Establish branch coverage for the case node. + VALUE branches = Qfalse; + rb_code_location_t case_location = { 0 }; + int branch_id = 0; + + if (PM_BRANCH_COVERAGE_P(iseq)) { + case_location = pm_code_location(scope_node, (const pm_node_t *) cast); + branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case"); + } + // This is the label where everything will fall into if none of the // conditions matched. LABEL *else_label = NEW_LABEL(location.line); @@ -4942,15 +5707,17 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // node instructions later. for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) { const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index]; - const pm_node_list_t *conditions = &clause->conditions; + pm_line_column_t clause_location = PM_NODE_START_LINE_COLUMN(parser, (const pm_node_t *) clause); - LABEL *label = NEW_LABEL(location.line); + const pm_node_list_t *conditions = &clause->conditions; + LABEL *label = NEW_LABEL(clause_location.line); // Compile each of the conditions for the when clause into the // cond_seq. Each one should have a unique comparison that then // jumps into the body if it matches. for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) { const pm_node_t *condition = conditions->nodes[condition_index]; + const pm_line_column_t condition_location = PM_NODE_START_LINE_COLUMN(parser, condition); // If we haven't already abandoned the optimization, then // we're going to try to compile the condition into the @@ -4960,25 +5727,25 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) { - PUSH_INSN(cond_seq, location, dup); + PUSH_INSN(cond_seq, condition_location, dup); pm_compile_node(iseq, condition, cond_seq, false, scope_node); - PUSH_INSN1(cond_seq, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY)); + PUSH_INSN1(cond_seq, condition_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY)); } else { if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) { const pm_string_node_t *string = (const pm_string_node_t *) condition; VALUE value = parse_static_literal_string(iseq, scope_node, condition, &string->unescaped); - PUSH_INSN1(cond_seq, location, putobject, value); + PUSH_INSN1(cond_seq, condition_location, putobject, value); } else { pm_compile_node(iseq, condition, cond_seq, false, scope_node); } - PUSH_INSN1(cond_seq, location, topn, INT2FIX(1)); - PUSH_SEND_WITH_FLAG(cond_seq, location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE)); + PUSH_INSN1(cond_seq, condition_location, topn, INT2FIX(1)); + PUSH_SEND_WITH_FLAG(cond_seq, condition_location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE)); } - PUSH_INSNL(cond_seq, location, branchif, label); + PUSH_INSNL(cond_seq, condition_location, branchif, label); } // Now, add the label to the body and compile the body of the @@ -4986,16 +5753,22 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // the statements to be executed, and then compiling a jump to // the end of the case node. PUSH_LABEL(body_seq, label); - PUSH_INSN(body_seq, location, pop); + PUSH_INSN(body_seq, clause_location, pop); + + // Establish branch coverage for the when clause. + if (PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t branch_location = pm_code_location(scope_node, clause->statements != NULL ? ((const pm_node_t *) clause->statements) : ((const pm_node_t *) clause)); + add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches); + } if (clause->statements != NULL) { pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node); } else if (!popped) { - PUSH_INSN(body_seq, location, putnil); + PUSH_INSN(body_seq, clause_location, putnil); } - PUSH_INSNL(body_seq, location, jump, end_label); + PUSH_INSNL(body_seq, clause_location, jump, end_label); } // Now that we have compiled the conditions and the bodies of the @@ -5017,16 +5790,31 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // Compile either the explicit else clause or an implicit else // clause. PUSH_LABEL(ret, else_label); - PUSH_INSN(ret, location, pop); if (cast->consequent != NULL) { + pm_line_column_t else_location = PM_NODE_START_LINE_COLUMN(parser, cast->consequent->statements != NULL ? ((const pm_node_t *) cast->consequent->statements) : ((const pm_node_t *) cast->consequent)); + PUSH_INSN(ret, else_location, pop); + + // Establish branch coverage for the else clause. + if (PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t branch_location = pm_code_location(scope_node, cast->consequent->statements != NULL ? ((const pm_node_t *) cast->consequent->statements) : ((const pm_node_t *) cast->consequent)); + add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches); + } + PM_COMPILE((const pm_node_t *) cast->consequent); + PUSH_INSNL(ret, else_location, jump, end_label); } - else if (!popped) { - PUSH_INSN(ret, location, putnil); - } + else { + PUSH_INSN(ret, location, pop); + + // Establish branch coverage for the implicit else clause. + if (PM_BRANCH_COVERAGE_P(iseq)) { + add_trace_branch_coverage(iseq, ret, &case_location, case_location.beg_pos.column, branch_id, "else", branches); + } - PUSH_INSNL(ret, location, jump, end_label); + if (!popped) PUSH_INSN(ret, location, putnil); + PUSH_INSNL(ret, location, jump, end_label); + } } PUSH_SEQ(ret, body_seq); @@ -5068,8 +5856,14 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // We're going to use this to uniquely identify each branch so that we // can track coverage information. + rb_code_location_t case_location; + VALUE branches = Qfalse; int branch_id = 0; - // VALUE branches = 0; + + if (PM_BRANCH_COVERAGE_P(iseq)) { + case_location = pm_code_location(scope_node, (const pm_node_t *) cast); + branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case"); + } // If there is only one pattern, then the behavior changes a bit. It // effectively gets treated as a match required node (this is how it is @@ -5109,12 +5903,12 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_LABEL(body_seq, body_label); PUSH_INSN1(body_seq, in_location, adjuststack, INT2FIX(in_single_pattern ? 6 : 2)); - // TODO: We need to come back to this and enable trace branch - // coverage. At the moment we can't call this function because it - // accepts a NODE* and not a pm_node_t*. - // add_trace_branch_coverage(iseq, body_seq, in_node->statements || in, branch_id++, "in", branches); + // Establish branch coverage for the in clause. + if (PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t branch_location = pm_code_location(scope_node, in_node->statements != NULL ? ((const pm_node_t *) in_node->statements) : ((const pm_node_t *) in_node)); + add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "in", branches); + } - branch_id++; if (in_node->statements != NULL) { PM_COMPILE_INTO_ANCHOR(body_seq, (const pm_node_t *) in_node->statements); } @@ -5141,16 +5935,13 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_INSN(cond_seq, location, pop); PUSH_INSN(cond_seq, location, pop); - // TODO: trace branch coverage - // add_trace_branch_coverage(iseq, cond_seq, cast->consequent, branch_id, "else", branches); - - if (else_node->statements != NULL) { - PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node->statements); - } - else if (!popped) { - PUSH_INSN(cond_seq, location, putnil); + // Establish branch coverage for the else clause. + if (PM_BRANCH_COVERAGE_P(iseq)) { + rb_code_location_t branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : ((const pm_node_t *) else_node)); + add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches); } + PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node); PUSH_INSNL(cond_seq, location, jump, end_label); PUSH_INSN(cond_seq, location, putnil); if (popped) PUSH_INSN(cond_seq, location, putnil); @@ -5160,8 +5951,8 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // the code to handle raising an appropriate error. PUSH_LABEL(cond_seq, else_label); - // TODO: trace branch coverage - // add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches); + // Establish branch coverage for the implicit else clause. + add_trace_branch_coverage(iseq, cond_seq, &case_location, case_location.beg_pos.column, branch_id, "else", branches); if (in_single_pattern) { pm_compile_pattern_error_handler(iseq, scope_node, node, cond_seq, end_label, popped); @@ -5255,7 +6046,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id)); PM_COMPILE_NOT_POPPED(cast->value); - ID method_id = pm_constant_id_lookup(scope_node, cast->operator); + ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator); int flags = VM_CALL_ARGS_SIMPLE; PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags)); @@ -5349,154 +6140,28 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // Foo::Bar &&= baz // ^^^^^^^^^^^^^^^^ const pm_constant_path_and_write_node_t *cast = (const pm_constant_path_and_write_node_t *) node; - const pm_constant_path_node_t *target = cast->target; - - const pm_constant_read_node_t *child = (const pm_constant_read_node_t *) target->child; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, child->name)); - LABEL *lfin = NEW_LABEL(location.line); - - if (target->parent) { - PM_COMPILE_NOT_POPPED(target->parent); - } - else { - PUSH_INSN1(ret, location, putobject, rb_cObject); - } - - PUSH_INSN(ret, location, dup); - PUSH_INSN1(ret, location, putobject, Qtrue); - PUSH_INSN1(ret, location, getconstant, name); - - if (!popped) PUSH_INSN(ret, location, dup); - PUSH_INSNL(ret, location, branchunless, lfin); - - if (!popped) PUSH_INSN(ret, location, pop); - PM_COMPILE_NOT_POPPED(cast->value); - - if (popped) { - PUSH_INSN1(ret, location, topn, INT2FIX(1)); - } - else { - PUSH_INSN1(ret, location, dupn, INT2FIX(2)); - PUSH_INSN(ret, location, swap); - } - - PUSH_INSN1(ret, location, setconstant, name); - PUSH_LABEL(ret, lfin); - - if (!popped) PUSH_INSN(ret, location, swap); - PUSH_INSN(ret, location, pop); - + pm_compile_constant_path_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node); return; } case PM_CONSTANT_PATH_OR_WRITE_NODE: { // Foo::Bar ||= baz // ^^^^^^^^^^^^^^^^ const pm_constant_path_or_write_node_t *cast = (const pm_constant_path_or_write_node_t *) node; - const pm_constant_path_node_t *target = cast->target; - - const pm_constant_read_node_t *child = (const pm_constant_read_node_t *) target->child; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, child->name)); - - LABEL *lassign = NEW_LABEL(location.line); - LABEL *lfin = NEW_LABEL(location.line); - - if (target->parent) { - PM_COMPILE_NOT_POPPED(target->parent); - } - else { - PUSH_INSN1(ret, location, putobject, rb_cObject); - } - - PUSH_INSN(ret, location, dup); - PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue); - PUSH_INSNL(ret, location, branchunless, lassign); - - PUSH_INSN(ret, location, dup); - PUSH_INSN1(ret, location, putobject, Qtrue); - PUSH_INSN1(ret, location, getconstant, name); - - if (!popped) PUSH_INSN(ret, location, dup); - PUSH_INSNL(ret, location, branchif, lfin); - - if (!popped) PUSH_INSN(ret, location, pop); - PUSH_LABEL(ret, lassign); - PM_COMPILE_NOT_POPPED(cast->value); - - if (popped) { - PUSH_INSN1(ret, location, topn, INT2FIX(1)); - } - else { - PUSH_INSN1(ret, location, dupn, INT2FIX(2)); - PUSH_INSN(ret, location, swap); - } - - PUSH_INSN1(ret, location, setconstant, name); - PUSH_LABEL(ret, lfin); - - if (!popped) PUSH_INSN(ret, location, swap); - PUSH_INSN(ret, location, pop); - + pm_compile_constant_path_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node); return; } case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { // Foo::Bar += baz // ^^^^^^^^^^^^^^^ const pm_constant_path_operator_write_node_t *cast = (const pm_constant_path_operator_write_node_t *) node; - const pm_constant_path_node_t *target = cast->target; - ID method_id = pm_constant_id_lookup(scope_node, cast->operator); - - const pm_constant_read_node_t *child = (const pm_constant_read_node_t *) target->child; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, child->name)); - - if (target->parent) { - PM_COMPILE_NOT_POPPED(target->parent); - } - else { - PUSH_INSN1(ret, location, putobject, rb_cObject); - } - - PUSH_INSN(ret, location, dup); - PUSH_INSN1(ret, location, putobject, Qtrue); - PUSH_INSN1(ret, location, getconstant, name); - - PM_COMPILE_NOT_POPPED(cast->value); - PUSH_CALL(ret, location, method_id, INT2FIX(1)); - PUSH_INSN(ret, location, swap); - - if (!popped) { - PUSH_INSN1(ret, location, topn, INT2FIX(1)); - PUSH_INSN(ret, location, swap); - } - - PUSH_INSN1(ret, location, setconstant, name); + pm_compile_constant_path_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node); return; } case PM_CONSTANT_PATH_WRITE_NODE: { // Foo::Bar = 1 // ^^^^^^^^^^^^ const pm_constant_path_write_node_t *cast = (const pm_constant_path_write_node_t *) node; - const pm_constant_path_node_t *target = cast->target; - - const pm_constant_read_node_t *child = (const pm_constant_read_node_t *) target->child; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, child->name)); - - if (target->parent) { - PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent); - } - else { - PUSH_INSN1(ret, location, putobject, rb_cObject); - } - - PM_COMPILE_NOT_POPPED(cast->value); - - if (!popped) { - PUSH_INSN(ret, location, swap); - PUSH_INSN1(ret, location, topn, INT2FIX(1)); - } - - PUSH_INSN(ret, location, swap); - PUSH_INSN1(ret, location, setconstant, name); - + pm_compile_constant_path_write_node(iseq, cast, 0, &location, ret, popped, scope_node); return; } case PM_CONSTANT_READ_NODE: { @@ -5514,82 +6179,28 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // Foo &&= bar // ^^^^^^^^^^^ const pm_constant_and_write_node_t *cast = (const pm_constant_and_write_node_t *) node; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name)); - LABEL *end_label = NEW_LABEL(location.line); - - pm_compile_constant_read(iseq, name, &cast->name_loc, ret, scope_node); - if (!popped) PUSH_INSN(ret, location, dup); - - PUSH_INSNL(ret, location, branchunless, end_label); - if (!popped) PUSH_INSN(ret, location, pop); - - PM_COMPILE_NOT_POPPED(cast->value); - if (!popped) PUSH_INSN(ret, location, dup); - - PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); - PUSH_INSN1(ret, location, setconstant, name); - PUSH_LABEL(ret, end_label); - + pm_compile_constant_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node); return; } case PM_CONSTANT_OR_WRITE_NODE: { // Foo ||= bar // ^^^^^^^^^^^ const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name)); - LABEL *set_label = NEW_LABEL(location.line); - LABEL *end_label = NEW_LABEL(location.line); - - PUSH_INSN(ret, location, putnil); - PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue); - PUSH_INSNL(ret, location, branchunless, set_label); - - pm_compile_constant_read(iseq, name, &cast->name_loc, ret, scope_node); - if (!popped) PUSH_INSN(ret, location, dup); - - PUSH_INSNL(ret, location, branchif, end_label); - if (!popped) PUSH_INSN(ret, location, pop); - - PUSH_LABEL(ret, set_label); - PM_COMPILE_NOT_POPPED(cast->value); - if (!popped) PUSH_INSN(ret, location, dup); - - PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); - PUSH_INSN1(ret, location, setconstant, name); - PUSH_LABEL(ret, end_label); - + pm_compile_constant_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node); return; } case PM_CONSTANT_OPERATOR_WRITE_NODE: { // Foo += bar // ^^^^^^^^^^ const pm_constant_operator_write_node_t *cast = (const pm_constant_operator_write_node_t *) node; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name)); - ID method_id = pm_constant_id_lookup(scope_node, cast->operator); - - pm_compile_constant_read(iseq, name, &cast->name_loc, ret, scope_node); - PM_COMPILE_NOT_POPPED(cast->value); - - PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); - if (!popped) PUSH_INSN(ret, location, dup); - - PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); - PUSH_INSN1(ret, location, setconstant, name); - + pm_compile_constant_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node); return; } case PM_CONSTANT_WRITE_NODE: { // Foo = 1 // ^^^^^^^ const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node; - VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name)); - - PM_COMPILE_NOT_POPPED(cast->value); - if (!popped) PUSH_INSN(ret, location, dup); - - PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); - PUSH_INSN1(ret, location, setconstant, name); - + pm_compile_constant_write_node(iseq, cast, 0, &location, ret, popped, scope_node); return; } case PM_DEF_NODE: { @@ -5686,7 +6297,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PM_COMPILE((const pm_node_t *) cast->statements); } else if (!popped) { - PUSH_INSN(ret, location, putnil); + PUSH_SYNTHETIC_PUTNIL(ret, iseq); } return; @@ -5745,33 +6356,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // Now, create the method call to each that will be used to iterate over // the collection, and pass the newly created iseq as the block. PUSH_SEND_WITH_BLOCK(ret, location, idEach, INT2FIX(0), child_iseq); - - // We need to put the label "retry_end_l" immediately after the last - // "send" instruction. This because vm_throw checks if the break cont is - // equal to the index of next insn of the "send". (Otherwise, it is - // considered "break from proc-closure". See "TAG_BREAK" handling in - // "vm_throw_start".) - // - // Normally, "send" instruction is at the last. However, qcall under - // branch coverage measurement adds some instructions after the "send". - // - // Note that "invokesuper" appears instead of "send". - { - INSN *iobj; - LINK_ELEMENT *last_elem = LAST_ELEMENT(ret); - iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem); - while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) { - iobj = (INSN*) get_prev_insn(iobj); - } - ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l); - - // LINK_ANCHOR has a pointer to the last element, but - // ELEM_INSERT_NEXT does not update it even if we add an insn to the - // last of LINK_ANCHOR. So this updates it manually. - if (&iobj->link == LAST_ELEMENT(ret)) { - ret->last = (LINK_ELEMENT*) retry_end_l; - } - } + pm_compile_retry_end_label(iseq, ret, retry_end_l); if (popped) PUSH_INSN(ret, location, pop); ISEQ_COMPILE_DATA(iseq)->current_block = prev_block; @@ -5914,7 +6499,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_INSN2(ret, location, invokesuper, new_callinfo(iseq, 0, argc, flag, NULL, block != NULL), block); if (cast->block != NULL) { - PUSH_LABEL(ret, retry_end_l); + pm_compile_retry_end_label(iseq, ret, retry_end_l); PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, block, retry_end_l); ISEQ_COMPILE_DATA(iseq)->current_block = previous_block; } @@ -5952,7 +6537,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_INSN1(ret, location, getglobal, name); PM_COMPILE_NOT_POPPED(cast->value); - ID method_id = pm_constant_id_lookup(scope_node, cast->operator); + ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator); int flags = VM_CALL_ARGS_SIMPLE; PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags)); @@ -6065,7 +6650,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // foo ? bar : baz // ^^^^^^^^^^^^^^^ const pm_if_node_t *cast = (const pm_if_node_t *) node; - pm_compile_conditional(iseq, &location, cast->statements, cast->consequent, cast->predicate, ret, popped, scope_node); + pm_compile_conditional(iseq, &location, PM_IF_NODE, (const pm_node_t *) cast, cast->statements, cast->consequent, cast->predicate, ret, popped, scope_node); return; } case PM_IMAGINARY_NODE: { @@ -6150,7 +6735,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id)); PM_COMPILE_NOT_POPPED(cast->value); - ID method_id = pm_constant_id_lookup(scope_node, cast->operator); + ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator); int flags = VM_CALL_ARGS_SIMPLE; PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags)); @@ -6286,7 +6871,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } else { const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node; - int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node); + int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL); if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length)); if (popped) PUSH_INSN(ret, location, pop); } @@ -6305,7 +6890,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } } else { - int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node); + int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL); if (length > 1) { PUSH_INSN1(ret, location, concatstrings, INT2FIX(length)); } @@ -6327,7 +6912,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_INSN(ret, location, putself); - int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node); + int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL); if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length)); PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE)); @@ -6400,7 +6985,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PM_COMPILE_NOT_POPPED(cast->value); - ID method_id = pm_constant_id_lookup(scope_node, cast->operator); + ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator); PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); if (!popped) PUSH_INSN(ret, location, dup); @@ -7205,53 +7790,63 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, const pm_return_node_t *cast = (const pm_return_node_t *) node; const pm_arguments_node_t *arguments = cast->arguments; - enum rb_iseq_type type = ISEQ_BODY(iseq)->type; - LABEL *splabel = 0; - - const rb_iseq_t *parent_iseq = iseq; - enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type; - while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) { - if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break; - parent_type = ISEQ_BODY(parent_iseq)->type; - } - - switch (parent_type) { - case ISEQ_TYPE_TOP: - case ISEQ_TYPE_MAIN: + if (PM_NODE_FLAG_P(cast, PM_RETURN_NODE_FLAGS_REDUNDANT)) { if (arguments) { - rb_warn("argument of top-level return is ignored"); + PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments); } - if (parent_iseq == iseq) { - type = ISEQ_TYPE_METHOD; + else { + PUSH_INSN(ret, location, putnil); } - break; - default: - break; } + else { + enum rb_iseq_type type = ISEQ_BODY(iseq)->type; + LABEL *splabel = 0; - if (type == ISEQ_TYPE_METHOD) { - splabel = NEW_LABEL(0); - PUSH_LABEL(ret, splabel); - PUSH_ADJUST(ret, location, 0); - } + const rb_iseq_t *parent_iseq = iseq; + enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type; + while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) { + if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break; + parent_type = ISEQ_BODY(parent_iseq)->type; + } - if (arguments) { - PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments); - } - else { - PUSH_INSN(ret, location, putnil); - } + switch (parent_type) { + case ISEQ_TYPE_TOP: + case ISEQ_TYPE_MAIN: + if (arguments) { + rb_warn("argument of top-level return is ignored"); + } + if (parent_iseq == iseq) { + type = ISEQ_TYPE_METHOD; + } + break; + default: + break; + } - if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) { - pm_add_ensure_iseq(ret, iseq, 1, scope_node); - PUSH_TRACE(ret, RUBY_EVENT_RETURN); - PUSH_INSN(ret, location, leave); - PUSH_ADJUST_RESTORE(ret, splabel); - if (!popped) PUSH_INSN(ret, location, putnil); - } - else { - PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETURN)); - if (popped) PUSH_INSN(ret, location, pop); + if (type == ISEQ_TYPE_METHOD) { + splabel = NEW_LABEL(0); + PUSH_LABEL(ret, splabel); + PUSH_ADJUST(ret, location, 0); + } + + if (arguments) { + PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments); + } + else { + PUSH_INSN(ret, location, putnil); + } + + if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) { + pm_add_ensure_iseq(ret, iseq, 1, scope_node); + PUSH_TRACE(ret, RUBY_EVENT_RETURN); + PUSH_INSN(ret, location, leave); + PUSH_ADJUST_RESTORE(ret, splabel); + if (!popped) PUSH_INSN(ret, location, putnil); + } + else { + PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETURN)); + if (popped) PUSH_INSN(ret, location, pop); + } } return; @@ -8150,7 +8745,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, break; } - if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE)) { + if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) { const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, scope_node->ast_node); ADD_TRACE(ret, RUBY_EVENT_END); ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line; @@ -8174,7 +8769,38 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, case PM_SHAREABLE_CONSTANT_NODE: { // A value that is being written to a constant that is being marked as // shared depending on the current lexical context. - PM_COMPILE(((const pm_shareable_constant_node_t *) node)->write); + const pm_shareable_constant_node_t *cast = (const pm_shareable_constant_node_t *) node; + + switch (PM_NODE_TYPE(cast->write)) { + case PM_CONSTANT_WRITE_NODE: + pm_compile_constant_write_node(iseq, (const pm_constant_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node); + break; + case PM_CONSTANT_AND_WRITE_NODE: + pm_compile_constant_and_write_node(iseq, (const pm_constant_and_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node); + break; + case PM_CONSTANT_OR_WRITE_NODE: + pm_compile_constant_or_write_node(iseq, (const pm_constant_or_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node); + break; + case PM_CONSTANT_OPERATOR_WRITE_NODE: + pm_compile_constant_operator_write_node(iseq, (const pm_constant_operator_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node); + break; + case PM_CONSTANT_PATH_WRITE_NODE: + pm_compile_constant_path_write_node(iseq, (const pm_constant_path_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node); + break; + case PM_CONSTANT_PATH_AND_WRITE_NODE: + pm_compile_constant_path_and_write_node(iseq, (const pm_constant_path_and_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node); + break; + case PM_CONSTANT_PATH_OR_WRITE_NODE: + pm_compile_constant_path_or_write_node(iseq, (const pm_constant_path_or_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node); + break; + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: + pm_compile_constant_path_operator_write_node(iseq, (const pm_constant_path_operator_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node); + break; + default: + rb_bug("Unexpected node type for shareable constant write: %s", pm_node_type_to_str(PM_NODE_TYPE(cast->write))); + break; + } + return; } case PM_SINGLETON_CLASS_NODE: { @@ -8321,9 +8947,9 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_SEQ(ret, args); PUSH_INSN2(ret, location, invokesuper, new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL), current_block); - PUSH_LABEL(ret, retry_end_l); - if (popped) PUSH_INSN(ret, location, pop); + pm_compile_retry_end_label(iseq, ret, retry_end_l); + if (popped) PUSH_INSN(ret, location, pop); ISEQ_COMPILE_DATA(iseq)->current_block = previous_block; PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, current_block, retry_end_l); @@ -8379,7 +9005,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, consequent = ((const pm_else_node_t *) cast->consequent)->statements; } - pm_compile_conditional(iseq, &location, consequent, (const pm_node_t *) cast->statements, cast->predicate, ret, popped, scope_node); + pm_compile_conditional(iseq, &location, PM_UNLESS_NODE, (const pm_node_t *) cast, consequent, (const pm_node_t *) cast->statements, cast->predicate, ret, popped, scope_node); return; } case PM_UNTIL_NODE: { @@ -8389,7 +9015,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // bar until foo // ^^^^^^^^^^^^^ const pm_until_node_t *cast = (const pm_until_node_t *) node; - pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, cast->statements, cast->predicate, ret, popped, scope_node); + pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node); return; } case PM_WHILE_NODE: { @@ -8399,7 +9025,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // bar while foo // ^^^^^^^^^^^^^ const pm_while_node_t *cast = (const pm_while_node_t *) node; - pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, cast->statements, cast->predicate, ret, popped, scope_node); + pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node); return; } case PM_X_STRING_NODE: { @@ -8643,7 +9269,8 @@ pm_parse_process_error(const pm_parse_result_t *result) } } - VALUE error = rb_exc_new(rb_eSyntaxError, pm_buffer_value(&buffer), pm_buffer_length(&buffer)); + VALUE message = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), result->node.encoding); + VALUE error = rb_exc_new_str(rb_eSyntaxError, message); rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding(); VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding); @@ -8654,6 +9281,9 @@ pm_parse_process_error(const pm_parse_result_t *result) return error; } +void rb_enc_compile_warning(rb_encoding *enc, const char *file, int line, const char *fmt, ...); +void rb_enc_compile_warn(rb_encoding *enc, const char *file, int line, const char *fmt, ...); + /** * Parse the parse result and raise a Ruby error if there are any syntax errors. * It returns an error if one should be raised. It is assumed that the parse @@ -8672,6 +9302,9 @@ pm_parse_process(pm_parse_result_t *result, pm_node_t *node) pm_scope_node_init(node, scope_node, NULL); scope_node->filepath_encoding = filepath_encoding; + scope_node->encoding = rb_enc_find(parser->encoding->name); + if (!scope_node->encoding) rb_bug("Encoding not found %s!", parser->encoding->name); + // Emit all of the various warnings from the parse. const pm_diagnostic_t *warning; const char *warning_filepath = (const char *) pm_string_source(&parser->filepath); @@ -8680,10 +9313,10 @@ pm_parse_process(pm_parse_result_t *result, pm_node_t *node) int line = pm_location_line_number(parser, &warning->location); if (warning->level == PM_WARNING_LEVEL_VERBOSE) { - rb_compile_warning(warning_filepath, line, "%s", warning->message); + rb_enc_compile_warning(scope_node->encoding, warning_filepath, line, "%s", warning->message); } else { - rb_compile_warn(warning_filepath, line, "%s", warning->message); + rb_enc_compile_warn(scope_node->encoding, warning_filepath, line, "%s", warning->message); } } @@ -8698,9 +9331,6 @@ pm_parse_process(pm_parse_result_t *result, pm_node_t *node) // Now set up the constant pool and intern all of the various constants into // their corresponding IDs. - scope_node->encoding = rb_enc_find(parser->encoding->name); - if (!scope_node->encoding) rb_bug("Encoding not found %s!", parser->encoding->name); - scope_node->parser = parser; scope_node->constants = calloc(parser->constant_pool.size, sizeof(ID)); @@ -8785,7 +9415,7 @@ pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t * be read. */ VALUE -pm_load_file(pm_parse_result_t *result, VALUE filepath) +pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error) { if (!pm_string_mapped_init(&result->input, RSTRING_PTR(filepath))) { #ifdef _WIN32 @@ -8794,9 +9424,21 @@ pm_load_file(pm_parse_result_t *result, VALUE filepath) int e = errno; #endif - VALUE err = rb_syserr_new(e, RSTRING_PTR(filepath)); - RB_GC_GUARD(filepath); - return err; + VALUE error; + + if (load_error) { + VALUE message = rb_str_buf_new_cstr(strerror(e)); + rb_str_cat2(message, " -- "); + rb_str_append(message, filepath); + + error = rb_exc_new3(rb_eLoadError, message); + rb_ivar_set(error, rb_intern_const("@path"), filepath); + } else { + error = rb_syserr_new(e, RSTRING_PTR(filepath)); + RB_GC_GUARD(filepath); + } + + return error; } pm_options_frozen_string_literal_init(&result->options); @@ -8843,7 +9485,7 @@ pm_parse_file(pm_parse_result_t *result, VALUE filepath) VALUE pm_load_parse_file(pm_parse_result_t *result, VALUE filepath) { - VALUE error = pm_load_file(result, filepath); + VALUE error = pm_load_file(result, filepath, false); if (NIL_P(error)) { error = pm_parse_file(result, filepath); } diff --git a/prism_compile.h b/prism_compile.h index e58bed271f..0f82782ec0 100644 --- a/prism_compile.h +++ b/prism_compile.h @@ -72,7 +72,7 @@ typedef struct { bool parsed; } pm_parse_result_t; -VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath); +VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error); VALUE pm_parse_file(pm_parse_result_t *result, VALUE filepath); VALUE pm_load_parse_file(pm_parse_result_t *result, VALUE filepath); VALUE pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath); @@ -3467,7 +3467,7 @@ proc_binding(VALUE self) env = VM_ENV_ENVVAL_PTR(block->as.captured.ep); env = env_clone(env, method_cref(method)); /* set empty iseq */ - empty = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP); + empty = rb_iseq_new(Qnil, name, name, Qnil, 0, ISEQ_TYPE_TOP); RB_OBJ_WRITE(env, &env->iseq, empty); break; } @@ -1804,17 +1804,17 @@ ractor_select_internal(rb_execution_context_t *ec, VALUE self, VALUE ractors, VA int state; EC_PUSH_TAG(ec); - if ((state = EC_EXEC_TAG() == TAG_NONE)) { + if ((state = EC_EXEC_TAG()) == TAG_NONE) { result = ractor_selector__wait(selector, do_receive, do_yield, yield_value, move); } - else { + EC_POP_TAG(); + if (state != TAG_NONE) { // ensure ractor_selector_clear(selector); // jump EC_JUMP_TAG(ec, state); } - EC_POP_TAG(); RB_GC_GUARD(ractors); return result; @@ -2012,12 +2012,11 @@ ractor_alloc(VALUE klass) rb_ractor_t * rb_ractor_main_alloc(void) { - rb_ractor_t *r = ruby_mimmalloc(sizeof(rb_ractor_t)); + rb_ractor_t *r = ruby_mimcalloc(1, sizeof(rb_ractor_t)); if (r == NULL) { fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n"); exit(EXIT_FAILURE); } - MEMZERO(r, rb_ractor_t, 1); r->pub.id = ++ractor_last_id; r->loc = Qnil; r->name = Qnil; @@ -1530,8 +1530,8 @@ reg_enc_error(VALUE re, VALUE str) { rb_raise(rb_eEncCompatError, "incompatible encoding regexp match (%s regexp with %s string)", - rb_enc_name(rb_enc_get(re)), - rb_enc_name(rb_enc_get(str))); + rb_enc_inspect_name(rb_enc_get(re)), + rb_enc_inspect_name(rb_enc_get(str))); } static inline int @@ -3449,8 +3449,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end, CASE(OP_MEMORY_END_PUSH_REC) MOP_IN(OP_MEMORY_END_PUSH_REC); GET_MEMNUM_INC(mem, p); STACK_GET_MEM_START(mem, stkp); /* should be before push mem-end. */ - STACK_PUSH_MEM_END(mem, s); mem_start_stk[mem] = GET_STACK_INDEX(stkp); + STACK_PUSH_MEM_END(mem, s); MOP_OUT; JUMP; @@ -4218,7 +4218,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end, timeout: xfree(xmalloc_base); - xfree(stk_base); + if (stk_base != stk_alloc || IS_NOT_NULL(msa->stack_p)) + xfree(stk_base); HANDLE_REG_TIMEOUT_IN_MATCH_AT; } @@ -4920,12 +4921,17 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s, UChar* range, UChar** low, UChar** high, UChar** low_prev) { UChar *p, *pprev = (UChar* )NULL; + size_t input_len = end - str; #ifdef ONIG_DEBUG_SEARCH fprintf(stderr, "forward_search_range: str: %"PRIuPTR" (%p), end: %"PRIuPTR" (%p), s: %"PRIuPTR" (%p), range: %"PRIuPTR" (%p)\n", (uintptr_t )str, str, (uintptr_t )end, end, (uintptr_t )s, s, (uintptr_t )range, range); #endif + if (reg->dmin > input_len) { + return 0; + } + p = s; if (reg->dmin > 0) { if (ONIGENC_IS_SINGLEBYTE(reg->enc)) { @@ -5062,6 +5068,11 @@ backward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar** low, UChar** high) { UChar *p; + size_t input_len = end - str; + + if (reg->dmin > input_len) { + return 0; + } range += reg->dmin; p = s; @@ -225,7 +225,7 @@ cmdline_options_init(ruby_cmdline_options_t *opt) return opt; } -static rb_ast_t *load_file(VALUE parser, VALUE fname, VALUE f, int script, +static VALUE load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t *opt); static VALUE open_load_file(VALUE fname_v, int *xflag); static void forbid_setid(const char *, const ruby_cmdline_options_t *); @@ -2056,10 +2056,11 @@ show_help(const char *progname, int help) usage(progname, help, tty, columns); } -static rb_ast_t * +static VALUE process_script(ruby_cmdline_options_t *opt) { rb_ast_t *ast; + VALUE ast_value; VALUE parser = rb_parser_new(); const unsigned int dump = opt->dump; @@ -2079,7 +2080,7 @@ process_script(ruby_cmdline_options_t *opt) ruby_set_script_name(progname); rb_parser_set_options(parser, opt->do_print, opt->do_loop, opt->do_line, opt->do_split); - ast = rb_parser_compile_string(parser, opt->script, opt->e_script, 1); + ast_value = rb_parser_compile_string(parser, opt->script, opt->e_script, 1); } else { VALUE f; @@ -2087,13 +2088,14 @@ process_script(ruby_cmdline_options_t *opt) f = open_load_file(opt->script_name, &xflag); opt->xflag = xflag != 0; rb_parser_set_context(parser, 0, f == rb_stdin); - ast = load_file(parser, opt->script_name, f, 1, opt); + ast_value = load_file(parser, opt->script_name, f, 1, opt); } + ast = rb_ruby_ast_data_get(ast_value); if (!ast->body.root) { rb_ast_dispose(ast); - return NULL; + return Qnil; } - return ast; + return ast_value; } /** @@ -2157,7 +2159,7 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result) } else { pm_options_command_line_set(options, command_line); - error = pm_load_file(result, opt->script_name); + error = pm_load_file(result, opt->script_name, true); // If reading the file did not error, at that point we load the command // line options. We do it in this order so that if the main script fails @@ -2237,6 +2239,7 @@ process_options_global_setup(const ruby_cmdline_options_t *opt, const rb_iseq_t static VALUE process_options(int argc, char **argv, ruby_cmdline_options_t *opt) { + VALUE ast_value = Qnil; struct { rb_ast_t *ast; pm_parse_result_t prism; @@ -2471,7 +2474,8 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) } if (!(*rb_ruby_prism_ptr())) { - if (!(result.ast = process_script(opt))) return Qfalse; + ast_value = process_script(opt); + if (!(result.ast = rb_ruby_ast_data_get(ast_value))) return Qfalse; } else { prism_script(opt, &result.prism); @@ -2553,7 +2557,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) } else { rb_ast_t *ast = result.ast; - iseq = rb_iseq_new_main(&ast->body, opt->script_name, path, parent, optimize); + iseq = rb_iseq_new_main(ast_value, opt->script_name, path, parent, optimize); rb_ast_dispose(ast); } } @@ -2604,7 +2608,7 @@ load_file_internal(VALUE argp_v) ruby_cmdline_options_t *opt = argp->opt; VALUE f = argp->f; int line_start = 1; - rb_ast_t *ast = 0; + VALUE ast_value = Qnil; rb_encoding *enc; ID set_encoding; @@ -2702,10 +2706,10 @@ load_file_internal(VALUE argp_v) if (NIL_P(f)) { f = rb_str_new(0, 0); rb_enc_associate(f, enc); - return (VALUE)rb_parser_compile_string_path(parser, orig_fname, f, line_start); + return rb_parser_compile_string_path(parser, orig_fname, f, line_start); } rb_funcall(f, set_encoding, 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-")); - ast = rb_parser_compile_file_path(parser, orig_fname, f, line_start); + ast_value = rb_parser_compile_file_path(parser, orig_fname, f, line_start); rb_funcall(f, set_encoding, 1, rb_parser_encoding(parser)); if (script && rb_parser_end_seen_p(parser)) { /* @@ -2723,7 +2727,7 @@ load_file_internal(VALUE argp_v) rb_define_global_const("DATA", f); argp->f = Qnil; } - return (VALUE)ast; + return ast_value; } /* disabling O_NONBLOCK, and returns 0 on success, otherwise errno */ @@ -2832,7 +2836,7 @@ restore_load_file(VALUE arg) return Qnil; } -static rb_ast_t * +static VALUE load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t *opt) { struct load_file_arg arg; @@ -2841,7 +2845,7 @@ load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t arg.script = script; arg.opt = opt; arg.f = f; - return (rb_ast_t *)rb_ensure(load_file_internal, (VALUE)&arg, + return rb_ensure(load_file_internal, (VALUE)&arg, restore_load_file, (VALUE)&arg); } @@ -2855,10 +2859,12 @@ rb_load_file(const char *fname) void * rb_load_file_str(VALUE fname_v) { - return rb_parser_load_file(rb_parser_new(), fname_v); + VALUE ast_value; + ast_value = rb_parser_load_file(rb_parser_new(), fname_v); + return (void *)rb_ruby_ast_data_get(ast_value); } -void * +VALUE rb_parser_load_file(VALUE parser, VALUE fname_v) { ruby_cmdline_options_t opt; diff --git a/ruby_parser.c b/ruby_parser.c index 16a868bc6b..1dcdfd8e79 100644 --- a/ruby_parser.c +++ b/ruby_parser.c @@ -1,5 +1,6 @@ /* This is a wrapper for parse.y */ +#include "internal/parse.h" #include "internal/re.h" #include "internal/ruby_parser.h" @@ -18,7 +19,6 @@ #include "internal/gc.h" #include "internal/hash.h" #include "internal/io.h" -#include "internal/parse.h" #include "internal/rational.h" #include "internal/re.h" #include "internal/string.h" @@ -32,40 +32,7 @@ #include "vm_core.h" #include "symbol.h" -struct ruby_parser { - rb_parser_t *parser_params; -}; - -static void -parser_mark(void *ptr) -{ - struct ruby_parser *parser = (struct ruby_parser*)ptr; - rb_ruby_parser_mark(parser->parser_params); -} - -static void -parser_free(void *ptr) -{ - struct ruby_parser *parser = (struct ruby_parser*)ptr; - rb_ruby_parser_free(parser->parser_params); -} - -static size_t -parser_memsize(const void *ptr) -{ - struct ruby_parser *parser = (struct ruby_parser*)ptr; - return rb_ruby_parser_memsize(parser->parser_params); -} - -static const rb_data_type_t ruby_parser_data_type = { - "parser", - { - parser_mark, - parser_free, - parser_memsize, - }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY -}; +#define parser_encoding const void static int is_ascii_string2(VALUE str) @@ -76,9 +43,9 @@ is_ascii_string2(VALUE str) RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 6, 0) static VALUE syntax_error_append(VALUE exc, VALUE file, int line, int column, - void *enc, const char *fmt, va_list args) + parser_encoding *enc, const char *fmt, va_list args) { - return rb_syntax_error_append(exc, file, line, column, (rb_encoding *)enc, fmt, args); + return rb_syntax_error_append(exc, file, line, column, enc, fmt, args); } static int @@ -94,9 +61,9 @@ dvar_defined(ID id, const void *p) } static int -is_usascii_enc(void *enc) +is_usascii_enc(parser_encoding *enc) { - return rb_is_usascii_enc((rb_encoding *)enc); + return rb_is_usascii_enc(enc); } static int @@ -118,21 +85,21 @@ is_notop_id2(ID id) } static VALUE -enc_str_new(const char *ptr, long len, void *enc) +enc_str_new(const char *ptr, long len, parser_encoding *enc) { - return rb_enc_str_new(ptr, len, (rb_encoding *)enc); + return rb_enc_str_new(ptr, len, enc); } static int -enc_isalnum(OnigCodePoint c, void *enc) +enc_isalnum(OnigCodePoint c, parser_encoding *enc) { - return rb_enc_isalnum(c, (rb_encoding *)enc); + return rb_enc_isalnum(c, enc); } static int -enc_precise_mbclen(const char *p, const char *e, void *enc) +enc_precise_mbclen(const char *p, const char *e, parser_encoding *enc) { - return rb_enc_precise_mbclen(p, e, (rb_encoding *)enc); + return rb_enc_precise_mbclen(p, e, enc); } static int @@ -148,105 +115,93 @@ mbclen_charfound_len(int len) } static const char * -enc_name(void *enc) +enc_name(parser_encoding *enc) { - return rb_enc_name((rb_encoding *)enc); + return rb_enc_name(enc); } static char * -enc_prev_char(const char *s, const char *p, const char *e, void *enc) +enc_prev_char(const char *s, const char *p, const char *e, parser_encoding *enc) { - return rb_enc_prev_char(s, p, e, (rb_encoding *)enc); + return rb_enc_prev_char(s, p, e, enc); } -static void * +static parser_encoding * enc_get(VALUE obj) { - return (void *)rb_enc_get(obj); + return rb_enc_get(obj); } static int -enc_asciicompat(void *enc) +enc_asciicompat(parser_encoding *enc) { - return rb_enc_asciicompat((rb_encoding *)enc); + return rb_enc_asciicompat(enc); } -static void * +static parser_encoding * utf8_encoding(void) { - return (void *)rb_utf8_encoding(); + return rb_utf8_encoding(); } static VALUE -enc_associate(VALUE obj, void *enc) +enc_associate(VALUE obj, parser_encoding *enc) { - return rb_enc_associate(obj, (rb_encoding *)enc); + return rb_enc_associate(obj, enc); } -static void * +static parser_encoding * ascii8bit_encoding(void) { - return (void *)rb_ascii8bit_encoding(); + return rb_ascii8bit_encoding(); } static int -enc_codelen(int c, void *enc) +enc_codelen(int c, parser_encoding *enc) { - return rb_enc_codelen(c, (rb_encoding *)enc); + return rb_enc_codelen(c, enc); } static int -enc_mbcput(unsigned int c, void *buf, void *enc) -{ - return rb_enc_mbcput(c, buf, (rb_encoding *)enc); -} - -static void * -enc_from_index(int idx) +enc_mbcput(unsigned int c, void *buf, parser_encoding *enc) { - return (void *)rb_enc_from_index(idx); + return rb_enc_mbcput(c, buf, enc); } static int -enc_isspace(OnigCodePoint c, void *enc) +enc_mbclen(const char *p, const char *e, parser_encoding *enc) { - return rb_enc_isspace(c, (rb_encoding *)enc); + return rb_enc_mbclen(p, e, enc); } -static ID -intern3(const char *name, long len, void *enc) -{ - return rb_intern3(name, len, (rb_encoding *)enc); -} - -static void * -enc_compatible(VALUE str1, VALUE str2) +static parser_encoding * +enc_from_index(int idx) { - return (void *)rb_enc_compatible(str1, str2); + return rb_enc_from_index(idx); } -static VALUE -enc_from_encoding(void *enc) +static int +enc_isspace(OnigCodePoint c, parser_encoding *enc) { - return rb_enc_from_encoding((rb_encoding *)enc); + return rb_enc_isspace(c, enc); } -static int -encoding_is_ascii8bit(VALUE obj) +static ID +intern3(const char *name, long len, parser_encoding *enc) { - return ENCODING_IS_ASCII8BIT(obj); + return rb_intern3(name, len, enc); } -static void * +static parser_encoding * usascii_encoding(void) { - return (void *)rb_usascii_encoding(); + return rb_usascii_encoding(); } static int -enc_symname_type(const char *name, long len, void *enc, unsigned int allowed_attrset) +enc_symname_type(const char *name, long len, parser_encoding *enc, unsigned int allowed_attrset) { - return rb_enc_symname_type(name, len, (rb_encoding *)enc, allowed_attrset); + return rb_enc_symname_type(name, len, enc, allowed_attrset); } typedef struct { @@ -267,7 +222,7 @@ reg_named_capture_assign_iter(const OnigUChar *name, const OnigUChar *name_end, long len = name_end - name; const char *s = (const char *)name; - return rb_reg_named_capture_assign_iter_impl(p, s, len, (void *)enc, &arg->succ_block, loc); + return rb_reg_named_capture_assign_iter_impl(p, s, len, enc, &arg->succ_block, loc); } static NODE * @@ -285,12 +240,6 @@ reg_named_capture_assign(struct parser_params* p, VALUE regexp, const rb_code_lo return RNODE_BLOCK(arg.succ_block)->nd_next; } -static VALUE -rbool(VALUE v) -{ - return RBOOL(v); -} - static int rtest(VALUE obj) { @@ -351,12 +300,6 @@ arg_error(void) return rb_eArgError; } -static rb_ast_t * -ast_new(VALUE nb) -{ - return IMEMO_NEW(rb_ast_t, imemo_ast, nb); -} - static VALUE static_id2sym(ID id) { @@ -364,25 +307,25 @@ static_id2sym(ID id) } static long -str_coderange_scan_restartable(const char *s, const char *e, void *enc, int *cr) +str_coderange_scan_restartable(const char *s, const char *e, parser_encoding *enc, int *cr) { - return rb_str_coderange_scan_restartable(s, e, (rb_encoding *)enc, cr); + return rb_str_coderange_scan_restartable(s, e, enc, cr); } static int -enc_mbminlen(void *enc) +enc_mbminlen(parser_encoding *enc) { - return rb_enc_mbminlen((rb_encoding *)enc); + return rb_enc_mbminlen(enc); } static bool -enc_isascii(OnigCodePoint c, void *enc) +enc_isascii(OnigCodePoint c, parser_encoding *enc) { - return rb_enc_isascii(c, (rb_encoding *)enc); + return rb_enc_isascii(c, enc); } static OnigCodePoint -enc_mbc_to_codepoint(const char *p, const char *e, void *enc) +enc_mbc_to_codepoint(const char *p, const char *e, parser_encoding *enc) { const OnigUChar *up = RBIMPL_CAST((const OnigUChar *)p); const OnigUChar *ue = RBIMPL_CAST((const OnigUChar *)e); @@ -390,7 +333,6 @@ enc_mbc_to_codepoint(const char *p, const char *e, void *enc) return ONIGENC_MBC_TO_CODE((rb_encoding *)enc, up, ue); } -VALUE rb_io_gets_internal(VALUE io); extern VALUE rb_eArgError; static const rb_parser_config_t rb_global_parser_config = { @@ -406,8 +348,6 @@ static const rb_parser_config_t rb_global_parser_config = { .nonempty_memcpy = nonempty_memcpy, .xmalloc_mul_add = rb_xmalloc_mul_add, - .ast_new = ast_new, - .compile_callback = rb_suppress_tracing, .reg_named_capture_assign = reg_named_capture_assign, @@ -417,8 +357,6 @@ static const rb_parser_config_t rb_global_parser_config = { .ary_push = rb_ary_push, .ary_new_from_args = rb_ary_new_from_args, .ary_unshift = rb_ary_unshift, - .array_len = rb_array_len, - .array_aref = RARRAY_AREF, .make_temporary_id = rb_make_temporary_id, .is_local_id = is_local_id2, @@ -439,8 +377,6 @@ static const rb_parser_config_t rb_global_parser_config = { .str_catf = rb_str_catf, .str_cat_cstr = rb_str_cat_cstr, - .str_subseq = rb_str_subseq, - .str_new_frozen = rb_str_new_frozen, .str_modify = rb_str_modify, .str_set_len = rb_str_set_len, .str_cat = rb_str_cat, @@ -456,7 +392,6 @@ static const rb_parser_config_t rb_global_parser_config = { .rstring_ptr = RSTRING_PTR, .rstring_end = RSTRING_END, .rstring_len = RSTRING_LEN, - .filesystem_str_new_cstr = rb_filesystem_str_new_cstr, .obj_as_string = rb_obj_as_string, .int2num = rb_int2num_inline, @@ -466,7 +401,6 @@ static const rb_parser_config_t rb_global_parser_config = { .io_write = rb_io_write, .io_flush = rb_io_flush, .io_puts = rb_io_puts, - .io_gets_internal = rb_io_gets_internal, .debug_output_stdout = rb_ractor_stdout, .debug_output_stderr = rb_ractor_stderr, @@ -485,14 +419,12 @@ static const rb_parser_config_t rb_global_parser_config = { .ascii8bit_encoding = ascii8bit_encoding, .enc_codelen = enc_codelen, .enc_mbcput = enc_mbcput, + .enc_mbclen = enc_mbclen, .enc_find_index = rb_enc_find_index, .enc_from_index = enc_from_index, .enc_isspace = enc_isspace, .enc_coderange_7bit = ENC_CODERANGE_7BIT, .enc_coderange_unknown = ENC_CODERANGE_UNKNOWN, - .enc_compatible = enc_compatible, - .enc_from_encoding = enc_from_encoding, - .encoding_is_ascii8bit = encoding_is_ascii8bit, .usascii_encoding = usascii_encoding, .enc_coderange_broken = ENC_CODERANGE_BROKEN, .enc_mbminlen = enc_mbminlen, @@ -534,11 +466,9 @@ static const rb_parser_config_t rb_global_parser_config = { .scan_digits = ruby_scan_digits, .strtod = ruby_strtod, - .rbool = rbool, .rtest = rtest, .nil_p = nil_p, .qnil = Qnil, - .qtrue = Qtrue, .qfalse = Qfalse, .eArgError = arg_error, .long2int = rb_long2int, @@ -547,7 +477,77 @@ static const rb_parser_config_t rb_global_parser_config = { .static_id2sym = static_id2sym, .str_coderange_scan_restartable = str_coderange_scan_restartable, }; +#endif + +enum lex_type { + lex_type_str, + lex_type_io, + lex_type_array, + lex_type_generic, +}; + +struct ruby_parser { + rb_parser_t *parser_params; + enum lex_type type; + union { + struct lex_pointer_string lex_str; + struct { + VALUE file; + } lex_io; + struct { + VALUE ary; + } lex_array; + } data; +}; + +static void +parser_mark(void *ptr) +{ + struct ruby_parser *parser = (struct ruby_parser*)ptr; + rb_ruby_parser_mark(parser->parser_params); + + switch (parser->type) { + case lex_type_str: + rb_gc_mark(parser->data.lex_str.str); + break; + case lex_type_io: + rb_gc_mark(parser->data.lex_io.file); + break; + case lex_type_array: + rb_gc_mark(parser->data.lex_array.ary); + break; + case lex_type_generic: + /* noop. Caller of rb_parser_compile_generic should mark the objects. */ + break; + } +} + +static void +parser_free(void *ptr) +{ + struct ruby_parser *parser = (struct ruby_parser*)ptr; + rb_ruby_parser_free(parser->parser_params); + xfree(parser); +} + +static size_t +parser_memsize(const void *ptr) +{ + struct ruby_parser *parser = (struct ruby_parser*)ptr; + return rb_ruby_parser_memsize(parser->parser_params); +} + +static const rb_data_type_t ruby_parser_data_type = { + "parser", + { + parser_mark, + parser_free, + parser_memsize, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY +}; +#ifdef UNIVERSAL_PARSER const rb_parser_config_t * rb_ruby_parser_config(void) { @@ -555,16 +555,17 @@ rb_ruby_parser_config(void) } rb_parser_t * -rb_parser_params_allocate(void) +rb_parser_params_new(void) { - return rb_ruby_parser_allocate(&rb_global_parser_config); + return rb_ruby_parser_new(&rb_global_parser_config); } - +#else rb_parser_t * rb_parser_params_new(void) { - return rb_ruby_parser_new(&rb_global_parser_config); + return rb_ruby_parser_new(); } +#endif /* UNIVERSAL_PARSER */ VALUE rb_parser_new(void) @@ -622,65 +623,239 @@ rb_parser_error_tolerant(VALUE vparser) rb_ruby_parser_error_tolerant(parser->parser_params); } -rb_ast_t* -rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE file, int start) +void +rb_parser_keep_tokens(VALUE vparser) { struct ruby_parser *parser; + + TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser); + rb_ruby_parser_keep_tokens(parser->parser_params); +} + +rb_parser_string_t * +rb_parser_lex_get_str(struct parser_params *p, struct lex_pointer_string *ptr_str) +{ + char *beg, *end, *start; + long len; + VALUE s = ptr_str->str; + + beg = RSTRING_PTR(s); + len = RSTRING_LEN(s); + start = beg; + if (ptr_str->ptr) { + if (len == ptr_str->ptr) return 0; + beg += ptr_str->ptr; + len -= ptr_str->ptr; + } + end = memchr(beg, '\n', len); + if (end) len = ++end - beg; + ptr_str->ptr += len; + return rb_str_to_parser_string(p, rb_str_subseq(s, beg - start, len)); +} + +static rb_parser_string_t * +lex_get_str(struct parser_params *p, rb_parser_input_data input, int line_count) +{ + return rb_parser_lex_get_str(p, (struct lex_pointer_string *)input); +} + +static void parser_aset_script_lines_for(VALUE path, rb_parser_ary_t *lines); + +static rb_ast_t* +parser_compile(rb_parser_t *p, rb_parser_lex_gets_func *gets, VALUE fname, rb_parser_input_data input, int line) +{ rb_ast_t *ast; + const char *ptr = 0; + long len = 0; + rb_encoding *enc = 0; + + if (!NIL_P(fname)) { + StringValueCStr(fname); + ptr = RSTRING_PTR(fname); + len = RSTRING_LEN(fname); + enc = rb_enc_get(fname); + } + + ast = rb_parser_compile(p, gets, ptr, len, enc, input, line); + parser_aset_script_lines_for(fname, ast->body.script_lines); + return ast; +} + +static rb_ast_t* +parser_compile_string0(struct ruby_parser *parser, VALUE fname, VALUE s, int line) +{ + VALUE str = rb_str_new_frozen(s); + + parser->type = lex_type_str; + parser->data.lex_str.str = str; + parser->data.lex_str.ptr = 0; + + return parser_compile(parser->parser_params, lex_get_str, fname, (rb_parser_input_data)&parser->data, line); +} + +static rb_encoding * +must_be_ascii_compatible(VALUE s) +{ + rb_encoding *enc = rb_enc_get(s); + if (!rb_enc_asciicompat(enc)) { + rb_raise(rb_eArgError, "invalid source encoding"); + } + return enc; +} + +static rb_ast_t* +parser_compile_string_path(struct ruby_parser *parser, VALUE f, VALUE s, int line) +{ + must_be_ascii_compatible(s); + return parser_compile_string0(parser, f, s, line); +} + +static rb_ast_t* +parser_compile_string(struct ruby_parser *parser, const char *f, VALUE s, int line) +{ + return parser_compile_string_path(parser, rb_filesystem_str_new_cstr(f), s, line); +} + +VALUE rb_io_gets_internal(VALUE io); + +static rb_parser_string_t * +lex_io_gets(struct parser_params *p, rb_parser_input_data input, int line_count) +{ + VALUE io = (VALUE)input; + VALUE line = rb_io_gets_internal(io); + if (NIL_P(line)) return 0; + return rb_str_to_parser_string(p, line); +} + +static rb_parser_string_t * +lex_gets_array(struct parser_params *p, rb_parser_input_data data, int index) +{ + VALUE array = (VALUE)data; + VALUE str = rb_ary_entry(array, index); + if (!NIL_P(str)) { + StringValue(str); + if (!rb_enc_asciicompat(rb_enc_get(str))) { + rb_raise(rb_eArgError, "invalid source encoding"); + } + return rb_str_to_parser_string(p, str); + } + else { + return 0; + } +} + +static rb_ast_t* +parser_compile_file_path(struct ruby_parser *parser, VALUE fname, VALUE file, int start) +{ + parser->type = lex_type_io; + parser->data.lex_io.file = file; + + return parser_compile(parser->parser_params, lex_io_gets, fname, (rb_parser_input_data)file, start); +} + +static rb_ast_t* +parser_compile_array(struct ruby_parser *parser, VALUE fname, VALUE array, int start) +{ + parser->type = lex_type_array; + parser->data.lex_array.ary = array; + + return parser_compile(parser->parser_params, lex_gets_array, fname, (rb_parser_input_data)array, start); +} + +static rb_ast_t* +parser_compile_generic(struct ruby_parser *parser, rb_parser_lex_gets_func *lex_gets, VALUE fname, VALUE input, int start) +{ + parser->type = lex_type_generic; + + return parser_compile(parser->parser_params, lex_gets, fname, (rb_parser_input_data)input, start); +} + +static void +ast_free(void *ptr) +{ + rb_ast_t *ast = (rb_ast_t *)ptr; + rb_ast_free(ast); +} + +static const rb_data_type_t ast_data_type = { + "AST", + { + NULL, + ast_free, + NULL, // No dsize() because this object does not appear in ObjectSpace. + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY +}; + +static VALUE +ast_alloc(void) +{ + return TypedData_Wrap_Struct(0, &ast_data_type, NULL); +} + +VALUE +rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE file, int start) +{ + struct ruby_parser *parser; + VALUE ast_value = ast_alloc(); TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser); - ast = rb_ruby_parser_compile_file_path(parser->parser_params, fname, file, start); + DATA_PTR(ast_value) = parser_compile_file_path(parser, fname, file, start); RB_GC_GUARD(vparser); - return ast; + return ast_value; } -void -rb_parser_keep_tokens(VALUE vparser) +VALUE +rb_parser_compile_array(VALUE vparser, VALUE fname, VALUE array, int start) { struct ruby_parser *parser; + VALUE ast_value = ast_alloc(); TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser); - rb_ruby_parser_keep_tokens(parser->parser_params); + DATA_PTR(ast_value) = parser_compile_array(parser, fname, array, start); + RB_GC_GUARD(vparser); + + return ast_value; } -rb_ast_t* -rb_parser_compile_generic(VALUE vparser, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int start) +VALUE +rb_parser_compile_generic(VALUE vparser, rb_parser_lex_gets_func *lex_gets, VALUE fname, VALUE input, int start) { struct ruby_parser *parser; - rb_ast_t *ast; + VALUE ast_value = ast_alloc(); TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser); - ast = rb_ruby_parser_compile_generic(parser->parser_params, lex_gets, fname, input, start); + DATA_PTR(ast_value) = parser_compile_generic(parser, lex_gets, fname, input, start); RB_GC_GUARD(vparser); - return ast; + return ast_value; } -rb_ast_t* +VALUE rb_parser_compile_string(VALUE vparser, const char *f, VALUE s, int line) { struct ruby_parser *parser; - rb_ast_t *ast; + VALUE ast_value = ast_alloc(); TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser); - ast = rb_ruby_parser_compile_string(parser->parser_params, f, s, line); + DATA_PTR(ast_value) = parser_compile_string(parser, f, s, line); RB_GC_GUARD(vparser); - return ast; + return ast_value; } -rb_ast_t* +VALUE rb_parser_compile_string_path(VALUE vparser, VALUE f, VALUE s, int line) { struct ruby_parser *parser; - rb_ast_t *ast; + VALUE ast_value = ast_alloc(); TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser); - ast = rb_ruby_parser_compile_string_path(parser->parser_params, f, s, line); + DATA_PTR(ast_value) = parser_compile_string_path(parser, f, s, line); RB_GC_GUARD(vparser); - return ast; + return ast_value; } VALUE @@ -689,7 +864,7 @@ rb_parser_encoding(VALUE vparser) struct ruby_parser *parser; TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser); - return rb_ruby_parser_encoding(parser->parser_params); + return rb_enc_from_encoding(rb_ruby_parser_encoding(parser->parser_params)); } VALUE @@ -726,12 +901,12 @@ rb_set_script_lines_for(VALUE vparser, VALUE path) rb_ruby_parser_set_script_lines(parser->parser_params); } } -#endif VALUE rb_parser_build_script_lines_from(rb_parser_ary_t *lines) { int i; + if (!lines) return Qnil; if (lines->data_type != PARSER_ARY_DATA_SCRIPT_LINE) { rb_bug("unexpected rb_parser_ary_data_type (%d) for script lines", lines->data_type); } @@ -949,12 +1124,12 @@ rb_node_encoding_val(const NODE *node) return rb_enc_from_encoding(RNODE_ENCODING(node)->enc); } -void -rb_parser_aset_script_lines_for(VALUE path, rb_parser_ary_t *lines) +static void +parser_aset_script_lines_for(VALUE path, rb_parser_ary_t *lines) { VALUE hash, script_lines; ID script_lines_id; - if (NIL_P(path) || !lines || FIXNUM_P((VALUE)lines)) return; + if (NIL_P(path) || !lines) return; CONST_ID(script_lines_id, "SCRIPT_LINES__"); if (!rb_const_defined_at(rb_cObject, script_lines_id)) return; hash = rb_const_get_at(rb_cObject, script_lines_id); @@ -963,3 +1138,30 @@ rb_parser_aset_script_lines_for(VALUE path, rb_parser_ary_t *lines) script_lines = rb_parser_build_script_lines_from(lines); rb_hash_aset(hash, path, script_lines); } + +VALUE +rb_ruby_ast_new(const NODE *const root) +{ + rb_ast_t *ast; + VALUE ast_value = TypedData_Make_Struct(0, rb_ast_t, &ast_data_type, ast); +#ifdef UNIVERSAL_PARSER + ast->config = &rb_global_parser_config; +#endif + ast->body = (rb_ast_body_t){ + .root = root, + .frozen_string_literal = -1, + .coverage_enabled = -1, + .script_lines = NULL, + .line_count = 0, + }; + return ast_value; +} + +rb_ast_t * +rb_ruby_ast_data_get(VALUE ast_value) +{ + rb_ast_t *ast; + if (NIL_P(ast_value)) return NULL; + TypedData_Get_Struct(ast_value, rb_ast_t, &ast_data_type, ast); + return ast; +} diff --git a/rubyparser.h b/rubyparser.h index 6d346a7ff4..fc8be9633a 100644 --- a/rubyparser.h +++ b/rubyparser.h @@ -9,7 +9,7 @@ #ifdef UNIVERSAL_PARSER -#define rb_encoding void +#define rb_encoding const void #define OnigCodePoint unsigned int #include "parser_st.h" #ifndef RUBY_RUBY_H @@ -73,6 +73,8 @@ enum rb_parser_shareability { rb_parser_shareable_everything, }; +typedef void* rb_parser_input_data; + /* * AST Node */ @@ -1206,20 +1208,24 @@ typedef struct RNode_ERROR { (n)->flags=(((n)->flags&~NODE_TYPEMASK)|((((unsigned long)(t))<<NODE_TYPESHIFT)&NODE_TYPEMASK)) typedef struct node_buffer_struct node_buffer_t; -/* T_IMEMO/ast */ + +#ifdef UNIVERSAL_PARSER +typedef struct rb_parser_config_struct rb_parser_config_t; +#endif + typedef struct rb_ast_body_struct { const NODE *root; rb_parser_ary_t *script_lines; - // script_lines is either: - // - a Fixnum that represents the line count of the original source, or - // - an rb_parser_ary_t* that contains the lines of the original source + int line_count; signed int frozen_string_literal:2; /* -1: not specified, 0: false, 1: true */ signed int coverage_enabled:2; /* -1: not specified, 0: false, 1: true */ } rb_ast_body_t; typedef struct rb_ast_struct { - VALUE flags; node_buffer_t *node_buffer; rb_ast_body_t body; +#ifdef UNIVERSAL_PARSER + const rb_parser_config_t *config; +#endif } rb_ast_t; @@ -1249,9 +1255,6 @@ typedef struct rb_parser_config_struct { void *(*nonempty_memcpy)(void *dest, const void *src, size_t t, size_t n); void *(*xmalloc_mul_add)(size_t x, size_t y, size_t z); - /* imemo */ - rb_ast_t *(*ast_new)(VALUE nb); - // VALUE rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg); VALUE (*compile_callback)(VALUE (*func)(VALUE), VALUE arg); NODE *(*reg_named_capture_assign)(struct parser_params* p, VALUE regexp, const rb_code_location_t *loc); @@ -1264,8 +1267,6 @@ typedef struct rb_parser_config_struct { VALUE (*ary_push)(VALUE ary, VALUE elem); VALUE (*ary_new_from_args)(long n, ...); VALUE (*ary_unshift)(VALUE ary, VALUE item); - long (*array_len)(VALUE a); - VALUE (*array_aref)(VALUE, long); /* Symbol */ ID (*make_temporary_id)(size_t n); @@ -1289,8 +1290,6 @@ typedef struct rb_parser_config_struct { RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3) VALUE (*str_catf)(VALUE str, const char *format, ...); VALUE (*str_cat_cstr)(VALUE str, const char *ptr); - VALUE (*str_subseq)(VALUE str, long beg, long len); - VALUE (*str_new_frozen)(VALUE orig); void (*str_modify)(VALUE str); void (*str_set_len)(VALUE str, long len); VALUE (*str_cat)(VALUE str, const char *ptr, long len); @@ -1308,7 +1307,6 @@ typedef struct rb_parser_config_struct { char *(*rstring_ptr)(VALUE str); char *(*rstring_end)(VALUE str); long (*rstring_len)(VALUE str); - VALUE (*filesystem_str_new_cstr)(const char *ptr); VALUE (*obj_as_string)(VALUE); /* Numeric */ @@ -1320,7 +1318,6 @@ typedef struct rb_parser_config_struct { VALUE (*io_write)(VALUE io, VALUE str); VALUE (*io_flush)(VALUE io); VALUE (*io_puts)(int argc, const VALUE *argv, VALUE out); - VALUE (*io_gets_internal)(VALUE io); /* IO (Ractor) */ VALUE (*debug_output_stdout)(void); @@ -1341,12 +1338,10 @@ typedef struct rb_parser_config_struct { rb_encoding *(*ascii8bit_encoding)(void); int (*enc_codelen)(int c, rb_encoding *enc); int (*enc_mbcput)(unsigned int c, void *buf, rb_encoding *enc); + int (*enc_mbclen)(const char *p, const char *e, rb_encoding *enc); int (*enc_find_index)(const char *name); rb_encoding *(*enc_from_index)(int idx); int (*enc_isspace)(OnigCodePoint c, rb_encoding *enc); - rb_encoding *(*enc_compatible)(VALUE str1, VALUE str2); - VALUE (*enc_from_encoding)(rb_encoding *enc); - int (*encoding_is_ascii8bit)(VALUE obj); rb_encoding *(*usascii_encoding)(void); int enc_coderange_broken; int (*enc_mbminlen)(rb_encoding *enc); @@ -1401,11 +1396,9 @@ typedef struct rb_parser_config_struct { double (*strtod)(const char *s00, char **se); /* Misc */ - VALUE (*rbool)(VALUE); int (*rtest)(VALUE obj); int (*nil_p)(VALUE obj); VALUE qnil; - VALUE qtrue; VALUE qfalse; VALUE (*eArgError)(void); int (*long2int)(long); @@ -1423,7 +1416,6 @@ typedef struct rb_parser_config_struct { RUBY_SYMBOL_EXPORT_BEGIN void rb_ruby_parser_free(void *ptr); -rb_ast_t* rb_ruby_parser_compile_string(rb_parser_t *p, const char *f, VALUE s, int line); #ifdef UNIVERSAL_PARSER rb_parser_t *rb_ruby_parser_allocate(const rb_parser_config_t *config); @@ -118,6 +118,7 @@ redblack_value(redblack_node_t * node) return (rb_shape_t *)((uintptr_t)node->value & (((uintptr_t)-1) - 1)); } +#ifdef HAVE_MMAP static redblack_id_t redblack_id_for(redblack_node_t * node) { @@ -292,6 +293,7 @@ redblack_insert(redblack_node_t * tree, ID key, rb_shape_t * value) return root; } } +#endif rb_shape_tree_t *rb_shape_tree_ptr = NULL; @@ -1213,9 +1215,7 @@ rb_shape_find_by_id(VALUE mod, VALUE id) void Init_default_shapes(void) { - rb_shape_tree_t *st = ruby_mimmalloc(sizeof(rb_shape_tree_t)); - memset(st, 0, sizeof(rb_shape_tree_t)); - rb_shape_tree_ptr = st; + rb_shape_tree_ptr = xcalloc(1, sizeof(rb_shape_tree_t)); #ifdef HAVE_MMAP rb_shape_tree_ptr->shape_list = (rb_shape_t *)mmap(NULL, rb_size_mul_or_raise(SHAPE_BUFFER_SIZE, sizeof(rb_shape_t), rb_eRuntimeError), diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb index 70e2c84961..37d8b3ac1a 100644 --- a/spec/bundler/commands/cache_spec.rb +++ b/spec/bundler/commands/cache_spec.rb @@ -386,6 +386,66 @@ RSpec.describe "bundle install with gem sources" do expect(the_bundle).to include_gems "rack 1.0.0" end + it "uses cached gems for secondary sources when cache_all_platforms configured" do + build_repo4 do + build_gem "foo", "1.0.0" do |s| + s.platform = "x86_64-linux" + end + + build_gem "foo", "1.0.0" do |s| + s.platform = "arm64-darwin" + end + end + + gemfile <<~G + source "https://gems.repo2" + + source "https://gems.repo4" do + gem "foo" + end + G + + lockfile <<~L + GEM + remote: https://gems.repo2/ + specs: + + GEM + remote: https://gems.repo4/ + specs: + foo (1.0.0-x86_64-linux) + foo (1.0.0-arm64-darwin) + + PLATFORMS + arm64-darwin + ruby + x86_64-linux + + DEPENDENCIES + foo + + BUNDLED WITH + #{Bundler::VERSION} + L + + simulate_platform "x86_64-linux" do + bundle "config set cache_all_platforms true" + bundle "config set path vendor/bundle" + bundle :cache, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + build_repo4 do + # simulate removal of all remote gems + end + + # delete compact index cache + FileUtils.rm_rf home(".bundle/cache/compact_index") + + bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + expect(the_bundle).to include_gems "foo 1.0.0 x86_64-linux" + end + end + it "does not reinstall already-installed gems" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb index 535df8e35a..0c7031e813 100644 --- a/spec/bundler/commands/help_spec.rb +++ b/spec/bundler/commands/help_spec.rb @@ -15,6 +15,13 @@ RSpec.describe "bundle help" do expect(out).to eq(%(["#{man_dir}/bundle-install.1"])) end + it "prexifes bundle commands with bundle- and resolves aliases when finding the man files" do + with_fake_man do + bundle "help package" + end + expect(out).to eq(%(["#{man_dir}/bundle-cache.1"])) + end + it "simply outputs the human readable file when there is no man on the path" do with_path_as("") do bundle "help install" diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index f0c9aaea8e..edc5887d7b 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -1024,6 +1024,29 @@ RSpec.describe "bundle install with gem sources" do end end + describe "when gemspecs are unreadable", :permissions do + let(:gemspec_path) { vendored_gems("specifications/rack-1.0.0.gemspec") } + + before do + gemfile <<~G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + G + bundle "config path vendor/bundle" + bundle :install + expect(out).to include("Bundle complete!") + expect(err).to be_empty + + FileUtils.chmod("-r", gemspec_path) + end + + it "shows a good error" do + bundle :install, raise_on_error: false + expect(err).to include(gemspec_path.to_s) + expect(err).to include("grant read permissions") + end + end + context "after installing with --standalone" do before do install_gemfile <<-G @@ -1384,4 +1407,33 @@ RSpec.describe "bundle install with gem sources" do expect(bundled_app(".bundle/config")).not_to exist end end + + context "when bundler installation is corrupt" do + before do + system_gems "bundler-9.99.8" + + replace_version_file("9.99.9", dir: system_gem_path("gems/bundler-9.99.8")) + end + + it "shows a proper error" do + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + + BUNDLED WITH + 9.99.8 + L + + install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", env: { "BUNDLER_VERSION" => "9.99.8" }, raise_on_error: false + + expect(err).not_to include("ERROR REPORT TEMPLATE") + expect(err).to include("The running version of Bundler (9.99.9) does not match the version of the specification installed for it (9.99.8)") + end + end end diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index f6793d393b..c6bb0f58af 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -252,6 +252,128 @@ RSpec.describe "bundle lock" do expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile, "(2.3.2)", "(#{rake_version})")) end + it "updates specific gems using --update, even if that requires unlocking other top level gems" do + build_repo4 do + build_gem "prism", "0.15.1" + build_gem "prism", "0.24.0" + + build_gem "ruby-lsp", "0.12.0" do |s| + s.add_dependency "prism", "< 0.24.0" + end + + build_gem "ruby-lsp", "0.16.1" do |s| + s.add_dependency "prism", ">= 0.24.0" + end + + build_gem "tapioca", "0.11.10" do |s| + s.add_dependency "prism", "< 0.24.0" + end + + build_gem "tapioca", "0.13.1" do |s| + s.add_dependency "prism", ">= 0.24.0" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "tapioca" + gem "ruby-lsp" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)} + specs: + prism (0.15.1) + ruby-lsp (0.12.0) + prism (< 0.24.0) + tapioca (0.11.10) + prism (< 0.24.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ruby-lsp + tapioca + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock --update tapioca --verbose" + + expect(lockfile).to include("tapioca (0.13.1)") + end + + it "updates specific gems using --update, even if that requires unlocking other top level gems, but only as few as possible" do + build_repo4 do + build_gem "prism", "0.15.1" + build_gem "prism", "0.24.0" + + build_gem "ruby-lsp", "0.12.0" do |s| + s.add_dependency "prism", "< 0.24.0" + end + + build_gem "ruby-lsp", "0.16.1" do |s| + s.add_dependency "prism", ">= 0.24.0" + end + + build_gem "tapioca", "0.11.10" do |s| + s.add_dependency "prism", "< 0.24.0" + end + + build_gem "tapioca", "0.13.1" do |s| + s.add_dependency "prism", ">= 0.24.0" + end + + build_gem "other-prism-dependent", "1.0.0" do |s| + s.add_dependency "prism", ">= 0.15.1" + end + + build_gem "other-prism-dependent", "1.1.0" do |s| + s.add_dependency "prism", ">= 0.15.1" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "tapioca" + gem "ruby-lsp" + gem "other-prism-dependent" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)} + specs: + other-prism-dependent (1.0.0) + prism (>= 0.15.1) + prism (0.15.1) + ruby-lsp (0.12.0) + prism (< 0.24.0) + tapioca (0.11.10) + prism (< 0.24.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ruby-lsp + tapioca + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock --update tapioca" + + expect(lockfile).to include("tapioca (0.13.1)") + expect(lockfile).to include("other-prism-dependent (1.0.0)") + end + it "preserves unknown checksum algorithms" do lockfile @lockfile.gsub(/(sha256=[a-f0-9]+)$/, "constant=true,\\1,xyz=123") diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index cfb86ebb54..8565e27ebf 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -1954,6 +1954,52 @@ RSpec.describe "bundle update conservative" do end end + context "when Gemfile dependencies have changed" do + before do + build_repo4 do + build_gem "nokogiri", "1.16.4" do |s| + s.platform = "arm64-darwin" + end + + build_gem "nokogiri", "1.16.4" do |s| + s.platform = "x86_64-linux" + end + + build_gem "prism", "0.25.0" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "nokogiri", ">=1.16.4" + gem "prism", ">=0.25.0" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.16.4-arm64-darwin) + nokogiri (1.16.4-x86_64-linux) + + PLATFORMS + arm64-darwin + x86_64-linux + + DEPENDENCIES + nokogiri (>= 1.16.4) + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "still works" do + simulate_platform "arm64-darwin-23" do + bundle "update" + end + end + end + context "error handling" do before do gemfile "source \"#{file_uri_for(gem_repo1)}\"" diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb index 8002978368..d89fdea6f1 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -183,50 +183,10 @@ RSpec.describe "install in deployment or frozen mode" do bundle "config set --local deployment true" end - it "prevents the replace by default" do - bundle :install, raise_on_error: false - - expect(err).to match(/The list of sources changed/) - end - - context "when allow_deployment_source_credential_changes is true" do - before { bundle "config set allow_deployment_source_credential_changes true" } - - it "allows the replace" do - bundle :install - - expect(out).to match(/Bundle complete!/) - end - end - - context "when allow_deployment_source_credential_changes is false" do - before { bundle "config set allow_deployment_source_credential_changes false" } - - it "prevents the replace" do - bundle :install, raise_on_error: false - - expect(err).to match(/The list of sources changed/) - end - end - - context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is true" do - before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "true" } - - it "allows the replace" do - bundle :install - - expect(out).to match(/Bundle complete!/) - end - end - - context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is false" do - before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "false" } - - it "prevents the replace" do - bundle :install, raise_on_error: false + it "allows the replace" do + bundle :install - expect(err).to match(/The list of sources changed/) - end + expect(out).to match(/Bundle complete!/) end end diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 7f664abc4d..4fd081e7d0 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -324,7 +324,7 @@ RSpec.describe "the lockfile format" do G end - it "generates a lockfile without credentials for a configured source" do + it "generates a lockfile without credentials" do bundle "config set http://localgemserver.test/ user:pass" install_gemfile(<<-G, artifice: "endpoint_strict_basic_authentication", quiet: true) @@ -354,7 +354,7 @@ RSpec.describe "the lockfile format" do specs: GEM - remote: http://user:pass@othergemserver.test/ + remote: http://othergemserver.test/ specs: rack (1.0.0) rack-obama (1.0) diff --git a/spec/bundler/plugins/hook_spec.rb b/spec/bundler/plugins/hook_spec.rb index 72feb14d84..f6ee0ba210 100644 --- a/spec/bundler/plugins/hook_spec.rb +++ b/spec/bundler/plugins/hook_spec.rb @@ -106,4 +106,103 @@ RSpec.describe "hook plugins" do expect(out).to include "installed gem rack : installed" end end + + context "before-require-all hook" do + before do + build_repo2 do + build_plugin "before-require-all-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_REQUIRE_ALL do |deps| + puts "gems to be required \#{deps.map(&:name).join(", ")}" + end + RUBY + end + end + + bundle "plugin install before-require-all-plugin --source #{file_uri_for(gem_repo2)}" + end + + it "runs before all rubygems are required" do + install_gemfile_and_bundler_require + expect(out).to include "gems to be required rake, rack" + end + end + + context "before-require hook" do + before do + build_repo2 do + build_plugin "before-require-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_REQUIRE do |dep| + puts "requiring gem \#{dep.name}" + end + RUBY + end + end + + bundle "plugin install before-require-plugin --source #{file_uri_for(gem_repo2)}" + end + + it "runs before each rubygem is required" do + install_gemfile_and_bundler_require + expect(out).to include "requiring gem rake" + expect(out).to include "requiring gem rack" + end + end + + context "after-require-all hook" do + before do + build_repo2 do + build_plugin "after-require-all-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_REQUIRE_ALL do |deps| + puts "required gems \#{deps.map(&:name).join(", ")}" + end + RUBY + end + end + + bundle "plugin install after-require-all-plugin --source #{file_uri_for(gem_repo2)}" + end + + it "runs after all rubygems are required" do + install_gemfile_and_bundler_require + expect(out).to include "required gems rake, rack" + end + end + + context "after-require hook" do + before do + build_repo2 do + build_plugin "after-require-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_REQUIRE do |dep| + puts "required gem \#{dep.name}" + end + RUBY + end + end + + bundle "plugin install after-require-plugin --source #{file_uri_for(gem_repo2)}" + end + + it "runs after each rubygem is required" do + install_gemfile_and_bundler_require + expect(out).to include "required gem rake" + expect(out).to include "required gem rack" + end + end + + def install_gemfile_and_bundler_require + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rake" + gem "rack" + G + + ruby <<-RUBY + require "bundler" + Bundler.require + RUBY + end end diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb index ffac30d6d8..50a5258dc7 100644 --- a/spec/bundler/runtime/inline_spec.rb +++ b/spec/bundler/runtime/inline_spec.rb @@ -638,4 +638,22 @@ RSpec.describe "bundler/inline#gemfile" do expect(out).to include("Installing timeout 999") end + + it "does not upcase ENV" do + script <<-RUBY + require 'bundler/inline' + + ENV['Test_Variable'] = 'value string' + puts("before: \#{ENV.each_key.select { |key| key.match?(/test_variable/i) }}") + + gemfile do + source "#{file_uri_for(gem_repo1)}" + end + + puts("after: \#{ENV.each_key.select { |key| key.match?(/test_variable/i) }}") + RUBY + + expect(out).to include("before: [\"Test_Variable\"]") + expect(out).to include("after: [\"Test_Variable\"]") + end end diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb index ccfe5d55b6..2d78825de4 100644 --- a/spec/bundler/runtime/setup_spec.rb +++ b/spec/bundler/runtime/setup_spec.rb @@ -767,6 +767,18 @@ end expect(err).to be_empty end + it "can require rubygems without warnings, when using a local cache", rubygems: ">= 3.5.10" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + bundle "package" + bundle %(exec ruby -w -e "require 'rubygems'") + + expect(err).to be_empty + end + context "when the user has `MANPATH` set", :man do before { ENV["MANPATH"] = "/foo#{File::PATH_SEPARATOR}" } @@ -1599,4 +1611,19 @@ end sys_exec "#{Gem.ruby} #{script}", raise_on_error: false expect(out).to include("requiring foo used the monkeypatch") end + + it "performs an automatic bundle install" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack", :group => :test + G + + bundle "config set auto_install 1" + + ruby <<-RUBY + require 'bundler/setup' + RUBY + expect(err).to be_empty + expect(out).to include("Installing rack 1.0.0") + end end diff --git a/spec/bundler/runtime/with_unbundled_env_spec.rb b/spec/bundler/runtime/with_unbundled_env_spec.rb index 84b198cfb6..135c71b0af 100644 --- a/spec/bundler/runtime/with_unbundled_env_spec.rb +++ b/spec/bundler/runtime/with_unbundled_env_spec.rb @@ -139,7 +139,7 @@ RSpec.describe "Bundler.with_env helpers" do describe "Bundler.with_original_env" do it "should set ENV to original_env in the block" do expected = Bundler.original_env - actual = Bundler.with_original_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) } + actual = Bundler.with_original_env { ENV.to_hash } expect(actual).to eq(expected) end @@ -157,7 +157,7 @@ RSpec.describe "Bundler.with_env helpers" do expected = Bundler.unbundled_env actual = Bundler.ui.silence do - Bundler.with_clean_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) } + Bundler.with_clean_env { ENV.to_hash } end expect(actual).to eq(expected) @@ -175,7 +175,7 @@ RSpec.describe "Bundler.with_env helpers" do describe "Bundler.with_unbundled_env" do it "should set ENV to unbundled_env in the block" do expected = Bundler.unbundled_env - actual = Bundler.with_unbundled_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) } + actual = Bundler.with_unbundled_env { ENV.to_hash } expect(actual).to eq(expected) end diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb index ab2dafb0b9..8f646b9358 100644 --- a/spec/bundler/support/builders.rb +++ b/spec/bundler/support/builders.rb @@ -518,7 +518,6 @@ module Spec if options[:rubygems_version] @spec.rubygems_version = options[:rubygems_version] - def @spec.mark_version; end def @spec.validate(*); end end diff --git a/spec/prism.mspec b/spec/prism.mspec index 16508bd547..42956c6e7b 100644 --- a/spec/prism.mspec +++ b/spec/prism.mspec @@ -1,45 +1,4 @@ # frozen_string_literal: true -# This is turned off because when we run with --parser=prism we explicitly turn -# off experimental warnings to make sure the output is consistent. -MSpec.register(:exclude, "Warning.[] returns default values for categories :deprecated and :experimental") - -## Language -MSpec.register(:exclude, "Hash literal expands an '**{}' or '**obj' element with the last key/value pair taking precedence") -MSpec.register(:exclude, "Hash literal expands an '**{}' and warns when finding an additional duplicate key afterwards") -MSpec.register(:exclude, "Hash literal merges multiple nested '**obj' in Hash literals") -MSpec.register(:exclude, "Hash literal raises a SyntaxError at parse time when Symbol key with invalid bytes") -MSpec.register(:exclude, "Hash literal raises a SyntaxError at parse time when Symbol key with invalid bytes and 'key: value' syntax used") -MSpec.register(:exclude, "Regexps with encoding modifiers supports /e (EUC encoding) with interpolation") -MSpec.register(:exclude, "Regexps with encoding modifiers supports /e (EUC encoding) with interpolation /o") -MSpec.register(:exclude, "Regexps with encoding modifiers preserves EUC-JP as /e encoding through interpolation") -MSpec.register(:exclude, "Regexps with encoding modifiers supports /s (Windows_31J encoding) with interpolation") -MSpec.register(:exclude, "Regexps with encoding modifiers supports /s (Windows_31J encoding) with interpolation and /o") -MSpec.register(:exclude, "Regexps with encoding modifiers preserves Windows-31J as /s encoding through interpolation") -MSpec.register(:exclude, "Regexps with encoding modifiers supports /u (UTF8 encoding) with interpolation") -MSpec.register(:exclude, "Regexps with encoding modifiers supports /u (UTF8 encoding) with interpolation and /o") -MSpec.register(:exclude, "Regexps with encoding modifiers preserves UTF-8 as /u encoding through interpolation") -MSpec.register(:exclude, "A Symbol literal raises an SyntaxError at parse time when Symbol with invalid bytes") - -## Core -MSpec.register(:exclude, "IO.popen with a leading Array argument accepts a trailing Hash of Process.exec options") -MSpec.register(:exclude, "IO.popen with a leading Array argument accepts an IO mode argument following the Array") -MSpec.register(:exclude, "TracePoint#inspect returns a String showing the event, method, path and line for a :return event") -MSpec.register(:exclude, "TracePoint.new includes multiple events when multiple event names are passed as params") -MSpec.register(:exclude, "TracePoint#path equals \"(eval at __FILE__:__LINE__)\" inside an eval for :end event") - -## Library -MSpec.register(:exclude, "Coverage.peek_result returns the result so far") -MSpec.register(:exclude, "Coverage.peek_result second call after require returns accumulated result") -MSpec.register(:exclude, "Coverage.result gives the covered files as a hash with arrays of count or nil") -MSpec.register(:exclude, "Coverage.result returns results for each mode separately when enabled :all modes") -MSpec.register(:exclude, "Coverage.result returns results for each mode separately when enabled any mode explicitly") -MSpec.register(:exclude, "Coverage.result returns the correct results when eval coverage is enabled") +# We need to respect the eval coverage setting. MSpec.register(:exclude, "Coverage.result returns the correct results when eval coverage is disabled") -MSpec.register(:exclude, "Coverage.result clears counters (sets 0 values) when stop is not specified but clear: true specified") -MSpec.register(:exclude, "Coverage.result does not clear counters when stop is not specified but clear: false specified") -MSpec.register(:exclude, "Coverage.result does not clear counters when stop: false and clear is not specified") -MSpec.register(:exclude, "Coverage.result clears counters (sets 0 values) when stop: false and clear: true specified") -MSpec.register(:exclude, "Coverage.result does not clear counters when stop: false and clear: false specified") -MSpec.register(:exclude, "Coverage.start measures coverage within eval") -MSpec.register(:exclude, "Socket.gethostbyaddr using an IPv6 address with an explicit address family raises SocketError when the address is not supported by the family") diff --git a/spec/ruby/core/binding/irb_spec.rb b/spec/ruby/core/binding/irb_spec.rb index 25521f0dd7..2607c7ef33 100644 --- a/spec/ruby/core/binding/irb_spec.rb +++ b/spec/ruby/core/binding/irb_spec.rb @@ -10,7 +10,7 @@ describe "Binding#irb" do IO.popen([envs, *ruby_exe, irb_fixture, chdir: dir], "r+") do |pipe| pipe.puts "a ** 2" pipe.puts "exit" - pipe.readlines.map(&:chomp) + pipe.readlines.map(&:chomp).reject(&:empty?) end end diff --git a/spec/ruby/core/encoding/inspect_spec.rb b/spec/ruby/core/encoding/inspect_spec.rb index 9a930b2a77..df96141db9 100644 --- a/spec/ruby/core/encoding/inspect_spec.rb +++ b/spec/ruby/core/encoding/inspect_spec.rb @@ -5,9 +5,23 @@ describe "Encoding#inspect" do Encoding::UTF_8.inspect.should be_an_instance_of(String) end - it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do - Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc| - enc.inspect.should =~ /#<Encoding:#{enc.name}>/ + ruby_version_is ""..."3.4" do + it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do + Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc| + enc.inspect.should =~ /#<Encoding:#{enc.name}>/ + end + end + end + + ruby_version_is "3.4" do + it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do + Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc| + if enc.name == "ASCII-8BIT" + enc.inspect.should == "#<Encoding:BINARY (ASCII-8BIT)>" + else + enc.inspect.should =~ /#<Encoding:#{enc.name}>/ + end + end end end diff --git a/spec/ruby/core/string/index_spec.rb b/spec/ruby/core/string/index_spec.rb index be79708045..835263a2cd 100644 --- a/spec/ruby/core/string/index_spec.rb +++ b/spec/ruby/core/string/index_spec.rb @@ -231,6 +231,17 @@ describe "String#index with Regexp" do $~.should == nil end + ruby_bug "#20421", ""..."3.3" do + it "always clear $~" do + "a".index(/a/) + $~.should_not == nil + + string = "blablabla" + string.index(/bla/, string.length + 1) + $~.should == nil + end + end + it "starts the search at the given offset" do "blablabla".index(/.{0}/, 5).should == 5 "blablabla".index(/.{1}/, 5).should == 5 diff --git a/spec/ruby/core/warning/element_reference_spec.rb b/spec/ruby/core/warning/element_reference_spec.rb index 8cb4018c20..c0ed37ef13 100644 --- a/spec/ruby/core/warning/element_reference_spec.rb +++ b/spec/ruby/core/warning/element_reference_spec.rb @@ -2,6 +2,10 @@ require_relative '../../spec_helper' describe "Warning.[]" do it "returns default values for categories :deprecated and :experimental" do + # If any warning options were set on the Ruby that will be executed, then + # it's possible this test will fail. In this case we will skip this test. + skip if ruby_exe.any? { |opt| opt.start_with?("-W") } + ruby_exe('p [Warning[:deprecated], Warning[:experimental]]').chomp.should == "[false, true]" ruby_exe('p [Warning[:deprecated], Warning[:experimental]]', options: "-w").chomp.should == "[true, true]" end diff --git a/spec/ruby/language/break_spec.rb b/spec/ruby/language/break_spec.rb index 627cb4a071..e725e77e80 100644 --- a/spec/ruby/language/break_spec.rb +++ b/spec/ruby/language/break_spec.rb @@ -372,7 +372,7 @@ describe "Executing break from within a block" do end.should_not raise_error end - it "raises LocalJumpError when converted into a proc during a a super call" do + it "raises LocalJumpError when converted into a proc during a super call" do cls1 = Class.new { def foo(&b); b; end } cls2 = Class.new(cls1) { def foo; super { break 1 }.call; end } diff --git a/spec/ruby/optional/capi/ext/io_spec.c b/spec/ruby/optional/capi/ext/io_spec.c index bcd3940e34..1a73331386 100644 --- a/spec/ruby/optional/capi/ext/io_spec.c +++ b/spec/ruby/optional/capi/ext/io_spec.c @@ -157,7 +157,7 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { return ret ? Qtrue : Qfalse; #else - UNREACHABLE; + UNREACHABLE_RETURN(Qnil); #endif } @@ -174,6 +174,10 @@ VALUE io_spec_rb_io_maybe_wait_writable(VALUE self, VALUE error, VALUE io, VALUE #endif #ifdef RUBY_VERSION_IS_3_1 +#ifdef SET_NON_BLOCKING_FAILS_ALWAYS +NORETURN(VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE timeout, VALUE read_p)); +#endif + VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE timeout, VALUE read_p) { int fd = io_spec_get_fd(io); #ifndef SET_NON_BLOCKING_FAILS_ALWAYS @@ -209,7 +213,7 @@ VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE return INT2NUM(ret); #else - UNREACHABLE; + UNREACHABLE_RETURN(Qnil); #endif } #endif @@ -3374,7 +3374,7 @@ rb_enc_cr_str_buf_cat(VALUE str, const char *ptr, long len, incompatible: rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s", - rb_enc_name(str_enc), rb_enc_name(ptr_enc)); + rb_enc_inspect_name(str_enc), rb_enc_inspect_name(ptr_enc)); UNREACHABLE_RETURN(Qundef); } @@ -5854,8 +5854,8 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) if (coderange_scan(p, beg0, str_enc) != ENC_CODERANGE_7BIT || coderange_scan(p+end0, len-end0, str_enc) != ENC_CODERANGE_7BIT) { rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s", - rb_enc_name(str_enc), - rb_enc_name(STR_ENC_GET(repl))); + rb_enc_inspect_name(str_enc), + rb_enc_inspect_name(STR_ENC_GET(repl))); } enc = STR_ENC_GET(repl); } @@ -11120,7 +11120,7 @@ str_compat_and_valid(VALUE str, rb_encoding *enc) rb_encoding *e = STR_ENC_GET(str); if (cr == ENC_CODERANGE_7BIT ? rb_enc_mbminlen(enc) != 1 : enc != e) { rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s", - rb_enc_name(enc), rb_enc_name(e)); + rb_enc_inspect_name(enc), rb_enc_inspect_name(e)); } } return str; diff --git a/template/Makefile.in b/template/Makefile.in index d9a3cbc065..813a727cf9 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -663,3 +663,28 @@ yes-test-leaked-globals: yes-test-leaked-globals-precheck PLATFORM=$(hdrdir)/ruby/$(PLATFORM_DIR).h $(srcdir)/configure.ac \ $(COMMONOBJS) $(LIBRUBY_FOR_LEAKED_GLOBALS:yes=$(LIBRUBY_SO)) $(ACTIONS_ENDGROUP) + +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) + $(XRUBY) -C "$(srcdir)" bin/gem install --no-document \ + --install-dir .bundle --conservative "rspec:~> 3" + $(ACTIONS_ENDGROUP) + +RSPECOPTS = +SYNTAX_SUGGEST_SPECS = +PREPARE_SYNTAX_SUGGEST = $(TEST_RUNNABLE)-test-syntax-suggest-prepare +test-syntax-suggest: $(TEST_RUNNABLE)-test-syntax-suggest +yes-test-syntax-suggest: $(PREPARE_SYNTAX_SUGGEST) + $(ACTIONS_GROUP) + $(XRUBY) -C $(srcdir) -Ispec/syntax_suggest:spec/lib .bundle/bin/rspec \ + --require rspec/expectations \ + --require spec_helper --require formatter_overrides --require spec_coverage \ + $(RSPECOPTS) spec/syntax_suggest/$(SYNTAX_SUGGEST_SPECS) + $(ACTIONS_ENDGROUP) +no-test-syntax-suggest: diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl index dc0a143004..e17a75da79 100644 --- a/template/prelude.c.tmpl +++ b/template/prelude.c.tmpl @@ -141,37 +141,39 @@ COMPILER_WARNING_POP #define PRELUDE_NAME(n) rb_usascii_str_new_static(prelude_name##n, sizeof(prelude_name##n)-1) #define PRELUDE_CODE(n) rb_utf8_str_new_static(prelude_code##n.L0, sizeof(prelude_code##n)) -static rb_ast_t * -prelude_ast(VALUE name, VALUE code, int line) +static VALUE +prelude_ast_value(VALUE name, VALUE code, int line) { - rb_ast_t *ast = rb_parser_compile_string_path(rb_parser_new(), name, code, line); + rb_ast_t *ast; + VALUE ast_value = rb_parser_compile_string_path(rb_parser_new(), name, code, line); + ast = rb_ruby_ast_data_get(ast_value); if (!ast || !ast->body.root) { if (ast) rb_ast_dispose(ast); rb_exc_raise(rb_errinfo()); } - return ast; + return ast_value; } % end % if @builtin_count > 0 -#define PRELUDE_AST(n, name_str, start_line) \ +#define PRELUDE_VAST(n, name_str, start_line) \ (((sizeof(prelude_name<%='##'%><%=%>n) - prefix_len - 2) == namelen) && \ (strncmp(prelude_name<%='##'%><%=%>n + prefix_len, feature_name, namelen) == 0) ? \ - prelude_ast((name_str) = PRELUDE_NAME(n), PRELUDE_CODE(n), start_line) : 0) + prelude_ast_value((name_str) = PRELUDE_NAME(n), PRELUDE_CODE(n), start_line) : Qnil) -rb_ast_t * -rb_builtin_ast(const char *feature_name, VALUE *name_str) +VALUE +rb_builtin_ast_value(const char *feature_name, VALUE *name_str) { const size_t prefix_len = rb_strlen_lit("<internal:"); size_t namelen = strlen(feature_name); - rb_ast_t *ast = 0; + VALUE ast_value = Qnil; % @preludes.each_value do |i, prelude, lines, sub, start_line| % if sub - if ((ast = PRELUDE_AST(<%=i%><%=%>, *name_str, <%=start_line%>)) != 0) return ast; + if (!NIL_P(ast_value = PRELUDE_VAST(<%=i%><%=%>, *name_str, <%=start_line%>))) return ast_value; % end % end - return ast; + return ast_value; } % end @@ -196,8 +198,10 @@ prelude_eval(VALUE code, VALUE name, int line) 0, /* int debug_level; */ }; - rb_ast_t *ast = prelude_ast(name, code, line); - rb_iseq_eval(rb_iseq_new_with_opt(&ast->body, name, name, Qnil, line, + rb_ast_t *ast; + VALUE ast_value = prelude_ast_value(name, code, line); + ast = rb_ruby_ast_data_get(ast_value); + rb_iseq_eval(rb_iseq_new_with_opt(ast_value, name, name, Qnil, line, NULL, 0, ISEQ_TYPE_TOP, &optimization, Qnil)); rb_ast_dispose(ast); diff --git a/test/.excludes-prism/TestCall.rb b/test/.excludes-prism/TestCall.rb deleted file mode 100644 index 969e32ea5a..0000000000 --- a/test/.excludes-prism/TestCall.rb +++ /dev/null @@ -1,3 +0,0 @@ -exclude(:test_call_op_asgn_keywords, "https://github.com/ruby/prism/issues/2438") -exclude(:test_call_op_asgn_keywords_mutable, "https://github.com/ruby/prism/issues/2438") -exclude(:test_kwsplat_block_order_op_asgn, "https://github.com/ruby/prism/issues/2438") diff --git a/test/.excludes-prism/TestCoverage.rb b/test/.excludes-prism/TestCoverage.rb new file mode 100644 index 0000000000..f122d6edbc --- /dev/null +++ b/test/.excludes-prism/TestCoverage.rb @@ -0,0 +1 @@ +exclude(:test_eval, "respect eval coverage setting") diff --git a/test/.excludes-prism/TestIRB/RubyLexTest.rb b/test/.excludes-prism/TestIRB/RubyLexTest.rb deleted file mode 100644 index 2274ae62cf..0000000000 --- a/test/.excludes-prism/TestIRB/RubyLexTest.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_code_block_open_with_should_continue, "symbol encoding") diff --git a/test/.excludes-prism/TestISeq.rb b/test/.excludes-prism/TestISeq.rb index 3768d8fc05..499ae2e53d 100644 --- a/test/.excludes-prism/TestISeq.rb +++ b/test/.excludes-prism/TestISeq.rb @@ -1,3 +1,2 @@ -exclude(:test_each_child, "https://github.com/ruby/prism/issues/2660") -exclude(:test_syntax_error_message, "Assertion checks against specific error format") -exclude(:test_trace_points, "https://github.com/ruby/prism/issues/2660") +exclude(:test_each_child, "https://bugs.ruby-lang.org/issues/20479") +exclude(:test_trace_points, "https://bugs.ruby-lang.org/issues/20479") diff --git a/test/.excludes-prism/TestM17N.rb b/test/.excludes-prism/TestM17N.rb index e9e0623689..0367ffa9f2 100644 --- a/test/.excludes-prism/TestM17N.rb +++ b/test/.excludes-prism/TestM17N.rb @@ -1,10 +1 @@ -exclude(:test_dynamic_eucjp_regexp, "https://github.com/ruby/prism/issues/2664") -exclude(:test_dynamic_sjis_regexp, "https://github.com/ruby/prism/issues/2664") -exclude(:test_dynamic_utf8_regexp, "https://github.com/ruby/prism/issues/2664") -exclude(:test_regexp_ascii, "https://github.com/ruby/prism/issues/2664") -exclude(:test_regexp_embed, "https://github.com/ruby/prism/issues/2664") -exclude(:test_regexp_mixed_unicode, "unknown") -exclude(:test_regexp_too_short_multibyte_character, "unknown") -exclude(:test_regexp_unicode, "unknown") -exclude(:test_regexp_usascii, "unknown") -exclude(:test_string_mixed_unicode, "unknown") +exclude(:test_regexp_usascii, "x80 should raise syntax error") diff --git a/test/.excludes-prism/TestMixedUnicodeEscape.rb b/test/.excludes-prism/TestMixedUnicodeEscape.rb index 09e3cc168b..753c119d14 100644 --- a/test/.excludes-prism/TestMixedUnicodeEscape.rb +++ b/test/.excludes-prism/TestMixedUnicodeEscape.rb @@ -1 +1 @@ -exclude(:test_basic, "unknown") +exclude(:test_basic, "should raise mixed encoding error") diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index 83af593b15..513a7ed942 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,28 +1,2 @@ -exclude(:test_dynamic_constant_assignment, "unknown") -exclude(:test_else_without_rescue, "unknown") -exclude(:test_error_def_in_argument, "unknown") -exclude(:test_float, "unknown") -exclude(:test_global_variable, "unknown") -exclude(:test_here_document, "unknown") -exclude(:test_heredoc_unterminated_interpolation, "unknown") -exclude(:test_invalid_char, "unknown") -exclude(:test_location_of_invalid_token, "unknown") -exclude(:test_op_asgn1_with_block, "unknown") -exclude(:test_parse_string, "unknown") -exclude(:test_percent, "unknown") -exclude(:test_question, "unknown") -exclude(:test_shareable_constant_value_ignored, "unknown") -exclude(:test_shareable_constant_value_nested, "ractor support") -exclude(:test_shareable_constant_value_nonliteral, "ractor support") -exclude(:test_shareable_constant_value_simple, "ractor support") -exclude(:test_shareable_constant_value_unfrozen, "ractor support") -exclude(:test_shareable_constant_value_unshareable_literal, "ractor support") -exclude(:test_string, "unknown") -exclude(:test_truncated_source_line, "unknown") -exclude(:test_unassignable, "unknown") -exclude(:test_unexpected_eof, "unknown") -exclude(:test_unexpected_token_after_numeric, "unknown") -exclude(:test_unterminated_regexp_error, "unknown") -exclude(:test_unused_variable, "missing warning") -exclude(:test_void_value_in_rhs, "unknown") -exclude(:test_words, "unknown") +exclude(:test_truncated_source_line, "truncate error message") +exclude(:test_void_value_in_rhs, "missing raising error for some void value expressions") diff --git a/test/.excludes-prism/TestPatternMatching.rb b/test/.excludes-prism/TestPatternMatching.rb deleted file mode 100644 index cfd0c6bed9..0000000000 --- a/test/.excludes-prism/TestPatternMatching.rb +++ /dev/null @@ -1,2 +0,0 @@ -exclude(:test_hash_pattern, "useless literal warning missing") -exclude(:test_invalid_syntax, "[a:] is disallowed") diff --git a/test/.excludes-prism/TestRegexp.rb b/test/.excludes-prism/TestRegexp.rb index 68ad1414a9..5852d870ef 100644 --- a/test/.excludes-prism/TestRegexp.rb +++ b/test/.excludes-prism/TestRegexp.rb @@ -1,6 +1,2 @@ -exclude(:test_invalid_escape_error, "unknown") -exclude(:test_invalid_fragment, "https://github.com/ruby/prism/issues/2664") -exclude(:test_unescape, "unknown") -exclude(:test_unicode_age_14_0, "https://github.com/ruby/prism/issues/2664") -exclude(:test_unicode_age_15_0, "https://github.com/ruby/prism/issues/2664") -exclude(:test_unicode_age, "https://github.com/ruby/prism/issues/2664") +exclude(:test_unescape, "unescapes in regexp missing some bytes") +exclude(:test_match_control_meta_escape, "unescapes in regexp missing some bytes") diff --git a/test/.excludes-prism/TestRequire.rb b/test/.excludes-prism/TestRequire.rb index a7f66c5d80..cb35d59c60 100644 --- a/test/.excludes-prism/TestRequire.rb +++ b/test/.excludes-prism/TestRequire.rb @@ -1 +1 @@ -exclude(:test_require_nonascii_path_shift_jis, "encoding") +exclude(:test_require_nonascii_path_shift_jis, "requiring non-ascii paths") diff --git a/test/.excludes-prism/TestRubyLiteral.rb b/test/.excludes-prism/TestRubyLiteral.rb index bf5dbcd36a..a25e1c47d9 100644 --- a/test/.excludes-prism/TestRubyLiteral.rb +++ b/test/.excludes-prism/TestRubyLiteral.rb @@ -1,6 +1 @@ -exclude(:test_debug_frozen_string_in_array_literal, "unknown") -exclude(:test_debug_frozen_string, "unknown") -exclude(:test_dregexp, "https://github.com/ruby/prism/issues/2664") -exclude(:test_hash_value_omission, "unknown") -exclude(:test_integer, "unknown") -exclude(:test_string, "https://github.com/ruby/prism/issues/2331") +exclude(:test_dregexp, "x80 should raise syntax error") diff --git a/test/.excludes-prism/TestRubyOptimization.rb b/test/.excludes-prism/TestRubyOptimization.rb deleted file mode 100644 index df22ca4f71..0000000000 --- a/test/.excludes-prism/TestRubyOptimization.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_peephole_string_literal_range, "unknown") diff --git a/test/.excludes-prism/TestRubyVM.rb b/test/.excludes-prism/TestRubyVM.rb deleted file mode 100644 index 6d4c3ca6fe..0000000000 --- a/test/.excludes-prism/TestRubyVM.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_keep_script_lines, "unknown") diff --git a/test/.excludes-prism/TestSetTraceFunc.rb b/test/.excludes-prism/TestSetTraceFunc.rb deleted file mode 100644 index dd0ea3b219..0000000000 --- a/test/.excludes-prism/TestSetTraceFunc.rb +++ /dev/null @@ -1,2 +0,0 @@ -exclude(:test_return, "unknown") -exclude(:test_return2, "unknown") diff --git a/test/.excludes-prism/TestSyntax.rb b/test/.excludes-prism/TestSyntax.rb index f8ae776930..0505330b1c 100644 --- a/test/.excludes-prism/TestSyntax.rb +++ b/test/.excludes-prism/TestSyntax.rb @@ -1,30 +1,9 @@ -exclude(:test__END___cr, "unknown") -exclude(:test_anonymous_block_forwarding, "unknown") -exclude(:test_anonymous_keyword_rest_forwarding, "unknown") -exclude(:test_anonymous_rest_forwarding, "unknown") -exclude(:test_argument_forwarding_with_super, "unknown") -exclude(:test_argument_forwarding, "unknown") -exclude(:test_brace_after_literal_argument, "unknown") -exclude(:test_dedented_heredoc_concatenation, "unknown") -exclude(:test_dedented_heredoc_continued_line, "unknown") -exclude(:test_dedented_heredoc_invalid_identifer, "unknown") -exclude(:test_duplicated_when, "unknown") -exclude(:test_error_message_encoding, "unknown") -exclude(:test_heredoc_cr, "unknown") -exclude(:test_heredoc_no_terminator, "unknown") -exclude(:test_invalid_encoding_symbol, "unknown") +exclude(:test_dedented_heredoc_continued_line, "heredoc line continuation dedent calculation") exclude(:test_it, "https://github.com/ruby/prism/issues/2323") -exclude(:test_keyword_invalid_name, "unknown") -exclude(:test_keyword_self_reference, "unknown") -exclude(:test_keywords_specified_and_not_accepted, "unknown") -exclude(:test_methoddef_endless_command, "unknown") -exclude(:test_numbered_parameter, "unknown") -exclude(:test_optional_self_reference, "unknown") -exclude(:test_safe_call_in_massign_lhs, "unknown") -exclude(:test_syntax_error_at_newline, "unknown") -exclude(:test_unassignable, "unknown") -exclude(:test_unexpected_fraction, "unknown") -exclude(:test_unterminated_heredoc_cr, "unknown") -exclude(:test_unterminated_heredoc, "unknown") -exclude(:test_warn_balanced, "unknown") -exclude(:test_warn_unreachable, "unknown") +exclude(:test_numbered_parameter, "should raise syntax error for numbered parameters in inner blocks") +exclude(:test_unterminated_heredoc_cr, "quoted \r heredoc terminators should not match \r\n") +exclude(:test_warn_balanced, "missing warning for ** being interpreted as a binary operator") + +exclude(:test_duplicated_when, "https://bugs.ruby-lang.org/issues/20401") +exclude(:test_optional_self_reference, "https://bugs.ruby-lang.org/issues/20478") +exclude(:test_keyword_self_reference, "https://bugs.ruby-lang.org/issues/20478") diff --git a/test/.excludes-prism/TestUnicodeEscape.rb b/test/.excludes-prism/TestUnicodeEscape.rb deleted file mode 100644 index add4911bc2..0000000000 --- a/test/.excludes-prism/TestUnicodeEscape.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_fail, "unknown") diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb index 16be47b458..4773280cae 100644 --- a/test/coverage/test_coverage.rb +++ b/test/coverage/test_coverage.rb @@ -5,22 +5,33 @@ require "tmpdir" require "envutil" class TestCoverage < Test::Unit::TestCase + # The command-line arguments that we will pass to the ruby subprocess invoked + # by assert_in_out_err. In general this is just requiring the coverage + # library, but if prism is enabled we want to additionally pass that option + # through. + ARGV = ["-rcoverage"] + + if RubyVM::InstructionSequence.compile('').to_a[4][:parser] == :prism + ARGV << "-W:no-experimental" + ARGV << "--parser=prism" + end + def test_result_without_start - assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not enabled/) + assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not enabled/) Coverage.result p :NG end; end def test_peek_result_without_start - assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not enabled/) + assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not enabled/) Coverage.peek_result p :NG end; end def test_result_with_nothing - assert_in_out_err(%w[-rcoverage], <<-"end;", ["{}"], []) + assert_in_out_err(ARGV, <<-"end;", ["{}"], []) Coverage.start p Coverage.result end; @@ -34,7 +45,7 @@ class TestCoverage < Test::Unit::TestCase end def test_coverage_running? - assert_in_out_err(%w[-rcoverage], <<-"end;", ["false", "true", "true", "false"], []) + assert_in_out_err(ARGV, <<-"end;", ["false", "true", "true", "false"], []) p Coverage.running? Coverage.start p Coverage.running? @@ -56,7 +67,7 @@ class TestCoverage < Test::Unit::TestCase EOS end - assert_in_out_err(%w[-rcoverage], <<-"end;", ["[1, 0, nil]", "[1, 1, nil]", "[1, 1, nil]"], []) + assert_in_out_err(ARGV, <<-"end;", ["[1, 0, nil]", "[1, 1, nil]", "[1, 1, nil]"], []) Coverage.start tmp = Dir.pwd require tmp + "/test.rb" @@ -92,7 +103,7 @@ class TestCoverage < Test::Unit::TestCase exp1 = { "#{tmp}/test.rb" => [1, 0, nil] }.inspect exp2 = {}.inspect exp3 = { "#{tmp}/test2.rb" => [1] }.inspect - assert_in_out_err(%w[-rcoverage], <<-"end;", [exp1, exp2, exp3], []) + assert_in_out_err(ARGV, <<-"end;", [exp1, exp2, exp3], []) Coverage.start tmp = Dir.pwd require tmp + "/test.rb" @@ -124,7 +135,7 @@ class TestCoverage < Test::Unit::TestCase f.puts "])" end - assert_in_out_err(%w[-rcoverage], <<-"end;", ["10003"], []) + assert_in_out_err(ARGV, <<-"end;", ["10003"], []) Coverage.start tmp = Dir.pwd require tmp + '/test.rb' @@ -153,7 +164,7 @@ class TestCoverage < Test::Unit::TestCase f.puts 'end' end - assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", ["[1, 1, 1, 400, nil, nil, nil, nil, nil, nil, nil]"], [], bug13305) + assert_in_out_err(["-W0", *ARGV], <<-"end;", ["[1, 1, 1, 400, nil, nil, nil, nil, nil, nil, nil]"], [], bug13305) Coverage.start(:all) tmp = Dir.pwd require tmp + '/test.rb' @@ -165,7 +176,7 @@ class TestCoverage < Test::Unit::TestCase end def test_eval_coverage - assert_in_out_err(%w[-rcoverage], <<-"end;", ["[1, 1, 1, nil, 0, nil]"], []) + assert_in_out_err(ARGV, <<-"end;", ["[1, 1, 1, nil, 0, nil]"], []) Coverage.start(eval: true, lines: true) eval(<<-RUBY, TOPLEVEL_BINDING, "test.rb") @@ -242,7 +253,7 @@ class TestCoverage < Test::Unit::TestCase Dir.chdir(tmp) { File.write("test.rb", code) - assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", stdout, []) + assert_in_out_err(["-W0", *ARGV], <<-"end;", stdout, []) Coverage.start(#{ opt }) tmp = Dir.pwd require tmp + '/test.rb' @@ -642,7 +653,7 @@ class TestCoverage < Test::Unit::TestCase "{:lines=>[0, 1, 1, nil, 0, nil, nil]}", "{:lines=>[0, 1, 0, nil, 1, nil, nil]}", ] - assert_in_out_err(%w[-rcoverage], <<-"end;", exp, []) + assert_in_out_err(ARGV, <<-"end;", exp, []) Coverage.start(lines: true) tmp = Dir.pwd f = tmp + "/test.rb" @@ -676,7 +687,7 @@ class TestCoverage < Test::Unit::TestCase "{:branches=>{[:if, 0, 2, 2, 6, 5]=>{[:then, 1, 3, 4, 3, 8]=>0, [:else, 2, 5, 4, 5, 12]=>1}}}", "{:branches=>{[:if, 0, 2, 2, 6, 5]=>{[:then, 1, 3, 4, 3, 8]=>0, [:else, 2, 5, 4, 5, 12]=>1}}}", ] - assert_in_out_err(%w[-rcoverage], <<-"end;", exp, []) + assert_in_out_err(ARGV, <<-"end;", exp, []) Coverage.start(branches: true) tmp = Dir.pwd f = tmp + "/test.rb" @@ -712,7 +723,7 @@ class TestCoverage < Test::Unit::TestCase "{:methods=>{[Object, :foo, 1, 0, 7, 3]=>1}}", "{:methods=>{[Object, :foo, 1, 0, 7, 3]=>1}}" ] - assert_in_out_err(%w[-rcoverage], <<-"end;", exp, []) + assert_in_out_err(ARGV, <<-"end;", exp, []) Coverage.start(methods: true) tmp = Dir.pwd f = tmp + "/test.rb" @@ -748,7 +759,7 @@ class TestCoverage < Test::Unit::TestCase "{:oneshot_lines=>[5]}", "{:oneshot_lines=>[]}", ] - assert_in_out_err(%w[-rcoverage], <<-"end;", exp, []) + assert_in_out_err(ARGV, <<-"end;", exp, []) Coverage.start(oneshot_lines: true) tmp = Dir.pwd f = tmp + "/test.rb" @@ -924,7 +935,7 @@ class TestCoverage < Test::Unit::TestCase end def test_coverage_state - assert_in_out_err(%w[-rcoverage], <<-"end;", [":idle", ":running", ":running", ":idle"], []) + assert_in_out_err(ARGV, <<-"end;", [":idle", ":running", ":running", ":idle"], []) p Coverage.state Coverage.start p Coverage.state @@ -934,7 +945,7 @@ class TestCoverage < Test::Unit::TestCase p Coverage.state end; - assert_in_out_err(%w[-rcoverage], <<-"end;", [":idle", ":suspended", ":running", ":suspended", ":running", ":suspended", ":idle"], []) + assert_in_out_err(ARGV, <<-"end;", [":idle", ":suspended", ":running", ":suspended", ":running", ":suspended", ":idle"], []) p Coverage.state Coverage.setup p Coverage.state @@ -952,14 +963,14 @@ class TestCoverage < Test::Unit::TestCase end def test_result_without_resume - assert_in_out_err(%w[-rcoverage], <<-"end;", ["{}"], []) + assert_in_out_err(ARGV, <<-"end;", ["{}"], []) Coverage.setup p Coverage.result end; end def test_result_after_suspend - assert_in_out_err(%w[-rcoverage], <<-"end;", ["{}"], []) + assert_in_out_err(ARGV, <<-"end;", ["{}"], []) Coverage.start Coverage.suspend p Coverage.result @@ -967,21 +978,21 @@ class TestCoverage < Test::Unit::TestCase end def test_resume_without_setup - assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not set up yet/) + assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not set up yet/) Coverage.resume p :NG end; end def test_suspend_without_setup - assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not running/) + assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not running/) Coverage.suspend p :NG end; end def test_double_resume - assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is already running/) + assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is already running/) Coverage.start Coverage.resume p :NG @@ -989,7 +1000,7 @@ class TestCoverage < Test::Unit::TestCase end def test_double_suspend - assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not running/) + assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not running/) Coverage.setup Coverage.suspend p :NG diff --git a/test/irb/command/test_custom_command.rb b/test/irb/command/test_custom_command.rb new file mode 100644 index 0000000000..3a3ad11d5a --- /dev/null +++ b/test/irb/command/test_custom_command.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true +require "irb" + +require_relative "../helper" + +module TestIRB + class CustomCommandIntegrationTest < TestIRB::IntegrationTestCase + def test_command_registration_can_happen_after_irb_require + write_ruby <<~RUBY + require "irb" + require "irb/command" + + class PrintCommand < IRB::Command::Base + category 'CommandTest' + description 'print_command' + def execute(*) + puts "Hello from PrintCommand" + end + end + + IRB::Command.register(:print!, PrintCommand) + + binding.irb + RUBY + + output = run_ruby_file do + type "print!" + type "exit" + end + + assert_include(output, "Hello from PrintCommand") + end + + def test_command_registration_accepts_string_too + write_ruby <<~RUBY + require "irb/command" + + class PrintCommand < IRB::Command::Base + category 'CommandTest' + description 'print_command' + def execute(*) + puts "Hello from PrintCommand" + end + end + + IRB::Command.register("print!", PrintCommand) + + binding.irb + RUBY + + output = run_ruby_file do + type "print!" + type "exit" + end + + assert_include(output, "Hello from PrintCommand") + end + + def test_arguments_propagation + write_ruby <<~RUBY + require "irb/command" + + class PrintArgCommand < IRB::Command::Base + category 'CommandTest' + description 'print_command_arg' + def execute(arg) + $nth_execution ||= 0 + puts "\#{$nth_execution} arg=\#{arg.inspect}" + $nth_execution += 1 + end + end + + IRB::Command.register(:print_arg, PrintArgCommand) + + binding.irb + RUBY + + output = run_ruby_file do + type "print_arg" + type "print_arg \n" + type "print_arg a r g" + type "print_arg a r g \n" + type "exit" + end + + assert_include(output, "0 arg=\"\"") + assert_include(output, "1 arg=\"\"") + assert_include(output, "2 arg=\"a r g\"") + assert_include(output, "3 arg=\"a r g\"") + end + + def test_def_extend_command_still_works + write_ruby <<~RUBY + require "irb" + + class FooBarCommand < IRB::Command::Base + category 'FooBarCategory' + description 'foobar_description' + def execute(*) + $nth_execution ||= 1 + puts "\#{$nth_execution} FooBar executed" + $nth_execution += 1 + end + end + + IRB::ExtendCommandBundle.def_extend_command(:foobar, FooBarCommand, nil, [:fbalias, IRB::Command::OVERRIDE_ALL]) + + binding.irb + RUBY + + output = run_ruby_file do + type "foobar" + type "fbalias" + type "help foobar" + type "exit" + end + + assert_include(output, "1 FooBar executed") + assert_include(output, "2 FooBar executed") + assert_include(output, "foobar_description") + end + + def test_no_meta_command_also_works + write_ruby <<~RUBY + require "irb/command" + + class NoMetaCommand < IRB::Command::Base + def execute(*) + puts "This command does not override meta attributes" + end + end + + IRB::Command.register(:no_meta, NoMetaCommand) + + binding.irb + RUBY + + output = run_ruby_file do + type "no_meta" + type "help no_meta" + type "exit" + end + + assert_include(output, "This command does not override meta attributes") + assert_include(output, "No description provided.") + assert_not_include(output, "Maybe IRB bug") + end + end +end diff --git a/test/irb/command/test_force_exit.rb b/test/irb/command/test_force_exit.rb index 9e86c644d6..191a786872 100644 --- a/test/irb/command/test_force_exit.rb +++ b/test/irb/command/test_force_exit.rb @@ -47,17 +47,5 @@ module TestIRB assert_match(/irb\(main\):001> 123/, output) end - - def test_forced_exit_out_of_irb_session - write_ruby <<~'ruby' - at_exit { puts 'un' + 'reachable' } - binding.irb - exit! # this will call exit! method overrided by command - ruby - output = run_ruby_file do - type "exit" - end - assert_not_include(output, 'unreachable') - end end end diff --git a/test/irb/command/test_help.rb b/test/irb/command/test_help.rb index c82c43a4c5..b34832b022 100644 --- a/test/irb/command/test_help.rb +++ b/test/irb/command/test_help.rb @@ -62,5 +62,14 @@ module TestIRB assert_match(/\$\s+Alias for `show_source`/, out) assert_match(/@\s+Alias for `whereami`/, out) end + + def test_help_lists_helper_methods + out = run_ruby_file do + type "help" + type "exit" + end + + assert_match(/Helper methods\s+conf\s+Returns the current IRB context/, out) + end end end diff --git a/test/irb/command/test_multi_irb_commands.rb b/test/irb/command/test_multi_irb_commands.rb new file mode 100644 index 0000000000..e313c0c5d2 --- /dev/null +++ b/test/irb/command/test_multi_irb_commands.rb @@ -0,0 +1,50 @@ +require "tempfile" +require_relative "../helper" + +module TestIRB + class MultiIRBTest < IntegrationTestCase + def setup + super + + write_ruby <<~'RUBY' + binding.irb + RUBY + end + + def test_jobs_command_with_print_deprecated_warning + out = run_ruby_file do + type "jobs" + type "exit" + end + + assert_match(/Multi-irb commands are deprecated and will be removed in IRB 2\.0\.0\. Please use workspace commands instead\./, out) + assert_match(%r|If you have any use case for multi-irb, please leave a comment at https://github.com/ruby/irb/issues/653|, out) + assert_match(/#0->irb on main \(#<Thread:0x.+ run>: running\)/, out) + end + + def test_irb_jobs_and_kill_commands + out = run_ruby_file do + type "irb" + type "jobs" + type "kill 1" + type "exit" + end + + assert_match(/#0->irb on main \(#<Thread:0x.+ sleep_forever>: stop\)/, out) + assert_match(/#1->irb#1 on main \(#<Thread:0x.+ run>: running\)/, out) + end + + def test_irb_fg_jobs_and_kill_commands + out = run_ruby_file do + type "irb" + type "fg 0" + type "jobs" + type "kill 1" + type "exit" + end + + assert_match(/#0->irb on main \(#<Thread:0x.+ run>: running\)/, out) + assert_match(/#1->irb#1 on main \(#<Thread:0x.+ sleep_forever>: stop\)/, out) + end + end +end diff --git a/test/irb/helper.rb b/test/irb/helper.rb index 1614b42adb..acaf6277f3 100644 --- a/test/irb/helper.rb +++ b/test/irb/helper.rb @@ -121,7 +121,9 @@ module TestIRB @envs["XDG_CONFIG_HOME"] ||= tmp_dir @envs["IRBRC"] = nil unless @envs.key?("IRBRC") - PTY.spawn(@envs.merge("TERM" => "dumb"), *cmd) do |read, write, pid| + envs_for_spawn = @envs.merge('TERM' => 'dumb', 'TEST_IRB_FORCE_INTERACTIVE' => 'true') + + PTY.spawn(envs_for_spawn, *cmd) do |read, write, pid| Timeout.timeout(TIMEOUT_SEC) do while line = safe_gets(read) lines << line @@ -196,7 +198,7 @@ module TestIRB end def write_ruby(program) - @ruby_file = Tempfile.create(%w{irb- .rb}) + @ruby_file = Tempfile.create(%w{irbtest- .rb}) @tmpfiles << @ruby_file @ruby_file.write(program) @ruby_file.close diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb index 72e036eab7..9d78f5233e 100644 --- a/test/irb/test_color.rb +++ b/test/irb/test_color.rb @@ -99,7 +99,7 @@ module TestIRB "foo %i[bar]" => "foo #{YELLOW}%i[#{CLEAR}#{YELLOW}bar#{CLEAR}#{YELLOW}]#{CLEAR}", "foo :@bar, baz, :@@qux, :$quux" => "foo #{YELLOW}:#{CLEAR}#{YELLOW}@bar#{CLEAR}, baz, #{YELLOW}:#{CLEAR}#{YELLOW}@@qux#{CLEAR}, #{YELLOW}:#{CLEAR}#{YELLOW}$quux#{CLEAR}", "`echo`" => "#{RED}#{BOLD}`#{CLEAR}#{RED}echo#{CLEAR}#{RED}#{BOLD}`#{CLEAR}", - "\t" => "\t", # not ^I + "\t" => Reline::Unicode.escape_for_print("\t") == ' ' ? ' ' : "\t", # not ^I "foo(*%W(bar))" => "foo(*#{RED}#{BOLD}%W(#{CLEAR}#{RED}bar#{CLEAR}#{RED}#{BOLD})#{CLEAR})", "$stdout" => "#{GREEN}#{BOLD}$stdout#{CLEAR}", "__END__" => "#{GREEN}__END__#{CLEAR}", diff --git a/test/irb/test_command.rb b/test/irb/test_command.rb index 03fdd37855..8cb8928adb 100644 --- a/test/irb/test_command.rb +++ b/test/irb/test_command.rb @@ -210,76 +210,6 @@ module TestIRB end end - class CustomCommandTestCase < CommandTestCase - def setup - @commands_backup = IRB::Command.commands - IRB::ExtendCommandBundle.class_variable_set(:@@command_override_policies, nil) - end - - def teardown - IRB::ExtendCommandBundle.class_variable_set(:@@command_override_policies, nil) - IRB::Command.instance_variable_set(:@commands, @commands_backup) - end - end - - class CommandArgTest < CustomCommandTestCase - class PrintArgCommand < IRB::Command::Base - category 'CommandTest' - description 'print_command_arg' - def execute(arg) - puts "arg=#{arg.inspect}" - end - end - - def test_arg - IRB::Command._register_with_aliases(:print_arg, PrintArgCommand, [:pa, IRB::ExtendCommandBundle::OVERRIDE_ALL]) - out, err = execute_lines("print_arg\n") - assert_empty err - assert_include(out, 'arg=""') - - out, err = execute_lines("print_arg \n") - assert_empty err - assert_include(out, 'arg=""') - - out, err = execute_lines("print_arg a r g\n") - assert_empty err - assert_include(out, 'arg="a r g"') - - out, err = execute_lines("print_arg a r g \n") - assert_empty err - assert_include(out, 'arg="a r g"') - - out, err = execute_lines("pa a r g \n") - assert_empty err - assert_include(out, 'arg="a r g"') - end - end - - class ExtendCommandBundleCompatibilityTest < CustomCommandTestCase - class FooBarCommand < IRB::Command::Base - category 'FooBarCategory' - description 'foobar_description' - def execute(_arg) - puts "FooBar executed" - end - end - - def test_def_extend_command - IRB::Command._register_with_aliases(:foobar, FooBarCommand, [:fbalias, IRB::ExtendCommandBundle::OVERRIDE_ALL]) - out, err = execute_lines("foobar\n") - assert_empty err - assert_include(out, "FooBar executed") - - out, err = execute_lines("fbalias\n") - assert_empty err - assert_include(out, "FooBar executed") - - out, err = execute_lines("show_cmds\n") - assert_include(out, "FooBarCategory") - assert_include(out, "foobar_description") - end - end - class MeasureTest < CommandTestCase def test_measure conf = { @@ -555,12 +485,11 @@ module TestIRB class CwwsTest < WorkspaceCommandTestCase def test_cwws_returns_the_current_workspace_object out, err = execute_lines( - "cwws", - "self.class" + "cwws" ) assert_empty err - assert_include(out, self.class.name) + assert_include(out, "Current workspace: #{self}") end end @@ -626,7 +555,7 @@ module TestIRB "pushws Foo.new\n", "popws\n", "cwws\n", - "_.class", + "self.class", ) assert_empty err assert_include(out, "=> #{self.class}") @@ -646,20 +575,19 @@ module TestIRB out, err = execute_lines( "chws #{self.class}::Foo.new\n", "cwws\n", - "_.class", + "self.class\n" ) assert_empty err + assert_include(out, "Current workspace: #<#{self.class.name}::Foo") assert_include(out, "=> #{self.class}::Foo") end def test_chws_does_nothing_when_receiving_no_argument out, err = execute_lines( "chws\n", - "cwws\n", - "_.class", ) assert_empty err - assert_include(out, "=> #{self.class}") + assert_include(out, "Current workspace: #{self}") end end diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index aff4b5b67c..cd3f2c8f62 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -662,6 +662,14 @@ module TestIRB assert_equal("irb(!ArgumentError)>", irb.send(:format_prompt, 'irb(%M)>', nil, 1, 1)) end + def test_prompt_format + main = 'main' + irb = IRB::Irb.new(IRB::WorkSpace.new(main), TestInputMethod.new) + assert_equal('%% main %m %main %%m >', irb.send(:format_prompt, '%%%% %m %%m %%%m %%%%m %l', '>', 1, 1)) + assert_equal('42,%i, 42,%3i,042,%03i', irb.send(:format_prompt, '%i,%%i,%3i,%%3i,%03i,%%03i', nil, 42, 1)) + assert_equal('42,%n, 42,%3n,042,%03n', irb.send(:format_prompt, '%n,%%n,%3n,%%3n,%03n,%%03n', nil, 1, 42)) + end + def test_lineno input = TestInputMethod.new([ "\n", diff --git a/test/irb/test_debugger_integration.rb b/test/irb/test_debugger_integration.rb index 839a0d43f0..8b1bddea17 100644 --- a/test/irb/test_debugger_integration.rb +++ b/test/irb/test_debugger_integration.rb @@ -67,6 +67,22 @@ module TestIRB assert_match(/IRB is already running with a debug session/, output) end + def test_debug_command_can_only_be_called_from_binding_irb + write_ruby <<~'ruby' + require "irb" + # trick test framework + puts "binding.irb" + IRB.start + ruby + + output = run_ruby_file do + type "debug" + type "exit" + end + + assert_include(output, "Debugging commands are only available when IRB is started with binding.irb") + end + def test_next write_ruby <<~'ruby' binding.irb @@ -244,28 +260,46 @@ module TestIRB def test_exit write_ruby <<~'RUBY' binding.irb - puts "hello" + puts "he" + "llo" RUBY output = run_ruby_file do - type "next" + type "debug" type "exit" end - assert_match(/irb\(main\):001> next/, output) + assert_match(/irb:rdbg\(main\):002>/, output) + assert_match(/hello/, output) + end + + def test_force_exit + write_ruby <<~'RUBY' + binding.irb + puts "he" + "llo" + RUBY + + output = run_ruby_file do + type "debug" + type "exit!" + end + + assert_match(/irb:rdbg\(main\):002>/, output) + assert_not_match(/hello/, output) end def test_quit write_ruby <<~'RUBY' binding.irb + puts "he" + "llo" RUBY output = run_ruby_file do - type "next" + type "debug" type "quit!" end - assert_match(/irb\(main\):001> next/, output) + assert_match(/irb:rdbg\(main\):002>/, output) + assert_not_match(/hello/, output) end def test_prompt_line_number_continues diff --git a/test/irb/test_helper_method.rb b/test/irb/test_helper_method.rb new file mode 100644 index 0000000000..291278c16a --- /dev/null +++ b/test/irb/test_helper_method.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true +require "irb" + +require_relative "helper" + +module TestIRB + class HelperMethodTestCase < TestCase + def setup + $VERBOSE = nil + @verbosity = $VERBOSE + save_encodings + IRB.instance_variable_get(:@CONF).clear + end + + def teardown + $VERBOSE = @verbosity + restore_encodings + end + + def execute_lines(*lines, conf: {}, main: self, irb_path: nil) + IRB.init_config(nil) + IRB.conf[:VERBOSE] = false + IRB.conf[:PROMPT_MODE] = :SIMPLE + IRB.conf.merge!(conf) + input = TestInputMethod.new(lines) + irb = IRB::Irb.new(IRB::WorkSpace.new(main), input) + irb.context.return_format = "=> %s\n" + irb.context.irb_path = irb_path if irb_path + IRB.conf[:MAIN_CONTEXT] = irb.context + IRB.conf[:USE_PAGER] = false + capture_output do + irb.eval_input + end + end + end + + module TestHelperMethod + class ConfTest < HelperMethodTestCase + def test_conf_returns_the_context_object + out, err = execute_lines("conf.ap_name") + + assert_empty err + assert_include out, "=> \"irb\"" + end + end + end + + class HelperMethodIntegrationTest < IntegrationTestCase + def test_arguments_propogation + write_ruby <<~RUBY + require "irb/helper_method" + + class MyHelper < IRB::HelperMethod::Base + description "This is a test helper" + + def execute( + required_arg, optional_arg = nil, *splat_arg, required_keyword_arg:, + optional_keyword_arg: nil, **double_splat_arg, &block_arg + ) + puts [required_arg, optional_arg, splat_arg, required_keyword_arg, optional_keyword_arg, double_splat_arg, block_arg.call].to_s + end + end + + IRB::HelperMethod.register(:my_helper, MyHelper) + + binding.irb + RUBY + + output = run_ruby_file do + type <<~INPUT + my_helper( + "required", "optional", "splat", required_keyword_arg: "required", + optional_keyword_arg: "optional", a: 1, b: 2 + ) { "block" } + INPUT + type "exit" + end + + assert_include(output, '["required", "optional", ["splat"], "required", "optional", {:a=>1, :b=>2}, "block"]') + end + + def test_helper_method_injection_can_happen_after_irb_require + write_ruby <<~RUBY + require "irb" + + class MyHelper < IRB::HelperMethod::Base + description "This is a test helper" + + def execute + puts "Hello from MyHelper" + end + end + + IRB::HelperMethod.register(:my_helper, MyHelper) + + binding.irb + RUBY + + output = run_ruby_file do + type "my_helper" + type "exit" + end + + assert_include(output, 'Hello from MyHelper') + end + + def test_helper_method_instances_are_memoized + write_ruby <<~RUBY + require "irb/helper_method" + + class MyHelper < IRB::HelperMethod::Base + description "This is a test helper" + + def execute(val) + @val ||= val + end + end + + IRB::HelperMethod.register(:my_helper, MyHelper) + + binding.irb + RUBY + + output = run_ruby_file do + type "my_helper(100)" + type "my_helper(200)" + type "exit" + end + + assert_include(output, '=> 100') + assert_not_include(output, '=> 200') + end + end +end diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb index f11d7398c8..3207c2898b 100644 --- a/test/irb/test_init.rb +++ b/test/irb/test_init.rb @@ -268,15 +268,95 @@ module TestIRB end end + class ConfigValidationTest < TestCase + def setup + @original_home = ENV["HOME"] + @original_irbrc = ENV["IRBRC"] + # To prevent the test from using the user's .irbrc file + ENV["HOME"] = @home = Dir.mktmpdir + IRB.instance_variable_set(:@existing_rc_name_generators, nil) + super + end + + def teardown + super + ENV["IRBRC"] = @original_irbrc + ENV["HOME"] = @original_home + File.unlink(@irbrc) + Dir.rmdir(@home) + end + + def test_irb_name_converts_non_string_values_to_string + assert_no_irb_validation_error(<<~'RUBY') + IRB.conf[:IRB_NAME] = :foo + RUBY + + assert_equal "foo", IRB.conf[:IRB_NAME] + end + + def test_irb_rc_name_only_takes_callable_objects + assert_irb_validation_error(<<~'RUBY', "IRB.conf[:IRB_RC] should be a callable object. Got :foo.") + IRB.conf[:IRB_RC] = :foo + RUBY + end + + def test_back_trace_limit_only_accepts_integers + assert_irb_validation_error(<<~'RUBY', "IRB.conf[:BACK_TRACE_LIMIT] should be an integer. Got \"foo\".") + IRB.conf[:BACK_TRACE_LIMIT] = "foo" + RUBY + end + + def test_prompt_only_accepts_hash + assert_irb_validation_error(<<~'RUBY', "IRB.conf[:PROMPT] should be a Hash. Got \"foo\".") + IRB.conf[:PROMPT] = "foo" + RUBY + end + + def test_eval_history_only_accepts_integers + assert_irb_validation_error(<<~'RUBY', "IRB.conf[:EVAL_HISTORY] should be an integer. Got \"foo\".") + IRB.conf[:EVAL_HISTORY] = "foo" + RUBY + end + + private + + def assert_irb_validation_error(rc_content, error_message) + write_rc rc_content + + assert_raise_with_message(TypeError, error_message) do + IRB.setup(__FILE__) + end + end + + def assert_no_irb_validation_error(rc_content) + write_rc rc_content + + assert_nothing_raised do + IRB.setup(__FILE__) + end + end + + def write_rc(content) + @irbrc = Tempfile.new('irbrc') + @irbrc.write(content) + @irbrc.close + ENV['IRBRC'] = @irbrc.path + end + end + class InitIntegrationTest < IntegrationTestCase - def test_load_error_in_rc_file_is_warned - write_rc <<~'IRBRC' - require "file_that_does_not_exist" - IRBRC + def setup + super write_ruby <<~'RUBY' binding.irb RUBY + end + + def test_load_error_in_rc_file_is_warned + write_rc <<~'IRBRC' + require "file_that_does_not_exist" + IRBRC output = run_ruby_file do type "'foobar'" @@ -293,10 +373,6 @@ module TestIRB raise "I'm an error" IRBRC - write_ruby <<~'RUBY' - binding.irb - RUBY - output = run_ruby_file do type "'foobar'" type "exit" diff --git a/test/irb/test_irb.rb b/test/irb/test_irb.rb index 84b9ee3644..28be744088 100644 --- a/test/irb/test_irb.rb +++ b/test/irb/test_irb.rb @@ -125,6 +125,26 @@ module TestIRB end end + class NestedBindingIrbTest < IntegrationTestCase + def test_current_context_restore + write_ruby <<~'RUBY' + binding.irb + RUBY + + output = run_ruby_file do + type '$ctx = IRB.CurrentContext' + type 'binding.irb' + type 'p context_changed: IRB.CurrentContext != $ctx' + type 'exit' + type 'p context_restored: IRB.CurrentContext == $ctx' + type 'exit' + end + + assert_include output, '{:context_changed=>true}' + assert_include output, '{:context_restored=>true}' + end + end + class IrbIOConfigurationTest < TestCase Row = Struct.new(:content, :current_line_spaces, :new_line_spaces, :indent_level) @@ -803,4 +823,95 @@ module TestIRB IRB::Irb.new(workspace, TestInputMethod.new) end end + + class BacktraceFilteringTest < TestIRB::IntegrationTestCase + def test_backtrace_filtering + write_ruby <<~'RUBY' + def foo + raise "error" + end + + def bar + foo + end + + binding.irb + RUBY + + output = run_ruby_file do + type "bar" + type "exit" + end + + assert_match(/irbtest-.*\.rb:2:in (`|'Object#)foo': error \(RuntimeError\)/, output) + frame_traces = output.split("\n").select { |line| line.strip.match?(/from /) }.map(&:strip) + + expected_traces = if RUBY_VERSION >= "3.3.0" + [ + /from .*\/irbtest-.*.rb:6:in (`|'Object#)bar'/, + /from .*\/irbtest-.*.rb\(irb\):1:in [`']<main>'/, + /from <internal:kernel>:\d+:in (`|'Kernel#)loop'/, + /from <internal:prelude>:\d+:in (`|'Binding#)irb'/, + /from .*\/irbtest-.*.rb:9:in [`']<main>'/ + ] + else + [ + /from .*\/irbtest-.*.rb:6:in (`|'Object#)bar'/, + /from .*\/irbtest-.*.rb\(irb\):1:in [`']<main>'/, + /from <internal:prelude>:\d+:in (`|'Binding#)irb'/, + /from .*\/irbtest-.*.rb:9:in [`']<main>'/ + ] + end + + expected_traces.reverse! if RUBY_VERSION < "3.0.0" + + expected_traces.each_with_index do |expected_trace, index| + assert_match(expected_trace, frame_traces[index]) + end + end + + def test_backtrace_filtering_with_backtrace_filter + write_rc <<~'RUBY' + class TestBacktraceFilter + def self.call(backtrace) + backtrace.reject { |line| line.include?("internal") } + end + end + + IRB.conf[:BACKTRACE_FILTER] = TestBacktraceFilter + RUBY + + write_ruby <<~'RUBY' + def foo + raise "error" + end + + def bar + foo + end + + binding.irb + RUBY + + output = run_ruby_file do + type "bar" + type "exit" + end + + assert_match(/irbtest-.*\.rb:2:in (`|'Object#)foo': error \(RuntimeError\)/, output) + frame_traces = output.split("\n").select { |line| line.strip.match?(/from /) }.map(&:strip) + + expected_traces = [ + /from .*\/irbtest-.*.rb:6:in (`|'Object#)bar'/, + /from .*\/irbtest-.*.rb\(irb\):1:in [`']<main>'/, + /from .*\/irbtest-.*.rb:9:in [`']<main>'/ + ] + + expected_traces.reverse! if RUBY_VERSION < "3.0.0" + + expected_traces.each_with_index do |expected_trace, index| + assert_match(expected_trace, frame_traces[index]) + end + end + end end diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb index 6b3171d265..cf297f3755 100644 --- a/test/net/http/test_https.rb +++ b/test/net/http/test_https.rb @@ -168,6 +168,7 @@ class TestNetHTTPS < Test::Unit::TestCase # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h. omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 1.1.0h') omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 3.2.') + omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 3.3.') http = Net::HTTP.new(HOST, config("port")) http.use_ssl = true diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index b798b897b4..17f73e4433 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -416,7 +416,7 @@ class TestObjSpace < Test::Unit::TestCase assert_equal('true', ObjectSpace.dump(true)) assert_equal('false', ObjectSpace.dump(false)) assert_equal('0', ObjectSpace.dump(0)) - assert_equal('{"type":"SYMBOL", "value":"foo"}', ObjectSpace.dump(:foo)) + assert_equal('{"type":"SYMBOL", "value":"test_dump_special_consts"}', ObjectSpace.dump(:test_dump_special_consts)) end def test_dump_singleton_class diff --git a/test/objspace/test_ractor.rb b/test/objspace/test_ractor.rb index b7008ea731..4901eeae2e 100644 --- a/test/objspace/test_ractor.rb +++ b/test/objspace/test_ractor.rb @@ -1,17 +1,17 @@ require "test/unit" class TestObjSpaceRactor < Test::Unit::TestCase - def test_tracing_does_not_crash - assert_ractor(<<~RUBY, require: 'objspace') - ObjectSpace.trace_object_allocations do - r = Ractor.new do - obj = 'a' * 1024 - Ractor.yield obj - end + def test_tracing_does_not_crash + assert_ractor(<<~RUBY, require: 'objspace') + ObjectSpace.trace_object_allocations do + r = Ractor.new do + obj = 'a' * 1024 + Ractor.yield obj + end - r.take - r.take - end - RUBY - end + r.take + r.take + end + RUBY + end end diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index 8faa570648..41885fd59b 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -331,6 +331,22 @@ class OpenSSL::TestCipher < OpenSSL::TestCase assert_equal tag1, tag2 end + def test_aes_keywrap_pad + # RFC 5649 Section 6; The second example + kek = ["5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8"].pack("H*") + key = ["466f7250617369"].pack("H*") + wrap = ["afbeb0f07dfbf5419200f2ccb50bb24f"].pack("H*") + + begin + cipher = OpenSSL::Cipher.new("id-aes192-wrap-pad").encrypt + rescue OpenSSL::Cipher::CipherError, RuntimeError + omit "id-aes192-wrap-pad is not supported: #$!" + end + cipher.key = kek + ct = cipher.update(key) << cipher.final + assert_equal wrap, ct + end + def test_non_aead_cipher_set_auth_data assert_raise(OpenSSL::Cipher::CipherError) { cipher = OpenSSL::Cipher.new("aes-128-cfb").encrypt diff --git a/test/openssl/test_digest.rb b/test/openssl/test_digest.rb index b0b5b9bed1..988330e405 100644 --- a/test/openssl/test_digest.rb +++ b/test/openssl/test_digest.rb @@ -88,7 +88,7 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end def test_sha512_truncate - pend "SHA512_224 is not implemented" unless digest_available?('SHA512-224') + pend "SHA512_224 is not implemented" unless digest_available?('sha512-224') sha512_224_a = "d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327" sha512_256_a = "455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8" @@ -100,7 +100,7 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end def test_sha3 - pend "SHA3 is not implemented" unless digest_available?('SHA3-224') + pend "SHA3 is not implemented" unless digest_available?('sha3-224') s224 = '6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7' s256 = 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a' s384 = '0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004' @@ -126,6 +126,15 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end end + def test_digests + digests = OpenSSL::Digest.digests + assert_kind_of Array, digests + assert_include digests, "md5" + assert_include digests, "sha1" + assert_include digests, "sha256" + assert_include digests, "sha512" + end + private def check_digest(oid) @@ -138,11 +147,8 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end def digest_available?(name) - begin - OpenSSL::Digest.new(name) - rescue RuntimeError - false - end + @digests ||= OpenSSL::Digest.digests + @digests.include?(name) end end diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb index b616883925..66e36a7ab4 100644 --- a/test/openssl/test_pair.rb +++ b/test/openssl/test_pair.rb @@ -250,12 +250,17 @@ module OpenSSL::TestPairM buf = +"garbage" assert_equal :wait_readable, s2.read_nonblock(100, buf, exception: false) - assert_equal "", buf + assert_equal "garbage", buf s1.close buf = +"garbage" - assert_equal nil, s2.read(100, buf) + assert_nil s2.read(100, buf) assert_equal "", buf + + buf = +"garbage" + ret = s2.read(0, buf) + assert_same buf, ret + assert_equal "", ret } end diff --git a/test/openssl/test_pkcs7.rb b/test/openssl/test_pkcs7.rb index ba8b93d034..c049ed444a 100644 --- a/test/openssl/test_pkcs7.rb +++ b/test/openssl/test_pkcs7.rb @@ -155,6 +155,21 @@ class OpenSSL::TestPKCS7 < OpenSSL::TestCase assert_equal(data, p7.decrypt(@rsa1024)) end + def test_empty_signed_data_ruby_bug_19974 + data = "-----BEGIN PKCS7-----\nMAsGCSqGSIb3DQEHAg==\n-----END PKCS7-----\n" + assert_raise(ArgumentError) { OpenSSL::PKCS7.new(data) } + + data = <<END +MIME-Version: 1.0 +Content-Disposition: attachment; filename="smime.p7m" +Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m" +Content-Transfer-Encoding: base64 + +#{data} +END + assert_raise(OpenSSL::PKCS7::PKCS7Error) { OpenSSL::PKCS7.read_smime(data) } + end + def test_graceful_parsing_failure #[ruby-core:43250] contents = File.read(__FILE__) assert_raise(ArgumentError) { OpenSSL::PKCS7.new(contents) } @@ -212,6 +227,12 @@ END assert_equal(p7.to_der, OpenSSL::PKCS7.read_smime(smime).to_der) end + def test_to_text + p7 = OpenSSL::PKCS7.new + p7.type = "signed" + assert_match(/signed/, p7.to_text) + end + def test_degenerate_pkcs7 ca_cert_pem = <<END -----BEGIN CERTIFICATE----- diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 66d63a981d..1471b0cb36 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -117,6 +117,30 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase } end + def test_socket_close_write + server_proc = proc do |ctx, ssl| + message = ssl.read + ssl.write(message) + ssl.close_write + ensure + ssl.close + end + + start_server(server_proc: server_proc) do |port| + ctx = OpenSSL::SSL::SSLContext.new + ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port, context: ctx) + ssl.sync_close = true + ssl.connect + + message = "abc"*1024 + ssl.write message + ssl.close_write + assert_equal message, ssl.read + ensure + ssl&.close + end + end + def test_add_certificate ctx_proc = -> ctx { # Unset values set by start_server diff --git a/test/openssl/test_ts.rb b/test/openssl/test_ts.rb index 7cb1a1fe8e..ac0469ad56 100644 --- a/test/openssl/test_ts.rb +++ b/test/openssl/test_ts.rb @@ -323,6 +323,8 @@ _end_of_pem_ resp = fac.create_timestamp(ee_key, ts_cert_ee, req) assert_equal(OpenSSL::Timestamp::Response::GRANTED, resp.status) assert_equal("1.2.3.4.6", resp.token_info.policy_id) + + assert_match(/1\.2\.3\.4\.6/, resp.to_text) end def test_response_bad_purpose diff --git a/test/openssl/test_x509req.rb b/test/openssl/test_x509req.rb index ff17c41163..b98754b8c8 100644 --- a/test/openssl/test_x509req.rb +++ b/test/openssl/test_x509req.rb @@ -39,11 +39,6 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase assert_equal(0, req.version) req = OpenSSL::X509::Request.new(req.to_der) assert_equal(0, req.version) - - req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest.new('SHA256')) - assert_equal(1, req.version) - req = OpenSSL::X509::Request.new(req.to_der) - assert_equal(1, req.version) end def test_subject @@ -106,7 +101,7 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase assert_equal(false, req.verify(@rsa2048)) assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) - req.version = 1 + req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBarFooBar") assert_equal(false, req.verify(@rsa1024)) rescue OpenSSL::X509::RequestError # RHEL 9 disables SHA1 end diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 679f6ed0a2..3670b90dd7 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -99,7 +99,7 @@ module Prism ) assert_errors expected, "BEGIN { 1 + }", [ - ["expected an expression after the operator", 10..11], + ["unexpected '}'; expected an expression after the operator", 12..13], ["unexpected '}', assuming it is closing the parent 'BEGIN' block", 12..13] ] end @@ -117,25 +117,25 @@ module Prism def test_unterminated_i_list assert_errors expression("%i["), "%i[", [ - ["expected a closing delimiter for the `%i` list", 0..3] + ["unterminated list; expected a closing delimiter for the `%i`", 0..3] ] end def test_unterminated_w_list assert_errors expression("%w["), "%w[", [ - ["expected a closing delimiter for the `%w` list", 0..3] + ["unterminated list; expected a closing delimiter for the `%w`", 0..3] ] end def test_unterminated_W_list assert_errors expression("%W["), "%W[", [ - ["expected a closing delimiter for the `%W` list", 0..3] + ["unterminated list; expected a closing delimiter for the `%W`", 0..3] ] end def test_unterminated_regular_expression assert_errors expression("/hello"), "/hello", [ - ["expected a closing delimiter for the regular expression", 0..1] + ["unterminated regexp meets end of file; expected a closing delimiter", 0..1] ] end @@ -143,7 +143,7 @@ module Prism source = "<<-END + /b\nEND\n" assert_errors expression(source), source, [ - ["expected a closing delimiter for the regular expression", 9..10] + ["unterminated regexp meets end of file; expected a closing delimiter", 9..10] ] end @@ -189,14 +189,13 @@ module Prism def test_unterminated_s_symbol assert_errors expression("%s[abc"), "%s[abc", [ - ["expected a closing delimiter for the dynamic symbol", 0..3] + ["unterminated quoted string; expected a closing delimiter for the dynamic symbol", 0..3] ] end def test_unterminated_parenthesized_expression assert_errors expression('(1 + 2'), '(1 + 2', [ - ["unexpected end of file, expecting end-of-input", 6..6], - ["unexpected end of file, assuming it is closing the parent top level context", 6..6], + ["unexpected end-of-input, assuming it is closing the parent top level context", 6..6], ["expected a matching `)`", 6..6] ] end @@ -209,21 +208,21 @@ module Prism def test_unterminated_argument_expression assert_errors expression('a %'), 'a %', [ - ["invalid `%` token", 2..3], - ["expected an expression after the operator", 2..3], - ["unexpected end of file, assuming it is closing the parent top level context", 3..3] + ["unterminated quoted string meets end of file", 2..3], + ["unexpected end-of-input; expected an expression after the operator", 3..3], + ["unexpected end-of-input, assuming it is closing the parent top level context", 3..3] ] end def test_unterminated_interpolated_symbol assert_error_messages ":\"#", [ - "expected a closing delimiter for the interpolated symbol" + "unterminated symbol; expected a closing delimiter for the interpolated symbol" ] end def test_cr_without_lf_in_percent_expression assert_errors expression("%\r"), "%\r", [ - ["invalid `%` token", 0..2], + ["unterminated string meets end of file", 2..2], ] end @@ -365,7 +364,7 @@ module Prism assert_error_messages "x.each { x end", [ "unexpected 'end', expecting end-of-input", "unexpected 'end', ignoring it", - "unexpected end of file, assuming it is closing the parent top level context", + "unexpected end-of-input, assuming it is closing the parent top level context", "expected a block beginning with `{` to end with `}`" ] end @@ -378,10 +377,13 @@ module Prism :a, Location(), Location(), - ArgumentsNode(1, [ - KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]), - SplatNode(Location(), expression("args")) - ]), + ArgumentsNode( + ArgumentsNodeFlags::CONTAINS_KEYWORDS | ArgumentsNodeFlags::CONTAINS_KEYWORD_SPLAT, + [ + KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]), + SplatNode(Location(), expression("args")) + ] + ), Location(), nil ) @@ -425,7 +427,7 @@ module Prism :a, Location(), Location(), - ArgumentsNode(0, [ + ArgumentsNode(ArgumentsNodeFlags::CONTAINS_KEYWORDS, [ KeywordHashNode(1, [ AssocNode( SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, nil, Location(), Location(), "foo"), @@ -690,13 +692,13 @@ module Prism expected = StringNode(StringFlags::FORCED_UTF8_ENCODING, Location(), Location(), nil, "\u0001\u0002") assert_errors expected, '?\u{0001 0002}', [ - ["invalid Unicode escape sequence; multiple codepoints are not allowed in a character literal", 9..12] + ["invalid Unicode escape sequence; Multiple codepoints at single character literal are disallowed", 9..12] ] end def test_invalid_hex_escape assert_errors expression('"\\xx"'), '"\\xx"', [ - ["invalid hexadecimal escape sequence", 1..3], + ["invalid hex escape sequence", 1..3], ] end @@ -713,12 +715,13 @@ module Prism assert_errors expected, '"\u{000z}"', [ ["invalid Unicode escape sequence", 7..7], + ["unterminated Unicode escape", 7..7] ] end def test_unterminated_unicode_brackets_should_be_a_syntax_error assert_errors expression('?\\u{3'), '?\\u{3', [ - ["invalid Unicode escape sequence; needs closing `}`", 1..5], + ["unterminated Unicode escape", 1..5], ] end @@ -861,7 +864,7 @@ module Prism :foo, Location(), nil, - ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), + ParametersNode([], [], nil, [ForwardingParameterNode()], [], ForwardingParameterNode(), nil), nil, [], Location(), @@ -1094,7 +1097,7 @@ module Prism ConstantReadNode(:A), nil, nil, - StatementsNode([ReturnNode(Location(), nil)]), + StatementsNode([ReturnNode(0, Location(), nil)]), Location(), :A ) @@ -1109,7 +1112,7 @@ module Prism [], Location(), ConstantReadNode(:A), - StatementsNode([ReturnNode(Location(), nil)]), + StatementsNode([ReturnNode(0, Location(), nil)]), Location(), :A ) @@ -1238,7 +1241,7 @@ module Prism expected = CallNode(0, receiver, Location(), :foo, Location(), nil, nil, nil, nil) assert_errors expected, "<<~FOO.foo\n", [ - ["could not find a terminator for the heredoc", 11..11] + ["unterminated heredoc; can't find string \"FOO\" anywhere before EOF", 3..6] ] end @@ -1381,7 +1384,6 @@ module Prism def test_invalid_number_underscores error_messages = ["invalid underscore placement in number"] - assert_error_messages "1__1", error_messages assert_error_messages "0b1__1", error_messages assert_error_messages "0o1__1", error_messages @@ -1389,6 +1391,7 @@ module Prism assert_error_messages "0d1__1", error_messages assert_error_messages "0x1__1", error_messages + error_messages = ["trailing '_' in number"] assert_error_messages "1_1_", error_messages assert_error_messages "0b1_1_", error_messages assert_error_messages "0o1_1_", error_messages @@ -1398,7 +1401,7 @@ module Prism end def test_alnum_delimiters - error_messages = ["invalid `%` token"] + error_messages = ["unknown type of %string"] assert_error_messages "%qXfooX", error_messages assert_error_messages "%QXfooX", error_messages @@ -1463,7 +1466,7 @@ module Prism def test_forwarding_arg_after_keyword_rest source = "def f(**,...);end" assert_errors expression(source), source, [ - ["unexpected `...` in parameters", 9..12], + ["unexpected parameter order", 9..12] ] end @@ -1479,8 +1482,7 @@ module Prism assert_errors expression(source), source, [ ["expected a `do` keyword or a `{` to open the lambda block", 3..3], - ["unexpected end of file, expecting end-of-input", 7..7], - ["unexpected end of file, assuming it is closing the parent top level context", 7..7], + ["unexpected end-of-input, assuming it is closing the parent top level context", 7..7], ["expected a lambda block beginning with `do` to end with `end`", 7..7] ] end @@ -1538,7 +1540,7 @@ module Prism assert_errors expression(source), source, [ ["expected a predicate expression for the `while` statement", 22..22], - ["unexpected end of file, assuming it is closing the parent top level context", 22..22], + ["unexpected end-of-input, assuming it is closing the parent top level context", 22..22], ["expected an `end` to close the `while` statement", 22..22] ] end @@ -1939,10 +1941,10 @@ module Prism RUBY assert_errors expression(source), source, [ - ["unexpected '..', expecting end-of-input", 3..5], - ["unexpected '..', ignoring it", 3..5], - ["unexpected '..', expecting end-of-input", 10..12], - ["unexpected '..', ignoring it", 10..12] + ["unexpected .., expecting end-of-input", 3..5], + ["unexpected .., ignoring it", 3..5], + ["unexpected .., expecting end-of-input", 10..12], + ["unexpected .., ignoring it", 10..12] ] end @@ -2079,7 +2081,7 @@ module Prism def test_forwarding_arg_and_block source = 'def foo(...) = foo(...) { }' assert_errors expression(source), source, [ - ['both a block argument and a forwarding argument; only one block is allowed', 24..27] + ['both block arg and actual block given; only one block is allowed', 24..27] ] end diff --git a/test/prism/format_errors_test.rb b/test/prism/format_errors_test.rb index a1edbef2e8..63206d5765 100644 --- a/test/prism/format_errors_test.rb +++ b/test/prism/format_errors_test.rb @@ -6,19 +6,53 @@ return if Prism::BACKEND == :FFI module Prism class FormatErrorsTest < TestCase - def test_format_errors - assert_equal <<~ERROR, Debug.format_errors("<>", false) + def test_basic + expected = <<~ERROR > 1 | <> | ^ unexpected '<', ignoring it | ^ unexpected '>', ignoring it ERROR - assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false) - > 1 | "%W"\u" + assert_equal expected, Debug.format_errors("<>", false) + end + + def test_multiple + expected = <<~ERROR + > 1 | "%W"\\u" | ^ unexpected backslash, ignoring it | ^ unexpected local variable or method, expecting end-of-input | ^ unterminated string meets end of file ERROR + + assert_equal expected, Debug.format_errors('"%W"\u"', false) + end + + def test_truncate_start + expected = <<~ERROR + > 1 | ... <> + | ^ unexpected '<', ignoring it + | ^ unexpected '>', ignoring it + ERROR + + assert_equal expected, Debug.format_errors("#{" " * 30}<>", false) + end + + def test_truncate_end + expected = <<~ERROR + > 1 | <#{" " * 30} ... + | ^ unexpected '<', ignoring it + ERROR + + assert_equal expected, Debug.format_errors("<#{" " * 30}a", false) + end + + def test_truncate_both + expected = <<~ERROR + > 1 | ... <#{" " * 30} ... + | ^ unexpected '<', ignoring it + ERROR + + assert_equal expected, Debug.format_errors("#{" " * 30}<#{" " * 30}a", false) end end end diff --git a/test/prism/index_write_test.rb b/test/prism/index_write_test.rb index 1c6f7bce89..cf90eb082f 100644 --- a/test/prism/index_write_test.rb +++ b/test/prism/index_write_test.rb @@ -4,7 +4,7 @@ require_relative "test_helper" module Prism class IndexWriteTest < TestCase - def test_keywords_3_3_0 + def test_keywords_3_3 assert_parse_success(<<~RUBY, "3.3.0") foo[bar: 1] = 1 foo[bar: 1] &&= 1 @@ -22,7 +22,7 @@ module Prism RUBY end - def test_block_3_3_0 + def test_block_3_3 assert_parse_success(<<~RUBY, "3.3.0") foo[&bar] = 1 foo[&bar] &&= 1 @@ -40,41 +40,41 @@ module Prism RUBY end - # def test_keywords_latest - # assert_parse_failure(<<~RUBY) - # foo[bar: 1] = 1 - # foo[bar: 1] &&= 1 - # foo[bar: 1] ||= 1 - # foo[bar: 1] += 1 - # RUBY + def test_keywords_latest + assert_parse_failure(<<~RUBY) + foo[bar: 1] = 1 + foo[bar: 1] &&= 1 + foo[bar: 1] ||= 1 + foo[bar: 1] += 1 + RUBY - # assert_parse_failure(<<~RUBY) - # def foo(**) - # bar[**] = 1 - # bar[**] &&= 1 - # bar[**] ||= 1 - # bar[**] += 1 - # end - # RUBY - # end + assert_parse_failure(<<~RUBY) + def foo(**) + bar[**] = 1 + bar[**] &&= 1 + bar[**] ||= 1 + bar[**] += 1 + end + RUBY + end - # def test_block_latest - # assert_parse_failure(<<~RUBY) - # foo[&bar] = 1 - # foo[&bar] &&= 1 - # foo[&bar] ||= 1 - # foo[&bar] += 1 - # RUBY + def test_block_latest + assert_parse_failure(<<~RUBY) + foo[&bar] = 1 + foo[&bar] &&= 1 + foo[&bar] ||= 1 + foo[&bar] += 1 + RUBY - # assert_parse_failure(<<~RUBY) - # def foo(&) - # bar[&] = 1 - # bar[&] &&= 1 - # bar[&] ||= 1 - # bar[&] += 1 - # end - # RUBY - # end + assert_parse_failure(<<~RUBY) + def foo(&) + bar[&] = 1 + bar[&] &&= 1 + bar[&] ||= 1 + bar[&] += 1 + end + RUBY + end private diff --git a/test/prism/location_test.rb b/test/prism/location_test.rb index 81417fbcb3..0724995671 100644 --- a/test/prism/location_test.rb +++ b/test/prism/location_test.rb @@ -298,7 +298,6 @@ module Prism def test_ConstantReadNode assert_location(ConstantReadNode, "Foo") - assert_location(ConstantReadNode, "Foo::Bar", 5...8, &:child) end def test_ConstantTargetNode @@ -478,7 +477,7 @@ module Prism end def test_IndexTargetNode - assert_location(IndexTargetNode, "foo[bar, &baz], = qux", 0...14) do |node| + assert_location(IndexTargetNode, "foo[bar], = qux", 0...8) do |node| node.lefts.first end end diff --git a/test/prism/newline_test.rb b/test/prism/newline_test.rb index e9975b346e..f7511f665c 100644 --- a/test/prism/newline_test.rb +++ b/test/prism/newline_test.rb @@ -7,7 +7,15 @@ return unless defined?(RubyVM::InstructionSequence) module Prism class NewlineTest < TestCase base = File.expand_path("../", __FILE__) - filepaths = Dir["*.rb", base: base] - %w[encoding_test.rb errors_test.rb parser_test.rb static_literals_test.rb unescape_test.rb] + filepaths = Dir["*.rb", base: base] - %w[ + encoding_test.rb + errors_test.rb + format_errors_test.rb + parser_test.rb + regexp_test.rb + static_literals_test.rb + unescape_test.rb + ] filepaths.each do |relative| define_method("test_newline_flags_#{relative}") do diff --git a/test/prism/redundant_return_test.rb b/test/prism/redundant_return_test.rb new file mode 100644 index 0000000000..c668169245 --- /dev/null +++ b/test/prism/redundant_return_test.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require_relative "test_helper" + +module Prism + class RedundantReturnTest < TestCase + def test_statements + assert_redundant_return("def foo; return; end") + refute_redundant_return("def foo; return; 1; end") + end + + def test_begin_implicit + assert_redundant_return("def foo; return; rescue; end") + refute_redundant_return("def foo; return; 1; rescue; end") + refute_redundant_return("def foo; return; rescue; else; end") + end + + def test_begin_explicit + assert_redundant_return("def foo; begin; return; rescue; end; end") + refute_redundant_return("def foo; begin; return; 1; rescue; end; end") + refute_redundant_return("def foo; begin; return; rescue; else; end; end") + end + + def test_if + assert_redundant_return("def foo; return if bar; end") + end + + def test_unless + assert_redundant_return("def foo; return unless bar; end") + end + + def test_else + assert_redundant_return("def foo; if bar; baz; else; return; end; end") + end + + def test_case_when + assert_redundant_return("def foo; case bar; when baz; return; end; end") + end + + def test_case_else + assert_redundant_return("def foo; case bar; when baz; else; return; end; end") + end + + def test_case_match_in + assert_redundant_return("def foo; case bar; in baz; return; end; end") + end + + def test_case_match_else + assert_redundant_return("def foo; case bar; in baz; else; return; end; end") + end + + private + + def assert_redundant_return(source) + assert find_return(source).redundant? + end + + def refute_redundant_return(source) + refute find_return(source).redundant? + end + + def find_return(source) + queue = [Prism.parse(source).value] + + while (current = queue.shift) + return current if current.is_a?(ReturnNode) + queue.concat(current.compact_child_nodes) + end + + flunk "Could not find return node in #{node.inspect}" + end + end +end diff --git a/test/prism/ruby_api_test.rb b/test/prism/ruby_api_test.rb index 6418887147..a1e2592d3d 100644 --- a/test/prism/ruby_api_test.rb +++ b/test/prism/ruby_api_test.rb @@ -209,6 +209,13 @@ module Prism assert_equal "", location.slice end + def test_location_slice_lines + result = Prism.parse("\nprivate def foo\nend\n") + method = result.value.statements.body.first.arguments.arguments.first + + assert_equal "private def foo\nend\n", method.slice_lines + end + def test_heredoc? refute parse_expression("\"foo\"").heredoc? refute parse_expression("\"foo \#{1}\"").heredoc? @@ -244,6 +251,53 @@ module Prism assert_equal 16, base[parse_expression("0x1")] end + def test_node_equality + assert_operator parse_expression("1"), :===, parse_expression("1") + assert_operator Prism.parse("1").value, :===, Prism.parse("1").value + + complex_source = "class Something; @var = something.else { _1 }; end" + assert_operator parse_expression(complex_source), :===, parse_expression(complex_source) + + refute_operator parse_expression("1"), :===, parse_expression("2") + refute_operator parse_expression("1"), :===, parse_expression("0x1") + + complex_source_1 = "class Something; @var = something.else { _1 }; end" + complex_source_2 = "class Something; @var = something.else { _2 }; end" + refute_operator parse_expression(complex_source_1), :===, parse_expression(complex_source_2) + end + + def test_node_tunnel + program = Prism.parse("foo(1) +\n bar(2, 3) +\n baz(3, 4, 5)").value + + tunnel = program.tunnel(1, 4).last + assert_kind_of IntegerNode, tunnel + assert_equal 1, tunnel.value + + tunnel = program.tunnel(2, 6).last + assert_kind_of IntegerNode, tunnel + assert_equal 2, tunnel.value + + tunnel = program.tunnel(3, 9).last + assert_kind_of IntegerNode, tunnel + assert_equal 4, tunnel.value + + tunnel = program.tunnel(3, 8) + assert_equal [ProgramNode, StatementsNode, CallNode, ArgumentsNode, CallNode, ArgumentsNode], tunnel.map(&:class) + end + + def test_location_adjoin + program = Prism.parse("foo.bar = 1").value + + location = program.statements.body.first.message_loc + adjoined = location.adjoin("=") + + assert_kind_of Location, adjoined + refute_equal location, adjoined + + assert_equal 4, adjoined.start_offset + assert_equal 9, adjoined.end_offset + end + private def parse_expression(source) diff --git a/test/prism/snapshots/arrays.txt b/test/prism/snapshots/arrays.txt index e8e53aacb9..90a4d8f3bb 100644 --- a/test/prism/snapshots/arrays.txt +++ b/test/prism/snapshots/arrays.txt @@ -960,8 +960,8 @@ │ ├── arguments: ∅ │ ├── closing_loc: (84,4)-(84,5) = "]" │ ├── block: ∅ - │ ├── operator: :+ - │ ├── operator_loc: (84,6)-(84,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (84,6)-(84,8) = "+=" │ └── value: │ @ IntegerNode (location: (84,9)-(84,10)) │ ├── flags: decimal @@ -1040,8 +1040,8 @@ │ ├── arguments: ∅ │ ├── closing_loc: (90,8)-(90,9) = "]" │ ├── block: ∅ - │ ├── operator: :+ - │ ├── operator_loc: (90,10)-(90,12) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (90,10)-(90,12) = "+=" │ └── value: │ @ IntegerNode (location: (90,13)-(90,14)) │ ├── flags: decimal @@ -1143,8 +1143,8 @@ │ │ └── block: ∅ │ ├── closing_loc: (96,7)-(96,8) = "]" │ ├── block: ∅ - │ ├── operator: :+ - │ ├── operator_loc: (96,9)-(96,11) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (96,9)-(96,11) = "+=" │ └── value: │ @ IntegerNode (location: (96,12)-(96,13)) │ ├── flags: decimal @@ -1262,8 +1262,8 @@ │ │ └── block: ∅ │ ├── closing_loc: (102,11)-(102,12) = "]" │ ├── block: ∅ - │ ├── operator: :+ - │ ├── operator_loc: (102,13)-(102,15) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (102,13)-(102,15) = "+=" │ └── value: │ @ IntegerNode (location: (102,16)-(102,17)) │ ├── flags: decimal @@ -1633,8 +1633,8 @@ │ │ │ └── expression: ∅ │ │ ├── closing_loc: (116,13)-(116,14) = "]" │ │ ├── block: ∅ - │ │ ├── operator: :+ - │ │ ├── operator_loc: (116,15)-(116,17) = "+=" + │ │ ├── binary_operator: :+ + │ │ ├── binary_operator_loc: (116,15)-(116,17) = "+=" │ │ └── value: │ │ @ IntegerNode (location: (116,18)-(116,19)) │ │ ├── flags: decimal diff --git a/test/prism/snapshots/blocks.txt b/test/prism/snapshots/blocks.txt index 0b1ec52e38..1c996ebd09 100644 --- a/test/prism/snapshots/blocks.txt +++ b/test/prism/snapshots/blocks.txt @@ -158,13 +158,13 @@ │ │ └── body: (length: 1) │ │ └── @ LocalVariableOperatorWriteNode (location: (7,24)-(7,33)) │ │ ├── name_loc: (7,24)-(7,28) = "memo" - │ │ ├── operator_loc: (7,29)-(7,31) = "+=" + │ │ ├── binary_operator_loc: (7,29)-(7,31) = "+=" │ │ ├── value: │ │ │ @ LocalVariableReadNode (location: (7,32)-(7,33)) │ │ │ ├── name: :x │ │ │ └── depth: 0 │ │ ├── name: :memo - │ │ ├── operator: :+ + │ │ ├── binary_operator: :+ │ │ └── depth: 0 │ ├── opening_loc: (7,12)-(7,13) = "{" │ └── closing_loc: (7,34)-(7,35) = "}" diff --git a/test/prism/snapshots/boolean_operators.txt b/test/prism/snapshots/boolean_operators.txt index ace8047e18..3bf33430c9 100644 --- a/test/prism/snapshots/boolean_operators.txt +++ b/test/prism/snapshots/boolean_operators.txt @@ -21,7 +21,7 @@ │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,6)) │ ├── name_loc: (3,0)-(3,1) = "a" - │ ├── operator_loc: (3,2)-(3,4) = "+=" + │ ├── binary_operator_loc: (3,2)-(3,4) = "+=" │ ├── value: │ │ @ CallNode (location: (3,5)-(3,6)) │ │ ├── flags: variable_call, ignore_visibility @@ -34,7 +34,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── name: :a - │ ├── operator: :+ + │ ├── binary_operator: :+ │ └── depth: 0 └── @ LocalVariableOrWriteNode (location: (5,0)-(5,7)) ├── name_loc: (5,0)-(5,1) = "a" diff --git a/test/prism/snapshots/constants.txt b/test/prism/snapshots/constants.txt index 59e234148a..1251833663 100644 --- a/test/prism/snapshots/constants.txt +++ b/test/prism/snapshots/constants.txt @@ -7,24 +7,21 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (1,0)-(1,1)) │ │ └── name: :A - │ ├── child: - │ │ @ ConstantReadNode (location: (1,3)-(1,4)) - │ │ └── name: :B - │ └── delimiter_loc: (1,1)-(1,3) = "::" + │ ├── name: :B + │ ├── delimiter_loc: (1,1)-(1,3) = "::" + │ └── name_loc: (1,3)-(1,4) = "B" ├── @ ConstantPathNode (location: (3,0)-(3,7)) │ ├── parent: │ │ @ ConstantPathNode (location: (3,0)-(3,4)) │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (3,0)-(3,1)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (3,3)-(3,4)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (3,1)-(3,3) = "::" - │ ├── child: - │ │ @ ConstantReadNode (location: (3,6)-(3,7)) - │ │ └── name: :C - │ └── delimiter_loc: (3,4)-(3,6) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (3,1)-(3,3) = "::" + │ │ └── name_loc: (3,3)-(3,4) = "B" + │ ├── name: :C + │ ├── delimiter_loc: (3,4)-(3,6) = "::" + │ └── name_loc: (3,6)-(3,7) = "C" ├── @ ConstantPathNode (location: (5,0)-(5,4)) │ ├── parent: │ │ @ CallNode (location: (5,0)-(5,1)) @@ -37,20 +34,18 @@ │ │ ├── arguments: ∅ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (5,3)-(5,4)) - │ │ └── name: :B - │ └── delimiter_loc: (5,1)-(5,3) = "::" + │ ├── name: :B + │ ├── delimiter_loc: (5,1)-(5,3) = "::" + │ └── name_loc: (5,3)-(5,4) = "B" ├── @ ConstantPathWriteNode (location: (7,0)-(7,8)) │ ├── target: │ │ @ ConstantPathNode (location: (7,0)-(7,4)) │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (7,0)-(7,1)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (7,3)-(7,4)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (7,1)-(7,3) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (7,1)-(7,3) = "::" + │ │ └── name_loc: (7,3)-(7,4) = "B" │ ├── operator_loc: (7,5)-(7,6) = "=" │ └── value: │ @ IntegerNode (location: (7,7)-(7,8)) @@ -117,7 +112,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (17,4)-(17,9)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (17,4)-(17,9)) │ │ ├── flags: ∅ @@ -199,7 +194,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (23,9)-(23,14)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (23,9)-(23,14)) │ │ ├── flags: ∅ @@ -249,10 +244,9 @@ │ ├── receiver: │ │ @ ConstantPathNode (location: (27,0)-(27,3)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (27,2)-(27,3)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (27,0)-(27,2) = "::" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (27,0)-(27,2) = "::" + │ │ └── name_loc: (27,2)-(27,3) = "A" │ ├── call_operator_loc: (27,3)-(27,5) = "::" │ ├── name: :foo │ ├── message_loc: (27,5)-(27,8) = "foo" @@ -264,10 +258,9 @@ │ ├── target: │ │ @ ConstantPathNode (location: (29,0)-(29,3)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (29,2)-(29,3)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (29,0)-(29,2) = "::" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (29,0)-(29,2) = "::" + │ │ └── name_loc: (29,2)-(29,3) = "A" │ ├── operator_loc: (29,4)-(29,5) = "=" │ └── value: │ @ IntegerNode (location: (29,6)-(29,7)) @@ -279,14 +272,12 @@ │ │ ├── parent: │ │ │ @ ConstantPathNode (location: (31,0)-(31,3)) │ │ │ ├── parent: ∅ - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (31,2)-(31,3)) - │ │ │ │ └── name: :A - │ │ │ └── delimiter_loc: (31,0)-(31,2) = "::" - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (31,5)-(31,6)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (31,3)-(31,5) = "::" + │ │ │ ├── name: :A + │ │ │ ├── delimiter_loc: (31,0)-(31,2) = "::" + │ │ │ └── name_loc: (31,2)-(31,3) = "A" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (31,3)-(31,5) = "::" + │ │ └── name_loc: (31,5)-(31,6) = "B" │ ├── operator_loc: (31,7)-(31,8) = "=" │ └── value: │ @ IntegerNode (location: (31,9)-(31,10)) @@ -296,20 +287,17 @@ │ ├── parent: │ │ @ ConstantPathNode (location: (33,0)-(33,3)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (33,2)-(33,3)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (33,0)-(33,2) = "::" - │ ├── child: - │ │ @ ConstantReadNode (location: (33,5)-(33,6)) - │ │ └── name: :B - │ └── delimiter_loc: (33,3)-(33,5) = "::" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (33,0)-(33,2) = "::" + │ │ └── name_loc: (33,2)-(33,3) = "A" + │ ├── name: :B + │ ├── delimiter_loc: (33,3)-(33,5) = "::" + │ └── name_loc: (33,5)-(33,6) = "B" ├── @ ConstantPathNode (location: (35,0)-(35,3)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (35,2)-(35,3)) - │ │ └── name: :A - │ └── delimiter_loc: (35,0)-(35,2) = "::" + │ ├── name: :A + │ ├── delimiter_loc: (35,0)-(35,2) = "::" + │ └── name_loc: (35,2)-(35,3) = "A" ├── @ CallNode (location: (37,0)-(37,8)) │ ├── flags: ∅ │ ├── receiver: @@ -329,10 +317,9 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (39,0)-(39,1)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (39,3)-(39,4)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (39,1)-(39,3) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (39,1)-(39,3) = "::" + │ │ └── name_loc: (39,3)-(39,4) = "B" │ ├── call_operator_loc: (39,4)-(39,6) = "::" │ ├── name: :true │ ├── message_loc: (39,6)-(39,10) = "true" @@ -488,10 +475,9 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (65,0)-(65,1)) │ │ └── name: :A - │ ├── child: - │ │ @ ConstantReadNode (location: (67,0)-(67,1)) - │ │ └── name: :C - │ └── delimiter_loc: (65,1)-(65,3) = "::" + │ ├── name: :C + │ ├── delimiter_loc: (65,1)-(65,3) = "::" + │ └── name_loc: (67,0)-(67,1) = "C" ├── @ CallNode (location: (69,0)-(69,8)) │ ├── flags: ∅ │ ├── receiver: @@ -532,10 +518,9 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (75,0)-(75,1)) │ │ └── name: :A - │ ├── child: - │ │ @ ConstantReadNode (location: (75,3)-(75,8)) - │ │ └── name: :BEGIN - │ └── delimiter_loc: (75,1)-(75,3) = "::" + │ ├── name: :BEGIN + │ ├── delimiter_loc: (75,1)-(75,3) = "::" + │ └── name_loc: (75,3)-(75,8) = "BEGIN" ├── @ CallNode (location: (77,0)-(77,8)) │ ├── flags: ∅ │ ├── receiver: @@ -636,10 +621,9 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (93,0)-(93,1)) │ │ └── name: :A - │ ├── child: - │ │ @ ConstantReadNode (location: (93,3)-(93,6)) - │ │ └── name: :END - │ └── delimiter_loc: (93,1)-(93,3) = "::" + │ ├── name: :END + │ ├── delimiter_loc: (93,1)-(93,3) = "::" + │ └── name_loc: (93,3)-(93,6) = "END" ├── @ CallNode (location: (95,0)-(95,9)) │ ├── flags: ∅ │ ├── receiver: @@ -1207,10 +1191,9 @@ │ │ ├── arguments: ∅ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (180,0)-(180,1)) - │ │ └── name: :C - │ └── delimiter_loc: (179,4)-(179,6) = "::" + │ ├── name: :C + │ ├── delimiter_loc: (179,4)-(179,6) = "::" + │ └── name_loc: (180,0)-(180,1) = "C" └── @ RangeNode (location: (182,0)-(184,10)) ├── flags: ∅ ├── left: diff --git a/test/prism/snapshots/defined.txt b/test/prism/snapshots/defined.txt index 53a5081811..c60173ff37 100644 --- a/test/prism/snapshots/defined.txt +++ b/test/prism/snapshots/defined.txt @@ -28,13 +28,13 @@ │ ├── value: │ │ @ LocalVariableOperatorWriteNode (location: (3,9)-(3,15)) │ │ ├── name_loc: (3,9)-(3,10) = "x" - │ │ ├── operator_loc: (3,11)-(3,13) = "%=" + │ │ ├── binary_operator_loc: (3,11)-(3,13) = "%=" │ │ ├── value: │ │ │ @ IntegerNode (location: (3,14)-(3,15)) │ │ │ ├── flags: decimal │ │ │ └── value: 2 │ │ ├── name: :x - │ │ ├── operator: :% + │ │ ├── binary_operator: :% │ │ └── depth: 0 │ ├── rparen_loc: (3,15)-(3,16) = ")" │ └── keyword_loc: (3,0)-(3,8) = "defined?" diff --git a/test/prism/snapshots/heredocs_nested.txt b/test/prism/snapshots/heredocs_nested.txt index f830b028c7..da13e48c51 100644 --- a/test/prism/snapshots/heredocs_nested.txt +++ b/test/prism/snapshots/heredocs_nested.txt @@ -4,7 +4,7 @@ @ StatementsNode (location: (1,0)-(12,4)) └── body: (length: 2) ├── @ InterpolatedStringNode (location: (1,0)-(1,7)) - │ ├── flags: ∅ + │ ├── flags: mutable │ ├── opening_loc: (1,0)-(1,7) = "<<~RUBY" │ ├── parts: (length: 4) │ │ ├── @ StringNode (location: (2,0)-(3,0)) @@ -19,7 +19,7 @@ │ │ │ │ @ StatementsNode (location: (4,0)-(4,6)) │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ StringNode (location: (4,0)-(4,6)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: frozen │ │ │ │ ├── opening_loc: (4,0)-(4,6) = "<<RUBY" │ │ │ │ ├── content_loc: (5,0)-(6,0) = " hello\n" │ │ │ │ ├── closing_loc: (6,0)-(7,0) = "RUBY\n" diff --git a/test/prism/snapshots/if.txt b/test/prism/snapshots/if.txt index eb33d1699d..4114d22722 100644 --- a/test/prism/snapshots/if.txt +++ b/test/prism/snapshots/if.txt @@ -162,6 +162,7 @@ │ │ @ StatementsNode (location: (14,0)-(14,6)) │ │ └── body: (length: 1) │ │ └── @ ReturnNode (location: (14,0)-(14,6)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (14,0)-(14,6) = "return" │ │ └── arguments: ∅ │ ├── consequent: ∅ @@ -306,7 +307,7 @@ │ │ ├── opening_loc: ∅ │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (25,4)-(25,6)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: contains_keywords │ │ │ └── arguments: (length: 1) │ │ │ └── @ KeywordHashNode (location: (25,4)-(25,6)) │ │ │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/method_calls.txt b/test/prism/snapshots/method_calls.txt index de9ba71ae0..6082b567f7 100644 --- a/test/prism/snapshots/method_calls.txt +++ b/test/prism/snapshots/method_calls.txt @@ -308,7 +308,7 @@ │ ├── opening_loc: (27,1)-(27,2) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (27,2)-(27,10)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (27,2)-(27,10)) │ │ ├── flags: ∅ @@ -779,7 +779,7 @@ │ ├── opening_loc: (60,3)-(60,4) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (60,4)-(60,32)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (60,4)-(60,6)) │ │ │ ├── flags: forced_us_ascii_encoding @@ -912,7 +912,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (64,4)-(64,15)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (64,4)-(64,6)) │ │ │ ├── flags: forced_us_ascii_encoding @@ -988,7 +988,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (66,3)-(66,17)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (66,3)-(66,17)) │ │ ├── flags: symbol_keys @@ -1020,7 +1020,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (68,3)-(68,40)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (68,3)-(68,40)) │ │ ├── flags: ∅ @@ -1075,7 +1075,7 @@ │ ├── opening_loc: (70,2)-(70,3) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (70,3)-(70,40)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (70,3)-(70,40)) │ │ ├── flags: ∅ @@ -1178,7 +1178,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (74,3)-(74,20)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (74,3)-(74,20)) │ │ ├── flags: symbol_keys @@ -1235,7 +1235,7 @@ │ ├── opening_loc: (80,3)-(80,4) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (81,0)-(82,5)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (81,0)-(81,2)) │ │ │ ├── flags: forced_us_ascii_encoding @@ -1292,7 +1292,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (87,4)-(87,21)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (87,4)-(87,21)) │ │ ├── flags: symbol_keys @@ -1339,7 +1339,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (89,10)-(89,21)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ IntegerNode (location: (89,10)-(89,11)) │ │ │ ├── flags: decimal @@ -1443,10 +1443,9 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (97,0)-(97,1)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (97,3)-(97,4)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (97,1)-(97,3) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (97,1)-(97,3) = "::" + │ │ └── name_loc: (97,3)-(97,4) = "B" │ ├── call_operator_loc: (97,4)-(97,6) = "::" │ ├── name: :C │ ├── message_loc: (97,6)-(97,7) = "C" @@ -1470,10 +1469,9 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (99,0)-(99,1)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (99,3)-(99,4)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (99,1)-(99,3) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (99,1)-(99,3) = "::" + │ │ └── name_loc: (99,3)-(99,4) = "B" │ ├── call_operator_loc: (99,4)-(99,6) = "::" │ ├── name: :C │ ├── message_loc: (99,6)-(99,7) = "C" @@ -1497,10 +1495,9 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (101,0)-(101,1)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (101,3)-(101,4)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (101,1)-(101,3) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (101,1)-(101,3) = "::" + │ │ └── name_loc: (101,3)-(101,4) = "B" │ ├── call_operator_loc: (101,4)-(101,6) = "::" │ ├── name: :C │ ├── message_loc: (101,6)-(101,7) = "C" @@ -1532,7 +1529,7 @@ │ ├── opening_loc: (103,3)-(103,4) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (103,4)-(103,11)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (103,4)-(103,11)) │ │ ├── flags: symbol_keys @@ -1561,7 +1558,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (105,4)-(105,28)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (105,4)-(105,28)) │ │ ├── flags: symbol_keys @@ -1617,7 +1614,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (107,4)-(107,24)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (107,4)-(107,24)) │ │ ├── flags: symbol_keys @@ -2417,7 +2414,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (156,5)-(156,19)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (156,5)-(156,19)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/methods.txt b/test/prism/snapshots/methods.txt index 22580494a4..b38640399b 100644 --- a/test/prism/snapshots/methods.txt +++ b/test/prism/snapshots/methods.txt @@ -814,6 +814,7 @@ │ │ │ │ @ StatementsNode (location: (99,0)-(99,10)) │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ ReturnNode (location: (99,0)-(99,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── keyword_loc: (99,0)-(99,6) = "return" │ │ │ │ └── arguments: │ │ │ │ @ ArgumentsNode (location: (99,7)-(99,10)) @@ -1295,7 +1296,7 @@ │ │ ├── opening_loc: ∅ │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (139,11)-(139,30)) - │ │ │ ├── flags: contains_keyword_splat + │ │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ │ └── arguments: (length: 1) │ │ │ └── @ KeywordHashNode (location: (139,11)-(139,30)) │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/modules.txt b/test/prism/snapshots/modules.txt index 1a0eb6328a..de1ea8feeb 100644 --- a/test/prism/snapshots/modules.txt +++ b/test/prism/snapshots/modules.txt @@ -72,10 +72,9 @@ │ │ │ ├── arguments: ∅ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (5,10)-(5,11)) - │ │ │ └── name: :M - │ │ └── delimiter_loc: (5,8)-(5,10) = "::" + │ │ ├── name: :M + │ │ ├── delimiter_loc: (5,8)-(5,10) = "::" + │ │ └── name_loc: (5,10)-(5,11) = "M" │ ├── body: ∅ │ ├── end_keyword_loc: (6,0)-(6,3) = "end" │ └── name: :M @@ -119,10 +118,9 @@ │ ├── constant_path: │ │ @ ConstantPathNode (location: (11,7)-(11,10)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (11,9)-(11,10)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (11,7)-(11,9) = "::" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (11,7)-(11,9) = "::" + │ │ └── name_loc: (11,9)-(11,10) = "A" │ ├── body: ∅ │ ├── end_keyword_loc: (12,0)-(12,3) = "end" │ └── name: :A @@ -144,10 +142,9 @@ │ │ │ ├── arguments: ∅ │ │ │ ├── closing_loc: (14,9)-(14,10) = "]" │ │ │ └── block: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (14,12)-(14,13)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (14,10)-(14,12) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (14,10)-(14,12) = "::" + │ │ └── name_loc: (14,12)-(14,13) = "B" │ ├── body: ∅ │ ├── end_keyword_loc: (15,0)-(15,3) = "end" │ └── name: :B @@ -175,10 +172,9 @@ │ │ │ └── value: 1 │ │ ├── closing_loc: (17,10)-(17,11) = "]" │ │ └── block: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (17,13)-(17,14)) - │ │ └── name: :B - │ └── delimiter_loc: (17,11)-(17,13) = "::" + │ ├── name: :B + │ ├── delimiter_loc: (17,11)-(17,13) = "::" + │ └── name_loc: (17,13)-(17,14) = "B" ├── body: ∅ ├── end_keyword_loc: (18,0)-(18,3) = "end" └── name: :B diff --git a/test/prism/snapshots/patterns.txt b/test/prism/snapshots/patterns.txt index 5662129dae..16298e7984 100644 --- a/test/prism/snapshots/patterns.txt +++ b/test/prism/snapshots/patterns.txt @@ -1454,14 +1454,12 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (64,7)-(64,10)) │ │ │ │ └── name: :Foo - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (64,12)-(64,15)) - │ │ │ │ └── name: :Bar - │ │ │ └── delimiter_loc: (64,10)-(64,12) = "::" - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (64,17)-(64,20)) - │ │ │ └── name: :Baz - │ │ └── delimiter_loc: (64,15)-(64,17) = "::" + │ │ │ ├── name: :Bar + │ │ │ ├── delimiter_loc: (64,10)-(64,12) = "::" + │ │ │ └── name_loc: (64,12)-(64,15) = "Bar" + │ │ ├── name: :Baz + │ │ ├── delimiter_loc: (64,15)-(64,17) = "::" + │ │ └── name_loc: (64,17)-(64,20) = "Baz" │ └── operator_loc: (64,4)-(64,6) = "=>" ├── @ MatchRequiredNode (location: (65,0)-(65,12)) │ ├── value: @@ -1478,10 +1476,9 @@ │ ├── pattern: │ │ @ ConstantPathNode (location: (65,7)-(65,12)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (65,9)-(65,12)) - │ │ │ └── name: :Foo - │ │ └── delimiter_loc: (65,7)-(65,9) = "::" + │ │ ├── name: :Foo + │ │ ├── delimiter_loc: (65,7)-(65,9) = "::" + │ │ └── name_loc: (65,9)-(65,12) = "Foo" │ └── operator_loc: (65,4)-(65,6) = "=>" ├── @ MatchRequiredNode (location: (66,0)-(66,22)) │ ├── value: @@ -1502,18 +1499,15 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantPathNode (location: (66,7)-(66,12)) │ │ │ │ ├── parent: ∅ - │ │ │ │ ├── child: - │ │ │ │ │ @ ConstantReadNode (location: (66,9)-(66,12)) - │ │ │ │ │ └── name: :Foo - │ │ │ │ └── delimiter_loc: (66,7)-(66,9) = "::" - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (66,14)-(66,17)) - │ │ │ │ └── name: :Bar - │ │ │ └── delimiter_loc: (66,12)-(66,14) = "::" - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (66,19)-(66,22)) - │ │ │ └── name: :Baz - │ │ └── delimiter_loc: (66,17)-(66,19) = "::" + │ │ │ │ ├── name: :Foo + │ │ │ │ ├── delimiter_loc: (66,7)-(66,9) = "::" + │ │ │ │ └── name_loc: (66,9)-(66,12) = "Foo" + │ │ │ ├── name: :Bar + │ │ │ ├── delimiter_loc: (66,12)-(66,14) = "::" + │ │ │ └── name_loc: (66,14)-(66,17) = "Bar" + │ │ ├── name: :Baz + │ │ ├── delimiter_loc: (66,17)-(66,19) = "::" + │ │ └── name_loc: (66,19)-(66,22) = "Baz" │ └── operator_loc: (66,4)-(66,6) = "=>" ├── @ MatchRequiredNode (location: (68,0)-(68,12)) │ ├── value: diff --git a/test/prism/snapshots/rescue.txt b/test/prism/snapshots/rescue.txt index 57cafde5a6..390b08ae0e 100644 --- a/test/prism/snapshots/rescue.txt +++ b/test/prism/snapshots/rescue.txt @@ -88,6 +88,7 @@ ├── @ RescueModifierNode (location: (10,0)-(10,17)) │ ├── expression: │ │ @ ReturnNode (location: (10,0)-(10,6)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (10,0)-(10,6) = "return" │ │ └── arguments: ∅ │ ├── keyword_loc: (10,7)-(10,13) = "rescue" @@ -379,7 +380,7 @@ │ │ │ ├── opening_loc: ∅ │ │ │ ├── arguments: │ │ │ │ @ ArgumentsNode (location: (29,4)-(29,6)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: contains_keywords │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ KeywordHashNode (location: (29,4)-(29,6)) │ │ │ │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/return.txt b/test/prism/snapshots/return.txt index 9a33076193..0dd26281c1 100644 --- a/test/prism/snapshots/return.txt +++ b/test/prism/snapshots/return.txt @@ -4,9 +4,11 @@ @ StatementsNode (location: (1,0)-(23,9)) └── body: (length: 10) ├── @ ReturnNode (location: (1,0)-(1,6)) + │ ├── flags: ∅ │ ├── keyword_loc: (1,0)-(1,6) = "return" │ └── arguments: ∅ ├── @ ReturnNode (location: (3,0)-(3,20)) + │ ├── flags: ∅ │ ├── keyword_loc: (3,0)-(3,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (3,7)-(3,20)) @@ -40,6 +42,7 @@ │ ├── opening_loc: (3,17)-(3,18) = "(" │ └── closing_loc: (3,19)-(3,20) = ")" ├── @ ReturnNode (location: (5,0)-(5,9)) + │ ├── flags: ∅ │ ├── keyword_loc: (5,0)-(5,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (5,7)-(5,9)) @@ -52,6 +55,7 @@ │ ├── flags: decimal │ └── value: 1 ├── @ ReturnNode (location: (7,0)-(7,8)) + │ ├── flags: ∅ │ ├── keyword_loc: (7,0)-(7,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (7,7)-(7,8)) @@ -61,6 +65,7 @@ │ ├── flags: decimal │ └── value: 1 ├── @ ReturnNode (location: (9,0)-(10,1)) + │ ├── flags: ∅ │ ├── keyword_loc: (9,0)-(9,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (9,7)-(10,1)) @@ -76,6 +81,7 @@ │ ├── flags: decimal │ └── value: 3 ├── @ ReturnNode (location: (12,0)-(12,14)) + │ ├── flags: ∅ │ ├── keyword_loc: (12,0)-(12,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (12,7)-(12,14)) @@ -91,6 +97,7 @@ │ ├── flags: decimal │ └── value: 3 ├── @ ReturnNode (location: (14,0)-(14,16)) + │ ├── flags: ∅ │ ├── keyword_loc: (14,0)-(14,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (14,7)-(14,16)) @@ -111,6 +118,7 @@ │ ├── opening_loc: (14,7)-(14,8) = "[" │ └── closing_loc: (14,15)-(14,16) = "]" ├── @ ReturnNode (location: (16,0)-(19,1)) + │ ├── flags: ∅ │ ├── keyword_loc: (16,0)-(16,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (16,6)-(19,1)) @@ -129,6 +137,7 @@ │ ├── opening_loc: (16,6)-(16,7) = "(" │ └── closing_loc: (19,0)-(19,1) = ")" ├── @ ReturnNode (location: (21,0)-(21,8)) + │ ├── flags: ∅ │ ├── keyword_loc: (21,0)-(21,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (21,6)-(21,8)) @@ -139,6 +148,7 @@ │ ├── opening_loc: (21,6)-(21,7) = "(" │ └── closing_loc: (21,7)-(21,8) = ")" └── @ ReturnNode (location: (23,0)-(23,9)) + ├── flags: ∅ ├── keyword_loc: (23,0)-(23,6) = "return" └── arguments: @ ArgumentsNode (location: (23,6)-(23,9)) diff --git a/test/prism/snapshots/seattlerb/assoc_label.txt b/test/prism/snapshots/seattlerb/assoc_label.txt index 923f5450f4..70490c0da4 100644 --- a/test/prism/snapshots/seattlerb/assoc_label.txt +++ b/test/prism/snapshots/seattlerb/assoc_label.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,5)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,2)-(1,5)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/seattlerb/block_return.txt b/test/prism/snapshots/seattlerb/block_return.txt index e91f5f2592..c863b28a22 100644 --- a/test/prism/snapshots/seattlerb/block_return.txt +++ b/test/prism/snapshots/seattlerb/block_return.txt @@ -4,6 +4,7 @@ @ StatementsNode (location: (1,0)-(1,27)) └── body: (length: 1) └── @ ReturnNode (location: (1,0)-(1,27)) + ├── flags: ∅ ├── keyword_loc: (1,0)-(1,6) = "return" └── arguments: @ ArgumentsNode (location: (1,7)-(1,27)) diff --git a/test/prism/snapshots/seattlerb/bug_249.txt b/test/prism/snapshots/seattlerb/bug_249.txt index 569bea14c5..ad61501a07 100644 --- a/test/prism/snapshots/seattlerb/bug_249.txt +++ b/test/prism/snapshots/seattlerb/bug_249.txt @@ -12,7 +12,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (1,6)-(4,28)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ CallNode (location: (1,6)-(4,9)) │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_hash_args.txt b/test/prism/snapshots/seattlerb/bug_hash_args.txt index 6f17e88714..e138db4d49 100644 --- a/test/prism/snapshots/seattlerb/bug_hash_args.txt +++ b/test/prism/snapshots/seattlerb/bug_hash_args.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,3)-(1,4) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,4)-(1,18)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ SymbolNode (location: (1,4)-(1,8)) │ │ ├── flags: forced_us_ascii_encoding diff --git a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt index e7256b337b..fe2d7f73c9 100644 --- a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt +++ b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,3)-(1,4) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,4)-(1,18)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ SymbolNode (location: (1,4)-(1,8)) │ │ ├── flags: forced_us_ascii_encoding diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc.txt b/test/prism/snapshots/seattlerb/call_arg_assoc.txt index 27c19fd339..f489bc7f19 100644 --- a/test/prism/snapshots/seattlerb/call_arg_assoc.txt +++ b/test/prism/snapshots/seattlerb/call_arg_assoc.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,9)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ IntegerNode (location: (1,2)-(1,3)) │ │ ├── flags: decimal diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt index 0193eb1dfc..5b191396de 100644 --- a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt +++ b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,15)) - │ ├── flags: contains_keyword_splat + │ ├── flags: contains_keywords, contains_keyword_splat │ └── arguments: (length: 2) │ ├── @ IntegerNode (location: (1,2)-(1,3)) │ │ ├── flags: decimal diff --git a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt index 91c7725525..f95b80cf7d 100644 --- a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt +++ b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,8)) - │ ├── flags: contains_keyword_splat + │ ├── flags: contains_keywords, contains_keyword_splat │ └── arguments: (length: 2) │ ├── @ CallNode (location: (1,2)-(1,3)) │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt index 2d6f81c818..8946206a3f 100644 --- a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt +++ b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt @@ -12,7 +12,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (1,2)-(1,11)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (1,2)-(1,11)) │ │ ├── flags: ∅ @@ -55,7 +55,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (3,2)-(3,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (3,2)-(3,8)) │ │ ├── flags: symbol_keys @@ -84,7 +84,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (5,2)-(5,8)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (5,2)-(5,8)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt index 312a1981a0..0ba5891cf6 100644 --- a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt +++ b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,9)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ IntegerNode (location: (1,2)-(1,3)) │ │ ├── flags: decimal diff --git a/test/prism/snapshots/seattlerb/call_assoc.txt b/test/prism/snapshots/seattlerb/call_assoc.txt index 438c256553..60784e6095 100644 --- a/test/prism/snapshots/seattlerb/call_assoc.txt +++ b/test/prism/snapshots/seattlerb/call_assoc.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,6)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,2)-(1,6)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/call_assoc_new.txt b/test/prism/snapshots/seattlerb/call_assoc_new.txt index b4d7e0bf83..dc25fb2493 100644 --- a/test/prism/snapshots/seattlerb/call_assoc_new.txt +++ b/test/prism/snapshots/seattlerb/call_assoc_new.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,5)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,2)-(1,5)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt index 9587e2e074..b3d652e879 100644 --- a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt +++ b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(5,3)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,2)-(5,3)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt index 8d0b285172..b2012f0f75 100644 --- a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt +++ b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,6)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,2)-(1,6)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/call_kwsplat.txt b/test/prism/snapshots/seattlerb/call_kwsplat.txt index 4199e97a44..e0620dc5f0 100644 --- a/test/prism/snapshots/seattlerb/call_kwsplat.txt +++ b/test/prism/snapshots/seattlerb/call_kwsplat.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,1)-(1,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,5)) - │ ├── flags: contains_keyword_splat + │ ├── flags: contains_keywords, contains_keyword_splat │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,2)-(1,5)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_86.txt b/test/prism/snapshots/seattlerb/case_in_86.txt index 5889137844..082aa74eca 100644 --- a/test/prism/snapshots/seattlerb/case_in_86.txt +++ b/test/prism/snapshots/seattlerb/case_in_86.txt @@ -30,10 +30,9 @@ │ │ ├── requireds: (length: 1) │ │ │ └── @ ConstantPathNode (location: (2,3)-(2,13)) │ │ │ ├── parent: ∅ - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (2,5)-(2,13)) - │ │ │ │ └── name: :NilClass - │ │ │ └── delimiter_loc: (2,3)-(2,5) = "::" + │ │ │ ├── name: :NilClass + │ │ │ ├── delimiter_loc: (2,3)-(2,5) = "::" + │ │ │ └── name_loc: (2,5)-(2,13) = "NilClass" │ │ ├── rest: │ │ │ @ SplatNode (location: (2,15)-(2,16)) │ │ │ ├── operator_loc: (2,15)-(2,16) = "*" diff --git a/test/prism/snapshots/seattlerb/case_in_86_2.txt b/test/prism/snapshots/seattlerb/case_in_86_2.txt index 18ce70ae93..346264f907 100644 --- a/test/prism/snapshots/seattlerb/case_in_86_2.txt +++ b/test/prism/snapshots/seattlerb/case_in_86_2.txt @@ -35,10 +35,9 @@ │ │ ├── posts: (length: 1) │ │ │ └── @ ConstantPathNode (location: (2,6)-(2,16)) │ │ │ ├── parent: ∅ - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (2,8)-(2,16)) - │ │ │ │ └── name: :NilClass - │ │ │ └── delimiter_loc: (2,6)-(2,8) = "::" + │ │ │ ├── name: :NilClass + │ │ │ ├── delimiter_loc: (2,6)-(2,8) = "::" + │ │ │ └── name_loc: (2,8)-(2,16) = "NilClass" │ │ ├── opening_loc: ∅ │ │ └── closing_loc: ∅ │ ├── statements: diff --git a/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt b/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt index c783af9ce5..d6fb80ef90 100644 --- a/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt +++ b/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt @@ -20,10 +20,9 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) │ │ │ │ └── name: :B - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (2,6)-(2,7)) - │ │ │ │ └── name: :C - │ │ │ └── delimiter_loc: (2,4)-(2,6) = "::" + │ │ │ ├── name: :C + │ │ │ ├── delimiter_loc: (2,4)-(2,6) = "::" + │ │ │ └── name_loc: (2,6)-(2,7) = "C" │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (2,8)-(2,9)) │ │ │ ├── name: :d diff --git a/test/prism/snapshots/seattlerb/case_in_multiple.txt b/test/prism/snapshots/seattlerb/case_in_multiple.txt index d8597c4bfa..eba0084f96 100644 --- a/test/prism/snapshots/seattlerb/case_in_multiple.txt +++ b/test/prism/snapshots/seattlerb/case_in_multiple.txt @@ -18,10 +18,9 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) │ │ │ │ └── name: :A - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (2,6)-(2,7)) - │ │ │ │ └── name: :B - │ │ │ └── delimiter_loc: (2,4)-(2,6) = "::" + │ │ │ ├── name: :B + │ │ │ ├── delimiter_loc: (2,4)-(2,6) = "::" + │ │ │ └── name_loc: (2,6)-(2,7) = "B" │ │ ├── statements: │ │ │ @ StatementsNode (location: (3,2)-(3,4)) │ │ │ └── body: (length: 1) @@ -39,10 +38,9 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (4,3)-(4,4)) │ │ │ └── name: :D - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (4,6)-(4,7)) - │ │ │ └── name: :E - │ │ └── delimiter_loc: (4,4)-(4,6) = "::" + │ │ ├── name: :E + │ │ ├── delimiter_loc: (4,4)-(4,6) = "::" + │ │ └── name_loc: (4,6)-(4,7) = "E" │ ├── statements: │ │ @ StatementsNode (location: (5,2)-(5,4)) │ │ └── body: (length: 1) diff --git a/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt b/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt index b018ac48d4..e09eed7d2f 100644 --- a/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt +++ b/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt @@ -9,14 +9,12 @@ │ ├── parent: │ │ @ ConstantPathNode (location: (1,0)-(1,3)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (1,2)-(1,3)) - │ │ │ └── name: :X - │ │ └── delimiter_loc: (1,0)-(1,2) = "::" - │ ├── child: - │ │ @ ConstantReadNode (location: (1,5)-(1,6)) - │ │ └── name: :Y - │ └── delimiter_loc: (1,3)-(1,5) = "::" + │ │ ├── name: :X + │ │ ├── delimiter_loc: (1,0)-(1,2) = "::" + │ │ └── name_loc: (1,2)-(1,3) = "X" + │ ├── name: :Y + │ ├── delimiter_loc: (1,3)-(1,5) = "::" + │ └── name_loc: (1,5)-(1,6) = "Y" ├── operator_loc: (1,7)-(1,10) = "||=" └── value: @ IntegerNode (location: (1,11)-(1,12)) diff --git a/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt b/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt index 8d9d94931b..398af888a8 100644 --- a/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt +++ b/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt @@ -7,10 +7,9 @@ ├── target: │ @ ConstantPathNode (location: (1,0)-(1,3)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (1,2)-(1,3)) - │ │ └── name: :X - │ └── delimiter_loc: (1,0)-(1,2) = "::" + │ ├── name: :X + │ ├── delimiter_loc: (1,0)-(1,2) = "::" + │ └── name_loc: (1,2)-(1,3) = "X" ├── operator_loc: (1,4)-(1,7) = "||=" └── value: @ IntegerNode (location: (1,8)-(1,9)) diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt b/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt index b1d61b3752..f9792aebb3 100644 --- a/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt +++ b/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt @@ -7,13 +7,12 @@ ├── target: │ @ ConstantPathNode (location: (1,0)-(1,3)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (1,2)-(1,3)) - │ │ └── name: :X - │ └── delimiter_loc: (1,0)-(1,2) = "::" - ├── operator_loc: (1,4)-(1,6) = "&=" + │ ├── name: :X + │ ├── delimiter_loc: (1,0)-(1,2) = "::" + │ └── name_loc: (1,2)-(1,3) = "X" + ├── binary_operator_loc: (1,4)-(1,6) = "&=" ├── value: │ @ IntegerNode (location: (1,7)-(1,8)) │ ├── flags: decimal │ └── value: 1 - └── operator: :& + └── binary_operator: :& diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt b/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt index 22f6682534..146455d327 100644 --- a/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt +++ b/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt @@ -7,10 +7,9 @@ ├── target: │ @ ConstantPathNode (location: (1,0)-(1,3)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (1,2)-(1,3)) - │ │ └── name: :X - │ └── delimiter_loc: (1,0)-(1,2) = "::" + │ ├── name: :X + │ ├── delimiter_loc: (1,0)-(1,2) = "::" + │ └── name_loc: (1,2)-(1,3) = "X" ├── operator_loc: (1,4)-(1,7) = "&&=" └── value: @ IntegerNode (location: (1,8)-(1,9)) diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_or.txt b/test/prism/snapshots/seattlerb/const_op_asgn_or.txt index 067e0fbb93..5e9dd39604 100644 --- a/test/prism/snapshots/seattlerb/const_op_asgn_or.txt +++ b/test/prism/snapshots/seattlerb/const_op_asgn_or.txt @@ -9,10 +9,9 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (1,0)-(1,1)) │ │ └── name: :X - │ ├── child: - │ │ @ ConstantReadNode (location: (1,3)-(1,4)) - │ │ └── name: :Y - │ └── delimiter_loc: (1,1)-(1,3) = "::" + │ ├── name: :Y + │ ├── delimiter_loc: (1,1)-(1,3) = "::" + │ └── name_loc: (1,3)-(1,4) = "Y" ├── operator_loc: (1,5)-(1,8) = "||=" └── value: @ IntegerNode (location: (1,9)-(1,10)) diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt index f420420fc3..2aadedd964 100644 --- a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt +++ b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt @@ -33,7 +33,7 @@ │ ├── opening_loc: (1,30)-(1,31) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (1,31)-(1,40)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (1,31)-(1,40)) │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/difficult2_.txt b/test/prism/snapshots/seattlerb/difficult2_.txt index a9b3736fe3..b53d4cad3f 100644 --- a/test/prism/snapshots/seattlerb/difficult2_.txt +++ b/test/prism/snapshots/seattlerb/difficult2_.txt @@ -52,7 +52,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (2,2)-(2,6)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (2,2)-(2,6)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/seattlerb/dstr_evstr.txt b/test/prism/snapshots/seattlerb/dstr_evstr.txt index 8d771e88c2..add8ad6f5c 100644 --- a/test/prism/snapshots/seattlerb/dstr_evstr.txt +++ b/test/prism/snapshots/seattlerb/dstr_evstr.txt @@ -13,7 +13,7 @@ │ │ │ @ StatementsNode (location: (1,3)-(1,6)) │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (1,3)-(1,6)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: frozen │ │ │ ├── opening_loc: (1,3)-(1,4) = "'" │ │ │ ├── content_loc: (1,4)-(1,5) = "a" │ │ │ ├── closing_loc: (1,5)-(1,6) = "'" diff --git a/test/prism/snapshots/seattlerb/dstr_str.txt b/test/prism/snapshots/seattlerb/dstr_str.txt index 70b5752ce3..6fe0781880 100644 --- a/test/prism/snapshots/seattlerb/dstr_str.txt +++ b/test/prism/snapshots/seattlerb/dstr_str.txt @@ -4,7 +4,7 @@ @ StatementsNode (location: (1,0)-(1,10)) └── body: (length: 1) └── @ InterpolatedStringNode (location: (1,0)-(1,10)) - ├── flags: ∅ + ├── flags: mutable ├── opening_loc: (1,0)-(1,1) = "\"" ├── parts: (length: 2) │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,7)) @@ -13,7 +13,7 @@ │ │ │ @ StatementsNode (location: (1,3)-(1,6)) │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (1,3)-(1,6)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: frozen │ │ │ ├── opening_loc: (1,3)-(1,4) = "'" │ │ │ ├── content_loc: (1,4)-(1,5) = "a" │ │ │ ├── closing_loc: (1,5)-(1,6) = "'" diff --git a/test/prism/snapshots/seattlerb/heredoc_nested.txt b/test/prism/snapshots/seattlerb/heredoc_nested.txt index 26d533a33d..a2322b9632 100644 --- a/test/prism/snapshots/seattlerb/heredoc_nested.txt +++ b/test/prism/snapshots/seattlerb/heredoc_nested.txt @@ -7,7 +7,7 @@ ├── flags: ∅ ├── elements: (length: 2) │ ├── @ InterpolatedStringNode (location: (1,1)-(1,4)) - │ │ ├── flags: ∅ + │ │ ├── flags: mutable │ │ ├── opening_loc: (1,1)-(1,4) = "<<A" │ │ ├── parts: (length: 3) │ │ │ ├── @ EmbeddedStatementsNode (location: (2,0)-(2,6)) @@ -16,7 +16,7 @@ │ │ │ │ │ @ StatementsNode (location: (2,2)-(2,5)) │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ StringNode (location: (2,2)-(2,5)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: frozen │ │ │ │ │ ├── opening_loc: (2,2)-(2,5) = "<<B" │ │ │ │ │ ├── content_loc: (3,0)-(4,0) = "b\n" │ │ │ │ │ ├── closing_loc: (4,0)-(5,0) = "B\n" diff --git a/test/prism/snapshots/seattlerb/index_0_opasgn.txt b/test/prism/snapshots/seattlerb/index_0_opasgn.txt index 239a549253..322eae9907 100644 --- a/test/prism/snapshots/seattlerb/index_0_opasgn.txt +++ b/test/prism/snapshots/seattlerb/index_0_opasgn.txt @@ -21,8 +21,8 @@ ├── arguments: ∅ ├── closing_loc: (1,2)-(1,3) = "]" ├── block: ∅ - ├── operator: :+ - ├── operator_loc: (1,4)-(1,6) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (1,4)-(1,6) = "+=" └── value: @ CallNode (location: (1,7)-(1,8)) ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/masgn_colon2.txt b/test/prism/snapshots/seattlerb/masgn_colon2.txt index 73ce8a71da..a0dfe72ffc 100644 --- a/test/prism/snapshots/seattlerb/masgn_colon2.txt +++ b/test/prism/snapshots/seattlerb/masgn_colon2.txt @@ -20,10 +20,9 @@ │ │ ├── arguments: ∅ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (1,6)-(1,7)) - │ │ └── name: :C - │ └── delimiter_loc: (1,4)-(1,6) = "::" + │ ├── name: :C + │ ├── delimiter_loc: (1,4)-(1,6) = "::" + │ └── name_loc: (1,6)-(1,7) = "C" ├── rest: ∅ ├── rights: (length: 0) ├── lparen_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/masgn_colon3.txt b/test/prism/snapshots/seattlerb/masgn_colon3.txt index 0cf4f8626d..f28ed7ecee 100644 --- a/test/prism/snapshots/seattlerb/masgn_colon3.txt +++ b/test/prism/snapshots/seattlerb/masgn_colon3.txt @@ -7,16 +7,14 @@ ├── lefts: (length: 2) │ ├── @ ConstantPathTargetNode (location: (1,0)-(1,3)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (1,2)-(1,3)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (1,0)-(1,2) = "::" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (1,0)-(1,2) = "::" + │ │ └── name_loc: (1,2)-(1,3) = "A" │ └── @ ConstantPathTargetNode (location: (1,5)-(1,8)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (1,7)-(1,8)) - │ │ └── name: :B - │ └── delimiter_loc: (1,5)-(1,7) = "::" + │ ├── name: :B + │ ├── delimiter_loc: (1,5)-(1,7) = "::" + │ └── name_loc: (1,7)-(1,8) = "B" ├── rest: ∅ ├── rights: (length: 0) ├── lparen_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt b/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt index 7a3e9affb5..edef23044a 100644 --- a/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt +++ b/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt @@ -24,11 +24,10 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (1,3)-(1,4)) │ │ │ │ └── name: :B - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (1,6)-(1,7)) - │ │ │ │ └── name: :C - │ │ │ └── delimiter_loc: (1,4)-(1,6) = "::" - │ │ ├── operator_loc: (1,8)-(1,10) = "*=" + │ │ │ ├── name: :C + │ │ │ ├── delimiter_loc: (1,4)-(1,6) = "::" + │ │ │ └── name_loc: (1,6)-(1,7) = "C" + │ │ ├── binary_operator_loc: (1,8)-(1,10) = "*=" │ │ ├── value: │ │ │ @ CallNode (location: (1,11)-(1,14)) │ │ │ ├── flags: ignore_visibility @@ -53,7 +52,7 @@ │ │ │ │ └── block: ∅ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ - │ │ └── operator: :* + │ │ └── binary_operator: :* │ ├── opening_loc: (1,2)-(1,3) = "(" │ └── closing_loc: (1,14)-(1,15) = ")" ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt index da10a474c3..1bb8bd0bc1 100644 --- a/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt +++ b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt @@ -22,7 +22,7 @@ ├── opening_loc: (1,3)-(1,4) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,4)-(1,8)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,4)-(1,8)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt index 79b0ef5d23..ff28a1798b 100644 --- a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt +++ b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt @@ -12,7 +12,7 @@ │ ├── opening_loc: (1,1)-(1,2) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (1,2)-(3,1)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (1,2)-(3,1)) │ │ ├── flags: symbol_keys @@ -42,7 +42,7 @@ │ ├── opening_loc: (5,1)-(5,2) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (5,2)-(6,1)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (5,2)-(6,1)) │ │ ├── flags: symbol_keys @@ -72,7 +72,7 @@ ├── opening_loc: (8,1)-(8,2) = "(" ├── arguments: │ @ ArgumentsNode (location: (8,2)-(8,11)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (8,2)-(8,11)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt index 8e6df3e812..523ccde455 100644 --- a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt +++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt @@ -9,11 +9,10 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (1,0)-(1,1)) │ │ └── name: :A - │ ├── child: - │ │ @ ConstantReadNode (location: (1,3)-(1,4)) - │ │ └── name: :B - │ └── delimiter_loc: (1,1)-(1,3) = "::" - ├── operator_loc: (1,5)-(1,7) = "*=" + │ ├── name: :B + │ ├── delimiter_loc: (1,1)-(1,3) = "::" + │ └── name_loc: (1,3)-(1,4) = "B" + ├── binary_operator_loc: (1,5)-(1,7) = "*=" ├── value: │ @ CallNode (location: (1,8)-(1,11)) │ ├── flags: ignore_visibility @@ -38,4 +37,4 @@ │ │ └── block: ∅ │ ├── closing_loc: ∅ │ └── block: ∅ - └── operator: :* + └── binary_operator: :* diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt index 0daadcf6ff..b9d00edc30 100644 --- a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt +++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt @@ -12,8 +12,8 @@ ├── message_loc: (1,3)-(1,4) = "b" ├── read_name: :b ├── write_name: :b= - ├── operator: :+ - ├── operator_loc: (1,5)-(1,7) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (1,5)-(1,7) = "+=" └── value: @ IntegerNode (location: (1,8)-(1,9)) ├── flags: decimal diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt index ea8603165b..c12ea3983c 100644 --- a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt +++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt @@ -12,8 +12,8 @@ ├── message_loc: (1,3)-(1,4) = "b" ├── read_name: :b ├── write_name: :b= - ├── operator: :* - ├── operator_loc: (1,5)-(1,7) = "*=" + ├── binary_operator: :* + ├── binary_operator_loc: (1,5)-(1,7) = "*=" └── value: @ CallNode (location: (1,8)-(1,11)) ├── flags: ignore_visibility diff --git a/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt b/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt index 50ccb762be..84eef70b25 100644 --- a/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt +++ b/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt @@ -40,15 +40,16 @@ │ │ └── block: ∅ │ ├── @ LocalVariableOperatorWriteNode (location: (3,2)-(3,8)) │ │ ├── name_loc: (3,2)-(3,3) = "y" - │ │ ├── operator_loc: (3,4)-(3,6) = "*=" + │ │ ├── binary_operator_loc: (3,4)-(3,6) = "*=" │ │ ├── value: │ │ │ @ IntegerNode (location: (3,7)-(3,8)) │ │ │ ├── flags: decimal │ │ │ └── value: 2 │ │ ├── name: :y - │ │ ├── operator: :* + │ │ ├── binary_operator: :* │ │ └── depth: 0 │ └── @ ReturnNode (location: (4,2)-(4,10)) + │ ├── flags: redundant │ ├── keyword_loc: (4,2)-(4,8) = "return" │ └── arguments: │ @ ArgumentsNode (location: (4,9)-(4,10)) diff --git a/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt b/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt index 5c2eb2da3c..d113f2af9d 100644 --- a/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt +++ b/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt @@ -5,7 +5,7 @@ └── body: (length: 2) ├── @ LocalVariableOperatorWriteNode (location: (1,6)-(2,11)) │ ├── name_loc: (1,6)-(1,9) = "foo" - │ ├── operator_loc: (1,10)-(1,12) = "+=" + │ ├── binary_operator_loc: (1,10)-(1,12) = "+=" │ ├── value: │ │ @ CallNode (location: (2,8)-(2,11)) │ │ ├── flags: variable_call, ignore_visibility @@ -18,7 +18,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── name: :foo - │ ├── operator: :+ + │ ├── binary_operator: :+ │ └── depth: 0 └── @ CallNode (location: (3,6)-(3,9)) ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/parse_line_return.txt b/test/prism/snapshots/seattlerb/parse_line_return.txt index 9194ae13ee..719a4da5da 100644 --- a/test/prism/snapshots/seattlerb/parse_line_return.txt +++ b/test/prism/snapshots/seattlerb/parse_line_return.txt @@ -20,6 +20,7 @@ │ │ @ StatementsNode (location: (3,10)-(3,19)) │ │ └── body: (length: 1) │ │ └── @ ReturnNode (location: (3,10)-(3,19)) + │ │ ├── flags: redundant │ │ ├── keyword_loc: (3,10)-(3,16) = "return" │ │ └── arguments: │ │ @ ArgumentsNode (location: (3,17)-(3,19)) diff --git a/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt b/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt index b767a5c17e..dc11e2ca3d 100644 --- a/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt +++ b/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt @@ -15,7 +15,7 @@ ├── opening_loc: (1,1)-(1,2) = "[" ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,6)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,2)-(1,6)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt b/test/prism/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt index c4bc53b723..1b8ec69b56 100644 --- a/test/prism/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt +++ b/test/prism/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt @@ -13,7 +13,7 @@ │ │ ├── closing_loc: ∅ │ │ └── unescaped: "1" │ ├── @ InterpolatedStringNode (location: (1,6)-(1,12)) - │ │ ├── flags: ∅ + │ │ ├── flags: mutable │ │ ├── opening_loc: ∅ │ │ ├── parts: (length: 1) │ │ │ └── @ EmbeddedStatementsNode (location: (1,6)-(1,12)) @@ -22,7 +22,7 @@ │ │ │ │ @ StatementsNode (location: (1,8)-(1,11)) │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ StringNode (location: (1,8)-(1,11)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: frozen │ │ │ │ ├── opening_loc: (1,8)-(1,11) = "<<A" │ │ │ │ ├── content_loc: (2,0)-(3,0) = "2\n" │ │ │ │ ├── closing_loc: (3,0)-(4,0) = "A\n" diff --git a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt index 64caf51bcb..bbc19d50ef 100644 --- a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt +++ b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt @@ -12,7 +12,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (1,5)-(1,12)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,5)-(1,12)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/seattlerb/return_call_assocs.txt b/test/prism/snapshots/seattlerb/return_call_assocs.txt index 69aae7b205..8948f7879b 100644 --- a/test/prism/snapshots/seattlerb/return_call_assocs.txt +++ b/test/prism/snapshots/seattlerb/return_call_assocs.txt @@ -4,10 +4,11 @@ @ StatementsNode (location: (1,0)-(11,14)) └── body: (length: 6) ├── @ ReturnNode (location: (1,0)-(1,17)) + │ ├── flags: ∅ │ ├── keyword_loc: (1,0)-(1,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (1,7)-(1,17)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ IntegerNode (location: (1,7)-(1,8)) │ │ ├── flags: decimal @@ -29,10 +30,11 @@ │ │ └── value: 1 │ └── operator_loc: (1,13)-(1,15) = "=>" ├── @ ReturnNode (location: (3,0)-(3,26)) + │ ├── flags: ∅ │ ├── keyword_loc: (3,0)-(3,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (3,7)-(3,26)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ IntegerNode (location: (3,7)-(3,8)) │ │ ├── flags: decimal @@ -67,6 +69,7 @@ │ │ └── value: 2 │ └── operator_loc: (3,22)-(3,24) = "=>" ├── @ ReturnNode (location: (5,0)-(5,14)) + │ ├── flags: ∅ │ ├── keyword_loc: (5,0)-(5,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (5,7)-(5,14)) @@ -81,7 +84,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (5,9)-(5,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (5,9)-(5,14)) │ │ ├── flags: symbol_keys @@ -102,6 +105,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ReturnNode (location: (7,0)-(7,12)) + │ ├── flags: ∅ │ ├── keyword_loc: (7,0)-(7,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (7,7)-(7,12)) @@ -116,7 +120,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (7,9)-(7,12)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (7,9)-(7,12)) │ │ ├── flags: symbol_keys @@ -137,6 +141,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ReturnNode (location: (9,0)-(9,13)) + │ ├── flags: ∅ │ ├── keyword_loc: (9,0)-(9,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (9,7)-(9,13)) @@ -151,7 +156,7 @@ │ ├── opening_loc: (9,8)-(9,9) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (9,9)-(9,12)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (9,9)-(9,12)) │ │ ├── flags: symbol_keys @@ -172,6 +177,7 @@ │ ├── closing_loc: (9,12)-(9,13) = ")" │ └── block: ∅ └── @ ReturnNode (location: (11,0)-(11,14)) + ├── flags: ∅ ├── keyword_loc: (11,0)-(11,6) = "return" └── arguments: @ ArgumentsNode (location: (11,7)-(11,14)) @@ -186,7 +192,7 @@ ├── opening_loc: (11,8)-(11,9) = "(" ├── arguments: │ @ ArgumentsNode (location: (11,9)-(11,13)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (11,9)-(11,13)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/safe_op_asgn.txt b/test/prism/snapshots/seattlerb/safe_op_asgn.txt index 7a9fd2b7f7..ebcedd6b5e 100644 --- a/test/prism/snapshots/seattlerb/safe_op_asgn.txt +++ b/test/prism/snapshots/seattlerb/safe_op_asgn.txt @@ -20,8 +20,8 @@ ├── message_loc: (1,3)-(1,4) = "b" ├── read_name: :b ├── write_name: :b= - ├── operator: :+ - ├── operator_loc: (1,5)-(1,7) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (1,5)-(1,7) = "+=" └── value: @ CallNode (location: (1,8)-(1,11)) ├── flags: ignore_visibility diff --git a/test/prism/snapshots/seattlerb/str_str.txt b/test/prism/snapshots/seattlerb/str_str.txt index f3f1213a0c..97031c8a65 100644 --- a/test/prism/snapshots/seattlerb/str_str.txt +++ b/test/prism/snapshots/seattlerb/str_str.txt @@ -4,7 +4,7 @@ @ StatementsNode (location: (1,0)-(1,10)) └── body: (length: 1) └── @ InterpolatedStringNode (location: (1,0)-(1,10)) - ├── flags: ∅ + ├── flags: mutable ├── opening_loc: (1,0)-(1,1) = "\"" ├── parts: (length: 2) │ ├── @ StringNode (location: (1,1)-(1,3)) @@ -19,7 +19,7 @@ │ │ @ StatementsNode (location: (1,5)-(1,8)) │ │ └── body: (length: 1) │ │ └── @ StringNode (location: (1,5)-(1,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: frozen │ │ ├── opening_loc: (1,5)-(1,6) = "'" │ │ ├── content_loc: (1,6)-(1,7) = "b" │ │ ├── closing_loc: (1,7)-(1,8) = "'" diff --git a/test/prism/snapshots/seattlerb/str_str_str.txt b/test/prism/snapshots/seattlerb/str_str_str.txt index b01f2b5794..b592d380ef 100644 --- a/test/prism/snapshots/seattlerb/str_str_str.txt +++ b/test/prism/snapshots/seattlerb/str_str_str.txt @@ -4,7 +4,7 @@ @ StatementsNode (location: (1,0)-(1,12)) └── body: (length: 1) └── @ InterpolatedStringNode (location: (1,0)-(1,12)) - ├── flags: ∅ + ├── flags: mutable ├── opening_loc: (1,0)-(1,1) = "\"" ├── parts: (length: 3) │ ├── @ StringNode (location: (1,1)-(1,3)) @@ -19,7 +19,7 @@ │ │ │ @ StatementsNode (location: (1,5)-(1,8)) │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (1,5)-(1,8)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: frozen │ │ │ ├── opening_loc: (1,5)-(1,6) = "'" │ │ │ ├── content_loc: (1,6)-(1,7) = "b" │ │ │ ├── closing_loc: (1,7)-(1,8) = "'" diff --git a/test/prism/snapshots/unless.txt b/test/prism/snapshots/unless.txt index 6611ffe63d..6c4aaf66a5 100644 --- a/test/prism/snapshots/unless.txt +++ b/test/prism/snapshots/unless.txt @@ -122,6 +122,7 @@ │ │ @ StatementsNode (location: (12,0)-(12,6)) │ │ └── body: (length: 1) │ │ └── @ ReturnNode (location: (12,0)-(12,6)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (12,0)-(12,6) = "return" │ │ └── arguments: ∅ │ ├── consequent: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/assignment.txt b/test/prism/snapshots/unparser/corpus/literal/assignment.txt index 99c8daf34c..7d3cc389c6 100644 --- a/test/prism/snapshots/unparser/corpus/literal/assignment.txt +++ b/test/prism/snapshots/unparser/corpus/literal/assignment.txt @@ -469,18 +469,16 @@ │ ├── target: │ │ @ ConstantPathNode (location: (18,0)-(18,5)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (18,2)-(18,5)) - │ │ │ └── name: :Foo - │ │ └── delimiter_loc: (18,0)-(18,2) = "::" + │ │ ├── name: :Foo + │ │ ├── delimiter_loc: (18,0)-(18,2) = "::" + │ │ └── name_loc: (18,2)-(18,5) = "Foo" │ ├── operator_loc: (18,6)-(18,7) = "=" │ └── value: │ @ ConstantPathNode (location: (18,8)-(18,13)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (18,10)-(18,13)) - │ │ └── name: :Bar - │ └── delimiter_loc: (18,8)-(18,10) = "::" + │ ├── name: :Bar + │ ├── delimiter_loc: (18,8)-(18,10) = "::" + │ └── name_loc: (18,10)-(18,13) = "Bar" ├── @ ClassVariableWriteNode (location: (19,0)-(19,7)) │ ├── name: :@@a │ ├── name_loc: (19,0)-(19,3) = "@@a" @@ -513,14 +511,12 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (22,0)-(22,4)) │ │ │ │ └── name: :Name - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (22,6)-(22,12)) - │ │ │ │ └── name: :Spaced - │ │ │ └── delimiter_loc: (22,4)-(22,6) = "::" - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (22,14)-(22,19)) - │ │ │ └── name: :CONST - │ │ └── delimiter_loc: (22,12)-(22,14) = "::" + │ │ │ ├── name: :Spaced + │ │ │ ├── delimiter_loc: (22,4)-(22,6) = "::" + │ │ │ └── name_loc: (22,6)-(22,12) = "Spaced" + │ │ ├── name: :CONST + │ │ ├── delimiter_loc: (22,12)-(22,14) = "::" + │ │ └── name_loc: (22,14)-(22,19) = "CONST" │ ├── operator_loc: (22,20)-(22,21) = "=" │ └── value: │ @ IntegerNode (location: (22,22)-(22,23)) diff --git a/test/prism/snapshots/unparser/corpus/literal/class.txt b/test/prism/snapshots/unparser/corpus/literal/class.txt index 34eb03edb3..5306888398 100644 --- a/test/prism/snapshots/unparser/corpus/literal/class.txt +++ b/test/prism/snapshots/unparser/corpus/literal/class.txt @@ -68,10 +68,9 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (11,6)-(11,7)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (11,9)-(11,10)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (11,7)-(11,9) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (11,7)-(11,9) = "::" + │ │ └── name_loc: (11,9)-(11,10) = "B" │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: ∅ @@ -87,14 +86,12 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (14,6)-(14,7)) │ │ │ │ └── name: :A - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (14,9)-(14,10)) - │ │ │ │ └── name: :B - │ │ │ └── delimiter_loc: (14,7)-(14,9) = "::" - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (14,12)-(14,13)) - │ │ │ └── name: :C - │ │ └── delimiter_loc: (14,10)-(14,12) = "::" + │ │ │ ├── name: :B + │ │ │ ├── delimiter_loc: (14,7)-(14,9) = "::" + │ │ │ └── name_loc: (14,9)-(14,10) = "B" + │ │ ├── name: :C + │ │ ├── delimiter_loc: (14,10)-(14,12) = "::" + │ │ └── name_loc: (14,12)-(14,13) = "C" │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: ∅ @@ -125,10 +122,9 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (20,10)-(20,11)) │ │ │ └── name: :B - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (20,13)-(20,14)) - │ │ │ └── name: :C - │ │ └── delimiter_loc: (20,11)-(20,13) = "::" + │ │ ├── name: :C + │ │ ├── delimiter_loc: (20,11)-(20,13) = "::" + │ │ └── name_loc: (20,13)-(20,14) = "C" │ ├── body: ∅ │ ├── end_keyword_loc: (21,0)-(21,3) = "end" │ └── name: :A @@ -140,20 +136,18 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (23,6)-(23,7)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (23,9)-(23,10)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (23,7)-(23,9) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (23,7)-(23,9) = "::" + │ │ └── name_loc: (23,9)-(23,10) = "B" │ ├── inheritance_operator_loc: (23,11)-(23,12) = "<" │ ├── superclass: │ │ @ ConstantPathNode (location: (23,13)-(23,17)) │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (23,13)-(23,14)) │ │ │ └── name: :C - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (23,16)-(23,17)) - │ │ │ └── name: :D - │ │ └── delimiter_loc: (23,14)-(23,16) = "::" + │ │ ├── name: :D + │ │ ├── delimiter_loc: (23,14)-(23,16) = "::" + │ │ └── name_loc: (23,16)-(23,17) = "D" │ ├── body: ∅ │ ├── end_keyword_loc: (24,0)-(24,3) = "end" │ └── name: :B @@ -222,10 +216,9 @@ ├── constant_path: │ @ ConstantPathNode (location: (34,6)-(34,9)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (34,8)-(34,9)) - │ │ └── name: :A - │ └── delimiter_loc: (34,6)-(34,8) = "::" + │ ├── name: :A + │ ├── delimiter_loc: (34,6)-(34,8) = "::" + │ └── name_loc: (34,8)-(34,9) = "A" ├── inheritance_operator_loc: ∅ ├── superclass: ∅ ├── body: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/defs.txt b/test/prism/snapshots/unparser/corpus/literal/defs.txt index 431843cc19..7858877172 100644 --- a/test/prism/snapshots/unparser/corpus/literal/defs.txt +++ b/test/prism/snapshots/unparser/corpus/literal/defs.txt @@ -225,10 +225,9 @@ │ │ │ │ ├── parent: │ │ │ │ │ @ ConstantReadNode (location: (26,5)-(26,8)) │ │ │ │ │ └── name: :Foo - │ │ │ │ ├── child: - │ │ │ │ │ @ ConstantReadNode (location: (26,10)-(26,13)) - │ │ │ │ │ └── name: :Bar - │ │ │ │ └── delimiter_loc: (26,8)-(26,10) = "::" + │ │ │ │ ├── name: :Bar + │ │ │ │ ├── delimiter_loc: (26,8)-(26,10) = "::" + │ │ │ │ └── name_loc: (26,10)-(26,13) = "Bar" │ │ │ ├── call_operator_loc: (26,13)-(26,14) = "." │ │ │ ├── name: :baz │ │ │ ├── message_loc: (26,14)-(26,17) = "baz" @@ -269,10 +268,9 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (30,5)-(30,8)) │ │ │ │ └── name: :Foo - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (30,10)-(30,13)) - │ │ │ │ └── name: :Bar - │ │ │ └── delimiter_loc: (30,8)-(30,10) = "::" + │ │ │ ├── name: :Bar + │ │ │ ├── delimiter_loc: (30,8)-(30,10) = "::" + │ │ │ └── name_loc: (30,10)-(30,13) = "Bar" │ │ ├── opening_loc: (30,4)-(30,5) = "(" │ │ └── closing_loc: (30,13)-(30,14) = ")" │ ├── parameters: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/dstr.txt b/test/prism/snapshots/unparser/corpus/literal/dstr.txt index e60a309b34..8893e8b75d 100644 --- a/test/prism/snapshots/unparser/corpus/literal/dstr.txt +++ b/test/prism/snapshots/unparser/corpus/literal/dstr.txt @@ -203,6 +203,7 @@ │ │ @ StatementsNode (location: (27,2)-(27,19)) │ │ └── body: (length: 1) │ │ └── @ ReturnNode (location: (27,2)-(27,19)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (27,2)-(27,8) = "return" │ │ └── arguments: │ │ @ ArgumentsNode (location: (27,9)-(27,19)) diff --git a/test/prism/snapshots/unparser/corpus/literal/literal.txt b/test/prism/snapshots/unparser/corpus/literal/literal.txt index dcf00bf4e0..98b88e11ce 100644 --- a/test/prism/snapshots/unparser/corpus/literal/literal.txt +++ b/test/prism/snapshots/unparser/corpus/literal/literal.txt @@ -635,7 +635,7 @@ │ │ │ @ StatementsNode (location: (53,3)-(53,11)) │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (53,3)-(53,11)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: frozen │ │ │ ├── opening_loc: (53,3)-(53,4) = "\"" │ │ │ ├── content_loc: (53,4)-(53,10) = "\\u0000" │ │ │ ├── closing_loc: (53,10)-(53,11) = "\"" @@ -707,7 +707,7 @@ │ │ │ @ StatementsNode (location: (59,4)-(59,9)) │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (59,4)-(59,9)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: frozen │ │ │ ├── opening_loc: (59,4)-(59,5) = "\"" │ │ │ ├── content_loc: (59,5)-(59,8) = "foo" │ │ │ ├── closing_loc: (59,8)-(59,9) = "\"" diff --git a/test/prism/snapshots/unparser/corpus/literal/module.txt b/test/prism/snapshots/unparser/corpus/literal/module.txt index 5dd8c03b51..6428aeea82 100644 --- a/test/prism/snapshots/unparser/corpus/literal/module.txt +++ b/test/prism/snapshots/unparser/corpus/literal/module.txt @@ -20,10 +20,9 @@ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (4,7)-(4,8)) │ │ │ └── name: :A - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (4,10)-(4,11)) - │ │ │ └── name: :B - │ │ └── delimiter_loc: (4,8)-(4,10) = "::" + │ │ ├── name: :B + │ │ ├── delimiter_loc: (4,8)-(4,10) = "::" + │ │ └── name_loc: (4,10)-(4,11) = "B" │ ├── body: ∅ │ ├── end_keyword_loc: (5,0)-(5,3) = "end" │ └── name: :B @@ -37,14 +36,12 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (7,7)-(7,8)) │ │ │ │ └── name: :A - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (7,10)-(7,11)) - │ │ │ │ └── name: :B - │ │ │ └── delimiter_loc: (7,8)-(7,10) = "::" - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (7,13)-(7,14)) - │ │ │ └── name: :C - │ │ └── delimiter_loc: (7,11)-(7,13) = "::" + │ │ │ ├── name: :B + │ │ │ ├── delimiter_loc: (7,8)-(7,10) = "::" + │ │ │ └── name_loc: (7,10)-(7,11) = "B" + │ │ ├── name: :C + │ │ ├── delimiter_loc: (7,11)-(7,13) = "::" + │ │ └── name_loc: (7,13)-(7,14) = "C" │ ├── body: ∅ │ ├── end_keyword_loc: (8,0)-(8,3) = "end" │ └── name: :C diff --git a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt index 8dc0849638..0761b47348 100644 --- a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt +++ b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt @@ -5,53 +5,53 @@ └── body: (length: 24) ├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,6)) │ ├── name_loc: (1,0)-(1,1) = "a" - │ ├── operator_loc: (1,2)-(1,4) = "+=" + │ ├── binary_operator_loc: (1,2)-(1,4) = "+=" │ ├── value: │ │ @ IntegerNode (location: (1,5)-(1,6)) │ │ ├── flags: decimal │ │ └── value: 2 │ ├── name: :a - │ ├── operator: :+ + │ ├── binary_operator: :+ │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (2,0)-(2,6)) │ ├── name_loc: (2,0)-(2,1) = "a" - │ ├── operator_loc: (2,2)-(2,4) = "-=" + │ ├── binary_operator_loc: (2,2)-(2,4) = "-=" │ ├── value: │ │ @ IntegerNode (location: (2,5)-(2,6)) │ │ ├── flags: decimal │ │ └── value: 2 │ ├── name: :a - │ ├── operator: :- + │ ├── binary_operator: :- │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,7)) │ ├── name_loc: (3,0)-(3,1) = "a" - │ ├── operator_loc: (3,2)-(3,5) = "**=" + │ ├── binary_operator_loc: (3,2)-(3,5) = "**=" │ ├── value: │ │ @ IntegerNode (location: (3,6)-(3,7)) │ │ ├── flags: decimal │ │ └── value: 2 │ ├── name: :a - │ ├── operator: :** + │ ├── binary_operator: :** │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (4,0)-(4,6)) │ ├── name_loc: (4,0)-(4,1) = "a" - │ ├── operator_loc: (4,2)-(4,4) = "*=" + │ ├── binary_operator_loc: (4,2)-(4,4) = "*=" │ ├── value: │ │ @ IntegerNode (location: (4,5)-(4,6)) │ │ ├── flags: decimal │ │ └── value: 2 │ ├── name: :a - │ ├── operator: :* + │ ├── binary_operator: :* │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (5,0)-(5,6)) │ ├── name_loc: (5,0)-(5,1) = "a" - │ ├── operator_loc: (5,2)-(5,4) = "/=" + │ ├── binary_operator_loc: (5,2)-(5,4) = "/=" │ ├── value: │ │ @ IntegerNode (location: (5,5)-(5,6)) │ │ ├── flags: decimal │ │ └── value: 2 │ ├── name: :a - │ ├── operator: :/ + │ ├── binary_operator: :/ │ └── depth: 0 ├── @ LocalVariableAndWriteNode (location: (6,0)-(6,7)) │ ├── name_loc: (6,0)-(6,1) = "a" @@ -162,8 +162,8 @@ │ ├── message_loc: (10,2)-(10,3) = "b" │ ├── read_name: :b │ ├── write_name: :b= - │ ├── operator: :+ - │ ├── operator_loc: (10,4)-(10,6) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (10,4)-(10,6) = "+=" │ └── value: │ @ IntegerNode (location: (10,7)-(10,8)) │ ├── flags: decimal @@ -178,8 +178,8 @@ │ ├── message_loc: (11,2)-(11,3) = "b" │ ├── read_name: :b │ ├── write_name: :b= - │ ├── operator: :- - │ ├── operator_loc: (11,4)-(11,6) = "-=" + │ ├── binary_operator: :- + │ ├── binary_operator_loc: (11,4)-(11,6) = "-=" │ └── value: │ @ IntegerNode (location: (11,7)-(11,8)) │ ├── flags: decimal @@ -194,8 +194,8 @@ │ ├── message_loc: (12,2)-(12,3) = "b" │ ├── read_name: :b │ ├── write_name: :b= - │ ├── operator: :** - │ ├── operator_loc: (12,4)-(12,7) = "**=" + │ ├── binary_operator: :** + │ ├── binary_operator_loc: (12,4)-(12,7) = "**=" │ └── value: │ @ IntegerNode (location: (12,8)-(12,9)) │ ├── flags: decimal @@ -210,8 +210,8 @@ │ ├── message_loc: (13,2)-(13,3) = "b" │ ├── read_name: :b │ ├── write_name: :b= - │ ├── operator: :* - │ ├── operator_loc: (13,4)-(13,6) = "*=" + │ ├── binary_operator: :* + │ ├── binary_operator_loc: (13,4)-(13,6) = "*=" │ └── value: │ @ IntegerNode (location: (13,7)-(13,8)) │ ├── flags: decimal @@ -226,8 +226,8 @@ │ ├── message_loc: (14,2)-(14,3) = "b" │ ├── read_name: :b │ ├── write_name: :b= - │ ├── operator: :/ - │ ├── operator_loc: (14,4)-(14,6) = "/=" + │ ├── binary_operator: :/ + │ ├── binary_operator_loc: (14,4)-(14,6) = "/=" │ └── value: │ @ IntegerNode (location: (14,7)-(14,8)) │ ├── flags: decimal @@ -293,8 +293,8 @@ │ │ └── block: ∅ │ ├── closing_loc: (17,3)-(17,4) = "]" │ ├── block: ∅ - │ ├── operator: :+ - │ ├── operator_loc: (17,5)-(17,7) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (17,5)-(17,7) = "+=" │ └── value: │ @ IntegerNode (location: (17,8)-(17,9)) │ ├── flags: decimal @@ -323,8 +323,8 @@ │ │ └── block: ∅ │ ├── closing_loc: (18,3)-(18,4) = "]" │ ├── block: ∅ - │ ├── operator: :- - │ ├── operator_loc: (18,5)-(18,7) = "-=" + │ ├── binary_operator: :- + │ ├── binary_operator_loc: (18,5)-(18,7) = "-=" │ └── value: │ @ IntegerNode (location: (18,8)-(18,9)) │ ├── flags: decimal @@ -353,8 +353,8 @@ │ │ └── block: ∅ │ ├── closing_loc: (19,3)-(19,4) = "]" │ ├── block: ∅ - │ ├── operator: :** - │ ├── operator_loc: (19,5)-(19,8) = "**=" + │ ├── binary_operator: :** + │ ├── binary_operator_loc: (19,5)-(19,8) = "**=" │ └── value: │ @ IntegerNode (location: (19,9)-(19,10)) │ ├── flags: decimal @@ -383,8 +383,8 @@ │ │ └── block: ∅ │ ├── closing_loc: (20,3)-(20,4) = "]" │ ├── block: ∅ - │ ├── operator: :* - │ ├── operator_loc: (20,5)-(20,7) = "*=" + │ ├── binary_operator: :* + │ ├── binary_operator_loc: (20,5)-(20,7) = "*=" │ └── value: │ @ IntegerNode (location: (20,8)-(20,9)) │ ├── flags: decimal @@ -413,8 +413,8 @@ │ │ └── block: ∅ │ ├── closing_loc: (21,3)-(21,4) = "]" │ ├── block: ∅ - │ ├── operator: :/ - │ ├── operator_loc: (21,5)-(21,7) = "/=" + │ ├── binary_operator: :/ + │ ├── binary_operator_loc: (21,5)-(21,7) = "/=" │ └── value: │ @ IntegerNode (location: (21,8)-(21,9)) │ ├── flags: decimal @@ -501,8 +501,8 @@ ├── message_loc: (24,4)-(24,5) = "A" ├── read_name: :A ├── write_name: :A= - ├── operator: :+ - ├── operator_loc: (24,6)-(24,8) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (24,6)-(24,8) = "+=" └── value: @ IntegerNode (location: (24,9)-(24,10)) ├── flags: decimal diff --git a/test/prism/snapshots/unparser/corpus/literal/rescue.txt b/test/prism/snapshots/unparser/corpus/literal/rescue.txt index d84366f3f5..d3c9d62166 100644 --- a/test/prism/snapshots/unparser/corpus/literal/rescue.txt +++ b/test/prism/snapshots/unparser/corpus/literal/rescue.txt @@ -42,6 +42,7 @@ │ ├── keyword_loc: (2,4)-(2,10) = "rescue" │ └── rescue_expression: │ @ ReturnNode (location: (2,11)-(2,21)) + │ ├── flags: ∅ │ ├── keyword_loc: (2,11)-(2,17) = "return" │ └── arguments: │ @ ArgumentsNode (location: (2,18)-(2,21)) @@ -81,6 +82,7 @@ │ │ ├── keyword_loc: (3,9)-(3,15) = "rescue" │ │ └── rescue_expression: │ │ @ ReturnNode (location: (3,16)-(3,26)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (3,16)-(3,22) = "return" │ │ └── arguments: │ │ @ ArgumentsNode (location: (3,23)-(3,26)) diff --git a/test/prism/snapshots/unparser/corpus/literal/send.txt b/test/prism/snapshots/unparser/corpus/literal/send.txt index b7eb064717..3fd7f719a1 100644 --- a/test/prism/snapshots/unparser/corpus/literal/send.txt +++ b/test/prism/snapshots/unparser/corpus/literal/send.txt @@ -1251,7 +1251,7 @@ │ ├── opening_loc: (63,7)-(63,8) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (63,8)-(63,16)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (63,8)-(63,16)) │ │ ├── flags: symbol_keys @@ -1297,7 +1297,7 @@ │ ├── opening_loc: (64,7)-(64,8) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (64,8)-(64,25)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ CallNode (location: (64,8)-(64,11)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1571,7 +1571,7 @@ │ ├── opening_loc: (70,3)-(70,4) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (70,4)-(70,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (70,4)-(70,8)) │ │ ├── flags: symbol_keys @@ -1617,7 +1617,7 @@ │ ├── opening_loc: (71,5)-(71,6) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (71,6)-(71,10)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (71,6)-(71,10)) │ │ ├── flags: symbol_keys @@ -1663,7 +1663,7 @@ │ ├── opening_loc: (72,5)-(72,6) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (72,6)-(72,9)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (72,6)-(72,9)) │ │ ├── flags: ∅ @@ -2082,7 +2082,7 @@ │ ├── opening_loc: (81,1)-(81,2) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (81,2)-(81,7)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (81,2)-(81,7)) │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/since/32.txt b/test/prism/snapshots/unparser/corpus/literal/since/32.txt index e72be6d8b7..2b28be2fa8 100644 --- a/test/prism/snapshots/unparser/corpus/literal/since/32.txt +++ b/test/prism/snapshots/unparser/corpus/literal/since/32.txt @@ -36,7 +36,7 @@ │ │ ├── opening_loc: (2,5)-(2,6) = "(" │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (2,6)-(2,18)) - │ │ │ ├── flags: contains_keyword_splat + │ │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ │ └── arguments: (length: 2) │ │ │ ├── @ LocalVariableReadNode (location: (2,6)-(2,14)) │ │ │ │ ├── name: :argument diff --git a/test/prism/snapshots/unparser/corpus/literal/variables.txt b/test/prism/snapshots/unparser/corpus/literal/variables.txt index bf79de385c..9ae8ad1207 100644 --- a/test/prism/snapshots/unparser/corpus/literal/variables.txt +++ b/test/prism/snapshots/unparser/corpus/literal/variables.txt @@ -29,25 +29,21 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (8,0)-(8,6)) │ │ └── name: :SCOPED - │ ├── child: - │ │ @ ConstantReadNode (location: (8,8)-(8,13)) - │ │ └── name: :CONST - │ └── delimiter_loc: (8,6)-(8,8) = "::" + │ ├── name: :CONST + │ ├── delimiter_loc: (8,6)-(8,8) = "::" + │ └── name_loc: (8,8)-(8,13) = "CONST" ├── @ ConstantPathNode (location: (9,0)-(9,10)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (9,2)-(9,10)) - │ │ └── name: :TOPLEVEL - │ └── delimiter_loc: (9,0)-(9,2) = "::" + │ ├── name: :TOPLEVEL + │ ├── delimiter_loc: (9,0)-(9,2) = "::" + │ └── name_loc: (9,2)-(9,10) = "TOPLEVEL" └── @ ConstantPathNode (location: (10,0)-(10,17)) ├── parent: │ @ ConstantPathNode (location: (10,0)-(10,10)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (10,2)-(10,10)) - │ │ └── name: :TOPLEVEL - │ └── delimiter_loc: (10,0)-(10,2) = "::" - ├── child: - │ @ ConstantReadNode (location: (10,12)-(10,17)) - │ └── name: :CONST - └── delimiter_loc: (10,10)-(10,12) = "::" + │ ├── name: :TOPLEVEL + │ ├── delimiter_loc: (10,0)-(10,2) = "::" + │ └── name_loc: (10,2)-(10,10) = "TOPLEVEL" + ├── name: :CONST + ├── delimiter_loc: (10,10)-(10,12) = "::" + └── name_loc: (10,12)-(10,17) = "CONST" diff --git a/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt b/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt index e100dd8ecb..7dd26a38dc 100644 --- a/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt +++ b/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt @@ -44,8 +44,8 @@ │ └── closing_loc: (1,10)-(1,11) = "\"" ├── closing_loc: (1,11)-(1,12) = "]" ├── block: ∅ - ├── operator: :+ - ├── operator_loc: (1,13)-(1,15) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (1,13)-(1,15) = "+=" └── value: @ InterpolatedStringNode (location: (1,16)-(1,25)) ├── flags: ∅ diff --git a/test/prism/snapshots/until.txt b/test/prism/snapshots/until.txt index d45e39644b..e855dc89f7 100644 --- a/test/prism/snapshots/until.txt +++ b/test/prism/snapshots/until.txt @@ -97,6 +97,7 @@ │ @ StatementsNode (location: (9,0)-(9,6)) │ └── body: (length: 1) │ └── @ ReturnNode (location: (9,0)-(9,6)) + │ ├── flags: ∅ │ ├── keyword_loc: (9,0)-(9,6) = "return" │ └── arguments: ∅ ├── @ UntilNode (location: (11,0)-(11,21)) diff --git a/test/prism/snapshots/while.txt b/test/prism/snapshots/while.txt index 36fab49960..a2972face0 100644 --- a/test/prism/snapshots/while.txt +++ b/test/prism/snapshots/while.txt @@ -97,6 +97,7 @@ │ @ StatementsNode (location: (9,0)-(9,6)) │ └── body: (length: 1) │ └── @ ReturnNode (location: (9,0)-(9,6)) + │ ├── flags: ∅ │ ├── keyword_loc: (9,0)-(9,6) = "return" │ └── arguments: ∅ ├── @ WhileNode (location: (11,0)-(11,21)) diff --git a/test/prism/snapshots/whitequark/args_args_assocs.txt b/test/prism/snapshots/whitequark/args_args_assocs.txt index 6297f212d8..d257a885ce 100644 --- a/test/prism/snapshots/whitequark/args_args_assocs.txt +++ b/test/prism/snapshots/whitequark/args_args_assocs.txt @@ -12,7 +12,7 @@ │ ├── opening_loc: (1,3)-(1,4) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (1,4)-(1,18)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ CallNode (location: (1,4)-(1,7)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -51,7 +51,7 @@ ├── opening_loc: (3,3)-(3,4) = "(" ├── arguments: │ @ ArgumentsNode (location: (3,4)-(3,18)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ CallNode (location: (3,4)-(3,7)) │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt index 969514a511..2d986dd90a 100644 --- a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt +++ b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt @@ -22,7 +22,7 @@ ├── opening_loc: (1,3)-(1,4) = "[" ├── arguments: │ @ ArgumentsNode (location: (1,4)-(1,18)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ CallNode (location: (1,4)-(1,7)) │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/whitequark/args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_assocs_comma.txt index b1b9fbeefe..64a25bbc45 100644 --- a/test/prism/snapshots/whitequark/args_assocs_comma.txt +++ b/test/prism/snapshots/whitequark/args_assocs_comma.txt @@ -22,7 +22,7 @@ ├── opening_loc: (1,3)-(1,4) = "[" ├── arguments: │ @ ArgumentsNode (location: (1,4)-(1,13)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,4)-(1,13)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/whitequark/bug_cmdarg.txt b/test/prism/snapshots/whitequark/bug_cmdarg.txt index 509dd7e818..32d05746a7 100644 --- a/test/prism/snapshots/whitequark/bug_cmdarg.txt +++ b/test/prism/snapshots/whitequark/bug_cmdarg.txt @@ -12,7 +12,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (1,7)-(1,15)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (1,7)-(1,15)) │ │ ├── flags: symbol_keys @@ -62,7 +62,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (5,2)-(5,26)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (5,2)-(5,26)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/whitequark/casgn_scoped.txt b/test/prism/snapshots/whitequark/casgn_scoped.txt index 4e3fd6fe44..42b90be061 100644 --- a/test/prism/snapshots/whitequark/casgn_scoped.txt +++ b/test/prism/snapshots/whitequark/casgn_scoped.txt @@ -9,10 +9,9 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (1,0)-(1,3)) │ │ └── name: :Bar - │ ├── child: - │ │ @ ConstantReadNode (location: (1,5)-(1,8)) - │ │ └── name: :Foo - │ └── delimiter_loc: (1,3)-(1,5) = "::" + │ ├── name: :Foo + │ ├── delimiter_loc: (1,3)-(1,5) = "::" + │ └── name_loc: (1,5)-(1,8) = "Foo" ├── operator_loc: (1,9)-(1,10) = "=" └── value: @ IntegerNode (location: (1,11)-(1,13)) diff --git a/test/prism/snapshots/whitequark/casgn_toplevel.txt b/test/prism/snapshots/whitequark/casgn_toplevel.txt index 11facfefb3..070d90a46b 100644 --- a/test/prism/snapshots/whitequark/casgn_toplevel.txt +++ b/test/prism/snapshots/whitequark/casgn_toplevel.txt @@ -7,10 +7,9 @@ ├── target: │ @ ConstantPathNode (location: (1,0)-(1,5)) │ ├── parent: ∅ - │ ├── child: - │ │ @ ConstantReadNode (location: (1,2)-(1,5)) - │ │ └── name: :Foo - │ └── delimiter_loc: (1,0)-(1,2) = "::" + │ ├── name: :Foo + │ ├── delimiter_loc: (1,0)-(1,2) = "::" + │ └── name_loc: (1,2)-(1,5) = "Foo" ├── operator_loc: (1,6)-(1,7) = "=" └── value: @ IntegerNode (location: (1,8)-(1,10)) diff --git a/test/prism/snapshots/whitequark/const_op_asgn.txt b/test/prism/snapshots/whitequark/const_op_asgn.txt index 4985f3e54b..71df6208d2 100644 --- a/test/prism/snapshots/whitequark/const_op_asgn.txt +++ b/test/prism/snapshots/whitequark/const_op_asgn.txt @@ -7,41 +7,39 @@ │ ├── target: │ │ @ ConstantPathNode (location: (1,0)-(1,3)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (1,2)-(1,3)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (1,0)-(1,2) = "::" - │ ├── operator_loc: (1,4)-(1,6) = "+=" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (1,0)-(1,2) = "::" + │ │ └── name_loc: (1,2)-(1,3) = "A" + │ ├── binary_operator_loc: (1,4)-(1,6) = "+=" │ ├── value: │ │ @ IntegerNode (location: (1,7)-(1,8)) │ │ ├── flags: decimal │ │ └── value: 1 - │ └── operator: :+ + │ └── binary_operator: :+ ├── @ ConstantOperatorWriteNode (location: (3,0)-(3,6)) │ ├── name: :A │ ├── name_loc: (3,0)-(3,1) = "A" - │ ├── operator_loc: (3,2)-(3,4) = "+=" + │ ├── binary_operator_loc: (3,2)-(3,4) = "+=" │ ├── value: │ │ @ IntegerNode (location: (3,5)-(3,6)) │ │ ├── flags: decimal │ │ └── value: 1 - │ └── operator: :+ + │ └── binary_operator: :+ ├── @ ConstantPathOperatorWriteNode (location: (5,0)-(5,9)) │ ├── target: │ │ @ ConstantPathNode (location: (5,0)-(5,4)) │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (5,0)-(5,1)) │ │ │ └── name: :B - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (5,3)-(5,4)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (5,1)-(5,3) = "::" - │ ├── operator_loc: (5,5)-(5,7) = "+=" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (5,1)-(5,3) = "::" + │ │ └── name_loc: (5,3)-(5,4) = "A" + │ ├── binary_operator_loc: (5,5)-(5,7) = "+=" │ ├── value: │ │ @ IntegerNode (location: (5,8)-(5,9)) │ │ ├── flags: decimal │ │ └── value: 1 - │ └── operator: :+ + │ └── binary_operator: :+ ├── @ DefNode (location: (7,0)-(7,21)) │ ├── name: :x │ ├── name_loc: (7,4)-(7,5) = "x" @@ -54,10 +52,9 @@ │ │ ├── target: │ │ │ @ ConstantPathNode (location: (7,7)-(7,10)) │ │ │ ├── parent: ∅ - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (7,9)-(7,10)) - │ │ │ │ └── name: :A - │ │ │ └── delimiter_loc: (7,7)-(7,9) = "::" + │ │ │ ├── name: :A + │ │ │ ├── delimiter_loc: (7,7)-(7,9) = "::" + │ │ │ └── name_loc: (7,9)-(7,10) = "A" │ │ ├── operator_loc: (7,11)-(7,14) = "||=" │ │ └── value: │ │ @ IntegerNode (location: (7,15)-(7,16)) @@ -83,10 +80,9 @@ │ │ @ ConstantPathNode (location: (9,7)-(9,14)) │ │ ├── parent: │ │ │ @ SelfNode (location: (9,7)-(9,11)) - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (9,13)-(9,14)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (9,11)-(9,13) = "::" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (9,11)-(9,13) = "::" + │ │ └── name_loc: (9,13)-(9,14) = "A" │ ├── operator_loc: (9,15)-(9,18) = "||=" │ └── value: │ @ IntegerNode (location: (9,19)-(9,20)) diff --git a/test/prism/snapshots/whitequark/const_scoped.txt b/test/prism/snapshots/whitequark/const_scoped.txt index 1e2bccef96..83af4b187b 100644 --- a/test/prism/snapshots/whitequark/const_scoped.txt +++ b/test/prism/snapshots/whitequark/const_scoped.txt @@ -7,7 +7,6 @@ ├── parent: │ @ ConstantReadNode (location: (1,0)-(1,3)) │ └── name: :Bar - ├── child: - │ @ ConstantReadNode (location: (1,5)-(1,8)) - │ └── name: :Foo - └── delimiter_loc: (1,3)-(1,5) = "::" + ├── name: :Foo + ├── delimiter_loc: (1,3)-(1,5) = "::" + └── name_loc: (1,5)-(1,8) = "Foo" diff --git a/test/prism/snapshots/whitequark/const_toplevel.txt b/test/prism/snapshots/whitequark/const_toplevel.txt index b54b069d06..3d7df5defc 100644 --- a/test/prism/snapshots/whitequark/const_toplevel.txt +++ b/test/prism/snapshots/whitequark/const_toplevel.txt @@ -5,7 +5,6 @@ └── body: (length: 1) └── @ ConstantPathNode (location: (1,0)-(1,5)) ├── parent: ∅ - ├── child: - │ @ ConstantReadNode (location: (1,2)-(1,5)) - │ └── name: :Foo - └── delimiter_loc: (1,0)-(1,2) = "::" + ├── name: :Foo + ├── delimiter_loc: (1,0)-(1,2) = "::" + └── name_loc: (1,2)-(1,5) = "Foo" diff --git a/test/prism/snapshots/whitequark/cpath.txt b/test/prism/snapshots/whitequark/cpath.txt index b892525646..e801456bf7 100644 --- a/test/prism/snapshots/whitequark/cpath.txt +++ b/test/prism/snapshots/whitequark/cpath.txt @@ -9,10 +9,9 @@ │ ├── constant_path: │ │ @ ConstantPathNode (location: (1,7)-(1,12)) │ │ ├── parent: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (1,9)-(1,12)) - │ │ │ └── name: :Foo - │ │ └── delimiter_loc: (1,7)-(1,9) = "::" + │ │ ├── name: :Foo + │ │ ├── delimiter_loc: (1,7)-(1,9) = "::" + │ │ └── name_loc: (1,9)-(1,12) = "Foo" │ ├── body: ∅ │ ├── end_keyword_loc: (1,14)-(1,17) = "end" │ └── name: :Foo @@ -24,10 +23,9 @@ │ ├── parent: │ │ @ ConstantReadNode (location: (3,7)-(3,10)) │ │ └── name: :Bar - │ ├── child: - │ │ @ ConstantReadNode (location: (3,12)-(3,15)) - │ │ └── name: :Foo - │ └── delimiter_loc: (3,10)-(3,12) = "::" + │ ├── name: :Foo + │ ├── delimiter_loc: (3,10)-(3,12) = "::" + │ └── name_loc: (3,12)-(3,15) = "Foo" ├── body: ∅ ├── end_keyword_loc: (3,17)-(3,20) = "end" └── name: :Foo diff --git a/test/prism/snapshots/whitequark/dedenting_heredoc.txt b/test/prism/snapshots/whitequark/dedenting_heredoc.txt index acb79e83d2..67896b2415 100644 --- a/test/prism/snapshots/whitequark/dedenting_heredoc.txt +++ b/test/prism/snapshots/whitequark/dedenting_heredoc.txt @@ -15,7 +15,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ InterpolatedStringNode (location: (1,2)-(1,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: mutable │ │ ├── opening_loc: (1,2)-(1,8) = "<<~\"E\"" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (2,0)-(3,0)) @@ -30,7 +30,7 @@ │ │ │ │ │ @ StatementsNode (location: (3,4)-(3,9)) │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ StringNode (location: (3,4)-(3,9)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: frozen │ │ │ │ │ ├── opening_loc: (3,4)-(3,5) = "\"" │ │ │ │ │ ├── content_loc: (3,5)-(3,8) = " y" │ │ │ │ │ ├── closing_loc: (3,8)-(3,9) = "\"" diff --git a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt index c06818a98b..acaf9c052d 100644 --- a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt +++ b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt @@ -36,7 +36,7 @@ │ ├── opening_loc: (1,26)-(1,27) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (1,27)-(1,39)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 2) │ │ ├── @ LocalVariableReadNode (location: (1,27)-(1,35)) │ │ │ ├── name: :argument diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt index adaf111fd7..b4235fb20a 100644 --- a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt +++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt @@ -33,7 +33,7 @@ │ ├── opening_loc: (1,16)-(1,17) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (1,17)-(1,19)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (1,17)-(1,19)) │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt index a03421455c..33779abcc1 100644 --- a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt +++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt @@ -33,7 +33,7 @@ │ ├── opening_loc: (1,16)-(1,17) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (1,17)-(1,35)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (1,17)-(1,35)) │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/whitequark/keyword_argument_omission.txt b/test/prism/snapshots/whitequark/keyword_argument_omission.txt index 62e8fecf4e..446b45b56b 100644 --- a/test/prism/snapshots/whitequark/keyword_argument_omission.txt +++ b/test/prism/snapshots/whitequark/keyword_argument_omission.txt @@ -12,7 +12,7 @@ ├── opening_loc: (1,3)-(1,4) = "(" ├── arguments: │ @ ArgumentsNode (location: (1,4)-(1,10)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,4)-(1,10)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt b/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt index 6d0bdfb817..db281e2f0d 100644 --- a/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt +++ b/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt @@ -39,7 +39,7 @@ │ ├── opening_loc: (1,20)-(1,21) = "(" │ ├── arguments: │ │ @ ArgumentsNode (location: (1,21)-(1,23)) - │ │ ├── flags: contains_keyword_splat + │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (1,21)-(1,23)) │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/whitequark/masgn_const.txt b/test/prism/snapshots/whitequark/masgn_const.txt index 56169deb17..ddfccb0d8b 100644 --- a/test/prism/snapshots/whitequark/masgn_const.txt +++ b/test/prism/snapshots/whitequark/masgn_const.txt @@ -7,10 +7,9 @@ │ ├── lefts: (length: 2) │ │ ├── @ ConstantPathTargetNode (location: (1,0)-(1,3)) │ │ │ ├── parent: ∅ - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (1,2)-(1,3)) - │ │ │ │ └── name: :A - │ │ │ └── delimiter_loc: (1,0)-(1,2) = "::" + │ │ │ ├── name: :A + │ │ │ ├── delimiter_loc: (1,0)-(1,2) = "::" + │ │ │ └── name_loc: (1,2)-(1,3) = "A" │ │ └── @ LocalVariableTargetNode (location: (1,5)-(1,8)) │ │ ├── name: :foo │ │ └── depth: 0 @@ -28,10 +27,9 @@ │ ├── @ ConstantPathTargetNode (location: (3,0)-(3,7)) │ │ ├── parent: │ │ │ @ SelfNode (location: (3,0)-(3,4)) - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (3,6)-(3,7)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (3,4)-(3,6) = "::" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (3,4)-(3,6) = "::" + │ │ └── name_loc: (3,6)-(3,7) = "A" │ └── @ LocalVariableTargetNode (location: (3,9)-(3,12)) │ ├── name: :foo │ └── depth: 0 diff --git a/test/prism/snapshots/whitequark/newline_in_hash_argument.txt b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt index d5e89d87f9..7ef006645b 100644 --- a/test/prism/snapshots/whitequark/newline_in_hash_argument.txt +++ b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt @@ -102,7 +102,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (10,8)-(11,1)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (10,8)-(11,1)) │ │ ├── flags: symbol_keys @@ -141,7 +141,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (13,8)-(14,1)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (13,8)-(14,1)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/whitequark/op_asgn.txt b/test/prism/snapshots/whitequark/op_asgn.txt index 8f24a35ad0..f726617904 100644 --- a/test/prism/snapshots/whitequark/op_asgn.txt +++ b/test/prism/snapshots/whitequark/op_asgn.txt @@ -20,8 +20,8 @@ │ ├── message_loc: (1,4)-(1,5) = "A" │ ├── read_name: :A │ ├── write_name: :A= - │ ├── operator: :+ - │ ├── operator_loc: (1,6)-(1,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (1,6)-(1,8) = "+=" │ └── value: │ @ IntegerNode (location: (1,9)-(1,10)) │ ├── flags: decimal @@ -43,8 +43,8 @@ │ ├── message_loc: (3,4)-(3,5) = "a" │ ├── read_name: :a │ ├── write_name: :a= - │ ├── operator: :+ - │ ├── operator_loc: (3,6)-(3,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (3,6)-(3,8) = "+=" │ └── value: │ @ IntegerNode (location: (3,9)-(3,10)) │ ├── flags: decimal @@ -66,8 +66,8 @@ ├── message_loc: (5,5)-(5,6) = "a" ├── read_name: :a ├── write_name: :a= - ├── operator: :+ - ├── operator_loc: (5,7)-(5,9) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (5,7)-(5,9) = "+=" └── value: @ IntegerNode (location: (5,10)-(5,11)) ├── flags: decimal diff --git a/test/prism/snapshots/whitequark/op_asgn_cmd.txt b/test/prism/snapshots/whitequark/op_asgn_cmd.txt index 109c2fd2ed..d2d86b1bf9 100644 --- a/test/prism/snapshots/whitequark/op_asgn_cmd.txt +++ b/test/prism/snapshots/whitequark/op_asgn_cmd.txt @@ -20,8 +20,8 @@ │ ├── message_loc: (1,4)-(1,5) = "A" │ ├── read_name: :A │ ├── write_name: :A= - │ ├── operator: :+ - │ ├── operator_loc: (1,6)-(1,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (1,6)-(1,8) = "+=" │ └── value: │ @ CallNode (location: (1,9)-(1,14)) │ ├── flags: ignore_visibility @@ -63,8 +63,8 @@ │ ├── message_loc: (3,4)-(3,5) = "a" │ ├── read_name: :a │ ├── write_name: :a= - │ ├── operator: :+ - │ ├── operator_loc: (3,6)-(3,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (3,6)-(3,8) = "+=" │ └── value: │ @ CallNode (location: (3,9)-(3,14)) │ ├── flags: ignore_visibility @@ -103,11 +103,10 @@ │ │ │ ├── arguments: ∅ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (5,5)-(5,6)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (5,3)-(5,5) = "::" - │ ├── operator_loc: (5,7)-(5,9) = "+=" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (5,3)-(5,5) = "::" + │ │ └── name_loc: (5,5)-(5,6) = "A" + │ ├── binary_operator_loc: (5,7)-(5,9) = "+=" │ ├── value: │ │ @ CallNode (location: (5,10)-(5,15)) │ │ ├── flags: ignore_visibility @@ -132,7 +131,7 @@ │ │ │ └── block: ∅ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ - │ └── operator: :+ + │ └── binary_operator: :+ └── @ CallOperatorWriteNode (location: (7,0)-(7,15)) ├── flags: ∅ ├── receiver: @@ -150,8 +149,8 @@ ├── message_loc: (7,5)-(7,6) = "a" ├── read_name: :a ├── write_name: :a= - ├── operator: :+ - ├── operator_loc: (7,7)-(7,9) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (7,7)-(7,9) = "+=" └── value: @ CallNode (location: (7,10)-(7,15)) ├── flags: ignore_visibility diff --git a/test/prism/snapshots/whitequark/op_asgn_index.txt b/test/prism/snapshots/whitequark/op_asgn_index.txt index fe077fae13..b302abdafe 100644 --- a/test/prism/snapshots/whitequark/op_asgn_index.txt +++ b/test/prism/snapshots/whitequark/op_asgn_index.txt @@ -30,8 +30,8 @@ │ └── value: 1 ├── closing_loc: (1,8)-(1,9) = "]" ├── block: ∅ - ├── operator: :+ - ├── operator_loc: (1,10)-(1,12) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (1,10)-(1,12) = "+=" └── value: @ IntegerNode (location: (1,13)-(1,14)) ├── flags: decimal diff --git a/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt b/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt index 87082aad94..319ed1a51a 100644 --- a/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt +++ b/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt @@ -30,8 +30,8 @@ │ └── value: 1 ├── closing_loc: (1,8)-(1,9) = "]" ├── block: ∅ - ├── operator: :+ - ├── operator_loc: (1,10)-(1,12) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (1,10)-(1,12) = "+=" └── value: @ CallNode (location: (1,13)-(1,18)) ├── flags: ignore_visibility diff --git a/test/prism/snapshots/whitequark/parser_bug_525.txt b/test/prism/snapshots/whitequark/parser_bug_525.txt index a69b8a207f..3a31a97cdc 100644 --- a/test/prism/snapshots/whitequark/parser_bug_525.txt +++ b/test/prism/snapshots/whitequark/parser_bug_525.txt @@ -12,7 +12,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (1,3)-(1,11)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 1) │ └── @ KeywordHashNode (location: (1,3)-(1,11)) │ ├── flags: symbol_keys diff --git a/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt b/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt index b269104f30..840e5a4fc0 100644 --- a/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt +++ b/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt @@ -5,7 +5,7 @@ └── body: (length: 1) └── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,22)) ├── name_loc: (1,0)-(1,3) = "foo" - ├── operator_loc: (1,4)-(1,6) = "+=" + ├── binary_operator_loc: (1,4)-(1,6) = "+=" ├── value: │ @ RescueModifierNode (location: (1,7)-(1,22)) │ ├── expression: @@ -32,5 +32,5 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── name: :foo - ├── operator: :+ + ├── binary_operator: :+ └── depth: 0 diff --git a/test/prism/snapshots/whitequark/return.txt b/test/prism/snapshots/whitequark/return.txt index ddfbae85c8..5c98259103 100644 --- a/test/prism/snapshots/whitequark/return.txt +++ b/test/prism/snapshots/whitequark/return.txt @@ -4,9 +4,11 @@ @ StatementsNode (location: (1,0)-(7,11)) └── body: (length: 4) ├── @ ReturnNode (location: (1,0)-(1,6)) + │ ├── flags: ∅ │ ├── keyword_loc: (1,0)-(1,6) = "return" │ └── arguments: ∅ ├── @ ReturnNode (location: (3,0)-(3,10)) + │ ├── flags: ∅ │ ├── keyword_loc: (3,0)-(3,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (3,7)-(3,10)) @@ -23,6 +25,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ReturnNode (location: (5,0)-(5,8)) + │ ├── flags: ∅ │ ├── keyword_loc: (5,0)-(5,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (5,6)-(5,8)) @@ -33,6 +36,7 @@ │ ├── opening_loc: (5,6)-(5,7) = "(" │ └── closing_loc: (5,7)-(5,8) = ")" └── @ ReturnNode (location: (7,0)-(7,11)) + ├── flags: ∅ ├── keyword_loc: (7,0)-(7,6) = "return" └── arguments: @ ArgumentsNode (location: (7,6)-(7,11)) diff --git a/test/prism/snapshots/whitequark/return_block.txt b/test/prism/snapshots/whitequark/return_block.txt index 5b8141e4f3..36746a361b 100644 --- a/test/prism/snapshots/whitequark/return_block.txt +++ b/test/prism/snapshots/whitequark/return_block.txt @@ -4,6 +4,7 @@ @ StatementsNode (location: (1,0)-(1,21)) └── body: (length: 1) └── @ ReturnNode (location: (1,0)-(1,21)) + ├── flags: ∅ ├── keyword_loc: (1,0)-(1,6) = "return" └── arguments: @ ArgumentsNode (location: (1,7)-(1,21)) diff --git a/test/prism/snapshots/whitequark/ruby_bug_11380.txt b/test/prism/snapshots/whitequark/ruby_bug_11380.txt index b7a7ef9a98..d5ec10b06c 100644 --- a/test/prism/snapshots/whitequark/ruby_bug_11380.txt +++ b/test/prism/snapshots/whitequark/ruby_bug_11380.txt @@ -12,7 +12,7 @@ ├── opening_loc: ∅ ├── arguments: │ @ ArgumentsNode (location: (1,2)-(1,21)) - │ ├── flags: ∅ + │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ LambdaNode (location: (1,2)-(1,15)) │ │ ├── locals: [] diff --git a/test/prism/snapshots/whitequark/ruby_bug_12073.txt b/test/prism/snapshots/whitequark/ruby_bug_12073.txt index 9db30f6fec..2b4d3eab2a 100644 --- a/test/prism/snapshots/whitequark/ruby_bug_12073.txt +++ b/test/prism/snapshots/whitequark/ruby_bug_12073.txt @@ -21,7 +21,7 @@ │ ├── opening_loc: ∅ │ ├── arguments: │ │ @ ArgumentsNode (location: (1,9)-(1,13)) - │ │ ├── flags: ∅ + │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 1) │ │ └── @ KeywordHashNode (location: (1,9)-(1,13)) │ │ ├── flags: symbol_keys @@ -75,10 +75,9 @@ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (3,21)-(3,22)) │ │ │ │ └── name: :A - │ │ │ ├── child: - │ │ │ │ @ ConstantReadNode (location: (3,24)-(3,25)) - │ │ │ │ └── name: :B - │ │ │ └── delimiter_loc: (3,22)-(3,24) = "::" + │ │ │ ├── name: :B + │ │ │ ├── delimiter_loc: (3,22)-(3,24) = "::" + │ │ │ └── name_loc: (3,24)-(3,25) = "B" │ │ └── @ StringNode (location: (3,27)-(3,29)) │ │ ├── flags: ∅ │ │ ├── opening_loc: (3,27)-(3,28) = "'" diff --git a/test/prism/snapshots/whitequark/ruby_bug_12402.txt b/test/prism/snapshots/whitequark/ruby_bug_12402.txt index df5aea00c3..4935007f8a 100644 --- a/test/prism/snapshots/whitequark/ruby_bug_12402.txt +++ b/test/prism/snapshots/whitequark/ruby_bug_12402.txt @@ -5,7 +5,7 @@ └── body: (length: 14) ├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,27)) │ ├── name_loc: (1,0)-(1,3) = "foo" - │ ├── operator_loc: (1,4)-(1,6) = "+=" + │ ├── binary_operator_loc: (1,4)-(1,6) = "+=" │ ├── value: │ │ @ RescueModifierNode (location: (1,7)-(1,27)) │ │ ├── expression: @@ -36,11 +36,11 @@ │ │ └── rescue_expression: │ │ @ NilNode (location: (1,24)-(1,27)) │ ├── name: :foo - │ ├── operator: :+ + │ ├── binary_operator: :+ │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,28)) │ ├── name_loc: (3,0)-(3,3) = "foo" - │ ├── operator_loc: (3,4)-(3,6) = "+=" + │ ├── binary_operator_loc: (3,4)-(3,6) = "+=" │ ├── value: │ │ @ RescueModifierNode (location: (3,7)-(3,28)) │ │ ├── expression: @@ -71,7 +71,7 @@ │ │ └── rescue_expression: │ │ @ NilNode (location: (3,25)-(3,28)) │ ├── name: :foo - │ ├── operator: :+ + │ ├── binary_operator: :+ │ └── depth: 0 ├── @ LocalVariableWriteNode (location: (5,0)-(5,26)) │ ├── name: :foo @@ -151,8 +151,8 @@ │ ├── message_loc: (9,4)-(9,5) = "C" │ ├── read_name: :C │ ├── write_name: :C= - │ ├── operator: :+ - │ ├── operator_loc: (9,6)-(9,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (9,6)-(9,8) = "+=" │ └── value: │ @ RescueModifierNode (location: (9,9)-(9,29)) │ ├── expression: @@ -192,8 +192,8 @@ │ ├── message_loc: (11,4)-(11,5) = "C" │ ├── read_name: :C │ ├── write_name: :C= - │ ├── operator: :+ - │ ├── operator_loc: (11,6)-(11,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (11,6)-(11,8) = "+=" │ └── value: │ @ RescueModifierNode (location: (11,9)-(11,30)) │ ├── expression: @@ -233,8 +233,8 @@ │ ├── message_loc: (13,4)-(13,5) = "m" │ ├── read_name: :m │ ├── write_name: :m= - │ ├── operator: :+ - │ ├── operator_loc: (13,6)-(13,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (13,6)-(13,8) = "+=" │ └── value: │ @ RescueModifierNode (location: (13,9)-(13,29)) │ ├── expression: @@ -274,8 +274,8 @@ │ ├── message_loc: (15,4)-(15,5) = "m" │ ├── read_name: :m │ ├── write_name: :m= - │ ├── operator: :+ - │ ├── operator_loc: (15,6)-(15,8) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (15,6)-(15,8) = "+=" │ └── value: │ @ RescueModifierNode (location: (15,9)-(15,30)) │ ├── expression: @@ -312,10 +312,9 @@ │ │ │ @ LocalVariableReadNode (location: (17,0)-(17,3)) │ │ │ ├── name: :foo │ │ │ └── depth: 0 - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (17,5)-(17,6)) - │ │ │ └── name: :C - │ │ └── delimiter_loc: (17,3)-(17,5) = "::" + │ │ ├── name: :C + │ │ ├── delimiter_loc: (17,3)-(17,5) = "::" + │ │ └── name_loc: (17,5)-(17,6) = "C" │ ├── operator_loc: (17,7)-(17,10) = "||=" │ └── value: │ @ RescueModifierNode (location: (17,11)-(17,31)) @@ -353,10 +352,9 @@ │ │ │ @ LocalVariableReadNode (location: (19,0)-(19,3)) │ │ │ ├── name: :foo │ │ │ └── depth: 0 - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (19,5)-(19,6)) - │ │ │ └── name: :C - │ │ └── delimiter_loc: (19,3)-(19,5) = "::" + │ │ ├── name: :C + │ │ ├── delimiter_loc: (19,3)-(19,5) = "::" + │ │ └── name_loc: (19,5)-(19,6) = "C" │ ├── operator_loc: (19,7)-(19,10) = "||=" │ └── value: │ @ RescueModifierNode (location: (19,11)-(19,32)) @@ -397,8 +395,8 @@ │ ├── message_loc: (21,5)-(21,6) = "m" │ ├── read_name: :m │ ├── write_name: :m= - │ ├── operator: :+ - │ ├── operator_loc: (21,7)-(21,9) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (21,7)-(21,9) = "+=" │ └── value: │ @ RescueModifierNode (location: (21,10)-(21,30)) │ ├── expression: @@ -438,8 +436,8 @@ │ ├── message_loc: (23,5)-(23,6) = "m" │ ├── read_name: :m │ ├── write_name: :m= - │ ├── operator: :+ - │ ├── operator_loc: (23,7)-(23,9) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (23,7)-(23,9) = "+=" │ └── value: │ @ RescueModifierNode (location: (23,10)-(23,31)) │ ├── expression: @@ -486,8 +484,8 @@ │ │ └── value: 0 │ ├── closing_loc: (25,5)-(25,6) = "]" │ ├── block: ∅ - │ ├── operator: :+ - │ ├── operator_loc: (25,7)-(25,9) = "+=" + │ ├── binary_operator: :+ + │ ├── binary_operator_loc: (25,7)-(25,9) = "+=" │ └── value: │ @ RescueModifierNode (location: (25,10)-(25,30)) │ ├── expression: @@ -534,8 +532,8 @@ │ └── value: 0 ├── closing_loc: (27,5)-(27,6) = "]" ├── block: ∅ - ├── operator: :+ - ├── operator_loc: (27,7)-(27,9) = "+=" + ├── binary_operator: :+ + ├── binary_operator_loc: (27,7)-(27,9) = "+=" └── value: @ RescueModifierNode (location: (27,10)-(27,31)) ├── expression: diff --git a/test/prism/snapshots/whitequark/ruby_bug_12669.txt b/test/prism/snapshots/whitequark/ruby_bug_12669.txt index 86b021351b..6151143ba8 100644 --- a/test/prism/snapshots/whitequark/ruby_bug_12669.txt +++ b/test/prism/snapshots/whitequark/ruby_bug_12669.txt @@ -5,11 +5,11 @@ └── body: (length: 4) ├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,18)) │ ├── name_loc: (1,0)-(1,1) = "a" - │ ├── operator_loc: (1,2)-(1,4) = "+=" + │ ├── binary_operator_loc: (1,2)-(1,4) = "+=" │ ├── value: │ │ @ LocalVariableOperatorWriteNode (location: (1,5)-(1,18)) │ │ ├── name_loc: (1,5)-(1,6) = "b" - │ │ ├── operator_loc: (1,7)-(1,9) = "+=" + │ │ ├── binary_operator_loc: (1,7)-(1,9) = "+=" │ │ ├── value: │ │ │ @ CallNode (location: (1,10)-(1,18)) │ │ │ ├── flags: ignore_visibility @@ -31,14 +31,14 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ ├── name: :b - │ │ ├── operator: :+ + │ │ ├── binary_operator: :+ │ │ └── depth: 0 │ ├── name: :a - │ ├── operator: :+ + │ ├── binary_operator: :+ │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,17)) │ ├── name_loc: (3,0)-(3,1) = "a" - │ ├── operator_loc: (3,2)-(3,4) = "+=" + │ ├── binary_operator_loc: (3,2)-(3,4) = "+=" │ ├── value: │ │ @ LocalVariableWriteNode (location: (3,5)-(3,17)) │ │ ├── name: :b @@ -66,7 +66,7 @@ │ │ │ └── block: ∅ │ │ └── operator_loc: (3,7)-(3,8) = "=" │ ├── name: :a - │ ├── operator: :+ + │ ├── binary_operator: :+ │ └── depth: 0 ├── @ LocalVariableWriteNode (location: (5,0)-(5,17)) │ ├── name: :a @@ -75,7 +75,7 @@ │ ├── value: │ │ @ LocalVariableOperatorWriteNode (location: (5,4)-(5,17)) │ │ ├── name_loc: (5,4)-(5,5) = "b" - │ │ ├── operator_loc: (5,6)-(5,8) = "+=" + │ │ ├── binary_operator_loc: (5,6)-(5,8) = "+=" │ │ ├── value: │ │ │ @ CallNode (location: (5,9)-(5,17)) │ │ │ ├── flags: ignore_visibility @@ -97,7 +97,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ ├── name: :b - │ │ ├── operator: :+ + │ │ ├── binary_operator: :+ │ │ └── depth: 0 │ └── operator_loc: (5,2)-(5,3) = "=" └── @ LocalVariableWriteNode (location: (7,0)-(7,16)) diff --git a/test/prism/snapshots/whitequark/ruby_bug_9669.txt b/test/prism/snapshots/whitequark/ruby_bug_9669.txt index a73000bed0..910b08c3ef 100644 --- a/test/prism/snapshots/whitequark/ruby_bug_9669.txt +++ b/test/prism/snapshots/whitequark/ruby_bug_9669.txt @@ -24,6 +24,7 @@ │ │ @ StatementsNode (location: (2,0)-(2,6)) │ │ └── body: (length: 1) │ │ └── @ ReturnNode (location: (2,0)-(2,6)) + │ │ ├── flags: redundant │ │ ├── keyword_loc: (2,0)-(2,6) = "return" │ │ └── arguments: ∅ │ ├── locals: [:b] diff --git a/test/prism/snapshots/whitequark/send_attr_asgn.txt b/test/prism/snapshots/whitequark/send_attr_asgn.txt index 392ae5587e..1d09daa4a6 100644 --- a/test/prism/snapshots/whitequark/send_attr_asgn.txt +++ b/test/prism/snapshots/whitequark/send_attr_asgn.txt @@ -69,10 +69,9 @@ │ │ │ ├── arguments: ∅ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ - │ │ ├── child: - │ │ │ @ ConstantReadNode (location: (5,5)-(5,6)) - │ │ │ └── name: :A - │ │ └── delimiter_loc: (5,3)-(5,5) = "::" + │ │ ├── name: :A + │ │ ├── delimiter_loc: (5,3)-(5,5) = "::" + │ │ └── name_loc: (5,5)-(5,6) = "A" │ ├── operator_loc: (5,7)-(5,8) = "=" │ └── value: │ @ IntegerNode (location: (5,9)-(5,10)) diff --git a/test/prism/snapshots/whitequark/var_op_asgn.txt b/test/prism/snapshots/whitequark/var_op_asgn.txt index f423a62dee..f20f612fa2 100644 --- a/test/prism/snapshots/whitequark/var_op_asgn.txt +++ b/test/prism/snapshots/whitequark/var_op_asgn.txt @@ -6,30 +6,30 @@ ├── @ ClassVariableOperatorWriteNode (location: (1,0)-(1,11)) │ ├── name: :@@var │ ├── name_loc: (1,0)-(1,5) = "@@var" - │ ├── operator_loc: (1,6)-(1,8) = "|=" + │ ├── binary_operator_loc: (1,6)-(1,8) = "|=" │ ├── value: │ │ @ IntegerNode (location: (1,9)-(1,11)) │ │ ├── flags: decimal │ │ └── value: 10 - │ └── operator: :| + │ └── binary_operator: :| ├── @ InstanceVariableOperatorWriteNode (location: (3,0)-(3,7)) │ ├── name: :@a │ ├── name_loc: (3,0)-(3,2) = "@a" - │ ├── operator_loc: (3,3)-(3,5) = "|=" + │ ├── binary_operator_loc: (3,3)-(3,5) = "|=" │ ├── value: │ │ @ IntegerNode (location: (3,6)-(3,7)) │ │ ├── flags: decimal │ │ └── value: 1 - │ └── operator: :| + │ └── binary_operator: :| ├── @ LocalVariableOperatorWriteNode (location: (5,0)-(5,6)) │ ├── name_loc: (5,0)-(5,1) = "a" - │ ├── operator_loc: (5,2)-(5,4) = "+=" + │ ├── binary_operator_loc: (5,2)-(5,4) = "+=" │ ├── value: │ │ @ IntegerNode (location: (5,5)-(5,6)) │ │ ├── flags: decimal │ │ └── value: 1 │ ├── name: :a - │ ├── operator: :+ + │ ├── binary_operator: :+ │ └── depth: 0 └── @ DefNode (location: (7,0)-(7,23)) ├── name: :a @@ -42,12 +42,12 @@ │ └── @ ClassVariableOperatorWriteNode (location: (7,7)-(7,18)) │ ├── name: :@@var │ ├── name_loc: (7,7)-(7,12) = "@@var" - │ ├── operator_loc: (7,13)-(7,15) = "|=" + │ ├── binary_operator_loc: (7,13)-(7,15) = "|=" │ ├── value: │ │ @ IntegerNode (location: (7,16)-(7,18)) │ │ ├── flags: decimal │ │ └── value: 10 - │ └── operator: :| + │ └── binary_operator: :| ├── locals: [] ├── def_keyword_loc: (7,0)-(7,3) = "def" ├── operator_loc: ∅ diff --git a/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt b/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt index d56c099c7e..0bfa06d5b7 100644 --- a/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt +++ b/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt @@ -5,7 +5,7 @@ └── body: (length: 1) └── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,12)) ├── name_loc: (1,0)-(1,3) = "foo" - ├── operator_loc: (1,4)-(1,6) = "+=" + ├── binary_operator_loc: (1,4)-(1,6) = "+=" ├── value: │ @ CallNode (location: (1,7)-(1,12)) │ ├── flags: ignore_visibility @@ -24,5 +24,5 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── name: :foo - ├── operator: :+ + ├── binary_operator: :+ └── depth: 0 diff --git a/test/prism/unescape_test.rb b/test/prism/unescape_test.rb index 2a352c5234..3f78a59b11 100644 --- a/test/prism/unescape_test.rb +++ b/test/prism/unescape_test.rb @@ -38,7 +38,7 @@ module Prism end def prism(escape) - result = Prism.parse(code(escape)) + result = Prism.parse(code(escape), encoding: "binary") if result.success? yield result.value.statements.body.first diff --git a/test/prism/warnings_test.rb b/test/prism/warnings_test.rb index 7eb1bbd2e1..b138a3cb43 100644 --- a/test/prism/warnings_test.rb +++ b/test/prism/warnings_test.rb @@ -44,6 +44,7 @@ module Prism def test_duplicated_hash_key assert_warning("{ a: 1, a: 2 }", "duplicated and overwritten") + assert_warning("{ a: 1, **{ a: 2 } }", "duplicated and overwritten") end def test_duplicated_when_clause @@ -63,6 +64,15 @@ module Prism assert_warning("if true\nelsif\nfalse; end", "end of line") end + def test_shareable_constant_value + assert_warning("foo # shareable_constant_value: none", "ignored") + assert_warning("\v # shareable_constant_value: none", "ignored") + + refute_warning("# shareable_constant_value: none") + refute_warning(" # shareable_constant_value: none") + refute_warning("\t\t# shareable_constant_value: none") + end + def test_string_in_predicate assert_warning("if 'foo'; end", "string") assert_warning("if \"\#{foo}\"; end", "string") diff --git a/test/reline/test_config.rb b/test/reline/test_config.rb index 9ead047ce4..6068292847 100644 --- a/test/reline/test_config.rb +++ b/test/reline/test_config.rb @@ -216,6 +216,38 @@ class Reline::Config::Test < Reline::TestCase end end + def test_nested_if_else + @config.read_lines(<<~LINES.lines) + $if Ruby + "\x1": "O" + $if NotRuby + "\x2": "X" + $else + "\x3": "O" + $if Ruby + "\x4": "O" + $else + "\x5": "X" + $endif + "\x6": "O" + $endif + "\x7": "O" + $else + "\x8": "X" + $if NotRuby + "\x9": "X" + $else + "\xA": "X" + $endif + "\xB": "X" + $endif + "\xC": "O" + LINES + keys = [0x1, 0x3, 0x4, 0x6, 0x7, 0xC] + key_bindings = keys.to_h { |k| [[k.ord], ['O'.ord]] } + assert_equal(key_bindings, @config.instance_variable_get(:@additional_key_bindings)[:emacs]) + end + def test_unclosed_if e = assert_raise(Reline::Config::InvalidInputrc) do @config.read_lines(<<~LINES.lines, "INPUTRC") @@ -243,6 +275,78 @@ class Reline::Config::Test < Reline::TestCase assert_equal "INPUTRC:1: unmatched endif", e.message end + def test_if_with_mode + @config.read_lines(<<~LINES.lines) + $if mode=emacs + "\C-e": history-search-backward # comment + $else + "\C-f": history-search-forward + $endif + LINES + + assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command]) + end + + def test_else + @config.read_lines(<<~LINES.lines) + $if mode=vi + "\C-e": history-search-backward # comment + $else + "\C-f": history-search-forward + $endif + LINES + + assert_equal({[6] => :history_search_forward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command]) + end + + def test_if_with_invalid_mode + @config.read_lines(<<~LINES.lines) + $if mode=vim + "\C-e": history-search-backward + $else + "\C-f": history-search-forward # comment + $endif + LINES + + assert_equal({[6] => :history_search_forward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command]) + end + + def test_mode_label_differs_from_keymap_label + @config.read_lines(<<~LINES.lines) + # Sets mode_label and keymap_label to vi + set editing-mode vi + # Change keymap_label to emacs. mode_label is still vi. + set keymap emacs + # condition=true because current mode_label is vi + $if mode=vi + # sets keybinding to current keymap_label=emacs + "\C-e": history-search-backward + $endif + LINES + assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command]) + end + + def test_if_without_else_condition + @config.read_lines(<<~LINES.lines) + set editing-mode vi + $if mode=vi + "\C-e": history-search-backward + $endif + LINES + + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:emacs]) + assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert]) + assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command]) + end + def test_default_key_bindings @config.add_default_key_binding('abcd'.bytes, 'EFGH'.bytes) @config.read_lines(<<~'LINES'.lines) @@ -313,6 +417,18 @@ class Reline::Config::Test < Reline::TestCase assert_equal expected, @config.key_bindings end + def test_key_bindings_with_reset + # @config.reset is called after each readline. + # inputrc file is read once, so key binding shouldn't be cleared by @config.reset + @config.add_default_key_binding('default'.bytes, 'DEFAULT'.bytes) + @config.read_lines(<<~'LINES'.lines) + "additional": "ADDITIONAL" + LINES + @config.reset + expected = { 'default'.bytes => 'DEFAULT'.bytes, 'additional'.bytes => 'ADDITIONAL'.bytes } + assert_equal expected, @config.key_bindings + end + def test_history_size @config.read_lines(<<~LINES.lines) set history-size 5000 diff --git a/test/reline/test_key_actor_emacs.rb b/test/reline/test_key_actor_emacs.rb index a9baf9ad37..013ca2f7b3 100644 --- a/test/reline/test_key_actor_emacs.rb +++ b/test/reline/test_key_actor_emacs.rb @@ -787,9 +787,22 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase input_keys('b') input_keys("\C-i", false) assert_line_around_cursor('foo_ba', '') + input_keys("\C-h") + input_key_by_symbol(:complete) + assert_line_around_cursor('foo_ba', '') + input_keys("\C-h", false) + input_key_by_symbol(:menu_complete) + assert_line_around_cursor('foo_bar', '') + input_key_by_symbol(:menu_complete) + assert_line_around_cursor('foo_baz', '') + input_keys("\C-h", false) + input_key_by_symbol(:menu_complete_backward) + assert_line_around_cursor('foo_baz', '') + input_key_by_symbol(:menu_complete_backward) + assert_line_around_cursor('foo_bar', '') end - def test_autocompletion_with_upward_navigation + def test_autocompletion @config.autocompletion = true @line_editor.completion_proc = proc { |word| %w{ @@ -806,31 +819,14 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase assert_line_around_cursor('Readline', '') input_keys("\C-i", false) assert_line_around_cursor('Regexp', '') - @line_editor.input_key(Reline::Key.new(:completion_journey_up, :completion_journey_up, false)) + input_key_by_symbol(:completion_journey_up) assert_line_around_cursor('Readline', '') - ensure - @config.autocompletion = false - end - - def test_autocompletion_with_upward_navigation_and_menu_complete_backward - @config.autocompletion = true - @line_editor.completion_proc = proc { |word| - %w{ - Readline - Regexp - RegexpError - }.map { |i| - i.encode(@encoding) - } - } - input_keys('Re') - assert_line_around_cursor('Re', '') - input_keys("\C-i", false) - assert_line_around_cursor('Readline', '') - input_keys("\C-i", false) + input_key_by_symbol(:complete) assert_line_around_cursor('Regexp', '') - @line_editor.input_key(Reline::Key.new(:menu_complete_backward, :menu_complete_backward, false)) + input_key_by_symbol(:menu_complete_backward) assert_line_around_cursor('Readline', '') + input_key_by_symbol(:menu_complete) + assert_line_around_cursor('Regexp', '') ensure @config.autocompletion = false end @@ -1441,4 +1437,72 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase @line_editor.__send__(:vi_editing_mode, nil) assert(@config.editing_mode_is?(:vi_insert)) end + + def test_undo + input_keys("\C-_", false) + assert_line_around_cursor('', '') + input_keys("aあb\C-h\C-h\C-h", false) + assert_line_around_cursor('', '') + input_keys("\C-_", false) + assert_line_around_cursor('a', '') + input_keys("\C-_", false) + assert_line_around_cursor('aあ', '') + input_keys("\C-_", false) + assert_line_around_cursor('aあb', '') + input_keys("\C-_", false) + assert_line_around_cursor('aあ', '') + input_keys("\C-_", false) + assert_line_around_cursor('a', '') + input_keys("\C-_", false) + assert_line_around_cursor('', '') + end + + def test_undo_with_cursor_position + input_keys("abc\C-b\C-h", false) + assert_line_around_cursor('a', 'c') + input_keys("\C-_", false) + assert_line_around_cursor('ab', 'c') + input_keys("あいう\C-b\C-h", false) + assert_line_around_cursor('abあ', 'うc') + input_keys("\C-_", false) + assert_line_around_cursor('abあい', 'うc') + end + + def test_undo_with_multiline + @line_editor.multiline_on + @line_editor.confirm_multiline_termination_proc = proc {} + input_keys("1\n2\n3", false) + assert_whole_lines(["1", "2", "3"]) + assert_line_index(2) + assert_line_around_cursor('3', '') + input_keys("\C-p\C-h\C-h", false) + assert_whole_lines(["1", "3"]) + assert_line_index(0) + assert_line_around_cursor('1', '') + input_keys("\C-_", false) + assert_whole_lines(["1", "", "3"]) + assert_line_index(1) + assert_line_around_cursor('', '') + input_keys("\C-_", false) + assert_whole_lines(["1", "2", "3"]) + assert_line_index(1) + assert_line_around_cursor('2', '') + input_keys("\C-_", false) + assert_whole_lines(["1", "2", ""]) + assert_line_index(2) + assert_line_around_cursor('', '') + input_keys("\C-_", false) + assert_whole_lines(["1", "2"]) + assert_line_index(1) + assert_line_around_cursor('2', '') + end + + def test_undo_with_many_times + str = "a" + "b" * 100 + input_keys(str, false) + 100.times { input_keys("\C-_", false) } + assert_line_around_cursor('a', '') + input_keys("\C-_", false) + assert_line_around_cursor('a', '') + end end diff --git a/test/reline/test_line_editor.rb b/test/reline/test_line_editor.rb index bf688ac3c6..7a38ecd596 100644 --- a/test/reline/test_line_editor.rb +++ b/test/reline/test_line_editor.rb @@ -112,6 +112,36 @@ class Reline::LineEditor end end + def test_multibyte + base = [0, 12, '一二三一二三'] + left = [0, 3, 'LLL'] + right = [9, 3, 'RRR'] + front = [3, 6, 'FFFFFF'] + # 一 FFFFFF 三 + # 一二三一二三 + assert_output '[COL_2]二三一二' do + @line_editor.render_line_differential([base, front], [base, nil]) + end + + # LLLFFFFFF 三 + # LLL 三一二三 + assert_output '[COL_3] 三一二' do + @line_editor.render_line_differential([base, left, front], [base, left, nil]) + end + + # 一 FFFFFFRRR + # 一二三一 RRR + assert_output '[COL_2]二三一 ' do + @line_editor.render_line_differential([base, right, front], [base, right, nil]) + end + + # LLLFFFFFFRRR + # LLL 三一 RRR + assert_output '[COL_3] 三一 ' do + @line_editor.render_line_differential([base, left, right, front], [base, left, right, nil]) + end + end + def test_complicated state_a = [nil, [19, 7, 'bbbbbbb'], [15, 8, 'cccccccc'], [10, 5, 'ddddd'], [18, 4, 'eeee'], [1, 3, 'fff'], [17, 2, 'gg'], [7, 1, 'h']] state_b = [[5, 9, 'aaaaaaaaa'], nil, [15, 8, 'cccccccc'], nil, [18, 4, 'EEEE'], [25, 4, 'ffff'], [17, 2, 'gg'], [2, 2, 'hh']] diff --git a/test/reline/test_reline.rb b/test/reline/test_reline.rb index 40c880c11f..a20a5c9f44 100644 --- a/test/reline/test_reline.rb +++ b/test/reline/test_reline.rb @@ -378,10 +378,28 @@ class Reline::Test < Reline::TestCase assert_equal("Reline::GeneralIO", out.chomp) end + def test_require_reline_should_not_trigger_winsize + pend if win? + lib = File.expand_path("../../lib", __dir__) + code = <<~RUBY + require "io/console" + def STDIN.tty?; true; end + def STDOUT.tty?; true; end + def STDIN.winsize; raise; end + require("reline") && p(Reline.core.io_gate) + RUBY + out = IO.popen([{}, Reline.test_rubybin, "-I#{lib}", "-e", code], &:read) + assert_equal("Reline::ANSI", out.chomp) + end + + def win? + /mswin|mingw/.match?(RUBY_PLATFORM) + end + def get_reline_encoding if encoding = Reline.core.encoding encoding - elsif RUBY_PLATFORM =~ /mswin|mingw/ + elsif win? Encoding::UTF_8 else Encoding::default_external diff --git a/test/reline/test_unicode.rb b/test/reline/test_unicode.rb index 834f7114c4..deba4d4681 100644 --- a/test/reline/test_unicode.rb +++ b/test/reline/test_unicode.rb @@ -38,11 +38,16 @@ class Reline::Unicode::Test < Reline::TestCase assert_equal [["ab\e]0;1\ac", nil, "\e]0;1\ad"], 2], Reline::Unicode.split_by_width("ab\e]0;1\acd", 3) end + def test_split_by_width_csi_reset_sgr_optimization + assert_equal [["\e[1ma\e[mb\e[2mc", nil, "\e[2md\e[0me\e[3mf", nil, "\e[3mg"], 3], Reline::Unicode.split_by_width("\e[1ma\e[mb\e[2mcd\e[0me\e[3mfg", 3) + assert_equal [["\e[1ma\1\e[mzero\e[0m\2\e[2mb", nil, "\e[1m\e[2mc"], 2], Reline::Unicode.split_by_width("\e[1ma\1\e[mzero\e[0m\2\e[2mbc", 2) + end + def test_take_range assert_equal 'cdef', Reline::Unicode.take_range('abcdefghi', 2, 4) assert_equal 'あde', Reline::Unicode.take_range('abあdef', 2, 4) - assert_equal 'zerocdef', Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4) - assert_equal 'bzerocde', Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4) + assert_equal "\1zero\2cdef", Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4) + assert_equal "b\1zero\2cde", Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4) assert_equal "\e[31mcd\e[42mef", Reline::Unicode.take_range("\e[31mabcd\e[42mefg", 2, 4) assert_equal "\e]0;1\acd", Reline::Unicode.take_range("ab\e]0;1\acd", 2, 3) assert_equal 'いう', Reline::Unicode.take_range('あいうえお', 2, 4) @@ -62,4 +67,26 @@ class Reline::Unicode::Test < Reline::TestCase assert_equal 10, Reline::Unicode.calculate_width('あいうえお') assert_equal 10, Reline::Unicode.calculate_width('あいうえお', true) end + + def test_take_mbchar_range + assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4) + assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, padding: true) + assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_begin: true) + assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_end: true) + assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4) + assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, padding: true) + assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, cover_begin: true) + assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, cover_end: true) + assert_equal ['う', 4, 2], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4) + assert_equal [' う ', 3, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, padding: true) + assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_begin: true) + assert_equal ['うえ', 4, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_end: true) + assert_equal ['いう ', 2, 5], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_begin: true, padding: true) + assert_equal [' うえ', 3, 5], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_end: true, padding: true) + assert_equal [' うえお ', 3, 10], Reline::Unicode.take_mbchar_range('あいうえお', 3, 10, padding: true) + assert_equal [" \e[41mうえお\e[0m ", 3, 10], Reline::Unicode.take_mbchar_range("あい\e[41mうえお", 3, 10, padding: true) + assert_equal ["\e[41m \e[42mい\e[43m ", 1, 4], Reline::Unicode.take_mbchar_range("\e[41mあ\e[42mい\e[43mう", 1, 4, padding: true) + assert_equal ["\e[31mc\1ABC\2d\e[0mef", 2, 4], Reline::Unicode.take_mbchar_range("\e[31mabc\1ABC\2d\e[0mefghi", 2, 4) + assert_equal ["\e[41m \e[42mい\e[43m ", 1, 4], Reline::Unicode.take_mbchar_range("\e[41mあ\e[42mい\e[43mう", 1, 4, padding: true) + end end diff --git a/test/reline/test_within_pipe.rb b/test/reline/test_within_pipe.rb index a42ca755fc..4f05255301 100644 --- a/test/reline/test_within_pipe.rb +++ b/test/reline/test_within_pipe.rb @@ -23,7 +23,6 @@ class Reline::WithinPipeTest < Reline::TestCase @reader.close @output_writer.close @config.reset - @config.reset_default_key_bindings Reline.test_reset end diff --git a/test/reline/yamatanooroti/multiline_repl b/test/reline/yamatanooroti/multiline_repl index 66bcf51e1a..8b82be60f4 100755 --- a/test/reline/yamatanooroti/multiline_repl +++ b/test/reline/yamatanooroti/multiline_repl @@ -12,7 +12,7 @@ opt = OptionParser.new opt.on('--dynamic-prompt') { Reline.prompt_proc = proc { |lines| lines.each_with_index.map { |l, i| - '[%04d]> ' % i + "\e[1m[%04d]>\e[m " % i } } } @@ -143,6 +143,11 @@ opt.on('--complete') { %w{String ScriptError SyntaxError Signal}.select{ |c| c.start_with?(target) } } } +opt.on('--complete-menu-with-perfect-match') { + Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| + %w{abs abs2}.select{ |c| c.start_with?(target) } + } +} opt.on('--autocomplete') { Reline.autocompletion = true Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| @@ -217,7 +222,7 @@ rescue end begin - prompt = ENV['RELINE_TEST_PROMPT'] || 'prompt> ' + prompt = ENV['RELINE_TEST_PROMPT'] || "\e[1mprompt>\e[m " puts 'Multiline REPL.' while code = Reline.readmultiline(prompt, true) { |code| TerminationChecker.terminated?(code) } case code.chomp diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb index 09d064934a..37a1c1a193 100644 --- a/test/reline/yamatanooroti/test_rendering.rb +++ b/test/reline/yamatanooroti/test_rendering.rb @@ -543,15 +543,10 @@ begin EOC end - def test_enable_bracketed_paste + def test_bracketed_paste omit if Reline.core.io_gate.win? - write_inputrc <<~LINES - set enable-bracketed-paste on - LINES start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') - write("\e[200~,") - write("def hoge\n 3\nend") - write("\e[200~.") + write("\e[200~def hoge\r\t3\rend\e[201~") close assert_screen(<<~EOC) Multiline REPL. @@ -561,6 +556,19 @@ begin EOC end + def test_bracketed_paste_with_undo + omit if Reline.core.io_gate.win? + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write("abc") + write("\e[200~def hoge\r\t3\rend\e[201~") + write("\C-_") + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> abc + EOC + end + def test_backspace_until_returns_to_initial start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("ABC") @@ -945,18 +953,30 @@ begin EOC end - def test_with_newline + def test_eof_with_newline omit if Reline.core.io_gate.win? cmd = %Q{ruby -e 'print(%Q{abc def \\e\\r})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })'} start_terminal(40, 50, ['bash', '-c', cmd]) sleep 1 - close + close rescue nil assert_screen(<<~'EOC') > abc def "abc def " EOC end + def test_eof_without_newline + omit if Reline.core.io_gate.win? + cmd = %Q{ruby -e 'print(%{hello})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })'} + start_terminal(40, 50, ['bash', '-c', cmd]) + sleep 1 + close rescue nil + assert_screen(<<~'EOC') + > hello + "hello" + EOC + end + def test_em_set_mark_and_em_exchange_mark start_terminal(10, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("aaa bbb ccc ddd\M-b\M-b\M-\x20\M-b\C-x\C-xX\C-x\C-xY") @@ -1006,6 +1026,47 @@ begin EOC end + def test_completion_menu_is_displayed_horizontally + start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete}, startup_message: 'Multiline REPL.') + write("S\t\t") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> S + ScriptError String + Signal SyntaxError + EOC + end + + def test_show_all_if_ambiguous_on + write_inputrc <<~LINES + set show-all-if-ambiguous on + LINES + start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete}, startup_message: 'Multiline REPL.') + write("S\t") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> S + ScriptError String + Signal SyntaxError + EOC + end + + def test_show_all_if_ambiguous_on_and_menu_with_perfect_match + write_inputrc <<~LINES + set show-all-if-ambiguous on + LINES + start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete-menu-with-perfect-match}, startup_message: 'Multiline REPL.') + write("a\t") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> abs + abs abs2 + EOC + end + def test_simple_dialog iterate_over_face_configs do |config_name, config_file| start_terminal(20, 50, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.') @@ -1132,7 +1193,36 @@ begin assert_screen(<<~'EOC') Multiline REPL. prompt> St - r String + r + String + Struct + EOC + end + + def test_autocomplete_target_at_end_of_line + start_terminal(20, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write(' ') + write('Str') + write("\C-i") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> Str + ing String + Struct + EOC + end + + def test_autocomplete_completed_input_is_wrapped + start_terminal(20, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write(' ') + write('Str') + write("\C-i") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> Stri + ng String Struct EOC end diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb index cbae6e7ed5..dc94da01ee 100644 --- a/test/ripper/test_parser_events.rb +++ b/test/ripper/test_parser_events.rb @@ -269,15 +269,27 @@ class TestRipper::ParserEvents < Test::Unit::TestCase def test_assign_error_backref thru_assign_error = false result = - parse('$` = 1', :on_assign_error) {thru_assign_error = true} + parse('$& = 1', :on_assign_error) {thru_assign_error = true} assert_equal true, thru_assign_error - assert_equal '[assign(assign_error(var_field($`)),1)]', result + assert_equal '[assign(assign_error(var_field($&)),1)]', result thru_assign_error = false result = - parse('$`, _ = 1', :on_assign_error) {thru_assign_error = true} + parse('$&, _ = 1', :on_assign_error) {thru_assign_error = true} assert_equal true, thru_assign_error - assert_equal '[massign([assign_error(var_field($`)),var_field(_)],1)]', result + assert_equal '[massign([assign_error(var_field($&)),var_field(_)],1)]', result + + thru_assign_error = false + result = + parse('$& += 1', :on_assign_error) {thru_assign_error = true} + assert_equal true, thru_assign_error + assert_equal '[assign_error(opassign(var_field($&),+=,1))]', result + + thru_assign_error = false + result = + parse('$& += cmd 1, 2', :on_assign_error) {thru_assign_error = true} + assert_equal true, thru_assign_error + assert_equal '[assign_error(opassign(var_field($&),+=,command(cmd,[1,2])))]', result end def test_assign_error_const_qualified diff --git a/test/ripper/test_ripper.rb b/test/ripper/test_ripper.rb index 6061496d9c..f129a41be9 100644 --- a/test/ripper/test_ripper.rb +++ b/test/ripper/test_ripper.rb @@ -170,6 +170,14 @@ end end; end + def test_sexp_no_memory_leak + assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true) + 1_000_000.times do + Ripper.sexp("") + end + end; + end + class TestInput < self Input = Struct.new(:lines) do def gets diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 29da607fc5..45c6b63963 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -337,6 +337,8 @@ class TestAst < Test::Unit::TestCase end def test_node_id_for_location + omit if compiling_with_prism? + exception = begin raise rescue => e @@ -356,6 +358,8 @@ class TestAst < Test::Unit::TestCase end def test_of_proc_and_method + omit if compiling_with_prism? + proc = Proc.new { 1 + 2 } method = self.method(__method__) @@ -385,6 +389,8 @@ class TestAst < Test::Unit::TestCase end def test_of_backtrace_location + omit if compiling_with_prism? + backtrace_location, lineno = sample_backtrace_location node = RubyVM::AbstractSyntaxTree.of(backtrace_location) assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node) @@ -396,6 +402,8 @@ class TestAst < Test::Unit::TestCase end def test_of_proc_and_method_under_eval + omit if compiling_with_prism? + keep_script_lines_back = RubyVM.keep_script_lines RubyVM.keep_script_lines = false @@ -425,6 +433,7 @@ class TestAst < Test::Unit::TestCase end def test_of_proc_and_method_under_eval_with_keep_script_lines + omit if compiling_with_prism? pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO keep_script_lines_back = RubyVM.keep_script_lines @@ -456,6 +465,8 @@ class TestAst < Test::Unit::TestCase end def test_of_backtrace_location_under_eval + omit if compiling_with_prism? + keep_script_lines_back = RubyVM.keep_script_lines RubyVM.keep_script_lines = false @@ -474,6 +485,7 @@ class TestAst < Test::Unit::TestCase end def test_of_backtrace_location_under_eval_with_keep_script_lines + omit if compiling_with_prism? pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO keep_script_lines_back = RubyVM.keep_script_lines @@ -736,6 +748,8 @@ dummy end def test_keep_script_lines_for_of + omit if compiling_with_prism? + proc = Proc.new { 1 + 2 } method = self.method(__method__) @@ -1238,6 +1252,24 @@ dummy end end + def test_memory_leak + assert_no_memory_leak([], "#{<<~"begin;"}", "\n#{<<~'end;'}", rss: true) + begin; + 1_000_000.times do + eval("") + end + end; + end + + private + + # We can't revisit instruction sequences to find node ids if the prism + # compiler was used instead of the parse.y compiler. In that case, we'll omit + # some tests. + def compiling_with_prism? + RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism + end + def assert_error_tolerant(src, expected, keep_tokens: false) begin verbose_bak, $VERBOSE = $VERBOSE, false diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index dbf91fe8c9..df4b6651b0 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -347,11 +347,17 @@ class TestISeq < Test::Unit::TestCase end end assert_equal([m1, e1.message], [m2, e2.message], feature11951) - message = e1.message.each_line - message.with_index(1) do |line, i| - next if /^ / =~ line - assert_send([line, :start_with?, __FILE__], - proc {message.map {|l, j| (i == j ? ">" : " ") + l}.join("")}) + + if e1.message.lines[0] == "#{__FILE__}:#{line}: syntax errors found\n" + # Prism lays out the error messages in line with the source, so the + # following assertions do not make sense in that context. + else + message = e1.message.each_line + message.with_index(1) do |line, i| + next if /^ / =~ line + assert_send([line, :start_with?, __FILE__], + proc {message.map {|l, j| (i == j ? ">" : " ") + l}.join("")}) + end end end @@ -850,4 +856,11 @@ class TestISeq < Test::Unit::TestCase RubyVM::InstructionSequence.compile_prism(Object.new) end end + + def test_load_from_binary_only_accepts_string_param + assert_raise(TypeError) do + var_0 = 0 + RubyVM::InstructionSequence.load_from_binary(var_0) + end + end end diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb index 907360b3a6..a6493374b5 100644 --- a/test/ruby/test_m17n.rb +++ b/test/ruby/test_m17n.rb @@ -1091,7 +1091,7 @@ class TestM17N < Test::Unit::TestCase assert_nil(e("\xa1\xa2\xa3\xa4").rindex(e("\xa3"))) s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4") - a_with_e = /EUC-JP and ASCII-8BIT/ + a_with_e = /EUC-JP and BINARY \(ASCII-8BIT\)/ assert_raise_with_message(Encoding::CompatibilityError, a_with_e) do s.index(a("\xb1\xa3")) end @@ -1099,7 +1099,7 @@ class TestM17N < Test::Unit::TestCase s.rindex(a("\xb1\xa3")) end - a_with_e = /ASCII-8BIT regexp with EUC-JP string/ + a_with_e = /BINARY \(ASCII-8BIT\) regexp with EUC-JP string/ assert_raise_with_message(Encoding::CompatibilityError, a_with_e) do s.index(Regexp.new(a("\xb1\xa3"))) end diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb index 79d9577737..bcd8892f23 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -570,13 +570,19 @@ class TestMarshal < Test::Unit::TestCase def test_class_ivar assert_raise(TypeError) {Marshal.load("\x04\x08Ic\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")} assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")} - assert_not_operator(TestClass, :instance_variable_defined?, :@bug) + assert_not_operator(TestClass, :instance_variable_defined?, :@ivar_bug) + + assert_raise(TypeError) {Marshal.load("\x04\x08[\x07c\x1bTestMarshal::TestClassI@\x06\x06:\x0e@ivar_bug\"\x08bug")} + assert_not_operator(TestClass, :instance_variable_defined?, :@ivar_bug) end def test_module_ivar assert_raise(TypeError) {Marshal.load("\x04\x08Im\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")} assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")} - assert_not_operator(TestModule, :instance_variable_defined?, :@bug) + assert_not_operator(TestModule, :instance_variable_defined?, :@ivar_bug) + + assert_raise(TypeError) {Marshal.load("\x04\x08[\x07m\x1cTestMarshal::TestModuleI@\x06\x06:\x0e@ivar_bug\"\x08bug")} + assert_not_operator(TestModule, :instance_variable_defined?, :@ivar_bug) end class TestForRespondToFalse diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index fe649cddb9..3857f3cc17 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -18,7 +18,7 @@ class TestParse < Test::Unit::TestCase end def test_else_without_rescue - assert_syntax_error(<<-END, %r":#{__LINE__+2}: else without rescue"o, [__FILE__, __LINE__+1]) + assert_syntax_error(<<-END, %r"(:#{__LINE__+2}:|#{__LINE__+2} \|.+?\n.+?\^~.+?;) else without rescue"o, [__FILE__, __LINE__+1]) begin else 42 @@ -555,34 +555,42 @@ class TestParse < Test::Unit::TestCase mesg = 'from the backslash through the invalid char' e = assert_syntax_error('"\xg1"', /hex escape/) - assert_equal(' ^~'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^~(?!~)/, e.message.lines.last, mesg) e = assert_syntax_error('"\u{1234"', 'unterminated Unicode escape') - assert_equal(' ^'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^(?!~)/, e.message.lines.last, mesg) e = assert_syntax_error('"\u{xxxx}"', 'invalid Unicode escape') - assert_equal(' ^'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^(?!~)/, e.message.lines.last, mesg) e = assert_syntax_error('"\u{xxxx', 'Unicode escape') - assert_pattern_list([ - /.*: invalid Unicode escape\n.*\n/, - / \^/, - /\n/, - /.*: unterminated Unicode escape\n.*\n/, - / \^/, - /\n/, - /.*: unterminated string.*\n.*\n/, - / \^\n/, - ], e.message) + if e.message.lines.first == "#{__FILE__}:#{__LINE__ - 1}: syntax errors found\n" + assert_pattern_list([ + /\s+\| \^ unterminated string;.+\n/, + /\s+\| \^ unterminated Unicode escape\n/, + /\s+\| \^ invalid Unicode escape sequence\n/, + ], e.message.lines[2..-1].join) + else + assert_pattern_list([ + /.*: invalid Unicode escape\n.*\n/, + / \^/, + /\n/, + /.*: unterminated Unicode escape\n.*\n/, + / \^/, + /\n/, + /.*: unterminated string.*\n.*\n/, + / \^\n/, + ], e.message) + end e = assert_syntax_error('"\M1"', /escape character syntax/) - assert_equal(' ^~~'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^~~(?!~)/, e.message.lines.last, mesg) e = assert_syntax_error('"\C1"', /escape character syntax/) - assert_equal(' ^~~'"\n", e.message.lines.last, mesg) + assert_match(/(^|\| ) \^~~(?!~)/, e.message.lines.last, mesg) src = '"\xD0\u{90'"\n""000000000000000000000000" - assert_syntax_error(src, /:#{__LINE__}: unterminated/o) + assert_syntax_error(src, /(:#{__LINE__}:|> #{__LINE__} \|.+) unterminated/om) assert_syntax_error('"\u{100000000}"', /invalid Unicode escape/) assert_equal("", eval('"\u{}"')) @@ -605,22 +613,22 @@ class TestParse < Test::Unit::TestCase assert_syntax_error("\"\\C-\\M-\x01\"", 'Invalid escape character syntax') e = assert_syntax_error('"\c\u0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\c\U0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\C-\u0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\C-\U0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\M-\u0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last) e = assert_syntax_error('"\M-\U0000"', 'Invalid escape character syntax') - assert_equal(' ^~~~~'"\n", e.message.lines.last) + assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last) e = assert_syntax_error(%["\\C-\u3042"], 'Invalid escape character syntax') - assert_match(/^\s \^(?# \\ ) ~(?# C ) ~(?# - ) ~+(?# U+3042 )$/x, e.message.lines.last) + assert_match(/(^|\|\s)\s \^(?# \\ ) ~(?# C ) ~(?# - ) ~+(?# U+3042 )($|\s)/x, e.message.lines.last) assert_not_include(e.message, "invalid multibyte char") end @@ -875,7 +883,8 @@ x = __ENCODING__ $test_parse_foobarbazqux = nil assert_equal(nil, $&) assert_equal(nil, eval('alias $& $preserve_last_match')) - assert_syntax_error('a = $#', /as a global variable name\na = \$\#\n \^~$/) + assert_syntax_error('a = $#', /as a global variable name/) + assert_syntax_error('a = $#', /a = \$\#\n(^|.+?\| ) \^~(?!~)/) end def test_invalid_instance_variable @@ -1259,8 +1268,8 @@ x = __ENCODING__ assert_syntax_error("def f r:def d; def f 0end", /unexpected/) end; - assert_syntax_error("def\nf(000)end", /^ \^~~/) - assert_syntax_error("def\nf(&0)end", /^ \^/) + assert_syntax_error("def\nf(000)end", /(^|\| ) \^~~/) + assert_syntax_error("def\nf(&0)end", /(^|\| ) \^/) end def test_method_location_in_rescue @@ -1336,17 +1345,21 @@ x = __ENCODING__ end def test_unexpected_token_after_numeric - assert_syntax_error('0000xyz', /^ \^~~\Z/) - assert_syntax_error('1.2i1.1', /^ \^~~\Z/) - assert_syntax_error('1.2.3', /^ \^~\Z/) + assert_syntax_error('0000xyz', /(^|\| ) \^~~(?!~)/) + assert_syntax_error('1.2i1.1', /(^|\| ) \^~~(?!~)/) + assert_syntax_error('1.2.3', /(^|\| ) \^~(?!~)/) assert_syntax_error('1.', /unexpected end-of-input/) assert_syntax_error('1e', /expecting end-of-input/) end def test_truncated_source_line - e = assert_syntax_error("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 0123456789012345678901234567890123456789", + lineno = __LINE__ + 1 + e = assert_syntax_error("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 123456789012345678901234567890123456789", /unexpected local variable or method/) + line = e.message.lines[1] + line.delete_prefix!("> #{lineno} | ") if line.start_with?(">") + assert_operator(line, :start_with?, "...") assert_operator(line, :end_with?, "...\n") end @@ -1390,11 +1403,11 @@ x = __ENCODING__ end def test_unexpected_eof - assert_syntax_error('unless', /^ \^\Z/) + assert_syntax_error('unless', /(^|\| ) \^(?!~)/) end def test_location_of_invalid_token - assert_syntax_error('class xxx end', /^ \^~~\Z/) + assert_syntax_error('class xxx end', /(^|\| ) \^~~(?!~)/) end def test_whitespace_warning diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index db6ad06b82..cfe3bd1e19 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -1331,7 +1331,7 @@ END end assert_block do - case {} + case C.new({}) in {} C.keys == nil end diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index c996b1785a..c72029ca80 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -1827,6 +1827,14 @@ class TestRegexp < Test::Unit::TestCase end; end + def test_bug_20453 + re = Regexp.new("^(a*)x$", timeout: 0.001) + + assert_raise(Regexp::TimeoutError) do + re =~ "a" * 1000000 + "x" + end + end + def per_instance_redos_test(global_timeout, per_instance_timeout, expected_timeout) assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") global_timeout = #{ EnvUtil.apply_timeout_scale(global_timeout).inspect } diff --git a/test/ruby/test_rubyvm.rb b/test/ruby/test_rubyvm.rb index d729aa5af8..053a914a8a 100644 --- a/test/ruby/test_rubyvm.rb +++ b/test/ruby/test_rubyvm.rb @@ -32,6 +32,7 @@ class TestRubyVM < Test::Unit::TestCase end def test_keep_script_lines + omit if compiling_with_prism? pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO prev_conf = RubyVM.keep_script_lines @@ -68,4 +69,12 @@ class TestRubyVM < Test::Unit::TestCase ensure RubyVM.keep_script_lines = prev_conf end + + private + + # RubyVM.keep_script_lines does not mean anything in the context of prism, so + # we should omit tests that are looking for that functionality. + def compiling_with_prism? + RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism + end end diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb index 4170f62899..41b71097d1 100644 --- a/test/ruby/test_symbol.rb +++ b/test/ruby/test_symbol.rb @@ -121,6 +121,7 @@ class TestSymbol < Test::Unit::TestCase def test_inspect_under_gc_compact_stress omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077 omit "very flaky on many platforms, more so with YJIT enabled" if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? + omit "very flaky on many platforms, more so with RJIT enabled" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? EnvUtil.under_gc_compact_stress do assert_inspect_evaled(':testing') end diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 44162f06cb..56b97789e5 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -889,6 +889,16 @@ e" assert_dedented_heredoc(expect, result) end + def test_dedented_heredoc_with_leading_blank_line + # the blank line has six leading spaces + result = " \n" \ + " b\n" + expect = " \n" \ + "b\n" + assert_dedented_heredoc(expect, result) + end + + def test_dedented_heredoc_with_blank_more_indented_line_escaped result = " a\n" \ "\\ \\ \\ \\ \\ \\ \n" \ @@ -996,7 +1006,7 @@ eom end def test_dedented_heredoc_concatenation - assert_equal("\n0\n1", eval("<<~0 '1'\n \n0\#{}\n0")) + assert_equal(" \n0\n1", eval("<<~0 '1'\n \n0\#{}\n0")) end def test_heredoc_mixed_encoding @@ -1238,6 +1248,20 @@ eom assert_syntax_error("a&.x,=0", /multiple assignment destination/) end + def test_safe_call_in_for_variable + assert_valid_syntax("for x&.bar in []; end") + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + foo = nil + for foo&.bar in [1]; end + assert_nil(foo) + + foo = Struct.new(:bar).new + for foo&.bar in [1]; end + assert_equal(1, foo.bar) + end; + end + def test_no_warning_logop_literal assert_warning("") do eval("true||raise;nil") @@ -1577,7 +1601,7 @@ eom end def test_syntax_error_at_newline - expected = "\n ^" + expected = /(\n|\| ) \^/ assert_syntax_error("%[abcdef", expected) assert_syntax_error("%[abcdef\n", expected) end @@ -1755,8 +1779,8 @@ eom assert_equal("instance ok", k.new.rescued("ok")) # Current technical limitation: cannot prepend "private" or something for command endless def - error = /syntax error, unexpected string literal/ - error2 = /syntax error, unexpected local variable or method/ + error = /(syntax error,|\^~*) unexpected string literal/ + error2 = /(syntax error,|\^~*) unexpected local variable or method/ assert_syntax_error('private def foo = puts "Hello"', error) assert_syntax_error('private def foo() = puts "Hello"', error) assert_syntax_error('private def foo(x) = puts x', error2) diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index df71dbffc0..796787e355 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -1458,7 +1458,7 @@ class TestYJIT < Test::Unit::TestCase end def test_str_concat_encoding_mismatch - assert_compiles(<<~'RUBY', result: "incompatible character encodings: ASCII-8BIT and EUC-JP") + assert_compiles(<<~'RUBY', result: "incompatible character encodings: BINARY (ASCII-8BIT) and EUC-JP") def bar(a, b) a << b rescue => e diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb index f97306717d..7014843bba 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -76,8 +76,6 @@ class Gem::TestCase < Test::Unit::TestCase attr_accessor :uri # :nodoc: - @@tempdirs = [] - def assert_activate(expected, *specs) specs.each do |spec| case spec @@ -451,9 +449,7 @@ class Gem::TestCase < Test::Unit::TestCase Dir.chdir @current_dir - FileUtils.rm_rf @tempdir - - restore_env + ENV.replace(@orig_env) Gem::ConfigFile.send :remove_const, :SYSTEM_WIDE_CONFIG_FILE Gem::ConfigFile.send :const_set, :SYSTEM_WIDE_CONFIG_FILE, @@ -481,12 +477,9 @@ class Gem::TestCase < Test::Unit::TestCase @back_ui.close - refute_directory_exists @tempdir, "may be still in use" - ghosts = @@tempdirs.filter_map do |test_name, tempdir| - test_name if File.exist?(tempdir) - end - @@tempdirs << [method_name, @tempdir] - assert_empty ghosts + FileUtils.rm_rf @tempdir + + refute_directory_exists @tempdir, "#{@tempdir} used by test #{method_name} is still in use" end def credential_setup @@ -541,6 +534,16 @@ class Gem::TestCase < Test::Unit::TestCase ENV["BUNDLE_GEMFILE"] = File.join(@tempdir, "Gemfile") end + def with_env(overrides, &block) + orig_env = ENV.to_h + ENV.replace(overrides) + begin + block.call + ensure + ENV.replace(orig_env) + end + end + ## # A git_gem is used with a gem dependencies file. The gem created here # has no files, just a gem specification for the given +name+ and +version+. @@ -1526,23 +1529,6 @@ Also, a list: PUBLIC_KEY = nil PUBLIC_CERT = nil end if Gem::HAVE_OPENSSL - - private - - def restore_env - unless Gem.win_platform? - ENV.replace(@orig_env) - return - end - - # Fallback logic for Windows below to workaround - # https://bugs.ruby-lang.org/issues/16798. Can be dropped once all - # supported rubies include the fix for that. - - ENV.clear - - @orig_env.each {|k, v| ENV[k] = v } - end end # https://github.com/seattlerb/minitest/blob/13c48a03d84a2a87855a4de0c959f96800100357/lib/minitest/mock.rb#L192 diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index 244b7749a5..40a473f8d6 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -516,7 +516,10 @@ class TestGem < Gem::TestCase Gem.clear_paths - assert_nil Gem::Specification.send(:class_variable_get, :@@all) + with_env("GEM_HOME" => "foo", "GEM_PATH" => "bar") do + assert_equal("foo", Gem.dir) + assert_equal("bar", Gem.path.first) + end end def test_self_configuration @@ -1281,7 +1284,6 @@ class TestGem < Gem::TestCase def test_self_try_activate_missing_extensions spec = util_spec "ext", "1" do |s| s.extensions = %w[ext/extconf.rb] - s.mark_version s.installed_by_version = v("2.2") end diff --git a/test/rubygems/test_gem_ci_detector.rb b/test/rubygems/test_gem_ci_detector.rb index 3caefce97d..a28ee49f4b 100644 --- a/test/rubygems/test_gem_ci_detector.rb +++ b/test/rubygems/test_gem_ci_detector.rb @@ -3,7 +3,7 @@ require_relative "helper" require "rubygems" -class TestCiDetector < Test::Unit::TestCase +class TestCiDetector < Gem::TestCase def test_ci? with_env("FOO" => "bar") { assert_equal(false, Gem::CIDetector.ci?) } with_env("CI" => "true") { assert_equal(true, Gem::CIDetector.ci?) } @@ -29,16 +29,4 @@ class TestCiDetector < Test::Unit::TestCase assert_equal(["dsari", "taskcluster"], Gem::CIDetector.ci_strings) end end - - private - - def with_env(overrides, &block) - @orig_env = ENV.to_h - ENV.replace(overrides) - begin - block.call - ensure - ENV.replace(@orig_env) - end - end end diff --git a/test/rubygems/test_gem_commands_rebuild_command.rb b/test/rubygems/test_gem_commands_rebuild_command.rb index 5e8c797e2d..3b7927c44e 100644 --- a/test/rubygems/test_gem_commands_rebuild_command.rb +++ b/test/rubygems/test_gem_commands_rebuild_command.rb @@ -105,7 +105,7 @@ class TestGemCommandsRebuildCommand < Gem::TestCase assert_equal old_spec.name, new_spec.name assert_equal old_spec.summary, new_spec.summary - reproduced + [reproduced, original] end def test_build_is_reproducible @@ -134,12 +134,21 @@ class TestGemCommandsRebuildCommand < Gem::TestCase # also testing that `gem rebuild` overrides the value. ENV["SOURCE_DATE_EPOCH"] = Time.new(2007, 8, 9, 10, 11, 12).to_s - rebuild_gem_file = util_test_rebuild_gem(@gem, [@gem_name, @gem_version], original_gem_file, gemspec_file, timestamp) + rebuild_gem_file, saved_gem_file = + util_test_rebuild_gem(@gem, [@gem_name, @gem_version], original_gem_file, gemspec_file, timestamp) rebuild_contents = File.read(rebuild_gem_file) assert_equal build_contents, rebuild_contents ensure ENV["SOURCE_DATE_EPOCH"] = epoch + if rebuild_gem_file + File.unlink(rebuild_gem_file) + dir = File.dirname(rebuild_gem_file) + Dir.rmdir(dir) + File.unlink(saved_gem_file) + Dir.rmdir(File.dirname(saved_gem_file)) + Dir.rmdir(File.dirname(dir)) + end end end diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb index 43f695f147..8eedb6c03a 100644 --- a/test/rubygems/test_gem_commands_setup_command.rb +++ b/test/rubygems/test_gem_commands_setup_command.rb @@ -159,6 +159,23 @@ class TestGemCommandsSetupCommand < Gem::TestCase end end + def test_destdir_flag_regenerates_binstubs + # install to destdir + destdir = File.join(@tempdir, "foo") + gem_bin_path = gem_install "destdir-only-gem", install_dir: destdir + + # change binstub manually + write_file gem_bin_path do |io| + io.puts "I changed it!" + end + + @cmd.options[:destdir] = destdir + @cmd.options[:prefix] = "/" + @cmd.execute + + assert_match(/\A#!/, File.read(gem_bin_path)) + end + def test_files_in assert_equal %w[rubygems.rb rubygems/requirement.rb rubygems/ssl_certs/rubygems.org/foo.pem], @cmd.files_in("lib").sort @@ -412,7 +429,7 @@ class TestGemCommandsSetupCommand < Gem::TestCase end end - def gem_install(name) + def gem_install(name, **options) gem = util_spec name do |s| s.executables = [name] s.files = %W[bin/#{name}] @@ -420,8 +437,8 @@ class TestGemCommandsSetupCommand < Gem::TestCase write_file File.join @tempdir, "bin", name do |f| f.puts "#!/usr/bin/ruby" end - install_gem gem - File.join @gemhome, "bin", name + install_gem gem, **options + File.join options[:install_dir] || @gemhome, "bin", name end def gem_install_with_plugin(name) diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock index b9fed0e2b0..abd1e0ae33 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock @@ -152,18 +152,18 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.94" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06dab8dbb0beb0a575a80c4b46355c8ace1f3dc5df60a3109758f205f1061366" +checksum = "47d30bcad206b51f2f66121190ca678dce1fdf3a2eae0ac5d838d1818b19bdf5" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.94" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164d44950a42f2ba2f94efdcb650e14764270f84d281352aebb261806da0b2ce" +checksum = "3cbd92f281615f3c2dcb9dcb0f0576624752afbf9a7f99173b37c4b55b62dd8a" dependencies = [ "bindgen", "lazy_static", diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml index 9f844b62be..ad3e7f9b76 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.94" +rb-sys = "0.9.97" diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock index 7e1617a663..1d174f569e 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock @@ -145,18 +145,18 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.94" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06dab8dbb0beb0a575a80c4b46355c8ace1f3dc5df60a3109758f205f1061366" +checksum = "47d30bcad206b51f2f66121190ca678dce1fdf3a2eae0ac5d838d1818b19bdf5" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.94" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164d44950a42f2ba2f94efdcb650e14764270f84d281352aebb261806da0b2ce" +checksum = "3cbd92f281615f3c2dcb9dcb0f0576624752afbf9a7f99173b37c4b55b62dd8a" dependencies = [ "bindgen", "lazy_static", diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml index a84cc8aabb..60cf49ce03 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.94" +rb-sys = "0.9.97" diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb index 4469750f9a..a3f95bb770 100644 --- a/test/rubygems/test_gem_package_tar_header.rb +++ b/test/rubygems/test_gem_package_tar_header.rb @@ -99,6 +99,31 @@ class TestGemPackageTarHeader < Gem::Package::TarTestCase assert_empty @tar_header end + def test_empty + @tar_header = Gem::Package::TarHeader.from(StringIO.new(Gem::Package::TarHeader::EMPTY_HEADER)) + + assert_empty @tar_header + assert_equal Gem::Package::TarHeader.new( + checksum: 0, + devmajor: 0, + devminor: 0, + empty: true, + gid: 0, + gname: "", + linkname: "", + magic: "", + mode: 0, + mtime: 0, + name: "", + prefix: "", + size: 0, + typeflag: "0", + uid: 0, + uname: "", + version: 0, + ), @tar_header + end + def test_equals2 assert_equal @tar_header, @tar_header assert_equal @tar_header, @tar_header.dup diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index e4bf882317..00e48498c6 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -145,6 +145,9 @@ class TestGemPlatform < Gem::TestCase "x86_64-openbsd3.9" => ["x86_64", "openbsd", "3.9"], "x86_64-openbsd4.0" => ["x86_64", "openbsd", "4.0"], "x86_64-openbsd" => ["x86_64", "openbsd", nil], + "wasm32-wasi" => ["wasm32", "wasi", nil], + "wasm32-wasip1" => ["wasm32", "wasi", nil], + "wasm32-wasip2" => ["wasm32", "wasi", nil], } test_cases.each do |arch, expected| diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 9e05649f7c..9395e34f75 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -56,7 +56,6 @@ end s.add_dependency "jabber4r", "> 0.0.0" s.add_dependency "pqa", ["> 0.4", "<= 0.6"] - s.mark_version s.files = %w[lib/code.rb] end end @@ -69,7 +68,6 @@ end s.license = "MIT" s.platform = platform - s.mark_version s.files = %w[lib/code.rb] s.installed_by_version = v("2.2") end @@ -96,7 +94,6 @@ end s.requirements << "A working computer" s.license = "MIT" - s.mark_version s.files = %w[lib/code.rb] end @@ -970,7 +967,10 @@ dependencies: [] def test_self_stubs_for_lazy_loading Gem.loaded_specs.clear - Gem::Specification.class_variable_set(:@@stubs, nil) + + specification_record = Gem::Specification.specification_record + + specification_record.instance_variable_set(:@stubs, nil) dir_standard_specs = File.join Gem.dir, "specifications" @@ -978,9 +978,9 @@ dependencies: [] save_gemspec("b-1", "1", dir_standard_specs) {|s| s.name = "b" } assert_equal ["a-1"], Gem::Specification.stubs_for("a").map(&:full_name) - assert_equal 1, Gem::Specification.class_variable_get(:@@stubs_by_name).length + assert_equal 1, specification_record.instance_variable_get(:@stubs_by_name).length assert_equal ["b-1"], Gem::Specification.stubs_for("b").map(&:full_name) - assert_equal 2, Gem::Specification.class_variable_get(:@@stubs_by_name).length + assert_equal 2, specification_record.instance_variable_get(:@stubs_by_name).length assert_equal( Gem::Specification.stubs_for("a").map(&:object_id), @@ -989,7 +989,7 @@ dependencies: [] Gem.loaded_specs.delete "a" Gem.loaded_specs.delete "b" - Gem::Specification.class_variable_set(:@@stubs, nil) + specification_record.instance_variable_set(:@stubs, nil) end def test_self_stubs_for_no_lazy_loading_after_all_specs_setup @@ -3187,7 +3187,7 @@ or set it to nil if you don't want to specify a license. end def test_removed_methods - assert_equal Gem::Specification::REMOVED_METHODS, [:rubyforge_project=] + assert_equal Gem::Specification::REMOVED_METHODS, [:rubyforge_project=, :mark_version] end def test_validate_removed_rubyforge_project @@ -3480,12 +3480,17 @@ Did you mean 'Ruby'? util_setup_validate @a1.rubygems_version = "3" - e = assert_raise Gem::InvalidSpecificationException do + + use_ui @ui do @a1.validate end - assert_equal "expected RubyGems version #{Gem::VERSION}, was 3", - e.message + expected = <<~EXPECTED + #{w}: expected RubyGems version #{Gem::VERSION}, was 3 + #{w}: See https://guides.rubygems.org/specification-reference/ for help + EXPECTED + + assert_equal expected, @ui.error end def test_validate_specification_version diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index 9e0c1aa3d8..aa5ab0ed67 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -188,22 +188,81 @@ class TestGemUninstaller < Gem::InstallerTestCase refute File.exist?(plugin_path), "plugin not removed" end - def test_remove_plugins_with_install_dir + def test_uninstall_with_install_dir_removes_plugins write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io| io.write "# do nothing" end @spec.files += %w[lib/rubygems_plugin.rb] - Gem::Installer.at(Gem::Package.build(@spec), force: true).install + package = Gem::Package.build(@spec) + + Gem::Installer.at(package, force: true).install plugin_path = File.join Gem.plugindir, "a_plugin.rb" assert File.exist?(plugin_path), "plugin not written" - Dir.mkdir "#{@gemhome}2" - Gem::Uninstaller.new(nil, install_dir: "#{@gemhome}2").remove_plugins @spec + install_dir = "#{@gemhome}2" + + Gem::Installer.at(package, force: true, install_dir: install_dir).install + + install_dir_plugin_path = File.join install_dir, "plugins/a_plugin.rb" + assert File.exist?(install_dir_plugin_path), "plugin not written" + + Gem::Specification.dirs = [install_dir] + Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall assert File.exist?(plugin_path), "plugin unintentionally removed" + refute File.exist?(install_dir_plugin_path), "plugin not removed" + end + + def test_uninstall_with_install_dir_regenerates_plugins + write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io| + io.write "# do nothing" + end + + @spec.files += %w[lib/rubygems_plugin.rb] + + install_dir = "#{@gemhome}2" + + package = Gem::Package.build(@spec) + + spec_v9 = @spec.dup + spec_v9.version = "9" + package_v9 = Gem::Package.build(spec_v9) + + Gem::Installer.at(package, force: true, install_dir: install_dir).install + Gem::Installer.at(package_v9, force: true, install_dir: install_dir).install + + install_dir_plugin_path = File.join install_dir, "plugins/a_plugin.rb" + assert File.exist?(install_dir_plugin_path), "plugin not written" + + Gem::Specification.dirs = [install_dir] + Gem::Uninstaller.new(@spec.name, version: "9", executables: true, install_dir: install_dir).uninstall + assert File.exist?(install_dir_plugin_path), "plugin unintentionally removed" + + Gem::Specification.dirs = [install_dir] + Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall + refute File.exist?(install_dir_plugin_path), "plugin not removed" + end + + def test_remove_plugins_user_installed + write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io| + io.write "# do nothing" + end + + @spec.files += %w[lib/rubygems_plugin.rb] + + Gem::Installer.at(Gem::Package.build(@spec), force: true, user_install: true).install + + plugin_path = File.join Gem.user_dir, "plugins/a_plugin.rb" + assert File.exist?(plugin_path), "plugin not written" + + Gem::Specification.dirs = [Gem.dir, Gem.user_dir] + + Gem::Uninstaller.new(@spec.name, executables: true, force: true, user_install: true).uninstall + + refute File.exist?(plugin_path), "plugin not removed" end def test_regenerate_plugins_for @@ -370,7 +429,7 @@ create_makefile '#{@spec.name}' end def test_uninstall_user_install - @user_spec = Gem::Specification.find_by_name "b" + Gem::Specification.dirs = [Gem.user_dir] uninstaller = Gem::Uninstaller.new(@user_spec.name, executables: true, @@ -394,6 +453,32 @@ create_makefile '#{@spec.name}' assert_same uninstaller, @post_uninstall_hook_arg end + def test_uninstall_user_install_with_symlinked_home + pend "Symlinks not supported or not enabled" unless symlink_supported? + + Gem::Specification.dirs = [Gem.user_dir] + + symlinked_home = File.join(@tempdir, "new-home") + FileUtils.ln_s(Gem.user_home, symlinked_home) + + ENV["HOME"] = symlinked_home + Gem.instance_variable_set(:@user_home, nil) + Gem.instance_variable_set(:@data_home, nil) + + uninstaller = Gem::Uninstaller.new(@user_spec.name, + executables: true, + user_install: true, + force: true) + + gem_dir = File.join @user_spec.gem_dir + + assert_path_exist gem_dir + + uninstaller.uninstall + + assert_path_not_exist gem_dir + end + def test_uninstall_wrong_repo Dir.mkdir "#{@gemhome}2" Gem.use_paths "#{@gemhome}2", [@gemhome] diff --git a/test/rubygems/test_webauthn_poller.rb b/test/rubygems/test_webauthn_poller.rb index 23290d8ea1..fd24081758 100644 --- a/test/rubygems/test_webauthn_poller.rb +++ b/test/rubygems/test_webauthn_poller.rb @@ -13,7 +13,7 @@ class WebauthnPollerTest < Gem::TestCase @fetcher = Gem::FakeFetcher.new Gem::RemoteFetcher.fetcher = @fetcher @credentials = { - email: "email@example.com", + identifier: "email@example.com", password: "password", } end @@ -121,4 +121,14 @@ class WebauthnPollerTest < Gem::TestCase assert_equal error.message, "Security device verification failed: The token in the link you used has either expired or been used already." end + + def test_poll_for_otp_missing_credentials + @credentials = { password: "password" } + + error = assert_raise Gem::WebauthnVerificationError do + Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials) + end + + assert_equal error.message, "Security device verification failed: Provided missing credentials" + end end diff --git a/test/test_delegate.rb b/test/test_delegate.rb index 57480b18ea..f7bedf37fb 100644 --- a/test/test_delegate.rb +++ b/test/test_delegate.rb @@ -3,14 +3,6 @@ require 'test/unit' require 'delegate' class TestDelegateClass < Test::Unit::TestCase - module PP - def mu_pp(obj) - str = super - str = "#<#{obj.class}: #{str}>" if Delegator === obj - str - end - end - module M attr_reader :m end @@ -215,7 +207,6 @@ class TestDelegateClass < Test::Unit::TestCase end def test_eql? - extend PP s0 = SimpleDelegator.new("foo") s1 = SimpleDelegator.new("bar") s2 = SimpleDelegator.new("foo") diff --git a/test/test_ipaddr.rb b/test/test_ipaddr.rb index e95b0d2d7a..3e5c3d2aa4 100644 --- a/test/test_ipaddr.rb +++ b/test/test_ipaddr.rb @@ -20,18 +20,21 @@ class TC_IPAddr < Test::Unit::TestCase a = IPAddr.new assert_equal("::", a.to_s) assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string) + assert_equal("::/128", a.cidr) assert_equal(Socket::AF_INET6, a.family) assert_equal(128, a.prefix) a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678") assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s) assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string) + assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678/128", a.cidr) assert_equal(Socket::AF_INET6, a.family) assert_equal(128, a.prefix) a = IPAddr.new("3ffe:505:2::/48") assert_equal("3ffe:505:2::", a.to_s) assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) + assert_equal("3ffe:505:2::/48", a.cidr) assert_equal(Socket::AF_INET6, a.family) assert_equal(false, a.ipv4?) assert_equal(true, a.ipv6?) @@ -41,6 +44,7 @@ class TC_IPAddr < Test::Unit::TestCase a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::") assert_equal("3ffe:505:2::", a.to_s) assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) + assert_equal("3ffe:505:2::/48", a.cidr) assert_equal(Socket::AF_INET6, a.family) assert_equal(48, a.prefix) assert_nil(a.zone_id) @@ -58,12 +62,14 @@ class TC_IPAddr < Test::Unit::TestCase a = IPAddr.new("0.0.0.0") assert_equal("0.0.0.0", a.to_s) assert_equal("0.0.0.0", a.to_string) + assert_equal("0.0.0.0/32", a.cidr) assert_equal(Socket::AF_INET, a.family) assert_equal(32, a.prefix) a = IPAddr.new("192.168.1.2") assert_equal("192.168.1.2", a.to_s) assert_equal("192.168.1.2", a.to_string) + assert_equal("192.168.1.2/32", a.cidr) assert_equal(Socket::AF_INET, a.family) assert_equal(true, a.ipv4?) assert_equal(false, a.ipv6?) @@ -72,6 +78,7 @@ class TC_IPAddr < Test::Unit::TestCase a = IPAddr.new("192.168.1.2/26") assert_equal("192.168.1.0", a.to_s) assert_equal("192.168.1.0", a.to_string) + assert_equal("192.168.1.0/26", a.cidr) assert_equal(Socket::AF_INET, a.family) assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.192>", a.inspect) assert_equal(26, a.prefix) @@ -79,6 +86,7 @@ class TC_IPAddr < Test::Unit::TestCase a = IPAddr.new("192.168.1.2/255.255.255.0") assert_equal("192.168.1.0", a.to_s) assert_equal("192.168.1.0", a.to_string) + assert_equal("192.168.1.0/24", a.cidr) assert_equal(Socket::AF_INET, a.family) assert_equal(24, a.prefix) diff --git a/test/win32/test_registry.rb b/test/win32/test_registry.rb new file mode 100644 index 0000000000..02cafc09b0 --- /dev/null +++ b/test/win32/test_registry.rb @@ -0,0 +1,97 @@ +if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM + begin + require 'win32/registry' + rescue LoadError + else + require 'test/unit' + end +end + +if defined?(Win32::Registry) + class TestWin32Registry < Test::Unit::TestCase + COMPUTERNAME = 'SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName' + VOLATILE_ENVIRONMENT = 'Volatile Environment' + + def test_predefined + assert_predefined_key Win32::Registry::HKEY_CLASSES_ROOT + assert_predefined_key Win32::Registry::HKEY_CURRENT_USER + assert_predefined_key Win32::Registry::HKEY_LOCAL_MACHINE + assert_predefined_key Win32::Registry::HKEY_USERS + assert_predefined_key Win32::Registry::HKEY_PERFORMANCE_DATA + assert_predefined_key Win32::Registry::HKEY_PERFORMANCE_TEXT + assert_predefined_key Win32::Registry::HKEY_PERFORMANCE_NLSTEXT + assert_predefined_key Win32::Registry::HKEY_CURRENT_CONFIG + assert_predefined_key Win32::Registry::HKEY_DYN_DATA + end + + def test_class_open + name1, keys1 = Win32::Registry.open(Win32::Registry::HKEY_LOCAL_MACHINE, "SYSTEM") do |reg| + assert_predicate reg, :open? + [reg.name, reg.keys] + end + name2, keys2 = Win32::Registry::HKEY_LOCAL_MACHINE.open("SYSTEM") do |reg| + assert_predicate reg, :open? + [reg.name, reg.keys] + end + assert_equal name1, name2 + assert_equal keys1, keys2 + end + + def test_read + computername = ENV['COMPUTERNAME'] + Win32::Registry::HKEY_LOCAL_MACHINE.open(COMPUTERNAME) do |reg| + assert_equal computername, reg['ComputerName'] + assert_equal [Win32::Registry::REG_SZ, computername], reg.read('ComputerName') + assert_raise(TypeError) {reg.read('ComputerName', Win32::Registry::REG_DWORD)} + end + end + + def test_create + desired = Win32::Registry::KEY_ALL_ACCESS + option = Win32::Registry::REG_OPTION_VOLATILE + Win32::Registry::HKEY_CURRENT_USER.open(VOLATILE_ENVIRONMENT, desired) do |reg| + v = self.class.unused_value(reg) + begin + reg.create(v, desired, option) {} + ensure + reg.delete_key(v, true) + end + end + end + + def test_write + desired = Win32::Registry::KEY_ALL_ACCESS + Win32::Registry::HKEY_CURRENT_USER.open(VOLATILE_ENVIRONMENT, desired) do |reg| + v = self.class.unused_value(reg) + begin + reg.write_s(v, "data") + assert_equal [Win32::Registry::REG_SZ, "data"], reg.read(v) + reg.write_i(v, 0x5fe79027) + assert_equal [Win32::Registry::REG_DWORD, 0x5fe79027], reg.read(v) + ensure + reg.delete(v) + end + end + end + + private + + def assert_predefined_key(key) + assert_kind_of Win32::Registry, key + assert_predicate key, :open? + assert_not_predicate key, :created? + end + + class << self + def unused_value(reg, prefix = "Test_", limit = 100, fail: true) + limit.times do + v = + rand(0x100000).to_s(36) + reg.read(v) + rescue + return v + end + omit "Unused value not found in #{reg}" if fail + end + end + end +end diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index 502ccceec5..15e5bd852f 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -801,7 +801,8 @@ if defined? Zlib gz.write "hi" gz.close - File.open(Dir.mktmpdir, File::RDWR | File::TMPFILE) do |io| + tmpdir = Dir.mktmpdir("zlib_file_tmpfile") + File.open(tmpdir, File::RDWR | File::TMPFILE) do |io| io.write sio.string io.rewind @@ -825,6 +826,8 @@ if defined? Zlib omit 'O_TMPFILE not supported (EISDIR)' rescue Errno::EOPNOTSUPP omit 'O_TMPFILE not supported (EOPNOTSUPP)' + ensure + Dir.rmdir(tmpdir) if tmpdir end end end @@ -988,6 +991,25 @@ if defined? Zlib assert_raise(ArgumentError) { f.read(-1) } assert_equal(str, f.read) end + + Zlib::GzipReader.open(t.path) do |f| + s = "".b + + assert_raise(ArgumentError) { f.read(-1, s) } + + assert_same s, f.read(1, s) + assert_equal "\xE3".b, s + + assert_same s, f.read(2, s) + assert_equal "\x81\x82".b, s + + assert_same s, f.read(6, s) + assert_equal "\u3044\u3046".b, s + + assert_nil f.read(1, s) + assert_equal "".b, s + assert_predicate f, :eof? + end } end @@ -1002,10 +1024,14 @@ if defined? Zlib Zlib::GzipReader.open(t.path) do |f| s = "".dup - f.readpartial(3, s) + assert_same s, f.readpartial(3, s) assert("foo".start_with?(s)) assert_raise(ArgumentError) { f.readpartial(-1) } + + assert_same s, f.readpartial(3, s) + + assert_predicate f, :eof? end } end diff --git a/thread_pthread.c b/thread_pthread.c index 82b5e362cc..b9421559f2 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -2569,10 +2569,7 @@ ubf_wakeup_thread(rb_thread_t *th) { RUBY_DEBUG_LOG("th:%u thread_id:%p", rb_th_serial(th), (void *)th->nt->thread_id); - int r = pthread_kill(th->nt->thread_id, SIGVTALRM); - if (r != 0) { - rb_bug_errno("pthread_kill", r); - } + pthread_kill(th->nt->thread_id, SIGVTALRM); } static void diff --git a/tool/bundler/dev_gems.rb b/tool/bundler/dev_gems.rb index acf7335578..1422cfc7a5 100644 --- a/tool/bundler/dev_gems.rb +++ b/tool/bundler/dev_gems.rb @@ -7,7 +7,7 @@ gem "rake", "~> 13.1" gem "rb_sys" gem "webrick", "~> 1.6" -gem "turbo_tests", "= 2.1.0" +gem "turbo_tests", "~> 2.2.3" gem "parallel_tests", "< 3.9.0" gem "parallel", "~> 1.19" gem "rspec-core", "~> 3.12" diff --git a/tool/lib/_tmpdir.rb b/tool/lib/_tmpdir.rb new file mode 100644 index 0000000000..fd429dab37 --- /dev/null +++ b/tool/lib/_tmpdir.rb @@ -0,0 +1,100 @@ +template = "rubytest." + +# This path is only for tests. +# Assume the directory by these environment variables are safe. +base = [ENV["TMPDIR"], ENV["TMP"], "/tmp"].find do |tmp| + next unless tmp and tmp.size <= 50 and File.directory?(tmp) + # On macOS, the default TMPDIR is very long, inspite of UNIX socket + # path length is limited. + # + # Also Rubygems creates its own temporary directory per tests, and + # some tests copy the full path of gemhome there. In that caes, the + # path contains both temporary names twice, and can exceed path name + # limit very easily. + tmp +end +begin + tmpdir = File.join(base, template + Random.new_seed.to_s(36)[-6..-1]) + Dir.mkdir(tmpdir, 0o700) +rescue Errno::EEXIST + retry +end +# warn "tmpdir(#{tmpdir.size}) = #{tmpdir}" + +pid = $$ +END { + if pid == $$ + begin + Dir.rmdir(tmpdir) + rescue Errno::ENOENT + rescue Errno::ENOTEMPTY + require_relative "colorize" + colorize = Colorize.new + ls = Struct.new(:colorize) do + def mode_inspect(m, s) + [ + (m & 0o4 == 0 ? ?- : ?r), + (m & 0o2 == 0 ? ?- : ?w), + (m & 0o1 == 0 ? (s ? s.upcase : ?-) : (s || ?x)), + ] + end + def decorate_path(path, st) + case + when st.directory? + color = "bold;blue" + type = "/" + when st.symlink? + color = "bold;cyan" + # type = "@" + when st.executable? + color = "bold;green" + type = "*" + when path.end_with?(".gem") + color = "green" + end + colorize.decorate(path, color) + (type || "") + end + def list_tree(parent, indent = "", &block) + children = Dir.children(parent).map do |child| + [child, path = File.join(parent, child), File.lstat(path)] + end + nlink_width = children.map {|child, path, st| st.nlink}.max.to_s.size + size_width = children.map {|child, path, st| st.size}.max.to_s.size + + children.each do |child, path, st| + m = st.mode + m = [ + (st.file? ? ?- : st.ftype[0]), + mode_inspect(m >> 6, (?s unless m & 04000 == 0)), + mode_inspect(m >> 3, (?s unless m & 02000 == 0)), + mode_inspect(m, (?t unless m & 01000 == 0)), + ].join("") + warn sprintf("%s* %s %*d %*d %s % s%s", + indent, m, nlink_width, st.nlink, size_width, st.size, + st.mtime.to_s, decorate_path(child, st), + (" -> " + decorate_path(File.readlink(path), File.stat(path)) if + st.symlink?)) + if st.directory? + list_tree(File.join(parent, child), indent + " ", &block) + end + yield path, st if block + end + end + end.new(colorize) + warn colorize.notice("Children under ")+colorize.fail(tmpdir)+":" + Dir.chdir(tmpdir) do + ls.list_tree(".") do |path, st| + if st.directory? + Dir.rmdir(path) + else + File.unlink(path) + end + end + end + require "fileutils" + FileUtils.rm_rf(tmpdir) + end + end +} + +ENV["TMPDIR"] = ENV["SPEC_TEMP_DIR"] = ENV["GEM_TEST_TMPDIR"] = tmpdir diff --git a/tool/lib/output.rb b/tool/lib/output.rb index 5c645daca6..8cb426ae4a 100644 --- a/tool/lib/output.rb +++ b/tool/lib/output.rb @@ -4,10 +4,15 @@ require_relative 'colorize' class Output attr_reader :path, :vpath - def initialize - @path = @timestamp = @ifchange = @color = nil - @overwrite = @create_only = false - @vpath = VPath.new + def initialize(path: nil, timestamp: nil, ifchange: nil, color: nil, + overwrite: false, create_only: false, vpath: VPath.new) + @path = path + @timestamp = timestamp + @ifchange = ifchange + @color = color + @overwrite = overwrite + @create_only = create_only + @vpath = vpath end COLOR_WHEN = { diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index d758b5fb02..2b0856b822 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -37,6 +37,26 @@ module Test class PendedError < AssertionFailedError; end + class << self + ## + # Extract the location where the last assertion method was + # called. Returns "<empty>" if _e_ does not have backtrace, or + # an empty string if no assertion method location was found. + + def location e + last_before_assertion = nil + + return '<empty>' unless e&.backtrace # SystemStackError can return nil. + + e.backtrace.reverse_each do |s| + break if s =~ /:in \W(?:.*\#)?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ + last_before_assertion = s + end + return "" unless last_before_assertion + /:in / =~ last_before_assertion ? $` : last_before_assertion + end + end + module Order class NoSort def initialize(seed) @@ -1778,15 +1798,7 @@ module Test end def location e # :nodoc: - last_before_assertion = "" - - return '<empty>' unless e&.backtrace # SystemStackError can return nil. - - e.backtrace.reverse_each do |s| - break if s =~ /in .(?:Test::Unit::(?:Core)?Assertions#)?(assert|refute|flunk|pass|fail|raise|must|wont)/ - last_before_assertion = s - end - last_before_assertion.sub(/:in .*$/, '') + Test::Unit.location e end ## diff --git a/tool/lib/test/unit/assertions.rb b/tool/lib/test/unit/assertions.rb index b4f1dbc176..aad422f7e7 100644 --- a/tool/lib/test/unit/assertions.rb +++ b/tool/lib/test/unit/assertions.rb @@ -768,7 +768,14 @@ EOT e = assert_raise(SyntaxError, mesg) do syntax_check(src, fname, line) end - assert_match(error, e.message, mesg) + + # Prism adds ANSI escape sequences to syntax error messages to + # colorize and format them. We strip them out here to make them easier + # to match against in tests. + message = e.message + message.gsub!(/\e\[.*?m/, "") + + assert_match(error, message, mesg) e end end diff --git a/tool/lrama/lib/lrama/command.rb b/tool/lrama/lib/lrama/command.rb index 94e86c6c94..12fc4fc7ec 100644 --- a/tool/lrama/lib/lrama/command.rb +++ b/tool/lrama/lib/lrama/command.rb @@ -47,6 +47,11 @@ module Lrama puts grammar.rules end + if options.trace_opts && options.trace_opts[:actions] + puts "Grammar rules with actions:" + grammar.rules.each { |rule| puts rule.with_actions } + end + File.open(options.outfile, "w+") do |f| Lrama::Output.new( out: f, diff --git a/tool/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb b/tool/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb index 1923e7819c..d8f3ae7897 100644 --- a/tool/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb +++ b/tool/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb @@ -13,8 +13,12 @@ module Lrama @rules << rule end - def find(token) - select_rules(token).last + def find_rule(token) + select_rules(@rules, token).last + end + + def find_inline(token) + @rules.select { |rule| rule.name == token.s_value && rule.is_inline }.last end def created_lhs(lhs_s_value) @@ -23,8 +27,9 @@ module Lrama private - def select_rules(token) - rules = select_rules_by_name(token.rule_name) + def select_rules(rules, token) + rules = select_not_inline_rules(rules) + rules = select_rules_by_name(rules, token.rule_name) rules = rules.select { |rule| rule.required_parameters_count == token.args_count } if rules.empty? raise "Invalid number of arguments. `#{token.rule_name}`" @@ -33,8 +38,12 @@ module Lrama end end - def select_rules_by_name(rule_name) - rules = @rules.select { |rule| rule.name == rule_name } + def select_not_inline_rules(rules) + rules.select { |rule| !rule.is_inline } + end + + def select_rules_by_name(rules, rule_name) + rules = rules.select { |rule| rule.name == rule_name } if rules.empty? raise "Parameterizing rule does not exist. `#{rule_name}`" else diff --git a/tool/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb b/tool/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb index 7f50be873c..3eb92f8ef4 100644 --- a/tool/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb +++ b/tool/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb @@ -9,6 +9,28 @@ module Lrama @user_code = nil @precedence_sym = nil end + + def resolve_user_code(bindings) + return unless user_code + + var_to_arg = {} + symbols.each do |sym| + resolved_sym = bindings.resolve_symbol(sym) + if resolved_sym != sym + var_to_arg[sym.s_value] = resolved_sym.s_value + end + end + + var_to_arg.each do |var, arg| + user_code.references.each do |ref| + if ref.name == var + ref.name = arg + end + end + end + + return user_code + end end end end diff --git a/tool/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb b/tool/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb index 9c1d46e4f5..38f0fca4ea 100644 --- a/tool/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb +++ b/tool/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb @@ -2,12 +2,14 @@ module Lrama class Grammar class ParameterizingRule class Rule - attr_reader :name, :parameters, :rhs_list, :required_parameters_count + attr_reader :name, :parameters, :rhs_list, :required_parameters_count, :tag, :is_inline - def initialize(name, parameters, rhs_list) + def initialize(name, parameters, rhs_list, tag: nil, is_inline: false) @name = name @parameters = parameters @rhs_list = rhs_list + @tag = tag + @is_inline = is_inline @required_parameters_count = parameters.count end end diff --git a/tool/lrama/lib/lrama/grammar/rule.rb b/tool/lrama/lib/lrama/grammar/rule.rb index 9281e0574f..0e06edc80d 100644 --- a/tool/lrama/lib/lrama/grammar/rule.rb +++ b/tool/lrama/lib/lrama/grammar/rule.rb @@ -19,7 +19,7 @@ module Lrama # TODO: Change this to display_name def to_s l = lhs.id.s_value - r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(", ") + r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(" ") "#{l} -> #{r}" end @@ -32,6 +32,10 @@ module Lrama "#{l}: #{r}" end + def with_actions + "#{to_s} {#{token_code&.s_value}}" + end + # opt_nl: ε <-- empty_rule # | '\n' <-- not empty_rule def empty_rule? diff --git a/tool/lrama/lib/lrama/grammar/rule_builder.rb b/tool/lrama/lib/lrama/grammar/rule_builder.rb index b2ccc3e243..ccb41e67f8 100644 --- a/tool/lrama/lib/lrama/grammar/rule_builder.rb +++ b/tool/lrama/lib/lrama/grammar/rule_builder.rb @@ -16,8 +16,13 @@ module Lrama @user_code = nil @precedence_sym = nil @line = nil + @rules = [] @rule_builders_for_parameterizing_rules = [] @rule_builders_for_derived_rules = [] + @rule_builders_for_inline_rules = [] + @parameterizing_rules = [] + @inline_rules = [] + @midrule_action_rules = [] end def add_rhs(rhs) @@ -52,12 +57,16 @@ module Lrama def setup_rules(parameterizing_rule_resolver) preprocess_references unless @skip_preprocess_references - process_rhs(parameterizing_rule_resolver) + if rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) } + resolve_inline(parameterizing_rule_resolver) + else + process_rhs(parameterizing_rule_resolver) + end build_rules end def rules - @parameterizing_rules + @midrule_action_rules + @rules + @parameterizing_rules + @inline_rules + @midrule_action_rules + @rules end private @@ -73,19 +82,25 @@ module Lrama def build_rules tokens = @replaced_rhs - rule = Rule.new( - id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code, - position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line - ) - @rules = [rule] - @parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder| - rule_builder.rules - end.flatten - @midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder| - rule_builder.rules - end.flatten - @midrule_action_rules.each do |r| - r.original_rule = rule + if tokens + rule = Rule.new( + id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code, + position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line + ) + @rules = [rule] + @parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder| + rule_builder.rules + end.flatten + @midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder| + rule_builder.rules + end.flatten + @midrule_action_rules.each do |r| + r.original_rule = rule + end + else + @inline_rules = @rule_builders_for_inline_rules.map do |rule_builder| + rule_builder.rules + end.flatten end end @@ -103,7 +118,7 @@ module Lrama when Lrama::Lexer::Token::Ident @replaced_rhs << token when Lrama::Lexer::Token::InstantiateRule - parameterizing_rule = parameterizing_rule_resolver.find(token) + parameterizing_rule = parameterizing_rule_resolver.find_rule(token) raise "Unexpected token. #{token}" unless parameterizing_rule bindings = Binding.new(parameterizing_rule, token.args) @@ -115,12 +130,12 @@ module Lrama @replaced_rhs << lhs_token parameterizing_rule_resolver.created_lhs_list << lhs_token parameterizing_rule.rhs_list.each do |r| - rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: token.lhs_tag, skip_preprocess_references: true) + rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: token.lhs_tag || parameterizing_rule.tag) rule_builder.lhs = lhs_token r.symbols.each { |sym| rule_builder.add_rhs(bindings.resolve_symbol(sym)) } rule_builder.line = line rule_builder.precedence_sym = r.precedence_sym - rule_builder.user_code = r.user_code + rule_builder.user_code = r.resolve_user_code(bindings) rule_builder.complete_input rule_builder.setup_rules(parameterizing_rule_resolver) @rule_builders_for_parameterizing_rules << rule_builder @@ -157,6 +172,41 @@ module Lrama "#{token.rule_name}_#{s_values.join('_')}" end + def resolve_inline(parameterizing_rule_resolver) + rhs.each_with_index do |token, i| + if inline_rule = parameterizing_rule_resolver.find_inline(token) + inline_rule.rhs_list.each_with_index do |inline_rhs| + rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: lhs_tag, skip_preprocess_references: true) + resolve_inline_rhs(rule_builder, inline_rhs, i) + rule_builder.lhs = lhs + rule_builder.line = line + rule_builder.user_code = replace_inline_user_code(inline_rhs, i) + rule_builder.complete_input + rule_builder.setup_rules(parameterizing_rule_resolver) + @rule_builders_for_inline_rules << rule_builder + end + end + end + end + + def resolve_inline_rhs(rule_builder, inline_rhs, index) + rhs.each_with_index do |token, i| + if index == i + inline_rhs.symbols.each { |sym| rule_builder.add_rhs(sym) } + else + rule_builder.add_rhs(token) + end + end + end + + def replace_inline_user_code(inline_rhs, index) + return user_code if inline_rhs.user_code.nil? + return user_code if user_code.nil? + + code = user_code.s_value.gsub(/\$#{index + 1}/, inline_rhs.user_code.s_value) + Lrama::Lexer::Token::UserCode.new(s_value: code, location: user_code.location) + end + def numberize_references # Bison n'th component is 1-origin (rhs + [user_code]).compact.each.with_index(1) do |token, i| diff --git a/tool/lrama/lib/lrama/lexer.rb b/tool/lrama/lib/lrama/lexer.rb index db8f384fe6..40622a51b4 100644 --- a/tool/lrama/lib/lrama/lexer.rb +++ b/tool/lrama/lib/lrama/lexer.rb @@ -37,6 +37,7 @@ module Lrama %code %rule %no-stdlib + %inline ) def initialize(grammar_file) diff --git a/tool/lrama/lib/lrama/lexer/grammar_file.rb b/tool/lrama/lib/lrama/lexer/grammar_file.rb index 6be0767004..3d3368625d 100644 --- a/tool/lrama/lib/lrama/lexer/grammar_file.rb +++ b/tool/lrama/lib/lrama/lexer/grammar_file.rb @@ -1,11 +1,21 @@ module Lrama class Lexer class GrammarFile + class Text < String + def inspect + length <= 50 ? super : "#{self[0..47]}...".inspect + end + end + attr_reader :path, :text def initialize(path, text) @path = path - @text = text.freeze + @text = Text.new(text).freeze + end + + def inspect + "<#{self.class}: @path=#{path}, @text=#{text.inspect}>" end def ==(other) diff --git a/tool/lrama/lib/lrama/option_parser.rb b/tool/lrama/lib/lrama/option_parser.rb index 3210b091ed..1e4d448fd1 100644 --- a/tool/lrama/lib/lrama/option_parser.rb +++ b/tool/lrama/lib/lrama/option_parser.rb @@ -119,8 +119,9 @@ module Lrama VALID_TRACES = %w[ none locations scan parse automaton bitsets - closure grammar rules resource sets muscles tools - m4-early m4 skeleton time ielr cex all + closure grammar rules actions resource + sets muscles tools m4-early m4 skeleton time + ielr cex all ] def validate_trace(trace) diff --git a/tool/lrama/lib/lrama/parser.rb b/tool/lrama/lib/lrama/parser.rb index 0a46f759c0..04603105b4 100644 --- a/tool/lrama/lib/lrama/parser.rb +++ b/tool/lrama/lib/lrama/parser.rb @@ -658,7 +658,7 @@ end module Lrama class Parser < Racc::Parser -module_eval(<<'...end parser.y/module_eval...', 'parser.y', 529) +module_eval(<<'...end parser.y/module_eval...', 'parser.y', 536) include Lrama::Report::Duration @@ -732,314 +732,322 @@ end ##### State transition tables begin ### racc_action_table = [ - 96, 50, 97, 156, 155, 78, 50, 50, 156, 199, - 78, 78, 50, 50, 199, 49, 78, 158, 69, 6, - 3, 7, 158, 200, 210, 154, 8, 50, 200, 49, - 40, 174, 175, 176, 47, 50, 46, 49, 53, 78, - 74, 50, 53, 49, 159, 53, 81, 98, 56, 159, - 201, 174, 175, 176, 94, 201, 22, 24, 25, 26, + 98, 51, 99, 163, 88, 79, 51, 51, 180, 163, + 79, 79, 51, 162, 180, 156, 79, 165, 157, 51, + 3, 50, 181, 165, 70, 51, 8, 50, 181, 79, + 75, 51, 6, 50, 7, 161, 82, 47, 51, 51, + 50, 50, 89, 82, 82, 166, 41, 51, 100, 50, + 182, 166, 82, 51, 48, 50, 182, 23, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 46, 50, 50, 49, 49, 91, 81, 81, 50, - 50, 49, 49, 50, 81, 49, 57, 78, 184, 58, - 59, 22, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 9, 50, 60, 49, - 13, 14, 15, 16, 17, 18, 61, 62, 19, 20, - 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 50, 50, 49, - 49, 78, 184, 50, 50, 49, 49, 78, 184, 50, - 50, 49, 49, 78, 184, 50, 50, 49, 49, 78, - 184, 50, 50, 49, 49, 78, 184, 50, 50, 49, - 49, 78, 78, 50, 50, 49, 49, 78, 78, 50, - 50, 49, 49, 78, 78, 50, 50, 190, 49, 78, - 78, 50, 50, 190, 49, 78, 78, 50, 50, 190, - 49, 78, 50, 50, 49, 49, 152, 203, 153, 204, - 174, 175, 176, 219, 221, 204, 204, 63, 64, 65, - 66, 87, 88, 92, 94, 99, 99, 99, 101, 107, - 111, 112, 115, 115, 115, 115, 118, 121, 122, 124, - 126, 127, 128, 129, 130, 133, 137, 138, 139, 142, - 143, 144, 146, 161, 163, 164, 165, 166, 167, 168, - 169, 142, 171, 179, 180, 189, 194, 195, 197, 202, - 189, 94, 194, 216, 218, 94, 194, 224, 94 ] + 37, 38, 47, 51, 51, 50, 50, 93, 79, 197, + 51, 51, 50, 50, 79, 197, 51, 51, 50, 50, + 79, 197, 23, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 9, 51, 54, + 50, 14, 15, 16, 17, 18, 19, 54, 54, 20, + 21, 22, 23, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 51, 51, + 50, 50, 79, 197, 51, 51, 50, 50, 79, 197, + 51, 51, 50, 50, 79, 197, 51, 51, 50, 50, + 79, 79, 51, 51, 50, 50, 79, 79, 51, 51, + 50, 50, 79, 79, 51, 51, 50, 207, 79, 79, + 51, 51, 207, 207, 79, 79, 51, 51, 50, 50, + 79, 187, 188, 189, 96, 187, 188, 189, 96, 217, + 221, 229, 218, 218, 218, 51, 51, 50, 50, 187, + 188, 189, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 90, 94, 96, 101, 101, 101, 103, + 109, 113, 114, 117, 117, 117, 117, 120, 47, 124, + 125, 127, 129, 130, 131, 132, 133, 136, 140, 141, + 142, 143, 146, 147, 148, 150, 160, 168, 170, 171, + 172, 173, 174, 176, 177, 178, 146, 184, 192, 193, + 200, 160, 204, 176, 211, 160, 215, 216, 178, 176, + 226, 176, 228, 96, 96, 176 ] racc_action_check = [ - 48, 141, 48, 141, 140, 141, 170, 188, 170, 188, - 170, 188, 207, 32, 207, 32, 207, 141, 32, 2, - 1, 2, 170, 188, 199, 140, 3, 14, 207, 14, - 7, 199, 199, 199, 13, 33, 9, 33, 15, 33, - 33, 34, 16, 34, 141, 17, 34, 48, 18, 170, - 188, 157, 157, 157, 157, 207, 9, 9, 9, 9, + 49, 145, 49, 145, 39, 145, 159, 183, 159, 183, + 159, 183, 201, 144, 201, 139, 201, 145, 139, 33, + 1, 33, 159, 183, 33, 34, 3, 34, 201, 34, + 34, 35, 2, 35, 2, 144, 35, 9, 36, 37, + 36, 37, 39, 36, 37, 145, 7, 38, 49, 38, + 159, 183, 38, 15, 14, 15, 201, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 41, 35, 36, 35, 36, 41, 35, 36, 37, - 68, 37, 68, 165, 37, 165, 19, 165, 165, 22, - 24, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 41, 41, 41, 41, 41, 41, 4, 69, 25, 69, - 4, 4, 4, 4, 4, 4, 26, 27, 4, 4, + 9, 9, 42, 69, 172, 69, 172, 42, 172, 172, + 173, 70, 173, 70, 173, 173, 174, 81, 174, 81, + 174, 174, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 4, 82, 16, + 82, 4, 4, 4, 4, 4, 4, 17, 18, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 166, 80, 166, - 80, 166, 166, 167, 81, 167, 81, 167, 167, 181, - 107, 181, 107, 181, 181, 185, 109, 185, 109, 185, - 185, 186, 115, 186, 115, 186, 186, 73, 74, 73, - 74, 73, 74, 112, 114, 112, 114, 112, 114, 134, - 159, 134, 159, 134, 159, 171, 201, 171, 201, 171, - 201, 202, 204, 202, 204, 202, 204, 210, 117, 210, - 117, 210, 131, 135, 131, 135, 136, 191, 136, 191, - 192, 192, 192, 213, 217, 213, 217, 28, 29, 30, - 31, 38, 39, 44, 45, 52, 54, 55, 56, 67, - 71, 72, 79, 84, 85, 86, 87, 93, 94, 100, - 102, 103, 104, 105, 106, 110, 118, 119, 120, 121, - 122, 123, 125, 145, 147, 148, 149, 150, 151, 152, - 153, 154, 156, 160, 162, 168, 173, 177, 187, 190, - 197, 198, 203, 206, 211, 216, 220, 222, 224 ] + 4, 4, 4, 4, 4, 4, 4, 4, 194, 109, + 194, 109, 194, 194, 198, 111, 198, 111, 198, 198, + 199, 117, 199, 117, 199, 199, 74, 75, 74, 75, + 74, 75, 114, 116, 114, 116, 114, 116, 137, 166, + 137, 166, 137, 166, 182, 184, 182, 184, 182, 184, + 204, 216, 204, 216, 204, 216, 218, 119, 218, 119, + 218, 164, 164, 164, 164, 179, 179, 179, 179, 208, + 214, 223, 208, 214, 223, 134, 138, 134, 138, 209, + 209, 209, 19, 20, 23, 25, 26, 27, 28, 29, + 30, 31, 32, 40, 45, 46, 53, 55, 56, 57, + 68, 72, 73, 80, 85, 86, 87, 88, 89, 95, + 96, 102, 104, 105, 106, 107, 108, 112, 120, 121, + 122, 123, 124, 125, 126, 128, 141, 149, 151, 152, + 153, 154, 155, 156, 157, 158, 161, 163, 167, 169, + 175, 178, 180, 186, 190, 200, 205, 207, 213, 217, + 220, 221, 222, 226, 228, 230 ] racc_action_pointer = [ - nil, 20, 9, 26, 97, nil, nil, 23, nil, 32, - nil, nil, nil, 28, 24, 19, 23, 26, 43, 67, - nil, nil, 70, nil, 71, 89, 97, 112, 212, 213, - 214, 215, 10, 32, 38, 69, 70, 76, 216, 220, - nil, 67, nil, nil, 200, 174, nil, nil, -5, nil, - nil, nil, 206, nil, 207, 208, 209, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 221, 77, 104, - nil, 224, 223, 164, 165, nil, nil, nil, nil, 224, - 135, 141, nil, nil, 225, 226, 227, 196, nil, nil, - nil, nil, nil, 195, 233, nil, nil, nil, nil, nil, - 237, nil, 238, 239, 240, 241, 242, 147, nil, 153, - 238, nil, 170, nil, 171, 159, nil, 195, 241, 236, - 246, 204, 199, 249, nil, 250, nil, nil, nil, nil, - nil, 199, nil, nil, 176, 200, 165, nil, nil, nil, - -19, -2, nil, nil, nil, 233, nil, 234, 235, 236, - 237, 238, 217, 255, 216, nil, 222, 4, nil, 177, - 243, nil, 244, nil, nil, 80, 134, 140, 220, nil, - 3, 182, nil, 258, nil, nil, nil, 265, nil, nil, - nil, 146, nil, nil, nil, 152, 158, 224, 4, nil, - 229, 166, 163, nil, nil, nil, nil, 225, 221, -16, - nil, 183, 188, 264, 189, nil, 253, 9, nil, nil, - 194, 272, nil, 172, nil, nil, 225, 173, nil, nil, - 268, nil, 257, nil, 228, nil ] + nil, 20, 22, 26, 98, nil, nil, 39, nil, 33, + nil, nil, nil, nil, 48, 50, 90, 98, 99, 207, + 194, nil, nil, 195, nil, 196, 197, 198, 213, 214, + 215, 216, 217, 16, 22, 28, 35, 36, 44, -1, + 221, nil, 68, nil, nil, 201, 174, nil, nil, -5, + nil, nil, nil, 207, nil, 208, 209, 210, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 222, 70, + 78, nil, 225, 224, 153, 154, nil, nil, nil, nil, + 225, 84, 105, nil, nil, 226, 227, 228, 197, 234, + nil, nil, nil, nil, nil, 197, 235, nil, nil, nil, + nil, nil, 239, nil, 240, 241, 242, 243, 244, 136, + nil, 142, 240, nil, 159, nil, 160, 148, nil, 184, + 243, 207, 239, 249, 206, 201, 252, nil, 253, nil, + nil, nil, nil, nil, 202, nil, nil, 165, 203, -26, + nil, 210, nil, nil, -10, -2, nil, nil, nil, 237, + nil, 238, 239, 240, 241, 242, 255, 259, 220, 3, + nil, 220, nil, 227, 143, nil, 166, 248, nil, 249, + nil, nil, 71, 77, 83, 228, nil, nil, 225, 147, + 232, nil, 171, 4, 172, nil, 265, nil, nil, nil, + 272, nil, nil, nil, 135, nil, nil, nil, 141, 147, + 229, 9, nil, nil, 177, 274, nil, 237, 158, 161, + nil, nil, nil, 233, 159, nil, 178, 271, 183, nil, + 260, 273, 262, 160, nil, nil, 232, nil, 233, nil, + 277, nil, nil ] racc_action_default = [ - -2, -136, -8, -136, -136, -3, -4, -136, 226, -136, - -9, -10, -11, -136, -136, -136, -136, -136, -136, -136, - -23, -24, -136, -28, -136, -136, -136, -136, -136, -136, - -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, - -7, -121, -94, -96, -136, -118, -120, -12, -125, -92, - -93, -124, -14, -83, -15, -16, -136, -20, -25, -29, - -32, -35, -38, -39, -40, -41, -42, -43, -49, -136, - -52, -69, -44, -73, -136, -76, -78, -79, -133, -45, - -86, -136, -89, -91, -46, -47, -48, -136, -5, -1, - -95, -122, -97, -136, -136, -13, -126, -127, -128, -80, - -136, -17, -136, -136, -136, -136, -136, -136, -53, -50, - -71, -70, -136, -77, -74, -136, -90, -87, -136, -136, - -136, -102, -136, -136, -84, -136, -21, -26, -30, -33, - -36, -51, -54, -72, -75, -88, -136, -56, -6, -123, - -98, -99, -103, -119, -81, -136, -18, -136, -136, -136, - -136, -136, -136, -136, -102, -101, -92, -118, -107, -136, - -136, -85, -136, -22, -27, -136, -136, -136, -60, -57, - -100, -136, -104, -134, -111, -112, -113, -136, -110, -82, - -19, -31, -129, -131, -132, -34, -37, -55, -58, -61, - -92, -136, -114, -105, -135, -108, -130, -60, -118, -92, - -65, -136, -136, -134, -136, -116, -136, -59, -62, -63, - -136, -136, -68, -136, -106, -115, -118, -136, -66, -117, - -134, -64, -136, -109, -118, -67 ] + -2, -138, -8, -138, -138, -3, -4, -138, 233, -138, + -9, -10, -11, -12, -138, -138, -138, -138, -138, -138, + -138, -24, -25, -138, -29, -138, -138, -138, -138, -138, + -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, + -138, -7, -123, -96, -98, -138, -120, -122, -13, -127, + -94, -95, -126, -15, -85, -16, -17, -138, -21, -26, + -30, -33, -36, -39, -40, -41, -42, -43, -44, -50, + -138, -53, -71, -45, -75, -138, -78, -80, -81, -135, + -46, -88, -138, -91, -93, -47, -48, -49, -138, -138, + -5, -1, -97, -124, -99, -138, -138, -14, -128, -129, + -130, -82, -138, -18, -138, -138, -138, -138, -138, -138, + -54, -51, -73, -72, -138, -79, -76, -138, -92, -89, + -138, -138, -138, -138, -104, -138, -138, -86, -138, -22, + -27, -31, -34, -37, -52, -55, -74, -77, -90, -138, + -58, -62, -6, -125, -100, -101, -105, -121, -83, -138, + -19, -138, -138, -138, -138, -138, -136, -138, -57, -60, + -63, -104, -103, -94, -120, -109, -138, -138, -87, -138, + -23, -28, -138, -138, -138, -138, -137, -59, -62, -120, + -94, -67, -138, -102, -138, -106, -136, -113, -114, -115, + -138, -112, -84, -20, -32, -131, -133, -134, -35, -38, + -62, -61, -64, -65, -138, -138, -70, -94, -138, -116, + -107, -110, -132, -56, -138, -68, -138, -136, -138, -118, + -138, -136, -138, -138, -108, -117, -120, -66, -120, -119, + -136, -69, -111 ] racc_goto_table = [ - 93, 75, 51, 68, 73, 193, 116, 108, 191, 173, - 196, 1, 117, 2, 196, 196, 141, 4, 42, 41, - 71, 89, 83, 83, 83, 83, 188, 79, 84, 85, - 86, 52, 54, 55, 5, 214, 181, 185, 186, 213, - 109, 113, 75, 116, 205, 114, 135, 217, 108, 170, - 90, 209, 223, 39, 119, 207, 71, 71, 10, 11, - 12, 116, 48, 95, 125, 162, 102, 147, 83, 83, - 108, 103, 148, 104, 149, 105, 150, 106, 131, 151, - 75, 67, 113, 134, 72, 110, 132, 136, 187, 211, - 222, 123, 160, 100, 145, 71, 140, 71, 177, 206, - 120, nil, 113, 83, nil, 83, nil, nil, nil, 157, - nil, nil, 172, nil, nil, nil, nil, nil, nil, 71, - nil, nil, nil, 83, nil, nil, nil, 178, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 157, 192, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 208, nil, nil, 198, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 212, - 192, 220, 215, nil, nil, 198, nil, nil, 192, 225 ] + 76, 95, 69, 52, 74, 158, 110, 175, 118, 119, + 145, 208, 1, 212, 186, 2, 43, 212, 212, 4, + 42, 72, 91, 84, 84, 84, 84, 5, 40, 203, + 122, 214, 80, 85, 86, 87, 10, 210, 11, 111, + 115, 76, 12, 223, 138, 116, 118, 183, 110, 92, + 53, 55, 56, 194, 198, 199, 13, 72, 72, 219, + 49, 97, 128, 169, 213, 118, 104, 151, 224, 84, + 84, 110, 227, 105, 152, 106, 153, 107, 134, 154, + 76, 232, 115, 108, 137, 155, 68, 73, 112, 135, + 139, 121, 201, 205, 222, 126, 167, 72, 102, 72, + 149, 144, 190, 115, 220, 84, 123, 84, nil, nil, + nil, 164, nil, nil, nil, nil, nil, nil, nil, 185, + nil, nil, 72, nil, nil, 179, 84, nil, nil, nil, + nil, nil, 191, nil, 202, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 206, 164, + 209, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 179, nil, nil, + 209, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 230, 209, 231, 225 ] racc_goto_check = [ - 41, 40, 34, 32, 46, 59, 53, 33, 43, 42, - 63, 1, 52, 2, 63, 63, 58, 3, 54, 4, - 34, 5, 34, 34, 34, 34, 39, 31, 31, 31, - 31, 14, 14, 14, 6, 59, 20, 20, 20, 43, - 32, 40, 40, 53, 42, 46, 52, 43, 33, 58, - 54, 42, 59, 7, 8, 39, 34, 34, 9, 10, - 11, 53, 12, 13, 15, 16, 17, 18, 34, 34, - 33, 21, 22, 23, 24, 25, 26, 27, 32, 28, - 40, 29, 40, 46, 30, 35, 36, 37, 38, 44, - 45, 48, 49, 50, 51, 34, 57, 34, 60, 61, - 62, nil, 40, 34, nil, 34, nil, nil, nil, 40, - nil, nil, 41, nil, nil, nil, nil, nil, nil, 34, - nil, nil, nil, 34, nil, nil, nil, 40, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 40, 40, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 41, nil, nil, 40, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, 40, - 40, 41, 40, nil, nil, 40, nil, nil, 40, 41 ] + 43, 44, 33, 35, 49, 40, 34, 39, 56, 55, + 60, 46, 1, 64, 45, 2, 57, 64, 64, 3, + 4, 35, 5, 35, 35, 35, 35, 6, 7, 45, + 8, 46, 32, 32, 32, 32, 9, 39, 10, 33, + 43, 43, 11, 46, 55, 49, 56, 60, 34, 57, + 15, 15, 15, 21, 21, 21, 12, 35, 35, 45, + 13, 14, 16, 17, 40, 56, 18, 19, 39, 35, + 35, 34, 39, 22, 23, 24, 25, 26, 33, 27, + 43, 39, 43, 28, 49, 29, 30, 31, 36, 37, + 38, 41, 42, 47, 48, 51, 52, 35, 53, 35, + 54, 59, 61, 43, 62, 35, 63, 35, nil, nil, + nil, 43, nil, nil, nil, nil, nil, nil, nil, 44, + nil, nil, 35, nil, nil, 43, 35, nil, nil, nil, + nil, nil, 43, nil, 44, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, 43, 43, + 43, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 43, nil, nil, + 43, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 44, 43, 44, 43 ] racc_goto_pointer = [ - nil, 11, 13, 15, 10, -20, 32, 47, -34, 54, - 55, 56, 48, 15, 16, -37, -81, 9, -59, nil, - -129, 13, -55, 14, -54, 15, -53, 16, -51, 49, - 51, -7, -29, -61, -12, 14, -24, -31, -80, -142, - -32, -45, -148, -163, -111, -128, -29, nil, -8, -52, - 40, -30, -69, -74, 9, nil, nil, -25, -105, -168, - -60, -96, 9, -171 ] + nil, 12, 15, 17, 11, -20, 25, 22, -60, 32, + 34, 38, 52, 45, 12, 34, -41, -87, 8, -62, + nil, -119, 14, -56, 15, -55, 16, -53, 21, -48, + 53, 53, -3, -31, -63, -12, 16, -23, -30, -149, + -136, 2, -86, -34, -45, -150, -173, -88, -121, -30, + nil, -6, -52, 44, -27, -73, -73, 7, nil, -23, + -114, -63, -107, 13, -181 ] racc_goto_default = [ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - 44, nil, nil, nil, nil, nil, nil, nil, nil, 23, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 70, 76, nil, nil, nil, nil, nil, - 183, nil, nil, nil, nil, nil, nil, 77, nil, nil, - nil, nil, 80, 82, nil, 43, 45, nil, nil, nil, - nil, nil, nil, 182 ] + 45, nil, nil, nil, nil, nil, nil, nil, nil, nil, + 24, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 71, 77, nil, nil, nil, nil, + nil, 46, 159, 196, nil, nil, nil, nil, nil, nil, + 78, nil, nil, nil, nil, 81, 83, nil, 44, nil, + nil, nil, nil, nil, 195 ] racc_reduce_table = [ 0, 0, :racc_error, - 5, 54, :_reduce_none, - 0, 55, :_reduce_none, - 2, 55, :_reduce_none, - 0, 60, :_reduce_4, - 0, 61, :_reduce_5, - 5, 59, :_reduce_6, - 2, 59, :_reduce_none, - 0, 56, :_reduce_8, + 5, 55, :_reduce_none, + 0, 56, :_reduce_none, 2, 56, :_reduce_none, - 1, 62, :_reduce_none, - 1, 62, :_reduce_none, - 2, 62, :_reduce_12, - 3, 62, :_reduce_none, - 2, 62, :_reduce_none, - 2, 62, :_reduce_15, - 2, 62, :_reduce_16, - 0, 68, :_reduce_17, - 0, 69, :_reduce_18, - 7, 62, :_reduce_19, - 0, 70, :_reduce_20, - 0, 71, :_reduce_21, - 6, 62, :_reduce_22, - 1, 62, :_reduce_23, - 1, 62, :_reduce_none, - 0, 74, :_reduce_25, - 0, 75, :_reduce_26, - 6, 63, :_reduce_27, - 1, 63, :_reduce_none, - 0, 76, :_reduce_29, - 0, 77, :_reduce_30, - 7, 63, :_reduce_31, - 0, 78, :_reduce_32, - 0, 79, :_reduce_33, - 7, 63, :_reduce_34, - 0, 80, :_reduce_35, - 0, 81, :_reduce_36, - 7, 63, :_reduce_37, - 2, 63, :_reduce_38, - 2, 63, :_reduce_39, - 2, 63, :_reduce_40, - 2, 63, :_reduce_41, - 2, 63, :_reduce_42, - 2, 72, :_reduce_none, - 2, 72, :_reduce_44, - 2, 72, :_reduce_45, - 2, 72, :_reduce_46, - 2, 72, :_reduce_47, - 2, 72, :_reduce_48, - 1, 82, :_reduce_49, - 2, 82, :_reduce_50, - 3, 82, :_reduce_51, - 1, 85, :_reduce_52, - 2, 85, :_reduce_53, - 3, 86, :_reduce_54, - 7, 64, :_reduce_55, - 1, 90, :_reduce_56, - 3, 90, :_reduce_57, - 1, 91, :_reduce_58, - 3, 91, :_reduce_59, - 0, 92, :_reduce_60, - 1, 92, :_reduce_61, - 3, 92, :_reduce_62, - 3, 92, :_reduce_63, - 5, 92, :_reduce_64, - 0, 97, :_reduce_65, - 0, 98, :_reduce_66, - 7, 92, :_reduce_67, - 3, 92, :_reduce_68, - 0, 88, :_reduce_none, - 1, 88, :_reduce_none, - 0, 89, :_reduce_none, - 1, 89, :_reduce_none, - 1, 83, :_reduce_73, - 2, 83, :_reduce_74, - 3, 83, :_reduce_75, - 1, 99, :_reduce_76, - 2, 99, :_reduce_77, - 1, 93, :_reduce_none, - 1, 93, :_reduce_none, - 0, 101, :_reduce_80, - 0, 102, :_reduce_81, - 6, 67, :_reduce_82, - 0, 103, :_reduce_83, - 0, 104, :_reduce_84, - 5, 67, :_reduce_85, - 1, 84, :_reduce_86, - 2, 84, :_reduce_87, - 3, 84, :_reduce_88, - 1, 105, :_reduce_89, - 2, 105, :_reduce_90, - 1, 106, :_reduce_none, - 1, 87, :_reduce_92, - 1, 87, :_reduce_93, - 1, 57, :_reduce_none, + 0, 61, :_reduce_4, + 0, 62, :_reduce_5, + 5, 60, :_reduce_6, + 2, 60, :_reduce_none, + 0, 57, :_reduce_8, 2, 57, :_reduce_none, - 1, 107, :_reduce_none, - 2, 107, :_reduce_none, - 4, 108, :_reduce_98, - 1, 110, :_reduce_99, - 3, 110, :_reduce_100, - 2, 110, :_reduce_none, - 0, 111, :_reduce_102, - 1, 111, :_reduce_103, - 3, 111, :_reduce_104, - 4, 111, :_reduce_105, - 6, 111, :_reduce_106, - 0, 113, :_reduce_107, - 0, 114, :_reduce_108, - 8, 111, :_reduce_109, - 3, 111, :_reduce_110, - 1, 95, :_reduce_111, - 1, 95, :_reduce_112, - 1, 95, :_reduce_113, - 1, 96, :_reduce_114, - 3, 96, :_reduce_115, - 2, 96, :_reduce_116, - 4, 96, :_reduce_117, - 0, 94, :_reduce_none, - 3, 94, :_reduce_119, - 1, 109, :_reduce_none, - 0, 58, :_reduce_none, - 0, 115, :_reduce_122, - 3, 58, :_reduce_123, - 1, 65, :_reduce_none, - 0, 66, :_reduce_none, - 1, 66, :_reduce_none, - 1, 66, :_reduce_none, - 1, 66, :_reduce_none, - 1, 73, :_reduce_129, - 2, 73, :_reduce_130, - 1, 116, :_reduce_none, - 1, 116, :_reduce_none, - 1, 100, :_reduce_133, - 0, 112, :_reduce_none, - 1, 112, :_reduce_none ] - -racc_reduce_n = 136 - -racc_shift_n = 226 + 1, 63, :_reduce_none, + 1, 63, :_reduce_none, + 1, 63, :_reduce_none, + 2, 63, :_reduce_13, + 3, 63, :_reduce_none, + 2, 63, :_reduce_none, + 2, 63, :_reduce_16, + 2, 63, :_reduce_17, + 0, 70, :_reduce_18, + 0, 71, :_reduce_19, + 7, 63, :_reduce_20, + 0, 72, :_reduce_21, + 0, 73, :_reduce_22, + 6, 63, :_reduce_23, + 1, 63, :_reduce_24, + 1, 63, :_reduce_none, + 0, 76, :_reduce_26, + 0, 77, :_reduce_27, + 6, 64, :_reduce_28, + 1, 64, :_reduce_none, + 0, 78, :_reduce_30, + 0, 79, :_reduce_31, + 7, 64, :_reduce_32, + 0, 80, :_reduce_33, + 0, 81, :_reduce_34, + 7, 64, :_reduce_35, + 0, 82, :_reduce_36, + 0, 83, :_reduce_37, + 7, 64, :_reduce_38, + 2, 64, :_reduce_39, + 2, 64, :_reduce_40, + 2, 64, :_reduce_41, + 2, 64, :_reduce_42, + 2, 64, :_reduce_43, + 2, 74, :_reduce_none, + 2, 74, :_reduce_45, + 2, 74, :_reduce_46, + 2, 74, :_reduce_47, + 2, 74, :_reduce_48, + 2, 74, :_reduce_49, + 1, 84, :_reduce_50, + 2, 84, :_reduce_51, + 3, 84, :_reduce_52, + 1, 87, :_reduce_53, + 2, 87, :_reduce_54, + 3, 88, :_reduce_55, + 8, 65, :_reduce_56, + 5, 66, :_reduce_57, + 1, 92, :_reduce_58, + 3, 92, :_reduce_59, + 1, 94, :_reduce_60, + 3, 94, :_reduce_61, + 0, 96, :_reduce_62, + 1, 96, :_reduce_63, + 3, 96, :_reduce_64, + 3, 96, :_reduce_65, + 6, 96, :_reduce_66, + 0, 101, :_reduce_67, + 0, 102, :_reduce_68, + 7, 96, :_reduce_69, + 3, 96, :_reduce_70, + 0, 90, :_reduce_none, + 1, 90, :_reduce_none, + 0, 91, :_reduce_none, + 1, 91, :_reduce_none, + 1, 85, :_reduce_75, + 2, 85, :_reduce_76, + 3, 85, :_reduce_77, + 1, 103, :_reduce_78, + 2, 103, :_reduce_79, + 1, 97, :_reduce_none, + 1, 97, :_reduce_none, + 0, 105, :_reduce_82, + 0, 106, :_reduce_83, + 6, 69, :_reduce_84, + 0, 107, :_reduce_85, + 0, 108, :_reduce_86, + 5, 69, :_reduce_87, + 1, 86, :_reduce_88, + 2, 86, :_reduce_89, + 3, 86, :_reduce_90, + 1, 109, :_reduce_91, + 2, 109, :_reduce_92, + 1, 110, :_reduce_none, + 1, 89, :_reduce_94, + 1, 89, :_reduce_95, + 1, 58, :_reduce_none, + 2, 58, :_reduce_none, + 1, 111, :_reduce_none, + 2, 111, :_reduce_none, + 4, 112, :_reduce_100, + 1, 113, :_reduce_101, + 3, 113, :_reduce_102, + 2, 113, :_reduce_none, + 0, 114, :_reduce_104, + 1, 114, :_reduce_105, + 3, 114, :_reduce_106, + 4, 114, :_reduce_107, + 6, 114, :_reduce_108, + 0, 115, :_reduce_109, + 0, 116, :_reduce_110, + 8, 114, :_reduce_111, + 3, 114, :_reduce_112, + 1, 99, :_reduce_113, + 1, 99, :_reduce_114, + 1, 99, :_reduce_115, + 1, 100, :_reduce_116, + 3, 100, :_reduce_117, + 2, 100, :_reduce_118, + 4, 100, :_reduce_119, + 0, 98, :_reduce_none, + 3, 98, :_reduce_121, + 1, 95, :_reduce_none, + 0, 59, :_reduce_none, + 0, 117, :_reduce_124, + 3, 59, :_reduce_125, + 1, 67, :_reduce_none, + 0, 68, :_reduce_none, + 1, 68, :_reduce_none, + 1, 68, :_reduce_none, + 1, 68, :_reduce_none, + 1, 75, :_reduce_131, + 2, 75, :_reduce_132, + 1, 118, :_reduce_none, + 1, 118, :_reduce_none, + 1, 104, :_reduce_135, + 0, 93, :_reduce_none, + 1, 93, :_reduce_none ] + +racc_reduce_n = 138 + +racc_shift_n = 233 racc_token_table = { false => 0, @@ -1085,18 +1093,19 @@ racc_token_table = { "(" => 40, ")" => 41, ":" => 42, - "," => 43, - "|" => 44, - "%empty" => 45, - "%prec" => 46, - "?" => 47, - "+" => 48, - "*" => 49, - "[" => 50, - "]" => 51, - "{...}" => 52 } - -racc_nt_base = 53 + "%inline" => 43, + "," => 44, + "|" => 45, + "%empty" => 46, + "%prec" => 47, + "?" => 48, + "+" => 49, + "*" => 50, + "[" => 51, + "]" => 52, + "{...}" => 53 } + +racc_nt_base = 54 racc_use_result_var = true @@ -1161,6 +1170,7 @@ Racc_token_to_s_table = [ "\"(\"", "\")\"", "\":\"", + "\"%inline\"", "\",\"", "\"|\"", "\"%empty\"", @@ -1183,6 +1193,7 @@ Racc_token_to_s_table = [ "bison_declaration", "grammar_declaration", "rule_declaration", + "inline_declaration", "variable", "value", "params", @@ -1209,7 +1220,9 @@ Racc_token_to_s_table = [ "int_opt", "alias", "rule_args", + "tag_opt", "rule_rhs_list", + "id_colon", "rule_rhs", "symbol", "named_ref_opt", @@ -1227,10 +1240,8 @@ Racc_token_to_s_table = [ "token_declaration_for_precedence", "rules_or_grammar_declaration", "rules", - "id_colon", "rhs_list", "rhs", - "tag_opt", "@21", "@22", "@23", @@ -1289,19 +1300,21 @@ module_eval(<<'.,.,', 'parser.y', 27) # reduce 11 omitted -module_eval(<<'.,.,', 'parser.y', 32) - def _reduce_12(val, _values, result) +# reduce 12 omitted + +module_eval(<<'.,.,', 'parser.y', 33) + def _reduce_13(val, _values, result) @grammar.expect = val[1] result end .,., -# reduce 13 omitted - # reduce 14 omitted -module_eval(<<'.,.,', 'parser.y', 37) - def _reduce_15(val, _values, result) +# reduce 15 omitted + +module_eval(<<'.,.,', 'parser.y', 38) + def _reduce_16(val, _values, result) val[1].each {|token| @grammar.lex_param = Grammar::Code::NoReferenceCode.new(type: :lex_param, token_code: token).token_code.s_value } @@ -1310,8 +1323,8 @@ module_eval(<<'.,.,', 'parser.y', 37) end .,., -module_eval(<<'.,.,', 'parser.y', 43) - def _reduce_16(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 44) + def _reduce_17(val, _values, result) val[1].each {|token| @grammar.parse_param = Grammar::Code::NoReferenceCode.new(type: :parse_param, token_code: token).token_code.s_value } @@ -1320,81 +1333,81 @@ module_eval(<<'.,.,', 'parser.y', 43) end .,., -module_eval(<<'.,.,', 'parser.y', 49) - def _reduce_17(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 50) + def _reduce_18(val, _values, result) begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 53) - def _reduce_18(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 54) + def _reduce_19(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 57) - def _reduce_19(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 58) + def _reduce_20(val, _values, result) @grammar.add_percent_code(id: val[1], code: val[4]) result end .,., -module_eval(<<'.,.,', 'parser.y', 61) - def _reduce_20(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 62) + def _reduce_21(val, _values, result) begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 65) - def _reduce_21(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 66) + def _reduce_22(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 69) - def _reduce_22(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 70) + def _reduce_23(val, _values, result) @grammar.initial_action = Grammar::Code::InitialActionCode.new(type: :initial_action, token_code: val[3]) result end .,., -module_eval(<<'.,.,', 'parser.y', 71) - def _reduce_23(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 72) + def _reduce_24(val, _values, result) @grammar.no_stdlib = true result end .,., -# reduce 24 omitted +# reduce 25 omitted -module_eval(<<'.,.,', 'parser.y', 76) - def _reduce_25(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 77) + def _reduce_26(val, _values, result) begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 80) - def _reduce_26(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 81) + def _reduce_27(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 84) - def _reduce_27(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 85) + def _reduce_28(val, _values, result) @grammar.set_union( Grammar::Code::NoReferenceCode.new(type: :union, token_code: val[3]), val[3].line @@ -1404,26 +1417,26 @@ module_eval(<<'.,.,', 'parser.y', 84) end .,., -# reduce 28 omitted +# reduce 29 omitted -module_eval(<<'.,.,', 'parser.y', 92) - def _reduce_29(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 93) + def _reduce_30(val, _values, result) begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 96) - def _reduce_30(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 97) + def _reduce_31(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 100) - def _reduce_31(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 101) + def _reduce_32(val, _values, result) @grammar.add_destructor( ident_or_tags: val[6], token_code: val[3], @@ -1434,24 +1447,24 @@ module_eval(<<'.,.,', 'parser.y', 100) end .,., -module_eval(<<'.,.,', 'parser.y', 108) - def _reduce_32(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 109) + def _reduce_33(val, _values, result) begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 112) - def _reduce_33(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 113) + def _reduce_34(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 116) - def _reduce_34(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 117) + def _reduce_35(val, _values, result) @grammar.add_printer( ident_or_tags: val[6], token_code: val[3], @@ -1462,24 +1475,24 @@ module_eval(<<'.,.,', 'parser.y', 116) end .,., -module_eval(<<'.,.,', 'parser.y', 124) - def _reduce_35(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 125) + def _reduce_36(val, _values, result) begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 128) - def _reduce_36(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 129) + def _reduce_37(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 132) - def _reduce_37(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 133) + def _reduce_38(val, _values, result) @grammar.add_error_token( ident_or_tags: val[6], token_code: val[3], @@ -1490,50 +1503,50 @@ module_eval(<<'.,.,', 'parser.y', 132) end .,., -module_eval(<<'.,.,', 'parser.y', 140) - def _reduce_38(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 141) + def _reduce_39(val, _values, result) @grammar.after_shift = val[1] result end .,., -module_eval(<<'.,.,', 'parser.y', 144) - def _reduce_39(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 145) + def _reduce_40(val, _values, result) @grammar.before_reduce = val[1] result end .,., -module_eval(<<'.,.,', 'parser.y', 148) - def _reduce_40(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 149) + def _reduce_41(val, _values, result) @grammar.after_reduce = val[1] result end .,., -module_eval(<<'.,.,', 'parser.y', 152) - def _reduce_41(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 153) + def _reduce_42(val, _values, result) @grammar.after_shift_error_token = val[1] result end .,., -module_eval(<<'.,.,', 'parser.y', 156) - def _reduce_42(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 157) + def _reduce_43(val, _values, result) @grammar.after_pop_stack = val[1] result end .,., -# reduce 43 omitted +# reduce 44 omitted -module_eval(<<'.,.,', 'parser.y', 162) - def _reduce_44(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 163) + def _reduce_45(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @grammar.add_type(id: id, tag: hash[:tag]) @@ -1544,8 +1557,8 @@ module_eval(<<'.,.,', 'parser.y', 162) end .,., -module_eval(<<'.,.,', 'parser.y', 170) - def _reduce_45(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 171) + def _reduce_46(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| sym = @grammar.add_term(id: id) @@ -1558,8 +1571,8 @@ module_eval(<<'.,.,', 'parser.y', 170) end .,., -module_eval(<<'.,.,', 'parser.y', 180) - def _reduce_46(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 181) + def _reduce_47(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| sym = @grammar.add_term(id: id) @@ -1572,8 +1585,8 @@ module_eval(<<'.,.,', 'parser.y', 180) end .,., -module_eval(<<'.,.,', 'parser.y', 190) - def _reduce_47(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 191) + def _reduce_48(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| sym = @grammar.add_term(id: id) @@ -1586,8 +1599,8 @@ module_eval(<<'.,.,', 'parser.y', 190) end .,., -module_eval(<<'.,.,', 'parser.y', 200) - def _reduce_48(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 201) + def _reduce_49(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| sym = @grammar.add_term(id: id) @@ -1600,8 +1613,8 @@ module_eval(<<'.,.,', 'parser.y', 200) end .,., -module_eval(<<'.,.,', 'parser.y', 211) - def _reduce_49(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 212) + def _reduce_50(val, _values, result) val[0].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: nil, replace: true) } @@ -1610,8 +1623,8 @@ module_eval(<<'.,.,', 'parser.y', 211) end .,., -module_eval(<<'.,.,', 'parser.y', 217) - def _reduce_50(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 218) + def _reduce_51(val, _values, result) val[1].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[0], replace: true) } @@ -1620,8 +1633,8 @@ module_eval(<<'.,.,', 'parser.y', 217) end .,., -module_eval(<<'.,.,', 'parser.y', 223) - def _reduce_51(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 224) + def _reduce_52(val, _values, result) val[2].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[1], replace: true) } @@ -1630,52 +1643,61 @@ module_eval(<<'.,.,', 'parser.y', 223) end .,., -module_eval(<<'.,.,', 'parser.y', 228) - def _reduce_52(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 229) + def _reduce_53(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 229) - def _reduce_53(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 230) + def _reduce_54(val, _values, result) result = val[0].append(val[1]) result end .,., -module_eval(<<'.,.,', 'parser.y', 231) - def _reduce_54(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 232) + def _reduce_55(val, _values, result) result = val result end .,., -module_eval(<<'.,.,', 'parser.y', 235) - def _reduce_55(val, _values, result) - rule = Grammar::ParameterizingRule::Rule.new(val[1].s_value, val[3], val[6]) +module_eval(<<'.,.,', 'parser.y', 236) + def _reduce_56(val, _values, result) + rule = Grammar::ParameterizingRule::Rule.new(val[1].s_value, val[3], val[7], tag: val[5]) @grammar.add_parameterizing_rule(rule) result end .,., -module_eval(<<'.,.,', 'parser.y', 239) - def _reduce_56(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 242) + def _reduce_57(val, _values, result) + rule = Grammar::ParameterizingRule::Rule.new(val[2].s_value, [], val[4], is_inline: true) + @grammar.add_parameterizing_rule(rule) + + result + end +.,., + +module_eval(<<'.,.,', 'parser.y', 246) + def _reduce_58(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 240) - def _reduce_57(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 247) + def _reduce_59(val, _values, result) result = val[0].append(val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 244) - def _reduce_58(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 251) + def _reduce_60(val, _values, result) builder = val[0] result = [builder] @@ -1683,8 +1705,8 @@ module_eval(<<'.,.,', 'parser.y', 244) end .,., -module_eval(<<'.,.,', 'parser.y', 249) - def _reduce_59(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 256) + def _reduce_61(val, _values, result) builder = val[2] result = val[0].append(builder) @@ -1692,8 +1714,8 @@ module_eval(<<'.,.,', 'parser.y', 249) end .,., -module_eval(<<'.,.,', 'parser.y', 255) - def _reduce_60(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 262) + def _reduce_62(val, _values, result) reset_precs result = Grammar::ParameterizingRule::Rhs.new @@ -1701,8 +1723,8 @@ module_eval(<<'.,.,', 'parser.y', 255) end .,., -module_eval(<<'.,.,', 'parser.y', 260) - def _reduce_61(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 267) + def _reduce_63(val, _values, result) reset_precs result = Grammar::ParameterizingRule::Rhs.new @@ -1710,8 +1732,8 @@ module_eval(<<'.,.,', 'parser.y', 260) end .,., -module_eval(<<'.,.,', 'parser.y', 265) - def _reduce_62(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 272) + def _reduce_64(val, _values, result) token = val[1] token.alias_name = val[2] builder = val[0] @@ -1722,8 +1744,8 @@ module_eval(<<'.,.,', 'parser.y', 265) end .,., -module_eval(<<'.,.,', 'parser.y', 273) - def _reduce_63(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 280) + def _reduce_65(val, _values, result) builder = val[0] builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]]) result = builder @@ -1732,18 +1754,18 @@ module_eval(<<'.,.,', 'parser.y', 273) end .,., -module_eval(<<'.,.,', 'parser.y', 279) - def _reduce_64(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 286) + def _reduce_66(val, _values, result) builder = val[0] - builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3]) + builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3], lhs_tag: val[5]) result = builder result end .,., -module_eval(<<'.,.,', 'parser.y', 285) - def _reduce_65(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 292) + def _reduce_67(val, _values, result) if @prec_seen on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec @code_after_prec = true @@ -1754,16 +1776,16 @@ module_eval(<<'.,.,', 'parser.y', 285) end .,., -module_eval(<<'.,.,', 'parser.y', 293) - def _reduce_66(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 300) + def _reduce_68(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 297) - def _reduce_67(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 304) + def _reduce_69(val, _values, result) user_code = val[3] user_code.alias_name = val[6] builder = val[0] @@ -1774,8 +1796,8 @@ module_eval(<<'.,.,', 'parser.y', 297) end .,., -module_eval(<<'.,.,', 'parser.y', 305) - def _reduce_68(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 312) + def _reduce_70(val, _values, result) sym = @grammar.find_symbol_by_id!(val[2]) @prec_seen = true builder = val[0] @@ -1786,168 +1808,168 @@ module_eval(<<'.,.,', 'parser.y', 305) end .,., -# reduce 69 omitted - -# reduce 70 omitted - # reduce 71 omitted # reduce 72 omitted -module_eval(<<'.,.,', 'parser.y', 320) - def _reduce_73(val, _values, result) +# reduce 73 omitted + +# reduce 74 omitted + +module_eval(<<'.,.,', 'parser.y', 327) + def _reduce_75(val, _values, result) result = [{tag: nil, tokens: val[0]}] result end .,., -module_eval(<<'.,.,', 'parser.y', 324) - def _reduce_74(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 331) + def _reduce_76(val, _values, result) result = [{tag: val[0], tokens: val[1]}] result end .,., -module_eval(<<'.,.,', 'parser.y', 328) - def _reduce_75(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 335) + def _reduce_77(val, _values, result) result = val[0].append({tag: val[1], tokens: val[2]}) result end .,., -module_eval(<<'.,.,', 'parser.y', 331) - def _reduce_76(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 338) + def _reduce_78(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 332) - def _reduce_77(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 339) + def _reduce_79(val, _values, result) result = val[0].append(val[1]) result end .,., -# reduce 78 omitted +# reduce 80 omitted -# reduce 79 omitted +# reduce 81 omitted -module_eval(<<'.,.,', 'parser.y', 339) - def _reduce_80(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 346) + def _reduce_82(val, _values, result) begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 343) - def _reduce_81(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 350) + def _reduce_83(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 347) - def _reduce_82(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 354) + def _reduce_84(val, _values, result) result = val[0].append(val[3]) result end .,., -module_eval(<<'.,.,', 'parser.y', 351) - def _reduce_83(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 358) + def _reduce_85(val, _values, result) begin_c_declaration("}") result end .,., -module_eval(<<'.,.,', 'parser.y', 355) - def _reduce_84(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 362) + def _reduce_86(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 359) - def _reduce_85(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 366) + def _reduce_87(val, _values, result) result = [val[2]] result end .,., -module_eval(<<'.,.,', 'parser.y', 364) - def _reduce_86(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 371) + def _reduce_88(val, _values, result) result = [{tag: nil, tokens: val[0]}] result end .,., -module_eval(<<'.,.,', 'parser.y', 368) - def _reduce_87(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 375) + def _reduce_89(val, _values, result) result = [{tag: val[0], tokens: val[1]}] result end .,., -module_eval(<<'.,.,', 'parser.y', 372) - def _reduce_88(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 379) + def _reduce_90(val, _values, result) result = val[0].append({tag: val[1], tokens: val[2]}) result end .,., -module_eval(<<'.,.,', 'parser.y', 375) - def _reduce_89(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 382) + def _reduce_91(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 376) - def _reduce_90(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 383) + def _reduce_92(val, _values, result) result = val[0].append(val[1]) result end .,., -# reduce 91 omitted +# reduce 93 omitted -module_eval(<<'.,.,', 'parser.y', 380) - def _reduce_92(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 387) + def _reduce_94(val, _values, result) on_action_error("ident after %prec", val[0]) if @prec_seen result end .,., -module_eval(<<'.,.,', 'parser.y', 381) - def _reduce_93(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 388) + def _reduce_95(val, _values, result) on_action_error("char after %prec", val[0]) if @prec_seen result end .,., -# reduce 94 omitted - -# reduce 95 omitted - # reduce 96 omitted # reduce 97 omitted -module_eval(<<'.,.,', 'parser.y', 391) - def _reduce_98(val, _values, result) +# reduce 98 omitted + +# reduce 99 omitted + +module_eval(<<'.,.,', 'parser.y', 398) + def _reduce_100(val, _values, result) lhs = val[0] lhs.alias_name = val[1] val[3].each do |builder| @@ -1960,8 +1982,8 @@ module_eval(<<'.,.,', 'parser.y', 391) end .,., -module_eval(<<'.,.,', 'parser.y', 402) - def _reduce_99(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 409) + def _reduce_101(val, _values, result) builder = val[0] if !builder.line builder.line = @lexer.line - 1 @@ -1972,8 +1994,8 @@ module_eval(<<'.,.,', 'parser.y', 402) end .,., -module_eval(<<'.,.,', 'parser.y', 410) - def _reduce_100(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 417) + def _reduce_102(val, _values, result) builder = val[2] if !builder.line builder.line = @lexer.line - 1 @@ -1984,10 +2006,10 @@ module_eval(<<'.,.,', 'parser.y', 410) end .,., -# reduce 101 omitted +# reduce 103 omitted -module_eval(<<'.,.,', 'parser.y', 420) - def _reduce_102(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 427) + def _reduce_104(val, _values, result) reset_precs result = Grammar::RuleBuilder.new(@rule_counter, @midrule_action_counter) @@ -1995,8 +2017,8 @@ module_eval(<<'.,.,', 'parser.y', 420) end .,., -module_eval(<<'.,.,', 'parser.y', 425) - def _reduce_103(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 432) + def _reduce_105(val, _values, result) reset_precs result = Grammar::RuleBuilder.new(@rule_counter, @midrule_action_counter) @@ -2004,8 +2026,8 @@ module_eval(<<'.,.,', 'parser.y', 425) end .,., -module_eval(<<'.,.,', 'parser.y', 430) - def _reduce_104(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 437) + def _reduce_106(val, _values, result) token = val[1] token.alias_name = val[2] builder = val[0] @@ -2016,8 +2038,8 @@ module_eval(<<'.,.,', 'parser.y', 430) end .,., -module_eval(<<'.,.,', 'parser.y', 438) - def _reduce_105(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 445) + def _reduce_107(val, _values, result) token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]], lhs_tag: val[3]) builder = val[0] builder.add_rhs(token) @@ -2028,8 +2050,8 @@ module_eval(<<'.,.,', 'parser.y', 438) end .,., -module_eval(<<'.,.,', 'parser.y', 446) - def _reduce_106(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 453) + def _reduce_108(val, _values, result) token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3], lhs_tag: val[5]) builder = val[0] builder.add_rhs(token) @@ -2040,8 +2062,8 @@ module_eval(<<'.,.,', 'parser.y', 446) end .,., -module_eval(<<'.,.,', 'parser.y', 454) - def _reduce_107(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 461) + def _reduce_109(val, _values, result) if @prec_seen on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec @code_after_prec = true @@ -2052,16 +2074,16 @@ module_eval(<<'.,.,', 'parser.y', 454) end .,., -module_eval(<<'.,.,', 'parser.y', 462) - def _reduce_108(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 469) + def _reduce_110(val, _values, result) end_c_declaration result end .,., -module_eval(<<'.,.,', 'parser.y', 466) - def _reduce_109(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 473) + def _reduce_111(val, _values, result) user_code = val[3] user_code.alias_name = val[6] user_code.tag = val[7] @@ -2073,8 +2095,8 @@ module_eval(<<'.,.,', 'parser.y', 466) end .,., -module_eval(<<'.,.,', 'parser.y', 475) - def _reduce_110(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 482) + def _reduce_112(val, _values, result) sym = @grammar.find_symbol_by_id!(val[2]) @prec_seen = true builder = val[0] @@ -2085,70 +2107,70 @@ module_eval(<<'.,.,', 'parser.y', 475) end .,., -module_eval(<<'.,.,', 'parser.y', 482) - def _reduce_111(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 489) + def _reduce_113(val, _values, result) result = "option" result end .,., -module_eval(<<'.,.,', 'parser.y', 483) - def _reduce_112(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 490) + def _reduce_114(val, _values, result) result = "nonempty_list" result end .,., -module_eval(<<'.,.,', 'parser.y', 484) - def _reduce_113(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 491) + def _reduce_115(val, _values, result) result = "list" result end .,., -module_eval(<<'.,.,', 'parser.y', 486) - def _reduce_114(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 493) + def _reduce_116(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 487) - def _reduce_115(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 494) + def _reduce_117(val, _values, result) result = val[0].append(val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 488) - def _reduce_116(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 495) + def _reduce_118(val, _values, result) result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])] result end .,., -module_eval(<<'.,.,', 'parser.y', 489) - def _reduce_117(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 496) + def _reduce_119(val, _values, result) result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[0].s_value, location: @lexer.location, args: val[2])] result end .,., -# reduce 118 omitted +# reduce 120 omitted -module_eval(<<'.,.,', 'parser.y', 492) - def _reduce_119(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 499) + def _reduce_121(val, _values, result) result = val[1].s_value result end .,., -# reduce 120 omitted +# reduce 122 omitted -# reduce 121 omitted +# reduce 123 omitted -module_eval(<<'.,.,', 'parser.y', 499) - def _reduce_122(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 506) + def _reduce_124(val, _values, result) begin_c_declaration('\Z') @grammar.epilogue_first_lineno = @lexer.line + 1 @@ -2156,8 +2178,8 @@ module_eval(<<'.,.,', 'parser.y', 499) end .,., -module_eval(<<'.,.,', 'parser.y', 504) - def _reduce_123(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 511) + def _reduce_125(val, _values, result) end_c_declaration @grammar.epilogue = val[2].s_value @@ -2165,44 +2187,44 @@ module_eval(<<'.,.,', 'parser.y', 504) end .,., -# reduce 124 omitted - -# reduce 125 omitted - # reduce 126 omitted # reduce 127 omitted # reduce 128 omitted -module_eval(<<'.,.,', 'parser.y', 515) - def _reduce_129(val, _values, result) +# reduce 129 omitted + +# reduce 130 omitted + +module_eval(<<'.,.,', 'parser.y', 522) + def _reduce_131(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 516) - def _reduce_130(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 523) + def _reduce_132(val, _values, result) result = val[0].append(val[1]) result end .,., -# reduce 131 omitted +# reduce 133 omitted -# reduce 132 omitted +# reduce 134 omitted -module_eval(<<'.,.,', 'parser.y', 521) - def _reduce_133(val, _values, result) +module_eval(<<'.,.,', 'parser.y', 528) + def _reduce_135(val, _values, result) result = Lrama::Lexer::Token::Ident.new(s_value: val[0]) result end .,., -# reduce 134 omitted +# reduce 136 omitted -# reduce 135 omitted +# reduce 137 omitted def _reduce_none(val, _values, result) val[0] diff --git a/tool/lrama/lib/lrama/state.rb b/tool/lrama/lib/lrama/state.rb index 45bfe5acf6..ceb74d856a 100644 --- a/tool/lrama/lib/lrama/state.rb +++ b/tool/lrama/lib/lrama/state.rb @@ -70,38 +70,16 @@ module Lrama reduce.look_ahead = look_ahead end - # Returns array of [Shift, next_state] def nterm_transitions - return @nterm_transitions if @nterm_transitions - - @nterm_transitions = [] - - shifts.each do |shift| - next if shift.next_sym.term? - - @nterm_transitions << [shift, @items_to_state[shift.next_items]] - end - - @nterm_transitions + @nterm_transitions ||= transitions.select {|shift, _| shift.next_sym.nterm? } end - # Returns array of [Shift, next_state] def term_transitions - return @term_transitions if @term_transitions - - @term_transitions = [] - - shifts.each do |shift| - next if shift.next_sym.nterm? - - @term_transitions << [shift, @items_to_state[shift.next_items]] - end - - @term_transitions + @term_transitions ||= transitions.select {|shift, _| shift.next_sym.term? } end def transitions - term_transitions + nterm_transitions + @transitions ||= shifts.map {|shift| [shift, @items_to_state[shift.next_items]] } end def selected_term_transitions diff --git a/tool/lrama/lib/lrama/version.rb b/tool/lrama/lib/lrama/version.rb index ccd593f344..ef840ce435 100644 --- a/tool/lrama/lib/lrama/version.rb +++ b/tool/lrama/lib/lrama/version.rb @@ -1,3 +1,3 @@ module Lrama - VERSION = "0.6.5".freeze + VERSION = "0.6.9".freeze end diff --git a/tool/merger.rb b/tool/merger.rb index d181a77f84..0d9957074f 100755 --- a/tool/merger.rb +++ b/tool/merger.rb @@ -57,11 +57,11 @@ class << Merger yield if block_given? STDERR.puts "\e[1;33m#{str} ([y]es|[a]bort|[r]etry#{'|[e]dit' if editfile})\e[0m" case STDIN.gets - when /\Aa/i then exit + when /\Aa/i then exit 1 when /\Ar/i then redo when /\Ay/i then break when /\Ae/i then system(ENV['EDITOR'], editfile) - else exit + else exit 1 end end end @@ -324,7 +324,10 @@ else end patch = resp.body.sub(/^diff --git a\/version\.h b\/version\.h\nindex .*\n--- a\/version\.h\n\+\+\+ b\/version\.h\n@@ .* @@\n(?:[-\+ ].*\n|\n)+/, '') - message = "\n\n#{(patch[/^Subject: (.*)\n\ndiff --git/m, 1] || "Message not found for revision: #{git_rev}\n")}" + message = "#{(patch[/^Subject: (.*)\n---\n /m, 1] || "Message not found for revision: #{git_rev}\n")}" + message.gsub!(/\G(.*)\n( .*)/, "\\1\\2") + message = "\n\n#{message}" + puts '+ git apply' IO.popen(['git', 'apply', '--3way'], 'wb') { |f| f.write(patch) } else diff --git a/tool/rdoc-srcdir b/tool/rdoc-srcdir index 10c63caf9e..10c63caf9e 100644..100755 --- a/tool/rdoc-srcdir +++ b/tool/rdoc-srcdir diff --git a/tool/rubyspec_temp.rb b/tool/rubyspec_temp.rb deleted file mode 100644 index 339bfce211..0000000000 --- a/tool/rubyspec_temp.rb +++ /dev/null @@ -1,13 +0,0 @@ -require "tmpdir" -require "fileutils" - -if (tmpdir = Dir.mktmpdir("rubyspec_temp.")).size > 80 - # On macOS, the default TMPDIR is very long, inspite of UNIX socket - # path length is limited. - Dir.rmdir(tmpdir) - tmpdir = Dir.mktmpdir("rubyspec_temp.", "/tmp") -end -# warn "tmpdir(#{tmpdir.size}) = #{tmpdir}" -END {FileUtils.rm_rf(tmpdir)} - -ENV["TMPDIR"] = ENV["SPEC_TEMP_DIR"] = tmpdir diff --git a/tool/test/testunit/test_assertion.rb b/tool/test/testunit/test_assertion.rb index 709b495572..1e19c102b8 100644 --- a/tool/test/testunit/test_assertion.rb +++ b/tool/test/testunit/test_assertion.rb @@ -50,4 +50,17 @@ class TestAssertion < Test::Unit::TestCase assert_pattern_list(pattern_list, actual, message) end end + + def test_caller_bactrace_location + begin + line = __LINE__; assert_fail_for_backtrace_location + rescue Test::Unit::AssertionFailedError => e + end + location = Test::Unit::Runner.new.location(e) + assert_equal "#{__FILE__}:#{line}", location + end + + def assert_fail_for_backtrace_location + assert false + end end diff --git a/tool/test_for_warn_bundled_gems/test.sh b/tool/test_for_warn_bundled_gems/test.sh index 2404571daf..a14d5bcedc 100755 --- a/tool/test_for_warn_bundled_gems/test.sh +++ b/tool/test_for_warn_bundled_gems/test.sh @@ -32,6 +32,10 @@ echo "* Show warning with bootsnap" ruby test_warn_bootsnap.rb echo +echo "* Show warning with bootsnap for gem with native extension" +ruby test_warn_bootsnap_rubyarchdir_gem.rb +echo + echo "* Show warning with zeitwerk" ruby test_warn_zeitwerk.rb echo diff --git a/tool/test_for_warn_bundled_gems/test_warn_bootsnap_rubyarchdir_gem.rb b/tool/test_for_warn_bundled_gems/test_warn_bootsnap_rubyarchdir_gem.rb new file mode 100644 index 0000000000..477933f6f2 --- /dev/null +++ b/tool/test_for_warn_bundled_gems/test_warn_bootsnap_rubyarchdir_gem.rb @@ -0,0 +1,11 @@ +require "bundler/inline" + +gemfile do + source "https://rubygems.org" + gem "bootsnap", require: false +end + +require 'bootsnap' +Bootsnap.setup(cache_dir: 'tmp/cache') + +require 'syslog' diff --git a/tool/update-NEWS-refs.rb b/tool/update-NEWS-refs.rb index 2b19f0fdaa..f48cac5ee1 100644 --- a/tool/update-NEWS-refs.rb +++ b/tool/update-NEWS-refs.rb @@ -13,8 +13,9 @@ if links.empty? || lines.last != "" raise "NEWS.md must end with a sequence of links" end -labels = links.keys.select {|k| !(k.start_with?("Feature") || k.start_with?("Bug"))} -new_src = lines.join("\n").gsub(/\[?\[((?:Feature|Bug)\s+#(\d+))\]\]?/) do +trackers = ["Feature", "Bug", "Misc"] +labels = links.keys.reject {|k| k.start_with?(*trackers)} +new_src = lines.join("\n").gsub(/\[?\[(#{Regexp.union(trackers)}\s+#(\d+))\]\]?/) do links[$1] ||= "https://bugs.ruby-lang.org/issues/#$2" "[[#$1]]" end.gsub(/\[\[#{Regexp.union(labels)}\]\]?/) do @@ -22,7 +23,7 @@ end.gsub(/\[\[#{Regexp.union(labels)}\]\]?/) do end.chomp + "\n\n" label_width = links.max_by {|k, _| k.size}.first.size + 4 -redmine_links, non_redmine_links = links.partition {|k,| k =~ /\A(Feature|Bug)\s+#\d+\z/ } +redmine_links, non_redmine_links = links.partition {|k,| k =~ /\A#{Regexp.union(trackers)}\s+#\d+\z/ } (redmine_links.sort_by {|k,| k[/\d+/].to_i } + non_redmine_links.reverse).each do |k, v| new_src << "[#{k}]:".ljust(label_width) << v << "\n" diff --git a/universal_parser.c b/universal_parser.c index 2055681889..c5e557ca30 100644 --- a/universal_parser.c +++ b/universal_parser.c @@ -59,7 +59,7 @@ #undef st_lookup #define st_lookup rb_parser_st_lookup -#define rb_encoding void +#define rb_encoding const void #undef xmalloc #define xmalloc p->config->malloc @@ -95,9 +95,6 @@ #undef rb_ary_new_from_args #define rb_ary_new_from_args p->config->ary_new_from_args #define rb_ary_unshift p->config->ary_unshift -#undef RARRAY_LEN -#define RARRAY_LEN p->config->array_len -#define RARRAY_AREF p->config->array_aref #define rb_make_temporary_id p->config->make_temporary_id #define is_local_id p->config->is_local_id @@ -122,8 +119,6 @@ #define rb_str_catf p->config->str_catf #undef rb_str_cat_cstr #define rb_str_cat_cstr p->config->str_cat_cstr -#define rb_str_subseq p->config->str_subseq -#define rb_str_new_frozen p->config->str_new_frozen #define rb_str_modify p->config->str_modify #define rb_str_set_len p->config->str_set_len #define rb_str_cat p->config->str_cat @@ -145,7 +140,6 @@ #define RSTRING_END p->config->rstring_end #undef RSTRING_LEN #define RSTRING_LEN p->config->rstring_len -#define rb_filesystem_str_new_cstr p->config->filesystem_str_new_cstr #define rb_obj_as_string p->config->obj_as_string #undef INT2NUM @@ -156,7 +150,6 @@ #define rb_io_write p->config->io_write #define rb_io_flush p->config->io_flush #define rb_io_puts p->config->io_puts -#define rb_io_gets_internal p->config->io_gets_internal #define rb_ractor_stdout p->config->debug_output_stdout #define rb_ractor_stderr p->config->debug_output_stderr @@ -175,14 +168,12 @@ #define rb_ascii8bit_encoding p->config->ascii8bit_encoding #define rb_enc_codelen p->config->enc_codelen #define rb_enc_mbcput p->config->enc_mbcput +#define rb_enc_mbclen p->config->enc_mbclen #define rb_enc_find_index p->config->enc_find_index #define rb_enc_from_index p->config->enc_from_index #define rb_enc_isspace p->config->enc_isspace #define ENC_CODERANGE_7BIT p->config->enc_coderange_7bit #define ENC_CODERANGE_UNKNOWN p->config->enc_coderange_unknown -#define rb_enc_compatible p->config->enc_compatible -#define rb_enc_from_encoding p->config->enc_from_encoding -#define ENCODING_IS_ASCII8BIT p->config->encoding_is_ascii8bit #define rb_usascii_encoding p->config->usascii_encoding #define rb_local_defined p->config->local_defined @@ -223,16 +214,12 @@ #define ruby_scan_digits p->config->scan_digits #define strtod p->config->strtod -#undef RBOOL -#define RBOOL p->config->rbool #undef RTEST #define RTEST p->config->rtest #undef NIL_P #define NIL_P p->config->nil_p #undef Qnil #define Qnil p->config->qnil -#undef Qtrue -#define Qtrue p->config->qtrue #undef Qfalse #define Qfalse p->config->qfalse #define rb_eArgError p->config->eArgError() @@ -1007,6 +1007,11 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co } #endif + // Invalidate JIT code that assumes cfp->ep == vm_base_ptr(cfp). + if (env->iseq) { + rb_yjit_invalidate_ep_is_bp(env->iseq); + } + return (VALUE)env; } @@ -1458,7 +1463,6 @@ rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const I const rb_env_t *env; rb_execution_context_t *ec = GET_EC(); const rb_iseq_t *base_iseq, *iseq; - rb_ast_body_t ast; rb_node_scope_t tmp_node; if (dyncount < 0) return 0; @@ -1476,17 +1480,14 @@ rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const I tmp_node.nd_body = 0; tmp_node.nd_args = 0; - ast.root = RNODE(&tmp_node); - ast.frozen_string_literal = -1; - ast.coverage_enabled = -1; - ast.script_lines = (rb_parser_ary_t *)INT2FIX(-1); + VALUE ast_value = rb_ruby_ast_new(RNODE(&tmp_node)); if (base_iseq) { - iseq = rb_iseq_new(&ast, ISEQ_BODY(base_iseq)->location.label, path, realpath, base_iseq, ISEQ_TYPE_EVAL); + iseq = rb_iseq_new(ast_value, ISEQ_BODY(base_iseq)->location.label, path, realpath, base_iseq, ISEQ_TYPE_EVAL); } else { VALUE tempstr = rb_fstring_lit("<temp>"); - iseq = rb_iseq_new_top(&ast, tempstr, tempstr, tempstr, NULL); + iseq = rb_iseq_new_top(ast_value, tempstr, tempstr, tempstr, NULL); } tmp_node.nd_tbl = 0; /* reset table */ ALLOCV_END(idtmp); @@ -2859,7 +2860,7 @@ rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, { rb_execution_context_t *ec = GET_EC(); const rb_control_frame_t *reg_cfp = ec->cfp; - const rb_iseq_t *iseq = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP); + const rb_iseq_t *iseq = rb_iseq_new(Qnil, filename, filename, Qnil, 0, ISEQ_TYPE_TOP); VALUE val; vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, @@ -3076,6 +3077,7 @@ ruby_vm_destruct(rb_vm_t *vm) rb_vm_postponed_job_free(); rb_id_table_free(vm->constant_cache); + st_free_table(vm->unused_block_warning_table); if (th) { xfree(th->nt); @@ -3301,7 +3303,6 @@ vm_default_params_setup(rb_vm_t *vm) static void vm_init2(rb_vm_t *vm) { - MEMZERO(vm, rb_vm_t, 1); rb_vm_living_threads_init(vm); vm->thread_report_on_exception = 1; vm->src_encoding_index = -1; @@ -4175,7 +4176,7 @@ Init_VM(void) rb_vm_t *vm = ruby_current_vm_ptr; rb_thread_t *th = GET_THREAD(); VALUE filename = rb_fstring_lit("<main>"); - const rb_iseq_t *iseq = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP); + const rb_iseq_t *iseq = rb_iseq_new(Qnil, filename, filename, Qnil, 0, ISEQ_TYPE_TOP); // Ractor setup rb_ractor_main_setup(vm, th->ractor, th); @@ -4237,15 +4238,14 @@ void Init_BareVM(void) { /* VM bootstrap: phase 1 */ - rb_vm_t * vm = ruby_mimmalloc(sizeof(*vm)); - rb_thread_t * th = ruby_mimmalloc(sizeof(*th)); + rb_vm_t *vm = ruby_mimcalloc(1, sizeof(*vm)); + rb_thread_t *th = ruby_mimcalloc(1, sizeof(*th)); if (!vm || !th) { fputs("[FATAL] failed to allocate memory\n", stderr); exit(EXIT_FAILURE); } // setup the VM - MEMZERO(th, rb_thread_t, 1); vm_init2(vm); rb_vm_postponed_job_queue_init(vm); @@ -4256,6 +4256,12 @@ Init_BareVM(void) vm->constant_cache = rb_id_table_create(0); vm->unused_block_warning_table = st_init_numtable(); + // TODO: remove before Ruby 3.4.0 release + const char *s = getenv("RUBY_TRY_UNUSED_BLOCK_WARNING_STRICT"); + if (s && strcmp(s, "1") == 0) { + vm->unused_block_warning_strict = true; + } + // setup main thread th->nt = ZALLOC(struct rb_native_thread); th->vm = vm; diff --git a/vm_callinfo.h b/vm_callinfo.h index 21e0755aa8..71ab9fe3fa 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -240,21 +240,6 @@ vm_ci_new_runtime_(ID mid, unsigned int flag, unsigned int argc, const struct rb #define VM_CALLINFO_NOT_UNDER_GC IMEMO_FL_USER0 -static inline bool -vm_ci_markable(const struct rb_callinfo *ci) -{ - if (! ci) { - return false; /* or true? This is Qfalse... */ - } - else if (vm_ci_packed_p(ci)) { - return true; - } - else { - VM_ASSERT(IMEMO_TYPE_P(ci, imemo_callinfo)); - return ! FL_ANY_RAW((VALUE)ci, VM_CALLINFO_NOT_UNDER_GC); - } -} - #define VM_CI_ON_STACK(mid_, flags_, argc_, kwarg_) \ (struct rb_callinfo) { \ .flags = T_IMEMO | \ @@ -575,7 +560,8 @@ struct rb_class_cc_entries { int len; const struct rb_callable_method_entry_struct *cme; struct rb_class_cc_entries_entry { - const struct rb_callinfo *ci; + unsigned int argc; + unsigned int flag; const struct rb_callcache *cc; } *entries; }; @@ -108,10 +108,10 @@ extern int ruby_assert_critical_section_entered; #if USE_SHARED_GC typedef struct gc_function_map { - void *(*init)(void); + void *(*objspace_alloc)(void); } rb_gc_function_map_t; -#define rb_gc_functions (GET_VM()->gc_functions_map) +#define rb_gc_functions (&GET_VM()->gc_functions_map) #endif /* @@ -761,7 +761,7 @@ typedef struct rb_vm_struct { struct rb_objspace *objspace; #if USE_SHARED_GC - rb_gc_function_map_t *gc_functions_map; + rb_gc_function_map_t gc_functions_map; #endif rb_at_exit_list *at_exit; @@ -774,6 +774,7 @@ typedef struct rb_vm_struct { struct rb_id_table *negative_cme_table; st_table *overloaded_cme_table; // cme -> overloaded_cme st_table *unused_block_warning_table; + bool unused_block_warning_strict; // This id table contains a mapping from ID to ICs. It does this with ID // keys and nested st_tables as values. The nested tables have ICs as keys @@ -1208,11 +1209,11 @@ typedef enum { RUBY_SYMBOL_EXPORT_BEGIN /* node -> iseq */ -rb_iseq_t *rb_iseq_new (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type); -rb_iseq_t *rb_iseq_new_top (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent); -rb_iseq_t *rb_iseq_new_main (const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt); -rb_iseq_t *rb_iseq_new_eval (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth); -rb_iseq_t *rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, +rb_iseq_t *rb_iseq_new (const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type); +rb_iseq_t *rb_iseq_new_top (const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent); +rb_iseq_t *rb_iseq_new_main (const VALUE ast_value, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt); +rb_iseq_t *rb_iseq_new_eval (const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth); +rb_iseq_t *rb_iseq_new_with_opt(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t*, VALUE script_lines); @@ -29,15 +29,6 @@ static VALUE rb_eUncaughtThrow; static ID id_result, id_tag, id_value; #define id_mesg idMesg -typedef enum call_type { - CALL_PUBLIC, - CALL_FCALL, - CALL_VCALL, - CALL_PUBLIC_KW, - CALL_FCALL_KW, - CALL_TYPE_MAX -} call_type; - static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope); static VALUE vm_call0_body(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv); @@ -403,71 +394,53 @@ NORETURN(static void uncallable_object(VALUE recv, ID mid)); static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid); static inline enum method_missing_reason rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self); -static const struct rb_callcache * -cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme) -{ - const struct rb_callcache *cc = NULL; - - RB_VM_LOCK_ENTER(); - { - struct rb_class_cc_entries *ccs; - struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass); - VALUE ccs_data; - - if (rb_id_table_lookup(cc_tbl, mid, &ccs_data)) { - // ok - ccs = (struct rb_class_cc_entries *)ccs_data; - } - else { - ccs = vm_ccs_create(klass, cc_tbl, mid, cme); - } - - for (int i=0; i<ccs->len; i++) { - cc = ccs->entries[i].cc; - if (vm_cc_cme(cc) == cme) { - break; - } - cc = NULL; - } - - if (cc == NULL) { - const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, NULL); // TODO: proper ci - cc = vm_cc_new(klass, cme, vm_call_general, cc_type_normal); - METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme); - vm_ccs_push(klass, ccs, ci, cc); - } - } - RB_VM_LOCK_LEAVE(); - - return cc; -} - static VALUE gccct_hash(VALUE klass, ID mid) { return (klass >> 3) ^ (VALUE)mid; } -NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index)); +NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo * ci)); static const struct rb_callcache * -gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index) +gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo *ci) { - const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid); - const struct rb_callcache *cc; + struct rb_call_data cd = { + .ci = ci, + .cc = NULL + }; - if (cme != NULL) { - cc = cc_new(klass, mid, argc, cme); - } - else { - cc = NULL; - } + vm_search_method_slowpath0(vm->self, &cd, klass); + + return vm->global_cc_cache_table[index] = cd.cc; +} + +static void +scope_to_ci(call_type scope, ID mid, int argc, struct rb_callinfo *ci) +{ + int flags = 0; - return vm->global_cc_cache_table[index] = cc; + switch(scope) { + case CALL_PUBLIC: + break; + case CALL_FCALL: + flags |= VM_CALL_FCALL; + break; + case CALL_VCALL: + flags |= VM_CALL_VCALL; + break; + case CALL_PUBLIC_KW: + flags |= VM_CALL_KWARG; + break; + case CALL_FCALL_KW: + flags |= (VM_CALL_KWARG | VM_CALL_FCALL); + break; + } + *ci = VM_CI_ON_STACK(mid, flags, argc, NULL); } static inline const struct rb_callcache * -gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc) +gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct rb_callinfo *ci) { VALUE klass; @@ -502,7 +475,7 @@ gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc) } RB_DEBUG_COUNTER_INC(gccct_miss); - return gccct_method_search_slowpath(vm, klass, mid, argc, index); + return gccct_method_search_slowpath(vm, klass, index, ci); } /** @@ -543,7 +516,10 @@ rb_call0(rb_execution_context_t *ec, break; } - const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc); + struct rb_callinfo ci; + scope_to_ci(scope, mid, argc, &ci); + + const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci); if (scope == CALL_PUBLIC) { RB_DEBUG_COUNTER_INC(call0_public); @@ -1060,7 +1036,11 @@ static inline VALUE rb_funcallv_scope(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope) { rb_execution_context_t *ec = GET_EC(); - const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc); + + struct rb_callinfo ci; + scope_to_ci(scope, mid, argc, &ci); + + const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci); VALUE self = ec->cfp->self; if (LIKELY(cc) && @@ -1773,6 +1753,7 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const VALUE parser = rb_parser_new(); const rb_iseq_t *const parent = vm_block_iseq(base_block); rb_iseq_t *iseq = NULL; + VALUE ast_value; rb_ast_t *ast; int isolated_depth = 0; @@ -1810,10 +1791,13 @@ eval_make_iseq(VALUE src, VALUE fname, int line, rb_parser_set_context(parser, parent, FALSE); if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser); - ast = rb_parser_compile_string_path(parser, fname, src, line); + ast_value = rb_parser_compile_string_path(parser, fname, src, line); + + ast = rb_ruby_ast_data_get(ast_value); + if (ast->body.root) { ast->body.coverage_enabled = coverage_enabled; - iseq = rb_iseq_new_eval(&ast->body, + iseq = rb_iseq_new_eval(ast_value, ISEQ_BODY(parent)->location.label, fname, Qnil, line, parent, isolated_depth); diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 84ef212053..a1893b1ba2 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -2006,9 +2006,6 @@ vm_ccs_push(VALUE klass, struct rb_class_cc_entries *ccs, const struct rb_callin if (! vm_cc_markable(cc)) { return; } - else if (! vm_ci_markable(ci)) { - return; - } if (UNLIKELY(ccs->len == ccs->capa)) { if (ccs->capa == 0) { @@ -2023,7 +2020,8 @@ vm_ccs_push(VALUE klass, struct rb_class_cc_entries *ccs, const struct rb_callin VM_ASSERT(ccs->len < ccs->capa); const int pos = ccs->len++; - RB_OBJ_WRITE(klass, &ccs->entries[pos].ci, ci); + ccs->entries[pos].argc = vm_ci_argc(ci); + ccs->entries[pos].flag = vm_ci_flag(ci); RB_OBJ_WRITE(klass, &ccs->entries[pos].cc, cc); if (RB_DEBUG_COUNTER_SETMAX(ccs_maxlen, ccs->len)) { @@ -2038,7 +2036,9 @@ rb_vm_ccs_dump(struct rb_class_cc_entries *ccs) { ruby_debug_printf("ccs:%p (%d,%d)\n", (void *)ccs, ccs->len, ccs->capa); for (int i=0; i<ccs->len; i++) { - vm_ci_dump(ccs->entries[i].ci); + ruby_debug_printf("CCS CI ID:flag:%x argc:%u\n", + ccs->entries[i].flag, + ccs->entries[i].argc); rp(ccs->entries[i].cc); } } @@ -2050,11 +2050,8 @@ vm_ccs_verify(struct rb_class_cc_entries *ccs, ID mid, VALUE klass) VM_ASSERT(ccs->len <= ccs->capa); for (int i=0; i<ccs->len; i++) { - const struct rb_callinfo *ci = ccs->entries[i].ci; const struct rb_callcache *cc = ccs->entries[i].cc; - VM_ASSERT(vm_ci_p(ci)); - VM_ASSERT(vm_ci_mid(ci) == mid); VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(vm_cc_class_check(cc, klass)); VM_ASSERT(vm_cc_check_cme(cc, ccs->cme)); @@ -2076,6 +2073,8 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci) VALUE ccs_data; if (cc_tbl) { + // CCS data is keyed on method id, so we don't need the method id + // for doing comparisons in the `for` loop below. if (rb_id_table_lookup(cc_tbl, mid, &ccs_data)) { ccs = (struct rb_class_cc_entries *)ccs_data; const int ccs_len = ccs->len; @@ -2088,14 +2087,20 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci) else { VM_ASSERT(vm_ccs_verify(ccs, mid, klass)); + // We already know the method id is correct because we had + // to look up the ccs_data by method id. All we need to + // compare is argc and flag + unsigned int argc = vm_ci_argc(ci); + unsigned int flag = vm_ci_flag(ci); + for (int i=0; i<ccs_len; i++) { - const struct rb_callinfo *ccs_ci = ccs->entries[i].ci; + unsigned int ccs_ci_argc = ccs->entries[i].argc; + unsigned int ccs_ci_flag = ccs->entries[i].flag; const struct rb_callcache *ccs_cc = ccs->entries[i].cc; - VM_ASSERT(vm_ci_p(ccs_ci)); VM_ASSERT(IMEMO_TYPE_P(ccs_cc, imemo_callcache)); - if (ccs_ci == ci) { // TODO: equality + if (ccs_ci_argc == argc && ccs_ci_flag == flag) { RB_DEBUG_COUNTER_INC(cc_found_in_ccs); VM_ASSERT(vm_cc_cme(ccs_cc)->called_id == mid); @@ -2416,13 +2421,13 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd) #undef EQ_UNREDEFINED_P -static inline const struct rb_callcache *gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc); // vm_eval.c +static inline const struct rb_callcache *gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct rb_callinfo *ci); // vm_eval.c NOINLINE(static VALUE opt_equality_by_mid_slowpath(VALUE recv, VALUE obj, ID mid)); static VALUE opt_equality_by_mid_slowpath(VALUE recv, VALUE obj, ID mid) { - const struct rb_callcache *cc = gccct_method_search(GET_EC(), recv, mid, 1); + const struct rb_callcache *cc = gccct_method_search(GET_EC(), recv, mid, &VM_CI_ON_STACK(mid, 0, 1, NULL)); if (cc && check_cfunc(vm_cc_cme(cc), rb_obj_equal)) { return RBOOL(recv == obj); @@ -2973,6 +2978,7 @@ warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq, { rb_vm_t *vm = GET_VM(); st_table *dup_check_table = vm->unused_block_warning_table; + st_data_t key; union { VALUE v; @@ -2984,14 +2990,17 @@ warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq, }; // relax check - st_data_t key = (st_data_t)cme->def->original_id; + if (!vm->unused_block_warning_strict) { + key = (st_data_t)cme->def->original_id; - if (st_lookup(dup_check_table, key, NULL)) { - return; + if (st_lookup(dup_check_table, key, NULL)) { + return; + } } // strict check // make unique key from pc and me->def pointer + key = 0; for (int i=0; i<SIZEOF_VALUE; i++) { // fprintf(stderr, "k1:%3d k2:%3d\n", k1.b[i], k2.b[SIZEOF_VALUE-1-i]); key |= (st_data_t)(k1.b[i] ^ k2.b[SIZEOF_VALUE-1-i]) << (8 * i); @@ -3027,7 +3036,6 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, { const struct rb_callinfo *ci = calling->cd->ci; const struct rb_callcache *cc = calling->cc; - bool cacheable_ci = vm_ci_markable(ci); if (UNLIKELY(!ISEQ_BODY(iseq)->param.flags.use_block && calling->block_handler != VM_BLOCK_HANDLER_NONE && @@ -3048,7 +3056,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, VM_ASSERT(ci == calling->cd->ci); VM_ASSERT(cc == calling->cc); - if (cacheable_ci && vm_call_iseq_optimizable_p(ci, cc)) { + if (vm_call_iseq_optimizable_p(ci, cc)) { if ((iseq->body->builtin_attrs & BUILTIN_ATTR_SINGLE_NOARG_LEAF) && !(ruby_vm_event_flags & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN))) { VM_ASSERT(iseq->body->builtin_attrs & BUILTIN_ATTR_LEAF); @@ -3078,12 +3086,12 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, if (LIKELY(!(vm_ci_flag(ci) & VM_CALL_TAILCALL))) { CC_SET_FASTPATH(cc, vm_call_iseq_setup_normal_opt_start, !IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) && - cacheable_ci && vm_call_cacheable(ci, cc)); + vm_call_cacheable(ci, cc)); } else { CC_SET_FASTPATH(cc, vm_call_iseq_setup_tailcall_opt_start, !IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) && - cacheable_ci && vm_call_cacheable(ci, cc)); + vm_call_cacheable(ci, cc)); } /* initialize opt vars for self-references */ @@ -3111,7 +3119,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, args_setup_kw_parameters(ec, iseq, ci_kws, ci_kw_len, ci_keywords, klocals); CC_SET_FASTPATH(cc, vm_call_iseq_setup_kwparm_kwarg, - cacheable_ci && vm_call_cacheable(ci, cc)); + vm_call_cacheable(ci, cc)); return 0; } @@ -3124,7 +3132,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, if (klocals[kw_param->num] == INT2FIX(0)) { /* copy from default_values */ CC_SET_FASTPATH(cc, vm_call_iseq_setup_kwparm_nokwarg, - cacheable_ci && vm_call_cacheable(ci, cc)); + vm_call_cacheable(ci, cc)); } return 0; diff --git a/vm_insnhelper.h b/vm_insnhelper.h index 286bc1f671..926700b90a 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -58,6 +58,14 @@ RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state; VM_REG_CFP = ec->cfp; \ } while (0) +typedef enum call_type { + CALL_PUBLIC, + CALL_FCALL, + CALL_VCALL, + CALL_PUBLIC_KW, + CALL_FCALL_KW +} call_type; + #if VM_COLLECT_USAGE_DETAILS enum vm_regan_regtype { VM_REGAN_PC = 0, diff --git a/vm_method.c b/vm_method.c index 5ca302a86a..3cacc010b8 100644 --- a/vm_method.c +++ b/vm_method.c @@ -31,7 +31,6 @@ vm_ccs_dump_i(ID mid, VALUE val, void *data) rp(ccs->cme); for (int i=0; i<ccs->len; i++) { - fprintf(stderr, " | [%d]\t", i); vm_ci_dump(ccs->entries[i].ci); rp_m( " | \t", ccs->entries[i].cc); } @@ -428,7 +427,6 @@ rb_vm_ci_lookup(ID mid, unsigned int flag, unsigned int argc, const struct rb_ca RB_VM_LOCK_LEAVE(); VM_ASSERT(ci); - VM_ASSERT(vm_ci_markable(ci)); return ci; } @@ -1016,7 +1014,6 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil old_def->type != VM_METHOD_TYPE_ALIAS) { const rb_iseq_t *iseq = 0; - rb_warning("method redefined; discarding old %"PRIsVALUE, rb_id2str(mid)); switch (old_def->type) { case VM_METHOD_TYPE_ISEQ: iseq = def_iseq_ptr(old_def); @@ -1028,10 +1025,16 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil break; } if (iseq) { - rb_compile_warning(RSTRING_PTR(rb_iseq_path(iseq)), - ISEQ_BODY(iseq)->location.first_lineno, - "previous definition of %"PRIsVALUE" was here", - rb_id2str(old_def->original_id)); + rb_warning( + "method redefined; discarding old %"PRIsVALUE"\n%s:%d: warning: previous definition of %"PRIsVALUE" was here", + rb_id2str(mid), + RSTRING_PTR(rb_iseq_path(iseq)), + ISEQ_BODY(iseq)->location.first_lineno, + rb_id2str(old_def->original_id) + ); + } + else { + rb_warning("method redefined; discarding old %"PRIsVALUE, rb_id2str(mid)); } } } @@ -586,7 +586,7 @@ wmap_size(VALUE self) * the key and the object as the value. This means that the key is of the type * `VALUE *` while the value is of the type `VALUE`. * - * The object is not not directly stored as keys in the table because + * The object is not directly stored as keys in the table because * `rb_gc_mark_weak` requires a pointer to the memory location to overwrite * when the object is reclaimed. Using a pointer into the ST table entry is not * safe because the pointer can change when the ST table is resized. @@ -985,7 +985,7 @@ wkmap_inspect(VALUE self) * * Keys in the map are compared by identity. * - * m = ObjectSpace::WeekMap.new + * m = ObjectSpace::WeakMap.new * key1 = "foo" * val1 = Object.new * m[key1] = val1 @@ -1041,13 +1041,13 @@ wkmap_inspect(VALUE self) * * val = nil * GC.start - * # There is no more references to `val`, yet the pair isn't + * # There are no more references to `val`, yet the pair isn't * # garbage-collected. * map["name"] #=> 2023-12-07 00:00:00 +0200 * * key = nil * GC.start - * # There is no more references to `key`, key and value are + * # There are no more references to `key`, key and value are * # garbage-collected. * map["name"] #=> nil * @@ -629,6 +629,12 @@ rb_get_iseq_body_stack_max(const rb_iseq_t *iseq) return iseq->body->stack_max; } +enum rb_iseq_type +rb_get_iseq_body_type(const rb_iseq_t *iseq) +{ + return iseq->body->type; +} + bool rb_get_iseq_flags_has_lead(const rb_iseq_t *iseq) { @@ -1041,7 +1047,6 @@ rb_yjit_multi_ractor_p(void) void rb_assert_iseq_handle(VALUE handle) { - RUBY_ASSERT_ALWAYS(rb_objspace_markable_object_p(handle)); RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_iseq)); } @@ -1158,20 +1163,14 @@ yjit_root_memsize(const void *ptr) return 0; // TODO: more accurate accounting } -// GC callback during compaction -static void -yjit_root_update_references(void *ptr) -{ - // Do nothing since we use rb_gc_mark(), which pins. -} - void rb_yjit_root_mark(void *ptr); // in Rust +void rb_yjit_root_update_references(void *ptr); // in Rust // Custom type for interacting with the GC // TODO: make this write barrier protected static const rb_data_type_t yjit_root_type = { "yjit_root", - {rb_yjit_root_mark, yjit_root_free, yjit_root_memsize, yjit_root_update_references}, + {rb_yjit_root_mark, yjit_root_free, yjit_root_memsize, rb_yjit_root_update_references}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; @@ -40,14 +40,15 @@ void rb_yjit_init(bool yjit_enabled); void rb_yjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop); void rb_yjit_constant_state_changed(ID id); void rb_yjit_iseq_mark(void *payload); -void rb_yjit_iseq_update_references(void *payload); -void rb_yjit_iseq_free(void *payload); +void rb_yjit_iseq_update_references(const rb_iseq_t *iseq); +void rb_yjit_iseq_free(const rb_iseq_t *iseq); void rb_yjit_before_ractor_spawn(void); void rb_yjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx); void rb_yjit_tracing_invalidate_all(void); void rb_yjit_show_usage(int help, int highlight, unsigned int width, int columns); void rb_yjit_lazy_push_frame(const VALUE *pc); void rb_yjit_invalidate_no_singleton_class(VALUE klass); +void rb_yjit_invalidate_ep_is_bp(const rb_iseq_t *iseq); #else // !USE_YJIT @@ -64,13 +65,14 @@ static inline void rb_yjit_init(bool yjit_enabled) {} static inline void rb_yjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {} static inline void rb_yjit_constant_state_changed(ID id) {} static inline void rb_yjit_iseq_mark(void *payload) {} -static inline void rb_yjit_iseq_update_references(void *payload) {} -static inline void rb_yjit_iseq_free(void *payload) {} +static inline void rb_yjit_iseq_update_references(const rb_iseq_t *iseq) {} +static inline void rb_yjit_iseq_free(const rb_iseq_t *iseq) {} static inline void rb_yjit_before_ractor_spawn(void) {} static inline void rb_yjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx) {} static inline void rb_yjit_tracing_invalidate_all(void) {} static inline void rb_yjit_lazy_push_frame(const VALUE *pc) {} static inline void rb_yjit_invalidate_no_singleton_class(VALUE klass) {} +static inline void rb_yjit_invalidate_ep_is_bp(const rb_iseq_t *iseq) {} #endif // #if USE_YJIT @@ -201,13 +201,27 @@ module RubyVM::YJIT # If a method or proc is passed in, get its iseq iseq = RubyVM::InstructionSequence.of(iseq) - if self.enabled? - # Produce the disassembly string - # Include the YARV iseq disasm in the string for additional context - iseq.disasm + "\n" + Primitive.rb_yjit_disasm_iseq(iseq) - else - iseq.disasm + if !self.enabled? + warn( + "YJIT needs to be enabled to produce disasm output, e.g.\n" + + "ruby --yjit-call-threshold=1 my_script.rb (see doc/yjit/yjit.md)" + ) + return nil end + + disasm_str = Primitive.rb_yjit_disasm_iseq(iseq) + + if !disasm_str + warn( + "YJIT disasm is only available when YJIT is built in dev mode, i.e.\n" + + "./configure --enable-yjit=dev (see doc/yjit/yjit.md)\n" + ) + return nil + end + + # Produce the disassembly string + # Include the YARV iseq disasm in the string for additional context + iseq.disasm + "\n" + disasm_str end # Produce a list of instructions compiled by YJIT for an iseq diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index c58df7c377..a7473c1bf6 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -174,6 +174,7 @@ fn main() { .allowlist_var("rb_cThread") .allowlist_var("rb_cArray") .allowlist_var("rb_cHash") + .allowlist_var("rb_cClass") // From include/ruby/internal/fl_type.h .allowlist_type("ruby_fl_type") @@ -298,6 +299,7 @@ fn main() { .allowlist_type("ruby_tag_type") .allowlist_type("ruby_vm_throw_flags") .allowlist_type("vm_check_match_type") + .allowlist_type("rb_iseq_type") // From yjit.c .allowlist_function("rb_iseq_(get|set)_yjit_payload") @@ -378,6 +380,7 @@ fn main() { // From internal/object.h .allowlist_function("rb_class_allocate_instance") + .allowlist_function("rb_obj_equal") // From gc.h and internal/gc.h .allowlist_function("rb_obj_info") @@ -415,6 +418,7 @@ fn main() { .allowlist_function("rb_get_iseq_body_parent_iseq") .allowlist_function("rb_get_iseq_body_iseq_encoded") .allowlist_function("rb_get_iseq_body_stack_max") + .allowlist_function("rb_get_iseq_body_type") .allowlist_function("rb_get_iseq_flags_has_lead") .allowlist_function("rb_get_iseq_flags_has_opt") .allowlist_function("rb_get_iseq_flags_has_kw") diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs index 8365c34955..a94d435b7c 100644 --- a/yjit/src/asm/arm64/mod.rs +++ b/yjit/src/asm/arm64/mod.rs @@ -215,6 +215,9 @@ pub const fn bcond_offset_fits_bits(offset: i64) -> bool { imm_fits_bits(offset, 19) } +/// CBZ and CBNZ also have a limit of 19 bits for the branch offset. +pub use bcond_offset_fits_bits as cmp_branch_offset_fits_bits; + /// B.cond - branch to target if condition is true pub fn bcond(cb: &mut CodeBlock, cond: u8, offset: InstructionOffset) { assert!(bcond_offset_fits_bits(offset.into()), "The offset must be 19 bits or less."); @@ -1096,6 +1099,48 @@ pub fn tst(cb: &mut CodeBlock, rn: A64Opnd, rm: A64Opnd) { cb.write_bytes(&bytes); } +/// CBZ - branch if a register is zero +pub fn cbz(cb: &mut CodeBlock, rt: A64Opnd, offset: InstructionOffset) { + assert!(imm_fits_bits(offset.into(), 19), "jump offset for cbz must fit in 19 bits"); + let bytes: [u8; 4] = if let A64Opnd::Reg(rt) = rt { + cbz_cbnz(rt.num_bits, false, offset, rt.reg_no) + } else { + panic!("Invalid operand combination to cbz instruction.") + }; + + cb.write_bytes(&bytes); +} + +/// CBNZ - branch if a register is non-zero +pub fn cbnz(cb: &mut CodeBlock, rt: A64Opnd, offset: InstructionOffset) { + assert!(imm_fits_bits(offset.into(), 19), "jump offset for cbz must fit in 19 bits"); + let bytes: [u8; 4] = if let A64Opnd::Reg(rt) = rt { + cbz_cbnz(rt.num_bits, true, offset, rt.reg_no) + } else { + panic!("Invalid operand combination to cbnz instruction.") + }; + + cb.write_bytes(&bytes); +} + +/// Encode Compare and Branch on Zero (CBZ) with `op=0` or Compare and Branch on Nonzero (CBNZ) +/// with `op=1`. +/// +/// <https://developer.arm.com/documentation/ddi0602/2024-03/Base-Instructions/CBZ--Compare-and-Branch-on-Zero-> +/// +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 | +/// | sf 0 1 1 0 1 0 op | +/// | imm19........................................................... Rt.............. | +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +fn cbz_cbnz(num_bits: u8, op: bool, offset: InstructionOffset, rt: u8) -> [u8; 4] { + ((Sf::from(num_bits) as u32) << 31 | + 0b11010 << 25 | + u32::from(op) << 24 | + truncate_imm::<_, 19>(offset) << 5 | + rt as u32).to_le_bytes() +} + #[cfg(test)] mod tests { use super::*; @@ -1271,6 +1316,24 @@ mod tests { } #[test] + fn test_cbz() { + let offset = InstructionOffset::from_insns(-1); + check_bytes("e0ffffb4e0ffff34", |cb| { + cbz(cb, X0, offset); + cbz(cb, W0, offset); + }); + } + + #[test] + fn test_cbnz() { + let offset = InstructionOffset::from_insns(2); + check_bytes("540000b554000035", |cb| { + cbnz(cb, X20, offset); + cbnz(cb, W20, offset); + }); + } + + #[test] fn test_brk_none() { check_bytes("000020d4", |cb| brk(cb, A64Opnd::None)); } diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index a62ea45e7e..3bf949ba7d 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -459,7 +459,34 @@ impl Assembler } asm.push_insn(insn); - }, + } + // Lower to Joz and Jonz for generating CBZ/CBNZ for compare-with-0-and-branch. + ref insn @ Insn::Cmp { ref left, right: ref right @ (Opnd::UImm(0) | Opnd::Imm(0)) } | + ref insn @ Insn::Test { ref left, right: ref right @ (Opnd::InsnOut { .. } | Opnd::Reg(_)) } if { + let same_opnd_if_test = if let Insn::Test { .. } = insn { + left == right + } else { + true + }; + + same_opnd_if_test && if let Some( + Insn::Jz(target) | Insn::Je(target) | Insn::Jnz(target) | Insn::Jne(target) + ) = iterator.peek() { + matches!(target, Target::SideExit { .. }) + } else { + false + } + } => { + let reg = split_load_operand(asm, *left); + match iterator.peek() { + Some(Insn::Jz(target) | Insn::Je(target)) => asm.push_insn(Insn::Joz(reg, *target)), + Some(Insn::Jnz(target) | Insn::Jne(target)) => asm.push_insn(Insn::Jonz(reg, *target)), + _ => () + } + + iterator.map_insn_index(asm); + iterator.next_unmapped(); // Pop merged jump instruction + } Insn::CCall { opnds, fptr, .. } => { assert!(opnds.len() <= C_ARG_OPNDS.len()); @@ -812,6 +839,45 @@ impl Assembler }; } + /// Emit a CBZ or CBNZ which branches when a register is zero or non-zero + fn emit_cmp_zero_jump(cb: &mut CodeBlock, reg: A64Opnd, branch_if_zero: bool, target: Target) { + if let Target::SideExitPtr(dst_ptr) = target { + let dst_addr = dst_ptr.as_offset(); + let src_addr = cb.get_write_ptr().as_offset(); + + if cmp_branch_offset_fits_bits((dst_addr - src_addr) / 4) { + // If the offset fits in one instruction, generate cbz or cbnz + let bytes = (dst_addr - src_addr) as i32; + if branch_if_zero { + cbz(cb, reg, InstructionOffset::from_bytes(bytes)); + } else { + cbnz(cb, reg, InstructionOffset::from_bytes(bytes)); + } + } else { + // Otherwise, we load the address into a register and + // use the branch register instruction. Note that because + // side exits should always be close, this form should be + // rare or impossible to see. + let dst_addr = dst_ptr.raw_addr(cb) as u64; + let load_insns: i32 = emit_load_size(dst_addr).into(); + + // Write out the inverse condition so that if + // it doesn't match it will skip over the + // instructions used for branching. + if branch_if_zero { + cbnz(cb, reg, InstructionOffset::from_insns(load_insns + 2)); + } else { + cbz(cb, reg, InstructionOffset::from_insns(load_insns + 2)); + } + emit_load_value(cb, Assembler::SCRATCH0, dst_addr); + br(cb, Assembler::SCRATCH0); + + } + } else { + unreachable!("We should only generate Joz/Jonz with side-exit targets"); + } + } + /// Emit a push instruction for the given operand by adding to the stack /// pointer and then storing the given value. fn emit_push(cb: &mut CodeBlock, opnd: A64Opnd) { @@ -1172,6 +1238,12 @@ impl Assembler Insn::Jo(target) => { emit_conditional_jump::<{Condition::VS}>(cb, compile_side_exit(*target, self, ocb)?); }, + Insn::Joz(opnd, target) => { + emit_cmp_zero_jump(cb, opnd.into(), true, compile_side_exit(*target, self, ocb)?); + }, + Insn::Jonz(opnd, target) => { + emit_cmp_zero_jump(cb, opnd.into(), false, compile_side_exit(*target, self, ocb)?); + }, Insn::IncrCounter { mem, value } => { let label = cb.new_label("incr_counter_loop".to_string()); cb.write_label(label); diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 1adff22f74..edc0eaf390 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -452,6 +452,12 @@ pub enum Insn { /// Jump if zero Jz(Target), + /// Jump if operand is zero (only used during lowering at the moment) + Joz(Opnd, Target), + + /// Jump if operand is non-zero (only used during lowering at the moment) + Jonz(Opnd, Target), + // Add a label into the IR at the point that this instruction is added. Label(Target), @@ -547,6 +553,9 @@ impl Insn { Insn::Jo(target) | Insn::Jz(target) | Insn::Label(target) | + Insn::JoMul(target) | + Insn::Joz(_, target) | + Insn::Jonz(_, target) | Insn::LeaJumpTarget { target, .. } => { Some(target) } @@ -595,6 +604,8 @@ impl Insn { Insn::Jo(_) => "Jo", Insn::JoMul(_) => "JoMul", Insn::Jz(_) => "Jz", + Insn::Joz(..) => "Joz", + Insn::Jonz(..) => "Jonz", Insn::Label(_) => "Label", Insn::LeaJumpTarget { .. } => "LeaJumpTarget", Insn::Lea { .. } => "Lea", @@ -755,6 +766,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> { Insn::LeaJumpTarget { .. } | Insn::PadInvalPatch | Insn::PosMarker(_) => None, + Insn::CPopInto(opnd) | Insn::CPush(opnd) | Insn::CRet(opnd) | @@ -763,6 +775,8 @@ impl<'a> Iterator for InsnOpndIterator<'a> { Insn::LiveReg { opnd, .. } | Insn::Load { opnd, .. } | Insn::LoadSExt { opnd, .. } | + Insn::Joz(opnd, _) | + Insn::Jonz(opnd, _) | Insn::Not { opnd, .. } => { match self.idx { 0 => { @@ -857,6 +871,7 @@ impl<'a> InsnOpndMutIterator<'a> { Insn::LeaJumpTarget { .. } | Insn::PadInvalPatch | Insn::PosMarker(_) => None, + Insn::CPopInto(opnd) | Insn::CPush(opnd) | Insn::CRet(opnd) | @@ -865,6 +880,8 @@ impl<'a> InsnOpndMutIterator<'a> { Insn::LiveReg { opnd, .. } | Insn::Load { opnd, .. } | Insn::LoadSExt { opnd, .. } | + Insn::Joz(opnd, _) | + Insn::Jonz(opnd, _) | Insn::Not { opnd, .. } => { match self.idx { 0 => { diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index d52ed265bd..4ca5e9be9c 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -796,6 +796,8 @@ impl Assembler } } + Insn::Joz(..) | Insn::Jonz(..) => unreachable!("Joz/Jonz should be unused for now"), + // Atomically increment a counter at a given memory location Insn::IncrCounter { mem, value } => { assert!(matches!(mem, Opnd::Mem(_))); diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 8ab5e0e230..072d96f1b0 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -46,7 +46,7 @@ type InsnGenFn = fn( /// Represents a [core::Block] while we build it. pub struct JITState { /// Instruction sequence for the compiling block - iseq: IseqPtr, + pub iseq: IseqPtr, /// The iseq index of the first instruction in the block starting_insn_idx: IseqIdx, @@ -101,6 +101,9 @@ pub struct JITState { /// A list of classes that are not supposed to have a singleton class. pub no_singleton_class_assumptions: Vec<VALUE>, + /// When true, the block is valid only when base pointer is equal to environment pointer. + pub no_ep_escape: bool, + /// When true, the block is valid only when there is a total of one ractor running pub block_assumes_single_ractor: bool, @@ -130,6 +133,7 @@ impl JITState { bop_assumptions: vec![], stable_constant_names_assumption: None, no_singleton_class_assumptions: vec![], + no_ep_escape: false, block_assumes_single_ractor: false, perf_map: Rc::default(), perf_stack: vec![], @@ -171,6 +175,23 @@ impl JITState { unsafe { *(self.pc.offset(arg_idx + 1)) } } + /// Return true if the current ISEQ could escape an environment. + /// + /// As of vm_push_frame(), EP is always equal to BP. However, after pushing + /// a frame, some ISEQ setups call vm_bind_update_env(), which redirects EP. + /// Also, some method calls escape the environment to the heap. + fn escapes_ep(&self) -> bool { + match unsafe { get_iseq_body_type(self.iseq) } { + // <main> frame is always associated to TOPLEVEL_BINDING. + ISEQ_TYPE_MAIN | + // Kernel#eval uses a heap EP when a Binding argument is not nil. + ISEQ_TYPE_EVAL => true, + // If this ISEQ has previously escaped EP, give up the optimization. + _ if iseq_escapes_ep(self.iseq) => true, + _ => false, + } + } + // Get the index of the next instruction fn next_insn_idx(&self) -> u16 { self.insn_idx + insn_len(self.get_opcode()) as u16 @@ -229,6 +250,33 @@ impl JITState { } } + pub fn assume_expected_cfunc( + &mut self, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + class: VALUE, + method: ID, + cfunc: *mut c_void, + ) -> bool { + let cme = unsafe { rb_callable_method_entry(class, method) }; + + if cme.is_null() { + return false; + } + + let def_type = unsafe { get_cme_def_type(cme) }; + if def_type != VM_METHOD_TYPE_CFUNC { + return false; + } + if unsafe { get_mct_func(get_cme_def_body_cfunc(cme)) } != cfunc { + return false; + } + + self.assume_method_lookup_stable(asm, ocb, cme); + + true + } + pub fn assume_method_lookup_stable(&mut self, asm: &mut Assembler, ocb: &mut OutlinedCb, cme: CmePtr) -> Option<()> { jit_ensure_block_entry_exit(self, asm, ocb)?; self.method_lookup_assumptions.push(cme); @@ -250,6 +298,19 @@ impl JITState { true } + /// Assume that base pointer is equal to environment pointer in the current ISEQ. + /// Return true if it's safe to assume so. + fn assume_no_ep_escape(&mut self, asm: &mut Assembler, ocb: &mut OutlinedCb) -> bool { + if jit_ensure_block_entry_exit(self, asm, ocb).is_none() { + return false; // out of space, give up + } + if self.escapes_ep() { + return false; // EP has been escaped in this ISEQ. disable the optimization to avoid an invalidation loop. + } + self.no_ep_escape = true; + true + } + fn get_cfp(&self) -> *mut rb_control_frame_struct { unsafe { get_ec_cfp(self.ec) } } @@ -533,14 +594,36 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { unsafe { CStr::from_ptr(rb_obj_info(val)).to_str().unwrap() } } + // Some types such as CString only assert the class field of the object + // when there has never been a singleton class created for objects of that class. + // Once there is a singleton class created they become their weaker + // `T*` variant, and we more objects should pass the verification. + fn relax_type_with_singleton_class_assumption(ty: Type) -> Type { + if let Type::CString | Type::CArray | Type::CHash = ty { + if has_singleton_class_of(ty.known_class().unwrap()) { + match ty { + Type::CString => return Type::TString, + Type::CArray => return Type::TArray, + Type::CHash => return Type::THash, + _ => (), + } + } + } + + ty + } + // Only able to check types when at current insn assert!(jit.at_current_insn()); let self_val = jit.peek_at_self(); let self_val_type = Type::from(self_val); + let learned_self_type = ctx.get_opnd_type(SelfOpnd); + let learned_self_type = relax_type_with_singleton_class_assumption(learned_self_type); + // Verify self operand type - if self_val_type.diff(ctx.get_opnd_type(SelfOpnd)) == TypeDiff::Incompatible { + if self_val_type.diff(learned_self_type) == TypeDiff::Incompatible { panic!( "verify_ctx: ctx self type ({:?}) incompatible with actual value of self {}", ctx.get_opnd_type(SelfOpnd), @@ -553,6 +636,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { for i in 0..top_idx { let learned_mapping = ctx.get_opnd_mapping(StackOpnd(i)); let learned_type = ctx.get_opnd_type(StackOpnd(i)); + let learned_type = relax_type_with_singleton_class_assumption(learned_type); let stack_val = jit.peek_at_stack(ctx, i as isize); let val_type = Type::from(stack_val); @@ -598,6 +682,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { let top_idx: usize = cmp::min(local_table_size as usize, MAX_TEMP_TYPES); for i in 0..top_idx { let learned_type = ctx.get_local_type(i); + let learned_type = relax_type_with_singleton_class_assumption(learned_type); let local_val = jit.peek_at_local(i as i32); let local_type = Type::from(local_val); @@ -1384,7 +1469,7 @@ fn gen_putobject( Some(KeepCompiling) } -/// Combine `putobject` and and `opt_ltlt` together if profitable, for example when +/// Combine `putobject` and `opt_ltlt` together if profitable, for example when /// left shifting an integer by a constant amount. fn fuse_putobject_opt_ltlt( jit: &mut JITState, @@ -2203,16 +2288,22 @@ fn gen_get_lep(jit: &JITState, asm: &mut Assembler) -> Opnd { fn gen_getlocal_generic( jit: &mut JITState, asm: &mut Assembler, + ocb: &mut OutlinedCb, ep_offset: u32, level: u32, ) -> Option<CodegenStatus> { - // Load environment pointer EP (level 0) from CFP - let ep_opnd = gen_get_ep(asm, level); + let local_opnd = if level == 0 && jit.assume_no_ep_escape(asm, ocb) { + // Load the local using SP register + asm.ctx.ep_opnd(-(ep_offset as i32)) + } else { + // Load environment pointer EP (level 0) from CFP + let ep_opnd = gen_get_ep(asm, level); - // Load the local from the block - // val = *(vm_get_ep(GET_EP(), level) - idx); - let offs = -(SIZEOF_VALUE_I32 * ep_offset as i32); - let local_opnd = Opnd::mem(64, ep_opnd, offs); + // Load the local from the block + // val = *(vm_get_ep(GET_EP(), level) - idx); + let offs = -(SIZEOF_VALUE_I32 * ep_offset as i32); + Opnd::mem(64, ep_opnd, offs) + }; // Write the local at SP let stack_top = if level == 0 { @@ -2230,29 +2321,29 @@ fn gen_getlocal_generic( fn gen_getlocal( jit: &mut JITState, asm: &mut Assembler, - _ocb: &mut OutlinedCb, + ocb: &mut OutlinedCb, ) -> Option<CodegenStatus> { let idx = jit.get_arg(0).as_u32(); let level = jit.get_arg(1).as_u32(); - gen_getlocal_generic(jit, asm, idx, level) + gen_getlocal_generic(jit, asm, ocb, idx, level) } fn gen_getlocal_wc0( jit: &mut JITState, asm: &mut Assembler, - _ocb: &mut OutlinedCb, + ocb: &mut OutlinedCb, ) -> Option<CodegenStatus> { let idx = jit.get_arg(0).as_u32(); - gen_getlocal_generic(jit, asm, idx, 0) + gen_getlocal_generic(jit, asm, ocb, idx, 0) } fn gen_getlocal_wc1( jit: &mut JITState, asm: &mut Assembler, - _ocb: &mut OutlinedCb, + ocb: &mut OutlinedCb, ) -> Option<CodegenStatus> { let idx = jit.get_arg(0).as_u32(); - gen_getlocal_generic(jit, asm, idx, 1) + gen_getlocal_generic(jit, asm, ocb, idx, 1) } fn gen_setlocal_generic( @@ -2264,11 +2355,11 @@ fn gen_setlocal_generic( ) -> Option<CodegenStatus> { let value_type = asm.ctx.get_opnd_type(StackOpnd(0)); - // Load environment pointer EP at level - let ep_opnd = gen_get_ep(asm, level); - // Fallback because of write barrier if asm.ctx.get_chain_depth() > 0 { + // Load environment pointer EP at level + let ep_opnd = gen_get_ep(asm, level); + // This function should not yield to the GC. // void rb_vm_env_write(const VALUE *ep, int index, VALUE v) let index = -(ep_offset as i64); @@ -2286,16 +2377,27 @@ fn gen_setlocal_generic( return Some(KeepCompiling); } - // Write barriers may be required when VM_ENV_FLAG_WB_REQUIRED is set, however write barriers - // only affect heap objects being written. If we know an immediate value is being written we - // can skip this check. - if !value_type.is_imm() { - // flags & VM_ENV_FLAG_WB_REQUIRED + let (flags_opnd, local_opnd) = if level == 0 && jit.assume_no_ep_escape(asm, ocb) { + // Load flags and the local using SP register + let local_opnd = asm.ctx.ep_opnd(-(ep_offset as i32)); + let flags_opnd = asm.ctx.ep_opnd(VM_ENV_DATA_INDEX_FLAGS as i32); + (flags_opnd, local_opnd) + } else { + // Load flags and the local for the level + let ep_opnd = gen_get_ep(asm, level); let flags_opnd = Opnd::mem( 64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_FLAGS as i32, ); + (flags_opnd, Opnd::mem(64, ep_opnd, -SIZEOF_VALUE_I32 * ep_offset as i32)) + }; + + // Write barriers may be required when VM_ENV_FLAG_WB_REQUIRED is set, however write barriers + // only affect heap objects being written. If we know an immediate value is being written we + // can skip this check. + if !value_type.is_imm() { + // flags & VM_ENV_FLAG_WB_REQUIRED asm.test(flags_opnd, VM_ENV_FLAG_WB_REQUIRED.into()); // if (flags & VM_ENV_FLAG_WB_REQUIRED) != 0 @@ -2319,8 +2421,7 @@ fn gen_setlocal_generic( let stack_top = asm.stack_pop(1); // Write the value at the environment pointer - let offs = -(SIZEOF_VALUE_I32 * ep_offset as i32); - asm.mov(Opnd::mem(64, ep_opnd, offs), stack_top); + asm.mov(local_opnd, stack_top); Some(KeepCompiling) } @@ -5879,8 +5980,10 @@ fn jit_rb_ary_push( ) -> bool { asm_comment!(asm, "Array#<<"); - // rb_ary_push allocates memory for buffer extension - jit_prepare_call_with_gc(jit, asm); + // rb_ary_push allocates memory for buffer extension and can raise FrozenError + // Not using a lazy frame here since the interpreter also has a truncated + // stack trace from opt_ltlt. + jit_prepare_non_leaf_call(jit, asm); let item_opnd = asm.stack_opnd(0); let ary_opnd = asm.stack_opnd(1); @@ -6058,6 +6161,64 @@ fn gen_block_given( asm.mov(out_opnd, block_given); } +// Codegen for rb_class_superclass() +fn jit_rb_class_superclass( + jit: &mut JITState, + asm: &mut Assembler, + _ocb: &mut OutlinedCb, + _ci: *const rb_callinfo, + cme: *const rb_callable_method_entry_t, + _block: Option<crate::codegen::BlockHandler>, + _argc: i32, + _known_recv_class: Option<VALUE>, +) -> bool { + extern "C" { + fn rb_class_superclass(klass: VALUE) -> VALUE; + } + + if !jit_prepare_lazy_frame_call(jit, asm, cme, StackOpnd(0)) { + return false; + } + + asm_comment!(asm, "Class#superclass"); + let recv_opnd = asm.stack_opnd(0); + let ret = asm.ccall(rb_class_superclass as *const u8, vec![recv_opnd]); + + asm.stack_pop(1); + let ret_opnd = asm.stack_push(Type::Unknown); + asm.mov(ret_opnd, ret); + + true +} + +fn jit_rb_case_equal( + jit: &mut JITState, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + _ci: *const rb_callinfo, + _cme: *const rb_callable_method_entry_t, + _block: Option<BlockHandler>, + _argc: i32, + known_recv_class: Option<VALUE>, +) -> bool { + if !jit.assume_expected_cfunc( asm, ocb, known_recv_class.unwrap(), ID!(eq), rb_obj_equal as _) { + return false; + } + + asm_comment!(asm, "case_equal: {}#===", get_class_name(known_recv_class)); + + // Compare the arguments + let arg1 = asm.stack_pop(1); + let arg0 = asm.stack_pop(1); + asm.cmp(arg0, arg1); + let ret_opnd = asm.csel_e(Qtrue.into(), Qfalse.into()); + + let stack_ret = asm.stack_push(Type::UnknownImm); + asm.mov(stack_ret, ret_opnd); + + true +} + fn jit_thread_s_current( _jit: &mut JITState, asm: &mut Assembler, @@ -8148,6 +8309,13 @@ fn gen_struct_aref( } } + if c_method_tracing_currently_enabled(jit) { + // Struct accesses need fire c_call and c_return events, which we can't support + // See :attr-tracing: + gen_counter_incr(asm, Counter::send_cfunc_tracing); + return None; + } + // This is a .send call and we need to adjust the stack if flags & VM_CALL_OPT_SEND != 0 { handle_opt_send_shift_stack(asm, argc); @@ -8192,6 +8360,13 @@ fn gen_struct_aset( return None; } + if c_method_tracing_currently_enabled(jit) { + // Struct accesses need fire c_call and c_return events, which we can't support + // See :attr-tracing: + gen_counter_incr(asm, Counter::send_cfunc_tracing); + return None; + } + // This is a .send call and we need to adjust the stack if flags & VM_CALL_OPT_SEND != 0 { handle_opt_send_shift_stack(asm, argc); @@ -8458,10 +8633,8 @@ fn gen_send_general( // Handling the C method tracing events for attr_accessor // methods is easier than regular C methods as we know the // "method" we are calling into never enables those tracing - // events. Once global invalidation runs, the code for the - // attr_accessor is invalidated and we exit at the closest - // instruction boundary which is always outside of the body of - // the attr_accessor code. + // events. We are never inside the code that needs to be + // invalidated when invalidation happens. gen_counter_incr(asm, Counter::send_cfunc_tracing); return None; } @@ -8721,11 +8894,16 @@ fn gen_send_general( } } +/// Get class name from a class pointer. +fn get_class_name(class: Option<VALUE>) -> String { + class.and_then(|class| unsafe { + cstr_to_rust_string(rb_class2name(class)) + }).unwrap_or_else(|| "Unknown".to_string()) +} + /// Assemble "{class_name}#{method_name}" from a class pointer and a method ID fn get_method_name(class: Option<VALUE>, mid: u64) -> String { - let class_name = class.and_then(|class| unsafe { - cstr_to_rust_string(rb_class2name(class)) - }).unwrap_or_else(|| "Unknown".to_string()); + let class_name = get_class_name(class); let method_name = if mid != 0 { unsafe { cstr_to_rust_string(rb_id2name(mid)) } } else { @@ -10060,6 +10238,10 @@ pub fn yjit_reg_method_codegen_fns() { yjit_reg_method(rb_cString, "<<", jit_rb_str_concat); yjit_reg_method(rb_cString, "+@", jit_rb_str_uplus); + yjit_reg_method(rb_cNilClass, "===", jit_rb_case_equal); + yjit_reg_method(rb_cTrueClass, "===", jit_rb_case_equal); + yjit_reg_method(rb_cFalseClass, "===", jit_rb_case_equal); + yjit_reg_method(rb_cArray, "empty?", jit_rb_ary_empty_p); yjit_reg_method(rb_cArray, "length", jit_rb_ary_length); yjit_reg_method(rb_cArray, "size", jit_rb_ary_length); @@ -10070,6 +10252,8 @@ pub fn yjit_reg_method_codegen_fns() { yjit_reg_method(rb_mKernel, "respond_to?", jit_obj_respond_to); yjit_reg_method(rb_mKernel, "block_given?", jit_rb_f_block_given_p); + yjit_reg_method(rb_cClass, "superclass", jit_rb_class_superclass); + yjit_reg_method(rb_singleton_class(rb_cThread), "current", jit_thread_s_current); } } diff --git a/yjit/src/core.rs b/yjit/src/core.rs index fb7d52cc5d..cd6e649aa0 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -53,11 +53,11 @@ pub enum Type { ImmSymbol, TString, // An object with the T_STRING flag set, possibly an rb_cString - CString, // An un-subclassed string of type rb_cString (can have instance vars in some cases) + CString, // An object that at one point had its class field equal rb_cString (creating a singleton class changes it) TArray, // An object with the T_ARRAY flag set, possibly an rb_cArray - CArray, // An un-subclassed array of type rb_cArray (can have instance vars in some cases) + CArray, // An object that at one point had its class field equal rb_cArray (creating a singleton class changes it) THash, // An object with the T_HASH flag set, possibly an rb_cHash - CHash, // An un-subclassed hash of type rb_cHash (can have instance vars in some cases) + CHash, // An object that at one point had its class field equal rb_cHash (creating a singleton class changes it) BlockParamProxy, // A special sentinel value indicating the block parameter should be read from // the current surrounding cfp @@ -1138,8 +1138,12 @@ pub fn for_each_off_stack_iseq_payload<F: FnMut(&mut IseqPayload)>(mut callback: /// Free the per-iseq payload #[no_mangle] -pub extern "C" fn rb_yjit_iseq_free(payload: *mut c_void) { +pub extern "C" fn rb_yjit_iseq_free(iseq: IseqPtr) { + // Free invariants for the ISEQ + iseq_free_invariants(iseq); + let payload = { + let payload = unsafe { rb_iseq_get_yjit_payload(iseq) }; if payload.is_null() { // Nothing to free. return; @@ -1266,7 +1270,8 @@ pub extern "C" fn rb_yjit_iseq_mark(payload: *mut c_void) { /// GC callback for updating GC objects in the per-iseq payload. /// This is a mirror of [rb_yjit_iseq_mark]. #[no_mangle] -pub extern "C" fn rb_yjit_iseq_update_references(payload: *mut c_void) { +pub extern "C" fn rb_yjit_iseq_update_references(iseq: IseqPtr) { + let payload = unsafe { rb_iseq_get_yjit_payload(iseq) }; let payload = if payload.is_null() { // Nothing to update. return; @@ -1657,6 +1662,9 @@ impl JITState { for klass in self.no_singleton_class_assumptions { track_no_singleton_class_assumption(blockref, klass); } + if self.no_ep_escape { + track_no_ep_escape_assumption(blockref, self.iseq); + } blockref } @@ -1798,6 +1806,13 @@ impl Context { return Opnd::mem(64, SP, offset); } + /// Get an operand for the adjusted environment pointer address using SP register. + /// This is valid only when a Binding object hasn't been created for the frame. + pub fn ep_opnd(&self, offset: i32) -> Opnd { + let ep_offset = self.get_stack_size() as i32 + 1; + self.sp_opnd(-ep_offset + offset) + } + /// Stop using a register for a given stack temp. /// This allows us to reuse the register for a value that we know is dead /// and will no longer be used (e.g. popped stack temp). @@ -2643,6 +2658,12 @@ fn regenerate_branch(cb: &mut CodeBlock, branch: &Branch) { branch.get_target_address(1).map(|addr| Target::CodePtr(addr)), ); + // If the entire block is the branch and the block could be invalidated, + // we need to pad to ensure there is room for invalidation patching. + if branch.start_addr == block.start_addr && branch_terminates_block && block.entry_exit.is_some() { + asm.pad_inval_patch(); + } + // Rewrite the branch let old_write_pos = cb.get_write_pos(); let old_dropped_bytes = cb.has_dropped_bytes(); @@ -3124,6 +3145,12 @@ pub fn defer_compilation( // Likely a stub due to the increased chain depth let target0_address = branch.set_target(0, blockid, &next_ctx, ocb); + // Pad the block if it has the potential to be invalidated. This must be + // done before gen_fn() in case the jump is overwritten by a fallthrough. + if jit.block_entry_exit.is_some() { + asm.pad_inval_patch(); + } + // Call the branch generation function asm_comment!(asm, "defer_compilation"); asm.mark_branch_start(&branch); @@ -3307,9 +3334,10 @@ pub fn invalidate_block_version(blockref: &BlockRef) { assert!( cb.get_write_ptr() <= block_end, - "invalidation wrote past end of block (code_size: {:?}, new_size: {})", + "invalidation wrote past end of block (code_size: {:?}, new_size: {}, start_addr: {:?})", block.code_size(), cb.get_write_ptr().as_offset() - block_start.as_offset(), + block.start_addr.raw_ptr(cb), ); cb.set_write_ptr(cur_pos); cb.set_dropped_bytes(cur_dropped_bytes); diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 9547e3fa2c..68c0304b06 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -83,7 +83,7 @@ #![allow(non_upper_case_globals)] use std::convert::From; -use std::ffi::CString; +use std::ffi::{CString, CStr}; use std::os::raw::{c_char, c_int, c_uint}; use std::panic::{catch_unwind, UnwindSafe}; @@ -170,6 +170,7 @@ pub use rb_iseq_encoded_size as get_iseq_encoded_size; pub use rb_get_iseq_body_local_iseq as get_iseq_body_local_iseq; pub use rb_get_iseq_body_iseq_encoded as get_iseq_body_iseq_encoded; pub use rb_get_iseq_body_stack_max as get_iseq_body_stack_max; +pub use rb_get_iseq_body_type as get_iseq_body_type; pub use rb_get_iseq_flags_has_lead as get_iseq_flags_has_lead; pub use rb_get_iseq_flags_has_opt as get_iseq_flags_has_opt; pub use rb_get_iseq_flags_has_kw as get_iseq_flags_has_kw; @@ -207,8 +208,6 @@ pub use rb_RCLASS_ORIGIN as RCLASS_ORIGIN; /// Helper so we can get a Rust string for insn_name() pub fn insn_name(opcode: usize) -> String { - use std::ffi::CStr; - unsafe { // Look up Ruby's NULL-terminated insn name string let op_name = raw_insn_name(VALUE(opcode)); @@ -607,7 +606,6 @@ pub fn rust_str_to_sym(str: &str) -> VALUE { pub fn cstr_to_rust_string(c_char_ptr: *const c_char) -> Option<String> { assert!(c_char_ptr != std::ptr::null()); - use std::ffi::CStr; let c_str: &CStr = unsafe { CStr::from_ptr(c_char_ptr) }; match c_str.to_str() { @@ -619,17 +617,20 @@ pub fn cstr_to_rust_string(c_char_ptr: *const c_char) -> Option<String> { /// A location in Rust code for integrating with debugging facilities defined in C. /// Use the [src_loc!] macro to crate an instance. pub struct SourceLocation { - pub file: CString, + pub file: &'static CStr, pub line: c_int, } /// Make a [SourceLocation] at the current spot. macro_rules! src_loc { () => { - // NOTE(alan): `CString::new` allocates so we might want to limit this to debug builds. - $crate::cruby::SourceLocation { - file: std::ffi::CString::new(file!()).unwrap(), // ASCII source file paths - line: line!().try_into().unwrap(), // not that many lines + { + // Nul-terminated string with static lifetime, make a CStr out of it safely. + let file: &'static str = concat!(file!(), '\0'); + $crate::cruby::SourceLocation { + file: unsafe { std::ffi::CStr::from_ptr(file.as_ptr().cast()) }, + line: line!().try_into().unwrap(), + } } }; } @@ -667,17 +668,16 @@ where Err(_) => { // Theoretically we can recover from some of these panics, // but it's too late if the unwind reaches here. - use std::{process, str}; let _ = catch_unwind(|| { // IO functions can panic too. eprintln!( "YJIT panicked while holding VM lock acquired at {}:{}. Aborting...", - str::from_utf8(loc.file.as_bytes()).unwrap_or("<not utf8>"), + loc.file.to_string_lossy(), line, ); }); - process::abort(); + std::process::abort(); } }; @@ -804,6 +804,7 @@ pub(crate) mod ids { name: hash content: b"hash" name: respond_to_missing content: b"respond_to_missing?" name: to_ary content: b"to_ary" + name: eq content: b"==" } } diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 359227d60d..a03c2d0f00 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -478,6 +478,16 @@ pub struct iseq_inline_iv_cache_entry { pub struct iseq_inline_cvar_cache_entry { pub entry: *mut rb_cvar_class_tbl_entry, } +pub const ISEQ_TYPE_TOP: rb_iseq_type = 0; +pub const ISEQ_TYPE_METHOD: rb_iseq_type = 1; +pub const ISEQ_TYPE_BLOCK: rb_iseq_type = 2; +pub const ISEQ_TYPE_CLASS: rb_iseq_type = 3; +pub const ISEQ_TYPE_RESCUE: rb_iseq_type = 4; +pub const ISEQ_TYPE_ENSURE: rb_iseq_type = 5; +pub const ISEQ_TYPE_EVAL: rb_iseq_type = 6; +pub const ISEQ_TYPE_MAIN: rb_iseq_type = 7; +pub const ISEQ_TYPE_PLAIN: rb_iseq_type = 8; +pub type rb_iseq_type = u32; pub const BUILTIN_ATTR_LEAF: rb_builtin_attr = 1; pub const BUILTIN_ATTR_SINGLE_NOARG_LEAF: rb_builtin_attr = 2; pub const BUILTIN_ATTR_INLINE_BLOCK: rb_builtin_attr = 4; @@ -962,6 +972,7 @@ extern "C" { pub static mut rb_mKernel: VALUE; pub static mut rb_cBasicObject: VALUE; pub static mut rb_cArray: VALUE; + pub static mut rb_cClass: VALUE; pub static mut rb_cFalseClass: VALUE; pub static mut rb_cFloat: VALUE; pub static mut rb_cHash: VALUE; @@ -1013,6 +1024,7 @@ extern "C" { pub fn rb_attr_get(obj: VALUE, name: ID) -> VALUE; pub fn rb_obj_info_dump(obj: VALUE); pub fn rb_class_allocate_instance(klass: VALUE) -> VALUE; + pub fn rb_obj_equal(obj1: VALUE, obj2: VALUE) -> VALUE; pub fn rb_reg_new_ary(ary: VALUE, options: ::std::os::raw::c_int) -> VALUE; pub fn rb_ary_tmp_new_from_values( arg1: VALUE, @@ -1153,6 +1165,7 @@ extern "C" { pub fn rb_get_iseq_body_local_table_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_get_iseq_body_iseq_encoded(iseq: *const rb_iseq_t) -> *mut VALUE; pub fn rb_get_iseq_body_stack_max(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; + pub fn rb_get_iseq_body_type(iseq: *const rb_iseq_t) -> rb_iseq_type; pub fn rb_get_iseq_flags_has_lead(iseq: *const rb_iseq_t) -> bool; pub fn rb_get_iseq_flags_has_opt(iseq: *const rb_iseq_t) -> bool; pub fn rb_get_iseq_flags_has_kw(iseq: *const rb_iseq_t) -> bool; diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs index e460293440..6639fd677b 100644 --- a/yjit/src/invariants.rs +++ b/yjit/src/invariants.rs @@ -59,6 +59,11 @@ pub struct Invariants { /// there has been a singleton class for the class after boot, so you cannot /// assume no singleton class going forward. no_singleton_classes: HashMap<VALUE, HashSet<BlockRef>>, + + /// A map from an ISEQ to a set of blocks that assume base pointer is equal + /// to environment pointer. When the set is empty, it means that EP has been + /// escaped in the ISEQ. + no_ep_escape_iseqs: HashMap<IseqPtr, HashSet<BlockRef>>, } /// Private singleton instance of the invariants global struct. @@ -76,6 +81,7 @@ impl Invariants { constant_state_blocks: HashMap::new(), block_constant_states: HashMap::new(), no_singleton_classes: HashMap::new(), + no_ep_escape_iseqs: HashMap::new(), }); } } @@ -154,6 +160,31 @@ pub fn has_singleton_class_of(klass: VALUE) -> bool { .map_or(false, |blocks| blocks.is_empty()) } +/// Track that a block will assume that base pointer is equal to environment pointer. +pub fn track_no_ep_escape_assumption(uninit_block: BlockRef, iseq: IseqPtr) { + Invariants::get_instance() + .no_ep_escape_iseqs + .entry(iseq) + .or_default() + .insert(uninit_block); +} + +/// Returns true if a given ISEQ has previously escaped an environment. +pub fn iseq_escapes_ep(iseq: IseqPtr) -> bool { + Invariants::get_instance() + .no_ep_escape_iseqs + .get(&iseq) + .map_or(false, |blocks| blocks.is_empty()) +} + +/// Forget an ISEQ remembered in invariants +pub fn iseq_free_invariants(iseq: IseqPtr) { + if unsafe { INVARIANTS.is_none() } { + return; + } + Invariants::get_instance().no_ep_escape_iseqs.remove(&iseq); +} + // Checks rb_method_basic_definition_p and registers the current block for invalidation if method // lookup changes. // A "basic method" is one defined during VM boot, so we can use this to check assumptions based on @@ -317,7 +348,7 @@ pub extern "C" fn rb_yjit_constant_state_changed(id: ID) { /// Callback for marking GC objects inside [Invariants]. /// See `struct yjijt_root_struct` in C. #[no_mangle] -pub extern "C" fn rb_yjit_root_mark() { +pub extern "C" fn rb_yjit_root_mark(_: *mut c_void) { // Call rb_gc_mark on exit location's raw_samples to // wrap frames in a GC allocated object. This needs to be called // at the same time as root mark. @@ -345,6 +376,23 @@ pub extern "C" fn rb_yjit_root_mark() { } } +#[no_mangle] +pub extern "C" fn rb_yjit_root_update_references(_: *mut c_void) { + if unsafe { INVARIANTS.is_none() } { + return; + } + let no_ep_escape_iseqs = &mut Invariants::get_instance().no_ep_escape_iseqs; + + // Make a copy of the table with updated ISEQ keys + let mut updated_copy = HashMap::with_capacity(no_ep_escape_iseqs.len()); + for (iseq, blocks) in mem::take(no_ep_escape_iseqs) { + let new_iseq = unsafe { rb_gc_location(iseq.into()) }.as_iseq(); + updated_copy.insert(new_iseq, blocks); + } + + *no_ep_escape_iseqs = updated_copy; +} + /// Remove all invariant assumptions made by the block by removing the block as /// as a key in all of the relevant tables. /// For safety, the block has to be initialized and the vm lock must be held. @@ -420,6 +468,10 @@ pub fn block_assumptions_free(blockref: BlockRef) { for (_, blocks) in invariants.no_singleton_classes.iter_mut() { blocks.remove(&blockref); } + // Remove tracking for blocks assuming EP doesn't escape + for (_, blocks) in invariants.no_ep_escape_iseqs.iter_mut() { + blocks.remove(&blockref); + } } /// Callback from the opt_setinlinecache instruction in the interpreter. @@ -497,22 +549,53 @@ pub extern "C" fn rb_yjit_invalidate_no_singleton_class(klass: VALUE) { // We apply this optimization only to Array, Hash, and String for now. if unsafe { [rb_cArray, rb_cHash, rb_cString].contains(&klass) } { - let no_singleton_classes = &mut Invariants::get_instance().no_singleton_classes; - match no_singleton_classes.get_mut(&klass) { + with_vm_lock(src_loc!(), || { + let no_singleton_classes = &mut Invariants::get_instance().no_singleton_classes; + match no_singleton_classes.get_mut(&klass) { + Some(blocks) => { + // Invalidate existing blocks and let has_singleton_class_of() + // return true when they are compiled again + for block in mem::take(blocks) { + invalidate_block_version(&block); + incr_counter!(invalidate_no_singleton_class); + } + } + None => { + // Let has_singleton_class_of() return true for this class + no_singleton_classes.insert(klass, HashSet::new()); + } + } + }); + } +} + +/// Invalidate blocks for a given ISEQ that assumes environment pointer is +/// equal to base pointer. +#[no_mangle] +pub extern "C" fn rb_yjit_invalidate_ep_is_bp(iseq: IseqPtr) { + // Skip tracking EP escapes on boot. We don't need to invalidate anything during boot. + if unsafe { INVARIANTS.is_none() } { + return; + } + + with_vm_lock(src_loc!(), || { + // If an EP escape for this ISEQ is detected for the first time, invalidate all blocks + // associated to the ISEQ. + let no_ep_escape_iseqs = &mut Invariants::get_instance().no_ep_escape_iseqs; + match no_ep_escape_iseqs.get_mut(&iseq) { Some(blocks) => { - // Invalidate existing blocks and let has_singleton_class_of() - // return true when they are compiled again + // Invalidate existing blocks and make jit.ep_is_bp() return false for block in mem::take(blocks) { invalidate_block_version(&block); - incr_counter!(invalidate_no_singleton_class); + incr_counter!(invalidate_ep_escape); } } None => { - // Let has_singleton_class_of() return true for this class - no_singleton_classes.insert(klass, HashSet::new()); + // Let jit.ep_is_bp() return false for this ISEQ + no_ep_escape_iseqs.insert(iseq, HashSet::new()); } } - } + }); } // Invalidate all generated code and patch C method return code to contain diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index 0a63fab8b0..6ffe28f12a 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -268,7 +268,7 @@ macro_rules! make_counters { /// The list of counters that are available without --yjit-stats. /// They are incremented only by `incr_counter!` and don't use `gen_counter_incr`. -pub const DEFAULT_COUNTERS: [Counter; 16] = [ +pub const DEFAULT_COUNTERS: [Counter; 17] = [ Counter::code_gc_count, Counter::compiled_iseq_entry, Counter::cold_iseq_entry, @@ -286,6 +286,7 @@ pub const DEFAULT_COUNTERS: [Counter; 16] = [ Counter::invalidate_constant_state_bump, Counter::invalidate_constant_ic_fill, Counter::invalidate_no_singleton_class, + Counter::invalidate_ep_escape, ]; /// Macro to increase a counter by name and count @@ -568,6 +569,7 @@ make_counters! { invalidate_constant_state_bump, invalidate_constant_ic_fill, invalidate_no_singleton_class, + invalidate_ep_escape, // Currently, it's out of the ordinary (might be impossible) for YJIT to leave gaps in // executable memory, so this should be 0. |